/*
 * TradieOS design tokens — the single source of truth.
 * Generated from DESIGN.md ("The Job Sheet") + .impeccable/design.json.
 *
 * Colour system: "Slate + Hi-Vis Amber". A neutral slate UI carries a single
 * hi-vis orange action colour (workwear energy). The warning status is a distinct
 * yellow so it never reads as the brand orange. Only --color-accent is themeable
 * per tenant; the status colours are fixed product vocabulary.
 *
 * Naming: --brand-* / --slate-* are the raw tonal ramps; the semantic aliases
 * below (--color-*) are what components consume. Change a ramp step and every
 * semantic token built on it follows.
 */

:root {
  /* ============ RAW RAMPS ============ */

  /* Brand — Hi-Vis Amber (orange) */
  --brand-50:  #fff7ed;
  --brand-100: #ffedd5;
  --brand-200: #fed7aa;
  --brand-300: #fdba74;
  --brand-400: #fb923c;
  --brand-500: #f97316;
  --brand-600: #ea580c;  /* primary action */
  --brand-700: #c2410c;  /* hover / pressed */
  --brand-800: #9a3412;
  --brand-900: #7c2d12;

  /* Neutral — Slate */
  --slate-50:  #f8fafc;
  --slate-100: #f1f5f9;
  --slate-200: #e2e8f0;
  --slate-300: #cbd5e1;
  --slate-400: #94a3b8;
  --slate-500: #64748b;
  --slate-600: #475569;
  --slate-700: #334155;
  --slate-800: #1e293b;
  --slate-900: #0f172a;

  /* ============ SEMANTIC ALIASES (what components use) ============ */

  /* neutrals */
  --color-ink: var(--slate-900);        /* primary text / dark sidebar base */
  --color-ink-soft: var(--slate-700);   /* secondary text */
  --color-muted: var(--slate-500);      /* captions, labels, metadata (AA on white) */
  --color-line: var(--slate-200);       /* borders + dividers */
  --color-canvas: var(--slate-100);     /* app background */
  --color-surface: #ffffff;             /* cards + sheets */
  --color-surface-raised: #fbfdff;      /* nested rows inside a card */
  --color-surface-muted: var(--slate-200); /* secondary button fill */

  /* accent (THEMEABLE per tenant) */
  --color-accent: var(--brand-600);
  --color-accent-strong: var(--brand-700);
  --color-accent-surface: var(--brand-50);   /* tint for selected/hover backgrounds */

  /* brand secondary */
  --color-teal: #0d9488;                /* retained for the identity gradient only */

  /* status (FIXED — never themed). Warning is a true yellow to clear the brand orange. */
  --color-success: #047857;  --color-success-surface: #d1fae5;
  --color-warning: #854d0e;  --color-warning-surface: #fef9c3;   /* yellow, not amber */
  --color-danger:  #dc2626;  --color-danger-surface:  #fee2e2;
  --color-pending: #6d28d9;  --color-pending-surface: #ede9fe;
  --color-info:    #1d4ed8;  --color-info-surface:    #dbeafe;

  /* ============ TYPOGRAPHY ============ */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --text-display: clamp(1.75rem, 4vw, 2.25rem); /* 28–36px */
  --text-headline: 1.375rem;   /* 22px */
  --text-metric: 1.75rem;      /* 28px — KPI/money */
  --text-title: 1rem;          /* 16px */
  --text-body: 0.9375rem;      /* 15px — default (field floor) */
  --text-label: 0.75rem;       /* 12px — uppercase tracked; absolute floor */

  --leading-tight: 1.15;  --leading-snug: 1.3;  --leading-normal: 1.5;
  --tracking-tight: -0.02em;  --tracking-label: 0.06em;
  --weight-regular: 400;  --weight-medium: 500;  --weight-semibold: 600;  --weight-bold: 700;  --weight-black: 800;

  /* ============ SPACING (4px base) ============ */
  --space-2xs: 4px;  --space-xs: 8px;  --space-sm: 12px;  --space-md: 16px;
  --space-lg: 24px;  --space-xl: 32px;  --space-2xl: 48px;

  /* ============ RADIUS ============ */
  --radius-sm: 6px;  --radius-md: 10px;  --radius-lg: 14px;  --radius-xl: 20px;  --radius-pill: 9999px;

  /* ============ ELEVATION (flat-by-default) ============ */
  --shadow-resting: 0 1px 2px rgba(15,23,42,.06), 0 4px 12px rgba(15,23,42,.06);
  --shadow-floating: 0 4px 6px rgba(15,23,42,.05), 0 10px 24px rgba(15,23,42,.10);
  --shadow-alert-ring: 0 0 0 3px rgba(220,38,38,.06), 0 1px 2px rgba(15,23,42,.06), 0 4px 12px rgba(15,23,42,.06);
  --shadow-focus: 0 0 0 3px rgba(234,88,12,.28);   /* orange focus ring */

  /* ============ MOTION ============ */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast: 120ms;

  /* ============ FIELD-HARDENING ============ */
  --tap-min: 44px;      /* minimum touch target */
  --tap-dense: 32px;    /* small inline-row action variant only */

  /* Brand identity gradients */
  --grad-brand: linear-gradient(135deg, var(--brand-400), var(--brand-600));
  --grad-hero:  linear-gradient(135deg, var(--brand-900), var(--brand-600) 55%, #f59e0b);

  /* Breakpoints (documented; applied in media queries / Tailwind):
   * --bp-sm: 640px; --bp-md: 768px; --bp-lg: 1024px; --bp-xl: 1280px;
   * Dashboard sidebar collapses to a bottom tab bar below lg. */
}

/*
 * TradieOS component layer — framework-agnostic, drop-in classes built on tokens.css.
 * These are the canonical `ds-*` primitives from DESIGN.md's Components section and
 * the .impeccable/design.json sidecar. React components in @tradieos/ui wrap these;
 * plain-HTML surfaces (and the CDN prototype) can use them directly.
 *
 * Requires tokens.css to be loaded first (provides the --color-*, --space-*, etc.).
 * Every interactive element meets the 44px field target; the -sm variant (32px) is
 * for dense inline row actions ONLY.
 */

/* ---------- Buttons ---------- */
.ds-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-xs);
  min-height: var(--tap-min); padding: 0 18px;
  font-family: var(--font-sans); font-size: var(--text-body); font-weight: var(--weight-semibold);
  border: none; border-radius: var(--radius-md); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), filter var(--dur-fast) var(--ease-standard);
}
.ds-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.ds-btn--sm { min-height: var(--tap-dense); padding: 0 12px; font-size: var(--text-label); font-weight: var(--weight-semibold); letter-spacing: normal; text-transform: none; }

