/* ==========================================================================
   Base — shared components

   The web mirror of `src/ui/`. Every class here is one of the app's own
   primitives, drawn with the same anatomy out of the same tokens
   (assets/css/tokens.css, generated from src/theme/tokens.ts).

   THE RULE THIS FILE ENFORCES: a section of the site does not draw its own
   button, its own card or its own chip. It reaches for the one that already
   exists, exactly as a screen reaches for `<Button>` rather than styling a
   `<Pressable>`. Every inconsistency this file was written to fix came from
   somewhere hand-rolling a primitive that already existed one import away.

   Load order: tokens.css → components.css → base.css → site.css → content.css.

     .btn      src/ui/Button.tsx    — the two-face press button
     .card     src/ui/Card.tsx      — surface + hairline, in both themes
     .row      src/ui/Row.tsx       — glyph well, title over meta, accessory
     .chip     src/ui/Chip.tsx      — the small labelled pill, four tones
     .stat     src/ui/StatTile.tsx  — value + muted unit + delta
     .sec      src/ui/Section.tsx   — titled block, fixed vertical rhythm
     .divider  src/ui/Section.tsx   — the hairline between rows
     .field    src/ui/Field.tsx     — an input on the one grey light mode has
     .bar      src/ui/Bar.tsx       — a track and a fill
     .t-*      src/ui/Text.tsx      — one class per type variant
   ========================================================================== */

/* ============================================================== type ======
   One class per variant in `type` (src/theme/tokens.ts), so `class="t-section"`
   on the web and `variant="section"` in a screen are the same instruction.

   The `font:` shorthand carries family, weight, size and leading together. That
   is deliberate: the app once lost every weight in the build because the weight
   was riding inside an unresolvable family name, and a shorthand cannot be
   half-applied the way four separate declarations can.
   ========================================================================== */

.t-hero       { font: var(--t-hero);        font-variant-numeric: tabular-nums; }
.t-display    { font: var(--t-display);     font-variant-numeric: tabular-nums; }
.t-number-lg  { font: var(--t-number-lg);   font-variant-numeric: tabular-nums; }
.t-title      { font: var(--t-title); }
.t-title-sm   { font: var(--t-title-sm); }
.t-stat       { font: var(--t-stat);        font-variant-numeric: tabular-nums; }
.t-section    { font: var(--t-section); }
.t-stat-value { font: var(--t-stat-value);  font-variant-numeric: tabular-nums; }
.t-nav-title  { font: var(--t-nav-title); }
.t-card-title { font: var(--t-card-title); }
.t-body       { font: var(--t-body); }
.t-body-med   { font: var(--t-body-med); }
.t-body-strong{ font: var(--t-body-strong); }
.t-control    { font: var(--t-control); }
.t-label      { font: var(--t-label); }
.t-label-strong { font: var(--t-label-strong); }
.t-meta       { font: var(--t-meta); }
.t-num-meta   { font: var(--t-num-meta);    font-variant-numeric: tabular-nums; }
.t-tiny       { font: var(--t-tiny);        font-variant-numeric: tabular-nums; }
.t-caps {
  font: var(--t-caps);
  letter-spacing: var(--t-caps-track);
  text-transform: uppercase;
}

/* Ink levels, named as the palette names them. */
.ink        { color: var(--c-ink); }
.ink-muted  { color: var(--c-ink-muted); }
.ink-faint  { color: var(--c-ink-faint); }
.ink-accent { color: var(--c-accent); }

/* Tabular figures on demand — a column of numbers that jitters as it counts is
   the same bug on a dashboard and in a comparison table. */
.tnum { font-variant-numeric: tabular-nums; }

