/* Brand palette and type per DEVROW_interactive_CI_Manual.pdf:
   headline Kanit SemiBold (600), subheadline Kanit Regular (400),
   body Kanit Light (300). */
:root {
  --mx: 0; --my: 0;
  --deep-black: #0e0e10;
  --polar-white: #ffffff;
  --electric-tangerine: #fe4716;
  --soft-tangerine: #fe8868;
  --pale-tangerine: #ffe9e4;
  --warm-grey: #877d7a;
  /* Text steps derived from the CI palette rather than invented greys: bright
     copy is Polar White held back a little, muted copy is Warm Grey itself. */
  --text: rgba(255,255,255,0.92);
  /* Panel ground. Deep Black at partial opacity, not white glass: a light
     translucent panel sits over the tangerine blobs and turns orange, which
     is what made the game cards glow red under the background. */
  --panel: rgba(14,14,16,0.62);
  /* wordmark size; "interactive" is derived from it, see .subword */
  --word-size: clamp(44px, 10vw, 132px);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; background: var(--deep-black); overflow: hidden; }
body { color: var(--text); font-family: Kanit, "Helvetica Neue", Arial, sans-serif; }
a { color: #fe4716; text-decoration: none; }
a:hover { color: #ffffff; }
.bg { position: fixed; z-index: 0; pointer-events: none; }
/* One colour per view, now applied to the brand's warm bloom rather than to a
   field of coloured blobs — the shift is a tint on the floor of the page, not a
   change of scenery. site.js writes data-view on <body>. */
.bg-stack { position: fixed; inset: 0; z-index: 0; pointer-events: none; filter: hue-rotate(0deg); transition: filter 900ms cubic-bezier(0.22,1,0.36,1); }
/* Angles picked by running the base green through the feColorMatrix that
   hue-rotate actually uses, rather than by eye: it is a matrix approximation,
   so the shift is not the angle you ask for. From rgb(34,197,94) at hue 142:
     -90deg -> rgb(216,149,34), hue  38  amber
      90deg -> rgb( 94,161,255), hue 215  blue
     150deg -> rgb(229,121,255), hue 288  magenta */
body[data-view="games"] .bg-stack { filter: hue-rotate(-90deg); }
body[data-view="legal"] .bg-stack { filter: hue-rotate(90deg); }
body[data-view="devlog"] .bg-stack { filter: hue-rotate(150deg); }
@media (prefers-reduced-motion: reduce) {
  .bg-stack { transition: none; }
}
/* The background follows the studio identity (DevRowBranding/web/
   devrow-itch-background.svg) rather than inventing its own: black ground, an
   isometric grid one step above it, and a single warm bloom anchored below the
   bottom edge. That anchoring is the point — the brand file notes that a radial
   centred inside the canvas "reads as a blob with a middle, and the eye finds
   the middle", which is exactly what the four coloured blobs here used to do.
   The green version is kept in backups/site-css-green-blobs.css.

   Grid geometry from the same file, scaled from its 3840px canvas: verticals
   every 384px and diagonals at +/-30deg, both a third of the size here. Line
   colour #110F0E and bloom colour #3E0600 (tangerine 950) are taken verbatim. */
.bg-grid { inset: -10%; opacity: 0.9; background-image:
  repeating-linear-gradient(90deg, #110F0E 0 1px, transparent 1px 128px),
  repeating-linear-gradient(30deg, #110F0E 0 1px, transparent 1px 96px),
  repeating-linear-gradient(-30deg, #110F0E 0 1px, transparent 1px 96px); }
.bg-bloom { inset: 0; background: radial-gradient(78% 78% at 50% 112%, rgba(62,6,0,0.30) 0%, rgba(62,6,0,0.10) 55%, rgba(62,6,0,0) 100%); }
.bg-mark { left: 50%; top: 50%; width: 78vmin; height: 78vmin; transform: translate(-50%, -50%) scale(1.1); opacity: 0.16; filter: blur(26px) saturate(120%); }
.vignette { inset: 0; background: radial-gradient(130% 100% at 50% 50%, rgba(14,14,16,0) 40%, rgba(14,14,16,0.85) 100%); }
.scanlines { inset: 0; z-index: 4; opacity: 0.32; background: repeating-linear-gradient(180deg, rgba(255,255,255,0.055) 0px, rgba(255,255,255,0.055) 1px, rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px); background-size: 100% 4px; animation: scan 3s linear infinite; }

.scroller { position: relative; z-index: 1; display: flex; height: 100vh; height: 100dvh; width: 100vw; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; scroll-behavior: smooth; scrollbar-width: none; -ms-overflow-style: none; }
.scroller::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* scroll-snap-stop: always — without it a fast swipe carries its momentum
   across several panels at once. This makes every panel a hard stop, so one
   swipe moves exactly one level. */
.panel { position: relative; flex: 0 0 100vw; height: 100vh; height: 100dvh; scroll-snap-align: start; scroll-snap-stop: always; }

/* the hero panel is itself a vertical scroller: hero on top, games below */
#home { overflow-y: auto; overflow-x: hidden; scroll-snap-type: y mandatory; scroll-behavior: smooth; scrollbar-width: none; }
#home::-webkit-scrollbar { width: 0; height: 0; display: none; }
.column { display: flex; flex-direction: column; }
.view { position: relative; height: 100vh; height: 100dvh; scroll-snap-align: start; scroll-snap-stop: always; }
#hero { display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 48px 24px; }
.hero { max-width: 860px; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 40px; text-align: center; perspective: 900px; transform-style: preserve-3d; }
.tilt { position: relative; display: flex; flex-direction: column; align-items: center; gap: 32px; transform-style: preserve-3d; transform: rotateY(calc(var(--mx) * 16deg)) rotateX(calc(var(--my) * -12deg)); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.lockup { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 34px); transform-style: preserve-3d; }
.lockup-mark { width: clamp(64px, 14vw, 176px); height: clamp(64px, 14vw, 176px); display: block; filter: drop-shadow(0 18px 50px rgba(254,71,22,0.45)); transform: translate3d(calc(var(--mx) * -30px), calc(var(--my) * -18px), 90px); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.wordblock { display: flex; flex-direction: column; align-items: flex-start; }
.wordwrap { position: relative; display: inline-flex; transform-style: preserve-3d; isolation: isolate; }
.word { margin: 0; font-size: var(--word-size); font-weight: 600; line-height: 0.9; letter-spacing: -0.01em; white-space: nowrap; color: var(--polar-white); filter: drop-shadow(0 24px 60px rgba(254,71,22,0.30)); transform: translate3d(calc(var(--mx) * -30px), calc(var(--my) * -18px), 90px); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
/* CI lockup: "interactive" is Kanit Light at 0.609x the wordmark, tracked
   0.194em so the two lines end up the same width (per the Wort-Bild-Marke). */
.subword { font-weight: 300; font-size: calc(var(--word-size) * 0.609); letter-spacing: 0.194em; margin-right: -0.194em; line-height: 1; color: var(--polar-white); transform: translate3d(calc(var(--mx) * -20px), calc(var(--my) * -12px), 60px); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }

.buttons { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; transform: translate3d(calc(var(--mx) * -14px), calc(var(--my) * -9px), 40px); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.btn { display: flex; align-items: center; gap: 10px; min-height: 56px; padding: 0 30px; color: #fff; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; border: 1px solid rgba(255,255,255,0.30); backdrop-filter: blur(16px) saturate(180%); -webkit-backdrop-filter: blur(16px) saturate(180%); transition: transform 220ms cubic-bezier(0.22,1,0.36,1), box-shadow 220ms ease; }
.btn:hover { color: #fff; transform: translateY(-3px); }
.btn-discord { border-radius: 16px; background: linear-gradient(150deg, rgba(255,255,255,0.26) 0%, rgba(88,101,242,0.85) 40%, rgba(74,86,224,0.9) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 16px 40px rgba(88,101,242,0.35); }
.btn-discord:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,0.65), 0 24px 56px rgba(88,101,242,0.5); }
.btn-bluesky { border-radius: 28px; background: linear-gradient(150deg, rgba(255,255,255,0.28) 0%, rgba(17,133,254,0.85) 40%, rgba(10,111,216,0.9) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 16px 40px rgba(17,133,254,0.35); }
.btn-bluesky:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,0.65), 0 24px 56px rgba(17,133,254,0.5); }
.btn-itch { border-radius: 20px; background: linear-gradient(150deg, rgba(255,255,255,0.26) 0%, rgba(250,92,92,0.85) 40%, rgba(226,73,73,0.9) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 16px 40px rgba(250,92,92,0.35); }
.btn-itch:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,0.65), 0 24px 56px rgba(250,92,92,0.5); }
.btn svg { width: 20px; height: 20px; display: block; fill: currentColor; }

.site-footer { width: 100%; max-width: 1100px; display: flex; align-items: center; justify-content: center; font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--warm-grey); }
/* Sits on the footer line, bottom right — not vertically centred, where it
   collides with the lockup. */
.nav-legal { position: absolute; z-index: 3; right: 28px; bottom: 44px; display: flex; align-items: center; gap: 16px; color: var(--warm-grey); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; transition: transform 240ms cubic-bezier(0.22,1,0.36,1), color 200ms ease; }
.nav-legal:hover { transform: translateX(8px); color: var(--electric-tangerine); }
.nav-legal .arrow { font-size: 30px; line-height: 1; }
#legal { overflow-y: auto; scrollbar-gutter: stable both-edges; display: flex; justify-content: center; padding: 64px 24px; }
/* Glass card in the same material as the hero buttons, drifting with the
   cursor like the hero lockup, so both panels read as one surface. */
.legal-inner { width: 100%; max-width: 760px; height: max-content; display: flex; flex-direction: column; gap: 44px; padding: clamp(28px, 5vw, 56px); border: 1px solid rgba(255,255,255,0.14); border-radius: 28px; background: var(--panel); box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), 0 40px 90px rgba(0,0,0,0.55); backdrop-filter: blur(18px) saturate(180%); -webkit-backdrop-filter: blur(18px) saturate(180%); transform: translate3d(calc(var(--mx) * -10px), calc(var(--my) * -6px), 0); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.legal-top { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.nav-back { display: flex; align-items: center; gap: 14px; color: var(--warm-grey); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; transition: transform 240ms cubic-bezier(0.22,1,0.36,1), color 200ms ease; }
.nav-back:hover { transform: translateX(-8px); color: var(--electric-tangerine); }
.nav-back .arrow { font-size: 30px; line-height: 1; }
/* Headings pick up the hero wordmark's gradient, held still. */
.legal h2, .devlog > .legal-inner > h2 { margin: 0; font-weight: 600; letter-spacing: -0.035em; line-height: 0.95; font-size: clamp(34px, 6vw, 60px); background: linear-gradient(105deg, var(--polar-white) 0%, var(--pale-tangerine) 34%, var(--electric-tangerine) 78%, var(--soft-tangerine) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent; }
.legal h2.sub { padding-top: 32px; border-top: 1px solid rgba(255,255,255,0.12); font-size: clamp(30px, 5vw, 50px); }
.block { display: flex; flex-direction: column; gap: 10px; }
.block:last-child { padding-bottom: 0; }
.label { font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--soft-tangerine); }
.block p { margin: 0; font-size: 17px; line-height: 1.7; color: var(--text); font-weight: 300; }
.block p.muted { color: var(--warm-grey); text-wrap: pretty; }

@keyframes scan { to { background-position: 0 240px; } }

/* below this the copyright line and the legal link would meet in the middle,
   so stack the link above it */
@media (max-width: 720px) {
  /* "Imprint & Privacy" is too wide to sit beside the devlog link at this
     size, so it wraps onto two right-aligned lines beside its arrow. */
  .nav-legal { bottom: 80px; align-items: center; gap: 10px; }
  .nav-legal span:first-child { max-width: 11ch; text-align: right; line-height: 1.3; }
  .nav-devlog { align-items: center; }
}
@media (max-width: 640px) {
  .lockup { flex-direction: column; gap: 18px; }
  .wordblock { align-items: center; }
  .nav-legal { right: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .scanlines { animation: none !important; }
}

/* ---- devlog panel (left of the hero) ---- */
#devlog { overflow-y: auto; scrollbar-gutter: stable both-edges; display: flex; justify-content: center; padding: 64px 24px; }
.devlog-inner { max-width: 820px; gap: 30px; }

.nav-forward:hover { transform: translateX(8px); }
.entry h2 { margin: 0; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; font-size: clamp(26px, 3.4vw, 38px); color: var(--polar-white); text-wrap: balance; }
.entry .lede { margin: 0; font-size: 17px; line-height: 1.6; font-weight: 300; color: var(--text); }
.entry h3 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; color: var(--pale-tangerine); }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.stat { display: flex; flex-direction: column; gap: 6px; padding: 16px 18px; border: 1px solid rgba(255,255,255,0.10); border-radius: 16px; background: rgba(255,255,255,0.04); }
.stat .v { font-size: 26px; font-weight: 600; line-height: 1.1; letter-spacing: -0.02em; color: var(--electric-tangerine); }
.stat .k { font-size: 12px; line-height: 1.4; font-weight: 300; color: var(--warm-grey); }
.devlog-more { margin: 0; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.12); font-size: 14px; }
.nav-devlog { position: absolute; z-index: 3; left: 28px; bottom: 44px; display: flex; align-items: center; gap: 16px; color: var(--warm-grey); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; transition: transform 240ms cubic-bezier(0.22,1,0.36,1), color 200ms ease; }
.nav-devlog:hover { transform: translateX(-8px); color: var(--electric-tangerine); }
.nav-devlog .arrow { font-size: 30px; line-height: 1; }

/* Visible scrollbar on the two text panels — without it there is nothing to
   say the panel scrolls. */
#legal, #devlog { scrollbar-width: thin; scrollbar-color: rgba(254,71,22,0.55) rgba(255,255,255,0.06); }
#legal::-webkit-scrollbar, #devlog::-webkit-scrollbar { width: 10px; }
#legal::-webkit-scrollbar-track, #devlog::-webkit-scrollbar-track { background: rgba(255,255,255,0.05); }
#legal::-webkit-scrollbar-thumb, #devlog::-webkit-scrollbar-thumb { background: rgba(254,71,22,0.55); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
#legal::-webkit-scrollbar-thumb:hover, #devlog::-webkit-scrollbar-thumb:hover { background: var(--electric-tangerine); background-clip: padding-box; }

@media (max-width: 720px) {
  .nav-devlog { left: 16px; bottom: 86px; }
}
.entry figure { margin: 0; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; overflow: hidden; background: rgba(0,0,0,0.35); }
.entry figure img { display: block; width: 100%; height: auto; }
.entry figcaption { padding: 12px 16px; border-top: 1px solid rgba(255,255,255,0.12); font-size: 13.5px; line-height: 1.55; font-weight: 300; color: var(--warm-grey); }

/* ---- devlog cards ---- */
/* The cards live in their own flex column so the sort toggle can reverse them
   without touching the heading or the back link. The gap repeats
   .devlog-inner's, which the cards used to inherit as direct children. */
.cards { display: flex; flex-direction: column; gap: 30px; }
.cards.is-oldest { flex-direction: column-reverse; }

.devlog-head { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.devlog-head h2 { margin: 0; }
.sort-toggle { display: inline-flex; align-items: center; gap: 10px; min-height: 40px; padding: 0 16px; border: 1px solid rgba(255,255,255,0.20); border-radius: 999px; background: rgba(255,255,255,0.04); color: var(--warm-grey); font: inherit; font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; transition: border-color 200ms ease, color 200ms ease, background 200ms ease; }
.sort-toggle:hover { border-color: var(--electric-tangerine); color: var(--polar-white); background: rgba(254,71,22,0.12); }
.sort-toggle .sort-arrow { font-size: 18px; line-height: 1; transition: transform 240ms cubic-bezier(0.22,1,0.36,1); }
.sort-toggle[aria-pressed="true"] .sort-arrow { transform: rotate(180deg); }

.card { border: 1px solid rgba(255,255,255,0.12); border-radius: 20px; overflow: hidden; background: var(--panel); transition: border-color 240ms ease, background 240ms ease, transform 300ms cubic-bezier(0.22,1,0.36,1), box-shadow 300ms ease; }
.card:hover { border-color: rgba(254,71,22,0.45); background: rgba(255,255,255,0.06); transform: translateY(-3px); box-shadow: 0 26px 60px rgba(0,0,0,0.45); }
.card-head { display: block; position: relative; width: 100%; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.card-shot { display: block; width: 100%; aspect-ratio: 21 / 9; object-fit: cover; filter: saturate(0.9) brightness(0.78); transition: transform 620ms cubic-bezier(0.22,1,0.36,1), filter 420ms ease; }
.card:hover .card-shot { transform: scale(1.04); filter: saturate(1) brightness(0.95); }
.card-text { display: flex; flex-direction: column; gap: 8px; padding: 22px clamp(20px, 3vw, 30px) 24px; }
.card-title { font-size: clamp(20px, 2.4vw, 27px); font-weight: 600; line-height: 1.2; letter-spacing: -0.015em; color: var(--polar-white); text-wrap: balance; padding-right: 44px; }
.card-toggle { position: absolute; right: clamp(20px, 3vw, 30px); bottom: 26px; width: 26px; height: 26px; }
.card-toggle::before, .card-toggle::after { content: ""; position: absolute; left: 50%; top: 50%; width: 15px; height: 2px; margin: -1px 0 0 -7.5px; border-radius: 2px; background: var(--electric-tangerine); transition: transform 320ms cubic-bezier(0.22,1,0.36,1); }
.card-toggle::after { transform: rotate(90deg); }
.card:hover .card-toggle::before { transform: rotate(180deg); }
.card:hover .card-toggle::after { transform: rotate(270deg); }

/* In the list a card is just its cover; the body only exists once the card has
   been moved into the reader. */
.card-body { display: none; }

.card .lede { margin: 0; font-size: 17px; line-height: 1.6; font-weight: 300; color: var(--text); }
.card h3 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; color: var(--pale-tangerine); }
.card figure { margin: 0; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; overflow: hidden; background: rgba(0,0,0,0.35); }
.card figure img { display: block; width: 100%; height: auto; }
.card figcaption { padding: 12px 16px; border-top: 1px solid rgba(255,255,255,0.12); font-size: 13.5px; line-height: 1.55; font-weight: 300; color: var(--warm-grey); }

/* ---- reader: the card flies out of the list toward the viewer ---- */
.reader { position: fixed; inset: 0; z-index: 30; display: flex; padding: clamp(16px, 4vh, 56px) clamp(16px, 4vw, 56px); overflow-y: auto; overscroll-behavior: contain; background: rgba(14,14,16,0.7); backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%); animation: reader-veil 320ms ease both; }
.reader[hidden] { display: none; }
.reader-shell { margin: auto; width: min(880px, 100%); perspective: 1400px; }
.reader .card { cursor: default; box-shadow: 0 50px 120px rgba(0,0,0,0.65); border-color: rgba(254,71,22,0.4); background: rgba(14,14,16,0.72); animation: card-fly-in 560ms cubic-bezier(0.16,1,0.3,1) both; }
.reader .card:hover { transform: none; box-shadow: 0 50px 120px rgba(0,0,0,0.65); }
.reader .card-head { cursor: default; }
.reader .card-toggle { display: none; }
.reader .card-title { padding-right: 0; }
/* the cover image belongs to the preview card; inside the reader the entry
   opens straight on its headline */
.reader .card-shot { display: none; }
.reader .card-text { padding-top: clamp(24px, 3vw, 32px); }
.reader .card-body { display: block; padding: 4px clamp(20px, 3vw, 34px) clamp(26px, 3vw, 36px); }
.reader .lede { margin: 0 0 30px; font-size: 18px; line-height: 1.6; }
.reader .stats { margin: 0 0 34px; }
.reader .block { margin: 34px 0 0; display: flex; flex-direction: column; gap: 10px; }
.reader .block + figure { margin-top: 18px; }
.reader figure + .block { margin-top: 38px; }
.reader h3 { font-size: 19px; line-height: 1.3; }
.reader .devlog-more { margin: 34px 0 0; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.12); font-size: 14px; }
.reader .card-body > * { animation: card-rise 520ms cubic-bezier(0.22,1,0.36,1) both; animation-delay: 200ms; }
.reader .card-body > *:nth-child(2) { animation-delay: 280ms; }
.reader .card-body > *:nth-child(n+3) { animation-delay: 330ms; }
/* closing runs its own keyframes rather than a reversed copy of the opening
   ones — a re-declared animation name does not restart, which made the close
   snap instead of play. */
/* the veil outlives the card on the way out, so the list is put back while it
   is still covered */
.reader.is-closing { animation: reader-veil-out 460ms ease 140ms both; }
.reader.is-closing .card { animation: card-fly-out 280ms cubic-bezier(0.4,0,0.9,0.4) both; }
.reader.is-closing .card-body > * { animation: none; }

.reader-close { position: fixed; top: clamp(12px, 3vh, 28px); right: clamp(12px, 3vw, 28px); z-index: 31; width: 46px; height: 46px; padding: 0; border: 1px solid rgba(255,255,255,0.22); border-radius: 999px; background: rgba(14,14,16,0.7); backdrop-filter: blur(10px); cursor: pointer; transition: transform 240ms cubic-bezier(0.22,1,0.36,1), border-color 200ms ease, background 200ms ease; }
.reader-close:hover { transform: rotate(90deg); border-color: var(--electric-tangerine); background: var(--electric-tangerine); }
.reader-close span, .reader-close span::before { content: ""; position: absolute; left: 50%; top: 50%; width: 18px; height: 2px; margin: -1px 0 0 -9px; border-radius: 2px; background: var(--polar-white); }
.reader-close span { transform: rotate(45deg); }
.reader-close span::before { margin: -1px 0 0 -9px; transform: rotate(90deg); }

@keyframes card-fly-in { from { opacity: 0; transform: translate3d(0, 60px, -420px) scale(0.9); } to { opacity: 1; transform: none; } }
@keyframes reader-veil { from { opacity: 0; } to { opacity: 1; } }
@keyframes reader-veil-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes card-fly-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translate3d(0, 40px, -320px) scale(0.92); } }
@keyframes card-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .reader, .reader .card, .reader .card-body > *, .card, .card-shot { animation: none !important; transition: none !important; }
}

/* ---- author card (imprint) ---- */
.author { display: flex; align-items: center; gap: clamp(16px, 2.6vw, 24px); margin-top: 2px; }
/* the cut-out portrait sits in a disc of flat-shaded facets, so the studio
   backdrop is replaced by the same language the game is drawn in */
.author-art { position: relative; flex: none; width: clamp(88px, 13vw, 116px); aspect-ratio: 1; border-radius: 50%; overflow: hidden; background: radial-gradient(70% 70% at 50% 38%, rgba(254,71,22,0.30) 0%, rgba(14,14,16,0.92) 72%); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.16), 0 16px 36px rgba(0,0,0,0.5); }
/* the disc behind the cut-out is the portrait itself, blown up and blurred —
   a soft warm halo rather than a graphic pattern */
.author-blur { position: absolute; left: 50%; top: 50%; width: 150%; height: auto; transform: translate(-50%, -46%) scale(1.15); filter: blur(16px) saturate(1.25) brightness(0.72); opacity: 0.85; }
/* height: auto matters — the img carries width/height attributes, and without
   it the attribute height wins and the face stretches */
.author-photo { position: absolute; left: 50%; bottom: 0; width: 100%; height: auto; transform: translateX(-50%); filter: saturate(0.9) contrast(1.03) drop-shadow(0 8px 18px rgba(0,0,0,0.55)); }
.author-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.author-name { margin: 0; font-size: clamp(19px, 2.2vw, 23px); font-weight: 600; letter-spacing: -0.015em; color: var(--polar-white); }
.author-bio { margin: 0; font-size: 15px; line-height: 1.6; font-weight: 300; color: var(--warm-grey); text-wrap: pretty; }

@media (max-width: 560px) {
  .author { flex-direction: column; align-items: flex-start; }
}

/* while an entry is open the scroller must not re-snap on the reflow, and the
   placeholder holds the list's shape so nothing moves behind the veil */
.card-slot { flex: none; }
body.is-reading .scroller { scroll-snap-type: none; }
/* the devlog panel has no heading — the back link sits on its own, pushed right */
.devlog-top { justify-content: flex-end; }

/* ---- battle cry: RTS unit-response line under the wordmark. The letters the
   studio name eats (F and TH -> V) are called out in Electric Tangerine. ---- */
.cry { display: flex; align-items: baseline; justify-content: center; gap: 12px; margin: -8px 0 0; min-height: 1.4em; font-size: clamp(12px, 1.5vw, 15px); font-weight: 300; letter-spacing: 0.22em; text-transform: uppercase; color: var(--warm-grey); transform: translate3d(calc(var(--mx) * -22px), calc(var(--my) * -13px), 70px); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.cry-mark { color: var(--electric-tangerine); font-size: 0.8em; opacity: 0.9; animation: cry-blink 2.4s steps(1, end) infinite; }
.cry-line { display: inline-block; animation: cry-in 420ms cubic-bezier(0.22,1,0.36,1) both; }
.cry-line b { font-weight: 600; color: var(--electric-tangerine); text-shadow: 0 0 18px rgba(254,71,22,0.55); }
.cry.is-out .cry-line { animation: cry-out 200ms ease both; }
@keyframes cry-in { from { opacity: 0; transform: translateX(-10px); letter-spacing: 0.34em; } to { opacity: 1; transform: none; } }
@keyframes cry-out { from { opacity: 1; } to { opacity: 0; transform: translateX(10px); } }
@keyframes cry-blink { 0%, 62% { opacity: 0.9; } 63%, 100% { opacity: 0.25; } }

@media (prefers-reduced-motion: reduce) {
  .cry-mark, .cry-line { animation: none !important; }
}

/* ---- games view, below the hero ---- */
/* sits above the centred copyright line, which is in the hero's own flow */
.nav-games { position: absolute; z-index: 3; left: 50%; bottom: 84px; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--warm-grey); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; transition: transform 240ms cubic-bezier(0.22,1,0.36,1), color 200ms ease; }
.nav-games:hover { transform: translateX(-50%) translateY(6px); color: var(--electric-tangerine); }
.nav-games .arrow { font-size: 28px; line-height: 1; animation: nudge 2.6s ease-in-out infinite; }
@keyframes nudge { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

#games { overflow-y: auto; scrollbar-gutter: stable both-edges; display: flex; justify-content: center; padding: 64px 24px; scrollbar-width: thin; scrollbar-color: rgba(254,71,22,0.55) rgba(255,255,255,0.06); }
#games::-webkit-scrollbar { width: 10px; }
#games::-webkit-scrollbar-track { background: rgba(255,255,255,0.05); }
#games::-webkit-scrollbar-thumb { background: rgba(254,71,22,0.55); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
.games-inner { width: 100%; max-width: 820px; height: max-content; display: flex; flex-direction: column; gap: 28px; }
.games-top { display: flex; justify-content: flex-end; }
.nav-up:hover { transform: translateY(-6px); }
#games h2 { margin: 0; }

.game { display: grid; grid-template-columns: minmax(0, 1fr); border: 1px solid rgba(255,255,255,0.14); border-radius: 24px; overflow: hidden; background: var(--panel); box-shadow: inset 0 1px 0 rgba(255,255,255,0.18), 0 30px 70px rgba(0,0,0,0.5); backdrop-filter: blur(18px) saturate(180%); -webkit-backdrop-filter: blur(18px) saturate(180%); transition: transform 300ms cubic-bezier(0.22,1,0.36,1), border-color 240ms ease; }
.game:hover { transform: translateY(-3px); border-color: rgba(254,71,22,0.45); }
.game-shot { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; display: block; filter: saturate(0.95) brightness(0.9); transition: transform 620ms cubic-bezier(0.22,1,0.36,1), filter 420ms ease; }
.game:hover .game-shot { transform: scale(1.04); filter: saturate(1) brightness(1); }
/* The cover is a full-width 16:9 band above the text, not a side column: the
   key visual carries the title lockup on its right third, which a narrow
   column crops straight off. */
/* ---- games that open as a page ---- */
/* the cover is the trigger, the same way .card-head is for a devlog entry */
.game-head { position: relative; display: block; padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; overflow: hidden; }
.game-head .game-shot { height: 100%; }
.game-shot-count { position: absolute; right: 12px; bottom: 12px; display: inline-flex; align-items: center; gap: 5px; padding: 5px 12px; border: 1px solid rgba(255,255,255,0.24); border-radius: 999px; background: rgba(14,14,16,0.66); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); color: var(--polar-white); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; transition: border-color 200ms ease, background 200ms ease; }
.game-head:hover .game-shot-count { border-color: var(--electric-tangerine); background: rgba(254,71,22,0.8); }

/* the shot list only exists once the card has been moved into the reader */
.game-body { display: none; }
.reader .game { grid-template-columns: 1fr; cursor: default; }
.reader .game-head { cursor: default; }
.reader .game-head .game-shot { aspect-ratio: 16 / 9; }
.reader .game-shot-count { display: none; }
.reader .game-body { display: flex; flex-direction: column; gap: 18px; padding: 0 clamp(22px, 3vw, 32px) clamp(22px, 3vw, 32px); }
.reader .game-body figure { margin: 0; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; overflow: hidden; background: rgba(0,0,0,0.35); }
/* the shots are lazy and only enter the page when the card opens, so the
   frame reserves their space — otherwise the panel collapses and reflows as
   each one arrives */
.reader .game-body img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; background: rgba(255,255,255,0.03); }


.game-text { display: flex; flex-direction: column; gap: 12px; padding: clamp(22px, 3vw, 32px); }
.game-status { align-self: flex-start; padding: 5px 12px; border: 1px solid rgba(254,71,22,0.5); border-radius: 999px; font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--soft-tangerine); }
.game-title { margin: 0; font-size: clamp(24px, 3vw, 32px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; color: var(--polar-white); }
.game-blurb { margin: 0; font-size: 16px; line-height: 1.65; font-weight: 300; color: var(--text); text-wrap: pretty; }
.game-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }
.game-link { min-height: 40px; display: inline-flex; align-items: center; padding: 0 18px; border: 1px solid rgba(255,255,255,0.24); border-radius: 999px; font-size: 13px; letter-spacing: 0.04em; color: var(--polar-white); }
.game-link:hover { border-color: var(--electric-tangerine); background: var(--electric-tangerine); color: var(--deep-black); }
/* Same pill as .game-link, deliberately without the hover state and with a
   dashed edge: it announces something that is coming, and nothing happens when
   you press it. Losing the hover is the point — a solid pill that lights up and
   then does nothing is the thing this is trying not to be. */
