/* ============================================================
   BC&V FREE AGENCY
   Shared stylesheet for the signing announcement series.

   Palette is the BC&V brand guide (24 Oct 2023) verbatim:
     #405069 dusty blue (primary)   #B3B3B3 silver (primary)
     #4AA6BC cyan (secondary)       #000000 / #FFFFFF

   This file is the single source of truth for the look. Signing pages carry
   content only. Do not fork it per signing.
   ============================================================ */

/* ============================================================
   WEBFONT

   Montserrat, self-hosted. Until this block existed the CSS named Montserrat
   and nothing ever loaded it, so every page fell through the stack to whatever
   the machine happened to have. On Windows that meant Century Gothic, which is
   not remotely the brand face and is what readers actually saw.

   Self-hosted rather than linked from Google Fonts: no third-party request per
   pageview, and the type still arrives on a network that blocks external font
   CDNs, which matters once this sits behind Cloudflare Access.

   One variable file per subset covers weights 400 to 800, the entire range
   these pages use. latin-ext is a separate face with its own unicode-range, so
   the browser only fetches it if a page actually contains one of those
   characters; ordinary English copy pays for the 38 KB latin file alone.

   font-display: swap paints the fallback immediately and repaints when the real
   face lands, so an announcement is never invisible while it loads.

   Montserrat is licensed under the SIL Open Font License 1.1. The licence
   travels with the files as fonts/OFL.txt. Keep it next to them.
   ============================================================ */

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/montserrat-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/montserrat-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --brand-blue:   #405069;
  --brand-silver: #B3B3B3;
  --brand-cyan:   #4AA6BC;

  /* Single committed palette: home whites. Neutrals are biased toward the
     brand dusty blue so the greys read as chosen rather than default.
     --accent is the brand cyan darkened to #237E95, which clears 4.7:1 on
     white, so it works both as text and as a chip ground with white on it. */
  --ground:       #FFFFFF;
  --surface:      #F2F4F7;
  --surface-2:    #E7EBF0;
  --line:         #DDE2E9;
  --line-strong:  #AEB7C4;
  --ink:          #10151C;
  --ink-2:        #405069;
  --ink-3:        #6B7684;
  --accent:       #237E95;
  --accent-ink:   #FFFFFF;
  --jersey-body:  #FFFFFF;
  --jersey-trim:  #405069;
  --jersey-num:   #405069;
  --jersey-name:  #405069;
  --old:          #8A919C;

  /* Montserrat is BC&V's brand body face. The display treatment is built from
     weight, case and tracking rather than a condensed face, because a
     condensed face silently falls back.

     The fallback chain is ordered so that IF Montserrat is missing the page
     still lands on a grotesk with a two-storey 'a' and normal-width round
     letters. Century Gothic was removed from it deliberately: it ships on
     every Windows machine, so it beat Segoe UI in the old order and won
     outright whenever Montserrat was absent. Its single-storey 'a' and very
     wide 'o' are nothing like Montserrat, and it was what actually rendered.
     Verify with a width measurement, not document.fonts.check, which reports
     true for names it cannot resolve. */
  --font-display: "Montserrat", "Segoe UI Variable Display", "Avenir Next",
                  -apple-system, "Segoe UI", system-ui, sans-serif;
  --font-body:    "Montserrat", "Segoe UI Variable Text", "Avenir Next",
                  -apple-system, "Segoe UI", system-ui, sans-serif;
  --font-mono:    ui-monospace, "Cascadia Code", "Consolas",
                  "SF Mono", Menlo, monospace;

  --shell: 1120px;

  /* Radius language, applied deliberately rather than everywhere. Small
     interactive controls get 2px so they read as pressable; structural panels
     (cards, quotes, stats, table, roster) stay square, which is what makes the
     hairline grid read as drawn rather than rounded off. Mixing the two at
     random was the single loudest unfinished tell on the page. */
  --r: 2px;

  /* Shadows are stacked, tight to wide. One big soft blur is the cheap tell;
     a real falloff curve is what makes an object sit on the page. */
  --lift-1: 0 1px 1px rgba(16, 21, 28, 0.04),
            0 2px 4px rgba(64, 80, 105, 0.05);
  --lift-2: 0 1px 1px rgba(16, 21, 28, 0.05),
            0 3px 6px rgba(64, 80, 105, 0.06),
            0 10px 20px rgba(64, 80, 105, 0.07);

  /* One motion vocabulary, so hovers across the page share a feel. */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --t-fast: 140ms;
  --t-mid: 240ms;

  /* These pages commit to one visual world (home whites) by design, so a
     viewer's OS dark preference must not repaint them into a scheme they were
     never laid out for. */
  color-scheme: light;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
}