/* ============================================================ button ======
   src/ui/Button.tsx — a solid top FACE resting on a darker EDGE, driven down
   onto the edge by exactly `layout.press` when pressed.

   The app does this with two stacked views and a transform. The web does it
   with one element and a hard `box-shadow`, which is the same picture with no
   extra markup — important, because these class names are already written into
   100+ generated pages, the shared nav and the shared CTA band.

   `margin-bottom: var(--l-press)` reserves the edge inside the layout box, so
   pressing changes the button's silhouette without moving anything under it.
   That is the failure mode of every naive version of this effect, and it is why
   the app's own version measures `height + depth` rather than height.

   Hover is a web-only addition — the app has no hover state to copy. It lifts
   the face one further pixel off its edge rather than recolouring, so the
   feedback is the same mechanic running the other way.
   ========================================================================== */

.btn {
  --face: var(--btn-ink-face);
  --edge: var(--btn-ink-edge);
  --label: var(--btn-ink-ink);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-sm);

  height: 48px;                       /* Button.tsx HEIGHT.md */
  padding-inline: var(--s-xl);
  margin-bottom: var(--l-press);      /* the edge, reserved */

  border: 0;
  border-radius: var(--r-lg);
  background: var(--face);
  color: var(--label);
  box-shadow: 0 var(--l-press) 0 0 var(--edge);

  font: var(--t-control);
  white-space: nowrap;
  text-align: center;
  cursor: pointer;

  /* 45ms down is the app's own DOWN timing: the face has to be down before the
     finger finishes landing, or the press reads as laggy. */
  transition: transform 45ms linear, box-shadow 45ms linear, background .18s ease;
}

.btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 calc(var(--l-press) + 1px) 0 0 var(--edge);
}

.btn:active {
  transform: translateY(var(--l-press));
  box-shadow: 0 0 0 0 var(--edge);
}

.btn[aria-disabled='true'],
.btn:disabled { opacity: .4; pointer-events: none; }

.btn__ico { width: 18px; height: 18px; flex: none; }   /* Button.tsx GLYPH.lg */

/* ------------------------------------------------------------- sizes ----- */

/* `layout.cta` — the full-width primary action at the top of a screen. */
.btn--lg { height: var(--l-cta); font: var(--t-card-title); }
.btn--sm {
  /* Button.tsx: `depth = size === 'sm' ? layout.press - 1 : layout.press` */
  --l-press: 3px;
  height: 38px;
  padding-inline: var(--s-md);
  font: var(--t-label-strong);
}
.btn--sm .btn__ico { width: 14px; height: 14px; }
.btn--full { display: flex; width: 100%; }

/* ------------------------------------------------------------- tones ----- */

.btn--ink     { --face: var(--btn-ink-face);     --edge: var(--btn-ink-edge);     --label: var(--btn-ink-ink); }
.btn--accent  { --face: var(--btn-accent-face);  --edge: var(--btn-accent-edge);  --label: var(--btn-accent-ink); }
.btn--success { --face: var(--btn-success-face); --edge: var(--btn-success-edge); --label: var(--btn-success-ink); }
.btn--danger  { --face: var(--btn-danger-face);  --edge: var(--btn-danger-edge);  --label: var(--btn-danger-ink); }
.btn--pr      { --face: var(--btn-pr-face);      --edge: var(--btn-pr-edge);      --label: var(--btn-pr-ink); }

/*
 * `quiet` follows the palette rather than fixing its own hex, because it is
 * meant to recede into the card it sits on. On a page that is the same white as
 * the card, `surfaceAlt` alone is nearly invisible — so this is the one tone
 * that also carries a hairline, which is the same line every other object on
 * this site uses to say it is there.
 */
.btn--quiet {
  --face: var(--btn-quiet-face);
  --edge: var(--btn-quiet-edge);
  --label: var(--btn-quiet-ink);
  border: 1px solid var(--c-hairline);
}

/*
 * No edge, no fill. For the third action in a row, where a face would put three
 * buttons in competition. It keeps the reserved margin so it still baselines
 * with the real buttons beside it.
 */
.btn--ghost {
  --face: transparent;
  --edge: transparent;
  --label: var(--c-ink);
}
.btn--ghost:hover  { background: var(--c-surface-alt); transform: none; box-shadow: none; }
.btn--ghost:active { transform: none; box-shadow: none; }