.ds-btn--primary { background: var(--color-accent); color: #fff; }
.ds-btn--primary:hover { background: var(--color-accent-strong); }
.ds-btn--secondary { background: var(--color-surface-muted); color: var(--color-ink-soft); }
.ds-btn--secondary:hover { filter: brightness(0.97); }
.ds-btn--success { background: var(--color-success); color: #fff; }
.ds-btn--warning { background: var(--color-warning); color: #fff; }
.ds-btn--danger { background: var(--color-danger); color: #fff; }
.ds-btn--pending { background: var(--color-pending); color: #fff; }
.ds-btn--primary:hover, .ds-btn--success:hover, .ds-btn--warning:hover,
.ds-btn--danger:hover, .ds-btn--pending:hover { filter: brightness(1.06); }

/* ---------- Inputs ---------- */
.ds-label { display: block; margin: 0 0 var(--space-2xs); font-size: var(--text-label); font-weight: var(--weight-semibold); letter-spacing: normal; text-transform: none; color: var(--color-ink-soft); }
.ds-input, .ds-select, .ds-textarea {
  width: 100%; min-height: var(--tap-min); padding: 0 14px;
  background: var(--color-surface); color: var(--color-ink);
  font-family: var(--font-sans); font-size: var(--text-body);
  border: 1.5px solid var(--color-line); border-radius: var(--radius-md);
}
.ds-textarea { padding: 10px 14px; line-height: var(--leading-normal); }
.ds-input:focus-visible, .ds-select:focus-visible, .ds-textarea:focus-visible {
  outline: none; border-color: var(--color-accent); box-shadow: var(--shadow-focus);
}

/* ---------- Cards ---------- */
.ds-card {
  background: var(--color-surface); border: 1px solid var(--color-line);
  border-radius: var(--radius-lg); padding: var(--space-lg); box-shadow: var(--shadow-resting);
}
.ds-card--alert { border-color: #fca5a5; box-shadow: var(--shadow-alert-ring); }
.ds-card__title { margin: 0 0 var(--space-sm); font-size: var(--text-title); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.ds-row { border: 1px solid var(--color-line); border-radius: var(--radius-md); padding: var(--space-sm) var(--space-md); background: var(--color-surface-raised); }

/* ---------- KPI / metric ---------- */
.ds-kpi { font-family: var(--font-sans); font-size: var(--text-metric); font-weight: var(--weight-black); letter-spacing: var(--tracking-tight); color: var(--color-ink); font-variant-numeric: tabular-nums; }
.ds-kpi__label { margin-top: var(--space-2xs); font-size: var(--text-label); font-weight: var(--weight-medium); letter-spacing: normal; text-transform: none; color: var(--color-muted); }
.ds-money { font-variant-numeric: tabular-nums; }

/* ---------- Status chips (colour + icon + word) ---------- */
.ds-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 10px; border-radius: var(--radius-pill);
  font-family: var(--font-sans); font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: 0.01em;
}
.ds-chip::before { font-weight: 700; }
.ds-chip--success { background: var(--color-success-surface); color: var(--color-success); }
.ds-chip--success::before { content: "\2713"; }        /* ✓ */
.ds-chip--warning { background: var(--color-warning-surface); color: var(--color-warning); }
.ds-chip--warning::before { content: "\26A0"; }        /* ⚠ */
.ds-chip--danger  { background: var(--color-danger-surface);  color: var(--color-danger); }
.ds-chip--danger::before  { content: "\26A0"; }        /* ⚠ */
.ds-chip--pending { background: var(--color-pending-surface); color: var(--color-pending); }
.ds-chip--pending::before { content: "\25CF"; }        /* ● */
.ds-chip--info    { background: var(--color-info-surface);    color: var(--color-info); }
.ds-chip--info::before    { content: "\2192"; }        /* → */
.ds-chip--neutral { background: var(--color-surface-muted); color: var(--color-muted); }
.ds-chip--neutral::before { content: "\2013"; }        /* – */

/* ---------- Navigation (dark sidebar) ---------- */
.ds-nav { display: flex; flex-direction: column; gap: 2px; }
.ds-nav__section { font-size: var(--text-label); letter-spacing: var(--tracking-label); text-transform: uppercase; color: #64748b; padding: var(--space-sm) var(--space-sm) var(--space-2xs); }
.ds-nav__item {
  display: flex; align-items: center; gap: 10px; min-height: var(--tap-min); padding: 0 var(--space-sm);
  border-radius: var(--radius-md); color: #cbd5e1; font-size: 0.875rem; font-weight: var(--weight-medium);
  text-decoration: none; cursor: pointer;
}
.ds-nav__item:hover { background: rgba(255,255,255,.06); }
.ds-nav__item--active { background: var(--color-accent); color: #fff; }
.ds-nav__badge { margin-left: auto; background: var(--color-danger); color: #fff; font-size: 0.65rem; font-weight: 700; border-radius: var(--radius-pill); padding: 1px 7px; }

/* ==========================================================================
 * SLICE 4 ADDITIONS — classes the React primitives in `src/` need and the
 * stub above does not define. Everything above this banner is byte-identical
 * to the committed stub and `test/css-stub.test.ts` asserts that it stays so:
 * this file grows by appending, never by rewriting.
 * ========================================================================== */

/* ---------- Buttons: restrained danger ----------
 * DESIGN.md's Buttons section is explicit that a destructive action is "red
 * text + 1px red border on white — NOT a fill", and that a filled red is "used
 * only for a final, unambiguous destructive confirm". The stub's
 * `.ds-btn--danger` is a fill, so it is the *confirm* variant, and the ordinary
 * destructive action needed a class that did not exist. Written as an addition
 * rather than a correction because the stub is append-only. */
.ds-btn--danger-quiet {
  background: var(--color-surface); color: var(--color-danger);
  border: 1px solid var(--color-danger);
}
.ds-btn--danger-quiet:hover { background: var(--color-danger-surface); }

/* ---------- Buttons: the disabled state ----------
 * The stub styles no disabled state at all, and ISS-18 is where that started
 * costing something. A quote may not be sent while any line is still "Please
 * select", so **Send quote** now sits shut beneath a warning naming the lines
 * that shut it — and it sat there at full accent, indistinguishable from the
 * recommended action, inviting the press it would refuse. DESIGN.md's One
 * Signal Rule cuts both ways: the accent fill promises the primary action is
 * available, and a control that cannot be used must not wear it.
 *
 * The fill goes neutral rather than merely translucent. Fading the accent to
 * 45% opacity composites the white label down to 1.80:1 against its own
 * background, which trades a misleading button for an unreadable one; muted ink
 * on the secondary fill measures 3.86:1 — better than the enabled primary's own
 * 3.56:1 — and still reads as switched off. `not-allowed` carries the same fact
 * to the cursor, so it is never colour alone.
 *
 * Appended rather than written into the Buttons block above, for the reason
 * `.ds-btn--danger-quiet` gives: the stub is append-only. These win on cascade
 * order over the variant fills, and the `:hover` rule outranks the brightness
 * group above it, so hovering a dead button no longer lights it up. */
.ds-btn:disabled { background: var(--color-surface-muted); color: var(--color-muted); cursor: not-allowed; }
.ds-btn:disabled:hover { filter: none; }

/* ---------- Badges (count / tag) ----------
 * The stub's `.ds-nav__badge` is the sidebar count badge and stays the nav's;
 * this is the standalone badge, sized to the label ramp rather than the nav's
 * one-off 0.65rem. Never colour alone: a badge always carries its number or
 * word as text content. */
.ds-badge {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2xs);
  min-width: 20px; padding: 1px 8px;
  font-family: var(--font-sans); font-size: var(--text-label); font-weight: var(--weight-bold);
  line-height: 1.5; border-radius: var(--radius-pill);
  font-variant-numeric: tabular-nums;
}
.ds-badge--neutral { background: var(--color-surface-muted); color: var(--color-ink-soft); }
.ds-badge--accent  { background: var(--color-accent);         color: #fff; }
.ds-badge--success { background: var(--color-success-surface); color: var(--color-success); }
.ds-badge--warning { background: var(--color-warning-surface); color: var(--color-warning); }
.ds-badge--danger  { background: var(--color-danger);          color: #fff; }
.ds-badge--pending { background: var(--color-pending-surface); color: var(--color-pending); }
.ds-badge--info    { background: var(--color-info-surface);    color: var(--color-info); }

/* ---------- Fields: error state ----------
 * DESIGN.md: "Error: danger border + a text message with an icon; never a red
 * border alone." The icon is injected here so no React code has to ship a
 * glyph, exactly as `.ds-chip::before` does for status. */
.ds-field { display: block; }
.ds-field__hint { margin-top: var(--space-2xs); font-size: var(--text-label); font-weight: var(--weight-medium); letter-spacing: normal; text-transform: none; color: var(--color-muted); }
.ds-field__error {
  display: flex; align-items: center; gap: 5px;
  margin-top: var(--space-2xs);
  font-size: var(--text-label); font-weight: var(--weight-semibold); letter-spacing: normal; text-transform: none;
  color: var(--color-danger);
}
.ds-field__error::before { content: "\26A0"; font-weight: 700; }   /* ⚠ */
.ds-input--invalid, .ds-select--invalid, .ds-textarea--invalid { border-color: var(--color-danger); }
.ds-input--invalid:focus-visible, .ds-select--invalid:focus-visible, .ds-textarea--invalid:focus-visible {
  border-color: var(--color-danger); box-shadow: 0 0 0 3px rgba(220,38,38,.28);
}

/* ---------- Tables ----------
 * Label typography on headers, tabular numerals on numeric cells (DESIGN.md's
 * Tabular Money Rule). Rows sit on Surface Raised, never a second grey. */
.ds-table__wrap { width: 100%; overflow-x: auto; }
.ds-table { width: 100%; border-collapse: collapse; font-family: var(--font-sans); font-size: var(--text-body); color: var(--color-ink); }
.ds-table__caption { caption-side: top; text-align: left; padding-bottom: var(--space-sm); font-size: var(--text-title); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.ds-table__header {
  padding: var(--space-xs) var(--space-sm); text-align: left; vertical-align: middle;
  font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--color-muted);
  border-bottom: 1px solid var(--color-line); white-space: nowrap;
}
/* `height`, not `min-height`: a `<td>` is `display: table-cell`, where CSS
 * declares `min-height` inapplicable — the declaration that used to sit here
 * was inert, and a plain-text row measured 43px in Chrome, one pixel under the
 * field floor. On a table cell `height` is treated as a MINIMUM, and the
 * explicit `box-sizing` is what makes that minimum 44px whether or not the
 * consuming app ships Tailwind's preflight; without it the bare content-box
 * default renders 69px. Measured both ways, and with the padding cut to 2px,
 * which the inert declaration did not survive. */
.ds-table__cell { box-sizing: border-box; padding: var(--space-sm); vertical-align: middle; height: var(--tap-min); border-bottom: 1px solid var(--color-line); }
.ds-table__row:last-child .ds-table__cell { border-bottom: none; }
.ds-table__row--raised { background: var(--color-surface-raised); }
.ds-table__cell--numeric, .ds-table__header--numeric { text-align: right; font-variant-numeric: tabular-nums; }
.ds-table__empty { padding: var(--space-lg) var(--space-sm); text-align: center; color: var(--color-muted); }

/* ---------- Dialog ----------
 * 20px+ radius and the floating shadow (DESIGN.md Shapes + Elevation); the
 * backdrop is a flat scrim, not a blur. */
.ds-dialog__backdrop {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-md);
  background: rgba(15,23,42,.45);
}
.ds-dialog {
  position: relative;
  width: 100%; max-width: 520px; max-height: 100%; overflow-y: auto;
  background: var(--color-surface); color: var(--color-ink);
  border-radius: var(--radius-xl); padding: var(--space-lg);
  box-shadow: var(--shadow-floating);
  font-family: var(--font-sans); font-size: var(--text-body);
}
.ds-dialog:focus-visible { outline: none; box-shadow: var(--shadow-focus), var(--shadow-floating); }
.ds-dialog__title { margin: 0 var(--tap-min) var(--space-sm) 0; font-size: var(--text-title); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.ds-dialog__body { color: var(--color-ink-soft); }
.ds-dialog__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-xs); margin-top: var(--space-lg); }
.ds-dialog__close {
  /* Top-right of the panel. Without this the button sits in the flow between
   * the title and the body, which is where it actually rendered. The panel is
   * the positioned ancestor. */
  position: absolute; top: var(--space-sm); right: var(--space-sm);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap-min); min-height: var(--tap-min);
  background: transparent; border: none; border-radius: var(--radius-md);
  color: var(--color-muted); cursor: pointer;
}
.ds-dialog__close::before { content: "\00D7"; font-size: 1.25rem; line-height: 1; }   /* × */
.ds-dialog__close:hover { background: var(--color-surface-muted); }
.ds-dialog__close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* ---------- Toast ----------
 * Status is a word here too: the tone tints the strip and injects the glyph,
 * and the component requires a title, so a toast can never be colour alone. */
.ds-toast__viewport {
  position: fixed; z-index: 60; bottom: var(--space-md); left: var(--space-md); right: var(--space-md);
  display: flex; flex-direction: column; gap: var(--space-xs);
  pointer-events: none;
}
.ds-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-surface); color: var(--color-ink);
  border: 1px solid var(--color-line); border-left-width: 4px;
  border-radius: var(--radius-md); box-shadow: var(--shadow-floating);
  font-family: var(--font-sans); font-size: var(--text-body);
}
.ds-toast::before { font-weight: 700; line-height: var(--leading-normal); }
.ds-toast__content { flex: 1; min-width: 0; }
.ds-toast__title { font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.ds-toast__body { margin-top: 2px; color: var(--color-ink-soft); }
.ds-toast__close {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap-dense); min-height: var(--tap-dense);
  background: transparent; border: none; border-radius: var(--radius-sm);
  color: var(--color-muted); cursor: pointer;
}
.ds-toast__close::before { content: "\00D7"; font-size: 1.1rem; line-height: 1; }   /* × */
.ds-toast__close:hover { background: var(--color-surface-muted); }
.ds-toast__close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.ds-toast--success { border-left-color: var(--color-success); color: var(--color-ink); }
.ds-toast--success::before { content: "\2713"; color: var(--color-success); }   /* ✓ */
.ds-toast--warning { border-left-color: var(--color-warning); }
.ds-toast--warning::before { content: "\26A0"; color: var(--color-warning); }   /* ⚠ */
.ds-toast--danger  { border-left-color: var(--color-danger); }
.ds-toast--danger::before  { content: "\26A0"; color: var(--color-danger); }    /* ⚠ */
.ds-toast--pending { border-left-color: var(--color-pending); }
.ds-toast--pending::before { content: "\25CF"; color: var(--color-pending); }   /* ● */
.ds-toast--info    { border-left-color: var(--color-info); }
.ds-toast--info::before    { content: "\2192"; color: var(--color-info); }      /* → */
.ds-toast--neutral { border-left-color: var(--color-line); }
.ds-toast--neutral::before { content: "\2013"; color: var(--color-muted); }     /* – */

/* ---------- Navigation: the focus ring the stub never gave it ----------
 * `.ds-nav__item` is stub content and the stub is append-only, so this is an
 * added rule winning on cascade order rather than an edit to the block above.
 *
 * It does NOT use `--shadow-focus`. That token is `rgba(234,88,12,.28)`, and
 * composited over the sidebar's `#0f172a` it resolves to `rgb(76,41,34)` — a
 * contrast ratio of 1.4:1 against the surface it sits on, which is less visible
 * than the `rgb(0,95,204)` user-agent ring it would be replacing (2.98:1). The
 * translucent token is built for the light surfaces every other primitive sits
 * on. A solid accent ring measures 5.02:1 here, so the dark sidebar gets the
 * solid one; this is the "focused element" appearance DESIGN.md's One Signal
 * Rule already sanctions for the accent. */
.ds-nav__item:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent); }

@media (min-width: 640px) {
  .ds-toast__viewport { left: auto; width: 380px; }
}

/*
 * The marketing layer, over `@tradieos/ui`'s tokens.css and components.css.
 *
 * ## What this file is allowed to contain
 *
 * Not one colour, type size, radius, shadow or gap of its own. Every value here
 * is a `var(--…)` from tokens.css, or a `calc()` over two of them, which is what
 * keeps DESIGN.md's One-Rhythm Rule ("every gap is a token step, no 13px, no
 * eyeballed padding") true by construction rather than by care.
 * `test/design-rules.test.ts` reads this file and fails on a hex colour or a raw
 * pixel length outside the small allowlist declared there — the same guard
 * `packages/ui` runs over its own TypeScript.
 *
 * The three exceptions are declared as named variables immediately below, each
 * with the sentence in DESIGN.md that fixes its value.
 *
 * ## The register
 *
 * DESIGN.md's Layout section: "Booking / marketing (Persuade): wider gutters,
 * larger type, one clear primary action per viewport, centred single-column
 * reading order." So section headings are centred and capped at a reading
 * measure, the hero lede is set at Headline rather than Body, and the vertical
 * rhythm is the top of the spacing scale. Card grids are the one multi-column
 * arrangement, and they collapse at `lg` exactly as the Grids rule requires —
 * not at a break chosen for how it looked.
 */

:root {
  /* "operator content caps at ~1120px" — Layout, Content measure. */
  --m-shell-max: 1120px;
  /* "reading/marketing copy caps at 65–75ch" — same sentence. */
  --m-prose-max: 68ch;
  /* The illustrative card is a phone-width object; it is the one element whose
   * job is to look like the product on a phone, so it keeps a phone's column. */
  --m-sheet-max: 420px;

  /* Text on the Ink bands (hero, footer). tokens.css names Ink as "primary text
   * and the dark sidebar base" but ships no aliases for what sits *on* it, and
   * the dashboard sidebar reaches for the slate ramp the same way. Both clear
   * the AA floor against Ink, and the ratios are computed rather than claimed —
   * see the contrast table in test/design-rules.test.ts. */
  --m-on-ink: var(--slate-300);
  --m-on-ink-muted: var(--slate-400);
}

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

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

html {
  /* Stops iOS inflating body copy in landscape, which would break the ramp. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  /* The 15px field floor is the *default*, not a special case. */
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  line-height: var(--leading-normal);
  color: var(--color-ink);
  background: var(--color-surface);
  -webkit-font-smoothing: antialiased;
}

/*
 * "Do keep a visible :focus-visible ring on every interactive element."
 * components.css gives one to every `ds-` primitive; this covers the anchors
 * this surface adds. The radius keeps the ring's corner on the shape it rings.
 */
a:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}

/* ---------- Skip link ---------- */

/*
 * Moved off-screen with a transform rather than a negative offset, so it keeps
 * its box and its focus ring when it comes back. It is the first focusable
 * element in the document.
 */
.m-skip {
  position: absolute;
  z-index: 2;
  top: var(--space-sm);
  left: var(--space-sm);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-md);
  border-radius: var(--radius-md);
  background: var(--color-accent);
  color: var(--color-surface);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  transform: translateY(-200%);
}

.m-skip:focus {
  transform: translateY(0);
}

/* ---------- Shell ---------- */

.m-shell {
  width: 100%;
  max-width: var(--m-shell-max);
  margin: 0 auto;
  /* The "wider gutters" half of the Persuade register. */
  padding-left: var(--space-lg);
  padding-right: var(--space-lg);
}

@media (min-width: 768px) {
  .m-shell {
    padding-left: var(--space-xl);
    padding-right: var(--space-xl);
  }
}

/* ---------- Header ---------- */

/*
 * Deliberately not sticky. Nothing here needs to follow the reader, a fixed
 * band costs a phone its scarcest resource, and `position: fixed` is what makes
 * a full-page screenshot lie — the acceptance gate for this slice is a vision
 * review of exactly those screenshots.
 */
.m-header {
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-line);
}

