/* ============================================================================
   CRIT HIT — SITE LAYOUT
   ----------------------------------------------------------------------------
   Page-level composition only. Components live in ui.css; tokens in tokens.css.
   Nothing here should be reusable — if it is, it belongs in the kit.

   Spec: docs/design-system.md
   ========================================================================= */

.skip-link {
  position: absolute; left: var(--s-4); top: -60px; z-index: 999;
  padding: var(--s-3) var(--s-4);
  background: var(--amber); color: var(--action-ink);
  border-radius: var(--r-sm); font-weight: 700; text-decoration: none;
  transition: top var(--t-quick) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

/* ==========================================================================
   HERO — "the lit room"
   --------------------------------------------------------------------------
   Not a bordered box with a logo in it. A dark room with one light source:
   a radial bloom behind the sign, a silhouetted castle in the mid-ground,
   and the copy sitting on the light.
   ========================================================================== */

.hero {
  position: relative; isolation: isolate; overflow: hidden;
  min-height: min(88vh, 820px);
  display: grid; align-items: center;
  padding-block: var(--s-9) var(--s-10);
}

/* The room itself: a still that always loads, with the ambient loop layered
   over it only when motion is allowed (see app.js). Both are pushed well down
   in brightness so the wordmark stays the brightest thing on the page. */
.hero__still,
.hero__media video {
  position: absolute; inset: 0; z-index: -3;
  width: 100%; height: 100%; object-fit: cover; object-position: 62% 50%;
  opacity: .5; filter: saturate(.85) brightness(.62);
  pointer-events: none;
}
.hero__media video { opacity: 0; transition: opacity 900ms var(--ease); }
.hero__media video.is-ready { opacity: .5; }
/* Keep the copy side readable regardless of what the footage does. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background: linear-gradient(90deg, var(--bg) 8%, rgba(6,5,12,.72) 42%, rgba(6,5,12,.15) 78%);
}

/* The light the sign throws. Two overlapping pools — amber from the inner
   tube, cobalt from the outer — which is why the logo sits in the page
   instead of on top of it. */
.hero__light {
  position: absolute; inset: -20% -10% 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(46% 42% at 50% 34%, rgba(255,165,31,.20), transparent 68%),
    radial-gradient(64% 56% at 50% 44%, rgba(67,97,255,.16), transparent 72%),
    radial-gradient(90% 70% at 50% 62%, rgba(255,46,136,.06), transparent 78%);
}
/* Floor grid, fading out — gives the "room" a ground plane without a border. */
.hero__floor {
  position: absolute; inset: auto 0 0; height: 46%; z-index: -2; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(67,97,255,.10) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(67,97,255,.10) 1px, transparent 1px);
  background-size: 68px 68px;
  transform: perspective(320px) rotateX(62deg);
  transform-origin: bottom center;
  mask-image: linear-gradient(to top, #000 0%, transparent 85%);
}
.hero__castle {
  position: absolute; right: clamp(-80px, -4vw, 0px); bottom: -2%;
  width: clamp(320px, 42vw, 640px); z-index: -1;
  opacity: .28; filter: grayscale(.35) brightness(.55) contrast(1.15);
  pointer-events: none;
  /* Short enough to feel attached to the pointer, long enough to smooth the
     jitter between move events. --t-slow here reads as lag. */
  transition: transform 220ms var(--ease-out);
  will-change: transform;
}

/* The logo is the display type — pairing it with a second oversized headline
   made the hero 1030px tall and pushed the primary action under the fold. */
.hero__inner { position: relative; z-index: 1; display: grid; gap: var(--s-4); justify-items: start; }
.hero__logo {
  width: clamp(240px, 30vw, 420px); height: auto;
  margin-block: var(--s-1) var(--s-2);
  filter: drop-shadow(0 0 50px rgba(255,165,31,.28)) drop-shadow(0 0 110px rgba(67,97,255,.22));
}
.hero__copy { max-width: 46ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-3); }
.hero__stats { display: flex; flex-wrap: wrap; gap: var(--s-5); margin-top: var(--s-4); }
.hero__stat { display: flex; flex-direction: column; gap: 4px; }
.hero__stat b { font-family: var(--font-pixel); font-size: var(--fs-pixel-md); color: var(--cyan); letter-spacing: var(--tr-pixel); }
.hero__stat span { font-size: var(--fs-xs); color: var(--fg-dim); }

@media (max-width: 800px) {
  .hero { min-height: auto; padding-block: var(--s-8); }
  .hero__castle { opacity: .16; right: -20%; }
}

/* ==========================================================================
   CABINET
   ========================================================================== */