/* ------------------------------------------------- legacy tone aliases ---- */

/*
 * `--dark` and `--plain` are written into 100+ generated content pages and into
 * the shared nav and CTA band (seo/lib/layout.mjs). They are kept as aliases
 * onto real tones rather than renamed, because renaming them is a
 * find-and-replace across every generated page for no visible gain.
 *
 * `--dark` is the site's primary and it maps to `accent`: the one interactive
 * accent, as a button, which is exactly what "Get the beta" is.
 */
.btn--dark { --face: var(--btn-accent-face); --edge: var(--btn-accent-edge); --label: var(--btn-accent-ink); }
.btn--plain {
  --face: var(--btn-quiet-face);
  --edge: var(--btn-quiet-edge);
  --label: var(--btn-quiet-ink);
  border: 1px solid var(--c-hairline);
}

/* ============================================================== card ======
   src/ui/Card.tsx — `surface` + a hairline border + `radius.lg`, in BOTH themes.

   The border is not decoration. The page and the card are the same colour, so
   the line is the only thing saying an object is there; drop it in light mode
   and a card renders as literally nothing. The shadow is light-mode only and
   deliberately tiny — it separates a card from a card, not a card from the page.
   ========================================================================== */

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
  border-radius: var(--r-lg);
  padding: var(--s-lg);
  box-shadow: 0 1px 3px rgb(10 10 11 / .04);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .card { box-shadow: none; }
}
[data-theme='dark'] .card { box-shadow: none; }

/*
 * A card nested INSIDE an elevated panel. It is already a colour step down from
 * the card it sits in, so it needs neither border nor shadow — a border there
 * reads as a table cell.
 */
.card--alt {
  background: var(--c-surface-alt);
  border: 0;
  box-shadow: none;
}

/* A card that is a link. The lift is the web's stand-in for the app's press. */
a.card, .card--go {
  transition: border-color .2s ease, box-shadow .2s ease, transform .2s cubic-bezier(.22,1,.36,1);
}
a.card:hover, .card--go:hover {
  border-color: var(--c-ink-faint);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgb(10 10 11 / .06);
}

.card__title { font: var(--t-card-title); color: var(--c-ink); margin: 0; }
.card__meta  { font: var(--t-meta); color: var(--c-ink-muted); margin: 0; }

/* ============================================================== row =======
   src/ui/Row.tsx — a leading glyph in a rounded well, a title over one line of
   meta, and one trailing accessory.
   ========================================================================== */

.row {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-md);
  border-radius: var(--r-lg);
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
}

.row--alt { background: var(--c-surface-alt); border: 0; }
/* Inside a list that draws its own dividers — a box around every item there is
   a box too many. */
.row--plain { background: none; border: 0; border-radius: 0; }

.row__well {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--c-surface-alt);
  color: var(--c-ink);
}
.row--alt .row__well { background: var(--c-surface-high); }
.row__well svg { width: 19px; height: 19px; }

.row__body  { flex: 1; min-width: 0; display: grid; gap: 1px; }
.row__title { font: var(--t-body-med); color: var(--c-ink); margin: 0; }
.row__meta  { font: var(--t-meta); color: var(--c-ink-muted); margin: 0; }

.row__value { text-align: right; }
.row__value b    { display: block; font: var(--t-stat-value); font-variant-numeric: tabular-nums; }
.row__value span { display: block; font: var(--t-meta); color: var(--c-ink-muted); }

.row__go { width: 13px; height: 13px; flex: none; color: var(--c-ink-faint); }

a.row { transition: border-color .2s ease, background .2s ease; }
a.row:hover { border-color: var(--c-ink-faint); }
a.row.row--plain:hover { background: var(--c-surface-alt); }