.m-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  /* A field target plus one step of air, rather than a chosen bar height. */
  min-height: calc(var(--tap-min) + var(--space-md));
  padding-top: var(--space-xs);
  padding-bottom: var(--space-xs);
}

.m-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  color: var(--color-ink);
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  text-decoration: none;
}

/*
 * Teal, because DESIGN.md gives it exactly one job: "Brand mark and the logo
 * gradient identity colour. Not an action colour". Keeping the mark off the
 * accent is what leaves the accent meaning "act".
 */
.m-brand__mark {
  display: grid;
  place-items: center;
  width: var(--tap-dense);
  height: var(--tap-dense);
  border-radius: var(--radius-md);
  background: var(--color-canvas);
  color: var(--color-teal);
}

.m-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
}

.m-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-sm);
  border-radius: var(--radius-md);
  color: var(--color-ink-soft);
  font-weight: var(--weight-semibold);
  text-decoration: none;
}

.m-nav__link:hover {
  background: var(--color-canvas);
  color: var(--color-ink);
}

/* Colour would not survive greyscale or a colour-blind reader on its own, so
 * "you are here" is also a rule under the word. */
.m-nav__link[aria-current="page"] {
  color: var(--color-ink);
  box-shadow: inset 0 -2px 0 0 var(--color-accent);
}

