/* Unbounded — the brand face. One VARIABLE file covering 200-900 rather than
   eight static cuts: fewer requests, and weight becomes a continuous axis the
   design can interpolate instead of four fixed steps.

   `font-display: swap` matters more than usual here — this is a 777KB TTF (no
   fontTools on hand to build a woff2, which would cut it by roughly two
   thirds), so the page must render in the fallback rather than block on it. */
@font-face {
  font-family: 'Unbounded';
  src: url('../assets/fonts/Unbounded-Variable.ttf') format('truetype-variations');
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #030304;
  --bg-alt: #08090a;
  --panel: #0a0b0c;
  --border: #1c2b2e;
  --border-strong: #2f4a4f;

  --blue: #00e5ff;
  --blue-dim: #007a8a;
  --red: #ff2851;
  --red-dim: #8a0021;

  --text: #c9f2ff;
  --text-dim: #5f7a80;

  --font-mono: 'IBM Plex Mono', ui-monospace, 'Courier New', monospace;
  --font-brand: 'Unbounded', 'Space Grotesk', system-ui, sans-serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-brand);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
}

a { color: inherit; }

/* ---------- CRT overlay ---------- */
.crt-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 500;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 229, 255, 0.03) 0px,
    rgba(0, 229, 255, 0.03) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: screen;
}
.crt-overlay::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 140px rgba(0, 0, 0, 0.85);
}

/* ---------- header ---------- */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 32px;
  border-bottom: 1px solid transparent;
  transition: padding 0.3s ease, background 0.3s ease, border-color 0.3s ease;
}

.site-header.scrolled {
  padding: 12px 32px;
  background: rgba(3, 3, 4, 0.9);
  border-bottom: 1px solid var(--border);
}

.logo {
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.02em;
  text-decoration: none;
  color: var(--text-dim);
}
.logo span { color: var(--blue); }

.site-nav { display: flex; gap: 26px; }
.site-nav a {
  text-decoration: none;
  font-size: 14px;
  color: var(--text-dim);
  transition: color 0.2s ease;
}
.site-nav a:hover { color: var(--red); }

/* ---------- hero ---------- */
.hero {
  position: relative;
  /* 100vh is the LARGE viewport on mobile — the height with the URL bar hidden
     — so anything anchored to the hero's bottom starts below the fold and only
     appears after a scroll. svh is the small viewport: the height actually on
     screen at load, which is where "learn more" and the attribution belong.
     The vh line stays first as the fallback for browsers without svh. */
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  text-align: left;
  background: var(--bg);
  /* Centres the wordmark WITHOUT a transform on it. That matters: the lockup
     blends with `mix-blend-mode: screen`, and an element only blends with the
     backdrop inside its nearest stacking context. Centring the h1 with
     translate(-50%,-50%) made the h1 itself a stacking context, so the video
     screened against an empty box instead of the page — and the knocked-out
     background came back as a visible rectangle. Every other child here is
     absolutely positioned, so they sit out of this grid untouched. */
  display: grid;
  place-items: center;
}

.filter-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* THE HERO BACKGROUND. Was a CRT screen: a looping loader video, barrel-warped
   by an SVG displacement map and masked to a bowed rectangle, sitting dead
   centre. It is gone — the centre is where the wordmark goes now, and a video
   competing with it there was the reason the wordmark had to live in a corner.

   What replaces it is quieter and cheaper: diamonds drifting slowly upward.
   The diamond is already the system's own mark for a committed state in the
   HUD, so the background is made of the product's vocabulary rather than
   decoration borrowed from nowhere. */
.hero-field {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;   /* never steals the wordmark's hover */
}

.hero-diamonds {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* Generated in main.js: count, position, size, drift, opacity and duration are
   randomised per diamond so the field never visibly repeats. Everything below
   reads those as custom properties. */
.diamond {
  position: absolute;
  left: var(--x, 50vw);
  top: 100%;
  width: var(--s, 14px);
  height: var(--s, 14px);
  border: 1px solid var(--blue);
  opacity: 0;
  animation: diamond-drift var(--dur, 52s) linear var(--delay, 0s) infinite;
}
/* A few are filled, so the field has some weight in it and does not read as a
   single repeated outline. */
.diamond.is-solid {
  background: var(--blue);
  border-color: transparent;
}

/* Up and slightly across, turning once over the whole climb. The turn is what
   makes it read as floating rather than scrolling. */
@keyframes diamond-drift {
  0%   { transform: translate3d(0, 0, 0) rotate(45deg);  opacity: 0; }
  12%  { opacity: var(--o, 0.2); }
  88%  { opacity: var(--o, 0.2); }
  100% { transform: translate3d(var(--dx, 0), -120vh, 0) rotate(405deg); opacity: 0; }
}

/* Motion is the whole effect, so when it is unwelcome the field becomes a
   still constellation rather than disappearing. */
@media (prefers-reduced-motion: reduce) {
  .diamond {
    animation: none;
    top: var(--y, 50vh);
    opacity: var(--o, 0.2);
    transform: rotate(45deg);
  }
}

.hero-scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.34) 0px,
    rgba(0, 0, 0, 0.34) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: 0.6;
}

/* Kept, but re-scoped: these used to sit inside the CRT mask and sell the
   curvature of a screen. They now grade the whole hero, which keeps the
   established texture without the screen that justified it. */
.hero-glare {
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 220px rgba(0, 0, 0, 0.92);
  background: radial-gradient(120% 90% at 50% 42%, rgba(0, 229, 255, 0.055), transparent 62%);
}

/* ONE BASELINE across the foot of the hero. The entry point and the
   attribution used to be two separately positioned corners that happened to be
   near the same height; they now share a row, so they are aligned by
   construction rather than by coincidence. */
