/* ═══════════════════════════════════════════════════════════════════════════
   UTILITIES — single-purpose classes
   ───────────────────────────────────────────────────────────────────────────
   These exist to kill the long tail of one-declaration inline styles
   (`style="text-align:right"` × 19, `style="color:#9ca3af"` × 17, and so on).

   A utility does exactly one thing and is named after it. If you find yourself
   stacking four utilities on the same element in three different places, that
   is a component — promote it to components.css.

   This is NOT a Tailwind. It is deliberately small and closed: only patterns
   that were already repeated 4+ times in frontend.html earned a class. Do not
   grow it speculatively.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── VISIBILITY ────────────────────────────────────────────────────────────
   `is-hidden` rather than `hidden` so it never collides with the HTML
   `hidden` attribute, and so JS toggling it reads as a state, not a fact. */
.is-hidden { display: none !important; }


/* ── TEXT COLOUR ───────────────────────────────────────────────────────────
   Four intents plus two neutral steps. Anything else needs a token. */
.text-muted   { color: var(--color-text-muted); }
.text-dim     { color: var(--color-text-dim); }
.text-body    { color: var(--color-text-body); }
.text-primary { color: var(--color-primary); }
.text-strong  { color: var(--color-text-strong); }
.text-danger  { color: var(--color-danger); }
.text-success { color: var(--color-success); }
.text-warn    { color: var(--color-warn); }


/* ── TEXT SIZE ─────────────────────────────────────────────────────────────
   Only the sizes that were actually being written inline. */
.text-2xs  { font-size: var(--text-2xs); }
.text-xs   { font-size: var(--text-xs); }
.text-sm   { font-size: var(--text-sm); }
.text-base { font-size: var(--text-base); }


/* ── TEXT WEIGHT & ALIGNMENT ─────────────────────────────────────────────── */
.fw-normal   { font-weight: var(--weight-normal); }
.fw-medium   { font-weight: var(--weight-medium); }
.fw-semibold { font-weight: var(--weight-semibold); }
.fw-bold     { font-weight: var(--weight-bold); }

.ta-left   { text-align: left; }
.ta-center { text-align: center; }
.ta-right  { text-align: right; }

.nowrap { white-space: nowrap; }
.mono   { font-family: var(--font-mono); }

/* Caps a run of prose at a readable line length. A paragraph inside a card is
   as wide as the card, and the cards on a desktop page are ~1150px — around
   150 characters at --text-xs, roughly twice what anybody can track back to
   the start of the next line. 68ch is the measure /styleguide already sets on
   its own body copy (.sg-lede, .sg-section > p), so this is that number
   getting a name rather than a new one. Explanatory copy only: never put it
   on a control, a table or a card. */
.measure { max-width: 68ch; }


/* ── FLEX & GRID ───────────────────────────────────────────────────────────
   The handful of layout shapes the app repeats. `.row` is a horizontal flex
   line with the app's default gap; the -sm / -lg variants cover the two other
   gaps that were being written inline. */
.row     { display: flex; align-items: center; gap: var(--space-3); }
.row-sm  { display: flex; align-items: center; gap: var(--space-1); }
.row-lg  { display: flex; align-items: center; gap: var(--space-4); }
.row-wrap { flex-wrap: wrap; }
.row-top  { align-items: flex-start; }
/* A row of stacked label+control pairs plus the button that acts on them: the
   controls share a bottom edge and the labels sit above their own control.
   Was `style="align-items:flex-end"` on the two filter rows that need it. */
.row-end  { align-items: flex-end; }
/* Pushes the first and last child of a .row apart — a section label on the
   left, the buttons that act on it on the right. */
.justify-between { justify-content: space-between; }

.flex-1  { flex: 1; }
.w-full  { width: 100%; }

/* Gap overrides for .row, indexed by space token like the margins. */
.gap-1 { gap: var(--space-1); }   /*  4px */
.gap-2 { gap: var(--space-2); }   /*  6px */
.gap-4 { gap: var(--space-4); }   /* 10px */
.gap-5 { gap: var(--space-5); }   /* 12px */

/* Says "this element responds to a click" — on rows and cells that carry an
   onclick but are not buttons. */
.clickable { cursor: pointer; }

/* Responsive grids. The column counts collapse at the breakpoints in
   responsive.css — which is the whole reason these exist as classes rather
   than inline styles: a media query cannot reach an inline style. */
.auto-grid    { display: grid; }
.auto-grid-2  { grid-template-columns: 1fr 1fr; }
.auto-grid-3  { grid-template-columns: repeat(3, minmax(0,1fr)); }
.auto-grid-4  { grid-template-columns: repeat(4, minmax(0,1fr)); }
.auto-grid-5  { grid-template-columns: repeat(5, 1fr); }
.photo-grid   { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

/* A row of .stat-tile. */
.stat-row { display: flex; gap: var(--space-5); flex-wrap: wrap; margin-bottom: var(--space-7); }


/* ── SPACING ───────────────────────────────────────────────────────────────
   THE NUMBER IS THE SPACE TOKEN INDEX. `.mb-4` is `--space-4`, which is 10px.
   It is not "the fourth margin size".

   This matters because the first cut of these utilities numbered them
   sequentially (mb-2=8px, mb-3=12px, mb-4=14px), which left the app's most
   common gap — 10px, 23 times inline — with no name you could give it without
   lying. Indexing by token means every step that exists in the scale is
   reachable, and a reader can go from class to value without a lookup. */
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: var(--space-3); }   /*  8px */
.mb-4 { margin-bottom: var(--space-4); }   /* 10px */
.mb-5 { margin-bottom: var(--space-5); }   /* 12px */
.mb-6 { margin-bottom: var(--space-6); }   /* 14px */
.mb-7 { margin-bottom: var(--space-7); }   /* 16px */
.mt-1 { margin-top: var(--space-1); }      /*  4px */
.mt-3 { margin-top: var(--space-3); }      /*  8px */
.mt-4 { margin-top: var(--space-4); }      /* 10px */
.mt-6 { margin-top: var(--space-6); }      /* 14px */
.mt-7 { margin-top: var(--space-7); }      /* 16px */
.ml-6 { margin-left: var(--space-6); }     /* 14px */


/* ── SCROLL ────────────────────────────────────────────────────────────────
   For a .card whose contents can outgrow it. Kept as a utility because
   .card deliberately does not clip (see components.css). */
.scroll   { overflow: auto;   -webkit-overflow-scrolling: touch; }
.scroll-y { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* LEGACY BRIDGE — momentum scrolling for the ~20 cards that still declare
   overflow inline (usually alongside a one-off max-height). Delete this rule
   once they all use a .scroll utility; until then, removing it would quietly
   make those panels scroll badly on iOS. */
.card[style*="overflow"] { -webkit-overflow-scrolling: touch; }
