/*
 * Fonts — contract §6.9.
 *
 * IBM Plex Sans (400/500/600/700) and IBM Plex Mono (400/500/600),
 * self-hosted as woff2 under packages/ui/fonts/, served from our own origin.
 * Do not add a <link> to fonts.googleapis.com anywhere that imports this file.
 *
 * font-display: swap is an invented convention — the contract does not
 * specify a loading strategy. Swap was chosen as the smallest reasonable
 * choice that avoids invisible text while the woff2 downloads.
 */

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-400-a2c8606cdfd70cfe.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-500-9fb99bacc82fdf20.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-600-f9ae023bf8c1b92b.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-700-823e2efeae7dccdd.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/ibm-plex-mono-400-1afad13b8fe04a38.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/ibm-plex-mono-500-e627b3db6d8b2b1f.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/ibm-plex-mono-600-48f9df33ad49932a.woff2") format('woff2');
}

/*
 * Design tokens — contract §6.2.
 *
 * One token set for the whole product (consumer, technician, CMS, public site).
 * Dark is the default theme. `[data-theme="light"]` overrides to light.
 *
 * `--warn-2-rgb` (dark theme): the contract's §6.2 table prints "240,180,120",
 * but the hex twin `#f0b878` converts to rgb(240,184,120) — a genuine
 * arithmetic mismatch in the contract table (180 vs 184 in the green channel).
 * This file uses 184, derived from the hex, per the invariant in §6.5 that a
 * `-rgb` token "must carry the same colour as its hex twin". Escalated in the
 * executor report rather than silently picked — see docs/DESIGN-DEVIATIONS.md
 * is NOT the right place for a contract defect, so it is reported upstream.
 */

:root,
[data-theme='dark'] {
  --bg: #232a20;
  --surface: #2b3328;
  --surface-rgb: 43, 51, 40;
  --surface-2: #343d31;
  --surface-2-rgb: 52, 61, 49;
  --ink: #f4efe4;
  --ink-rgb: 244, 239, 228;
  --inv-surface: #f4efe4;
  --inv-ink: #0e2a22;
  --inv-rgb: 14, 42, 34;
  --accent: #a9dca4;
  --accent-rgb: 169, 220, 164;
  --accent-deep: #2f6b4f;
  --on-accent: #0e2a22;
  --muted-1: #cddccf;
  --muted-2: #a3bda9;
  --muted-3: #94a99b;
  --inv-muted: #2c4a3d;
  --chrome: #ded7c6;
  --bar-rgb: 35, 42, 32;
  --grad-top: #1b4331;
  --warn: #f0d79a;
  --warn-rgb: 240, 215, 154;
  --warn-2: #f0b878;
  --warn-2-rgb: 240, 184, 120;
  --danger: #e0b3a4;
  --danger-rgb: 224, 179, 164;
  --info: #9ec9e0;
  --info-rgb: 158, 201, 224;

  /* Fixed — same in both themes. The ink used on --chrome, which never inverts. */
  --fixed-ink: #0e2a22;
  --fixed-ink-rgb: 14, 42, 34;

  /* Fixed — the design's safety block, amber action and overlay scrim
     (consumer.dc.html literals #f6e0c4/#8a4b1d/#241206/#4a2e14, #f0d79a,
     rgba(6,20,16,…)); the design keeps them in both themes. */
  --safety: #f6e0c4;
  --safety-label: #8a4b1d;
  --safety-ink: #241206;
  --safety-ink-rgb: 36, 18, 6;
  --safety-ink-2: #4a2e14;
  --amber: #f0d79a;
  --scrim-rgb: 6, 20, 16;
}

[data-theme='light'] {
  --bg: #ded7c6;
  --surface: #f4efe4;
  --surface-rgb: 244, 239, 228;
  --surface-2: #fffdf6;
  --surface-2-rgb: 255, 253, 246;
  --ink: #0e2a22;
  --ink-rgb: 14, 42, 34;
  --inv-surface: #0e2a22;
  --inv-ink: #f4efe4;
  --inv-rgb: 244, 239, 228;
  --accent: #2f6b4f;
  --accent-rgb: 47, 107, 79;
  --accent-deep: #a9dca4;
  --on-accent: #f4efe4;
  --muted-1: #2c4a3d;
  --muted-2: #3d5a4b;
  --muted-3: #4d6357;
  --inv-muted: #cddccf;
  --chrome: #f4efe4;
  --bar-rgb: 244, 239, 228;
  --grad-top: #efe7d6;
  --warn: #7a4c07;
  --warn-rgb: 122, 76, 7;
  --warn-2: #8a4b1d;
  --warn-2-rgb: 138, 75, 29;
  --danger: #9c3b2c;
  --danger-rgb: 156, 59, 44;
  --info: #2f5f6b;
  --info-rgb: 47, 95, 107;

  /* Fixed — same in both themes. */
  --fixed-ink: #0e2a22;
  --fixed-ink-rgb: 14, 42, 34;

  /* Fixed — the design's safety block, amber action and overlay scrim
     (consumer.dc.html literals #f6e0c4/#8a4b1d/#241206/#4a2e14, #f0d79a,
     rgba(6,20,16,…)); the design keeps them in both themes. */
  --safety: #f6e0c4;
  --safety-label: #8a4b1d;
  --safety-ink: #241206;
  --safety-ink-rgb: 36, 18, 6;
  --safety-ink-2: #4a2e14;
  --amber: #f0d79a;
  --scrim-rgb: 6, 20, 16;
}

/*
 * Type scale — contract §6.6, plus the Större-text multiplier from §6.7.
 *
 * `--ui-text-scale` is a root-level multiplier (default 1) set by
 * `src/text-size.ts`. Every role's font-size is expressed as
 * `calc(<base> * var(--ui-text-scale, 1))` so the whole ramp scales together.
 * Line heights are unitless ratios and scale automatically with the
 * font-size they apply to — no separate handling needed.
 *
 * A component names a role via one of these classes and MUST NOT set a raw
 * `font-size` (§6.6). `packages/ui` does not know about React; components in
 * C2–C4 apply these class names directly or compose them into their own
 * component classes.
 */

:root {
  --ui-text-scale: 1;

  --ui-font-sans:
    'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ui-font-mono:
    'IBM Plex Mono', ui-monospace, 'SFMono-Regular', 'Consolas', monospace;
}

.ui-type-statement {
  font-family: var(--ui-font-sans);
  font-size: calc(20px * var(--ui-text-scale, 1));
  font-weight: 600;
  line-height: 1.25;
}

.ui-type-body {
  font-family: var(--ui-font-sans);
  font-size: calc(15px * var(--ui-text-scale, 1));
  font-weight: 400;
  line-height: 1.5;
}

.ui-type-small {
  font-family: var(--ui-font-sans);
  font-size: calc(13px * var(--ui-text-scale, 1));
  font-weight: 400;
  line-height: 1.4;
}

.ui-type-micro {
  font-family: var(--ui-font-sans);
  font-size: calc(12px * var(--ui-text-scale, 1));
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.06em;
}

/*
 * Pre-calm roles still read outside this task's scope, kept byte-identical
 * so those readers render as before until their own calm tasks migrate them:
 * - `apps/site` reads `ui-type-cta` for its hero buttons (task bd-calm-site).
 * - `draft-toolbar.tsx` (another task this wave) reads `title`, `caption`
 *   and `meta`. `ui-type-body` above is unchanged in metrics, so it needs
 *   no legacy copy.
 * Every other pre-calm role had no reader left and is gone. Nothing in
 * `apps/app` or in a calmed `packages/ui` component may use these; the calm
 * guard tests fail on any such use.
 */