.hero-baseline {
  position: absolute;
  left: clamp(20px, 4vw, 64px);
  right: clamp(20px, 4vw, 64px);
  bottom: calc(clamp(24px, 5vh, 64px) + env(safe-area-inset-bottom, 0px));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.hero-forged { margin: 0; }

.logo-placeholder {
  margin: 0 0 22px;
  padding: 10px 18px;
  border: 1px dashed var(--border-strong);
  color: var(--text-dim);
}
.logo-placeholder-text {
  font-size: 12px;
  letter-spacing: 0.06em;
}

/* THE WORDMARK. It was type — `magi` at clamp(3rem, 10vw, 7rem) in --blue —
   sitting bottom-left because the CRT screen owned the centre. With the screen
   gone it takes the centre, and it is the liquid-metal lockup rather than set
   type: the mark and the letterforms arrive together as one artwork. */
/* The centred stack. `relative` with NO z-index: positioned, so it paints
   above the absolutely positioned field that precedes it in the DOM, but
   z-index:auto means it creates no stacking context and the video inside can
   still reach the page to blend against. A z-index here silently breaks the
   background knockout — see .hero-mark. */
.hero-stack {
  position: relative;
  width: min(74vw, 940px);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.hero-title {
  margin: 0;
  width: 100%;
  line-height: 0;          /* a video, not a line of text */
}

/* KEYING THE BACKGROUND OUT IN THE BROWSER.

   The source is H.264, which carries no alpha, and it was rendered as a DARK
   logo on a LIGHT GREY field — the inverse of this site. With no ffmpeg on
   hand to key it into a WebM with alpha, the knockout happens here, in three
   steps that have to run in this order:

     invert(1)     light grey background -> near-black,  dark logo -> bright
     contrast(...) crushes what is left of that background to TRUE black
     screen        true black contributes nothing to what it is over, so the
                   background disappears and only the metal survives

   hue-rotate(180deg) is the fourth step and it is not optional: invert() flips
   HUE as well as lightness, so without it the cool metal came back sepia and
   olive. Rotating a half-turn puts the hues back where they were while the
   lightness stays flipped — the logo goes light, the colour does not move.

   Contrast is the tuned number. Too little and the background stays as a
   faintly visible rectangle over the diamonds; too much and the metal's own
   mid-tones blow out, which is the whole thing worth looking at. */
.hero-mark {
  display: block;
  width: 100%;
  height: auto;
  transform-origin: 50% 50%;
  /* Order is load-bearing. brightness() runs AFTER contrast() on purpose:
     contrast has already crushed the background to true black, and anything
     multiplied by zero stays zero, so lifting the metal cannot lift the
     background back into view. Put brightness first and the knockout breaks. */
  /* Touch runs this same chain in pixels instead — see heroMark() in main.js.
     Change one and change the other, or the phone drifts off colour again. */
  filter: invert(1) hue-rotate(180deg) contrast(1.42) brightness(1.22) saturate(1.12);
  mix-blend-mode: screen;
  transition: transform 420ms cubic-bezier(0.2, 0.7, 0.2, 1);
  will-change: transform;
}
/* Enlarge on hover. Scale rather than width, so it costs no layout and cannot
   reflow the page behind it. The drop-shadow that used to sit here is gone:
   under `screen` it would be composited as light, not as a shadow. */
.hero-title:hover .hero-mark,
.hero-title:focus-visible .hero-mark {
  transform: scale(1.075);
}

/* An accessible name for the lockup, since it is now a decorative video. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
  .hero-mark { transition: none; }
}

.forged-by {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 14px;
  margin: 0;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 500;
  /* Set in caps rather than typed in caps, so the copy stays sentence case in
     the markup and the styling stays reversible. */
  text-transform: uppercase;
  letter-spacing: 0.22em;
}
.sz-logo {
  height: 84px;
  width: auto;
  display: block;
}

.hero-sub .hl {
  color: var(--blue);
  font-weight: 600;
  background: rgba(0, 229, 255, 0.12);
  padding: 1px 6px;
  text-shadow: 0 0 12px rgba(0, 229, 255, 0.45);
}

/* Centred on the lockup and hard against it. It reads as the lockup's caption
   now rather than as copy in a corner, so it is measured from the mark rather
   than from the viewport edge. */
.hero-sub {
  max-width: 62ch;
  margin: 6px auto 0;
  color: var(--text-dim);
  font-size: clamp(0.82rem, 1.05vw, 1rem);
  font-weight: 300;
  letter-spacing: 0.01em;
  text-align: center;
  /* Reserves the line box before typing starts, so the baseline below does not
     jump when the text arrives. */
  min-height: 2.6em;
}
/* Typed by main.js. Until it runs the text is present for anyone without JS
   and for the accessibility tree; `is-typing` only hides what has not been
   emitted yet. */
.hero-sub.is-typing .cursor { opacity: 1; }
.cursor {
  display: inline-block;
  color: var(--red);
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* THE ENTRY POINT. Was a bracketed `[ scroll_to_begin ]` rectangle: it named
   the mechanism (scrolling) rather than the offer, and a rectangle among a
   hero built of diamonds and a round mark read as borrowed chrome.

   A ring instead — it echoes the pentacle in the lockup, it survives being
   small, and it sits on the baseline as a single mark rather than a slab. The
   copy names the act, not the input device. */
.cta-dot {
  flex: none;
  width: 94px;
  height: 94px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 2px;
  text-decoration: none;
  color: var(--blue);
  background: transparent;
  transition: background 0.25s ease, color 0.25s ease,
              box-shadow 0.25s ease, border-color 0.25s ease;
}
.cta-dot-label {
  font-size: 10px;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: 0.16em;
  text-indent: 0.16em;     /* balance the trailing letter-space */
  text-align: center;
}
.cta-dot-arrow {
  font-size: 15px;
  line-height: 1;
  transition: transform 0.25s ease;
}
.cta-dot:hover,
.cta-dot:focus-visible {
  color: var(--bg);
  background: var(--blue);
  border-color: var(--blue);
  box-shadow: 0 0 28px rgba(0, 229, 255, 0.45);
}
.cta-dot:hover .cta-dot-arrow { transform: translateY(3px); }

/* ---------- deck section: video + sticky card stack ---------- */
.deck-section {
  position: relative;
  display: flex;
}

.video-bg-wrap {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 56%;
  flex-shrink: 0;
  overflow: visible;
  z-index: 0;
  background: var(--bg-alt);
  border-right: 1px solid var(--border);
}

.bg-video {
  position: absolute;
  inset: 0;
  clip-path: inset(0);
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  border: none;
  background: none;
  box-shadow: none;
  z-index: 2;
  mix-blend-mode: screen;
}

.video-placeholder {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: auto;
  width: 100vw;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  pointer-events: none;
}

.ascii-static {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  color: var(--blue-dim);
  font-size: 9px;
  line-height: 10px;
  letter-spacing: 0;
  white-space: pre;
  overflow: hidden;
  opacity: 0.65;
}

.signal-box {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 22px 32px;
  background: rgba(3, 3, 4, 0.75);
  border: 1px solid var(--border-strong);
}

.placeholder-label {
  font-size: 1.1rem;
  letter-spacing: 0.06em;
  color: var(--red);
  text-shadow: 0 0 12px var(--red-dim);
  animation: flicker 3.2s infinite;
}
.placeholder-sub {
  font-size: 11px;
  color: var(--text-dim);
}
@keyframes flicker {
  0%, 92%, 100% { opacity: 1; }
  93% { opacity: 0.3; }
  95% { opacity: 0.9; }
  96% { opacity: 0.2; }
  97% { opacity: 1; }
}

.cards-col {
  position: relative;
  z-index: 2;
  width: 44%;
  flex-shrink: 0;
  background: var(--bg);
}

/* One gap between every card, at every width. The slots used to be a screen
   tall each, which is what held a card pinned under the header while that
   screen scrolled by — but that hold IS the empty space between the cards, and
   it ran as wide as 486px on one pair and 174px on the next, since the cards
   are nowhere near the same height. A slot now hugs its card, so the deck
   scrolls as one column with the same 28px between every pair. */
.card-slot {
  min-height: auto;
  display: flex;
  align-items: center;
  padding: 14px 6vw 14px 4vw;
}

.card-slot:first-child {
  align-items: flex-start;
  padding-top: 24px;
}

.card-slot:last-child {
  min-height: auto;
  padding-bottom: 0;
}

/* guarantees the clearance above the dojos section in every layout mode */
.card-slot:last-child .deck-card { margin-bottom: 0; }
/* and never less than 10px once the deck stacks and the slots lose their
   generous desktop padding */
/* the same 28px again, this time as the clearance to the demo section below */
.deck-section { padding-bottom: 28px; }

.deck-card {
  position: sticky;
  top: 56px;
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--border-strong);
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.5s ease, transform 0.5s ease, border-color 0.3s ease;
  clip-path: polygon(
    14px 0, calc(100% - 14px) 0,
    calc(100% - 14px) 14px, 100% 14px,
    100% calc(100% - 14px), calc(100% - 14px) calc(100% - 14px),
    calc(100% - 14px) 100%, 14px 100%,
    14px calc(100% - 14px), 0 calc(100% - 14px),
    0 14px, 14px 14px
  );
}

/* double border: inner frame inset from the card's own border, matching notch corners */
.deck-card::before {
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px solid var(--border);
  pointer-events: none;
  z-index: 2;
  clip-path: polygon(
    10px 0, calc(100% - 10px) 0,
    calc(100% - 10px) 10px, 100% 10px,
    100% calc(100% - 10px), calc(100% - 10px) calc(100% - 10px),
    calc(100% - 10px) 100%, 10px 100%,
    10px calc(100% - 10px), 0 calc(100% - 10px),
    0 10px, 10px 10px
  );
}

.deck-card.in-view {
  opacity: 1;
  transform: translateY(0);
}

.deck-card:hover { border-color: var(--blue-dim); }

/* ── CARD HEAD ──────────────────────────────────────────────────────────────
   Was a macOS window bar: three traffic lights and `02_problem.txt`. Two things
   were wrong with it and neither was cosmetic.

   The traffic lights are another operating system's chrome. They say "this is
   a window on a Mac", which is a claim about the frame, not about the product —
   and the product is a heads-up display, not a text editor. The `.txt` filename
   made the same mistake twice: it framed the content as a file being read
   rather than as a panel being watched.

   What replaces it is the software's own vocabulary, lifted from the HUD:
   an index number in a keyed box, the section name set in the brand face, a
   dashed rail carrying the eye across, and the diamond that marks a committed
   state. The card's notched corners were already right; only the head was
   borrowed.

   It sits INSIDE the card's inner frame — margin clears the ::before inset —
   so the title reads as part of the instrument rather than as a lid on it. */
.card-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(10px, 1.2vw, 18px);
  margin: 7px 7px 0;      /* clears the inset frame drawn by .deck-card::before */
  padding: clamp(9px, 1.6vh, 15px) clamp(10px, 1vw, 16px);
  border-bottom: 1px solid var(--border);
  background: linear-gradient(90deg, rgba(0, 229, 255, 0.05), transparent 62%);
}

