/*
  scorex.tech — shared styles for the pages a person browses: /, /support/, /privacy/.

  EVERY VALUE BELOW TRACES TO `Branding/scorex-tokens.json`. That file is the system of record
  and it is explicit about the things that are easy to get subtly wrong:

    color        "Every colour used anywhere must resolve to color.neutral or color.court."
                 There is no separate status channel — no green success, no red error.
    typography   three faces, three duties, and "Condensed/display type never spreads into
                 body copy, settings, or long-form admin text."
    the mark     "theme colour drives the pip / checker only, never the X"
    radius       card 12 / button 10 / input 8 / dialog 16 — a LOCKED scale, not a preference
    spacing      2 4 8 12 16 24 32 48 64 96 — also LOCKED

  If you are about to add a hex literal here, it is almost certainly wrong: use a variable, and
  if no variable fits, the question is for the tokens file, not for this stylesheet.

  DELIBERATELY NOT USED BY /c/index.html OR 404.html. Those two must render correctly when
  everything else about this deployment is broken:

    /c/<code>   is what a scanned court sticker reaches. A printed label is permanent, so
                that page has to keep working through any future rebuild of this site.
    404.html    is load-bearing for App Link verification (see its own header). A 404 that
                depends on a stylesheet request is a 404 with a second way to go wrong.

  Both keep their own inline styles. That duplication is the point; do not "tidy" it away.
*/

/* ---- type ---------------------------------------------------------------
   Self-hosted: this host makes no third-party requests, and a page that says the product
   needs no internet should not itself depend on a font CDN. Built by build_fonts.py from the
   exact TTFs the Android app ships, so site and app cannot drift onto different cuts.
   `swap`: text is readable immediately in the fallback and reflows when Plex lands — the
   alternative is invisible copy on a slow club connection. */

@font-face {
  font-family: "Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/fonts/plex-sans-400.woff2") format("woff2");
}
@font-face {
  font-family: "Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/fonts/plex-sans-600.woff2") format("woff2");
}
@font-face {
  font-family: "Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/fonts/plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "Plex Condensed"; font-style: italic; font-weight: 700; font-display: swap;
  src: url("/fonts/plex-cond-700i.woff2") format("woff2");
}

:root {
  color-scheme: dark;

  /* color.neutral.dark — the ramp, with the token's own description for each step */
  --carbon: #0d0f13;   /* 950  page background            */
  --surface: #171a21;  /* 900  card / surface background  */
  --raised: #1e222a;   /* 800  raised surface / hover     */
  --line: #262a32;     /* 700  border, hairline           */
  --line-strong: #2e333d; /* 600 border, stronger         */
  --muted: #6b707a;    /* 300  secondary text, mono labels */
  --body: #9aa1ae;     /* 200  tertiary text              */
  --ink: #edf0f4;      /* 0    primary text / headline    */

  /* color.court.primary.options.blue — the default. The ONLY role beyond neutral, and the
     token file is emphatic that serve/advantage/fault/win/live are all this colour at
     different tints, never a separate meaning-colour. */
  --accent: #2e6be6;

  --font-read: "Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-data: "Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-display: "Plex Condensed", "Plex Sans", -apple-system, BlinkMacSystemFont, sans-serif;

  /* layout.radius_scale — LOCKED */
  --r-card: 12px;
  --r-btn: 10px;
  --r-input: 8px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--carbon);
  color: var(--body);
  font: 400 16px/1.65 var(--font-read);
  -webkit-font-smoothing: antialiased;
}

/* `height: auto` is load-bearing, not tidiness. Every <img> here carries width/height
   attributes (they reserve the box and stop the page reflowing as images arrive), and those
   attributes become a presentational height. Without this rule the browser honours that
   literal pixel height while max-width shrinks the width, and the hero renders 556 px wide by
   its full 1024 px tall — stretched, and pushing everything below it off the fold. */
img { max-width: 100%; height: auto; display: block; }
a { color: var(--accent); }

.wrap { width: min(100% - 3rem, 68rem); margin-inline: auto; }
.narrow { width: min(100% - 3rem, 44rem); margin-inline: auto; }

/* typography.family.display — "italic, bold, tracked", scoped to section headers and big
   display moments. h3 is NOT display: a card title sits next to body copy, and the token
   rule forbids condensed type spreading there. */