/* ---------- Hero ---------- */

.m-hero {
  background: var(--color-ink);
  color: var(--m-on-ink);
  padding-top: var(--space-2xl);
  padding-bottom: var(--space-2xl);
  text-align: center;
}

@media (min-width: 768px) {
  .m-hero {
    padding-top: calc(var(--space-2xl) + var(--space-xl));
    padding-bottom: calc(var(--space-2xl) + var(--space-xl));
  }
}

.m-h1 {
  max-width: var(--m-prose-max);
  margin: 0 auto var(--space-md);
  color: var(--color-surface);
  font-size: var(--text-display);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.m-lede {
  max-width: var(--m-prose-max);
  margin: 0 auto var(--space-lg);
  /* Headline size at regular weight: the "larger type" the Persuade register
   * asks for, taken off the ramp rather than invented between two steps. */
  font-size: var(--text-headline);
  font-weight: var(--weight-regular);
  line-height: var(--leading-normal);
}

.m-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
}

.m-cta {
  text-decoration: none;
}

.m-hero__note {
  max-width: var(--m-prose-max);
  margin: var(--space-lg) auto 0;
  color: var(--m-on-ink-muted);
}

/* ---------- Sections ---------- */

.m-section {
  padding-top: var(--space-2xl);
  padding-bottom: var(--space-2xl);
}