/* A 2px accent rule pinned to the very top edge of every page. It is the same
   accent, and the same gesture, as the cold open's wire-rule wipe: the intro
   draws the line, the page keeps it. Broadcast graphics do this and it costs
   one pseudo-element. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: 2px;
  background: var(--accent);
  z-index: 80;
}

.shell {
  width: min(100% - 2.5rem, var(--shell));
  margin-inline: auto;
}

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

.eyebrow {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0;
}

.num { font-variant-numeric: tabular-nums; }

/* Letter-spacing is applied after every glyph including the last, so any
   tracked uppercase label sitting inside a box or a centred line carries dead
   space on its right that the eye reads as a misalignment. Every tracked chip
   and button below subtracts its own tracking from its right padding. Nothing
   about this is visible on its own; together it is most of the difference
   between set type and typed type. */

p { text-wrap: pretty; }

a { color: var(--accent); }

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

/* ============================================================
   COLD OPEN  (breaking news bomb, then the page beneath)
   ============================================================ */

/* The page is light, but the cold open is not. A breaking-news alert is a
   dark broadcast graphic, and resolving from black into the white page is
   the transition. Tokens are redefined locally so every child inherits the
   dark values without needing its own override. */
.coldopen {
  --ground:      #0A0B0D;
  --ink:         #F2F4F7;
  --ink-3:       #77808D;
  --line-strong: #38414D;
  --accent:      #4AA6BC;
  --accent-ink:  #0A0B0D;

  position: fixed;
  inset: 0;
  z-index: 90;

  /* Not a flat black fill. A low-chroma tonal grade lifted slightly toward the
     brand dusty blue, centred a little above the middle so the wire copy sits
     in the light part and the corners fall away. Same palette, no new hue. */
  background:
    radial-gradient(115% 85% at 50% 36%,
                    #15191F 0%, #0C0E12 58%, #06070A 100%),
    var(--ground);
  color: var(--ink);
  place-items: center;
  padding: 1.5rem;

  /* Any click dismisses, not just the button, so the whole surface says so. */
  cursor: pointer;

  /* Only scripting can dismiss a fixed full-screen overlay, so without it the
     overlay never appears at all rather than trapping the reader behind it. */
  display: none;
}

/* Micro-noise over the grade. Large flat dark areas band badly on 8-bit
   panels, and a trace of grain both hides the banding and reads as broadcast
   rather than as a div. Absolutely positioned so it leaves the grid flow and
   does not fight place-items: center. */
.coldopen::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
.js .coldopen { display: grid; }
.js .coldopen[hidden] { display: none; }
.coldopen.out {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.55s ease-out;
}

.coldopen-inner { width: min(100%, 760px); }

.wire-rule {
  height: 3px;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(0);
  animation: wipe 0.45s cubic-bezier(0.4, 0, 0.1, 1) forwards;
}

.wire-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.1rem;
  opacity: 0;
  animation: rise 0.4s ease-out 0.35s forwards;
}

.chip-breaking {
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  padding-right: calc(0.6rem - 0.2em); /* subtract the trailing tracking */
  border-radius: var(--r);
}

.wire-src {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* On a phone the source line has nowhere to go beside the chip and breaks
   mid-timestamp, orphaning "CT". Stack it instead. */
@media (max-width: 460px) {
  .wire-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }
}