.ui-type-title {
  font-family: var(--ui-font-sans);
  font-size: calc(17px * var(--ui-text-scale, 1));
  font-weight: 600;
  line-height: 1.25;
}

.ui-type-caption {
  font-family: var(--ui-font-sans);
  font-size: calc(13px * var(--ui-text-scale, 1));
  font-weight: 400;
  line-height: 1.4;
}

.ui-type-meta {
  font-family: var(--ui-font-sans);
  font-size: calc(12.5px * var(--ui-text-scale, 1));
  font-weight: 600;
  line-height: 1.3;
}

/* The site's hero buttons (consumer.dc.html *Börja felsöka*: 700 22px). */
.ui-type-cta {
  font-family: var(--ui-font-sans);
  font-size: calc(22px * var(--ui-text-scale, 1));
  font-weight: 700;
  line-height: 1.2;
}

/*
 * Invariants — contract §6.5.
 *
 * Tokens for the values, plus the small set of utility classes needed to
 * apply the compound invariants (sticky header, app background) consistently.
 * These are not components — C2–C4 compose them.
 */

:root {
  --ui-radius: 8px;

  --ui-focus-ring-width: 3px;
  --ui-focus-ring-offset: 3px;

  /* The one calm shadow (calm-ui, contract §6.5): nothing larger or tinted. */
  --ui-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.25);

  --ui-tap-target: 44px;
  --ui-tap-target-mobile-header: 46px;

  --ui-sticky-blur: 14px;
  /* rgba(var(--bar-rgb), .92) per §6.5 — bar-rgb is theme-aware from tokens.css */
  --ui-sticky-bg: rgba(var(--bar-rgb), 0.92);
}

/* Kept so a reader of `--grad-top` outside `apps/app` (the CMS phone
   preview spells this gradient out) still resolves it; nothing in the app
   reads it anymore — the app page is flat `var(--bg)` (calm-ui). */
:root {
  --ui-app-gradient: radial-gradient(
    120% 60% at 50% 0%,
    var(--grad-top) 0%,
    var(--surface) 55%,
    var(--bg) 100%
  );
}

/*
 * Focus ring — "Never remove it" (§6.5). Applied as a global default on
 * every focusable element via :focus-visible so no component has to opt in,
 * and no component may opt out without contract review.
 */
:focus-visible {
  outline: var(--ui-focus-ring-width) solid var(--accent);
  outline-offset: var(--ui-focus-ring-offset);
}

/*
 * Minimum tap target. Apply to any interactive element; does not shrink at
 * any Större-text setting because it is independent of --ui-text-scale.
 */
.ui-tap-target {
  min-width: var(--ui-tap-target);
  min-height: var(--ui-tap-target);
}

.ui-tap-target--mobile-header {
  min-width: var(--ui-tap-target-mobile-header);
  min-height: var(--ui-tap-target-mobile-header);
}

/* Sticky header treatment. */
.ui-sticky-header {
  position: sticky;
  top: 0;
  background: var(--ui-sticky-bg);
  backdrop-filter: blur(var(--ui-sticky-blur));
  -webkit-backdrop-filter: blur(var(--ui-sticky-blur));
}

/* App page background: flat `var(--bg)` (calm-ui, contract §6.5). */
.ui-app-background {
  background: var(--bg);
}

/*
 * Motion — contract §6.8.
 *
 * Keyframes ported verbatim from the <style> blocks in the design files
 * (extracted with a balanced-brace parse of the minified CSS, not
 * approximated). Only the animations §6.8 names are ported; C2-C4 apply
 * `animation-name` / `animation-duration` etc. to the elements that use
 * them, since no components exist in this task.
 *
 * Source-file note on `sheetUp`: §6.8 says "the CMS uses its own set
 * including cmsRise and sheetUp", implying both live in design/cms.dc.html.
 * `cmsRise` does. `sheetUp` does not — it is absent from cms.dc.html and
 * only defined in design/technician.dc.html (which this task was not asked
 * to read). This is reported as a contract defect in the executor report;
 * the keyframe below is ported from technician.dc.html because it does
 * exist verbatim there, which is more faithful to "port, do not invent"
 * than filing a false "missing keyframe" deviation would have been.
 *
 * `cms.dc.html` also declares `cmsFade` and `cmsSpin`, which §6.8 does not
 * name. They are not ported here — see the executor report.
 *
 * Calm (owner, 2026-10-02, card *CalmUI integrated*): the decorative loops
 * (`felsokPulse`, `felsokBreathe`, `webBreathe`) are removed with their uses;
 * `webBar` stays as the `AudioPlayer`'s playback feedback (§7.2) and the
 * spinner rotates. Every remaining animation MUST respect
 * `prefers-reduced-motion: reduce` (below).
 */

