/* ==========================================================================
   HFP — design tokens + base layer
   --------------------------------------------------------------------------
   The single source of truth for colour, type, space, radius, motion and
   layers. Loaded by index.html, login.html and app.html. Nothing downstream
   may declare a raw hex, a px font-size, or a magic radius.

   THEME: `color-scheme: light dark` + `light-dark()` honours the OS preference
   with ZERO JavaScript. That is a requirement here, not a preference — the
   site's `_headers` sets `script-src 'self'` with no 'unsafe-inline', so the
   usual anti-FOUC inline theme script is blocked outright. An explicit user
   choice flips one property (`color-scheme`), not a second palette.

   Every contrast ratio in the comments is computed (WCAG 2.x) against that
   theme's --bg. See DESIGN.md §4.1.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts — self-hosted. The CSP blocks Google Fonts and every font CDN.
   Download once and commit:
     fonts/archivo-var.woff2          Archivo variable (wght 100–900, wdth 62–125), OFL
     fonts/jetbrains-mono-var.woff2   JetBrains Mono variable, OFL
     fonts/OFL.txt                    licence notice — required by the OFL
   No unicode-range: it is a *filter*, not an optimisation, and any codepoint
   outside it silently falls back mid-sentence (↑ ↓ ≈ are used in the UI).
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Archivo";
  src: url("./fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("./fonts/jetbrains-mono-var.woff2") format("woff2-variations");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}
/* Metric-matched fallback — this is what keeps CLS near zero while the real
   font loads. MEASURE these against Archivo before shipping; they are a
   starting point, not gospel. */
