/* ============================================================================
   CORE — Create. Own. Run. Everything.
   ----------------------------------------------------------------------------
   The whole visual system lives here. Nothing is imported and nothing is
   fetched: the three font families are the woff2 faces already sitting in
   assets/media, and every shape is drawn with borders, clip-paths and
   gradients so it stays crisp at any zoom.

   Where OSCS was a light Windows-95 desktop — bevelled chrome, Courier New,
   dusty blue — CORE is an arcade cabinet in a dark room: ink-black plates with
   notched corners, hard 2px edges, scanlines over everything, and a red that
   does the shouting.
   ========================================================================= */

/* ------------------------------------------------------------------ fonts */

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(../media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2) format('woff2');
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(../media/7178b3e590c64307-s.21jp631_3pja2.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+1E00-1E9F, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(../media/797e433ab948586e-s.p.0r6juujl39pe6.woff2) format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(../media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+1E00-1E9F, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Orbitron';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(../media/bccff27c95bdea70-s.p.3w5t_ued6eglv.woff2) format('woff2');
}

/* ------------------------------------------------------------------ tokens */

:root {
  /* The warp, written by mountWarpDrive() in js/core/ui.js. `--warp` is 0 when
     the page is still and climbs toward 1 with scroll speed; the two sky
     offsets are the parallax. Defaults here so the sky is correct before the
     module runs, and stays correct if it never does. */
  --warp: 0;
  --sky-near: 0px;
  --sky-far: 0px;

  /* brand */
  --red: #ff2e3b;
  --red-deep: #b81723;
  --red-soft: #ff6a72;
  --gold: #ffc63b;
  --gold-deep: #c48a12;
  --cyan: #35e2f2;
  --cyan-deep: #12909c;

  /* dark is the default surface */
  --ink: #0b0c10;
  --plate: #14161c;
  --plate-2: #1b1e26;
  --plate-3: #22262f;
  --edge: #2f3440;
  --edge-hi: #444b5a;
  --bone: #eceff4;
  --mute: #99a1b0;
  --mute-2: #6c7484;

  --bg: var(--ink);
  --fg: var(--bone);
  --fg-mute: var(--mute);
  --panel: var(--plate);
  --panel-2: var(--plate-2);
  --panel-3: var(--plate-3);
  --line: var(--edge);
  --line-hi: var(--edge-hi);
  --accent: var(--red);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.55);

  --mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --sans: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display: 'Orbitron', 'Geist Mono', ui-monospace, monospace;

  --notch: 10px;
  --shell: 1180px;
}

/* The light cut. Same geometry, inverted ground — paper with red ink, not a
   washed-out version of the dark theme. */
html.light {
  --bg: #eef0f4;
  --fg: #0b0c10;
  --fg-mute: #5b6270;
  --panel: #ffffff;
  --panel-2: #f4f6f9;
  --panel-3: #e7ebf1;
  --line: #c9cfda;
  --line-hi: #0b0c10;
  --shadow: 0 10px 26px rgba(11, 12, 16, 0.12);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background-color: var(--bg);
  /* A pixel starfield, and nothing else — the sky is the texture now, so the
     engineering grid that used to sit under it has gone. Two tiles: the bright
     one laid large, and a dim one laid smaller so it repeats more often — near
     stars and far ones. Two depths at two spacings is also what stops either
     tile's repeat from reading as a repeat. Both sizes are whole multiples of
     the tiles' 64 pixels, so every star lands on a grid square instead of
     straddling two.
     Laid big on purpose. The field is a backdrop and it is competing with the
     page if you can count it — at the old 512/320 the far tile alone repeated a
     dozen times across a laptop and the whole thing read as static. */
  background-image:
    url(../img/core/stars.svg),
    url(../img/core/stars-far.svg);
  /* Both layers drift with the scroll and stretch with its speed — see the
     warp block further down. The near tile moves and stretches roughly three
     times as much as the far one, which is what turns two flat tiles into a
     depth. */
  background-size:
    640px calc(640px * (1 + var(--warp) * 0.5)),
    448px calc(448px * (1 + var(--warp) * 0.18));
  background-position: 0 var(--sky-near), 170px calc(110px + var(--sky-far));
  background-attachment: fixed;
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Scanlines across the whole viewport — the one effect that most says
   "cabinet" without costing a single image. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.14) 0px,
    rgba(0, 0, 0, 0.14) 1px,
    transparent 1px,
    transparent 3px
  );
  /* Thickens slightly at speed: the tube struggling to keep up with the
     picture. Small on purpose — this one is felt rather than seen. */
  opacity: calc(0.34 + var(--warp) * 0.13);
  mix-blend-mode: multiply;
}

html.light body::after {
  opacity: calc(0.2 + var(--warp) * 0.09);
}

/* The same sky in negative: identical star positions, drawn in ink instead of
   light. Only the files change — the sizes and offsets above still apply. */
html.light body {
  background-image:
    url(../img/core/stars-ink.svg),
    url(../img/core/stars-far-ink.svg);
}

/* ---- the twinklers ------------------------------------------------------
   The dense field above is a still picture. These are the few stars that
   flare: four-point sparkles scattered by js/core/ui.js, each with its own
   size, position and clock, so they light up at unrelated moments instead of
   pulsing together. Without JS the sky is simply still, which is a fine sky. */

.starfield {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  /* Up a little at speed, so the streaks carry. */
  opacity: calc(0.82 + var(--warp) * 0.18);
}