/* -- design/consumer.dc.html : "web" set (public site) -- */
@keyframes webRise {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes webFade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/*
 * `webBar` stays: it is not decoration but the `AudioPlayer`'s playback
 * feedback (§7.2) — the bars bounce only while the recording plays, and
 * under `prefers-reduced-motion` they stay still and played bars take the
 * accent colour. The decorative loops (`felsokPulse`, `felsokBreathe`,
 * `webBreathe`) are gone (calm-ui, contract §6.8); the spinner reports
 * work by rotating instead.
 */
@keyframes webBar {
  0%,
  100% {
    transform: scaleY(0.4);
  }
  50% {
    transform: scaleY(1);
  }
}

/* -- design/consumer.dc.html : nav transitions (shared with technician) -- */
@keyframes navNextA {
  from {
    opacity: 0;
    transform: translateX(48px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes navNextB {
  from {
    opacity: 0;
    transform: translateX(48px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes navPrevA {
  from {
    opacity: 0;
    transform: translateX(-48px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes navPrevB {
  from {
    opacity: 0;
    transform: translateX(-48px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* -- design/consumer.dc.html : "felsok" set (consumer app) -- */
@keyframes felsokRise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes felsokSlideLeft {
  from {
    opacity: 0;
    transform: translateX(-18px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* -- design/cms.dc.html -- */
@keyframes cmsRise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* -- design/technician.dc.html (see file-header note on sheetUp's source) -- */
@keyframes sheetUp {
  from {
    transform: translateY(102%);
  }
  to {
    transform: none;
  }
}

/*
 * Every animation collapses under prefers-reduced-motion: reduce.
 * A universal duration/iteration clamp is used rather than per-animation
 * reduced variants, because no component exists yet to know which element
 * plays which animation — see the executor report for this as an invented
 * convention. This still satisfies §6.8: motion collapses to (effectively)
 * nothing, and any transform/opacity end-state resolves immediately.
 * The delay is cleared too: a kept stagger (`.ui-in`) turns the screen
 * entrance into a top-to-bottom pop-in, which is still motion.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/*
 * Component styles — contract §6.5, §6.6, §7.1, §7.2.
 *
 * Rules this file obeys (guarded by test/components-css.test.ts):
 * - No hex colour. Every colour is a `var(--*)` token or a
 *   `rgba(var(--*-rgb), …)` composite, exactly as §6.5 requires for the
 *   `-rgb` twins. The only exception is the literal black in the overlay
 *   scrim and the dialog shadow (`rgba(0, 0, 0, …)`): §6.2 defines no scrim
 *   or shadow token and the design composites that literal value, so it is
 *   kept and disclosed in the executor report rather than approximated
 *   with a theme token that would read wrong in one theme.
 * - No `font-size`. Type comes from the `ui-type-*` role classes in
 *   `type-scale.css` (§6.6: statement/body/small/micro), applied in markup.
 *   Weight never exceeds 600 (calm-ui): buttons and choice labels carry 600
 *   against roles that carry 400.
 * - No hardcoded radius. Every corner is `var(--ui-radius)` (§6.5: 8px),
 *   `50%` for dots/rings, or `0` for the square bottom edge of a sheet.
 * - Focus ring: every interactive element keeps the global `:focus-visible`
 *   rule from `invariants.css` (`3px solid var(--accent)`, offset `3px`).
 *   This file never sets `outline: none` — the guard test fails on it.
 * - Tap targets: every control is at least `44px` (§6.5).
 */

.ui-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  border-radius: var(--ui-radius);
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
}

.ui-button--md {
  min-height: 52px;
  padding: 0 22px;
}

.ui-button--lg {
  min-height: 60px;
  padding: 0 26px;
}

/* The www home hero is the one calm-ui exception (contract §17): its accent
   shadow, 700 weight and hover lift stay as built for apps/site. The app uses lg. */
.ui-button--hero {
  min-width: 239px;
  min-height: 74px;
  padding: 0 34px;
  font-weight: 700;
  letter-spacing: -0.01em;
  box-shadow: 0 14px 34px -10px rgba(var(--accent-rgb), 0.55);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}

.ui-button--hero:hover {
  filter: none;
  transform: translateY(-2px);
  box-shadow: 0 20px 44px -10px rgba(var(--accent-rgb), 0.7);
}

.ui-button--hero:active {
  transform: none;
}

.ui-button--full-width {
  width: 100%;
}

.ui-button--primary {
  border: 0;
  background: var(--accent);
  color: var(--on-accent);
}

.ui-button--primary:hover {
  filter: brightness(1.05);
}

.ui-button--primary:active {
  filter: brightness(0.94);
}

.ui-button--secondary {
  font-weight: 600;
  border: 1px solid rgba(var(--ink-rgb), 0.2);
  background: transparent;
  color: var(--ink);
}

.ui-button--secondary:hover {
  background: rgba(var(--ink-rgb), 0.12);
}

.ui-button--ghost {
  font-weight: 600;
  border: 0;
  background: rgba(var(--ink-rgb), 0.09);
  color: var(--muted-1);
}

.ui-button--ghost:hover {
  background: rgba(var(--ink-rgb), 0.17);
}

.ui-button--danger {
  font-weight: 600;
  border: 1px solid rgba(var(--danger-rgb), 0.55);
  background: transparent;
  color: var(--danger);
}

.ui-button--danger:hover {
  background: rgba(var(--danger-rgb), 0.12);
}

.ui-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
}

.ui-button__icon {
  display: inline-flex;
  align-items: center;
  flex: none;
}

.ui-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  flex: none;
  border: 1px solid rgba(var(--ink-rgb), 0.16);
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.06);
  color: var(--ink);
  cursor: pointer;
}

.ui-icon-button--sm {
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
}

.ui-icon-button--md {
  width: 46px;
  height: 46px;
  min-width: 46px;
  min-height: 46px;
}

.ui-icon-button:hover {
  background: rgba(var(--ink-rgb), 0.13);
}

.ui-icon-button--danger {
  border-color: rgba(var(--danger-rgb), 0.55);
  color: var(--danger);
}

.ui-icon-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-pen-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  flex: none;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--ui-radius);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

.ui-pen-button:hover {
  background: rgba(var(--ink-rgb), 0.08);
}

.ui-pen-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-surface--1 {
  background: var(--surface);
}

.ui-surface--2 {
  background: var(--surface-2);
}

.ui-card {
  display: block;
  box-sizing: border-box;
  border: 1px solid rgba(var(--ink-rgb), 0.14);
  border-radius: var(--ui-radius);
  padding: 16px;
}

.ui-card--interactive {
  cursor: pointer;
  text-align: left;
  width: 100%;
  color: inherit;
}

.ui-card--interactive:hover {
  border-color: rgba(var(--ink-rgb), 0.24);
}

.ui-card--selected {
  border-color: rgba(var(--accent-rgb), 0.55);
  background: rgba(var(--accent-rgb), 0.1);
}

.ui-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.ui-field__label {
  color: var(--ink);
}

.ui-field__required {
  color: var(--danger);
}

.ui-field__hint {
  color: var(--muted-2);
}

.ui-field__error {
  color: var(--danger);
}

.ui-input,
.ui-textarea,
.ui-select {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border: 1px solid rgba(var(--ink-rgb), 0.18);
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  color: var(--ink);
}

.ui-input,
.ui-select {
  min-height: 52px;
  padding: 0 14px;
}

.ui-textarea {
  min-height: 52px;
  padding: 12px 14px;
  resize: vertical;
}

.ui-input::placeholder,
.ui-textarea::placeholder {
  color: var(--muted-3);
  opacity: 1;
}

.ui-input--invalid,
.ui-textarea--invalid {
  border-color: var(--danger);
}

.ui-input:disabled,
.ui-textarea:disabled,
.ui-select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* In-place editing in the CMS phone and answers strip: theme-aware like
   .ui-textarea, so the text stays readable on the dark consumer screen. */
.ui-editable__field {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border: 1px solid rgba(var(--ink-rgb), 0.18);
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  color: var(--ink);
  min-height: 52px;
  padding: 12px 14px;
  resize: vertical;
}

.ui-editable__field::placeholder {
  color: var(--muted-3);
  opacity: 1;
}

.ui-checkbox {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  color: var(--ink);
  cursor: pointer;
}

.ui-checkbox__input {
  flex: none;
  width: 22px;
  height: 22px;
  accent-color: var(--accent);
  cursor: pointer;
}

.ui-checkbox--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 24px;
  padding: 3px 10px;
  border: 0;
  border-radius: var(--ui-radius);
  font-weight: 500;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* The design's state pills (cms.dc.html queue): a tint and its ink, no border. */
.ui-badge--neutral {
  background: rgba(var(--ink-rgb), 0.07);
  color: var(--muted-2);
}

.ui-badge--accent {
  background: rgba(var(--accent-rgb), 0.13);
  color: var(--accent);
}

.ui-badge--warn {
  background: rgba(var(--warn-rgb), 0.14);
  color: var(--warn);
}

.ui-badge--danger {
  background: rgba(var(--danger-rgb), 0.1);
  color: var(--danger);
}

.ui-badge--info {
  background: rgba(var(--info-rgb), 0.12);
  color: var(--info);
}

.ui-spinner {
  display: inline-block;
  flex: none;
  box-sizing: border-box;
  border: 3px solid rgba(var(--ink-rgb), 0.2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: ui-spin-rotate 0.9s linear infinite;
}

@keyframes ui-spin-rotate {
  to {
    transform: rotate(360deg);
  }
}

.ui-spinner--sm {
  width: 20px;
  height: 20px;
}

.ui-spinner--md {
  width: 28px;
  height: 28px;
}

.ui-skeleton {
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.08);
}

.ui-skeleton--text-line {
  height: 14px;
}

.ui-skeleton--text-line + .ui-skeleton--text-line {
  margin-top: 8px;
}

.ui-skeleton--text-line:last-child {
  width: 60%;
}

.ui-skeleton--block {
  min-height: 88px;
}

.ui-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 32px 24px;
  text-align: center;
}

.ui-empty__title {
  color: var(--ink);
  margin: 0;
}

.ui-empty__body {
  color: var(--muted-2);
  margin: 0;
}

.ui-empty__action {
  margin-top: 8px;
}

.ui-toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  box-sizing: border-box;
  border: 1px solid rgba(var(--ink-rgb), 0.14);
  border-left: 4px solid var(--muted-3);
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  padding: 14px 8px 14px 16px;
}

.ui-toast--info {
  border-left-color: var(--info);
}

.ui-toast--success {
  border-left-color: var(--accent);
}

.ui-toast--warning {
  border-left-color: var(--warn);
}

.ui-toast--error {
  border-left-color: var(--danger);
}

.ui-toast__content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ui-toast__title {
  color: var(--ink);
}

.ui-toast__body {
  color: var(--muted-2);
}

.ui-toast__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  margin-top: -10px;
  margin-right: -2px;
  border: 0;
  border-radius: var(--ui-radius);
  background: transparent;
  color: var(--muted-1);
  cursor: pointer;
}

.ui-toast__close:hover {
  background: rgba(var(--ink-rgb), 0.1);
}

.ui-sheet__overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}

.ui-sheet__panel {
  box-sizing: border-box;
  width: 100%;
  max-height: 86vh;
  overflow: auto;
  border: 1px solid rgba(var(--accent-rgb), 0.25);
  border-bottom: 0;
  border-radius: var(--ui-radius) var(--ui-radius) 0 0;
  background: var(--surface-2);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  animation: sheetUp 0.2s ease both;
}

.ui-sheet__header {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.ui-sheet__title {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  margin: 0;
}

.ui-lightbox__overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
  background: rgba(0, 0, 0, 0.8);
}

.ui-lightbox__content {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  display: flex;
}

.ui-lightbox__image {
  max-width: min(92vw, 100%);
  max-height: 84vh;
  border-radius: var(--ui-radius);
  touch-action: none;
}

.ui-lightbox__close {
  position: absolute;
  top: -8px;
  right: -8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  border: 1px solid rgba(var(--ink-rgb), 0.2);
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}

.ui-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.14);
}

.ui-tabs__tab {
  min-height: 44px;
  padding: 0 16px;
  margin-bottom: -1px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  color: var(--muted-2);
  cursor: pointer;
  white-space: nowrap;
}

.ui-tabs__tab:hover {
  color: var(--ink);
}

.ui-tabs__tab--selected {
  border-bottom-color: var(--accent);
  color: var(--ink);
}

.ui-tabs__tab:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

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

.ui-table__header-cell {
  padding: 10px 12px;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.16);
  color: var(--muted-2);
  text-align: left;
}

.ui-table__header-cell--center {
  text-align: center;
}

.ui-table__header-cell--right {
  text-align: right;
}

.ui-table__sort-button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

.ui-table__sort-arrow {
  color: var(--accent);
}

.ui-table__cell {
  padding: 12px;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.1);
  color: var(--ink);
  text-align: left;
  vertical-align: top;
}

.ui-table__cell--center {
  text-align: center;
}

.ui-table__cell--right {
  text-align: right;
}

.ui-table__row--clickable {
  cursor: pointer;
}

.ui-table__row--clickable:hover {
  background: rgba(var(--ink-rgb), 0.06);
}

.ui-table__empty {
  color: var(--muted-2);
  padding: 24px 12px;
  text-align: center;
}

.ui-theme-toggle {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid rgba(var(--ink-rgb), 0.16);
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.05);
}

.ui-theme-toggle__option {
  min-height: 44px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--ui-radius);
  background: transparent;
  color: var(--muted-1);
  cursor: pointer;
  white-space: nowrap;
}

.ui-theme-toggle__option--selected {
  background: var(--accent);
  color: var(--on-accent);
}

.ui-step-header {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.ui-step-header__title {
  color: var(--ink);
  margin: 0;
  text-wrap: pretty;
}

.ui-step-header__help {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 8px 14px 8px 12px;
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  border-radius: var(--ui-radius);
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--accent);
  cursor: pointer;
}

.ui-step-header__help:hover {
  background: rgba(var(--accent-rgb), 0.2);
}

.ui-choice-button {
  display: flex;
  align-items: center;
  gap: 14px;
  box-sizing: border-box;
  width: 100%;
  min-height: 68px;
  padding: 13px 18px;
  border: 1px solid rgba(var(--ink-rgb), 0.16);
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.07);
  color: var(--ink);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.ui-choice-button:hover {
  background: rgba(var(--ink-rgb), 0.13);
}

.ui-choice-button--selected {
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.12);
}

.ui-choice-button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.ui-choice-button__label {
  flex: 1;
  min-width: 0;
}

.ui-choice-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

/*
 * Hazard card (cards 11/13/14, §9's hazard row, §6.3). A calm card on the
 * page surface: the severity colour goes on the border and the icon and
 * nowhere else, so a warning and a danger differ at a glance without the
 * text sitting on a full-colour block.
 */
.ui-hazard {
  display: flex;
  flex-direction: column;
  gap: 14px;
  box-sizing: border-box;
  border: 1px solid;
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  padding: 20px;
}

/* Plain CSS, so a real media query — a Tailwind variant in here reaches the
   browser verbatim and is dropped. */
@media (max-width: 720px) {
  .ui-hazard {
    padding: 16px;
  }
}

.ui-hazard--warning {
  border-color: var(--warn);
}

.ui-hazard--danger {
  border-color: var(--danger);
}

.ui-hazard__icon {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.06);
}

