/* The overlay page (site/overlay/): the SPAM Overlay showcase, then the companion SPAM Meta site. It wears the site's own
   tokens, controls and game card (../../css/base.css, cards.css); this file adds only what the page needs on top: its header,
   the hero stage that shows the overlay at work, the feature list, the steps, the privacy lines, the FAQ and the previews of
   SPAM Meta. Every value comes from DESIGN.md's closed scales; the one curve and the four durations are base.css tokens. */

/* ---------------------------------------------------------------- the ambient field (../../css/ambient.css), showcase palette:
   the game board's own violet instead of SPAM Meta's lifted indigo, and a faint violet lamp behind the hero stage. Same
   ground (Night), still no glow and no colour wash; the field holds still while the hero loop plays (showcase.js). */
:root {
  --amb-pool-a: rgba(52, 38, 112, .62);
  --amb-pool-b: rgba(42, 36, 82, .72);
  --amb-warm: rgba(197, 140, 240, .05);
}

/* ---------------------------------------------------------------- header: brand, page sections, tools */
.ov-chrome { top: 0; }
.ov-chrome-in { display: flex; align-items: center; gap: 24px; min-height: 64px; }
.ov-chrome .brand { flex: none; }
.ov-nav { display: flex; gap: 4px; margin-inline: auto; }
.ov-nav a { display: inline-flex; align-items: center; height: 44px; padding: 0 12px; border-radius: 20px; color: var(--ink-2); font: 600 16px/1 var(--text); text-decoration: none; white-space: nowrap;
  transition: background var(--t-state) var(--curve), color var(--t-state) var(--curve); }
.ov-nav a:hover { background: rgba(255, 255, 255, .1); color: var(--ink); }
.ov-nav a:active { background: rgba(0, 0, 0, .35); }
.ov-chrome .tools { margin-left: auto; }
.ov-nav + .tools { margin-left: 0; }
.ov-metalink { height: 36px; padding: 0 12px; font-size: 16px; }
/* the section the visitor is reading: ink text and a 2px cyan bar under the link (cyan marks what is selected); the bar is
   always there and only its opacity changes, so nothing moves */
.ov-nav a, .ov-subnav a { position: relative; }
.ov-nav a::after, .ov-subnav a::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: 4px; height: 2px; border-radius: 4px; background: var(--accent);
  opacity: 0; transition: opacity var(--t-fade) var(--curve); }
.ov-nav a[aria-current], .ov-subnav a[aria-current] { color: var(--ink); }
.ov-nav a[aria-current]::after, .ov-subnav a[aria-current]::after { opacity: 1; }

/* the compact section row under 1100px: 44px, sticky with the header (it is part of it), scrolls sideways when the words
   do not fit, the hidden edge fading as on SPAM Meta's tab row */
.ov-subnav-w { display: none; }
.ov-subnav { display: flex; align-items: center; gap: 4px; height: 44px; margin-inline: -8px; padding-inline: 8px; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain; }
.ov-subnav::-webkit-scrollbar { display: none; }
.ov-subnav a { flex: none; display: inline-flex; align-items: center; height: 36px; padding: 0 12px; border-radius: 20px; color: var(--muted); font: 600 14px/1 var(--text); text-decoration: none; white-space: nowrap;
  transition: background var(--t-state) var(--curve), color var(--t-state) var(--curve); }
.ov-subnav a::after { bottom: 2px; }
.ov-subnav a::before { content: ""; position: absolute; inset: -4px 0; }
.ov-subnav a:hover { background: rgba(255, 255, 255, .1); color: var(--ink); }
.ov-subnav a:active { background: rgba(0, 0, 0, .35); }
.ov-subnav a:focus-visible { outline-offset: -2px; }
.ov-subnav.more-r { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent); }
.ov-subnav.more-l { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 48px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 48px), transparent); }
.ov-subnav.more-l.more-r { -webkit-mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent); mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent); }
@media (max-width: 1099px) {
  .ov-nav { display: none; }
  .ov-nav + .tools { margin-left: auto; }
  .ov-subnav-w { display: block; }
  :root { --chrome-h: 108px; }
  .ov-chrome-in { min-height: 64px; }
}
@media (min-width: 1100px) and (max-width: 1199px) { :root { --chrome-h: 64px; } }
/* on a phone the header keeps the brand and the language; SPAM Meta stays one tap away in its own section and the footer */
@media (max-width: 479px) { .ov-chrome-in { gap: 8px; } .ov-metalink { display: none; } }

/* ---------------------------------------------------------------- sections */
main { display: block; }
/* explicit top and bottom (not padding-block): the static design check reads physical sides only, and the meta band's top
   rule needs its inset seen. Anchors land under the header by html's scroll-padding (base.css, --chrome-h). */
.band { padding-top: 48px; padding-bottom: 48px; }
.band h2 { margin-bottom: 12px; }
.sec-head { display: grid; gap: 12px; margin-bottom: 32px; }
.sec-head h2 { margin: 0; }
.sec-lede { color: var(--ink-2); }
.row-head { grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 12px 24px; }
.row-head > div { display: grid; gap: 12px; }