.wire-head .chip-breaking { animation: pulse 1.6s ease-in-out infinite; }

.wire-body {
  font-size: clamp(1.5rem, 4.4vw, 2.85rem);
  line-height: 1.16;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 1.4rem 0 0;
  text-wrap: balance;
  opacity: 0;
  animation: rise 0.55s ease-out 0.65s forwards;
}
.wire-body b {
  font-weight: 700;
  color: var(--accent);
  white-space: nowrap;
}

.wire-foot {
  margin-top: 1.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  opacity: 0;
  animation: rise 0.4s ease-out 1.15s forwards;
}

.skip {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  background: none;
  border: 1px solid var(--line-strong);
  color: var(--ink-3);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.5rem 0.9rem;
  padding-right: calc(0.9rem - 0.16em);
  cursor: pointer;
  border-radius: var(--r);
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.skip:hover { color: var(--ink); border-color: var(--ink-3); }

@keyframes wipe { to { transform: scaleX(1); } }
@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* ============================================================
   MASTHEAD
   ============================================================ */

.masthead { border-bottom: 1px solid var(--line); }
.masthead .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.35rem 0 1.25rem; /* extra on top to clear the pinned accent rule */
  flex-wrap: wrap;
}
.mark {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  text-decoration: none;
}
.mark-b {
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--ink);
}
.mark-s {
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.mast-actions { display: flex; gap: 0.5rem; align-items: center; }
.btn-ghost {
  background: none;
  border: 1px solid var(--line-strong);
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.5rem 0.85rem;
  padding-right: calc(0.85rem - 0.14em);
  border-radius: var(--r);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.btn-ghost:hover {
  color: var(--ink);
  border-color: var(--ink-3);
  background: var(--surface);
}

/* ============================================================
   HERO / JERSEY
   ============================================================ */

.hero { padding: clamp(3rem, 8vw, 6rem) 0 clamp(2.5rem, 6vw, 4.75rem); }

.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.25rem, 5vw, 4rem);
  align-items: center;
}
@media (min-width: 860px) {
  /* The jersey is the only real object on the page, so it gets close to half
     the hero rather than the leftover column it had before. */
  .hero-grid { grid-template-columns: 1fr 0.9fr; }
}

.hero-kicker {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin-bottom: 1.4rem;
  flex-wrap: wrap;
}
.chip-signed {
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  padding-right: calc(0.6rem - 0.2em);
  border-radius: var(--r);
}

.hero h1 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 800;
  font-size: clamp(2.75rem, 9vw, 5.5rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
  margin: 0;
  text-wrap: balance;
}
.hero h1 .lo {
  color: var(--ink-3);
  display: block;
  font-size: 0.3em;
  font-weight: 700;
  letter-spacing: 0.14em;
  margin-bottom: 0.7rem;
}

.hero-sub {
  margin: 1.5rem 0 0;
  font-size: 1.0625rem;
  color: var(--ink-2);
  max-width: 46ch;
}

/* Fixed 2x2 rather than auto-fit. With four terms in a column this narrow,
   auto-fit resolved to three across and orphaned the fourth on its own row,
   leaving a hole under it; and at four across every value wrapped to two
   lines. Two columns is the only arrangement that stays balanced and keeps
   each value on one line. */
.terms {
  margin-top: 2rem;
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 1.75rem;
}
.term dt {
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.3rem;
}
.term dd {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink);
}

.jersey-wrap {
  display: grid;
  place-items: center;
  position: relative;
}

/* A soft spotlight in brand blue behind the jersey. On flat white the garment
   read as clip art pasted onto the page; a faint pool of tone under it gives
   it somewhere to sit. Low alpha on an existing brand colour, no new hue. */
.jersey-wrap::before {
  content: "";
  position: absolute;
  inset: -6% -4% 4%;
  background: radial-gradient(58% 54% at 50% 46%,
              rgba(64, 80, 105, 0.10) 0%,
              rgba(64, 80, 105, 0.05) 46%,
              rgba(64, 80, 105, 0) 73%);
  pointer-events: none;
}

