diff --git a/app.css b/app.css index f3587e7..d01a3a8 100644 --- a/app.css +++ b/app.css @@ -4856,3 +4856,174 @@ footer a:hover { color: var(--accent-2); } .hi-section { padding: .4rem 1rem; } .hi-overview { padding: .6rem 1rem; } } + +/* ── VideoGame — Steam store page ── */ +.card.game-store { + background: #1b2838; + border-color: rgba(255,255,255,.06); + box-shadow: + 0 2px 8px rgba(0,0,0,.4), + 0 12px 40px rgba(0,0,0,.5); + overflow: hidden; +} +.card.game-store .hd.hidden { display: none; } + +/* Banner with game art */ +.gs-banner { + position: relative; + height: 360px; + overflow: hidden; + margin: 0; +} +.gs-banner-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* Dark gradient scrim for text legibility */ +.gs-scrim { + position: absolute; + inset: 0; + background: + linear-gradient(to bottom, + rgba(27,40,56,0) 0%, + rgba(27,40,56,.15) 40%, + rgba(27,40,56,.7) 70%, + rgba(27,40,56,.97) 95%, + rgba(27,40,56,1) 100% + ); + pointer-events: none; +} + +/* Title + meta overlay on banner */ +.gs-banner-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 0 1.5rem 1.2rem; + z-index: 2; +} +.gs-title { + font-size: 2.2rem; + font-weight: 800; + color: #fff; + line-height: 1.1; + letter-spacing: -.02em; + margin: 0 0 .35rem; + text-shadow: 0 2px 16px rgba(0,0,0,.6); +} +.gs-banner-meta { + font-size: .8rem; + color: rgba(255,255,255,.5); + letter-spacing: .03em; +} + +/* Body area — dark Steam surface */ +.card.game-store .bd { + background: #1b2838; + color: rgba(255,255,255,.8); + padding-top: .6rem; +} +.card.game-store .bd::before { + display: none; +} + +/* Tag chips */ +.gs-tags { + display: flex; + flex-wrap: wrap; + gap: .3rem; + margin-bottom: .8rem; +} +.gs-tag { + font-size: .72rem; + font-weight: 600; + padding: .25rem .6rem; + border-radius: 3px; + letter-spacing: .02em; +} +.gs-tag-genre { + background: rgba(102,192,244,.12); + color: #66c0f4; + border: 1px solid rgba(102,192,244,.2); +} +.gs-tag-platform { + background: rgba(255,255,255,.06); + color: rgba(255,255,255,.5); + border: 1px solid rgba(255,255,255,.08); +} + +/* Price box + Add to Cart */ +.gs-price-box { + display: flex; + align-items: center; + justify-content: flex-end; + padding: .4rem 0 .8rem; +} +.gs-cart-btn { + display: inline-flex; + align-items: center; + gap: .4rem; + background: linear-gradient(to bottom, #75b022 5%, #588a1b 95%); + color: #d2efa0; + font-size: .85rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .03em; + padding: .55rem 1.4rem; + border: none; + border-radius: 2px; + cursor: pointer; + text-shadow: 0 1px 2px rgba(0,0,0,.3); + box-shadow: 0 2px 6px rgba(0,0,0,.3); + transition: background .15s; +} +.gs-cart-btn:hover { + background: linear-gradient(to bottom, #8ed629 5%, #6ba022 95%); + color: #fff; +} +.gs-cart-icon { + font-size: .9rem; +} + +/* Store blurb */ +.gs-blurb { + padding: .6rem 0 1rem; +} +.gs-blurb-label { + font-size: .9rem; + font-weight: 700; + color: #fff; + margin-bottom: .5rem; + letter-spacing: .01em; +} +.gs-blurb-body { + font-size: .84rem; + line-height: 1.75; + color: rgba(255,255,255,.65); + margin: 0; +} + +/* Edit/JSON-LD disclosure in dark theme */ +.card.game-store details { + border-color: rgba(255,255,255,.08); +} +.card.game-store summary { + color: rgba(255,255,255,.4); +} +.card.game-store .row .k { + color: rgba(255,255,255,.4); +} +.card.game-store .row .v { + color: rgba(255,255,255,.75); +} + +@media (max-width: 480px) { + .gs-banner { height: 240px; } + .gs-title { font-size: 1.5rem; } + .gs-banner-overlay { padding: 0 1rem .8rem; } + .gs-blurb { padding: .4rem 0 .8rem; } +} diff --git a/app.js b/app.js index 623389f..d4cb26e 100644 --- a/app.js +++ b/app.js @@ -3171,6 +3171,115 @@ handled.add("answerCount"); handled.add("acceptedAnswer"); } + // ── VIDEOGAME (Steam store page) ── + if (cfg.type === "VideoGame") { + const card = document.querySelector(".card"); + if (card) card.classList.add("game-store"); + + // Hide normal header + const hd = document.querySelector(".hd"); + if (hd) hd.classList.add("hidden"); + + // Remove generic hero — we build our own banner + const heroMount = document.getElementById("hero-mount"); + if (heroMount) heroMount.querySelector('.hero')?.remove(); + + // ── Game art banner with title overlay ── + const imgUrl = data["image"]; + if (imgUrl) { + const banner = document.createElement("div"); banner.className = "gs-banner"; + const img = document.createElement("img"); img.className = "gs-banner-img"; + img.src = imgUrl; img.alt = data[cfg.titleProp] || ""; + img.loading = "lazy"; + banner.appendChild(img); + + // Scrim for legibility + const scrim = document.createElement("div"); scrim.className = "gs-scrim"; + banner.appendChild(scrim); + + // Title overlay + const titleOverlay = document.createElement("div"); titleOverlay.className = "gs-banner-overlay"; + const h1 = document.createElement("h1"); h1.className = "gs-title"; + h1.textContent = data[cfg.titleProp] || ""; + titleOverlay.appendChild(h1); + + // Release year + publisher as small meta inside banner + const metaParts = []; + const pub = data["publisher"]; + if (pub) metaParts.push(pub); + const rel = data["datePublished"]; + if (rel) { + const d = new Date(rel); + const yr = isNaN(d.getTime()) ? rel : d.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" }); + metaParts.push(yr); + } + if (metaParts.length) { + const meta = document.createElement("div"); meta.className = "gs-banner-meta"; + meta.textContent = metaParts.join(" · "); + titleOverlay.appendChild(meta); + } + + banner.appendChild(titleOverlay); + heroMount.appendChild(banner); + handled.add("image"); + handled.add("name"); + handled.add("publisher"); + handled.add("datePublished"); + } + + // ── Tag chips: genre + platform ── + const tagItems = []; + const genre = data["genre"]; + if (genre) { + genre.split(",").forEach(g => tagItems.push({ label: g.trim(), cls: "gs-tag-genre" })); + } + const platform = data["gamePlatform"]; + if (platform) { + platform.split(",").forEach(p => tagItems.push({ label: p.trim(), cls: "gs-tag-platform" })); + } + if (tagItems.length) { + const tags = document.createElement("div"); tags.className = "gs-tags"; + tagItems.forEach(t => { + const tag = document.createElement("span"); tag.className = "gs-tag " + t.cls; + tag.textContent = t.label; + tags.appendChild(tag); + }); + view.appendChild(tags); + handled.add("genre"); + handled.add("gamePlatform"); + } + + // ── Price box + Add to Cart ── + const priceBox = document.createElement("div"); priceBox.className = "gs-price-box"; + const url = data["url"]; + // Use the URL as a link wrapper if available + const cartBtn = document.createElement("button"); cartBtn.className = "gs-cart-btn"; + cartBtn.type = "button"; + cartBtn.innerHTML = "🛒 Add to Cart"; + priceBox.appendChild(cartBtn); + view.appendChild(priceBox); + if (url) { + // Make the whole price box clickable + priceBox.style.cursor = "pointer"; + priceBox.addEventListener("click", () => window.open(url, "_blank")); + handled.add("url"); + } + + // ── Description as store blurb ── + const desc = data["description"]; + if (desc) { + const blurb = document.createElement("div"); blurb.className = "gs-blurb"; + const label = document.createElement("div"); label.className = "gs-blurb-label"; + label.textContent = "About This Game"; + blurb.appendChild(label); + const body = document.createElement("p"); body.className = "gs-blurb-body"; + body.textContent = desc; + blurb.appendChild(body); + view.appendChild(blurb); + handled.add("description"); + } + } + return handled; }