/* The screen takes everything the deck does not need. The deck is a fixed
   ~340px panel rather than a proportional column, so widening the viewport
   grows the game rather than the controls list. */
.cabinet__grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--grid-gap); align-items: start; }
/* Stack early: below this the two-column split costs the screen more width than
   the side-by-side layout is worth. */
@media (max-width: 1500px) { .cabinet__grid { grid-template-columns: 1fr; } }

/* Stacked, the deck becomes a wide strip under the screen — which is where an
   arcade cabinet keeps its controls anyway — and the screen gets the full
   container width instead of surrendering 364px to a sidebar. */
@media (max-width: 1500px) {
  .cabinet__grid > aside .ch-panel__body { display: grid; grid-template-columns: 200px 1fr; gap: var(--s-5); align-items: start; }
  .cabinet__grid > aside .deck__art { grid-row: span 4; }
  .cabinet__grid > aside .ch-keys { grid-column: 2; }
  .cabinet__grid > aside .deck__notes,
  .cabinet__grid > aside .deck__status,
  .cabinet__grid > aside .ch-row { grid-column: 2; }
}
@media (max-width: 700px) {
  .cabinet__grid > aside .ch-panel__body { grid-template-columns: 1fr; }
  .cabinet__grid > aside .deck__art { grid-row: auto; }
  .cabinet__grid > aside .ch-keys,
  .cabinet__grid > aside .deck__notes,
  .cabinet__grid > aside .deck__status,
  .cabinet__grid > aside .ch-row { grid-column: 1; }
}

.cabinet__toolbar {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--void-700);
}
.cabinet__now { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.cabinet__title { font-weight: 700; font-size: var(--fs-sm); color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Attract mode: a rotating real capture, not a static backdrop. */
.attract { position: absolute; inset: 0; z-index: 1; overflow: hidden; }
.attract__shot {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 900ms var(--ease);
  filter: saturate(.9) brightness(.72);
}
.attract__shot.is-on { opacity: 1; }
.attract__veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6,5,12,.55), rgba(6,5,12,.9));
}
.attract__body {
  position: absolute; inset: auto 0 0; z-index: 2;
  display: grid; gap: var(--s-3); justify-items: center;
  padding: var(--s-6) var(--s-5) var(--s-7);
  text-align: center;
}
.attract__bios { position: absolute; top: var(--s-5); left: var(--s-5); z-index: 3; }
.attract__title { font-family: var(--font-pixel); font-size: var(--fs-pixel-lg); letter-spacing: var(--tr-pixel); color: var(--fg); }
.attract__ticker { position: absolute; inset: auto 0 0; z-index: 3; overflow: hidden; border-top: 1px solid rgba(255,255,255,.07); background: rgba(6,5,12,.6); }
.attract__track {
  display: flex; gap: var(--s-6); white-space: nowrap; padding: var(--s-2) 0;
  font-family: var(--font-pixel); font-size: 10px; letter-spacing: var(--tr-pixel); color: var(--amber);
  animation: ticker 26s linear infinite;
}
@keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .attract__track { animation: none; } }

.crt__overlay {
  position: absolute; inset: 0; z-index: 4;
  display: grid; place-content: center; gap: var(--s-4); justify-items: center;
  padding: var(--s-6); text-align: center;
  background: rgba(6,5,12,.86); backdrop-filter: blur(4px);
}
.crt__overlay .ch-progress { width: min(280px, 60%); }

/* Control deck */
.deck__art { border-radius: var(--r-sm); overflow: hidden; border: 1px solid var(--line); }
.deck__art img { width: 100%; height: 132px; object-fit: cover; object-position: center 42%; }
.deck__game { font-weight: 700; color: var(--fg); }
.deck__notes { display: grid; gap: var(--s-2); list-style: none; padding: 0; margin: 0; }
.deck__notes li { position: relative; padding-left: var(--s-4); font-size: var(--fs-sm); color: var(--fg-muted); }
.deck__notes li::before { content: "›"; position: absolute; left: 0; color: var(--amber); font-weight: 700; }
.deck__status { font-size: var(--fs-xs); color: var(--fg-dim); min-height: 1.2em; }
.deck__status.is-error { color: var(--danger); }

/* ==========================================================================
   LIBRARY
   ========================================================================== */

.library__head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-5); flex-wrap: wrap; margin-bottom: var(--s-6); }
.library__grid { list-style: none; padding: 0; margin: 0; }
.library__grid > li { display: flex; }
.library__grid > li > * { flex: 1; }
.library__empty { padding: var(--s-8); text-align: center; color: var(--fg-dim); }