/* The stretch. A seven-pixel sparkle pulled to nine times its height is a
   streak of light; scaled from the middle, so it grows both ways off where the
   star actually is rather than sliding off it. Transform only — the twinkle
   keyframes own opacity, and the two never touch. */
.starfield i {
  position: absolute;
  display: block;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  opacity: 0;
  transform: scaleY(calc(1 + var(--warp) * 9));
  animation: twinkle var(--dur, 8s) steps(1, end) var(--delay, 0s) infinite;
}

/* The shape comes from a class rather than an inline style, so switching theme
   swaps every twinkler to its ink twin without re-running the JS. */
.tw-sparkle {
  background-image: url(../img/core/star-sparkle.svg);
}

.tw-plus {
  background-image: url(../img/core/star-plus.svg);
}

html.light .tw-sparkle {
  background-image: url(../img/core/star-sparkle-ink.svg);
}

html.light .tw-plus {
  background-image: url(../img/core/star-plus-ink.svg);
}

/* Lit for well under a tenth of its cycle — a star that spends most of its
   time off is a star you notice when it comes on. steps(1) so it flicks
   through the three brightnesses rather than dissolving between them, which is
   what a pixel star does. */
@keyframes twinkle {
  0% {
    opacity: 0;
  }
  88% {
    opacity: 0.45;
  }
  91% {
    opacity: 1;
  }
  96% {
    opacity: 0.45;
  }
  99% {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .starfield {
    display: none;
  }
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

::selection {
  background: var(--red);
  color: #fff;
}

:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
}

/* ----------------------------------------------------------------- layout */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 20px;
}

.stack > * + * {
  margin-top: 18px;
}

.section {
  padding: 56px 0;
}

.section-tight {
  padding: 32px 0;
}

.grid {
  display: grid;
  gap: 18px;
}

.g2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.g3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.g4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 900px) {
  .g3,
  .g4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 600px) {
  .g2,
  .g3,
  .g4 {
    grid-template-columns: minmax(0, 1fr);
  }
  .section {
    padding: 40px 0;
  }
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.spacer {
  flex: 1 1 auto;
}

/* ------------------------------------------------------------------- type */

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.08;
}

p {
  margin: 0 0 1em;
}
p:last-child {
  margin-bottom: 0;
}

