/* Reset, design tokens, typography, and small utilities shared by every page. */

/* ---- Reset --------------------------------------------------------------- */

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

/* Quiet, consistent scrollbars across page and component scroll areas. */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}

*::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.16);
  border-radius: 999px;
}

*::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.3);
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100vh;
}

img, picture, svg, video {
  display: block;
  max-width: 100%;
}

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

button {
  cursor: pointer;
}

button:disabled {
  cursor: not-allowed;
}

ul[role='list'], ol[role='list'] {
  list-style: none;
  padding: 0;
}

a {
  color: inherit;
}

table {
  border-collapse: collapse;
  width: 100%;
}

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

/* ---- Design tokens ---------------------------------------------------------- */

:root {
  /* ======================================================================
     Dark redesign (2026-10-24), Hakbang 1 -- colors, radius, and the new
     translucent badge-tint tokens, all in this one file, per explicit
     instruction. Picked by eye against the reference screenshot, not
     pixel-sampled -- expect a small follow-up tuning pass once real pages
     are viewed against them. Full reasoning: docs/UI-MIGRATION-CHECKLIST.md.

     --brand-strong is DELIBERATELY left at its pre-redesign value below --
     NOT retoned to the new palette. pages/auth.css (login/register,
     explicitly out of scope for this whole redesign) reads this same
     variable directly for its identity-panel background, with contrast
     ratios tuned pixel-by-pixel in an earlier session (6.06:1 / 7.55:1,
     see CLAUDE.md's Progress log). Changing this value would silently
     reskin login/register through the shared variable even though no file
     of theirs is touched -- exactly the invisible side effect this step
     exists to catch, not cause. The sidebar's own background (layout.css,
     currently var(--brand-strong)) therefore stays its old maroon until
     the shared shell is actually rebuilt (Hakbang 2), which gives it its
     own dedicated token instead of reusing this one. ====================== */
  --brand: #ef4444;
  --brand-strong: #7a0000;
  --ink: #f2f2f4;
  --muted: #9b98a3;
  --line: #26262b;
  --bg: #0a0a0c;
  --surface: #16161a;

  /* Load status colors -- used ONLY for workload status, never for
     decoration. Kept as three distinct hues per explicit decision, tuned to
     roughly the same saturation/vividness as --brand so they read as one
     system -- except --load-balanced's lightness, pulled down slightly on
     purpose: a pure-saturated green at the same lightness as the other
     three reads as neon against a near-black background, a well-known
     dark-UI exception, not a miss. --load-over stays orange, never red, so
     it can never be confused with the brand accent. */
  --load-under: #3b82f6;
  --load-balanced: #10b981;
  --load-over: #f97316;
  /* The Workload page alone draws Overloaded in this red (bar, chart legend
     swatch, stat-card dot). --load-over above stays orange for every other
     page; see pages/workload.css. */
  --wl-overloaded-bar: #de0307;
  /* Percent text for the same Overloaded status on the Workload page. */
  --wl-overloaded-text: #ff5a5f;
  /* The Workload page alone draws Underloaded in this blue (bar, chart legend
     swatch, stat-card dot, percent text). --load-under stays #3b82f6 for
     every other page; see pages/workload.css. */
  --wl-underloaded: #1e90ff;
  /* "At limit" (2026-10-25): a display-only state for a 'balanced' member
     who has reached a limit (utils/format.js isAtLoadLimit). An ALIAS, not a
     new hex: the one existing color that sits between Balanced green and
     Overloaded orange. Note it is NOT exclusively a load color -- the same
     amber already means "In review" (--status-in-review), "High" priority,
     and the TEST environment badge. Measured against Overloaded orange:
     CIEDE2000 19.6 for normal vision, but only 1.6 under deuteranopia -- so
     the stripes on an at-limit bar, not the color, are what tell the two
     apart for a red-green colorblind reader. */
  --load-at-limit: var(--status-in-review);

  /* Task status colors -- badges, the sprint timeline's deadline markers, etc.
     Independent of the load status colors above even where a hue matches --
     --status-in-progress/--status-done keep the same deliberate coincidence
     with --load-under/--load-balanced that existed before this redesign.
     --status-in-review gets its own amber, distinct from the new
     --load-over orange, so "in review" and "Overloaded" don't read the same. */
  --status-in-progress: #3b82f6;
  --status-in-review: #d29922;
  --status-done: #10b981;

  /* Semantic aliases so component CSS doesn't hardcode raw hex. --danger
     does double duty (plain text via .text-danger, and a solid button fill
     with white text via .btn--danger) -- picked as a middle-vividness red
     that reads legibly as both, distinct from --brand so a validation error
     and a branded action never look identical. */
  --danger: #e5484d;
  --focus-ring: #3b82f6;

  /* Translucent badge/chip backgrounds, replacing what used to be ~14
     hardcoded opaque light-mode hex values scattered across components.css
     (status/priority/load badges, the SP chip, the timeline's "today"
     column, the Capacity panel's warning, the TEST env badge). Translucent
     so each one composites correctly over --surface regardless of its exact
     tone, rather than needing a second hand-picked opaque hex per hue. */
  --tint-blue: rgba(59, 130, 246, 0.18);
  --tint-amber: rgba(210, 153, 34, 0.18);
  --tint-green: rgba(16, 185, 129, 0.18);
  --tint-red: rgba(229, 72, 77, 0.18);
  --tint-orange: rgba(249, 115, 22, 0.18);
  --tint-brand: rgba(239, 68, 68, 0.14);

  /* The skeleton-loader shimmer's highlight sweep -- was a hardcoded light
     hex (#eee6e6) that assumed a light --line; a translucent white stays
     visible against the shimmer's --line-colored track at any dark tone. */
  --skeleton-highlight: rgba(255, 255, 255, 0.08);

  /* Fonts -- one family (Geist), for both headings and body text. The two
     token names stay (several components still reference them by name) but
     now resolve to the same stack; headings and body are told apart by
     weight and size, not by a different typeface. Geist Mono is scoped to
     --font-mono alone, for the one place this app shows something that
     reads as an identifier rather than prose (an ID number) -- see
     .text-mono below. */
  --font-sans: 'Geist', 'Segoe UI', system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-heading: var(--font-sans);
  --font-body: var(--font-sans);
  --font-mono: 'Geist Mono', ui-monospace, 'Cascadia Code', Consolas, 'Courier New', monospace;

  /* Type scale */
  --text-xs: 12px;
  --text-sm: 14px;
  --text-md: 16px;
  --text-lg: 20px;
  --text-xl: 25px;
  --text-2xl: 31px;

  /* Spacing scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  /* Radius */
  --radius-sm: 6px;
  --radius-lg: 10px;

  /* Layout */
  --sidebar-width: 248px;
  --content-max-width: 1280px;
  --topbar-height: 64px;

  /* Elevation -- a plain black-based rgba; the old values were built from
     the light-mode --ink color and don't do anything useful as a shadow on
     a near-black background. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5);
}

/* ---- Base typography ------------------------------------------------------- */

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-md);
  line-height: 1.5;
  /* Digits render at a fixed width everywhere in the app, not just in the
     few places someone remembered to ask for it -- a dashboard/workload/
     sprint-board full of changing numbers (9% -> 10%, SP totals, dates)
     would otherwise visibly shift width on every update. This is a global,
     inherited rule on purpose: it only ever affects how the ten digit
     glyphs are drawn, never any other character, so there is nothing for
     it to break by reaching further than a hand-picked list of selectors
     would. Reaches into the app's own inline-SVG chart text too (an SVG
     <text> element inherits CSS font properties the same way an HTML
     element does), covering the burndown/velocity/workload chart labels
     without a second rule. */
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 600;
  line-height: 1.25;
}