h1, h2 {
  font-family: var(--font-display);
  font-style: italic; font-weight: 700; letter-spacing: .005em;
  color: var(--ink); line-height: 1.1;
}
h1 { font-size: clamp(2.25rem, 5.5vw, 3.5rem); margin: 0 0 1rem; }
h2 { font-size: clamp(1.5rem, 2.8vw, 2rem); margin: 0 0 1rem; }
h3 {
  font-family: var(--font-read); font-weight: 600; font-size: 1.0625rem;
  color: var(--ink); line-height: 1.3; margin: 0 0 .375rem;
}

/* typography.family.data — "mono labels". Every uppercase micro-label on the site is one. */
.eyebrow {
  font-family: var(--font-data); font-size: .75rem;
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--muted); margin: 0 0 .875rem;
}

/* ---- header / footer ---------------------------------------------------- */

.site-head {
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
  background: color-mix(in srgb, var(--carbon) 88%, transparent);
  backdrop-filter: blur(8px);
}
.site-head .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 1rem 0;
}

/* The lockup on page 1 of `Branding/Identity Doc.pdf`: the standalone X, then the wordmark
   set in Plex — not the drawn wordmark in `Branding/Logo/Brand/`, which is the LEGACY
   speed-lines mark and is not this identity.

   THE X IS NEVER TINTED. tokens: "court.primary appears only as a ring/pip around it — never
   recolours the mark itself". An earlier version of this file set the X letter of "ScoreX" in
   the accent; it looked fine and was against the system. If the header wants brand colour, it
   goes on something that is not the mark. */
.brand {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: 1.25rem; font-weight: 600; letter-spacing: -.01em;
  color: var(--ink); text-decoration: none;
}
/* The mark is an <img> pointing at mark-x.svg, so it cannot inherit currentColor — the file
   is authored in ink (#edf0f4) instead. That is fine here and only here: this lockup is always
   ink on carbon. Anywhere the mark needs to take a colour from its context, inline the SVG. */
.brand img { height: 1.05em; width: auto; display: block; }

.site-head nav { display: flex; gap: 1.5rem; font-size: .9375rem; }
.site-head nav a { color: var(--body); text-decoration: none; }
.site-head nav a:hover { color: var(--ink); }

.site-foot {
  border-top: 1px solid var(--line);
  margin-top: 5rem; padding: 2.5rem 0 3.5rem;
  font-size: .875rem; color: var(--muted);
}
.site-foot .wrap { display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between; }
.site-foot nav { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.site-foot a { color: var(--body); text-decoration: none; }
.site-foot a:hover { color: var(--ink); }

/* ---- sections ----------------------------------------------------------- */

section { padding: 4.5rem 0; }
.lede { font-size: 1.125rem; color: var(--body); max-width: 40rem; }

/* ---- the section break --------------------------------------------------

   `graphic_language.crossing_paths_divider`: "two lines approaching and stopping just short of
   touching (the notch); used as section dividers, NEVER a plain rule." The gap is the point —
   it is the crossing notch of the mark, restated at page scale — and the X sits in it.

   This is also `principles.list[3]`, "never decorate": the divider is not an ornament, it is the
   same drawing the whole identity is built from, doing a structural job. A `border-top` here
   would be the generic thing the system explicitly rules out, which is what was here before.

   One gradient, hard stops, transparent through the middle. The percentage stops stay ordered
   at every viewport this site supports: at 375 px the gap edge is 153 px and the 18% stop is
   67 px, so they never cross over. */

:root {
  --rule-gap: 2.125rem;   /* half-width of the notch the mark sits in */
  --rule-mark: 0.9375rem;
}

section + section { position: relative; }

section + section::before,
.prose hr {
  content: "";
  display: block;
  border: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0,
    var(--line) 18%,
    var(--line) calc(50% - var(--rule-gap)),
    transparent calc(50% - var(--rule-gap)),
    transparent calc(50% + var(--rule-gap)),
    var(--line) calc(50% + var(--rule-gap)),
    var(--line) 82%,
    transparent 100%);
}
section + section::before { position: absolute; top: 0; left: 0; right: 0; }
.prose hr { position: relative; margin: 3rem 0 1.5rem; }

/* The mark in the notch. Dimmed rather than recoloured: it is the ink mark at low emphasis, not
   a grey second version of it — and per the tokens the accent may never touch the X. */