.display {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.mono {
  font-family: var(--mono);
}

/* Small all-caps labels do a lot of the work in this system — section
   numbers, stat captions, tags. */
.label {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.label-accent {
  color: var(--accent);
}

.lede {
  font-size: 18px;
  color: var(--fg-mute);
  max-width: 62ch;
}

.muted {
  color: var(--fg-mute);
}

.hl {
  color: var(--red);
}
.hl-gold {
  color: var(--gold);
}
.hl-cyan {
  color: var(--cyan);
}

.h-xl {
  font-size: clamp(38px, 7vw, 76px);
}

/* A display heading that answers to the pointer — foreground to red. Not a
   link, so it keeps the default cursor. */
.h-hot {
  transition: color 0.2s ease;
}

.h-hot:hover {
  color: var(--red);
}
.h-lg {
  font-size: clamp(28px, 4.4vw, 46px);
}
.h-md {
  font-size: clamp(21px, 2.6vw, 28px);
}

/* The block at the top of an inner page — eyebrow, title, and the line of copy
   under it — centred as a group. The wordmark and the tab row above it are both
   centred, so a heading hard against the left margin read as a step out of line
   rather than as the start of the page. */
.page-head {
  text-align: center;
}

/* `.lede` already carries its own measure. Centring the block only moves the
   text inside its box, so the box has to be centred too — otherwise a short
   lede ends up centred within a left-hand column. */
.page-head .lede {
  margin-left: auto;
  margin-right: auto;
}

/* -------------------------------------------------------- notched plates */

/* The signature shape: a rectangle with two corners sliced off. Drawn as a
   border + clip-path pair so the cut edge keeps its outline. */
.plate {
  position: relative;
  background: var(--panel);
  border: 2px solid var(--line);
  clip-path: polygon(
    var(--notch) 0,
    100% 0,
    100% calc(100% - var(--notch)),
    calc(100% - var(--notch)) 100%,
    0 100%,
    0 var(--notch)
  );
  padding: 22px;
}

.plate-flat {
  background: var(--panel-2);
}

.plate-tight {
  padding: 14px 16px;
}

.plate-glow {
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.plate-glow:hover {
  border-color: var(--accent);
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(255, 46, 59, 0.25), 0 14px 34px rgba(255, 46, 59, 0.18);
}

/* The story page's prose plate. Sized up from the body's 16px — three short
   paragraphs alone in a big panel read as rattling around in it at body size. */
.story-prose {
  padding: 28px 30px;
  font-size: clamp(17px, 1.5vw, 19px);
  line-height: 1.75;
}

.story-prose p {
  margin-bottom: 1.2em;
}

.story-prose p:last-child {
  margin-bottom: 0;
}

/* A hairline rule that fades at both ends. */
.rule {
  height: 2px;
  border: 0;
  margin: 18px 0;
  background: linear-gradient(90deg, transparent, var(--line-hi) 12%, var(--line-hi) 88%, transparent);
  opacity: 0.7;
}

.rule-red {
  background: linear-gradient(90deg, transparent, var(--red) 12%, var(--red) 88%, transparent);
  opacity: 1;
}

/* Numbered section header — "01 / ROSTER" with a rule running to the edge. */
.sec-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 24px;
}

.sec-head .num {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--red);
  padding-top: 6px;
}

.sec-head .bar {
  flex: 1 1 auto;
  height: 2px;
  background: var(--line);
  margin-bottom: 6px;
}

/* --------------------------------------------------------------- buttons */

.btn {
  --btn-bg: var(--panel-2);
  --btn-fg: var(--fg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 18px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 2px solid var(--line);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
}

.btn:hover {
  border-color: var(--red);
  color: #fff;
  background: var(--red);
  transform: translateY(-2px);
}

.btn:active {
  transform: translateY(0);
}

.btn-primary {
  --btn-bg: var(--red);
  --btn-fg: #fff;
  border-color: var(--red);
}

.btn-primary:hover {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}

.btn-ghost {
  --btn-bg: transparent;
}

.btn-sm {
  padding: 8px 12px;
  font-size: 11px;
}

.btn[disabled] {
  opacity: 0.4;
  pointer-events: none;
}

/* -------------------------------------------------------------- masthead */

/* The wordmark ships in two cuts — bone-on-dark and ink-on-light — because a
   pixel logo can't be recoloured with `filter` without wrecking its drop
   shadow. Both sit in the markup and the theme picks one. Used by the masthead
   and the footer. */
.wordmark .wm-light {
  display: none;
}

html.light .wordmark .wm-dark {
  display: none;
}

html.light .wordmark .wm-light {
  display: block;
}

/* The logo sits centred at the top of the page with the tabs in a plate
   underneath it — no bar pinned across the top. The mark is the first thing
   on every page, at the size the page deserves. */

.masthead {
  position: relative;
  padding: 44px 0 0;
}

/* A red bloom behind the wordmark, strongest on the home page. */
.masthead::before {
  content: '';
  position: absolute;
  top: -220px;
  left: 50%;
  width: 820px;
  height: 620px;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(255, 46, 59, 0.14), transparent);
  pointer-events: none;
}

.masthead-home::before {
  background: radial-gradient(closest-side, rgba(255, 46, 59, 0.24), transparent);
  width: 980px;
  height: 740px;
}

html.light .masthead::before {
  background: radial-gradient(closest-side, rgba(255, 46, 59, 0.1), transparent);
}

.masthead .shell {
  position: relative;
}

/* One size on every page. The wordmark used to be cut down to 230px away from
   the home page, which made every other tab look like a lesser version of the
   site rather than another room in it.
   533px is thirteen times the wordmark's own 41-pixel grid. Any width that is
   not a whole multiple of it renders some columns of the letterform a pixel
   wider than others — at the old 430px it was 10.5x, and the uneven strokes are
   visible on a mark this chunky. */
.masthead-logo {
  display: block;
  position: relative;
  width: min(533px, 86vw);
  margin: 0 auto 24px;
  /* Room either side so the glitch displacement isn't clipped, and a clip so
     the scanline sweep starts and ends off the mark. */
  padding: 0 6px;
  overflow: hidden;
  transition: transform 0.2s ease;
}

.masthead-logo:hover {
  transform: translateY(-2px);
}

/* A soft band drifting down the wordmark, always running. This is the motion
   you notice — the glitch below is the punctuation. Kept to ~10% white so it
   reads as a CRT refresh passing over rather than a highlight sweeping. */
.masthead-logo::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 16%;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent,
    rgba(255, 255, 255, 0.1) 40%,
    rgba(53, 226, 242, 0.09) 60%,
    transparent
  );
  animation: wordmark-sweep 4.5s linear infinite;
}

html.light .masthead-logo::after {
  background: linear-gradient(
    180deg,
    transparent,
    rgba(11, 12, 16, 0.07) 40%,
    rgba(255, 46, 59, 0.08) 60%,
    transparent
  );
}

@keyframes wordmark-sweep {
  0% {
    transform: translateY(-110%);
  }
  100% {
    transform: translateY(730%);
  }
}

.masthead-logo img {
  width: 100%;
  height: auto;
  image-rendering: pixelated;
  /* Still for four and a half seconds, then half a second of signal trouble.
     Rare enough not to become wallpaper, often enough to actually be seen. */
  animation: wordmark-glitch 5s linear infinite;
}

/* Hard cuts, not slides: each step holds its value until the next one, so the
   displacement snaps the way a dropped frame would rather than easing. The
   offsets stay within a few pixels and the colour fringes are translucent —
   interference, not strobing. */
@keyframes wordmark-glitch {
  0%,
  89.9% {
    transform: translate3d(0, 0, 0);
    filter: none;
    opacity: 1;
  }
  90%,
  91.4% {
    transform: translate3d(-4px, 0, 0);
    filter: drop-shadow(4px 0 0 rgba(53, 226, 242, 0.55));
    opacity: 0.85;
  }
  91.5%,
  92.9% {
    transform: translate3d(4px, 0, 0);
    filter: drop-shadow(-4px 0 0 rgba(255, 46, 59, 0.6));
    opacity: 1;
  }
  93%,
  93.9% {
    transform: translate3d(-2px, 1px, 0);
    filter: drop-shadow(3px 0 0 rgba(53, 226, 242, 0.4));
    opacity: 0.9;
  }
  94%,
  95.4% {
    transform: translate3d(2px, -1px, 0);
    filter: drop-shadow(-2px 0 0 rgba(255, 46, 59, 0.45));
    opacity: 1;
  }
  95.5%,
  96.4% {
    transform: translate3d(-1px, 0, 0);
    filter: drop-shadow(1px 0 0 rgba(53, 226, 242, 0.3));
    opacity: 1;
  }
  96.5%,
  100% {
    transform: translate3d(0, 0, 0);
    filter: none;
    opacity: 1;
  }
}