/* Fallback cover when a capture is missing: the accent as a lit gradient plus
   the pixel icon, so a newly listed game still looks deliberate. */
.cover-fallback {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  background:
    radial-gradient(70% 70% at 50% 30%, color-mix(in srgb, var(--accent) 34%, transparent), transparent 70%),
    linear-gradient(160deg, var(--void-850), var(--void-1000));
}
.cover-fallback canvas { width: 64px; height: 64px; image-rendering: pixelated; opacity: .9; }

/* Continue rail */
.continue { margin-bottom: var(--s-7); }

/* ==========================================================================
   TUBE RULE — a photographed neon tube as a section divider
   --------------------------------------------------------------------------
   Only the physical glass is in the file; the halo could not be recovered
   cleanly from the source's baked checkerboard, so the bloom is a drop-shadow.
   That is also a quarter of the bytes.
   ========================================================================== */

.tube-rule { display: flex; justify-content: center; padding-block: var(--s-6) 0; }
.tube-rule img {
  width: min(100%, 900px); height: 10px; object-fit: fill;
  opacity: .9;
  filter: drop-shadow(0 0 10px var(--amber-bloom)) drop-shadow(0 0 34px var(--amber-bloom));
}

/* ==========================================================================
   MARQUEE BAND — the banner asset, integrated instead of pasted
   ========================================================================== */

.band { position: relative; isolation: isolate; overflow: hidden; }
.band__img {
  width: 100%; max-height: 340px; object-fit: cover; object-position: center;
  /* the wash that makes its magenta agree with the page ground */
  filter: saturate(1.05) brightness(.82);
}
/* Fades top and bottom so it emerges from the page rather than butting into it.
   Keep these shallow — at 42% each they ate 84% of the band and the artwork
   read as an empty dark strip. */
.band::before, .band::after {
  content: ""; position: absolute; left: 0; right: 0; height: 20%; z-index: 1; pointer-events: none;
}
.band::before { top: 0;    background: linear-gradient(180deg, var(--bg), transparent); }
.band::after  { bottom: 0; background: linear-gradient(0deg,   var(--bg), transparent); }
/* A left-side scrim instead, so the overlaid headline stays legible without
   dimming the whole sign. */
.band__copy::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(90deg, rgba(6,5,12,.88) 0%, rgba(6,5,12,.55) 38%, transparent 62%);
}
.band__copy {
  position: absolute; inset: 0; z-index: 2; isolation: isolate;
  display: grid; align-content: center; gap: var(--s-3);
}
@media (max-width: 700px) { .band__img { max-height: 220px; } .band__copy { background: rgba(6,5,12,.5); } }

/* ==========================================================================
   VAULT — small, honest, not a third of the page
   ========================================================================== */

.vault__list { display: flex; flex-wrap: wrap; gap: var(--s-3); list-style: none; padding: 0; margin: 0; }
.vault__item {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--surface); border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
}
/* The rebuilds have key art but do not run yet. Dimmed and desaturated it reads
   as "coming", which is the honest signal; full-strength art would advertise a
   game that opens a broken screen. */
.vault__art {
  position: absolute; inset: 0; z-index: -1;
  width: 100%; height: 100%; object-fit: cover;
  opacity: .3; filter: grayscale(.45) brightness(.72);
}
.vault__item.has-art::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(90deg, var(--surface) 6%, rgba(16,12,28,.62) 100%);
}
.vault__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent, var(--fg-dim)); opacity: .7; flex: none; }
.vault__name { font-weight: 700; font-size: var(--fs-sm); color: var(--fg-muted); }
.vault__note { font-size: var(--fs-xs); color: var(--fg-dim); }

/* ==========================================================================
   FOOTER
   ========================================================================== */

.footer { border-top: 1px solid var(--line); background: var(--bg-alt); padding-block: var(--s-8) var(--s-6); }
.footer__grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--grid-gap); }
.footer__col h3 { font-family: var(--font-pixel); font-size: var(--fs-pixel-sm); letter-spacing: var(--tr-eyebrow); color: var(--fg-dim); text-transform: uppercase; margin-bottom: var(--s-4); }
.footer__col ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-2); }
.footer__col a { color: var(--fg-muted); text-decoration: none; font-size: var(--fs-sm); }
.footer__col a:hover { color: var(--fg); text-decoration: underline; }
.footer__brand { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.footer__fine { margin-top: var(--s-7); padding-top: var(--s-5); border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--fg-dim); }
@media (max-width: 800px) { .footer__grid { grid-template-columns: 1fr; gap: var(--s-6); } }
