/* Legacy variable names — kept so every existing consumer keeps reading
   `var(--bg)` / `var(--card)` / `var(--accent)` etc. without a parallel
   refactor. The VALUES are now token references (see styles/tokens.css)
   so the brand-primitive layer becomes the single source of truth. The
   `:root.light-mode` overrides below + the matching block in the index.html
   critical CSS keep the variable VALUES in sync across modes.

   These names are scheduled to be renamed to the semantic aliases in
   themes/mad.css (--bg → --bg-page, --card → --bg-surface, etc.) in a
   later pass — for now they coexist so foundation lands cleanly. */
:root {
  --bg:          var(--color-neutral-900);
  --card:        var(--color-neutral-800);
  --card-border: var(--color-card-border-dark);
  --text:        var(--color-text-dark);
  --text-dim:    var(--color-text-dim-dark);
  /* Quiet punctuation / dimmed zeros. 48 call sites across the app; until
     2026-08-17 the ONLY definition was a <style> block the Home tab injects
     on render, so any surface reached without Home first (deep link, hard
     refresh on a hash route, an emailed report link) resolved it to nothing
     and drew those elements at full brightness. Card 1785963981934-9wuc73.
     Kept as the same color-mix Home used so nothing on Home shifts. */
  --text-faint:  color-mix(in srgb, var(--text-dim) 60%, transparent);
  --accent:      var(--color-teal-500);
  --orange:      var(--color-orange-500);
  --green:       var(--color-green-500);
  --red:         var(--color-red-500);
  --yellow:      var(--color-yellow-500);
  --blue:        var(--color-blue-500);
  --purple:      var(--color-purple-500);
  /* --avl / --gvl / --jup / --oak used to sit here: one variable per
     MADabolic gym, aliasing a palette primitive. Every deploy shipped four
     MADabolic gym names in a stylesheet to every browser on it, including
     Gym Builder customers who have never heard of those gyms. A location's
     colour belongs to the location, and the tenant registry has carried one
     per location the whole time — see FALLBACK_LOCATIONS in src/state.js and
     locColor() in tabs/freezes.js. */
}

/* Light mode — flips the same legacy names. Status colors (green/orange/
   red/etc.) stay identical between modes so semantic readings ("green
   = on target") don't change. The accent drops a step (teal-light) since
   the dark-mode teal washes out on white.

   ⚠ THIS SAID "but still passes WCAG AA" AND IT DOES NOT. Measured while
   building the per-tenant brand deriver, which needed a bar to hold gyms to:
   #00b894 on this block's --bg (#F5F7FA) is 2.36:1, and 2.54:1 on --card
   (#FFFFFF). AA for normal text is 4.5:1; even the 3:1 bar for large text and
   UI components is out of reach. The dark pairing is fine — #00D4AA on
   #0A0A0A is 10.37:1.

   Left alone deliberately, and only the claim corrected. This is MADabolic's
   own accent on MADabolic's own chrome, it is what the product has looked
   like in light mode for its whole life, and repainting it is a brand
   decision rather than a branding-engine one. A gym that sets its OWN colour
   gets the derived pair instead, which does clear 4.5:1 in both modes — see
   deriveBrandPalette in netlify/functions/_tenants.js. */
:root.light-mode {
  --bg:          var(--color-neutral-50);
  --card:        var(--color-neutral-0);
  --card-border: var(--color-neutral-200);
  --text:        var(--color-text-light);
  --text-dim:    var(--color-text-dim-light);
  --text-faint:  color-mix(in srgb, var(--text-dim) 60%, transparent);
  --accent:      var(--color-teal-light);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-feature-settings: 'cv11', 'ss01';
  background: var(--bg);
  color: var(--text);
  height: 100vh;
  overflow: hidden;
}

/* Tabular-numerals utility — applied to every metric number class site-wide
   so columns like 501 / 751 / 1,082 line up digit-for-digit instead of the
   proportional widths that ship with most fonts. JetBrains Mono is the
   chosen mono typeface; fall back through a safe stack if it fails to
   load from Google Fonts. */
.num,
.scorecard-value,
.ratio-card-value,
.stat-num,
.indicator-value,
.data-table .num-cell,
.metric-num {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' on, 'lnum' on;
  letter-spacing: -0.01em;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

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

input, select, textarea {
  background: var(--bg);
  border: 1px solid var(--card-border);
  border-radius: 6px;
  padding: 6px 10px;
  color: var(--text);
}

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

button {
  cursor: pointer;
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  font-weight: 500;
  transition: opacity 0.15s;
}
button:hover { opacity: 0.85; }

.btn-primary {
  background: var(--accent);
  color: var(--bg);
}

.btn-secondary {
  background: var(--card);
  border: 1px solid var(--card-border);
  color: var(--text);
}

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
}
/* Status palette — only red/orange/green carry meaning. Informational
   chips (ontrack/frozen) drop to neutral; unbilled folds into the
   orange/warning lane. 2026-04-20 palette simplification pass.
   Post-ChatGPT review: .badge-frozen and .badge-ontrack use full --text
   (not --text-dim) at 11px uppercase to clear WCAG AA on the card
   background. Frozen also gets a subtle card-border fill so it reads
   as a bounded chip, not floating text. */
.badge-promoter { background: rgba(34,197,94,0.15); color: var(--green); }
.badge-ontrack  { background: rgba(228,228,231,0.08); color: var(--text);  border: 1px solid var(--card-border); }
.badge-nurture  { background: rgba(249,115,22,0.15); color: var(--orange); }
.badge-atrisk   { background: rgba(239,68,68,0.15);  color: var(--red); }
.badge-frozen   { background: rgba(228,228,231,0.05); color: var(--text);  border: 1px solid var(--card-border); }
.badge-unbilled { background: rgba(249,115,22,0.15); color: var(--orange); }

.spinner {
  width: 24px; height: 24px;
  border: 3px solid var(--card-border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── madOS brand loader ──────────────────────────────────────────────
   Whoop-style trace: a teal circle draws around an "M", both strokes
   tracing in then erasing in a continuous loop. Used by loadingHtml()
   in place of the generic CSS-border spinner. Stroke length math:
   ring circumference 2π × 24 ≈ 151; M path length ≈ 76 (measured). */
.mad-trace {
  width: 56px;
  height: 56px;
  color: var(--accent);
  display: block;
}
.mad-trace-ring {
  stroke-dasharray: 151;
  stroke-dashoffset: 151;
  animation: mad-trace-ring 2.4s ease-in-out infinite;
}
.mad-trace-letter {
  stroke-dasharray: 76;
  stroke-dashoffset: 76;
  animation: mad-trace-letter 2.4s ease-in-out infinite;
}
@keyframes mad-trace-ring {
  0%   { stroke-dashoffset: 151; }
  50%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -151; }
}
@keyframes mad-trace-letter {
  0%, 18%  { stroke-dashoffset: 76; }
  58%      { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: -76; }
}

.skipper-working-pulse {
  animation: skipper-working-pulse 1.4s ease-in-out infinite;
}
@keyframes skipper-working-pulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 0.85; }
}

.loading-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 200px;
  color: var(--text-dim);
  font-size: 13px;
}

/* Cycling exercise phrases inside loadingHtml() */
.loading-phrases {
  position: relative;
  height: 1.4em;
  min-width: 110px;
  text-align: center;
}
.lp-word {
  position: absolute;
  left: 0; right: 0;
  opacity: 0;
  animation: lp-fade 9.8s infinite;
}
@keyframes lp-fade {
  0%, 1%    { opacity: 0; }
  3%, 12%   { opacity: 1; }
  15%, 100% { opacity: 0; }
}