.jersey {
  position: relative;
  width: min(100%, 420px);
  height: auto;
  /* Stacked, tight to wide. The single 36px blur it had before was uniformly
     soft, which is what made it read as a sticker rather than cloth. */
  filter: drop-shadow(0 1px 1px rgba(16, 21, 28, 0.06))
          drop-shadow(0 6px 10px rgba(64, 80, 105, 0.08))
          drop-shadow(0 20px 34px rgba(64, 80, 105, 0.13));
}

.jersey-caption {
  margin-top: 1.4rem;
  text-align: center;
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  /* Centred tracked type sits half its tracking left of true centre, because
     the trailing space after the last glyph is inside the centred box. */
  text-indent: 0.09em;
}

/* ============================================================
   SECTIONS
   ============================================================ */

.band { border-top: 1px solid var(--line); }
.band-fill { background: var(--surface); }

/* Roughly 40 percent more air top and bottom than before. Nothing else about
   these sections changed; the extra space is most of why the page reads calmer.
   Kept as one clamp so the cadence between every band stays identical. */
.sec { padding: clamp(3.5rem, 7.5vw, 6.5rem) 0; }

.sec-head { margin-bottom: 2.75rem; }
.sec-head h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 800;
  font-size: clamp(1.625rem, 4vw, 2.375rem);
  line-height: 0.98;
  letter-spacing: -0.025em;
  margin: 0.6rem 0 0;
  text-wrap: balance;
}
.sec-head p { margin: 1rem 0 0; color: var(--ink-2); max-width: 62ch; }

/* ---------- combine stats ---------- */