/* ============================================================== chip ======
   src/ui/Chip.tsx — the small labelled pill. Four tones and no more, because
   every screen that wanted a badge used to invent one.
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px var(--s-md);
  border-radius: var(--r-sm);
  background: var(--c-surface-alt);
  color: var(--c-ink-muted);
  font: var(--t-label-strong);
  white-space: nowrap;
}
.chip--sm     { padding: 3px var(--s-sm); font: var(--t-tiny); }
.chip--solid  { background: var(--c-ink); color: var(--c-on-ink); }
.chip--accent { background: var(--c-accent-soft); color: var(--c-accent); }

/*
 * The `color` tone: an identity hue's own wash, for something that IS that
 * colour — a sport, a macro, a heart-rate zone. Pass the hue in `--chip-color`:
 *
 *   <span class="chip chip--color" style="--chip-color: var(--sport-run)">Run</span>
 *
 * 14% is Chip.tsx's own alpha.
 */
.chip--color {
  background: color-mix(in srgb, var(--chip-color) 14%, transparent);
  color: var(--chip-color);
}

/* ============================================================== stat ======
   src/ui/StatTile.tsx and src/ui/InsightCard.tsx — the lower half of every tile
   in the app: VALUE + muted unit on the left, a delta on the right. The two are
   pixel-identical on purpose, because they sit on the same dashboard and any
   disagreement between them reads as a rendering bug.
   ========================================================================== */

.stat { display: grid; gap: 2px; }
.stat__value {
  font: var(--t-stat);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink);
  margin: 0;
}
.stat__unit   { font: var(--t-meta); color: var(--c-ink-muted); }
.stat__label  { font: var(--t-card-title); color: var(--c-ink); margin: 0; }
.stat__detail { font: var(--t-meta); color: var(--c-ink-muted); margin: 0; }

.stat__delta       { font: var(--t-label-strong); }
.stat__delta--up   { color: var(--semantic-good); }
.stat__delta--down { color: var(--semantic-bad); }
.stat__delta--flat { color: var(--c-ink-muted); }

/* =========================================================== section ======
   src/ui/Section.tsx — a titled block with a fixed vertical rhythm. Both
   numbers come from `layout.section` / `layout.sectionGap`, once, so the
   distance between two blocks never depends on which block it is.
   ========================================================================== */

.sec         { margin-top: var(--l-section); display: grid; gap: var(--l-section-gap); }
.sec--first  { margin-top: 0; }
.sec__head   { display: flex; align-items: center; gap: var(--s-sm); }
.sec__title  { font: var(--t-section); color: var(--c-ink); margin: 0; flex: 1; }
.sec__action { font: var(--t-label); color: var(--c-accent); }
.sec__caption{ font: var(--t-meta); color: var(--c-ink-muted); margin: 2px 0 0; }

/*
 * The hairline between rows. Inset to the text column rather than the container
 * edge — a rule that runs under a leading glyph cuts the glyph off from its own
 * label.
 */
.divider { height: 1px; background: var(--c-hairline); border: 0; margin: 0; }
.divider--inset { margin-left: calc(36px + var(--s-md)); }

/* ============================================================= field ======
   src/ui/Field.tsx — an input is `surfaceAlt`, which is the only grey the light
   theme has and is always a small object inside a card, never a page.
   ========================================================================== */

.field {
  width: 100%;
  height: var(--l-row);
  padding-inline: var(--s-md);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--c-surface-alt);
  color: var(--c-ink);
  font: var(--t-body);
}
.field::placeholder { color: var(--c-ink-faint); }
.field:focus { outline: 0; border-color: var(--c-accent); background: var(--c-surface); }

/* ============================================================== bar =======
   src/ui/Bar.tsx and src/ui/MacroBar.tsx — a track plus a fill. The track is
   the one grey that survives in light mode; the fill carries the identity
   colour, passed in `--bar-fill`.
   ========================================================================== */

.bar {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--c-bar-track);
  overflow: hidden;
}
.bar__fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--bar-fill, var(--c-accent));
}
