/* =============================================================================
   base.css — reset, element defaults, page chrome, and the shared widget kit.
   Everything here is available to BOTH modes. The game agent should reuse
   .btn, .chip, .card, .eyebrow, .panel, .scroll-x and .sr-only rather than
   re-inventing them, so the two modes look like one site.
   Depends on: tokens.css (load it first).
   ========================================================================== */

/* --- reset ---------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Sticky header must not cover the target of an in-page jump. */
  scroll-padding-top: calc(var(--header-h) + var(--sp-5));
}

body {
  margin: 0;
  /* Explicit ground: the host paints behind the page, and a transparent body
     borrows a theme that is not ours. */
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  font-weight: var(--weight-regular);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  /* No horizontal body scroll at any width. `clip` rather than `hidden` so
     position: sticky inside the page still works. */
  overflow-x: clip;
}

/* The mown-pitch ground. SHOOTOUT ONLY. Read mode is a document on paper, not
   a match on grass, so the stripes only exist while the game is mounted.
   Fixed, behind everything, decorative, cheap. */
html[data-mode="shootout"] body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      100deg,
      var(--turf-stripe) 0 72px,
      transparent 72px 144px
    );
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul {
  margin: 0;
}

ul[class], ol[class] { list-style: none; padding: 0; }

img, svg, video, canvas {
  display: block;
  max-width: 100%;
  height: auto;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; }

/* Inline links are underlined, always. An underline is the oldest and clearest
   signal on the web that a word is a link; colour alone is not, and dropping
   the underline is one of the tells of a generated design. The offset and
   thinner stroke keep it from cutting descenders.

   Components that are already unmistakably interactive — pills, buttons, cards,
   nav — opt out individually with `text-decoration: none`. */
a {
  color: var(--brand-ink);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in oklab, currentColor 45%, transparent);
  transition: text-decoration-color var(--dur-1) var(--ease-out);
}
a:hover { text-decoration-color: currentColor; }

:target { scroll-margin-top: calc(var(--header-h) + var(--sp-6)); }

/* --- focus ---------------------------------------------------------------- */
/* One ring everywhere. Vermilion on light, amber on dark; both clear 3:1
   against every surface they can land on. */

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
:focus:not(:focus-visible) { outline: none; }

/* --- reduced motion ------------------------------------------------------- */
/* The token switch handles travel distance; this handles anything that slipped
   through, including third-party-free but still-animated CSS of our own. */

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

/* --- typographic primitives ---------------------------------------------- */

/* --font-heading is the body face in read mode and the condensed display face
   in shootout mode — see the MODE block at the foot of tokens.css. Headings
   are NOT uppercased and NOT condensed here; sentence case in the page's own
   text face is what a written document looks like. */
.display,
h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

h4, h5, h6 {
  font-family: var(--font-body);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
}

p { text-wrap: pretty; }

/* The small label above a heading. Sentence case and lightly tracked: it is a
   quiet signpost, not a lower-third graphic. The broadcast treatment — caps,
   condensed, wide tracking — is restored for the game at the foot of this file. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--ink-muted);
}
.eyebrow__num {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-normal);
  color: var(--accent-ink);
  padding: 0.15em 0.45em;
  border: 1px solid var(--accent-edge);
  background: var(--accent-tint);
  border-radius: var(--radius-xs);
}

.lede {
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  color: var(--ink);
  max-width: var(--measure-narrow);
}

.prose > p + p { margin-top: var(--sp-6); }
.prose p {
  max-width: var(--measure);
  color: var(--ink-muted);
}
/* Inline links inside running text keep the underline from the element
   default and take the brand colour — nothing else. */
.prose a { color: var(--brand-ink); }

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

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--wrap-narrow); }