/* The founding line, sitting between the motto and the tabs. */
.masthead-est {
  text-align: center;
  margin: 10px 0 26px;
}

/* ------------------------------------------------------------------ tabs */

/* Full width, matching the crew strip below it so the two panels line up.
   Three columns — an empty spacer the width of the toggle, the tabs, then the
   toggle — which is what keeps the tabs optically centred rather than pushed
   left by the button on the right. */
.tabs {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 36px;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  width: 100%;
}

/* Spread edge to edge of the plate rather than bunched in the middle. The gap
   is the minimum, not the spacing — once the row wraps, space-between would
   push a short last line apart, so the wrapped case falls back to centred. */
.tabs-row {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 7px;
  min-width: 0;
}

.tabs .icon-btn {
  grid-column: 3;
  justify-self: end;
}

/* Only earned its place when the plate hugged its contents; in the wide
   layout the tabs and the toggle are already far apart. */
.tab-sep {
  display: none;
}

.tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 11px 17px;
  background: var(--panel-2);
  border: 2px solid var(--line);
  color: var(--fg-mute);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease,
    transform 0.16s ease, box-shadow 0.16s ease;
}

.tab:hover {
  color: var(--fg);
  border-color: var(--red);
  transform: translateY(-2px);
}

.tab.active {
  background: var(--red);
  border-color: var(--red);
  color: #fff;
  box-shadow: 0 0 0 1px rgba(255, 46, 59, 0.3), 0 6px 18px rgba(255, 46, 59, 0.26);
}

@media (max-width: 720px) {
  /* Drop the balancing spacer and let the wrapped rows use the full width,
     with the toggle pinned to the top-right of the plate. */
  .tabs {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    padding: 10px;
  }
  /* Narrow enough that the row always wraps, so spreading it would strand the
     last line's tabs at opposite ends of the plate. */
  .tabs-row {
    grid-column: 1;
    justify-content: center;
  }
  .tabs .icon-btn {
    grid-column: 2;
  }
  .tab {
    flex: 1 1 auto;
    padding: 11px 10px;
    font-size: 10px;
    letter-spacing: 0.12em;
  }
  .masthead {
    padding-top: 30px;
  }
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  background: var(--panel-2);
  border: 2px solid var(--line);
  color: var(--fg);
  cursor: pointer;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  transition: border-color 0.16s ease, color 0.16s ease, background 0.16s ease;
}

.icon-btn:hover {
  border-color: var(--red);
  color: var(--red);
}

/* ---------------------------------------------------------------- ticker */

/* The motto, running edge to edge. Two copies of the track slide left in
   lockstep so the loop never shows a seam. */
.ticker {
  overflow: hidden;
  border-top: 2px solid var(--line);
  border-bottom: 2px solid var(--line);
  background: var(--red);
  color: #fff;
  padding: 9px 0;
}

/* renderTicker() sets the duration inline once it has measured the track —
   see js/core/ui.js. The value here only covers the gap before that runs. */
.ticker-track {
  display: flex;
  width: max-content;
  animation: ticker-slide 26s linear infinite;
  will-change: transform;
}

.ticker-track span {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  padding: 0 22px;
  white-space: nowrap;
}

.ticker:hover .ticker-track {
  animation-play-state: paused;
}

@keyframes ticker-slide {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

.ticker-dark {
  background: var(--panel);
  color: var(--fg);
}

/* ------------------------------------------------------------------ hero */

/* The hero picks up under the masthead — the wordmark and the tabs are already
   above it, so this is the expansion, the lede, the calls to action and the
   group banner. */
.hero {
  position: relative;
  padding: 34px 0 48px;
}

.hero-inner {
  position: relative;
  text-align: center;
}

/* "CREATE OWN RUN EVERYTHING" spelled out under the mark, one word per cell,
   each with its initial in red. */
.expand {
  display: flex;
  justify-content: center;
  gap: clamp(8px, 2.4vw, 26px);
  flex-wrap: wrap;
  margin-top: 14px;
}

.expand span {
  font-family: var(--mono);
  font-size: clamp(11px, 1.7vw, 15px);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fg-mute);
  opacity: 0;
  transform: translateY(6px);
  animation: expand-in 0.5s ease forwards;
}

.expand span b {
  color: var(--red);
  font-weight: 800;
}

@keyframes expand-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero-band {
  margin: 34px auto 0;
  max-width: 1000px;
}

.hero-band img {
  width: 100%;
  image-rendering: pixelated;
  border: 2px solid var(--line);
}

/* ---- the crew: six standing figures, names underneath -------------------
   Each figure is its own link. No overlay box on hover — the character strikes
   a pose and the name goes red, which reads as interactive without covering
   the art. */

.crew {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0;
  align-items: end;
  padding: 26px 10px 0;
  border: 2px solid var(--line);
  background: var(--panel);
}