@font-face {
  font-family: "Archivo Fallback";
  /* All four originals are Mac/Windows-only, so on Linux and Android none resolved
     and this face errored — the CLS protection silently did nothing for exactly the
     platforms most likely to be on a slow connection. Liberation Sans and Arimo are
     Arial-metric, DejaVu/Noto/Roboto are the common defaults. */
  src: local("Helvetica Neue"), local("Helvetica"), local("Arial"), local("Segoe UI"),
       local("Liberation Sans"), local("Arimo"), local("Roboto"), local("Noto Sans"), local("DejaVu Sans");
  size-adjust: 96%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light dark;                       /* default: follow the OS */

  /* ---- surfaces: warm paper by day, basalt at night ---- */
  --bg:            light-dark(#FAFAF8, #0B0D10);
  --surface:       light-dark(#FFFFFF, #121519);
  --surface-2:     light-dark(#F2F3F1, #181C22);
  --surface-3:     light-dark(#E9EBE8, #20252C);
  --border:        light-dark(#E2E4E0, #252B33);
  --border-strong: light-dark(#C6CAC5, #39424D);

  /* ---- text ----                            light : dark  */
  --fg:   light-dark(#14171A, #F2F4F7);   /*     17.2 : 17.7  */
  --fg-2: light-dark(#5A6169, #A6AEBA);   /*      6.0 :  8.7  */
  --fg-3: light-dark(#6B727A, #7B8493);   /*      4.7 :  5.2  */
  /* --fg-3 is legal ONLY on --bg and --surface. On --surface-2 it drops
     below 4.5:1 — use --fg-2 inside chips, wells and inputs. */

  /* ---- interactive: the ONLY blue in the system ---- */
  --accent:        light-dark(#0A6CD8, #6BB6FF);   /* 4.8 : 9.0 */
  --accent-strong: light-dark(#085AB4, #2E9BFF);   /* filled buttons */
  --accent-fg:     light-dark(#FFFFFF, #04121F);   /* text on an accent fill */
  --accent-soft:   light-dark(#0A6CD81A, #6BB6FF24);

  /* ---- categories: race type ONLY, never chrome ---- */
  --trail: light-dark(#2E7D4F, #79C98A);   /* utanvega  4.8 : 9.8 */
  --road:  light-dark(#9A5B12, #E8A857);   /* gata      5.2 : 9.4 */
  --other: light-dark(#5B4FC4, #A79BFF);   /* annað     6.0 : 8.1 */
  --trail-soft: light-dark(#2E7D4F1A, #79C98A21);
  --road-soft:  light-dark(#9A5B121A, #E8A85721);
  --other-soft: light-dark(#5B4FC41A, #A79BFF21);

  /* ---- the same hues as INK on their own soft tint (DESIGN §4.1 rule 9) ----
     A hue that passes AA on --bg does not pass on its own 10–14% tint of itself: the tint lifts
     the background toward the hue, so the pair loses ~0,5. Light needs a darker ink; dark does
     not (the tint sits on a dark surface and the pair measures 7,0–7,2). These lived in
     styles.css, which the landing and login pages do NOT load — so components.css could not
     reach them and .msg[data-tone="ok"] shipped at 4,25:1 on the login page with the fix one
     stylesheet away. Tokens belong here. */
  --trail-ink:  light-dark(#216040, #79C98A);   /* on --trail-soft:  6,6 : 7,2 */
  --road-ink:   light-dark(#7A470D, #E8A857);   /* on --road-soft:   6,9 : 7,0 */
  --other-ink:  light-dark(#473CA0, #A79BFF);   /* on --other-soft:  8,0 : 7,1 */
  --accent-ink: light-dark(#085AB4, #6BB6FF);   /* on --accent-soft: 6,6 : 7,2 */

  /* ---- status ---- */
  --danger:      light-dark(#C0322F, #FF7A7A);
  --danger-soft: light-dark(#C0322F1A, #FF7A7A24);
  --warn:        light-dark(#8A6100, #F0C25A);
  --warn-soft:   light-dark(#8A61001A, #F0C25A24);
  --success:     var(--trail);

  /* ---- elevation ---- */
  --shadow-1: 0 1px  2px light-dark(#28324612, #0000004D);
  --shadow-2: 0 4px 16px light-dark(#2832461A, #00000057);
  --shadow-3: 0 16px 48px light-dark(#28324624, #00000070);
  --scrim: light-dark(#28324699, #05070Ab3);

  /* ---- focus: an OUTLINE, not a box-shadow. A box-shadow ring needs an
     inner spacer in the PARENT's colour, which is wrong the moment the
     element sits on a card, in a sheet, or over a scrim. ---- */
  --ring: 2px solid var(--accent);
  --ring-offset: 2px;

  /* ---- typography ---- */
  --font-sans: "Archivo", "Archivo Fallback", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --wdth-display: 112%;   /* headings — the athletic-signage voice */
  --wdth-eyebrow: 118%;   /* uppercase micro-labels, a touch wider still */
  --wdth-ui: 100%;

  --fs-caption: 0.6875rem;  /* 11 */
  --fs-label:   0.75rem;    /* 12 */
  --fs-sm:      0.8125rem;  /* 13 */
  --fs-body:    0.9375rem;  /* 15 — base UI text */
  --fs-input:   1rem;       /* 16 — FORM CONTROLS ONLY. Below 16px, mobile
                               Safari zooms the viewport on focus and never
                               zooms back out. Not a scale violation. */
  --fs-lg:      1.0625rem;  /* 17 */
  --fs-h3:      1.3125rem;  /* 21 */
  --fs-h2:      1.75rem;    /* 28 */
  --fs-h1:      2.375rem;   /* 38 */
  --fs-display: clamp(2.75rem, 6vw, 4.5rem);

  --lh-tight: 1.1;
  --lh-snug:  1.3;
  --lh-body:  1.55;
  --tr-tight: -0.02em;
  --tr-flat:  0;
  --tr-wide:  0.06em;

  /* ---- space: 4px base, these 11 values only ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;
  --sp-12: 48px; --sp-16: 64px; --sp-20: 80px;

  /* ---- radius: five values only ---- */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px; --r-full: 999px;

  /* ---- motion ---- */
  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 260ms; --dur-4: 400ms;
  --ease:     cubic-bezier(0.2, 0, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in:  cubic-bezier(0.7, 0, 0.84, 0);

  /* ---- layers. Verified against vendor/leaflet/leaflet.css — do not
     re-derive: tile 200 · pane/overlay 400 · shadow 500 · marker 600 ·
     tooltip 650 · popup 700 · .leaflet-control 800 · .leaflet-top/-bottom
     1000. App UI drawn over a map must clear 1000; 800 would TIE the
     controls and let source order decide. ---- */
  --z-base: 0; --z-sticky: 100; --z-nav: 200;
  --z-map-ui: 1010; --z-overlay: 1050; --z-modal: 1100; --z-toast: 1200;

  /* ---- layout ---- */
  --w-content: 1180px;   /* app shell */
  --w-list: 880px;       /* scannable single-column lists */
  --w-prose: 68ch;
  --header-h: 60px;
  --tabbar-h: 56px;

  /* ---- charts ---- */
  --chart-ink: var(--fg);
  --chart-grid: light-dark(#EDEEEB, #1E242B);
  --chart-axis: var(--fg-3);
  --chart-fill: color-mix(in oklab, var(--accent) 16%, transparent);
  --chart-cursor: var(--accent);
  --chart-band: color-mix(in oklab, var(--fg) 5%, transparent);
  --chart-h-sm: 72px; --chart-h-md: 96px; --chart-h-lg: 180px;
  /* §9's contour motif — how loud the ornament is. These are NUMBERS, so they cannot
     ride on light-dark(), which is a <color> function: fed a number it is invalid at
     computed-value time and opacity silently falls back to 1. That is exactly how the
     motif first rendered — a solid black band across Heim. Theme variants are spelled
     out under the palette below. §9: 8–12% on dark, 5–7% on light. */
  --contour-op: 0.06;        /* art sitting behind text */
  --contour-op-seam: 0.07;   /* the Heim divider, which has nothing over it */
  --chart-bar-r: 3px;
}

/* An explicit choice overrides the OS.

   NOTE: put data-theme on <html> or <body> only, never on a nested subtree.
   light-dark() resolves where a property is DECLARED, not where it is
   inherited — so a nested [data-theme] would flip backgrounds (declared on
   the element) while text inherited from an ancestor kept the other theme's
   colour. Re-declaring colour here makes a scoped subtree behave, but the
   supported usage is still one theme root per document. */
[data-theme="light"] { color-scheme: light; color: var(--fg); }
[data-theme="dark"]  { color-scheme: dark;  color: var(--fg); }

/* --------------------------------------------------------------------------
   light-dark() fallback — Safari only shipped it in 17.5 (May 2024), and the
   failure mode is TOTAL: an unsupported light-dark() makes every colour token
   invalid at computed-value time, so the whole site renders unstyled. The
   usual "declare the fallback first" trick does NOT work for custom properties
   (they accept any token sequence at parse time, so the light-dark() line wins
   and then fails), hence an explicit @supports block. Delete this once the
   floor is iOS 17.5+.
   -------------------------------------------------------------------------- */
@supports not (color: light-dark(#000, #fff)) {
  :root {
    --bg: #FAFAF8; --surface: #FFFFFF; --surface-2: #F2F3F1; --surface-3: #E9EBE8;
    --border: #E2E4E0; --border-strong: #C6CAC5;
    --fg: #14171A; --fg-2: #5A6169; --fg-3: #6B727A;
    --accent: #0A6CD8; --accent-strong: #085AB4; --accent-fg: #FFFFFF; --accent-soft: #0A6CD81A;
    --trail: #2E7D4F; --road: #9A5B12; --other: #5B4FC4;
    --trail-ink: #216040; --road-ink: #7A470D; --other-ink: #473CA0; --accent-ink: #085AB4;
    --trail-soft: #2E7D4F1A; --road-soft: #9A5B121A; --other-soft: #5B4FC41A;
    --danger: #C0322F; --danger-soft: #C0322F1A; --warn: #8A6100; --warn-soft: #8A61001A;
    --shadow-1: 0 1px 2px #28324612; --shadow-2: 0 4px 16px #2832461A; --shadow-3: 0 16px 48px #28324624;
    --scrim: #28324699; --chart-grid: #EDEEEB;
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bg: #0B0D10; --surface: #121519; --surface-2: #181C22; --surface-3: #20252C;
      --border: #252B33; --border-strong: #39424D;
      --fg: #F2F4F7; --fg-2: #A6AEBA; --fg-3: #7B8493;
      --accent: #6BB6FF; --accent-strong: #2E9BFF; --accent-fg: #04121F; --accent-soft: #6BB6FF24;
      --trail: #79C98A; --road: #E8A857; --other: #A79BFF;
      --trail-ink: #79C98A; --road-ink: #E8A857; --other-ink: #A79BFF; --accent-ink: #6BB6FF;
      --trail-soft: #79C98A21; --road-soft: #E8A85721; --other-soft: #A79BFF21;
      --danger: #FF7A7A; --danger-soft: #FF7A7A24; --warn: #F0C25A; --warn-soft: #F0C25A24;
      --shadow-1: 0 1px 2px #0000004D; --shadow-2: 0 4px 16px #00000057; --shadow-3: 0 16px 48px #00000070;
      --scrim: #05070Ab3; --chart-grid: #1E242B;
    }
  }
  [data-theme="dark"] {
    --bg: #0B0D10; --surface: #121519; --surface-2: #181C22; --surface-3: #20252C;
    --border: #252B33; --border-strong: #39424D;
    --fg: #F2F4F7; --fg-2: #A6AEBA; --fg-3: #7B8493;
    --accent: #6BB6FF; --accent-strong: #2E9BFF; --accent-fg: #04121F; --accent-soft: #6BB6FF24;
    --trail: #79C98A; --road: #E8A857; --other: #A79BFF;
    --trail-ink: #79C98A; --road-ink: #E8A857; --other-ink: #A79BFF; --accent-ink: #6BB6FF;
    --trail-soft: #79C98A21; --road-soft: #E8A85721; --other-soft: #A79BFF21;
    --danger: #FF7A7A; --danger-soft: #FF7A7A24; --warn: #F0C25A; --warn-soft: #F0C25A24;
    --shadow-1: 0 1px 2px #0000004D; --shadow-2: 0 4px 16px #00000057; --shadow-3: 0 16px 48px #00000070;
    --scrim: #05070Ab3; --chart-grid: #1E242B;
  }
}

/* Contour opacity per theme. Outside the @supports block on purpose: these are numbers,
   so they need the explicit media-query + [data-theme] pair in EVERY browser, not only
   the ones without light-dark(). §9: 8–12% on dark, 5–7% on light — "4% on #0B0D10 is a
   two-step luminance change, i.e. invisible". */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --contour-op: 0.09; --contour-op-seam: 0.12; }
}
[data-theme="dark"] { --contour-op: 0.09; --contour-op-seam: 0.12; }

@media (max-width: 767px) { :root { --header-h: 52px; } }

/* --------------------------------------------------------------------------
   Utilities

   NOTE: the reset and the base element styling (body, h1–h4, p, a, img/svg,
   autofill, reduced-motion) live in base.css, NOT here. That split is what lets
   the app load this file for the tokens and the fonts while keeping its own
   reset in styles.css — see the header of base.css for the measurements.
   Everything below is opt-in: it only applies to a class you write yourself.
   -------------------------------------------------------------------------- */

/* Tabular figures wherever digits sit in a column. */
.u-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* The athletic-signage voice. */
.u-display { font-stretch: var(--wdth-display); letter-spacing: var(--tr-tight); }

/* Uppercase eyebrow label. */
.u-eyebrow {
  font-size: var(--fs-caption);
  font-weight: 700;
  font-stretch: var(--wdth-eyebrow);
  letter-spacing: var(--tr-wide);
  text-transform: uppercase;
  color: var(--fg-3);
}

/* Visual height may go below 44px. The HIT AREA may not. Baked into .btn--sm,
   .seg button and .chip--filter in components.css; this utility is for the
   one-offs — .fav-btn, row actions in the gear and injury lists. */
.hit-44 { position: relative; }
.hit-44::before {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  translate: -50% -50%;
  min-width: 44px;
  min-height: 44px;
}

.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 {
  position: absolute; left: var(--sp-2); top: var(--sp-2);
  z-index: var(--z-toast);
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  translate: 0 -200%;
  transition: translate var(--dur-2) var(--ease-out);
}
.skip-link:focus-visible { translate: 0 0; text-decoration: none; }

.icon { width: 20px; height: 20px; flex: none; stroke-width: 2; }
.icon-sm { width: 16px; height: 16px; stroke-width: 2.25; }
.icon-lg { width: 24px; height: 24px; stroke-width: 1.75; }