/* The accent bar. Short, hard, and hung off the head's own bottom rule — the
   one piece of pure signal colour on the card. */
.card-head::after {
  content: '';
  position: absolute;
  left: clamp(10px, 1vw, 16px);
  bottom: -1px;
  width: 44px;
  height: 2px;
  background: var(--blue);
}
.card-head.accent-red::after { background: var(--red); }
.card-head.accent-red { background: linear-gradient(90deg, rgba(255, 40, 81, 0.05), transparent 62%); }

.card-idx {
  flex: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--text-dim);
  border: 1px solid var(--border-strong);
  padding: 3px 7px;
}

.card-name {
  margin: 0;
  font-size: clamp(0.95rem, 2.1vh, 1.4rem);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-indent: 0.2em;      /* balance the trailing letter-space */
  color: var(--text);
  white-space: nowrap;
}

/* Texture, never data — a dashed rail carrying the eye to the marks. */
.card-rail {
  flex: 1;
  min-width: 12px;
  height: 1px;
  background: repeating-linear-gradient(to right,
    var(--border-strong) 0 6px, transparent 6px 13px);
}

/* The diamond is the HUD's marker for a committed state. Reusing it here is
   the whole point of the change: the site and the software mark things the
   same way. */
.card-marks {
  flex: none;
  display: flex;
  gap: 6px;
}
.card-marks i {
  width: 7px;
  height: 7px;
  transform: rotate(45deg);
  background: var(--border-strong);
}
.card-marks i:nth-child(2) { background: var(--red); }
.card-marks i:nth-child(3) { background: var(--blue); }

.term-body { padding: clamp(12px, 3vh, 32px) clamp(16px, 2vw, 30px); }

.card-tag {
  display: inline-block;
  font-size: clamp(10px, 1.2vh, 12px);
  letter-spacing: 0.06em;
  margin-bottom: clamp(7px, 1.4vh, 16px);
}
.tag-red { color: var(--red); }
.tag-blue { color: var(--blue); }

.deck-card h2 {
  font-size: clamp(0.95rem, 1.9vh, 1.5rem);
  font-weight: 600;
  margin: 0 0 clamp(7px, 1.3vh, 14px);
  line-height: 1.3;
  color: var(--text);
}

.deck-card p {
  color: var(--text-dim);
  font-size: clamp(0.66rem, 1.55vh, 0.95rem);
  line-height: 1.5;
  margin: 0;
}

.deck-card p + p { margin-top: clamp(5px, 1.4vh, 14px); }

.deck-card em { font-style: normal; color: var(--blue); }

.company-mark {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.6vw, 18px);
  margin: 0 0 clamp(9px, 1.6vh, 18px);
}

.company-mark .pentacle {
  width: 84px;
  height: 84px;
  object-fit: contain;
  flex-shrink: 0;
  display: block;
}

.company-mark-text { min-width: 0; }
.company-mark-text h2 { margin: 0 0 6px; }

.mark-forged {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

.sz-logo-inline {
  height: 26px;
  width: auto;
  display: block;
}

.fail-list {
  list-style: none;
  margin: clamp(10px, 1.6vh, 18px) 0 clamp(10px, 1.5vh, 16px);
  padding: 0;
  font-size: clamp(0.64rem, 1.45vh, 0.9rem);
  color: var(--text-dim);
}

.fail-list li {
  position: relative;
  padding-left: 20px;
  margin-top: clamp(4px, 0.7vh, 7px);
}

.fail-list li::before {
  content: "!";
  position: absolute;
  left: 4px;
  color: var(--red);
}

/* ---------- footer ---------- */
.site-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 32px;
  border-top: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 12px;
}

/* ---------- responsive ---------- */

/* ---------- team ---------- */
.mark-sub {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--blue);
}

.team-list {
  list-style: none;
  margin: clamp(8px, 1.4vh, 14px) 0 0;
  padding: 0;
}

/* the team card carries the most content, so it rides higher and tighter to
   stay inside the viewport while pinned */
#team { top: 56px; }
#team .term-body { padding: clamp(10px, 2.4vh, 26px) clamp(14px, 1.8vw, 26px); }
#team .pentacle { width: clamp(32px, 5vh, 44px); height: clamp(32px, 5vh, 44px); }
#team .company-mark { margin-bottom: clamp(7px, 1.2vh, 12px); }

.member {
  display: flex;
  gap: 14px;
  padding: clamp(3px, 1vh, 12px) 0;
  border-top: 1px solid var(--border);
}
.member:first-child { border-top: none; padding-top: 4px; }

.member-shot {
  width: clamp(56px, 12vh, 104px);
  height: clamp(56px, 12vh, 104px);
  flex-shrink: 0;
  object-fit: cover;
  object-position: center top;
  filter: grayscale(1) contrast(1.05);
  border: 1px solid var(--border-strong);
  background: var(--bg-alt);
}

.member-text { min-width: 0; }

.member-text h3 {
  margin: 0;
  font-size: clamp(0.78rem, 1.5vh, 0.95rem);
  font-weight: 600;
  color: var(--text);
}

.member-role {
  margin: 2px 0 clamp(3px, 0.7vh, 6px);
  font-size: clamp(9px, 1.1vh, 11px);
  letter-spacing: 0.04em;
  color: var(--blue-dim);
}

.member-bio {
  margin: 0;
  font-size: clamp(0.6rem, 1.35vh, 0.82rem);
  line-height: 1.4;
  color: var(--text-dim);
}

.member-links {
  display: flex;
  gap: 10px;
  margin: clamp(4px, 0.8vh, 8px) 0 0;
}
.member-links a {
  display: inline-flex;
  color: var(--blue-dim);
  transition: color 0.15s;
}
.member-links a:hover,
.member-links a:focus-visible { color: var(--blue); }
.member-links svg {
  width: clamp(14px, 1.8vh, 18px);
  height: clamp(14px, 1.8vh, 18px);
  fill: currentColor;
}
.member-links svg[fill="none"] { fill: none; }