.ui-hazard__glyph {
  width: 0;
  height: 0;
  border-right: 10px solid transparent;
  border-bottom: 20px solid;
  border-left: 10px solid transparent;
}

.ui-hazard__icon[data-tone='warning'] .ui-hazard__glyph {
  border-bottom-color: var(--warn);
}

.ui-hazard__icon[data-tone='danger'] .ui-hazard__glyph {
  border-bottom-color: var(--danger);
}

.ui-hazard__message {
  color: var(--ink);
  margin: 0;
  text-wrap: pretty;
}

/*
 * One action row (cards 11/13/14, §8.1). The 2026-09-26 deviation: Back and
 * the forward actions must not push each other off the row — a long label
 * wraps inside its own button instead, so `white-space: normal` overrides the
 * `nowrap` every Button carries and `min-width: 0` lets it shrink. The row
 * itself may still wrap between buttons; what it may not do is squeeze a
 * button until its words break one letter per line.
 */
.ui-hazard__actions {
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 10px;
}

/* Back is one short word: it holds its width, and the forward actions take
   what is left, wrapping their labels rather than the row. */
.ui-hazard__actions > :first-child {
  flex: none;
}

.ui-hazard__actions > * {
  min-width: 0;
  white-space: normal;
}

/* A long acknowledge label wraps inside a phone-width banner instead of overflowing it. */
.ui-hazard__actions .ui-button {
  max-width: 100%;
  white-space: normal;
}

.ui-progress {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding-bottom: 4px;
}

.ui-progress__bar {
  flex: 1;
  min-width: 0;
}

/* 600 13px muted-2 in the design: the caption role at the design's weight. */
.ui-progress__label {
  flex: none;
  color: var(--muted-2);
  font-weight: 600;
  white-space: nowrap;
}

.ui-progress__track {
  height: 6px;
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.12);
  overflow: hidden;
}

.ui-progress__fill {
  height: 100%;
  border-radius: var(--ui-radius);
  background: var(--accent);
  transition: width 0.35s ease;
}

.ui-progress__fill--indeterminate {
  width: 40%;
}

.ui-audio {
  display: flex;
  align-items: center;
  gap: 12px;
  box-sizing: border-box;
  border: 1px solid rgba(var(--ink-rgb), 0.12);
  border-radius: var(--ui-radius);
  background: rgba(var(--ink-rgb), 0.05);
  padding: 12px 16px;
}

.ui-audio__waveform {
  display: flex;
  align-items: center;
  gap: 3px;
  flex: none;
  height: 32px;
}