/* Wide content scrolls inside its own container, never the page. */
.scroll-x {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- skip link ------------------------------------------------------------ */

.skip-link {
  position: absolute;
  top: var(--sp-3);
  left: var(--sp-3);
  z-index: var(--z-top);
  transform: translateY(calc(-200% * var(--motion-ok)));
  opacity: 0;
  pointer-events: none;
  padding: var(--sp-3) var(--sp-6);
  border-radius: var(--radius-pill);
  background: var(--accent-strong);
  color: var(--chalk-0);
  font-family: var(--font-body);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  text-decoration: none;
  box-shadow: var(--shadow-3);
  transition: transform var(--dur-2) var(--ease-out),
              opacity var(--dur-2) var(--ease-out);
}
.skip-link:focus-visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

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

.btn {
  --btn-bg: var(--brand);
  --btn-fg: var(--brand-on);
  --btn-edge: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-7);
  min-height: 2.75rem;          /* comfortable touch target                  */
  border: 1px solid var(--btn-edge);
  border-radius: var(--radius-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              background-color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out);
}
.btn:hover { box-shadow: var(--shadow-2); }
.btn:active { transform: translateY(calc(1px * var(--motion-ok))); }

.btn--accent { --btn-bg: var(--accent-strong); --btn-fg: var(--chalk-0); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--accent { --btn-fg: var(--accent-on); }
}

.btn--quiet {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-edge: var(--line-strong);
  box-shadow: none;
}
.btn--quiet:hover {
  --btn-edge: var(--brand);
  --btn-fg: var(--brand-ink);
  background: var(--brand-tint);
  box-shadow: none;
}

.btn--small {
  padding: var(--sp-2) var(--sp-5);
  min-height: 2.25rem;
  font-size: var(--step--1);
}

/* --- chips ---------------------------------------------------------------- */
/* The pill used for tech, coursework and any short tag. */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.2em 0.7em;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.6;
  letter-spacing: var(--tracking-normal);
  white-space: nowrap;
}
.chip--brand  { background: var(--brand-tint);  border-color: var(--brand-edge);  color: var(--brand-ink); }
.chip--accent { background: var(--accent-tint); border-color: var(--accent-edge); color: var(--accent-ink); }
.chip--signal { background: var(--signal-tint); border-color: var(--signal-edge); color: var(--signal-ink); }
.chip--info   { background: var(--info-tint);   border-color: var(--info-edge);   color: var(--info-ink); }

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: 0;
  margin: 0;
  list-style: none;
}

/* --- cards and panels ----------------------------------------------------- */

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}

/* A tinted inset block for secondary information inside a card. */
.panel {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--sp-5);
}

/* --- page chrome ---------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  background: color-mix(in oklab, var(--bg) 82%, transparent);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: var(--sp-3) var(--gutter);
}

/* Wordmark: monogram tile + name. CSS-drawn, no image and therefore no request.
   Read mode gets a flat brand tile — a monogram, the way a person initials a
   letter. The floodlit ball-panel version is restored for the game below. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  text-decoration: none;
  color: var(--ink);
  min-width: 0;
}
.wordmark__mark {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--brand);
  color: var(--brand-on);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  box-shadow: var(--shadow-1);
}
.wordmark__text {
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 26.25rem) {
  .wordmark__text { display: none; }
}

/* --- mode switch ---------------------------------------------------------- */
/* The game is the default landing experience, so read mode is what someone
   switches TO on purpose — and the way back to the game has to be just as
   findable. Two things make the round trip obvious in both directions:
   the switch itself, and a cue beside it that always names the OTHER mode.
   Neither ever says only where you are; both say where you can go. */

.mode-switch {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex: none;
  min-width: 0;
}

/* The cue. Always the invitation to the mode you are NOT in. Both lines are
   hidden until main.js has set html[data-mode], so there is no flash of both.
   aria-hidden: the toggle button already carries this information properly
   for assistive technology via its label and aria-pressed state. */
.mode-cue {
  display: none;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--step--1);
  color: var(--ink-muted);
  white-space: nowrap;
}
@media (min-width: 48rem) {
  .mode-cue { display: flex; }
}
.mode-cue__line { display: none; }
html[data-mode="site"]     .mode-cue__line--play { display: inline; }
html[data-mode="shootout"] .mode-cue__line--read { display: inline; }
/* An arrow pointing at the switch, drawn from two borders. */
.mode-cue::after {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  flex: none;
  border-top: 1.5px solid var(--ink-faint);
  border-right: 1.5px solid var(--ink-faint);
  transform: rotate(45deg);
}