/* ── member → bio popup ────────────────────────────────────────────────────────
   The whole row opens the bio. On hover the photo takes its colour back, a rail
   lights on the left and the "read more" cue slides in; on touch there is no
   hover to discover it with, so the cue simply stays visible. */
.member {
  position: relative;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
.member::before {
  content: '';
  position: absolute;
  left: -10px;
  top: 10%;
  bottom: 10%;
  width: 2px;
  background: var(--blue);
  opacity: 0;
  transform: scaleY(0.4);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.member .member-shot { transition: filter 0.25s ease; }
.member:hover { background-color: rgba(0, 229, 255, 0.04); }
.member:hover::before,
.member:focus-within::before { opacity: 1; transform: none; }
.member:hover .member-shot { filter: none; }
.member:hover h3 { color: var(--blue); }

/* The cue is always on screen, not only on hover: an outlined button that
   reads as the way in. Hovering anywhere on the row fills it, so the whole
   member is plainly the target. */
.member-open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: clamp(6px, 1vh, 10px) 0 0;
  padding: clamp(5px, 0.8vh, 7px) clamp(10px, 1.2vh, 14px);
  font-family: var(--font-mono);
  font-size: clamp(9px, 1.15vh, 11px);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue);
  background: rgba(0, 229, 255, 0.06);
  border: 1px solid var(--blue-dim);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.member-open-arrow {
  display: inline-block;
  transition: transform 0.2s ease;
}
.member:hover .member-open,
.member-open:focus-visible {
  color: var(--bg);
  background: var(--blue);
  border-color: var(--blue);
  box-shadow: 0 0 18px rgba(0, 229, 255, 0.35);
}
.member:hover .member-open-arrow { transform: translateX(4px); }
.member-open:focus-visible { outline: 1px solid var(--blue); outline-offset: 3px; }

/* the photo carries a matching badge, so the picture also reads as clickable */
.member-shot-wrap {
  position: relative;
  flex-shrink: 0;
  align-self: flex-start;
}
.member-shot-wrap::after {
  /* the plus is drawn as two crossed bars, not set as a "+" glyph: a glyph
     centres on its own font metrics, which left it sitting high in the dot */
  content: '';
  position: absolute;
  right: -6px;
  bottom: -6px;
  width: 22px;
  height: 22px;
  background-color: var(--blue);
  background-image: linear-gradient(var(--bg), var(--bg)), linear-gradient(var(--bg), var(--bg));
  background-size: 10px 2px, 2px 10px;
  background-position: center center;
  background-repeat: no-repeat;
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--bg-alt);
  transition: transform 0.2s ease;
}
.member:hover .member-shot-wrap::after { transform: scale(1.15) rotate(90deg); }

.bio-dialog {
  /* fit-content is what lets margin:auto centre a modal dialog; `auto` would
     stretch it across the viewport and pin the card to the left edge */
  width: fit-content;
  height: fit-content;
  max-width: none;
  max-height: none;
  margin: auto;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  overflow: visible;
}
.bio-dialog::backdrop {
  background: rgba(3, 3, 4, 0.72);
  backdrop-filter: blur(4px);
}
.bio-dialog[open] .bio-card { animation: bio-in 0.22s ease-out; }
@keyframes bio-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

/* Square at every size: the side is the smallest of 760px and the viewport's
   width and height, so it never needs to scroll the page to be seen whole. */
.bio-card {
  display: flex;
  flex-direction: column;
  width: min(760px, calc(100vw - 32px), calc(100dvh - 48px));
  aspect-ratio: 1 / 1;
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  box-shadow: 0 16px 48px rgba(211, 211, 211, 0.2);
  overflow: hidden;
}

.bio-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  padding: 12px 14px 12px 22px;
  border-bottom: 1px solid var(--border);
}
.bio-count {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--blue-dim);
}
.bio-nav { display: flex; gap: 6px; }
.bio-step,
.bio-close {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 16px;
  line-height: 1;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}
.bio-close { font-size: 20px; margin-left: 6px; }
.bio-step:hover,
.bio-close:hover { color: var(--blue); border-color: var(--blue-dim); background: rgba(0, 229, 255, 0.06); }
.bio-step:focus-visible,
.bio-close:focus-visible { outline: 1px solid var(--blue); outline-offset: 2px; }

/* The name and photo run across the top. Under them, two blocks split by a
   rule: on the left the bio scrolls in its own box, with a "scroll for more"
   pill while there is more and any call to action pinned beneath; on the
   right the gallery shows one photo whole, its caption, then its arrows. */
.bio-scroll {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  padding: 20px;
  overflow: hidden;
  overscroll-behavior: contain;
}
.bio-scroll { grid-template-rows: auto minmax(0, 1fr); }
.bio-scroll > .bio-head { grid-column: 1 / -1; }
.bio-scroll.has-gallery { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 0; }
.bio-scroll > .bio-cue { display: none; }
.bio-scroll.is-swapping { animation: bio-swap 0.2s ease-out; }
@keyframes bio-swap {
  from { opacity: 0; transform: translateX(var(--swap-from, 0)); }
  to { opacity: 1; transform: none; }
}

.bio-main {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
}
.has-gallery > .bio-main { padding-right: 20px; }

.bio-head {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.bio-shot {
  width: 72px;
  height: 72px;
  flex-shrink: 0;
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: var(--bg);
}
.bio-head h2 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}
.bio-head .member-role { font-size: 10px; margin-bottom: 6px; }
.bio-head .member-links { margin: 0; }

.bio-text {
  position: relative;
  flex: 1;
  min-height: 0;
  padding: 0 6px 0 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.bio-text p {
  margin: 0 0 14px;
  font-size: 0.8rem;
  line-height: 1.65;
  color: var(--text);
  opacity: 0.88;
}

/* Pinned to the bottom of whichever block scrolls: a fade over the cut-off
   text and a pill that scrolls it on. Hidden once there is nothing below. */
.bio-cue {
  position: sticky;
  bottom: 0;
  height: 64px;
  margin-top: -64px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 6px;
  background: linear-gradient(to bottom, rgba(8, 9, 10, 0), var(--bg-alt) 80%);
  pointer-events: none;
}
.bio-cue[hidden] { display: none !important; }
.bio-cue-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue);
  background: var(--bg-alt);
  border: 1px solid var(--blue-dim);
  border-radius: 999px;
  cursor: pointer;
  pointer-events: auto;
  transition: color 0.15s, background-color 0.15s, border-color 0.15s;
}
.bio-cue-btn span { display: inline-block; animation: bio-nudge 1.6s ease-in-out infinite; }
.bio-cue-btn:hover { color: var(--bg); background: var(--blue); border-color: var(--blue); }
@keyframes bio-nudge { 50% { transform: translateY(3px); } }

/* An outbound call to action inside a bio: a full-width bar, so it reads as
   the next step rather than another link in the text. */
.bio-cta {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin: 0;
  padding: 14px 16px;
  color: var(--text);
  text-decoration: none;
  background: rgba(0, 229, 255, 0.06);
  border: 1px solid var(--blue-dim);
  border-radius: 12px;
  transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
}
.bio-cta-text { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; line-height: 1.4; }
.bio-cta-kicker {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue);
}
.bio-cta-arrow {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  color: var(--bg);
  background: var(--blue);
  border-radius: 50%;
  transition: transform 0.2s ease;
}
.bio-cta:hover,
.bio-cta:focus-visible {
  background: rgba(0, 229, 255, 0.12);
  border-color: var(--blue);
  box-shadow: 0 0 18px rgba(0, 229, 255, 0.2);
}
.bio-cta:hover .bio-cta-arrow { transform: translate(2px, -2px); }
.bio-cta:focus-visible { outline: 1px solid var(--blue); outline-offset: 3px; }