.crew-member {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

/* The two frames are stacked, not laid out one after the other: the standing
   one holds the space and the pose sits on top of it, so the swap can't move
   anything around it. */
.crew-art {
  position: relative;
  display: block;
  width: 100%;
  max-width: 132px;
}

.crew-member img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

.crew-pose {
  position: absolute;
  inset: 0;
  opacity: 0;
}

/* Cut, don't fade. A crossfade between two pixel drawings ghosts the arms
   through each other; swapping outright reads as a frame of animation. */
.crew-member:hover .crew-still,
.crew-member:focus-visible .crew-still {
  opacity: 0;
}

.crew-member:hover .crew-pose,
.crew-member:focus-visible .crew-pose {
  opacity: 1;
}

/* The red floor line is these borders sitting shoulder to shoulder, so it runs
   unbroken across the strip while each name stays inside its own link. */
.crew-name {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 11px 4px 14px;
  border-top: 3px solid var(--red);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  color: var(--fg-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.18s ease;
}

.crew-member:hover .crew-name,
.crew-member:focus-visible .crew-name {
  color: var(--red);
}

@media (max-width: 860px) {
  .crew {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    row-gap: 18px;
    padding-top: 20px;
  }
}

@media (max-width: 460px) {
  .crew {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .crew-name {
    font-size: 10px;
    letter-spacing: 0.04em;
  }
}

/* ---- clickable group banner ---------------------------------------------
   The strip is a single image, so each character gets a transparent link laid
   over their slice. Positions come from data-group.js (written by the pixel
   generator), so they can't drift out of sync with the artwork. */

.group-band {
  position: relative;
  line-height: 0;
}

.band-hot {
  position: absolute;
  top: 0;
  bottom: 0;
  border: 2px solid transparent;
}

/* Nothing is drawn on hover — no label, no outline, no tint. The banner is one
   picture and it stays one picture; the cursor turning into a pointer is the
   only thing that says a character is clickable.

   Keyboard focus is the exception. It never fires from the mouse, and without
   a ring there'd be no way to tell which of the six is selected while tabbing
   through them. */
.band-hot:focus-visible {
  border-color: var(--accent);
}

/* ------------------------------------------------------------ stat strip */

.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border: 2px solid var(--line);
  background: var(--panel);
}

.stats > div {
  padding: 18px 16px;
  border-right: 2px solid var(--line);
  text-align: center;
}

.stats > div:last-child {
  border-right: 0;
}

.stat-n {
  font-family: var(--display);
  font-size: clamp(22px, 3.4vw, 34px);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--fg);
  display: block;
}

.stat-k {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-mute);
}


/* Three-tile variant. Used by the home strip and the live board's console. */
.stats-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 700px) {
  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .stats > div:nth-child(2n) {
    border-right: 0;
  }
  .stats > div:nth-child(-n + 2) {
    border-bottom: 2px solid var(--line);
  }
  /* The odd third tile runs the full width rather than leaving a hole. */
  .stats-3 > div:nth-child(3) {
    grid-column: 1 / -1;
    border-right: 0;
  }
}

/* ------------------------------------------------------------ member card */

.member {
  display: block;
  position: relative;
  background: var(--panel);
  border: 2px solid var(--line);
  overflow: hidden;
  clip-path: polygon(
    14px 0,
    100% 0,
    100% calc(100% - 14px),
    calc(100% - 14px) 100%,
    0 100%,
    0 14px
  );
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.member:hover {
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}

/* The card is an <a> wrapping spans, so every part needs an explicit display —
   aspect-ratio and padding are both no-ops on an inline box. */
.member-art,
.member-body,
.member-name,
.member-real,
.member-role {
  display: block;
}

.member-art {
  position: relative;
  aspect-ratio: 1;
  background: var(--panel-2);
  overflow: hidden;
}

.member-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
  transition: transform 0.3s ease;
}

/* Every character on the site blinks at random — the same face with the eyes
   shut, flashed over the still for ~160ms at a time. The timing lives in
   mountBlinks() (js/core/ui.js): CSS animations can only repeat a fixed
   cycle, and the blinks land at random 2–7s intervals. The frames snap on
   and off with no transition — a crossfade between two pixel faces just
   looks like the drawing is out of focus. */
.member-blink,
.blink-frame,
.crew-blink {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}

.member:hover .member-art img,
.member:focus-visible .member-art img {
  transform: scale(1.06);
}

/* While the crew figure is mid-pose the standing blink frame is the wrong
   body — hold it off until the pointer leaves. Visibility rather than
   opacity, because the blink driver owns the inline opacity. */
.crew-member:hover .crew-blink,
.crew-member:focus-visible .crew-blink {
  visibility: hidden;
}

/* The member's accent colour bleeds in from the bottom on hover. */
.member-art::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 4px;
  background: var(--accent);
}

.member-body {
  padding: 16px;
}

.member-name {
  font-family: var(--display);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.member-real {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--fg-mute);
  margin-top: 3px;
}

.member-role {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin-top: 10px;
}

.member-meta {
  display: flex;
  gap: 14px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 2px solid var(--line);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-mute);
}

/* The index number sitting in the corner of each card. */
.member-idx {
  position: absolute;
  top: 0;
  right: 0;
  padding: 5px 10px;
  background: var(--accent);
  color: #fff;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.14em;
  z-index: 2;
}

/* ---------------------------------------------------------------- videos */

.video {
  display: block;
  position: relative;
  background: var(--panel);
  border: 2px solid var(--line);
  overflow: hidden;
  clip-path: polygon(
    14px 0,
    100% 0,
    100% calc(100% - 14px),
    calc(100% - 14px) 100%,
    0 100%,
    0 14px
  );
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.video:hover {
  border-color: var(--red);
  transform: translateY(-4px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}

.video-thumb {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--panel-3);
  overflow: hidden;
}

.video-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.35s ease;
}

.video:hover .video-thumb img {
  transform: scale(1.05);
}