/* ---------------------------------------------------------------- hero */
/* the hero is its own stacking context with nothing painted behind it: room for the site's shared ambient background
   (site/css/ambient.css, built on another branch) to sit under it at z-index -1 without touching this file's layout */
.hero { position: relative; isolation: isolate; padding-block: 24px 48px; }
.hero-copy { display: grid; gap: 16px; justify-items: start; max-width: 880px; }
.hero h1 { font-size: 40px; }
.hero-lede { font-size: 18px; color: var(--ink-2); }
.hero-act { display: flex; flex-wrap: wrap; gap: 8px 12px; margin-top: 8px; }
.hero-note { color: var(--muted); font: 600 14px/1.4 var(--text); }

/* the stage: one picture, the game window with the overlay on it, as the live overlay (0.1.5) draws it: a quiet layer. One
   sunken frame (the screen) holds the board (two rows of real, full-size game cards, no box of its own) and, on its right edge,
   stand-ins for the game's END TURN disc and timer ring, because the overlay's marks are placed by them: the win chance
   readout centred just above the disc, the end-turn dot left of the timer ring. At rest only the readout and ONE cyan dot
   show (on the suggested spell line); hovering that line moves the dot to the best target, which carries its death chance and
   the damage. The tag and the pause button sit on the frame's top strip. */
.stage { position: relative; margin: 24px 0 0; }
.stage-bar { position: absolute; left: 16px; right: 12px; top: 12px; z-index: 4; display: flex; align-items: center; justify-content: space-between; gap: 8px; height: 44px; pointer-events: none; }
.stage-bar > * { pointer-events: auto; }
.stage-pp .i-play, .stage-pp[aria-pressed="true"] .i-pause { display: none; }
.stage-pp[aria-pressed="true"] .i-play { display: block; }
.stage-pp { flex: none; }
/* the strip sits on the arena: both pieces get the shade the overlay's own chips use, so they read on any part of the plate */
.stage-bar .tag.soft, .stage-bar .stage-pp { background: var(--kit-shade); color: var(--ink-2); }
.stage-bar .stage-pp:hover { background: var(--panel-hi); color: var(--ink); }
.stage-bar .stage-pp:active { background: var(--socket); }
.stage-bar .stage-pp:disabled, .stage-bar .stage-pp:disabled:hover { background: var(--kit-shade); }
.stage-pp .ico { width: 16px; height: 16px; }
.stage-pp:disabled, .stage-pp:disabled:hover { opacity: .5; cursor: default; }
.stage-in { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 216px; align-items: center; border-radius: 12px; border: 1px solid var(--line); box-shadow: var(--sunken);
  background: var(--socket) url("../media/arena-800.webp") center / cover no-repeat; }
@media (min-width: 900px) { .stage-in { background-image: url("../media/arena-1600.webp"); } }
.board { grid-area: 1 / 1; display: grid; gap: 32px; padding: 64px 0 32px 24px; }
.dock { grid-area: 1 / 2; display: grid; justify-content: center; justify-items: start; gap: 8px; padding: 64px 24px 32px 0; opacity: 0; transition: opacity var(--t-fade) var(--curve); }
.stage.ready .dock { opacity: 1; }
/* the data could not load: one line centred on the board's own grid cell (the dock fades out: it would show figures for cards
   that are not there) */
.stage-fail { grid-area: 1 / 1; align-self: center; justify-self: center; position: relative; z-index: 3; max-width: calc(100% - 32px); margin: 0;
  padding: 8px 12px; border-radius: 8px; background: var(--kit-shade); color: var(--ink-2); font: 600 14px/1.4 var(--text); text-align: center; }
.stage.failed .dock { opacity: 0; }

/* the board: their row on top, yours below. Each slot holds a real game card at the client's own ratio (1432 x 2048); the
   frame under it keeps the size while the data loads */
.row { display: grid; grid-template-columns: repeat(4, minmax(0, 152px)); gap: 16px; justify-content: center; }
.slot { position: relative; aspect-ratio: 1432 / 2048; }
.slot > .gc { position: absolute; inset: 0; }
.slot .gc { opacity: 0; transition: opacity var(--t-fade) var(--curve); }
.stage.ready .slot .gc { opacity: 1; }
.slot-frame { position: absolute; inset: 0; border-radius: 8px; background: linear-gradient(180deg, var(--panel-hi), var(--socket)); transition: opacity var(--t-fade) var(--curve); }
.stage.ready .slot-frame { opacity: 0; }