.bio-gallery-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  padding-left: 20px;
  border-left: 1px solid var(--border);
}

/* The frame is only a window onto the strip, with no border of its own. The
   border sits on each photo, so it hugs that photo's own size and shape, and
   the photo is centred in the space the frame gives it. */
.bio-gallery-frame {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.bio-gallery {
  display: flex;
  height: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.bio-gallery::-webkit-scrollbar { display: none; }
.bio-gallery li {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex: 0 0 100%;
  height: 100%;
  scroll-snap-align: start;
}
.bio-gallery img,
.bio-gallery video {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  min-height: 0;
  max-height: calc(100% - 3.4em);   /* leaves the caption its two lines */
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--bg);
}
.bio-gallery li.is-placeholder {
  border: 1px dashed var(--border-strong);
  border-radius: 14px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

/* centred, so it sits under the picture whether the photo is wide or narrow */
.bio-gallery-caption {
  flex-shrink: 0;
  align-self: stretch;
  margin: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.45;
  color: var(--text-dim);
}

.bio-gallery-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.bio-gallery-count {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}
.bio-gallery-step {
  width: 36px;
  height: 36px;
  padding: 0;
  font-size: 15px;
  color: var(--bg);
  background: var(--blue);
  border: 1px solid var(--blue);
  border-radius: 50%;
  cursor: pointer;
  transition: opacity 0.15s, transform 0.15s, background-color 0.15s, color 0.15s;
}
.bio-gallery-step:hover { transform: scale(1.08); }
.bio-gallery-step[disabled] {
  color: var(--text-dim);
  background: transparent;
  border-color: var(--border-strong);
  cursor: default;
  transform: none;
}
.bio-gallery-step:focus-visible { outline: 1px solid var(--blue); outline-offset: 3px; }

html.bio-open { overflow: hidden; }

/* Too narrow for two columns inside a square: the blocks stack, bio above
   gallery, and the body scrolls as one. */
@media (max-width: 640px) {
  .bio-scroll,
  .bio-scroll.has-gallery {
    display: block;
    padding: 16px;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
  }
  /* sticky offsets sit inside the scroller's padding, so reach past it */
  .bio-scroll > .bio-cue { display: flex; bottom: -16px; margin-bottom: -16px; }
  .bio-head { margin-bottom: 16px; }
  .bio-text { overflow: visible; padding-right: 0; }
  .bio-text > .bio-cue { display: none; }
  .has-gallery > .bio-main { padding-right: 0; }
  .bio-bar { padding-left: 16px; }
  .bio-shot { width: 60px; height: 60px; }
  .bio-gallery-panel {
    margin-top: 18px;
    padding: 18px 0 0;
    border-left: 0;
    border-top: 1px solid var(--border);
  }
  .bio-gallery-frame { flex: none; height: min(80vw, 320px); }
}

@media (prefers-reduced-motion: reduce) {
  .bio-dialog[open] .bio-card,
  .bio-scroll.is-swapping,
  .bio-cue-btn span { animation: none; }
}

/* ---------- video caption + collapse tab ---------- */
/* The label follows the cursor across the footage rather than sitting in a
   corner naming the athletes. Position is written by main.js on a rAF loop, so
   only opacity transitions here — animating transform as well would fight the
   loop and make the label lag its own movement. */
.video-cursor {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 6;
  padding: 7px 13px;
  font-size: 12px;
  letter-spacing: 0.09em;
  white-space: nowrap;
  color: var(--red);
  background: rgba(3, 3, 4, 0.78);
  border: 1px solid rgba(255, 60, 74, 0.55);
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
  will-change: transform;
}
.video-bg-wrap.is-tracking .video-cursor { opacity: 1; }

/* No cursor to follow on a touch screen, so the label never appears there. */
@media (hover: none) {
  .video-cursor { display: none; }
}

.deck-toggle {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  padding: 18px 6px;
  font-family: inherit;
  font-size: 12px;
  color: var(--blue);
  background: rgba(3, 3, 4, 0.85);
  border: 1px solid var(--border-strong);
  border-right: none;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
.deck-toggle:hover { color: var(--text); background: rgba(3, 3, 4, 0.95); }

.video-bg-wrap, .cards-col { transition: width 0.45s ease; }

.deck-section.expanded .video-bg-wrap { width: 100%; border-right: none; }
.deck-section.expanded .cards-col { width: 0; overflow: hidden; }
.deck-section.expanded .card-slot { padding: 0; }
.deck-section.expanded .deck-card { opacity: 0; visibility: hidden; }

/* ---------- dojos ---------- */
.dojos {
  padding: 90px 6vw 100px;
  border-top: 1px solid var(--border);
  background: var(--bg);
}

.dojos-head { margin-bottom: 40px; }
.dojos-head h2 {
  margin: 0 0 8px;
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  font-weight: 600;
  color: var(--text);
}
.dojos-head p { margin: 0; color: var(--text-dim); font-size: 0.9rem; }

.dojo-row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}

.dojo a {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px 14px 14px;
  text-decoration: none;
  border: 1px solid var(--border);
  background: var(--panel);
  transition: border-color 0.25s ease, transform 0.25s ease;
}
.dojo a:hover, .dojo a:focus-visible {
  border-color: var(--blue-dim);
  transform: translateY(-2px);
}

.dojo-badge {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.dojo-badge img { width: 100%; height: 100%; object-fit: contain; display: block; }

.dojo-name {
  font-size: 0.9rem;
  color: var(--text);
  letter-spacing: 0.02em;
}

.dojo-loc {
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--blue);
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.dojo a:hover .dojo-loc,
.dojo a:focus-visible .dojo-loc { opacity: 1; transform: translateX(0); }


/* ---------- demo / HUD ---------- */
.demo {
  position: relative;
  z-index: 2;
  padding: 90px 6vw 20px;
  background: var(--bg);
  border-top: 1px solid var(--border);
}

.demo-head { margin-bottom: 32px; }
.demo-head h2 {
  margin: 0 0 8px;
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  font-weight: 600;
  color: var(--text);
}
.demo-head p { margin: 0; color: var(--text-dim); font-size: 0.9rem; max-width: 66ch; }

.hud-app {
  --hud-line: rgba(255, 255, 255, 0.1);
  border: 1px solid var(--border-strong);
  background: #050506;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
  font-size: 12px;
}

/* inline wordmark, for prose and chrome */
/* The wordmark png carries 43.34% of empty canvas below its baseline (measured
   off the alpha: ink to row 217 of 383). An inline-block baselines on its BOX
   bottom, so without that correction the mark floats above the line it sits in.
   The shift is expressed in em so it holds at any type size. */
.wm {
  height: 0.92em;
  width: auto;
  display: inline-block;
  vertical-align: baseline;
  transform: translateY(0.399em);   /* 0.4334 × 0.92em */
}
.demo-head .wm { height: 0.85em; transform: translateY(0.368em); }

/* file tabs above the app frame */
.app-shell { position: relative; }

.app-tabs { display: flex; gap: 4px; padding-left: 10px; }

.app-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 16px;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  background: #08090a;
  border: 1px solid var(--border-strong);
  border-bottom: none;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
.app-tab .wm { height: 11px; opacity: 0.65; transition: opacity 0.2s ease; }
.app-tab:hover { color: var(--text); }
.app-tab:hover .wm { opacity: 0.9; }
.app-tab.is-active {
  color: var(--text);
  background: #050506;
  position: relative;
  z-index: 2;
  margin-bottom: -1px;
  padding-bottom: 9px;
}
.app-tab.is-active .wm { opacity: 1; }
.tab-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--border-strong);
}
.app-tab.is-active .tab-dot { background: var(--blue); }
#tab-sys.is-active .tab-dot { background: var(--red); }

.app-view[hidden] { display: none; }

/* recording indicator */
.ver-tag {
  margin-left: auto;
  padding: 2px 8px;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--blue);
  border: 1px solid var(--blue-dim);
}

