/* Vendored from design-system/css/df-utilities.css.
   Re-vendor via scripts/sync_design_system.sh; import a new drop with --import.
   Do not hand-edit this file. */

/* =============================================================
   df-utilities.css — Downfield Forecast utility classes
   Pairs with colors_and_type.css + df.css.
   Link order: colors_and_type.css FIRST, then df.css, then this file.

   Principle: utility names match design-system ROLES, not raw tokens.
   `.df-text-muted` (semantic) over `.df-text-ink-4` (token-name) so
   the system can re-bind what "muted" means later (e.g. dark mode)
   without rewriting templates. Curated for the cases that actually
   came up in the 54-template port — not a Tailwind clone.
   ============================================================= */

/* ---------- TEXT COLOR ---------- */
/* TEXT COLOR — every utility below is AA-safe for normal text on all four
   paper surfaces in both themes (worst case 4.57:1, on --paper-4).

   That is true because the foreground ladder maps 1:1 onto the ink ramp:
   --fg -> --ink, --fg-2 -> --ink-2, --fg-muted -> --ink-3,
   --fg-subtle -> --ink-4. Before v0.3.2 the ladder skipped --ink-2, which
   pushed .df-text-subtle onto --ink-5 (1.91:1 on --paper-4) — a token WCAG
   exempts as disabled-only. It read as the next step of a ramp and got used
   222 times for live content, because a class name one line below
   .df-text-muted will always beat a warning in a changelog.

   So: do NOT repoint any --fg-* role at --ink-5, and do NOT add a
   .df-text-* utility for it. --ink-5 is for disabled controls and for
   --border-strong, where the text threshold does not apply. If you need a
   step below --fg-subtle, there isn't one — see colors_and_type.css.

   ---- THE CALL-SITE RULE (v0.3.3) ----
   The rule above is a rule about the SYSTEM. This is the rule about YOUR CODE,
   and it is the one that actually protects you:

     Consume a --fg-* role. Never name an --ink-* rung outside this token file.

   A role is the thing the system can re-point later. A rung named at a call
   site is frozen at the moment it was written — so when v0.3.2 realigned the
   ladder, every site that reached --ink-5 THROUGH the role was fixed, and every
   site that named --ink-5 DIRECTLY was not. One consumer had 64 of the latter
   (63 inline styles across 26 templates, one local rule), all still at 2.41:1
   after the drop. Not one of them was a disabled control.

   The same applies in SVG: fill= and stroke= on a <text> element are text color
   and are subject to the same AA threshold, but a sweep that greps for "color:"
   will not see them. Our own reference chart had two axis labels at
   fill="var(--ink-5)" for exactly this reason — found and fixed in v0.3.3.

   Roles, in order: --fg, --fg-2, --fg-muted, --fg-subtle.
   Borders: --border, --border-soft, --border-hover, --border-strong.
   State edges: --pos-border, --neg-border, --warn-border, --info-border. Not the
   -soft tokens — those are surfaces, and they vanish as borders in dark mode.
   --ink-* rungs are raw material. They belong here and nowhere else. */
.df-text-default { color: var(--fg); }
.df-text-2       { color: var(--fg-2); }
.df-text-muted   { color: var(--fg-muted); }
.df-text-subtle  { color: var(--fg-subtle); }
.df-text-on-dark { color: var(--fg-on-dark); }

.df-text-pos     { color: var(--pos); }
.df-text-neg     { color: var(--neg); }
.df-text-warn    { color: var(--warn); }
.df-text-info    { color: var(--info); }
.df-text-brand   { color: var(--field); }
.df-text-pigskin { color: var(--pigskin); }
.df-text-gold    { color: var(--gold-ink); }

/* ---------- TEXT TREATMENT ---------- */
.df-text-mono       { font-family: var(--font-mono); font-feature-settings: "tnum" 1, "zero" 1; }
.df-text-display    { font-family: var(--font-display); }
.df-text-reading    { font-family: var(--font-reading); }
.df-text-tnum       { font-feature-settings: "tnum" 1, "zero" 1; }   /* tabular numerals on any font */
.df-text-truncate   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.df-text-balance    { text-wrap: balance; }
.df-text-pretty     { text-wrap: pretty; }

/* ---------- BACKGROUND ---------- */
.df-bg-paper        { background: var(--paper); }
.df-bg-paper-2      { background: var(--paper-2); }
.df-bg-paper-3      { background: var(--paper-3); }
.df-bg-paper-4      { background: var(--paper-4); }
.df-bg-ink          { background: var(--ink); }
.df-bg-field-soft   { background: var(--field-soft); }
.df-bg-pigskin-soft { background: var(--pigskin-soft); }
.df-bg-pos-soft     { background: var(--pos-soft); }
.df-bg-neg-soft     { background: var(--neg-soft); }
.df-bg-warn-soft    { background: var(--warn-soft); }
.df-bg-info-soft    { background: var(--info-soft); }

/* ---------- RULES ---------- */
.df-rule-top        { border-top:    1px solid var(--rule); }
.df-rule-bottom     { border-bottom: 1px solid var(--rule); }
.df-rule-left       { border-left:   1px solid var(--rule); }
.df-rule-right      { border-right:  1px solid var(--rule); }
.df-rule-soft-top   { border-top:    1px solid var(--rule-2); }
.df-rule-soft-bottom{ border-bottom: 1px solid var(--rule-2); }

/* ---------- VERTICAL STACK ----------
   .df-stack-N  → flex column, gap = var(--s-N).
   Used everywhere a section has multiple cards or a card has
   multiple internal blocks. Maps directly to the spacing scale. */
.df-stack-1 { display: flex; flex-direction: column; gap: var(--s-1); }
.df-stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }
.df-stack-3 { display: flex; flex-direction: column; gap: var(--s-3); }
.df-stack-4 { display: flex; flex-direction: column; gap: var(--s-4); }
.df-stack-5 { display: flex; flex-direction: column; gap: var(--s-5); }
.df-stack-6 { display: flex; flex-direction: column; gap: var(--s-6); }
.df-stack-7 { display: flex; flex-direction: column; gap: var(--s-7); }
.df-stack-8 { display: flex; flex-direction: column; gap: var(--s-8); }
.df-stack-9 { display: flex; flex-direction: column; gap: var(--s-9); }

/* ---------- HORIZONTAL ROW ----------
   .df-row         → align-items: center, gap = s-2 (the default for icon+label,
                     pill clusters, button rows).
   .df-row-tight   → tighter gap (s-1) for very dense clusters.
   .df-row-loose   → roomier gap (s-4) for top-level header items.
   .df-row-between → space-between, baseline align, wraps. THE header pattern. */
.df-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.df-row-tight {
  display: flex;
  align-items: center;
  gap: var(--s-1);
}
.df-row-loose {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}
.df-row-between {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}

/* Center-baseline variant — when the right side has buttons the
   baseline-align makes them sit too low. Use when row contents
   include controls. */
.df-row-between-center {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}

/* ---------- ALIGNMENT MICRO-UTILITIES ----------
   Just the two we needed during the port — keep the surface tiny. */
.df-text-left   { text-align: left; }
.df-text-center { text-align: center; }
.df-text-right  { text-align: right; }

.df-grow        { flex: 1 1 auto; min-width: 0; }
.df-shrink-0    { flex-shrink: 0; }

/* ---------- VISIBILITY ---------- */
.df-hidden { display: none !important; }
@media (max-width: 720px) {
  .df-hide-on-mobile  { display: none !important; }
}
@media (min-width: 721px) {
  .df-hide-on-desktop { display: none !important; }
}