@media (min-width: 768px) {
  .m-section {
    padding-top: calc(var(--space-2xl) + var(--space-xl));
    padding-bottom: calc(var(--space-2xl) + var(--space-xl));
  }
}

.m-section--alt {
  background: var(--color-canvas);
}

.m-section--contact {
  background: var(--color-canvas);
  border-top: 1px solid var(--color-line);
}

.m-sectionhead {
  max-width: var(--m-prose-max);
  margin: 0 auto var(--space-xl);
  text-align: center;
}

/*
 * Ink Soft rather than Muted, which is what this was first, and which failed.
 *
 * tokens.css says Muted is "Bumped darker than a typical grey so it holds
 * ≥4.5:1 on white" — and that guarantee is scoped to white. Half the kickers on
 * this site sit on Canvas, where Muted measures below the AA floor, and a 12px
 * bold label is not WCAG "large text" so the floor is the full 4.5:1. PRODUCT.md
 * treats AA as a floor rather than a target, so the colour moves; the contrast
 * table in test/design-rules.test.ts now checks this pairing on both surfaces.
 */
.m-kicker {
  margin: 0 0 var(--space-xs);
  font-size: var(--text-label);
  font-weight: var(--weight-bold);
  line-height: 1.2;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

/*
 * The hero eyebrow stays a quiet slate rather than taking the brand amber.
 * The accent is the "come here" colour and there is already one of those in
 * this band — the primary call to action — so a second amber element would be
 * exactly the multiplication The One Signal Rule forbids.
 */
.m-kicker--onink {
  color: var(--m-on-ink-muted);
}

.m-h2 {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-headline);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.m-lede--section {
  margin-bottom: 0;
  font-size: var(--text-title);
  font-weight: var(--weight-regular);
  color: var(--color-ink-soft);
}

.m-h3 {
  margin: 0;
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

/* ---------- Tiles ---------- */

/*
 * "Grids: 2/3/4-up card grids collapse to a single column below lg (`980px` was
 * the prototype's break; standardised to the `lg` token). Never force a 3-up
 * grid onto a phone." So the single column runs all the way to 1024px, and the
 * column count is the one the section's own tile count divides into.
 */
.m-tiles {
  display: grid;
  gap: var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 1024px) {
  .m-tiles--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .m-tiles--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.m-tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.m-tile__icon {
  display: grid;
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  margin-bottom: var(--space-xs);
  border-radius: var(--radius-md);
  background: var(--color-canvas);
  color: var(--color-ink-soft);
}

.m-tile__body {
  margin: 0;
  color: var(--color-ink-soft);
}

/* ---------- Steps ---------- */

.m-steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  max-width: var(--m-prose-max);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.m-step {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
}

.m-step__number {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
}

.m-step__body > p {
  margin: var(--space-2xs) 0 0;
  color: var(--color-ink-soft);
}

.m-section__more {
  max-width: var(--m-prose-max);
  /* More space above a heading than below it, and the same above a closing
   * action: `xl` over the `lg` that separates the steps. */
  margin: var(--space-xl) auto 0;
  text-align: center;
}

/* ---------- Links ---------- */

/*
 * Accent Deep rather than the accent itself. The accent misses the AA floor as
 * text on white and Accent Deep clears it; both ratios are computed by the
 * contrast table in test/design-rules.test.ts rather than asserted here. The
 * accent's own hue still carries the "act" signal on the primary button, where
 * what is being read is white on amber.
 */
.m-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--tap-min);
  color: var(--color-accent-strong);
  font-weight: var(--weight-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.m-link--lead {
  font-size: var(--text-headline);
}

.m-contact__action {
  margin: var(--space-lg) 0 0;
}

/* ---------- The illustrative work item ---------- */

.m-sheet-figure {
  max-width: var(--m-sheet-max);
  margin: var(--space-2xl) auto 0;
}

.m-sheet {
  text-align: left;
}

.m-sheet__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
}

.m-sheet__ref {
  margin: 0;
  font-size: var(--text-label);
  font-weight: var(--weight-bold);
  line-height: 1.2;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

.m-sheet__business {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
  color: var(--color-ink);
}

.m-sheet__summary {
  margin: var(--space-md) 0 0;
  color: var(--color-ink-soft);
}

.m-sheet__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: var(--space-md) 0 0;
}

.m-sheet__fact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.m-sheet__label {
  margin: 0;
  color: var(--color-muted);
}

.m-sheet__value {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  text-align: right;
  color: var(--color-ink);
}

/*
 * "Do" list: money is never body-sized. The Arm's-Length Rule accepts Metric
 * *or* Title weight; a figure inside a card this size is a Title.
 */
.m-sheet__value--money {
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
}

.m-sheet__rail-label {
  margin: var(--space-lg) 0 var(--space-xs);
  font-size: var(--text-label);
  font-weight: var(--weight-bold);
  line-height: 1.2;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

/*
 * DESIGN.md's Event Timeline signature: "a muted, left-bordered list (2px Line
 * rail, 12px muted text)". The rail and the muted tone are kept; the size is
 * not. 12px is the dashboard's density, and this is a marketing page whose
 * whole register is "larger type" — so the text sits at the 15px running-copy
 * floor instead, which is the stricter of the two rules.
 */
.m-timeline {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: 0 0 0 var(--space-sm);
  border-left: 2px solid var(--color-line);
  list-style: none;
}

.m-timeline__item {
  display: flex;
  gap: var(--space-sm);
  color: var(--color-muted);
}

.m-timeline__time {
  flex: none;
}

.m-sheet__caption {
  max-width: var(--m-prose-max);
  margin: var(--space-md) auto 0;
  color: var(--m-on-ink-muted);
  text-align: center;
}

/* ---------- Footer ---------- */

.m-footer {
  background: var(--color-ink);
  color: var(--m-on-ink);
  padding-top: var(--space-xl);
  padding-bottom: var(--space-xl);
}

.m-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.m-brand--footer {
  color: var(--color-surface);
}

.m-brand--footer .m-brand__mark {
  background: var(--slate-800);
}

.m-footer__note {
  margin: 0;
  color: var(--m-on-ink-muted);
}

/* ---------- Icons ---------- */

.m-icon {
  flex: none;
}