.rec-badge[hidden] { display: none; }
.rec-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--red);
  border: 1px solid rgba(255, 60, 74, 0.5);
}
.rec-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--red);
  animation: recPulse 1.1s ease-in-out infinite;
}
@keyframes recPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* dashboard: empty state */
#dash-video { opacity: 0; transition: opacity 0.45s ease; }
.app-shell[data-state="ready"] #dash-video { opacity: 1; }

.dash-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 24px;
  text-align: center;
  background: rgba(2, 2, 3, 0.82);
}
.dash-empty[hidden] { display: none; }
.dash-empty p {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(0.76rem, 1.5vh, 0.9rem);
  line-height: 1.7;
  color: var(--text-dim);
}
.dash-empty .wm { height: 0.9em; }

.hud-btn {
  padding: 10px 20px;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.09em;
  color: #000;
  background: var(--blue);
  border: 1px solid var(--blue);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.hud-btn:hover {
  background: transparent;
  color: var(--blue);
  box-shadow: 0 0 18px rgba(0, 229, 255, 0.25);
}

/* the action rail sits 3px under the feed, flush right */
.dash-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 3px;
}
.dash-actions[hidden] { display: none; }

/* aggregating */
.dash-loading {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: rgba(2, 2, 3, 0.88);
}
.dash-loading[hidden] { display: none; }
.load-spin {
  width: 22px; height: 22px;
  border: 1px solid rgba(0, 229, 255, 0.25);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.load-copy { font-size: 11px; letter-spacing: 0.14em; color: var(--blue); }
.load-sub { font-size: 9px; letter-spacing: 0.07em; color: var(--text-dim); }

/* per-card loading */
.hud-spin {
  margin-left: 8px;
  font-size: 10px;
  color: var(--blue);
  opacity: 0;
}
.hud-panel.is-loading .hud-spin { opacity: 1; }
.hud-panel.is-loading .hud-panel-body { position: relative; min-height: 74px; }
.hud-panel.is-loading .hud-panel-body > * { visibility: hidden; }
.hud-panel.is-loading .hud-panel-body::after {
  content: '';
  position: absolute;
  inset: 10px 11px;
  background:
    linear-gradient(var(--hud-line) 0 0) 0 4px / 100% 6px no-repeat,
    linear-gradient(var(--hud-line) 0 0) 0 22px / 72% 6px no-repeat,
    linear-gradient(var(--hud-line) 0 0) 0 40px / 88% 6px no-repeat,
    linear-gradient(var(--hud-line) 0 0) 0 58px / 60% 6px no-repeat;
  animation: shimmer 1.3s ease-in-out infinite;
}
@keyframes shimmer { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.85; } }

.panel-null {
  margin: 4px 0;
  font-size: 10px;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.28);
}

/* session + breakdown rows */
.sess {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 7px 0;
  border-top: 1px solid var(--hud-line);
}
.sess:first-child { border-top: 0; padding-top: 0; }
.sess-id { font-size: 11px; color: var(--text); }
.sess-meta { font-size: 9px; letter-spacing: 0.05em; color: var(--text-dim); }

/* highlights */
.hl-badge {
  position: absolute;
  top: 10px; left: 10px;
  padding: 4px 9px;
  font-size: 9px;
  letter-spacing: 0.08em;
  color: var(--blue);
  background: rgba(2, 2, 3, 0.85);
  border: 1px solid var(--blue-dim);
}
.hl-badge[hidden] { display: none; }

.hl-strip { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
.hl-strip[hidden] { display: none; }
.hl-item {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 7px 9px;
  font-family: inherit;
  text-align: left;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--hud-line);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.hl-item:hover { color: var(--text); border-color: var(--blue-dim); }
.hl-item.is-active { color: var(--text); border-color: var(--blue); background: rgba(0, 229, 255, 0.06); }
.hl-name { font-size: 10px; letter-spacing: 0.05em; }
.hl-meta { font-size: 9px; color: var(--text-dim); }

/* top bar */
.hud-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--hud-line);
  background: rgba(255, 255, 255, 0.02);
}
.hud-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  letter-spacing: 0.14em;
  color: var(--text);
}
.hud-brand i { font-style: normal; color: var(--text-dim); margin-left: 6px; }
.hud-mark {
  width: 15px; height: 15px;
  border: 1px solid var(--text);
  border-radius: 50%;
  position: relative;
}
.hud-mark::after {
  content: '';
  position: absolute; inset: 3px;
  border-radius: 50%;
  background: var(--text);
}
.hud-mark.sm { width: 11px; height: 11px; }
.hud-mark.sm::after { inset: 2px; }
.hud-rule { color: var(--text-dim); letter-spacing: 0.2em; }
.tb { display: inline-flex; align-items: baseline; gap: 7px; white-space: nowrap; }
.tb i { font-style: normal; font-size: 10px; letter-spacing: 0.09em; color: var(--text-dim); }
.tb b { font-weight: 500; color: var(--text); }
.tb b.ok { color: var(--blue); }
.hud-top .tb:last-child { margin-left: 0; }

/* grid */
.hud-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2.1fr) minmax(0, 1.1fr);
  gap: 1px;
  background: var(--hud-line);
}
.hud-col, .hud-stage { min-width: 0; background: #050506; padding: 12px; }
.hud-col { display: flex; flex-direction: column; gap: 12px; }

.hud-panel { border: 1px solid var(--hud-line); background: rgba(255, 255, 255, 0.015); }
.hud-panel.is-red { border-left: 2px solid var(--red); }

.hud-panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 11px;
  border-bottom: 1px solid var(--hud-line);
  font-size: 11px;
  letter-spacing: 0.11em;
  color: var(--text);
}
.hud-key { margin-left: auto; font-size: 10px; color: var(--text-dim); }
.hud-sq { width: 8px; height: 8px; background: var(--text); }
.hud-panel-body { padding: 10px 11px; }

.hud-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 3px 0;
  color: var(--text-dim);
}
.hud-row b { font-weight: 500; color: var(--text); }
.hud-row b.bind { color: var(--blue); }
.hud-row b.bind.is-loose { color: var(--red); }

/* combat analysis bars */
.sig { padding: 5px 0; }
.sig-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: 10px;
  letter-spacing: 0.07em;
  color: var(--text);
}
.sig-top span i { font-style: normal; color: var(--text-dim); margin-left: 5px; }
.sig-track { height: 3px; margin-top: 4px; background: rgba(255, 255, 255, 0.07); }
.sig-fill { display: block; height: 100%; background: var(--red); transition: width 0.4s ease; }
.hud-binary {
  padding: 7px 11px;
  border-top: 1px solid var(--hud-line);
  font-size: 9px;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.22);
}