/* the overlay's marks: every one stays in the page and only its opacity changes */
.mk { position: absolute; pointer-events: none; opacity: 0; transition: opacity var(--t-state) var(--curve); }
.mk.on { opacity: 1; }
/* the hovered spell line: its row on the card, lifted one step (placed by stage.js as a share of the card) */
.mk-row { left: var(--rx, 0%); top: var(--ry, 0%); width: var(--rw, 0%); height: var(--rh, 0%); border-radius: 4px; background: rgba(255, 255, 255, .16); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5); }
/* THE dot: the one cyan mark (the action colour), a black ring so it reads on any art */
.mk-dot, .dot-s { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px #000; }
.mk-dot { margin: -7px 0 0 -7px; }
.dot-s { display: inline-block; flex: none; }
.dot-spell { left: var(--rx, 0%); top: calc(var(--ry, 0%) + var(--rh, 0%) / 2); }
.dot-target { left: 50%; top: 22%; }
/* the best target's read-out: the death chance (skull and figure) with the damage range under a down triangle, red */
.mk-out { left: 50%; top: 32%; display: grid; justify-items: center; gap: 4px; transform: translateX(-50%); white-space: nowrap; }
.mk-out .ol-voice { display: inline-flex; align-items: baseline; gap: 8px; }
.mk-out .ol-voice.small { font-size: 16px; }
/* the stand-in HUD: the disc with its label and the ring of the timer; the end-turn dot sits between them, left of the ring.
   The win chance readout is centred over the disc. */
.ol-win { display: flex; justify-content: center; align-items: center; gap: 8px; width: 96px; color: var(--ink); white-space: nowrap; font: 400 24px/1 var(--display); letter-spacing: .01em;
  text-shadow: 2px 0 0 #000, -2px 0 0 #000, 0 2px 0 #000, 0 -2px 0 #000, 2px 2px 0 #000, -2px 2px 0 #000, 2px -2px 0 #000, -2px -2px 0 #000, 0 3px 0 #000; }
.ol-win b { font-weight: 400; }
.ol-win .ico { width: 20px; height: 20px; color: var(--ink); stroke-width: 2.2; filter: drop-shadow(0 1px 0 #000); }
.ol-win { flex-wrap: wrap; row-gap: 8px; }
/* the readout's gauge (overlay 0.1.20): a 4px track under the figure, the win chance filled from the left in the second ink, a
   tick at 50% that stands 2px over and under it, so it reads over the fill and over the empty track alike. Drawn still. */
.ol-gauge { position: relative; flex: 0 0 100%; height: 4px; border-radius: 4px; background: var(--socket); box-shadow: 0 0 0 1px #000; }
.ol-gauge i { position: absolute; inset: 0; border-radius: 4px; background: var(--ink-2); transform-origin: left center; transform: scaleX(0); }
.ol-gauge::after { content: ""; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 2px; margin-left: -1px; border-radius: 4px; background: var(--ink-2); box-shadow: 0 0 0 1px #000; }
/* the feature sample: the figure on its line, the gauge under it */
.sample.s-wr { display: inline-grid; grid-template-columns: auto auto; align-items: center; justify-content: center; gap: 8px; }
.sample.s-wr .ol-gauge { grid-column: 1 / -1; }
.sample.s-wr .ol-gauge i { transform: scaleX(.58); }   /* the sample's figure, ~58 % */
.hud { position: relative; display: flex; align-items: center; gap: 24px; }
.hud-end { display: grid; place-items: center; width: 96px; height: 96px; padding: 0 12px; border-radius: 50%; background: var(--panel); border: 2px solid var(--muted); box-shadow: var(--shadow);
  color: var(--ink); font: 400 16px/1.1 var(--display); letter-spacing: .01em; text-align: center; }
.hud-end { order: 1; }
.hud-timer { order: 2; flex: none; width: 40px; height: 40px; border-radius: 50%; border: 4px solid var(--muted); box-shadow: var(--shadow); }
.dot-end { left: 108px; top: 50%; margin: -7px 0 0 -7px; }

/* the overlay's in-game voice: the display face in ink with a hard dark outline, readable on any part of the board.
   Stacked hard shadows, never a text stroke (DESIGN.md, The No-Outline-On-Crowds Rule). */
.ol-voice { font: 400 18px/1 var(--display); color: var(--ink); letter-spacing: .01em;
  text-shadow: 2px 0 0 #000, -2px 0 0 #000, 0 2px 0 #000, 0 -2px 0 #000, 2px 2px 0 #000, -2px 2px 0 #000, 2px -2px 0 #000, -2px -2px 0 #000, 0 3px 0 #000; }
.ol-voice b { font-weight: 400; font-size: 22px; }
.ol-voice .ico { color: var(--loss); stroke-width: 2.4; filter: drop-shadow(0 1px 0 #000); }
.ol-voice .ico.crown { color: var(--ink); }
/* a death at 50% or more: the skull is solid */
.ico.skull.solid { fill: color-mix(in srgb, currentColor 60%, transparent); }
.ol-voice .lost { color: var(--loss); }
.ol-voice .lost b { font-size: inherit; }

/* the pointer the illustration moves; it only ever moves by transform, on the one curve */
.cursor { position: absolute; left: 0; top: 0; z-index: 3; width: 24px; height: 24px; pointer-events: none; fill: var(--ink); stroke: #000; stroke-width: 1.5; stroke-linejoin: round;
  transform: translate(var(--cx, 0), var(--cy, 0)); transition: transform var(--t-move) var(--curve), opacity var(--t-fade) var(--curve); opacity: 0; }
.stage.ready .cursor { opacity: 1; }

/* ---------------------------------------------------------------- what it shows */
.feats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 48px; }
.fgroup.wide { grid-column: 1 / -1; }
.fgroup h3 { padding-bottom: 12px; }
.flist { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 48px; }
.fgroup:not(.wide) .flist { grid-template-columns: minmax(0, 1fr); }
.feat { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "t s" "p s"; align-content: start; gap: 4px 16px; padding: 16px 0; border-top: 1px solid var(--line); }
.feat-t { grid-area: t; }
.feat-t h4 { font-size: 18px; }
.feat > p { grid-area: p; color: var(--ink-2); }
.sample { grid-area: s; align-self: start; display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 8px 12px; border-radius: 8px; background: var(--socket); box-shadow: var(--sunken); white-space: nowrap; }
.sample.ol-voice { font-size: 16px; }
/* a sample wider than a word (the danger alert): under the name, so the text keeps the row's full measure */
.feat.s-under { grid-template-columns: minmax(0, 1fr); grid-template-areas: "t" "s" "p"; }
.feat.s-under .sample { justify-self: start; margin: 4px 0; }
kbd { display: inline-flex; align-items: center; height: 28px; padding: 0 8px; border-radius: 4px; background: var(--panel-hi); box-shadow: 0 2px 0 var(--socket); color: var(--ink); font: 700 14px/1 var(--text); white-space: nowrap; }

/* the three headline features (damage, death, win change; the shop's rarity odds went with overlay 0.1.26, the game shows them):
   from 760px each is one row, its picture in the left three fifths (larger than the old two-by-two grid gave it), its name, two sentences and what
   the made-up figures say on the right; one column on a phone, the picture first. Every frame takes its picture's own ratio
   (showcase.js sets it), nothing is cropped, and the three frames share one column so they line up whatever the text length. */
.keyf { display: grid; grid-template-columns: minmax(0, 1fr); gap: 32px; align-items: start; }
.kf { align-content: start; gap: 12px; }
.kf.shot { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); grid-template-rows: auto 1fr; gap: 12px 48px; }
.kf.shot > .shot-frame { grid-column: 1; grid-row: 1 / span 2; }
.kf.shot > .kf-text { grid-column: 2; grid-row: 1; margin-top: 0; }
.kf.shot > figcaption { grid-column: 2; grid-row: 2; align-self: start; }
.kf-text { display: grid; gap: 4px; margin-top: 4px; }
.kf-text h3 { font-size: 22px; text-wrap: balance; }
.kf-text p { color: var(--ink-2); max-width: 60ch; text-wrap: pretty; }
.keyf + .feats { margin-top: 48px; }
.shot { margin: 0; display: grid; gap: 8px; }
.shot figcaption { color: var(--ink-2); font: 600 14px/1.4 var(--text); }
.shot figcaption .tag { margin-right: 4px; vertical-align: 1px; }
.shot-frame { position: relative; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; background: var(--socket); box-shadow: var(--sunken); }
.shot-frame img, .shot-frame video { display: block; width: 100%; height: 100%; object-fit: cover; }
/* the picture opens full size: hover and focus draw the one ring (inset, the frame clips), pressed dims a step */
.shot-open { position: absolute; inset: 0; display: block; border-radius: 8px; cursor: zoom-in; }
.shot-open::after { content: ""; position: absolute; inset: 0; border-radius: 8px; box-shadow: inset 0 0 0 2px var(--accent); opacity: 0; transition: opacity var(--t-state) var(--curve); }
.shot-open:hover::after, .shot-open:focus-visible::after { opacity: 1; }
.shot-open:focus-visible { outline: none; }
.shot-open:active img { opacity: .85; }
/* the zoom cue: a 32px socket chip in the frame's bottom-right corner, its glyph brightening with the frame's hover */
.shot-zoom { position: absolute; right: 8px; bottom: 8px; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 8px;
  background: var(--kit-shade); color: var(--ink-2); transition: color var(--t-state) var(--curve); }
.shot-open:hover .shot-zoom, .shot-open:focus-visible .shot-zoom { color: var(--ink); }
/* the picture viewer (showcase.js): a full-screen native modal, transparent itself; a shade (socket at 94%, no blur) fades in
   behind the picture, which arrives with a fade and 24px of travel (280ms) and leaves with a fade (200ms). The picture keeps
   its own ratio inside the screen (24px of air, 72px above for the close control); the caption under it in Soft Lavender. */
.ov-viewer { position: fixed; inset: 0; width: 100vw; height: 100vh; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
  background: transparent; color: var(--ink); overflow: hidden; }
.ov-viewer::backdrop { background: transparent; }
.ovv-shade { position: absolute; inset: 0; background: var(--socket); opacity: 0; transition: opacity var(--t-fade) var(--curve); cursor: zoom-out; }
.ovv-fig { position: absolute; inset: 72px 24px 24px; margin: 0; display: grid; place-content: center; justify-items: center; gap: 12px; cursor: zoom-out;
  opacity: 0; transform: translateY(24px); transition: opacity var(--t-fade) var(--curve), transform var(--t-fade) var(--curve); }
.ovv-img { display: block; width: auto; height: auto; max-width: min(1600px, calc(100vw - 48px)); max-height: calc(100dvh - 140px); border-radius: 8px; box-shadow: var(--shadow); cursor: default; }
.ovv-cap { max-width: 68ch; color: var(--ink-2); font: 600 14px/1.4 var(--text); text-align: center; }
.ovv-cap .tag { margin-right: 8px; vertical-align: 1px; box-shadow: inset 0 0 0 1px var(--edge); }   /* reads as a tag on the shade */
.ovv-close { position: absolute; top: 16px; right: 24px; border: 0; background: var(--kit-shade); opacity: 0; transition: opacity var(--t-fade) var(--curve), background-color var(--t-state) var(--curve), color var(--t-state) var(--curve); }
.ov-viewer.on .ovv-close { opacity: 1; }
.ov-viewer.on .ovv-shade { opacity: .94; }          /* no blur here, so the shade is near opaque: the page stays out of the way */
.ov-viewer.on .ovv-fig { opacity: 1; transform: none; transition-duration: var(--t-move); }
@media (max-width: 759px) { .ovv-fig { inset: 64px 8px 16px; } .ovv-img { max-width: calc(100vw - 16px); } .ovv-close { top: 8px; right: 8px; } }
@media (prefers-reduced-motion: reduce) {
  .ovv-fig, .ov-viewer.on .ovv-fig { transform: none; }
  .ovv-shade, .ovv-fig, .ovv-close { transition-duration: var(--t-state) !important; }
}
/* the empty state while the screenshot is not in yet: the frame keeps its place and says so */
.shot-wait { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 8px; color: var(--muted); font: 600 14px/1.4 var(--text); }
.shot-wait .ico { width: 24px; height: 24px; }

/* ---------------------------------------------------------------- how it works (a real sequence: numbered) */
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px 48px; margin-top: 24px; }
.steps li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 16px; align-items: start; }
.step-n { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--panel); border: 1px solid var(--line); color: var(--ink); font: 400 22px/1 var(--display); }
.steps h3 { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; font-size: 22px; }
.steps p { color: var(--ink-2); }
.steps.small { grid-template-columns: minmax(0, 1fr); gap: 16px; margin: 24px 0; }
.steps.small h3 { font-size: 18px; margin: 12px 0 4px; }
/* the AI behind the dot: two facts under the steps, each a measured figure in the display face (Title 22, under the section's
   28 so the heading leads) over one Soft Lavender line, ruled like the feature rows; two columns from 1024px, a figure beside
   its line under it */
.ai-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 48px; margin: 48px 0 0; }
.ai-facts > div { display: grid; gap: 8px; align-content: start; padding-top: 16px; border-top: 1px solid var(--line); }
.ai-facts dt { font: 400 22px/1 var(--display); letter-spacing: .01em; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ai-facts dd { margin: 0; color: var(--ink-2); max-width: 44ch; text-wrap: pretty; }

/* ---------------------------------------------------------------- privacy: four ruled lines at the reading measure; the first
   (what it reads) carries the eye, the three others (never) the cross */
.priv { margin-top: 24px; max-width: 800px; }
.checks li { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-top: 1px solid var(--line); color: var(--ink); }
.checks li:last-child { border-bottom: 1px solid var(--line); }
.checks .ico { flex: none; margin-top: 4px; color: var(--muted); }
.checks li > span { display: inline; }

/* the coming-soon state of the download: a sunken strip, a clock, two lines; not a control, no hover */
.soon { display: flex; gap: 12px; align-items: flex-start; max-width: 520px; padding: 16px; border-radius: 8px; background: var(--socket); box-shadow: var(--sunken); }
.soon .ico { flex: none; width: 24px; height: 24px; margin-top: 0; color: var(--muted); }
.soon-h { font: 400 18px/1.15 var(--display); letter-spacing: .01em; color: var(--ink); }
.soon-t { margin-top: 4px; color: var(--ink-2); }

/* ---------------------------------------------------------------- the download
   One record (data/overlay-release.json, js/release.js) puts the hero note, the hero button and the install section in one of
   four states, written on body[data-dl]: loading, soon (no public release: a strip and a note, no link anywhere), ready (the
   button with the version and size, then the check-the-file details) or error (the record could not be read: the same strip
   shape, a retry, still no link). Only opacity and the grid rows animate; every panel keeps its own node. */
.get { max-width: 560px; }
.get .soon { max-width: none; }
.get > :not([hidden]) { animation: dl-in var(--t-fade) var(--curve) both; }
.get > .dl-loading { animation: none; }
@keyframes dl-in { from { opacity: 0; } to { opacity: 1; } }
/* loading: the coming-soon strip with its words as still bars (no pulse: nothing moves while it waits), so it swaps without a jump */
.dl-loading .sk { display: block; width: 56%; height: 20px; border-radius: 4px; background: var(--panel); }
.dl-loading .sk2 { width: 100%; height: 44px; margin-top: 8px; background: linear-gradient(var(--panel), var(--panel)) 0 0 / 100% 14px no-repeat, linear-gradient(var(--panel), var(--panel)) 0 24px / 64% 14px no-repeat; }
.dl-error .soon-t { max-width: 44ch; }
.dl-retry { margin: 8px 0 0 -16px; }
/* the hero note: a clock while there is no release, the info glyph when the record could not be read, nothing once it is out;
   it stays mounted (and hidden) while loading so the hero never shifts */
.hero-note .ico { display: none; vertical-align: -3px; margin-right: 8px; }
body[data-dl="soon"] .hero-note .i-soon, body[data-dl="error"] .hero-note .i-err { display: inline-block; }
body[data-dl="loading"] .hero-note { visibility: hidden; }
/* the link follows the note after its word space; when it wraps it starts flush left (no margin to indent it) */
.hero-note .link { font-weight: 700; white-space: nowrap; }
/* the download button: label, then the version and size after a hairline, on the one cyan face */
.dl-btn { gap: 12px; }
.dl-btn .dl-label { display: inline; }
.dl-meta { display: inline-flex; align-items: center; gap: 8px; padding-left: 12px; border-left: 1px solid rgba(13, 10, 26, .3); font: 700 14px/1 var(--text); }
.dl-size { opacity: .8; }
/* ready: a panel holding the button, the facts and the details disclosure */
.dl-card { display: grid; gap: 12px; justify-items: start; }
.dl-card .collapse { justify-self: stretch; }
.dl-act { margin-top: 0; }
.dl-facts { color: var(--ink-2); }
.dl-more { margin-left: -12px; }
.dl-detail { display: grid; gap: 16px; padding-top: 12px; }
.dl-block { display: grid; gap: 8px; justify-items: start; }
.dl-block + .dl-block { padding-top: 16px; border-top: 1px solid var(--line); }
.dl-block h4 { font-size: 18px; }
.dl-block p { color: var(--ink-2); }
.dl-hash { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; width: 100%; }
.dl-hash code, .dl-how code { display: block; padding: 8px 12px; border-radius: 8px; background: var(--socket); box-shadow: var(--sunken); color: var(--ink);
  font: 600 14px/1.5 ui-monospace, "Cascadia Mono", Consolas, monospace; overflow-wrap: anywhere; user-select: all; -webkit-user-select: all; }
.dl-how { width: 100%; display: grid; gap: 8px; }
/* the second package (when the release has both): a text link under the facts, the same weight as the release-page link */
.dl-alt { display: inline-flex; justify-self: start; align-items: center; gap: 8px; font-weight: 700; }
.dl-alt .ico { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.dl-alt-size { color: var(--muted); font-weight: 400; }
/* the check-the-file blocks, one per package: the name, the command, then the fingerprint and its copy button */
.dl-files { display: grid; gap: 16px; width: 100%; }
.dl-file { display: grid; gap: 8px; width: 100%; }
.dl-file + .dl-file { padding-top: 16px; border-top: 1px solid var(--line); }
.dl-file-n { color: var(--ink); font-weight: 700; overflow-wrap: anywhere; }
.dl-notes, .dl-release { font-weight: 700; }
.dl-release { display: inline-flex; align-items: center; gap: 8px; }
.dl-release .ico { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }

/* ---------------------------------------------------------------- install */
.install { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 32px 48px; align-items: start; }
.keys h3 { margin-bottom: 12px; font-size: 22px; }
/* from 760px the list is simply open (the disclosure button is a phone control) */
.keys-d { display: none; }
.keys-c { grid-template-rows: 1fr; visibility: visible; }
.keys-btn { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: calc(100% + 24px); min-height: 44px; margin: -8px -12px; padding: 0 12px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink); font: 400 18px/1.15 var(--display); letter-spacing: .01em; text-align: left; cursor: pointer; transition: background var(--t-state) var(--curve); }
.keys-btn:hover { background: rgba(255, 255, 255, .1); }
.keys-btn:active { background: rgba(0, 0, 0, .35); }
.keys-btn .ico { color: var(--muted); transition: transform var(--t-move) var(--curve); }
.keys-btn[aria-expanded="true"] .ico { transform: rotate(180deg); }
.keys dl { margin: 0; display: grid; }
.keys dl > div { display: grid; grid-template-columns: 168px minmax(0, 1fr); gap: 12px; align-items: center; padding: 8px 0; border-top: 1px solid var(--line); }
.keys dt { display: flex; flex-wrap: wrap; gap: 4px; }
.keys dd { margin: 0; color: var(--ink-2); }

/* ---------------------------------------------------------------- FAQ: one disclosure per question (DESIGN.md "Disclosure") */
.faq-wrap > * { max-width: 800px; }
.faq { margin-top: 16px; }
/* each question is a ruled row with 8px of inset above and below its control, so nothing sits on a rule (the design check
   flagged the last row, whose closed answer lay flush on the bottom rule) */
.faq-item { padding-top: 8px; padding-bottom: 8px; border-top: 1px solid var(--line); }
.faq-more-in .faq-item:last-child { border-bottom: 1px solid var(--line); }
.faq-item h3 { font-size: 18px; }
.faq-more { grid-template-rows: 1fr; visibility: visible; }
.faq-morebtn { display: none; }
.faq-q { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 44px; padding: 8px 12px; margin-inline: -12px; width: calc(100% + 24px); border: 0; border-radius: 8px;
  background: transparent; color: var(--ink); font: 400 18px/1.15 var(--display); letter-spacing: .01em; text-align: left; cursor: pointer;
  transition: background var(--t-state) var(--curve); }
.faq-q:hover { background: rgba(255, 255, 255, .1); }
.faq-q:active { background: rgba(0, 0, 0, .35); }
.faq-q .ico { color: var(--muted); transition: transform var(--t-move) var(--curve); }
.faq-q[aria-expanded="true"] .ico { transform: rotate(180deg); }
.faq-a p { padding: 8px 0 8px; color: var(--ink-2); }
/* a config line quoted in an answer: the hash socket's mono face, inline, on the Panel step (a fact, not a control) */
.ov-code { padding: 0 4px; border-radius: 4px; background: var(--panel); color: var(--ink); font: 600 14px/1.5 ui-monospace, "Cascadia Mono", Consolas, monospace; white-space: nowrap; }

/* ---------------------------------------------------------------- SPAM Meta previews */
.meta-band { border-top: 1px solid var(--line); }
.meta-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); grid-auto-rows: auto; gap: 24px; }
.mp { position: relative; display: grid; gap: 16px; align-content: start; transition: background var(--t-state) var(--curve); }
.mp-decks { grid-row: span 3; }
.mp-t { display: grid; gap: 4px; }
.mp-go { display: inline-flex; align-items: center; gap: 4px; justify-self: start; min-height: 24px; color: var(--accent); font: 400 18px/1 var(--display); text-decoration: none; }
.mp-go .ico { transition: transform var(--t-state) var(--curve); }
/* the whole panel is the link's hit area; hover lightens the panel one step, keyboard focus rings it */
.mp-go::after { content: ""; position: absolute; inset: 0; border-radius: 12px; }
.mp-go:focus-visible { outline: none; }
.mp:has(.mp-go:hover) { background: var(--panel-hi); }
.mp:has(.mp-go:active) { background: var(--socket); }
.mp:has(.mp-go:focus-visible) { box-shadow: 0 0 0 2px var(--night), 0 0 0 4px var(--accent); }
.mp-decks-list { display: grid; }
.mp-deck { display: grid; grid-template-columns: 48px minmax(0, 1fr) 152px; gap: 16px; align-items: center; padding: 16px 0; border-top: 1px solid var(--line); min-height: 128px; }
.mp-deck:first-child { border-top: 0; padding-top: 4px; }
.mp-rank { font: 400 28px/1 var(--display); color: var(--muted); }
/* the deck's likely place: the Deck tier list's quiet "likely #1–9", the last line under its Elo */
.mp-stat .mp-range { white-space: nowrap; }
.mp-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 104px)); gap: 8px; padding: 6px 4px 8px; }
.mp-stat { display: grid; gap: 4px; }
.mp-stat b { font: 400 28px/1 var(--display); font-weight: 400; }
.mp-stat span { color: var(--muted); font: 600 14px/1.35 var(--text); }
.skel-deck { min-height: 128px; }
.skel-deck::before { content: ""; grid-column: 1 / -1; height: 96px; border-radius: 8px; background: var(--socket); box-shadow: var(--sunken); }
.mp-icons { display: grid; gap: 12px; }
.mp-tier { display: grid; grid-template-columns: 40px minmax(0, 1fr); gap: 12px; align-items: center; }
.mp-tier-l { display: grid; place-items: center; height: 40px; border-radius: 8px; color: var(--accent-ink); font: 400 18px/1 var(--display); }
.mp-tier-l.t-s { background: var(--tier-s); }
.mp-tier-l.t-a { background: var(--tier-a); }
.mp-tier-c { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0 4px 4px; }
/* a mini card (cards.css .mc) that is not a control: art and element orb, no hover ring */
.mp-mini { position: relative; display: block; width: 48px; aspect-ratio: 1432 / 2048; border: 2px solid var(--card-edge); border-radius: 4px; background: var(--card-edge); }
/* the rest of a crowded tier: one mini-card-sized socket with the count, not a control (the panel is the link) */
.mp-more { display: grid; place-items: center; width: 48px; aspect-ratio: 1432 / 2048; border-radius: 4px; background: var(--socket); box-shadow: var(--sunken);
  color: var(--ink-2); font: 400 18px/1 var(--display); font-variant-numeric: tabular-nums; }