.game-note { min-height: 40px; display: inline-flex; align-items: center; padding: 0 18px; border: 1px dashed rgba(255,255,255,0.28); border-radius: 999px; font-size: 13px; letter-spacing: 0.04em; color: var(--warm-grey); cursor: default; }

/* One card at a time on a phone. The cards are shorter than the scrollport
   (628 and 598 against 812), so they can be centred rather than aligned to the
   top, and scroll-snap-stop keeps a flick from skipping past one — the same
   rule the panels use. Left off on wider screens, where several cards are
   meant to be visible at once. */
@media (max-width: 720px) {
  /* The tail padding is what lets the last card reach the middle: without it
     the container runs out of scroll and the final card rests bottom-aligned,
     44px off centre. The empty space it adds is unreachable — mandatory snap
     has no snap point down there, so the list springs back to the card. */
  /* proximity, not mandatory. Mandatory never let the list rest at scrollTop 0,
     so it never reached its top edge and never handed the scroll on to the hero
     panel above — swiping back up was impossible. Proximity still snaps each
     card to the middle when you come near one, and lets the boundary be
     reached. */
  #games { scroll-snap-type: y proximity; padding-top: 0; padding-bottom: 40vh; }
  .game { scroll-snap-align: center; scroll-snap-stop: always; }
  /* The back row is both the sticky header and the list's top snap point. It
     has to be a snap point: proximity alone still pulled the list off zero
     towards the first card, and a list that can never rest at its top edge
     never hands the scroll up to the hero panel. Its own top padding replaces
     the panel's, so it sits at offset 0 and the snap position really is 0. */
  /* Not sticky any more: it only had to be while the list could not rest at
     its top. Now that it is the top snap point, scrolling up reaches it, and
     it stops floating over the card art. Its own top padding replaces the
     panel's, so it sits at offset 0 and the snap position really is 0. */
  .games-top { padding: 56px 0 4px; scroll-snap-align: start; }
}