/* A real switch: two labelled stops and a knob that slides between them, so it
   reads as a toggle at a glance rather than as a button that might be a link.
   aria-pressed is maintained by main.js; all state here is driven off it. */

.mode-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 3px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  cursor: pointer;
  box-shadow: var(--shadow-inset);
  transition: border-color var(--dur-2) var(--ease-out),
              background-color var(--dur-2) var(--ease-out);
}
.mode-toggle:hover { border-color: var(--brand); }

/* The knob. Sits under the labels and slides on aria-pressed. */
.mode-toggle__knob {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc(50% - 3px);
  border-radius: var(--radius-pill);
  background: linear-gradient(140deg, var(--pitch-500), var(--pitch-700));
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-3) var(--ease-emphatic),
              background var(--dur-3) var(--ease-out);
  pointer-events: none;
}
.mode-toggle[aria-pressed="true"] .mode-toggle__knob {
  transform: translateX(100%);
  background: linear-gradient(140deg, var(--flare-400), var(--flare-600));
}

.mode-toggle__opt {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  /* Fixed and equal so the knob's 50% travel lands exactly. */
  width: 5.25rem;
  min-height: 2rem;
  justify-content: center;
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--ink-muted);
  transition: color var(--dur-2) var(--ease-out);
}
.mode-toggle[aria-pressed="false"] .mode-toggle__opt--site,
.mode-toggle[aria-pressed="true"]  .mode-toggle__opt--play { color: var(--chalk-0); }

/* The stop you are NOT on must not read as a dead label. It darkens to full
   ink on hover and carries a hairline of its own, so both halves of the switch
   look like places you can go. */
.mode-toggle[aria-pressed="true"]  .mode-toggle__opt--site,
.mode-toggle[aria-pressed="false"] .mode-toggle__opt--play {
  color: var(--ink);
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}
.mode-toggle:hover[aria-pressed="true"]  .mode-toggle__opt--site,
.mode-toggle:hover[aria-pressed="false"] .mode-toggle__opt--play {
  color: var(--brand-ink);
  box-shadow: inset 0 0 0 1px var(--brand);
  background: var(--brand-tint);
}

/* Tiny CSS-drawn glyphs so the switch reads at a glance without an icon pack. */
.mode-toggle__dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  background: currentColor;
  opacity: 0.85;
}
.mode-toggle__opt--site .mode-toggle__dot {
  /* three stacked rules = a page of text */
  height: 0.55rem;
  background:
    linear-gradient(currentColor 0 0) 0 0 / 100% 2px no-repeat,
    linear-gradient(currentColor 0 0) 0 50% / 100% 2px no-repeat,
    linear-gradient(currentColor 0 0) 0 100% / 70% 2px no-repeat;
}
.mode-toggle__opt--play .mode-toggle__dot { border-radius: 50%; }

@media (max-width: 22.5rem) {
  .mode-toggle__opt { width: 4rem; padding-inline: var(--sp-3); }
  .mode-toggle__dot { display: none; }
}

/* --- player number -------------------------------------------------------- */
/* A squad number on a shirt back: fixed plate, big numerals, name-arch caption.
   Presentation only — the count comes from js/visitors.js via js/site.js.

   THE SPACE IS RESERVED, THE CONTENT IS NOT RENDERED UNTIL IT IS REAL.
   The wrapper's width and height are fixed and identical in every state, so
   the header never reflows whether the number arrives, arrives late, or never
   arrives at all. `visibility: hidden` (not `display: none`) is what holds the
   box open, and it also removes the widget from the accessibility tree, so an
   unresolved counter is silent as well as invisible. There is no spinner and
   no placeholder digit: an unknown count shows nothing, never a zero. */