/* A play glyph that fills red on hover, drawn with borders so it costs nothing. */
.video-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, rgba(11, 12, 16, 0) 45%, rgba(11, 12, 16, 0.72) 100%);
}

.video-play::after {
  content: '';
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 13px 0 13px 22px;
  border-color: transparent transparent transparent #fff;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.6));
  transform: scale(0.9);
  opacity: 0.85;
  transition: transform 0.2s ease, opacity 0.2s ease, border-left-color 0.2s ease;
}

.video:hover .video-play::after {
  transform: scale(1.15);
  opacity: 1;
  border-left-color: var(--red);
}

/* Runtime-free badges sitting on the thumbnail. */
.video-tag {
  position: absolute;
  top: 0;
  left: 0;
  padding: 5px 10px;
  background: var(--red);
  color: #fff;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  z-index: 2;
}

.video-views {
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 4px 9px;
  background: rgba(11, 12, 16, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: #fff;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  z-index: 2;
}

.video-body {
  display: block;
  padding: 15px 16px;
}

.video-title {
  display: block;
  font-weight: 700;
  font-size: 15px;
  line-height: 1.35;
  /* Two lines, then ellipsis — titles on this channel run long. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.video-meta {
  display: flex;
  gap: 14px;
  margin-top: 10px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-mute);
}

/* The newest upload, given the full width above the grid. */
.video-feature {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  align-items: stretch;
}

.video-feature .video-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 26px 28px;
  border-left: 2px solid var(--line);
}

.video-feature .video-title {
  font-family: var(--display);
  font-size: clamp(19px, 2.2vw, 27px);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.2;
  -webkit-line-clamp: 3;
}

.video-feature .video-desc {
  margin-top: 12px;
  color: var(--fg-mute);
  font-size: 14px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

@media (max-width: 780px) {
  .video-feature {
    grid-template-columns: minmax(0, 1fr);
  }
  .video-feature .video-body {
    border-left: 0;
    border-top: 2px solid var(--line);
    padding: 18px 18px 22px;
  }
}

/* The home page's live heading. Not a numbered section — it comes and goes
   with whether anyone is streaming, so it reads as an alert rather than a
   fixed part of the page. */
.onair {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 13px;
  background: var(--red);
  color: #fff;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

.onair .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  animation: pulse 1.4s ease-in-out infinite;
}

/* ----------------------------------------------------------- status board */

/* Console: the stat strip with a control bar welded onto the bottom of it. */
.console {
  border: 2px solid var(--line);
  background: var(--panel);
}

/* The console's strip sits flush inside the panel — see .stats-3 for the
   three-column layout it shares with the home page. */
.console .stats {
  border: 0;
  border-bottom: 2px solid var(--line);
}

.console-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 14px;
  background: var(--panel-2);
}

/* The refresh glyph spins only while a fetch is actually in flight. */
.refresh-icon {
  width: 13px;
  height: 13px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
}

.btn.is-busy .refresh-icon {
  animation: spin 0.7s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---- live cards: portrait beside the stream, not a thumbnail grid ---- */

.live-card {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  background: var(--panel);
  border: 2px solid var(--line);
  border-left: 6px solid var(--accent);
  overflow: hidden;
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.live-card:hover {
  border-color: var(--red);
  border-left-color: var(--accent);
  transform: translateY(-3px);
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
}

.live-art {
  position: relative;
  display: block;
  background: var(--panel-2);
  border-right: 2px solid var(--line);
}

.live-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

.live-body {
  display: block;
  padding: 14px 16px;
  min-width: 0;
}

.live-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.live-viewers {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--red);
  white-space: nowrap;
}

.live-name {
  display: block;
  font-family: var(--display);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.live-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 6px;
  font-size: 14px;
  color: var(--fg-mute);
}

.live-meta {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 2px solid var(--line);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-mute);
}

@media (max-width: 520px) {
  .live-card {
    grid-template-columns: 88px minmax(0, 1fr);
  }
}

/* ---- offline: a dense list, not a second grid of cards ---- */

.off-list {
  border: 2px solid var(--line);
  background: var(--panel);
}

.off-row {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  border-bottom: 2px solid var(--line);
  border-left: 4px solid transparent;
  transition: background 0.16s ease, border-left-color 0.16s ease;
}

.off-row:last-child {
  border-bottom: 0;
}

.off-row:hover {
  background: var(--panel-2);
  border-left-color: var(--accent);
}

.off-row img {
  border: 2px solid var(--line);
}

/* The wrapper that lets the tiny offline portrait carry a blink overlay. The
   overlay gets a transparent border so its art lines up with the base image's
   bordered box. */
.off-art {
  position: relative;
  display: block;
}

.off-art .blink-frame {
  border: 2px solid transparent;
}

/* In the profile panel the strip under the portrait is part of the box — the
   overlay must scale with the image alone, not the image plus strip. */
.profile-art .blink-frame {
  height: auto;
}

.off-name {
  font-family: var(--display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.off-tag {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-mute);
  white-space: nowrap;
}


/* ---------------------------------------------------------------- badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border: 2px solid var(--line);
  background: var(--panel-2);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.badge-live {
  border-color: var(--red);
  color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, transparent);
}

.badge-live .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
  animation: pulse 1.4s ease-in-out infinite;
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.35;
    transform: scale(0.75);
  }
}

.badge-gold {
  border-color: var(--gold);
  color: var(--gold);
}

.badge-cyan {
  border-color: var(--cyan);
  color: var(--cyan);
}

/* ----------------------------------------------------------- social tiles */

.socials {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}

.social {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 13px 14px;
  background: var(--panel-2);
  border: 2px solid var(--line);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
}

.social:hover {
  transform: translateY(-2px);
  border-color: var(--brandc, var(--red));
  background: color-mix(in srgb, var(--brandc, var(--red)) 10%, var(--panel-2));
}

.social svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--brandc, var(--fg));
}