@media (max-width: 720px) {
  .game { grid-template-columns: 1fr; }
  .game-shot { aspect-ratio: 16 / 9; min-height: 0; }
  /* the side links move up onto their own row here, so the games link has to
     clear that row as well as the copyright. This override has to live after
     the base .nav-games rule above, not with the earlier hero media query. */
  .nav-games { bottom: 128px; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-games .arrow { animation: none; }
}

/* ---- support strip, in the hero ---- */
/* Deliberately not called "newsletter": ad blockers cosmetically filter class
   names containing that word, which removed the whole block for anyone running
   uBlock or similar. The link points at subscribe.php on this domain for the
   same reason — a direct sibforms.com href is on EasyPrivacy. */
/* Brevo's hosted form is an outbound link, so this block makes no third-party
   request. It reuses the imprint portrait treatment (.author-art), but kept
   deliberately slim: the hero is one viewport tall and already carries the
   lockup, the battle cry, two buttons, the corner links and the footer. */
/* A dark, mostly opaque ground rather than the 155deg white gradient the
   cards use. Two reasons: the gradient's stops land as a visible diagonal
   seam on a panel this wide and short, and a light translucent ground let
   .bg-mark — the big fixed wordmark centred behind the hero — read straight
   through as a hard angular streak. The backdrop blur still does the glass
   work; the background behind now arrives as a glow, not a shape. */
.support { display: flex; align-items: center; gap: clamp(14px, 2vw, 22px); max-width: 780px; padding: 14px clamp(16px, 2vw, 22px); border: 1px solid rgba(255,255,255,0.14); border-radius: 28px; background: rgba(14,14,16,0.62); box-shadow: 0 20px 50px rgba(0,0,0,0.45); backdrop-filter: blur(18px) saturate(180%); -webkit-backdrop-filter: blur(18px) saturate(180%); text-align: left; transform: translate3d(calc(var(--mx) * -10px), calc(var(--my) * -6px), 0); transition: transform 260ms cubic-bezier(0.22,1,0.36,1); }
.support-art { width: clamp(48px, 6vw, 62px); }
.support-text { display: flex; align-items: center; gap: clamp(12px, 2vw, 20px); min-width: 0; }
.support-body { margin: 0; font-size: 14px; line-height: 1.5; font-weight: 300; color: var(--text); text-wrap: pretty; }
.support-cta { flex: none; border-color: var(--electric-tangerine); }
.support-cta:hover { background: var(--electric-tangerine); color: var(--deep-black); }
@media (max-width: 900px) {
  /* Stays a single row here on purpose. Stacking it doubles the height and the
     strip runs straight into the games arrow below — the hero is exactly one
     viewport tall, so vertical space is the scarce thing. The pitch drops out
     and the portrait plus the button carry it; the full story is one tap away. */
  .support { padding: 8px 14px; gap: 12px; border-radius: 999px; }
  .support-body { display: none; }
  .support-art { width: 44px; }
  .support-cta { font-size: 12px; padding: 0 14px; }
  /* the strip costs the hero ~66px it did not have, so the stack tightens to
     pay for it rather than growing into the games arrow underneath */
  .hero { gap: 22px; }
  .tilt { gap: 20px; }
  /* three stacked buttons at full height overflow into the games arrow at this
     size, so they lose a little height and padding rather than the hero losing
     a button */
  .buttons { gap: 10px; }
  .btn { min-height: 48px; padding: 0 22px; font-size: 15px; }
}

/* ---- itch.io embed ---- */
/* The widget is a fixed 552x167. The card column is narrower than that on most
   screens, so the frame scales down as a block rather than overflowing: width
   is fluid and the height follows the widget's own 552:167 ratio, which keeps
   its internal layout from clipping. */
.embed { grid-column: 1 / -1; min-width: 0; overflow: hidden; padding: 0 clamp(22px, 3vw, 32px) clamp(22px, 3vw, 32px); }
/* Fixed 552x167 widget: it does not reflow, so it keeps its full height and
   only the width gives, down to the point where itch's own layout still fits. */
.embed-fit { width: 100%; }
.embed iframe { display: block; width: 100%; max-width: 552px; height: 167px; border: 1px solid rgba(255,255,255,0.14); border-radius: 12px; background: rgba(0,0,0,0.35); color-scheme: dark; }

/* itch's widget has a narrow layout of its own — cover on top, title and button
   stacked under it — which it switches to when the frame is about 208px wide.
   Handing it that width beats scaling the 552px one down: nothing is shrunk, so
   the type stays at its intended size and the button stays a real tap target. */
@media (max-width: 640px) {
  .embed iframe { width: 208px; max-width: 100%; height: 200px; }
}