/* stage */
.hud-feed {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--hud-line);
}
.hud-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.reticles .r {
  position: absolute;
  width: 14px; height: 14px;
  border: 1px solid rgba(255, 255, 255, 0.35);
}
.reticles .tl { top: 8px; left: 8px; border-right: 0; border-bottom: 0; }
.reticles .tr { top: 8px; right: 8px; border-left: 0; border-bottom: 0; }
.reticles .bl { bottom: 8px; left: 8px; border-right: 0; border-top: 0; }
.reticles .br { bottom: 8px; right: 8px; border-left: 0; border-top: 0; }

/* v6: the state reel is a card under the feed, not glass over the footage */
.state-card { margin-top: 10px; }
.state-card .hud-panel-head b { font-weight: 500; color: var(--blue); }
.state-card .hud-panel-head b.is-drama { color: var(--red); }
.head-bars { margin-left: auto; color: rgba(255, 255, 255, 0.3); letter-spacing: 0; }
.pulse-badge { margin-left: 10px; font-size: 9px; color: var(--red); }
.pulse-badge[hidden] { display: none; }

/* collapse affordance, as in the preview */
.hud-panel-head[data-collapse] { cursor: pointer; user-select: none; }
.hud-panel-head[data-collapse]:hover { background: rgba(255, 255, 255, 0.04); }
.chev {
  width: 7px; height: 7px;
  margin-left: 10px;
  flex: none;
  border-right: 1.5px solid var(--text-dim);
  border-bottom: 1.5px solid var(--text-dim);
  transform: rotate(45deg) translateY(-15%);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
.hud-panel-head:hover .chev { border-color: var(--text); }
.hud-panel.is-collapsed .hud-panel-body { display: none; }
.hud-panel.is-collapsed .hud-panel-head { border-bottom: 0; }
.hud-panel.is-collapsed .chev { transform: rotate(-135deg) translateY(-15%); }

/* THROW card, right column */
.throw-card[hidden] { display: none; }
.hud-dot-red { width: 8px; height: 8px; background: var(--red); }
.throw-art {
  height: 54px;
  margin-bottom: 9px;
  background:
    repeating-linear-gradient(115deg, var(--blue) 0 3px, transparent 3px 11px) 0 0 / 44% 100% no-repeat,
    repeating-linear-gradient(90deg, rgba(0,229,255,.65) 0 2px, transparent 2px 9px) 56% 50% / 44% 42% no-repeat,
    #000;
  border: 1px solid rgba(0, 229, 255, 0.3);
}
.throw-meta {
  margin: 0 0 9px;
  font-size: 9px;
  line-height: 1.7;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
.meta-key { color: var(--red); }
.meta-id { color: var(--text-dim); }

/* the state reel: the committed row is a payline, rule-capped, per v6 */
.hud-ladder {
  margin: 4px 0 12px;
  padding: 0;
  list-style: none;
  text-align: center;
  pointer-events: none;
}
.hud-ladder li {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-size: 9px;
  letter-spacing: 0.12em;
  line-height: 2;
  color: rgba(255, 255, 255, 0.22);
  transition: color 0.3s ease, font-size 0.3s ease;
}
.hud-ladder li.is-live {
  font-size: 15px;
  letter-spacing: 0.16em;
  line-height: 2.4;
  color: var(--blue);
}
.hud-ladder li.is-live::before,
.hud-ladder li.is-live::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}
.hud-ladder li.is-live.is-drama { color: var(--red); }

.hud-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
}
.chip {
  padding: 4px 9px;
  font-size: 9px;
  letter-spacing: 0.07em;
  color: var(--text);
  background: rgba(0, 0, 0, 0.7);
  border: 1px solid var(--hud-line);
}
.chip.is-red { color: var(--red); border-color: rgba(255, 60, 74, 0.5); }

.hud-statebar {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 10px;
  padding: 8px 11px;
  border: 1px solid var(--hud-line);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}
.hud-statebar b { font-weight: 500; color: var(--blue); }
.hud-statebar b.is-drama { color: var(--red); }
.pulse-badge { margin-left: auto; font-size: 9px; color: var(--red); }

/* beat strip - the clickable control */
.beat-strip { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
.beat {
  flex: 1 1 auto;
  padding: 7px 6px;
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--hud-line);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.beat:hover { color: var(--text); border-color: var(--blue-dim); }
.beat.is-active { color: #000; background: var(--blue); border-color: var(--blue); }
.beat.is-drama.is-active { background: var(--red); border-color: var(--red); color: #fff; }

/* roster */
.athlete { padding: 9px 0; border-top: 1px solid var(--hud-line); }
.athlete:first-child { border-top: 0; padding-top: 0; }
.athlete-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.slot {
  padding: 1px 5px;
  font-size: 9px;
  color: #000;
  background: var(--red);
}
.role-uke .slot { background: var(--blue); }
.who { font-size: 13px; letter-spacing: 0.06em; color: var(--text); }
.role {
  margin-left: auto;
  padding: 1px 6px;
  font-size: 9px;
  letter-spacing: 0.08em;
  color: var(--red);
  border: 1px solid rgba(255, 60, 74, 0.5);
}
.role-uke .role { color: var(--blue); border-color: var(--blue-dim); }
.athlete.is-unbound { opacity: 0.55; }

/* bottom bar */
.hud-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
  padding: 9px 14px;
  border-top: 1px solid var(--hud-line);
  font-size: 10px;
  letter-spacing: 0.07em;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.02);
}
.hud-bottom b { font-weight: 500; color: var(--text); }

@media (max-width: 860px) {
  .deck-section { flex-direction: column; }

  .video-bg-wrap {
    position: relative;
    width: 100%;
    height: 46vh;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  .cards-col { width: 100%; }

  .card-slot { min-height: auto; padding: 40px 6vw; }

  .deck-card { position: relative; top: 0; opacity: 1; transform: none; }
}

/* Mobile: the throw footage becomes a fixed background layer that the cards
   scroll over, rather than a stacked panel above them. */
@media (max-width: 860px) {
  .video-bg-wrap {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    border-right: none;
    z-index: 0;
  }

  .bg-video {
    opacity: 0.55;
    object-fit: contain;
  }

  .cards-col {
    position: relative;
    z-index: 1;
  }

  .cards-col { background: transparent; }

  .deck-card {
    background: rgba(6, 6, 8, 0.86);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
  }

  /* the fixed video sits behind everything, so these need their own ground */
  .hero,
  .dojos,
  .site-footer {
    position: relative;
    z-index: 2;
    background: var(--bg);
  }

  .deck-card { max-height: none; }
  .term-body { max-height: none; overflow: visible; }
  .deck-toggle { display: none; }
  .dojo-loc { opacity: 1; transform: none; }
  .member { flex-direction: row; }
}

/* Very short desktop windows can't fit a pinned card no matter how far the
   type scales, so the deck stops pinning and simply flows instead of clipping. */
@media (min-width: 861px) and (max-height: 560px) {
  .deck-card { position: relative; top: 0; max-height: none; }
  /* #team's sticky offset outranks .deck-card's `top: 0` on id specificity, so
     it has to be cleared here too — otherwise the team card sits 56px low and
     the gap above it reads as half again the others. */
  #team { top: 0; }
}

@media (max-width: 860px) {
  .hero-stack {
    width: 88vw;
  }
  .hero-baseline {
    left: clamp(16px, 5vw, 32px);
    right: clamp(16px, 5vw, 32px);
    bottom: 28px;
  }
  .cta-dot { width: 80px; height: 80px; }
  .sz-logo { height: 60px; }

  .hero-forged {
    left: clamp(16px, 5vw, 32px);
    right: auto;
    bottom: 20px;
  }
  .hero-forged .sz-logo { height: 40px; }
}

@media (max-width: 860px) {
  .demo { padding: 60px 5vw 16px; }

  .hud-grid { grid-template-columns: minmax(0, 1fr); }
  .app-tabs { padding-left: 6px; }
  .app-tab { padding: 7px 11px; font-size: 10px; letter-spacing: 0.05em; }
  .dash-empty { gap: 14px; padding: 18px; }
  .dash-empty p { font-size: 0.72rem; line-height: 1.65; }
  .hud-btn { padding: 9px 16px; font-size: 10px; }
  .hl-item { flex: 1 1 45%; }
  .hud-app { font-size: 11px; }
  .hud-top { gap: 5px 12px; padding: 9px 11px; }
  .hud-top .tb:last-child { margin-left: 0; }
  .tb-wide { display: none; }
  .hud-col, .hud-stage { padding: 10px; }
  .hud-ladder li { line-height: 1.7; }
  .hud-ladder li.is-live { font-size: 14px; }
  .beat { flex: 1 1 30%; }
  .hud-bottom { gap: 5px 14px; padding: 8px 11px; }
}


/* the nav gained entries; keep it inside narrow viewports */
@media (max-width: 860px) {
  .site-header { padding: 12px 16px; gap: 12px; }
  .site-header.scrolled { padding: 10px 16px; }
  .site-nav { gap: 14px; flex-wrap: wrap; justify-content: flex-end; }
  .site-nav a { font-size: 12px; }
  .logo { font-size: 14px; }
}

@media (max-width: 520px) {
  .site-nav { gap: 10px; }
  .site-nav a { font-size: 11px; }
  .clip { min-width: 158px; }
}


/* ── Where monospace survives ───────────────────────────────────────────────
   Unbounded is the brand face and sets the whole site. These keep the mono
   stack, and the reason is structural rather than stylistic: they need a FIXED
   ADVANCE WIDTH. Proportional digits make the HUD's readouts shift sideways
   every time a value changes, and ASCII art built on a character grid collapses
   outright in a proportional face. */
pre,
code,
.ascii-static,
.beat,
[class*="hud-"] {
  font-family: var(--font-mono);
}

/* ── demo video ───────────────────────────────────────────────────────────────
   Footage of the build running on a monitor beside the mat, recorded with
   the sound stripped. It scales with its column like any image. */
.demo-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--border-strong);
  background: #04050a;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
  overflow: hidden;
}
.demo-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}