.social-txt {
  min-width: 0;
}

.social-net {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-mute);
  display: block;
}

.social-handle {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* brand colours, applied per tile via --brandc */
.n-twitch {
  --brandc: #9146ff;
}
.n-youtube {
  --brandc: #ff0033;
}
.n-x {
  --brandc: #7a8290;
}
.n-instagram {
  --brandc: #e4405f;
}
.n-tiktok {
  --brandc: #25f4ee;
}
.n-kick {
  --brandc: #53fc18;
}
.n-patreon {
  --brandc: #ff6b4a;
}
.n-discord {
  --brandc: #5865f2;
}
.n-email {
  --brandc: var(--gold);
}
.n-linktree {
  --brandc: #43e660;
}

/* ------------------------------------------------------------ member page */

.profile {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}

/* The portrait column is much shorter than the bio, so pin it while the copy
   scrolls past. Nothing is fixed to the top of the viewport, so it can sit
   close to the edge. */
.profile > :first-child {
  position: sticky;
  top: 20px;
}

@media (max-width: 860px) {
  .profile {
    grid-template-columns: minmax(0, 1fr);
  }
  .profile > :first-child {
    position: static;
  }
}

.profile-art {
  border: 2px solid var(--line);
  background: var(--panel-2);
  position: relative;
}

.profile-art img {
  width: 100%;
  image-rendering: pixelated;
}

.profile-art .strip {
  height: 6px;
  background: var(--accent);
}

.fact-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.fact-list li {
  display: flex;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 13px;
}

.fact-list li:last-child {
  border-bottom: 0;
}

.fact-list dt,
.fact-k {
  flex: 0 0 118px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-mute);
  padding-top: 2px;
}

.fact-v {
  flex: 1 1 auto;
  min-width: 0;
}

/* A vertical run of dated events with a rail down the left. */
.timeline {
  position: relative;
  margin: 0;
  padding: 0 0 0 30px;
  list-style: none;
}

.timeline::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--line);
}

.timeline li {
  position: relative;
  padding: 0 0 22px;
}

.timeline li::before {
  content: '';
  position: absolute;
  left: -30px;
  top: 7px;
  width: 14px;
  height: 14px;
  background: var(--bg);
  border: 2px solid var(--red);
}

.timeline li:last-child {
  padding-bottom: 0;
}

.tl-when {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red);
}

.tl-what {
  font-weight: 700;
  margin-top: 2px;
}

.tl-note {
  color: var(--fg-mute);
  font-size: 14px;
  margin-top: 2px;
}

/* --------------------------------------------------------------- pixel bits */

.pix {
  image-rendering: pixelated;
}

/* Sticker row: the props from the banner, gently bobbing. */
.stickers {
  display: flex;
  justify-content: center;
  gap: clamp(10px, 3vw, 34px);
  flex-wrap: wrap;
  padding: 10px 0;
}

/* Sized by height, not width. The stickers are no longer all squares — a rifle
   is four times as wide as it is tall — and matching their widths would print
   the rifle at the size of a postage stamp next to a football the size of a
   plate. Equal heights is what makes a mixed row read as one set. */
.stickers img {
  height: clamp(44px, 6vw, 68px);
  width: auto;
  max-width: 100%;
  image-rendering: pixelated;
  animation: bob 3.4s ease-in-out infinite;
}

/* Except where the art is drawn on a much taller grid than the rest.
   Equal heights only reads as one set while the stickers are drawn at roughly
   one scale — around twelve to fifty pixels tall. A sixty-nine pixel figure
   forced to the same height renders at about 1:1 while a thirty-two pixel ball
   beside it renders at better than 2:1, so its pixels come out half the size
   and it reads as a different, finer set rather than as a bigger sticker.
   Giving it proportionally more height puts the two back on one pixel scale.
   Add to this list if another tall one turns up. */
.stickers img[src$='sticker-itachi.svg'] {
  height: clamp(88px, 12vw, 138px);
}

/* The award is drawn on a 64x70 grid — twice the resolution of the props
   around it — so it takes proportionally more height for the same reason. */
.stickers img[src$='sticker-award.svg'] {
  height: clamp(76px, 10vw, 116px);
}

/* The sheet on the brand page wraps each sticker in a download link, so the
   stagger keys off the direct child rather than the <img> itself. */
.stickers > :nth-child(2n) img,
.stickers > img:nth-child(2n) {
  animation-delay: -0.6s;
}
.stickers > :nth-child(3n) img,
.stickers > img:nth-child(3n) {
  animation-delay: -1.3s;
}

@keyframes bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-7px);
  }
}

/* ---------------------------------------------------------------- footer */

.footer {
  border-top: 2px solid var(--line);
  background: var(--panel);
  margin-top: 60px;
  padding: 44px 0 28px;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 32px;
}

@media (max-width: 800px) {
  .footer-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }
}

.footer h4 {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin-bottom: 12px;
}

.footer-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

.footer-links a {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--fg);
  transition: color 0.16s ease, padding-left 0.16s ease;
}