.player-number {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
  width: 2.75rem;            /* plate only; the caption is hidden here        */
  height: 2.5rem;
  visibility: hidden;        /* the reserved-but-empty default state          */
}
@media (min-width: 56rem) {
  .player-number { width: calc(2.75rem + var(--sp-3) + 7rem); }
}
.player-number[data-state="ready"] { visibility: visible; }

.player-number__plate {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.5rem;
  flex: none;
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  background: var(--brand-tint);
  color: var(--brand-ink);
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--step-1);
  line-height: 1;
  letter-spacing: var(--tracking-tighter);
  font-variant-numeric: tabular-nums;
}
/* Longer counts step down rather than overflowing the plate. data-len is set
   by site.js from the digit count, so the plate width never has to change. */
.player-number__plate[data-len="3"] { font-size: var(--step-0); }
.player-number__plate[data-len="4"] { font-size: var(--step--1); }
.player-number__plate[data-len="5"],
.player-number__plate[data-len="6"] { font-size: var(--step--2); }

/* "≈" when the count is not exact. Small, raised, never mistaken for a digit. */
.player-number__approx {
  font-size: 0.62em;
  opacity: 0.75;
  margin-right: 0.04em;
}

/* Below 56rem only the plate is reserved, so the caption must not take part in
   layout at all. Above it, the caption gets a fixed column and clamps to two
   lines, so a long label cannot change the widget's footprint either. */
.player-number__caption { display: none; }
@media (min-width: 56rem) {
  .player-number__caption {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    width: 7rem;
    overflow: hidden;
    font-size: var(--step--2);
    line-height: var(--leading-snug);
    color: var(--ink-faint);
  }
}

/* --- boot fallback -------------------------------------------------------- */
/* Lives inside #app-root and is removed by main.js on a successful mount. If
   scripts are off or the module graph fails, it is what a visitor gets, so it
   carries the three things worth having. */

.boot-note {
  max-width: var(--measure-narrow);
  margin: var(--sp-11) auto;
  padding: var(--sp-7);
  text-align: center;
  color: var(--ink-muted);
  font-size: var(--step--1);
}
.boot-note ul {
  list-style: none;
  padding: 0;
  margin: var(--sp-5) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  justify-content: center;
}

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

.site-footer {
  border-top: 1px solid var(--line);
  margin-top: var(--section-gap);
  padding-block: var(--sp-8) var(--sp-9);
  color: var(--ink-faint);
  font-size: var(--step--1);
}
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.site-footer a { color: var(--ink-muted); }

/* In shootout mode the canvas owns the viewport; page chrome stays, the
   footer would only be in the way. */
html[data-mode="shootout"] .site-footer { display: none; }

/* =============================================================================
   SHOOTOUT RESTORES — the broadcast voice, scoped to the game.

   Everything below is a treatment read mode deliberately does not have: the
   condensed display face, all-caps labels, wide tracking, the floodlit
   monogram. The game is a sports broadcast and should look like one; a
   personal site being read by a recruiter on a phone should not.

   Nothing here introduces new components. Each rule re-applies a treatment to
   a shared component that read mode has quietened, so the two modes stay one
   site with two voices rather than two codebases.

   The pitch-stripe ground is scoped the same way, up at the top of this file,
   and --font-heading flips to --font-display in tokens.css.
   ========================================================================== */

html[data-mode="shootout"] .eyebrow {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
}

html[data-mode="shootout"] .btn,
html[data-mode="shootout"] .skip-link {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

html[data-mode="shootout"] .wordmark__mark {
  background:
    radial-gradient(circle at 30% 25%, var(--pitch-400) 0%, transparent 55%),
    linear-gradient(145deg, var(--pitch-600), var(--pitch-800));
  color: var(--chalk-0);
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: var(--weight-black);
  box-shadow: var(--shadow-1), inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
html[data-mode="shootout"] .wordmark__text {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}

/* On the pitch the squad number is worn, not printed on a form. */
html[data-mode="shootout"] .player-number__plate {
  border-color: transparent;
  background: linear-gradient(145deg, var(--pitch-600), var(--pitch-800));
  color: var(--chalk-0);
  font-family: var(--font-display);
  box-shadow: var(--shadow-1), inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