/* ── header lockup + mobile menu ───────────────────────────────────────────── */
.logo { display: inline-flex; align-items: center; }
.logo-lockup { height: 20px; width: auto; display: block; }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 34px;
  height: 28px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-strong);
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 16px;
  height: 1px;
  margin: 0 auto;
  background: var(--text);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

@media (max-width: 860px) {
  /* the lockup stays; the links fold into the menu */
  .nav-toggle { display: flex; }
  .site-header { position: fixed; }
  .site-nav {
    position: absolute;
    top: 100%;
    right: 0;
    left: 0;
    display: none;
    flex-direction: column;
    gap: 0;
    padding: 6px 0;
    background: rgba(3, 3, 4, 0.97);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a { padding: 12px 20px; font-size: 13px; }
  .site-nav a + a { border-top: 1px solid var(--border); }
}

/* ── UCHI-MATA! on touch ──────────────────────────────────────────────────────
   Struck at the point of contact, with two echoes trailing behind it. The
   echoes are the same word scaled out and faded — the label does not travel,
   it rings. */
.uchi-burst {
  position: fixed;
  z-index: 400;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
.uchi-burst span {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  padding: 7px 13px;
  white-space: nowrap;
  font-size: 13px;
  letter-spacing: 0.09em;
  color: var(--red);
  background: rgba(3, 3, 4, 0.78);
  border: 1px solid rgba(255, 60, 74, 0.55);
  animation: uchi-strike 620ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
.uchi-burst span.echo {
  background: transparent;
  animation: uchi-echo 900ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}

@keyframes uchi-strike {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.82); }
  22%  { opacity: 1; transform: translate(-50%, -50%) scale(1.04); }
  70%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -58%) scale(1); }
}
@keyframes uchi-echo {
  0%   { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(2.1); }
}

/* Reduced motion: the word still lands, it just does not ring. */
.uchi-burst.is-still span { animation: uchi-hold 800ms linear both; }
@keyframes uchi-hold {
  0%, 80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100%    { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}

/* ── touch: blending is not reliable here ─────────────────────────────────────
   WebKit drops mix-blend-mode in several compositing situations — notably an
   element inside a position:fixed ancestor, which is exactly what the deck's
   video becomes on mobile. The blend silently paints nothing, which is why the
   throw was missing on phones while working on desktop.

   Both clips are keyed the same way: their ground is crushed to black and the
   blend makes black contribute nothing. Without the blend the ground is still
   black, and this page's own ground is #030304 — so dropping the blend and
   leaning on contrast alone costs almost nothing visually and actually renders. */
@media (hover: none) {
  /* Dropping the blend was the wrong fix: without it the keyed-out ground is
     painted as opaque black, which is the dark rectangle that appeared around
     both clips. The blend stays; what changes is that on touch the frames are
     keyed in JS and drawn into a canvas, where black becomes genuinely
     transparent rather than relying on a blend mode WebKit may ignore. */
  .hero-stack.is-canvas .hero-mark { opacity: 0; position: absolute; }
}

/* ── touch: paint the clip through a canvas ───────────────────────────────────
   Dropping the blend was not enough — the video still did not appear on phones.
   Compositing a video inside a fixed, blended, filtered stack is the fragile
   part, so on touch the frames are drawn into a canvas instead and the video
   element stops painting entirely. A canvas is just pixels: no blend mode, no
   compositing path to fall through.

   The video stays in the DOM at opacity 0 rather than display:none, because a
   video that is not rendered may stop decoding — and then there is no frame to
   draw. */
.bg-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: none;
  z-index: 3;          /* above the ascii field (1) and the video it replaces (2) */
  pointer-events: none;
}
.video-bg-wrap.is-canvas .bg-canvas { display: block; }
.video-bg-wrap.is-canvas .bg-video { opacity: 0; }

.hero-mark-canvas {
  display: none;
  width: 100%;
  height: auto;
  pointer-events: none;
}
/* Keyed on the class JS adds, not on a media query: a bare .hero-mark-canvas
   rule declared later in the file would otherwise win on order and keep the
   canvas hidden — which is exactly what happened the first time. */
.hero-stack.is-canvas .hero-mark-canvas { display: block; }

/* ── mobile: one spacing between every card ───────────────────────────────────
   The slots inherited a 100vh minimum from the desktop sticky layout, which
   :last-child kept even here — so the gaps ran 80, 191, 57 instead of matching.
   Stacked, a slot is just a card with air around it. */
@media (max-width: 860px) {
  .card-slot { padding: 14px 6vw; }
  .card-slot:first-child { padding-top: 24px; }
  .card-slot:last-child {
    min-height: auto;
    padding-bottom: 0;
  }
  .card-slot:last-child .deck-card { margin-bottom: 0; }

  /* #team carries a sticky offset for the desktop deck (id specificity, so the
     mobile `.deck-card { top: 0 }` never reached it). Stacked, that offset just
     pushed the team card 56px down out of its own slot — which is where the odd
     gap and the overlap into the demo section came from. */
  #team { top: 0; }

  /* .deck-section keeps its 10px, which is the gap to the demo section */
}