.combine {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.stat { background: var(--ground); padding: 2.25rem 1.75rem; }
.stat-v {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 7vw, 3.5rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stat-v.hot { color: var(--accent); }
.stat-u {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 0.6rem;
}
.stat-n { margin-top: 0.5rem; font-size: 0.875rem; color: var(--ink-2); }

/* ---------- tale of the tape ---------- */

.tape-scroll { overflow-x: auto; }

table.tape {
  width: 100%;
  min-width: 620px;
  border-collapse: collapse;
  font-size: 0.9375rem;
}
table.tape th, table.tape td {
  padding: 1.15rem 1rem;
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
/* Tracking the eye across eight rows of a three-column comparison is the whole
   job of this table, so the row under the cursor lifts very slightly. */
table.tape tbody tr { transition: background-color var(--t-fast) var(--ease); }
table.tape tbody tr:hover { background: var(--surface); }
table.tape thead th {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 700;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 0.75rem;
}
table.tape thead th.c-new { color: var(--accent); }
table.tape tbody th {
  font-weight: 600;
  color: var(--ink-2);
  width: 32%;
}
td.c-old { color: var(--old); }
td.c-new { color: var(--ink); font-weight: 600; }
table.tape tbody tr:last-child th,
table.tape tbody tr:last-child td { border-bottom: none; }

.tape-note {
  margin-top: 1.1rem;
  font-size: 0.8125rem;
  color: var(--ink-3);
  max-width: 70ch;
}

/* ---------- scouting report ---------- */

.scout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@media (min-width: 800px) {
  .scout { grid-template-columns: 1fr 1fr; }
}
.card {
  border: 1px solid var(--line);
  background: var(--ground);
  padding: 2.25rem;
}
.card h3 {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin: 0 0 1.25rem;
  color: var(--ink-3);
  font-weight: 700;
}
.card h3 .dot {
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 0.55rem;
  vertical-align: middle;
}
.dot-pro { background: var(--accent); }
.dot-dev { background: var(--brand-silver); }

.card ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.95rem; }
.card li {
  padding-left: 1.1rem;
  position: relative;
  font-size: 0.9375rem;
  color: var(--ink-2);
  line-height: 1.55;
}
.card li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 1px;
  background: var(--line-strong);
}
.card li b { color: var(--ink); font-weight: 600; }

.coach {
  margin-top: 1.5rem;
  border-left: 2px solid var(--accent);
  padding: 0.35rem 0 0.35rem 1.1rem;
  font-size: 0.9375rem;
  color: var(--ink-2);
}
.coach b { color: var(--ink); font-weight: 600; }

/* ---------- system fit ---------- */

.fit {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.75rem;
}
@media (min-width: 860px) {
  .fit { grid-template-columns: 1fr 1fr; }
}
.fit-block h3 {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.9rem;
  font-weight: 700;
}
.fit-block p { margin: 0 0 0.9rem; color: var(--ink-2); font-size: 0.9375rem; }
.fit-block p.tight { margin-top: 0.9rem; }

.calls { display: grid; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.calls li {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  padding: 0.6rem 0.8rem;
  color: var(--ink);
  border-radius: var(--r);
}
.calls li::before { content: "\201C"; color: var(--ink-3); }
.calls li::after  { content: "\201D"; color: var(--ink-3); }

code.inline {
  font-family: var(--font-mono);
  font-size: 0.875rem;
}

.outfile {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  padding: 0.7rem 0.8rem;
  color: var(--ink);
  border-radius: var(--r);
  overflow-x: auto;
  white-space: nowrap;
}

/* ---------- press conference ---------- */

.pressers { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 800px) { .pressers { grid-template-columns: 1fr 1fr; } }
.pressers.solo { margin-top: 1.5rem; }
.pressers.solo .quote { grid-column: 1 / -1; }

.quote {
  border: 1px solid var(--line);
  padding: 2.25rem;
  background: var(--ground);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.quote blockquote {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--ink);
  text-wrap: pretty;
}
.quote figcaption { margin-top: auto; }
.q-name {
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--ink);
  letter-spacing: 0.01em;
}
.q-role {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 0.25rem;
}
/* The skill's own quote is the one deliberate accent moment on the light page:
   the only gradient, so it reads as chosen rather than as decoration, and it
   marks the single card whose words needed nobody's approval. */
.quote.player {
  border-color: var(--accent);
  background:
    linear-gradient(180deg,
      rgba(35, 126, 149, 0.05) 0%,
      rgba(35, 126, 149, 0.016) 40%,
      rgba(35, 126, 149, 0) 100%),
    var(--ground);
}
.quote.player .q-role { color: var(--accent); }

/* ---------- roster grid ---------- */

.roster {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.slot {
  background: var(--ground);
  padding: 1.5rem 1.25rem 1.6rem;
  min-height: 140px;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  text-decoration: none;
  color: inherit;
  transition: background-color var(--t-fast) var(--ease);
}
a.slot:hover { background: var(--surface); }
.slot-n {
  font-family: var(--font-display);
  font-size: 1.625rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.slot.filled .slot-n { color: var(--accent); }
.slot-t {
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--ink);
  letter-spacing: 0.01em;
}
.slot.open .slot-t { color: var(--ink-3); font-weight: 500; }
.slot-m {
  margin-top: auto;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.slot.filled .slot-m { color: var(--accent); }

.roster-more { margin-top: 1.25rem; font-size: 0.875rem; }

/* ============================================================
   INDEX PAGE
   ============================================================ */

.index-hero { padding: clamp(3.5rem, 9vw, 7rem) 0 clamp(2.5rem, 5vw, 4rem); }
.index-hero h1 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 800;
  font-size: clamp(2.5rem, 8vw, 4.75rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  margin: 0.7rem 0 0;
  text-wrap: balance;
}
.index-hero p {
  margin: 1.4rem 0 0;
  font-size: 1.0625rem;
  color: var(--ink-2);
  max-width: 56ch;
}

.signings {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.signing-row {
  background: var(--ground);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.4rem 1.5rem;
  padding: 1.85rem 1.5rem;
  align-items: start;
  text-decoration: none;
  color: inherit;
  transition: background-color var(--t-fast) var(--ease);
}
@media (min-width: 720px) {
  .signing-row { grid-template-columns: 4.5rem 1fr auto; align-items: center; }
}
a.signing-row:hover { background: var(--surface); }
.sr-n {
  font-family: var(--font-display);
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  transition: transform var(--t-mid) var(--ease);
}
/* The roster number leads the row, so it is what moves when the row is live.
   Two pixels; enough to feel, not enough to notice. */
a.signing-row:hover .sr-n { transform: translateX(2px); }
.signing-row.open .sr-n { color: var(--line-strong); }
.sr-body h2 {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.signing-row.open .sr-body h2 { color: var(--ink-3); font-weight: 500; }
.sr-body p {
  margin: 0.35rem 0 0;
  font-size: 0.9375rem;
  color: var(--ink-2);
  max-width: 60ch;
}
.sr-meta {
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
a.signing-row .sr-meta { color: var(--accent); }

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

.foot { border-top: 1px solid var(--line); padding: 3.5rem 0 4.5rem; }
.foot-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}
@media (min-width: 800px) { .foot-grid { grid-template-columns: 1.4fr 1fr; } }
.foot h3 {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.8rem;
  font-weight: 700;
}
.foot p {
  margin: 0 0 0.8rem;
  font-size: 0.8125rem;
  color: var(--ink-3);
  line-height: 1.65;
  max-width: 62ch;
}
.foot p.legal { font-size: 0.75rem; opacity: 0.8; }

/* ---------- scroll reveal ---------- */

/* Scoped to .js, which the inline snippet in each page's head sets. Without
   scripting nothing is ever hidden, so a page whose JavaScript is disabled or
   fails to load still renders in full rather than going blank. */
.js .rv { opacity: 0; transform: translateY(14px); }
.js .rv.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s ease-out, transform 0.55s var(--ease);
}

/* ---------- staggered reveal ---------- */

/* Multi-child blocks arrive in sequence instead of as one slab. The container
   itself stops animating (otherwise its fade and each child's fade multiply
   and the first frames go double-transparent) and hands the motion to its
   children.

   Only containers whose children are standalone bordered blocks are listed.
   The hairline grids (.combine, .roster, .signings) draw their dividers as a
   line-coloured container background showing through 1px gaps, so fading their
   cells in one at a time would expose that grey underneath as a flash. Those
   deliberately stay whole-block reveals.

   Needs no markup change: it keys off the container classes already there. */
.js .scout.rv,
.js .pressers.rv,
.js .fit.rv {
  opacity: 1;
  transform: none;
}
.js .scout.rv > *,
.js .pressers.rv > *,
.js .fit.rv > * {
  opacity: 0;
  transform: translateY(12px);
}
.js .scout.rv.in > *,
.js .pressers.rv.in > *,
.js .fit.rv.in > * {
  opacity: 1;
  transform: none;
  transition: opacity 0.5s ease-out, transform 0.5s var(--ease);
}
.js .scout.rv.in > :nth-child(2),
.js .pressers.rv.in > :nth-child(2),
.js .fit.rv.in > :nth-child(2) { transition-delay: 90ms; }
.js .scout.rv.in > :nth-child(3),
.js .pressers.rv.in > :nth-child(3),
.js .fit.rv.in > :nth-child(3) { transition-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  .js .rv, .js .rv.in { opacity: 1; transform: none; transition: none; }

  /* The staggered children are hidden by their own rules, so clearing the
     container is not enough; without this they would never appear at all for a
     reader who asked for less motion. */
  .js .scout.rv > *,
  .js .pressers.rv > *,
  .js .fit.rv > *,
  .js .scout.rv.in > *,
  .js .pressers.rv.in > *,
  .js .fit.rv.in > * {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }

  .wire-rule, .wire-head, .wire-body, .wire-foot {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .coldopen { display: none; }

  /* Every hover and nudge added for polish is motion too. */
  .sr-n, .slot, .signing-row, .btn-ghost, .skip, table.tape tbody tr {
    transition: none;
  }
  a.signing-row:hover .sr-n { transform: none; }
}