.mp-mini img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top; border-radius: 4px; display: block; }
.mp-fig { font: 400 40px/1 var(--display); }
.mp-err { margin-top: 16px; }

/* ---------------------------------------------------------------- footer */
.foot .link { color: var(--accent); }
/* the legal line keeps the reading measure */
.ov .foot-legal { max-width: 68ch; }

/* ---------------------------------------------------------------- responsive */
@media (max-width: 1023px) {
  /* the END TURN stand-in moves under the board, still inside the screen */
  .stage-in { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0 16px 16px; }
  .board { padding: 64px 0 32px; }
  .dock { grid-area: 2 / 1; padding: 0 0 16px; }
  .flist { grid-template-columns: minmax(0, 1fr); }
  .steps { grid-template-columns: minmax(0, 1fr); }
  .ai-facts { grid-template-columns: minmax(0, 1fr); margin-top: 32px; }
  .ai-facts > div { grid-template-columns: 128px minmax(0, 1fr); gap: 16px; align-items: baseline; padding: 16px 0; }
  .ai-facts > div:last-child { border-bottom: 1px solid var(--line); }
  .install { grid-template-columns: minmax(0, 1fr); }
  .meta-grid { grid-template-columns: minmax(0, 1fr); }
  .mp-decks { grid-row: auto; }
}
@media (max-width: 759px) {
  /* a tighter rhythm on a phone: 24px bands instead of 48 */
  .band { padding-top: 24px; padding-bottom: 24px; }
  .hero { padding-block: 24px; }
  .hero h1 { font-size: 28px; }
  .hero-lede { font-size: 16px; }
  .stage { margin-top: 24px; }
  .stage-bar { position: static; height: 44px; margin-bottom: 8px; }
  .stage-in { padding: 0 8px 8px; }
  .board { gap: 16px; padding: 16px 0 24px; }
  /* two cards a row on a phone: the stage puts the caster and the target in the second slot of their rows */
  .row { grid-template-columns: repeat(2, minmax(0, 152px)); gap: 8px; }
  .slot:nth-child(n+3) { display: none; }
  .ol-voice { font-size: 14px; }
  .ol-voice b { font-size: 16px; }
  .mk-out .ol-voice.small { font-size: 14px; }
  .feats { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  /* the sample sits beside the name when it fits, else under it: never a block of its own after the text */
  .feat { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 12px; padding: 12px 0; }
  .feat-t { flex: 1 1 auto; }
  .feat > p { flex: 1 0 100%; order: 2; }
  .sample { flex: none; min-height: 36px; padding: 4px 8px; }
  .fgroup h3 { padding-bottom: 8px; }
  .kf.shot { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .kf.shot > .shot-frame, .kf.shot > .kf-text, .kf.shot > figcaption { grid-column: auto; grid-row: auto; }
  .kf.shot > .kf-text { margin-top: 4px; }
  .keyf + .feats { margin-top: 32px; }
  .steps { margin-top: 16px; gap: 16px; }
  .priv { margin-top: 16px; }
  .keys-h { display: none; }
  .keys .keys-d { display: block; margin: 0; }
  .keys-c { grid-template-rows: 0fr; visibility: hidden; }
  .keys-c.open { grid-template-rows: 1fr; visibility: visible; }
  .keys-c dl { padding-top: 12px; }
  .faq-more { grid-template-rows: 0fr; visibility: hidden; }
  .faq-more.open { grid-template-rows: 1fr; visibility: visible; }
  .faq-morebtn { display: inline-flex; margin: 8px 0 0 -16px; }
  .soon { padding: 12px; }
  .dl-hash { grid-template-columns: minmax(0, 1fr); }
  .dl-hash .copy { justify-self: start; }
  .keys h3 { font-size: 18px; }
  .ai-facts { margin-top: 24px; }
  .ai-facts > div { grid-template-columns: 104px minmax(0, 1fr); gap: 12px; padding: 12px 0; }
  .ai-facts dt { font-size: 18px; }
  /* the step titles under the section heading: Control 18, as the install steps (the 22px h2 leads) */
  .steps h3 { font-size: 18px; }
  .row-head { grid-template-columns: minmax(0, 1fr); }
  .row-head .btn { justify-self: start; }
  .keys dl > div { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .mp-deck { grid-template-columns: 40px minmax(0, 1fr); }
  .mp-stat { grid-column: 2; }
  .mp-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* a deck on a phone: its four cards two by two, as SPAM Meta draws them (DESIGN.md, Layout) */
@media (max-width: 479px) {
  /* the download button on a phone: full width, the version and size on a line of their own under the label */
  .dl-btn { display: grid; grid-template-columns: auto auto; justify-content: center; align-items: center; column-gap: 8px; row-gap: 4px; width: 100%; height: auto; min-height: 56px; padding: 8px 20px; }
  .dl-meta { grid-column: 1 / -1; justify-self: center; padding-left: 0; border-left: 0; }
  .mp-cards { grid-template-columns: repeat(2, minmax(0, 120px)); }
  /* two decks are enough of a preview on a phone; the third is one tap away on SPAM Meta */
  .mp-deck:nth-child(3) { display: none; }
}
@media (max-width: 419px) {
  .mp-deck { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .mp-stat { grid-column: auto; }
}

/* the kill switch: no travel, no stagger, no sliding selection (base.css shortens every duration as well) */
@media (prefers-reduced-motion: reduce) {
  .cursor { transition: none; }
  .get > :not([hidden]) { animation: none; }
}