section + section::after,
.prose hr::after {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  width: var(--rule-mark); height: var(--rule-mark);
  transform: translate(-50%, -50%);
  background: url("/img/mark-x.svg") center / contain no-repeat;
  opacity: .45;
}

.btn {
  display: inline-block; padding: .8125rem 1.5rem; border-radius: var(--r-btn);
  font-family: var(--font-read); font-weight: 600; font-size: .9375rem; text-decoration: none;
  border: 1px solid var(--accent); background: var(--accent); color: #fff;
}
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn + .btn { margin-left: .75rem; }
.actions { margin-top: 2rem; }

/* The Play buttons are STUBS. Distribution is internal-testing-only, so there is no public
   listing URL to point at yet and a live-looking button would be a promise the store cannot
   keep. They render as unavailable and do not navigate — see the comment at each call site
   for the one-line change that turns them on. */
.btn.soon {
  background: transparent; color: var(--muted); border-color: var(--line);
  cursor: not-allowed;
}
.btn.soon:hover { color: var(--body); }
.btn .sub {
  display: block; font-family: var(--font-data); font-size: .6875rem; font-weight: 400;
  letter-spacing: .1em; text-transform: uppercase; opacity: .85;
}

/* ---- cards -------------------------------------------------------------- */

.grid { display: grid; gap: 1.5rem; }
.grid.two   { grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }

/* interaction.cards: "neutral.900 surface, neutral.700 border; court.primary appears only as
   an accent pip/ring, never a full-card wash." */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); overflow: hidden;
}
/* A fixed 4:3 window, so a portrait shot (the court-side controller) and the landscape ones
   line up in the same grid. `height: auto` lets aspect-ratio decide the height; object-fit
   crops to fill rather than letterboxing. Framing lives here, not in build_images.py — a crop
   you can see in a browser is a crop you can argue with. */
.card img {
  width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover;
  background: #fff;   /* the packaging renders sit on white; avoids a dark seam while loading */
}
.card .body { padding: 1.25rem 1.375rem 1.5rem; }
.card p { margin: 0; font-size: .9375rem; }

/* Step numbers are data, so they are mono. */
.steps { counter-reset: step; }
.step { counter-increment: step; }
.step h3::before {
  content: counter(step, decimal-leading-zero);
  display: inline-grid; place-items: center;
  width: 2rem; height: 2rem; margin-right: .625rem;
  border-radius: 50%; border: 1px solid var(--line-strong);
  font-family: var(--font-data); font-size: .75rem; color: var(--accent);
  vertical-align: -.5rem;
}

/* ---- prose pages (privacy, support) ------------------------------------- */

.prose { padding: 3.5rem 0 1rem; }
.prose h2 { margin: 2.5rem 0 .625rem; font-size: 1.25rem; }
.prose h2:first-of-type { margin-top: 2rem; }
.prose p { margin: 0 0 1rem; }
.prose ul { padding-left: 1.25rem; margin: 0 0 1rem; }
.prose li { margin-bottom: .4rem; }
.prose code { font-family: var(--font-data); font-size: .9em; color: var(--ink); }
.prose .updated { font-family: var(--font-data); font-size: .8125rem; color: var(--muted); margin-top: -.5rem; }
/* `.prose hr` is styled up in "the section break" — it is the same crossing-paths divider, not a
   rule. There must be no second declaration for it down here: this file cascades, and the plain
   `border-top` that used to live on this line silently reinstated the flat rule on top of the
   new divider, which computes as a 1px border and reads as a hairline through the mark. */

/* A note block, for the one or two places a caveat has to be impossible to skim past.
   The accent is a rail, not a wash — see interaction.cards above. */
.note {
  border: 1px solid var(--line); border-left: 2px solid var(--accent);
  background: var(--surface); border-radius: var(--r-input);
  padding: 1.125rem 1.25rem; margin: 0 0 1.5rem;
  font-size: .9375rem;
}
.note :last-child { margin-bottom: 0; }

/* ---- terse feature row --------------------------------------------------- */