/*
 * The equaliser bounce (card 24, §6.8). Applied only while the recording
 * plays, and never under `prefers-reduced-motion` — the component then
 * carries progress as colour instead. `animation-duration` and
 * `animation-delay` are deliberately absent here: each bar's own pace is
 * set inline from the design's `webBars` map, so the shorthand below
 * contributes only the name, timing function and iteration count.
 */
.ui-audio__bar {
  animation-name: webBar;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

.ui-audio__element {
  flex: 1;
  min-width: 0;
}

.ui-pending {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  padding: 6px 12px;
  border: 1px solid transparent;
  border-radius: var(--ui-radius);
}

.ui-pending--pending,
.ui-pending--retrying {
  border-color: rgba(var(--warn-rgb), 0.4);
  background: rgba(var(--warn-rgb), 0.12);
  color: var(--warn);
}

.ui-pending--failed {
  border-color: rgba(var(--danger-rgb), 0.4);
  background: rgba(var(--danger-rgb), 0.12);
  color: var(--danger);
}

button.ui-pending {
  min-height: 44px;
  cursor: pointer;
}

button.ui-pending:hover {
  filter: brightness(1.1);
}

.ui-tried,
.ui-tenant,
.ui-known {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  box-sizing: border-box;
  border: 1px solid transparent;
  border-radius: var(--ui-radius);
  padding: 14px 16px;
}

.ui-tried,
.ui-known {
  border-color: rgba(var(--info-rgb), 0.28);
  background: rgba(var(--info-rgb), 0.1);
}

.ui-tenant {
  border-color: rgba(var(--warn-rgb), 0.4);
  background: rgba(var(--warn-rgb), 0.12);
}

.ui-tried__dot,
.ui-tenant__dot,
.ui-known__dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
}

.ui-tried__dot,
.ui-known__dot {
  background: var(--info);
}

.ui-known__action {
  flex: none;
  align-self: center;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.ui-tenant__dot {
  background: var(--warn);
}

.ui-tried__label,
.ui-tenant__label,
.ui-known__label {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  margin: 0;
}

@media (min-width: 768px) {
  .ui-sheet__overlay {
    align-items: center;
    padding: 24px;
    box-sizing: border-box;
  }

  .ui-sheet__panel {
    width: min(560px, 100%);
    border-bottom: 1px solid rgba(var(--accent-rgb), 0.25);
    border-radius: var(--ui-radius);
    animation: webRise 0.18s ease both;
    box-shadow: var(--ui-shadow-sm);
  }
}

/*
 * §7.3 result/summary and §7.4 identification/account (C3).
 *
 * `.ui-mono` is the one addition outside those two rows: a font-family-only
 * modifier (no font-size) so `PlateCandidateList` and `SymptomSearch`'s
 * error-code mode can render in IBM Plex Mono per §6.9 without setting a
 * raw size — the `ui-type-*` class next to it still owns that.
 */
.ui-mono {
  font-family: var(--ui-font-mono);
}

.ui-certainty {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  box-sizing: border-box;
  border: 1px solid transparent;
  border-radius: var(--ui-radius);
  padding: 14px 16px;
  border-color: rgba(var(--info-rgb), 0.28);
  background: rgba(var(--info-rgb), 0.1);
}

.ui-certainty__dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--info);
}

.ui-certainty__text {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  margin: 0;
}

.ui-cause-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ui-cause-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.ui-cause-card__ordinal {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
}

.ui-cause-card__title {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  margin: 0;
}

.ui-cause-card__body {
  color: var(--ink);
  margin: 0;
}

.ui-cause-card__reasoning {
  color: var(--muted-2);
  margin: 0;
}