h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-md); }

small, .text-sm { font-size: var(--text-sm); }
.text-xs { font-size: var(--text-xs); }

p {
  color: var(--ink);
}

.text-muted {
  color: var(--muted);
}

.text-danger {
  color: var(--danger);
}

/* The one place this app shows something that reads as an identifier
   rather than prose -- an ID number -- not story points, percentages, or
   dates, which stay in Geist Sans so the UI doesn't start reading like a
   terminal. A plain utility (not an .input BEM modifier) since it's used
   on both a form input and a styleguide table cell. */
.text-mono {
  font-family: var(--font-mono);
}

/* ---- Focus ------------------------------------------------------------------ */

:focus {
  outline: none;
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

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

.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -48px;
  background: var(--surface);
  color: var(--ink);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  z-index: 100;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: var(--space-3);
}

.u-flex { display: flex; }
.u-items-center { align-items: center; }
.u-justify-between { justify-content: space-between; }
.u-wrap { flex-wrap: wrap; }
.u-gap-2 { gap: var(--space-2); }
.u-gap-3 { gap: var(--space-3); }

.u-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* !important is deliberate: this utility must win regardless of load order
   against component classes like .btn's `display: inline-flex`. */
.u-visually-hide-below-900 {
  display: none !important;
}

@media (min-width: 900px) {
  .u-visually-hide-below-900 {
    /* revert (not initial) so it falls back to whatever display the element's
       own class would have set -- inline-flex for a button, inline for a span. */
    display: revert !important;
  }
}