.grid.four { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.terse { margin-top: 2.5rem; }
.terse p { margin: 0; font-size: .9375rem; color: var(--muted); }
.terse strong {
  display: block; margin-bottom: .25rem;
  font-family: var(--font-read); font-weight: 600; color: var(--ink);
}

/* ---- the mesh diagram ---------------------------------------------------
   Text-free by design: it replaces three paragraphs that were saying the same thing worse. The
   pulses run OUTWARD from the lead court because that is the real topology — one master handing
   assignments to peers, not a ring passing them along. Drawing a ring would be a prettier lie. */

.mesh { margin: 2.5rem 0 0; }
.mesh svg { width: 100%; height: auto; max-width: 44rem; margin-inline: auto; display: block; }
.mesh__nodes rect { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.5; }
.mesh__lead { fill: var(--surface); stroke: var(--accent); stroke-width: 2; }

.mesh__pulses path { stroke-dashoffset: 230; animation: mesh-pulse 3.2s linear infinite; }
.mesh__pulses path:nth-child(2) { animation-delay: .4s; }
.mesh__pulses path:nth-child(3) { animation-delay: .8s; }
.mesh__pulses path:nth-child(4) { animation-delay: 1.2s; }
@keyframes mesh-pulse { from { stroke-dashoffset: 230; } to { stroke-dashoffset: 0; } }

/* Principle 3 again. Without the pulses the diagram still reads — four courts joined to a lead
   court — so reduced motion loses nothing but the movement. */
@media (prefers-reduced-motion: reduce) {
  .mesh__pulses { display: none; }
}

/* ---- the display in the photograph --------------------------------------- */

figure.scene { margin: 3.5rem 0 0; }
figure.scene figcaption { margin-top: .875rem; font-size: .875rem; color: var(--muted); }

/* The positioning context is this wrapper, which holds the plate AND NOTHING ELSE.
   `.scene__screen`'s percentages resolve against it, so they mean a fraction of the photograph.
   When the <figure> was the containing block they resolved against plate + dots + caption, the
   panel rendered at 93% of the plate instead of 64%, and its bottom hung 35px below the image. */
.scene__frame { position: relative; line-height: 0; }
.scene__frame .scene__plate { border-radius: var(--r-card); border: 1px solid var(--line); }

/* MEASURED off `scene-court-display.jpg`, not eyeballed: the blank panel occupies
   x 404..2284, y 245..1274 of the 2688x1520 render — 15.03% / 16.12% / 69.94% / 67.70% — and its
   left, middle and right edges agree within 1px, so it is square to camera and needs no
   perspective transform. Re-measure if the photograph is ever regenerated. */
.scene__screen {
  position: absolute;
  left: 15.03%; top: 16.12%; width: 69.94%; height: 67.70%;
  overflow: hidden;
}

/* Swipeable strip of real spectator frames. Scroll-snap does the swiping natively — no carousel
   library, and it works with touch, trackpad and keyboard alike.

   `contain`, NOT `cover`. The panel is 1.827:1 and the screenshots are 1.653:1, so one of them
   has to give. `cover` filled the panel by cropping ~10% off the screenshot — which ate the
   header bar with the court name, or the sponsor strip, depending on the alignment. `contain`
   fits the whole screen and leaves ~53px each side, and because `.scene__screen` has NO
   background the photograph's own matte slab shows through there. The result reads as the
   display's bezel, which is what a real one has anyway, and nothing is lost. Verified by
   compositing it offline before it was written. */
.shots {
  display: flex; height: 100%;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.shots::-webkit-scrollbar { display: none; }
.shots img {
  flex: 0 0 100%; width: 100%; height: 100%;
  object-fit: contain; scroll-snap-align: start;
}
.shots:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.shots__dots { display: flex; gap: .5rem; justify-content: center; margin-top: .875rem; }
.shots__dots button {
  width: .5rem; height: .5rem; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--line-strong);
}
.shots__dots button[aria-selected="true"] { background: var(--accent); }

/* ---- hardware rail -------------------------------------------------------
   One row at every width. Below the breakpoint it scrolls sideways with snap points instead of
   wrapping — four stacked cards is a long scroll for a section people skim rather than read. */

.rail {
  margin-top: 2.5rem;
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(15rem, 1fr);
  gap: 1.25rem;
}
@media (max-width: 60rem) {
  .rail {
    grid-auto-columns: 72%;
    overflow-x: auto; scroll-snap-type: x mandatory;
    scrollbar-width: none;
    /* bleed to the screen edges so a half-visible next card signals "scrollable" */
    margin-inline: calc(50% - 50vw); padding-inline: calc(50vw - 50% + .25rem);
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail > * { scroll-snap-align: start; }
}

/* ---- the phone ----------------------------------------------------------
   A drawn frame, not a photograph: a blank phone in a hand is a free stock asset at best, and a
   wasted image-generation credit at worst. */
.phone {
  margin: 0 auto;
  width: min(100%, 20rem);
  padding: 10px;
  background: linear-gradient(160deg, #23272e, #14171d);
  border: 1px solid var(--line-strong);
  border-radius: 30px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}
.phone img { border-radius: 21px; width: 100%; display: block; }

.app-grid { margin-top: 2rem; align-items: center; }

.ticks { list-style: none; padding: 0; margin: 0 0 .5rem; }
.ticks li { position: relative; padding-left: 1.5rem; margin-bottom: .625rem; }
.ticks li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: .5rem; height: .5rem; border-radius: 2px; background: var(--accent);
}
.fineprint { font-size: .8125rem; color: var(--muted); margin: 1rem 0 0; }

/* ---- hero ---------------------------------------------------------------
   Full-bleed video with the banner in front. The scrim is a left-weighted gradient rather than a
   flat wash: the copy sits left, the scoreboard sits right, and dimming the whole frame to make
   text readable would throw away the thing the video is there to show. */

.hero {
  position: relative; isolation: isolate;
  display: grid; align-items: center;
  min-height: min(78vh, 40rem);
  padding: 5rem 0;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}
.hero__media {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
}
.hero__scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(90deg, var(--carbon) 0%, color-mix(in srgb, var(--carbon) 82%, transparent) 38%,
                    color-mix(in srgb, var(--carbon) 25%, transparent) 68%, transparent 100%),
    linear-gradient(0deg, color-mix(in srgb, var(--carbon) 70%, transparent), transparent 45%);
}
.hero__body { max-width: 34rem; }
.hero h1 { text-shadow: 0 2px 24px rgba(0, 0, 0, .5); }

@media (max-width: 56rem) {
  /* Portrait: there is no "left" to hide the copy in, so the scrim becomes a bottom-up wash and
     the text sits under the board rather than over it. */
  .hero { min-height: 0; padding: 3.5rem 0 3rem; }
  .hero__scrim {
    background: linear-gradient(0deg, var(--carbon) 18%,
                color-mix(in srgb, var(--carbon) 72%, transparent) 55%,
                color-mix(in srgb, var(--carbon) 45%, transparent) 100%);
  }
}

/* ---- platform pillars ----------------------------------------------------
   The landing page's table of contents and the two audience doorways: link-cards, same
   surface/border rules as .card (interaction.cards), whole card clickable because a card that
   says "Tournaments" and only reacts on an invisible word is a fake affordance. The accent
   arrives on hover as a border, never as a wash. */

.pillars { margin-top: 2.5rem; }
.pillar {
  display: block; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 1.375rem 1.5rem 1.5rem;
}
.pillar:hover { border-color: var(--accent); }
.pillar p { margin: 0; font-size: .9375rem; color: var(--body); }
.pillar .go { margin-top: .875rem; font-weight: 600; color: var(--accent); font-size: .9375rem; }

/* ---- section pages (/clubs/, /players/, /tournaments/, /camera/) ---------
   A quieter opening than the landing hero: no video, no photograph fighting the headline.
   The pages share every other primitive with the landing page. */

.page-hero { padding: 4.5rem 0 3.5rem; border-bottom: 1px solid var(--line); }
.page-hero h1 { max-width: 46rem; }

/* Two-column feature/image split. Stacks below the breakpoint with the image after the copy —
   the copy is the argument, the image is the evidence. */
.split { display: grid; gap: 2.5rem; align-items: center; }
@media (min-width: 56rem) { .split { grid-template-columns: 1fr 1fr; } }
.split figure { margin: 0; }
.split figure img { border-radius: var(--r-card); border: 1px solid var(--line); }
.split figcaption { margin-top: .75rem; font-family: var(--font-data); font-size: .8125rem; color: var(--muted); }

/* The roadmap tag. This site labels unshipped work — the tag IS the claims discipline, so it
   must never be restyled into something that reads as a feature badge. Mono, muted, bordered. */
.tag {
  display: inline-block; vertical-align: middle; margin-left: .375rem;
  font-family: var(--font-data); font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line-strong); border-radius: 4px; padding: .0625rem .4375rem;
}