.ui-cause-card__steps {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-cause-card__step {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.ui-cause-card__step-num {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(var(--ink-rgb), 0.14);
  color: var(--ink);
}

.ui-cause-card__note {
  color: var(--muted-2);
  margin: 0;
}

.ui-feedback {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.ui-feedback__options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ui-ownership {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.ui-ownership__prefill {
  color: var(--muted-2);
  margin: 0;
}

.ui-model-picker {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.ui-plate-candidates {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.ui-plate-candidates__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-plate-candidate {
  display: flex;
  align-items: center;
  gap: 14px;
}

.ui-plate-candidate__info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ui-plate-candidate__brand {
  color: var(--ink);
}

.ui-plate-candidate__model {
  color: var(--muted-2);
}

.ui-unsupported,
.ui-unreadable {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  box-sizing: border-box;
  border-radius: var(--ui-radius);
  padding: 18px 20px;
  background: rgba(var(--warn-rgb), 0.1);
}

.ui-unsupported__title,
.ui-unreadable__title {
  color: var(--ink);
  margin: 0;
}

.ui-unsupported__message,
.ui-unreadable__message {
  color: var(--ink);
  margin: 0;
}

.ui-unsupported__model {
  color: var(--muted-2);
  margin: 0;
}

.ui-unreadable__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.ui-symptom-search {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.ui-symptom-search__results {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ui-interpretation {
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-sizing: border-box;
  border-radius: var(--ui-radius);
  padding: 18px 20px;
  background: rgba(var(--info-rgb), 0.1);
}

.ui-interpretation__text {
  color: var(--ink);
  margin: 0;
}

.ui-interpretation__candidates {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-interpretation__candidate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.ui-tried-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.ui-otp {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.ui-otp__digits {
  display: flex;
  gap: 10px;
}

.ui-otp__digit {
  box-sizing: border-box;
  width: 44px;
  height: 58px;
  border: 1px solid rgba(var(--ink-rgb), 0.18);
  border-radius: var(--ui-radius);
  background: var(--surface-2);
  color: var(--ink);
  text-align: center;
}

.ui-otp__digit--invalid {
  border-color: var(--danger);
}

.ui-otp__digit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-otp__resend {
  align-self: flex-start;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}

.ui-otp__resend:disabled {
  color: var(--muted-3);
  cursor: not-allowed;
}

/*
 * CMS components — contract §7.5 (C4). Same token rules as above: colours
 * are `var(--*)` or `rgba(var(--*-rgb), …)`, type comes from `ui-type-*`
 * classes in markup, corners are `var(--ui-radius)` / `50%` / `0`, every
 * control clears `44px`, and focus stays on the global `:focus-visible`
 * ring from `invariants.css`.
 */

.ui-step-card,
.ui-result-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ui-step-card__head,
.ui-result-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.ui-step-card__position {
  color: var(--muted-2);
}

.ui-step-card__spacer,
.ui-result-card__spacer {
  flex: 1;
  min-width: 8px;
}

.ui-step-card__body,
.ui-result-card__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

.ui-step-card__id,
.ui-result-card__id {
  color: var(--muted-2);
}

.ui-step-card__issues,
.ui-result-card__issues {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-step-card__issue,
.ui-result-card__issue {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.ui-overview-map {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

.ui-overview-map__hint {
  color: var(--muted-2);
  margin: 0;
}

.ui-overview-map__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-overview-map__node {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  min-height: 56px;
  padding: 10px 14px;
  border: 1px solid rgba(var(--ink-rgb), 0.12);
  border-radius: var(--ui-radius);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}

.ui-overview-map__node--focused {
  border-color: var(--accent);
  background: rgba(var(--accent-rgb), 0.1);
}

.ui-overview-map__node:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-overview-map__dot {
  flex: none;
  width: 10px;
  height: 10px;
  background: var(--accent);
}

.ui-overview-map__dot--step {
  border-radius: 50%;
}

.ui-overview-map__dot--result {
  border-radius: 0;
  background: var(--warn-2);
}

.ui-overview-map__label {
  flex: 1;
  min-width: 0;
}

.ui-overview-map__meta {
  color: var(--muted-2);
}

.ui-overview-map__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  color: var(--muted-2);
}

.ui-overview-map__legend-item {
  display: flex;
  align-items: center;
  gap: 7px;
}

.ui-target-picker {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ui-media-upload {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ui-media-upload__current {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.ui-media-upload__key {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.ui-media-upload__label {
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-sizing: border-box;
  min-height: 44px;
  padding: 14px 16px;
  border: 1px dashed rgba(var(--ink-rgb), 0.3);
  border-radius: var(--ui-radius);
  background: transparent;
  cursor: pointer;
}

.ui-media-upload__label--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-media-upload__prompt {
  color: var(--accent);
}

.ui-media-upload__input {
  min-height: 44px;
  color: var(--ink);
}

.ui-media-upload__hint {
  color: var(--muted-2);
}

.ui-media-upload__error {
  color: var(--danger);
}

.ui-retarget {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ui-retarget__intro {
  margin: 0;
}

.ui-retarget__list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-retarget__row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ui-retarget__label {
  color: var(--ink);
}

.ui-retarget__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.ui-issue-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.ui-issue-list__group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ui-issue-list__heading {
  color: var(--muted-2);
  margin: 0;
}

.ui-issue-list__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-issue-list__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid rgba(var(--ink-rgb), 0.12);
  border-radius: var(--ui-radius);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}

.ui-issue-list__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ui-issue-list__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ui-issue-list__meta {
  color: var(--muted-2);
}

.ui-locale-grid {
  min-width: 0;
  overflow-x: auto;
}

.ui-locale-grid__gap {
  border-style: dashed;
  border-color: var(--warn);
}

.ui-draft-toolbar {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

.ui-draft-toolbar__status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.ui-draft-toolbar__version {
  color: var(--muted-2);
}

.ui-draft-toolbar__issues {
  color: var(--muted-2);
}

.ui-draft-toolbar__conflict {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border: 1px solid rgba(var(--warn-rgb), 0.35);
  border-radius: var(--ui-radius);
  background: rgba(var(--warn-rgb), 0.1);
}

.ui-draft-toolbar__conflict-title {
  color: var(--warn);
  margin: 0;
}

.ui-draft-toolbar__conflict-body {
  color: var(--warn);
  margin: 0;
}

.ui-draft-toolbar__conflict-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.ui-draft-toolbar__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/*
 * SideNav — the CMS section sidebar (§8.3, E6). Vertical section list
 * with the queue count badge on the first item, as in the left column of
 * `design/cms.dc.html`. Colours are `var(--*)` / `rgba(var(--*-rgb), …)`,
 * type comes from `ui-type-*` classes in markup (weight set here is not
 * size), corners are `var(--ui-radius)` / `0`, every item clears `44px`,
 * and focus stays on the global `:focus-visible` ring from
 * `invariants.css`. The 8px dot keeps square corners (`0`): the design's
 * near-square 2px radius has no token and the radius guard forbids a raw
 * `2px`.
 */

.ui-side-nav {
  flex: 1 1 210px;
  max-width: 250px;
  min-width: 200px;
  align-self: stretch;
  box-sizing: border-box;
  padding: 18px 14px;
  background: var(--surface-2);
  border-right: 1px solid rgba(var(--ink-rgb), 0.12);
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.ui-side-nav__list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-side-nav__row {
  margin: 0;
  padding: 0;
}

.ui-side-nav__item {
  display: flex;
  align-items: center;
  gap: 11px;
  box-sizing: border-box;
  width: 100%;
  min-height: 48px;
  padding: 0 13px;
  border: 0;
  border-radius: var(--ui-radius);
  background: transparent;
  color: var(--muted-2);
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.ui-side-nav__item:hover {
  background: rgba(var(--ink-rgb), 0.07);
}

.ui-side-nav__item--active {
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--ink);
  font-weight: 600;
}

.ui-side-nav__dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 0;
  background: rgba(var(--ink-rgb), 0.18);
}

.ui-side-nav__item--active .ui-side-nav__dot {
  background: var(--accent);
}

.ui-side-nav__label {
  flex: 1;
  min-width: 0;
}

.ui-side-nav__badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: var(--ui-radius);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 500;
  text-align: center;
}

/*
 * Quiet scrollbars (owner, 2026-10-03, card *CalmUI integrated*): one
 * global rule for the app, the CMS and the site — every page and list
 * scrolls with a thin bar in the theme's own colours. Track transparent,
 * thumb ink at rest and darker on hover; `scrollbar-width`/`scrollbar-color`
 * for Firefox and the `::-webkit-scrollbar` equivalents for Chromium and
 * Safari. Ink comes from the theme token, so light and dark both work.
 */

* {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--ink-rgb), 0.25) transparent;
}

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

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

*::-webkit-scrollbar-thumb {
  background: rgba(var(--ink-rgb), 0.25);
  border-radius: var(--ui-radius);
}

*::-webkit-scrollbar-thumb:hover {
  background: rgba(var(--ink-rgb), 0.4);
}

/*
 * Document base — the `body`/control rules every design file declares in
 * its own <style> block (`body{margin:0;background:var(--bg);font-family:
 * "IBM Plex Sans";color:var(--ink)}`, `a{color:var(--accent)}`, controls
 * inherit the family). Without it the browser's 8px body margin shows as a
 * white frame and unstyled text falls back to the UA serif. Layered as
 * `base` so every Tailwind utility (layer `utilities`) still wins.
 */

@layer base {
  /* Borders are opt-in, as in the design's inline styles: a side named by a
     utility (`border-b`) must not bring the UA's medium width on the others. */
  *,
  ::before,
  ::after {
    border-width: 0;
    border-style: solid;
  }

  html,
  body {
    margin: 0;
    padding: 0;
    /* Pages scroll only vertically (cards 38/39): anything wider than the
       screen is clipped at the page instead of letting it be dragged
       sideways. `clip`, not `hidden`, so nothing becomes a scroll container
       and sticky positioning keeps working. */
    overflow-x: clip;
  }

  html {
    background: var(--bg);
  }

  body {
    min-height: 100vh;
    background: var(--bg);
    /* The chalk grain (§6.1): one faint tiled texture over the dark page
       background, shared by the app, the CMS and the site through this one
       rule. Light pages unset it below; the site's light theme unsets it in
       its own light block (it follows the device without data-theme). */
    background-image: url("/assets/chalk-grain-b8608bc5fdf22b52.svg");
    color: var(--ink);
    font-family: var(--ui-font-sans);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    -webkit-text-size-adjust: 100%;
  }

  a {
    color: var(--accent);
  }

  a:hover {
    color: var(--ink);
  }

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

  h1,
  h2,
  h3,
  h4,
  p,
  ul,
  ol,
  dl,
  dd,
  figure {
    margin: 0;
  }

  img {
    max-width: 100%;
  }

  /* Nothing tiled on light pages: the grain is dark-mode chalk only. */
  [data-theme='light'] body {
    background-image: none;
  }
}

/*
 * Screen entrance — calm-ui (contract §6.8): a short fade (≤200 ms) when a
 * screen opens, no slide, no stagger. `enter()` still passes each block's
 * index as `--i`; it no longer delays anything, so every call site keeps
 * working unchanged.
 */
.ui-in {
  animation: webFade 0.16s ease both;
}

[data-nav-dir='back'] .ui-in {
  animation-name: webFade;
}

.ui-fade {
  animation: webFade 0.16s ease both;
}

.ui-rise {
  animation: webRise 0.2s ease both;
}

/* The menu overlay entrance (consumer.dc.html `menuOpen`): shortened to the
   calm 200 ms ceiling; the slide itself is a functional open transition. */
.ui-slide-left {
  animation: felsokSlideLeft 0.2s ease both;
}

.ui-spin {
  animation: cmsSpin 1s linear infinite;
}

@keyframes cmsSpin {
  to {
    transform: rotate(360deg);
  }
}

  /* The site follows the device in CSS alone (§6.1): no data-theme attribute,
     no script, no button. Dark tokens come from tokens.css `:root`; the light
     values below are that file's `[data-theme='light']` row, scoped to the
     site so nothing else is touched. `color-scheme` lives on `.site` and on
     `:root:has(.site)` — never a bare `:root` — so the root follows the page
     into dark mode on www (classic scrollbars and other root-level UA chrome
     read the root), while importing this stylesheet into the app (the shared
     front page, which never renders `.site`) changes nothing there. */
  :root:has(.site) {
    color-scheme: light dark;
  }
  .site {
    color-scheme: light dark;
  }
  @media (prefers-color-scheme: light) {
    /* Nothing tiled on light pages: the grain is dark-mode chalk only.
       Scoped to pages that render the site, so the app's other screens keep
       their own background once the front page unmounts. */
    body:has(.site) {
      background-image: none;
    }
    .site {
      color-scheme: light;
      --bg: #ded7c6;
      --surface: #f4efe4;
      --surface-rgb: 244, 239, 228;
      --surface-2: #fffdf6;
      --surface-2-rgb: 255, 253, 246;
      --ink: #0e2a22;
      --ink-rgb: 14, 42, 34;
      --inv-surface: #0e2a22;
      --inv-ink: #f4efe4;
      --inv-rgb: 244, 239, 228;
      --accent: #2f6b4f;
      --accent-rgb: 47, 107, 79;
      --accent-deep: #a9dca4;
      --on-accent: #f4efe4;
      --muted-1: #2c4a3d;
      --muted-2: #3d5a4b;
      --muted-3: #4d6357;
      --inv-muted: #cddccf;
      --chrome: #f4efe4;
      --bar-rgb: 244, 239, 228;
      --warn: #7a4c07;
      --warn-rgb: 122, 76, 7;
      --warn-2: #8a4b1d;
      --warn-2-rgb: 138, 75, 29;
      --danger: #9c3b2c;
      --danger-rgb: 156, 59, 44;
      --info: #2f5f6b;
      --info-rgb: 47, 95, 107;
    }
  }
  .site {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background: var(--bg);
    color: var(--ink);
  }

  /* The app's home-page top bar (app-header on `start`): no bar of its own
     — no fill, border or blur — the wordmark centred, the flag button on
     the right, and nothing on the left (no menu button, no placeholder). */
  .site-topbar {
    flex: none;
    padding: 16px 24px 0;
  }
  .site-topbar-row {
    position: relative;
    height: 46px;
  }
  .site-wordmark {
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 46px;
    padding: 0;
    border: 0;
    background: transparent;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.2;
    white-space: nowrap;
    color: var(--ink);
    text-decoration: none;
  }
  .site-wordmark:hover {
    color: var(--ink);
  }
  /* The flag button shows the current language (§17): its flag and code, no
     arrow. A native details/summary keeps it working without a framework; the
     list below is only as wide as its text and carries no active-dot marker —
     the current row takes the app's accent tint instead. */
  .site-lang {
    position: absolute;
    right: 0;
    top: 0;
  }
  .site-flag {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 46px;
    padding: 0 13px;
    border-radius: var(--ui-radius);
    border: 1px solid rgba(var(--ink-rgb), 0.10);
    background: rgba(var(--ink-rgb), 0.06);
    color: var(--ink);
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    cursor: pointer;
    list-style: none;
  }
  .site-flag::-webkit-details-marker {
    display: none;
  }
  .site-flag::marker {
    content: '';
  }
  .site-flag:hover {
    background: rgba(var(--ink-rgb), 0.16);
  }
  .site-flag svg {
    display: block;
    border-radius: 2px;
  }
  .site-lang-list {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 31;
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: max-content;
    padding: 6px;
    border-radius: var(--ui-radius);
    border: 1px solid rgba(var(--ink-rgb), 0.10);
    background: var(--surface);
  }
  .site-lang-item {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 0 12px;
    border-radius: var(--ui-radius);
    color: var(--ink);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
  }
  .site-lang-item:hover {
    background: rgba(var(--ink-rgb), 0.08);
    color: var(--ink);
  }
  .site-lang-item svg {
    display: block;
    border-radius: 2px;
  }
  .site-lang-item--current {
    background: rgba(var(--accent-rgb), 0.14);
  }
  @media (max-width: 720px) {
    .site-topbar {
      padding: 10px 16px 0;
    }
  }

  /* The app's centred column: 880px of content like the app shell's inner
     column, with the same 24px gutters outside it (928px border-box). */
  .site-main {
    box-sizing: border-box;
    width: 100%;
    max-width: 928px;
    margin: 0 auto;
    padding: 26px 24px 72px;
    display: flex;
    flex-direction: column;
    gap: 22px;
    flex: 1;
  }
  @media (max-width: 720px) {
    .site-main {
      padding: 18px 16px 44px;
    }
  }

  /* The home page (§17, front-page-design.png): the photo spans the full
     viewport width behind the hero text, breaking out of the centred
     column; three numbered steps; the account card beside the types card. */
  .site-start {
    display: flex;
    flex-direction: column;
    gap: 40px;
  }
  .site-hero {
    position: relative;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    overflow: clip;
  }
  /* The photo bleeds off every viewport edge, so it meets nothing; the
     short fades live on the image's own top and bottom edges, where it
     meets the flat page, and the page itself stays flat. */
  .site-hero-media {
    position: absolute;
    inset: 0;
    /* The fades melt into the surrounding page: light in light mode, the
       wall green in dark. */
    background: var(--bg);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, black 72px, black calc(100% - 72px), transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, black 72px, black calc(100% - 72px), transparent 100%);
  }
  .site-hero-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* The machine fills the source's right third; the headline sits on the
       dark wall to the left, so the crop anchors right. */
    object-position: right center;
    display: block;
  }
  /* A fixed dark scrim over the photo's left half: the photo is the same
     in both themes, so the headline keeps one readable pairing in light
     and dark alike. */
  .site-hero-media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, rgba(6, 20, 16, 0.55), transparent 62%);
    pointer-events: none;
  }
  .site-hero-inner {
    position: relative;
    z-index: 1;
    box-sizing: border-box;
    width: 100%;
    max-width: 928px;
    margin: 0 auto;
    padding: 72px 24px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 20px;
    min-height: 560px;
  }
  .site-hero-title {
    margin: 0;
    font-family: 'Newsreader', Georgia, 'Times New Roman', serif;
    font-weight: 550;
    font-size: clamp(40px, 7vw, 64px);
    line-height: 1.04;
    letter-spacing: -0.01em;
    /* Fixed: the photo is the same dark wall in both themes, so the hero
       text keeps the dark pairing in light mode too. Everything below the
       hero follows the theme. */
    color: #f4efe4;
    text-wrap: pretty;
  }
  .site-hero-line {
    display: block;
  }
  .site-hero-accent {
    font-style: italic;
    font-weight: 500;
    color: #a9dca4;
  }
  .site-hero-lead {
    margin: 0;
    font-size: 17px;
    line-height: 1.55;
    color: #cddccf;
    max-width: 54ch;
    text-wrap: pretty;
  }
  /* The note, drawn like chalk paint on the wall: real selectable text,
     roughened and slightly faded by an SVG turbulence/displacement filter
     (defined beside it in HomeView, no image of text), tilted two degrees. */
  .site-hero-note {
    margin: 0;
    max-width: 30em;
    transform: rotate(-2deg);
    transform-origin: left center;
    font-family: 'Newsreader', Georgia, 'Times New Roman', serif;
    font-style: italic;
    font-weight: 600;
    font-size: 19px;
    line-height: 1.35;
    color: #dcebd4;
    opacity: 0.94;
    filter: url(#site-chalk);
    text-shadow: 0 1px 1px rgba(6, 20, 16, 0.8);
    text-wrap: pretty;
  }
  .site-hero-chalkdef {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
  }
  .site-cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    padding-top: 4px;
  }
  @media (min-width: 761px) {
    /* Finding r3-1: the 761-1100 band stopped at the width r2 happened to
       name, so at 1101-1170 the desktop lead ran back onto the towel and
       machine. One rule for every width above 760 sizes the text column
       relative to the viewport, so it always stays on the dark wall left
       of the machine and towel; the desktop crop and scrim are untouched. */
    .site-hero-title {
      max-width: min(546px, 40vw);
    }
    .site-hero-lead {
      max-width: min(546px, 40vw);
    }
    .site-hero-note {
      max-width: min(30em, 40vw);
    }
  }
  @media (max-width: 760px) {
    /* DECISIONS #26: on phones the chalk note, headline, lead and button
       sit on the photo as on a computer. The overlay layout stays; only the
       sizing and the scrim change. The scrim darkens the whole photo,
       strongest behind the text at the bottom, so every hero text passes
       WCAG AA at its size against the photo in both themes (the photo is
       the same dark wall in both, so the text keeps the fixed light
       pairing). */
    .site-hero-inner {
      padding: 88px 16px 40px;
      min-height: 560px;
      justify-content: flex-end;
    }
    .site-hero-media::after {
      background: linear-gradient(to top, rgba(6, 20, 16, 0.94) 0%, rgba(6, 20, 16, 0.74) 55%, rgba(6, 20, 16, 0.40) 100%);
    }
    .site-hero-title {
      font-size: clamp(38px, 11vw, 48px);
    }
  }

  /* Three numbered steps: number and title joined on one line, no icons,
     no arrows, the text under them. */
  .site-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 28px 40px;
  }
  .site-step {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
  }
  .site-step-title {
    margin: 0;
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--ink);
    text-wrap: pretty;
  }
  .site-step-number {
    flex: none;
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--accent);
  }
  .site-step p {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-2);
    text-wrap: pretty;
  }
  @media (max-width: 760px) {
    .site-steps {
      grid-template-columns: 1fr;
    }
  }

  /* The account card beside the types card: the account card is the
     narrower one. Stacked below 760px. */
  .site-cards {
    display: grid;
    grid-template-columns: 5fr 6fr;
    gap: 12px;
    align-items: stretch;
  }
  .site-account-card,
  .site-types-card {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 24px;
    border: 1px solid rgba(var(--ink-rgb), 0.1);
    border-radius: var(--ui-radius);
    background: var(--surface);
  }
  .site-card-title,
  .site-types-card h2 {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--ink);
    text-wrap: pretty;
  }
  .site-types-card p {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-2);
    text-wrap: pretty;
  }
  .site-account-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
  }
  .site-account-point {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--ink);
    text-wrap: pretty;
  }
  .site-check {
    margin-top: 3px;
    font-size: 13px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--accent);
  }
  .site-account-free {
    font-size: 13px;
    color: var(--muted-2);
  }
  /* The outlined account button: a smaller, transparent sibling of the
     filled hero button, with an accent border and arrow. */
  .site-outline-button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 8px 18px;
    border: 1px solid var(--accent);
    border-radius: var(--ui-radius);
    background: transparent;
    color: var(--ink);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.3;
    text-decoration: none;
  }
  .site-outline-button:hover {
    background: rgba(var(--accent-rgb), 0.12);
    color: var(--ink);
  }
  .site-types-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .site-types-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--ink);
  }
  .site-types-list li::before {
    content: '';
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
  }
  @media (max-width: 760px) {
    .site-cards {
      grid-template-columns: 1fr;
    }
  }

  /* Type — calm roles (§6.6): statement once per screen for the page
     title, body for reading text, micro for eyebrows. The home hero keeps
     its own serif style and is excluded here. */
  .site-main h1:not(.site-hero-title) {
    max-width: 22ch;
    font-size: 20px;
    line-height: 1.25;
    font-weight: 600;
    text-wrap: pretty;
  }
  .site-lead,
  .site-lead p {
    max-width: 58ch;
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-1);
    text-wrap: pretty;
  }
  .site-eyebrow {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 13px;
    line-height: 1.4;
    font-weight: 500;
    color: var(--muted-2);
    margin-top: 12px;
  }

  /* Quiet rows — the symptom, category and blog lists. No box around
     each line: title at full contrast, the second line muted, hover tint
     only. */
  .site-rows {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
  }
  .site-rows > li + li {
    border-top: 1px solid rgba(var(--ink-rgb), 0.1);
  }
  .site-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 44px;
    justify-content: center;
    padding: 12px 0;
    color: var(--ink);
    text-decoration: none;
  }
  a.site-row:hover {
    background: rgba(var(--ink-rgb), 0.06);
    color: var(--ink);
  }
  .site-row-title {
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    text-wrap: pretty;
  }
  .site-row-text {
    font-size: 13px;
    line-height: 1.4;
    color: var(--muted-2);
    text-wrap: pretty;
  }
  .site-row-kicker {
    font-size: 13px;
    line-height: 1.4;
    font-weight: 500;
    color: var(--muted-2);
  }
  .site-arrow::after {
    content: ' →';
    color: var(--accent);
  }

  /* Plain symptom sections — one title and running text, no boxes. */
  .site-cause {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 66ch;
  }
  .site-cause h2 {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--ink);
    text-wrap: pretty;
  }
  .site-cause p,
  .site-cause li {
    margin: 0;
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-1);
    text-wrap: pretty;
  }
  .site-cause ul {
    margin: 0;
    padding-left: 20px;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .site-cause-kicker {
    font-size: 13px;
    line-height: 1.4;
    font-weight: 500;
    color: var(--muted-2);
  }

  .site-codes {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 12px;
    padding: 0;
    list-style: none;
  }
  .site-codes li {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-family: var(--ui-font-mono);
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.06em;
    color: var(--muted-1);
  }
  .site-note p {
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-2);
  }

  /* Blog body — rendered Markdown. */
  .site-prose {
    max-width: 66ch;
    font-size: 15px;
    line-height: 1.5;
    color: var(--muted-1);
  }
  .site-prose > * + * {
    margin-top: 14px;
  }
  .site-prose h2,
  .site-prose h3 {
    color: var(--ink);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.5;
    margin-top: 26px;
  }
  .site-prose ul,
  .site-prose ol { padding-left: 22px; }

  .site-footer {
    padding: 22px 24px 30px;
    border-top: 1px solid rgba(var(--ink-rgb), 0.1);
    font-size: 15px;
    color: var(--muted-3);
    text-align: center;
  }

/*
 * Site-only serif display face (contract §6.9, §17): Newsreader for the home
 * hero headline. Loaded by the home page alone — www and the app's shared
 * front page — nothing else uses it. The
 * `../fonts/` spelling matches `@felsok3/ui`'s own stylesheets, so the
 * renderer's single `url()` rewrite hashes every font reference at once.
 * The two files also exist under `../fonts/` on disk (`src/fonts/`, copied
 * from `@felsok3/ui`), so bundlers importing this stylesheet (the app's
 * shared front page) resolve them; the worker only reads this text and is
 * unaffected.
 */

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/newsreader-latin-400-700-normal-4f77e5f968cd4704.woff2") format('woff2');
}

@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/newsreader-latin-400-700-italic-79c01e7ab5b9fc58.woff2") format('woff2');
}