.footer-links a:hover {
  color: var(--red);
  padding-left: 5px;
}

/* The build credit under the wordmark. Mono and small, so it sits with the
   motto above it rather than competing with the tagline for attention — the
   two names are the only part meant to be clicked. */
.built-by {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--fg-mute);
}

.built-by a {
  color: var(--fg);
  border-bottom: 1px solid var(--line-hi);
  transition: color 0.16s ease, border-color 0.16s ease;
}

.built-by a:hover,
.built-by a:focus-visible {
  color: var(--red);
  border-bottom-color: var(--red);
}

.footer-bottom {
  margin-top: 34px;
  padding-top: 18px;
  border-top: 2px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-mute);
  letter-spacing: 0.08em;
}

/* -------------------------------------------------------------- utilities */

.center {
  text-align: center;
}

.mt0 {
  margin-top: 0;
}
.mt8 {
  margin-top: 8px;
}
.mt16 {
  margin-top: 16px;
}
.mt24 {
  margin-top: 24px;
}
.mt32 {
  margin-top: 32px;
}
.mb0 {
  margin-bottom: 0;
}
.mb8 {
  margin-bottom: 8px;
}
.mb16 {
  margin-bottom: 16px;
}
.mb24 {
  margin-bottom: 24px;
}

.wrap-anywhere {
  overflow-wrap: anywhere;
}

/* Cards fade up as they scroll into view; the stagger comes from an inline
   delay. The starting state is scoped to `.js-reveal`, which the module sets on
   <html> once it's running — without JS the content is simply visible rather
   than stuck at opacity 0. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(14px);
}

.rise {
  animation: rise-in 0.44s ease forwards;
}

@keyframes rise-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Skip link, visible only once focused. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 16px;
  background: var(--red);
  color: #fff;
  font-family: var(--mono);
  font-size: 12px;
}

.skip:focus {
  left: 12px;
  top: 12px;
}

/* --------------------------------------------------------------- forms */

.field {
  display: block;
  margin-bottom: 16px;
}

.field span {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin-bottom: 7px;
}

.field input,
.field textarea,
.field select {
  width: 100%;
  padding: 12px 14px;
  background: var(--panel-2);
  border: 2px solid var(--line);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 14px;
}

.field textarea {
  min-height: 150px;
  resize: vertical;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--red);
}

.note {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-mute);
}

/* ------------------------------------------------------------- copy chip */

.copy {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  background: var(--panel-2);
  border: 2px solid var(--line);
  font-family: var(--mono);
  font-size: 13px;
  cursor: pointer;
  transition: border-color 0.16s ease;
}

.copy:hover {
  border-color: var(--gold);
}

.copy[data-copied='1'] {
  border-color: var(--cyan);
  color: var(--cyan);
}

/* ------------------------------------------------------------ brand kit */

/* The kit sits between two rails of stickers — the props flank the assets,
   and each one downloads on click. */
.brand-layout {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr) 84px;
  gap: 24px;
  align-items: stretch;
}

.brand-main > * + * {
  margin-top: 48px;
}

.brand-rail {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 22px;
  align-items: center;
  padding: 18px 10px;
  background: var(--panel);
  border: 2px solid var(--line);
}

.brand-rail a {
  display: block;
  width: 100%;
  transition: transform 0.15s ease;
}

.brand-rail a:hover,
.brand-rail a:focus-visible {
  transform: scale(1.15);
}

.brand-rail img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

@media (max-width: 980px) {
  .brand-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  .brand-main {
    order: 1;
  }
  .brand-rail {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    order: 2;
  }
  .brand-rail a {
    width: 56px;
  }
}

/* ---- the logo builder ---- */

.builder-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 5vw, 48px);
  flex-wrap: wrap;
  padding: 38px 22px;
  border: 2px solid var(--line);
  transition: background 0.2s ease;
}

#wm-preview {
  width: min(340px, 62%);
  image-rendering: pixelated;
}

#mark-preview {
  width: clamp(48px, 8vw, 72px);
  image-rendering: pixelated;
}

.builder-controls {
  display: flex;
  gap: 30px;
  flex-wrap: wrap;
  margin-top: 20px;
}

.chip-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.chip-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 2px solid var(--line);
  cursor: pointer;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  transition: transform 0.12s ease, border-color 0.12s ease;
}

.chip-btn:hover {
  transform: translateY(-2px);
  border-color: var(--line-hi);
}

.chip-btn.sel {
  border-color: var(--red);
  box-shadow: 0 0 0 2px rgba(255, 46, 59, 0.35);
}

/* ---- portrait tiles ---- */

.portrait-tile {
  background: var(--panel);
  border: 2px solid var(--line);
  overflow: hidden;
  transition: border-color 0.16s ease, transform 0.16s ease;
}

.portrait-tile:hover {
  border-color: var(--accent);
  transform: translateY(-3px);
}

.pt-art {
  position: relative;
  display: block;
  background: var(--panel-2);
}

.pt-art img {
  display: block;
  width: 100%;
  image-rendering: pixelated;
}

.pt-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border-top: 2px solid var(--line);
}

.pt-name {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------- swatch grid */

.swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}

.swatch {
  border: 2px solid var(--line);
}

.swatch .chip {
  height: 74px;
}

.swatch .meta {
  padding: 10px 12px;
  background: var(--panel-2);
  font-family: var(--mono);
  font-size: 11px;
}

.swatch .meta b {
  display: block;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 10px;
}

.swatch .meta span {
  color: var(--fg-mute);
}
