/* Generated by scripts/sync-ds.py. Do not edit: change the design system and re-run.
   Source: bwpromo/design@db10f23b50f7c1aaa132fe8b67fd1484382f81ab design-system/tokens/{typography.css,colors.css,spacing.css,radius.css,layout.css,elevation.css,motion.css,base.css}
   Font faces whose files the design system does not ship are dropped; url(../fonts/…) is rewritten to url(fonts/…). */

/* ═══ design-system/tokens/typography.css ═══ */
/* Docnote Design System — Typography
   Inter Variable + opsz cuts · weights · sizes · leading · tracking · utility classes
   v2.0 · 2026

   Note: @font-face url() paths are relative to this file's location
   (design-system/tokens/), so they reach back to ../fonts/. */

/* Inter Variable — optical-size axis (opsz 14–32) + full weight axis */
@font-face {
  font-family: 'Inter Variable';
  src: url('fonts/Inter-VariableFont_opsz_wght.ttf') format('truetype-variations'),
       url('fonts/Inter-VariableFont_opsz_wght.ttf') format('truetype');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter Variable';
  src: url('fonts/Inter-Italic-VariableFont_opsz_wght.ttf') format('truetype-variations'),
       url('fonts/Inter-Italic-VariableFont_opsz_wght.ttf') format('truetype');
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/* Inter static cuts — 18pt (body), 24pt (title), 28pt (display) optical sizes.
   Kept as fallbacks for environments that do not honour truetype-variations. */

:root {

  /* ─── Families ─── */
  --font-inter:  'Inter Variable', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-mono:   'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', ui-monospace, Consolas, monospace;
  --font-system: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* ─── Weights (full Inter axis) ─── */
  --weight-thin:        100;
  --weight-extralight:  200;
  --weight-light:       300;
  --weight-regular:     400;
  --weight-medium:      500;
  --weight-semibold:    600;
  --weight-bold:        700;
  --weight-extrabold:   800;
  --weight-black:       900;

  /* ─── Primitive size scale (rem · base 16px · ≈1.25 major-third) ─── */
  --size-10: 0.625rem;   /* 10 */
  --size-11: 0.6875rem;  /* 11 */
  --size-12: 0.75rem;    /* 12 */
  --size-13: 0.8125rem;  /* 13 */
  --size-14: 0.875rem;   /* 14 */
  --size-15: 0.9375rem;  /* 15 — entry-family body (off the major-third scale; established by .ac__body across welcome / login / create-password / account-created-success). Added 2026-05-11. */
  --size-16: 1rem;       /* 16 — base */
  --size-18: 1.125rem;   /* 18 */
  --size-20: 1.25rem;    /* 20 */
  --size-24: 1.5rem;     /* 24 */
  --size-28: 1.75rem;    /* 28 */
  --size-32: 2rem;       /* 32 */
  --size-40: 2.5rem;     /* 40 */
  --size-48: 3rem;       /* 48 */
  --size-56: 3.5rem;     /* 56 */
  --size-64: 4rem;       /* 64 */
  --size-72: 4.5rem;     /* 72 */
  --size-80: 5rem;       /* 80 */
  --size-96: 6rem;       /* 96 */

  /* ─── Semantic sizes by role ─── */
  /* Display */
  --font-size-display-2xl: var(--size-96);
  --font-size-display-xl:  var(--size-80);
  --font-size-display-lg:  var(--size-64);
  --font-size-display-md:  var(--size-56);
  --font-size-display-sm:  var(--size-48);
  /* Title */
  --font-size-title-xl:    var(--size-40);
  --font-size-title-lg:    var(--size-32);
  --font-size-title-md:    var(--size-28);
  --font-size-title-sm:    var(--size-24);
  --font-size-title-xs:    var(--size-20);
  /* Body */
  --font-size-body-xl:     var(--size-20);
  --font-size-body-lg:     var(--size-18);
  --font-size-body-md:     var(--size-16);   /* default body */
  --font-size-body-sm:     var(--size-14);
  --font-size-body-xs:     var(--size-13);
  /* Label */
  --font-size-label-lg:    var(--size-14);
  --font-size-label-md:    var(--size-13);
  --font-size-label-sm:    var(--size-12);
  /* --font-size-label-xs · DEPRECATED 2026-05-10 (typography-master audit pass).
     Was --size-11 (11 px regular weight, normal tracking) — failed the chrome /
     meta legibility floor because the small size + light weight + neutral
     tracking combination didn't carry letter recognition. Aliased to
     --font-size-label-sm (12 px) for backward compat — existing consumers
     continue to work but see a 12 px label instead of 11 px. Migrate consumers
     to --font-size-label-sm directly; remove the alias in v2.2.
     The .t-overline 11 px usage is preserved (different role — uppercase +
     tracking-widest +0.12em + semibold compensates for letter recognition). */
  --font-size-label-xs:    var(--font-size-label-sm);
  /* Caption / Overline / Code */
  --font-size-caption:     var(--size-12);
  --font-size-overline:    var(--size-11);
  /* Price hero · 28 px. Promoted to a semantic token 2026-05-14 (tatooine
     PR) when .service-detail-card V5 introduced a price-as-hero tier above
     the title in its right rail. Aliases the canonical --size-28 (which
     already sits in the major-third scale between --size-24 and --size-32).
     Consumer rule: any surface that surfaces a single price / total as
     the visual hero of a card composes this token rather than hardcoding
     28 px or rounding to title-md. Sits between --font-size-title-sm (24)
     and --font-size-title-md (28) in the canonical hierarchy — the alias
     gives the price-hero use case its own semantic anchor distinct from
     a title heading. */
  --font-size-price-hero:  var(--size-28);
  /* Metric hero (added 2026-10-21 · Finance Dashboard). An alias, not a new
     size: 56px (display-md) on desktop, 40px (title-xl) under 768px. */
  --font-size-metric-hero: var(--font-size-display-md);
  --font-size-code:        var(--size-14);
  --font-size-code-md:     var(--size-14);
  --font-size-code-sm:     var(--size-13);

  /* ─── Line heights ─── */
  --leading-none:       1;
  --leading-tight:      1.1;
  --leading-snug:       1.25;
  --leading-normal:     1.4;
  --leading-relaxed:    1.5;
  --leading-loose:      1.65;
  --leading-spacious:   1.75;

  /* ─── Line-height steps · 4px baseline ─── (canonical 2026-10-30 · approved by Justin)
     Every text role's line box is a whole multiple of 4px, so stacked text
     lands on the same 4px rhythm as --space-*. In rem so a line grows with the
     user's font size the way --size-* does (16px root). The unitless
     --leading-* ratios above stay for the raw .t-* ladder and marketing tiers.
     Canonical reference: preview/layouts-grid.html § Vertical rhythm. */
  --line-16: 1rem;      /* 16 · overline 11 · meta 12 */
  --line-20: 1.25rem;   /* 20 · label · data · item-title 14 · body-sm 14 */
  --line-24: 1.5rem;    /* 24 · body 16 · card-title 16 · section-title 18 · page-lead 15 */
  --line-28: 1.75rem;   /* 28 · page-title at 22 (≤ 480px) */
  --line-32: 2rem;      /* 32 · figure 28 · page-title at 24 (≤ 768px) */
  --line-36: 2.25rem;   /* 36 · page-title 28 */

  /* ─── Tracking ─── */
  --tracking-tight:   -0.04em;
  --tracking-snug:    -0.02em;
  --tracking-normal:   0em;
  --tracking-wide:     0.02em;
  --tracking-wider:    0.05em;
  --tracking-widest:   0.12em;
}

/* ─── Semantic type utility classes ─── */
/* Display tier · marketing & section dividers — Bold, tight tracking, tight leading */
.t-display-2xl { font-size: var(--font-size-display-2xl); font-weight: 700; line-height: 1.0;  letter-spacing: -0.04em;  color: var(--color-text-heading); }
.t-display-xl  { font-size: var(--font-size-display-xl);  font-weight: 700; line-height: 1.0;  letter-spacing: -0.04em;  color: var(--color-text-heading); }
.t-display-lg  { font-size: var(--font-size-display-lg);  font-weight: 700; line-height: 1.05; letter-spacing: -0.03em;  color: var(--color-text-heading); }
.t-display-md  { font-size: var(--font-size-display-md);  font-weight: 700; line-height: 1.1;  letter-spacing: -0.03em;  color: var(--color-text-heading); }
@media (max-width: 767px) { :root { --font-size-metric-hero: var(--font-size-title-xl); } }
.t-metric-hero { font-size: var(--font-size-metric-hero); font-weight: 700; line-height: 1.07; letter-spacing: -0.03em; color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
.t-display-sm  { font-size: var(--font-size-display-sm);  font-weight: 700; line-height: 1.15; letter-spacing: -0.02em;  color: var(--color-text-heading); }

/* Title tier · page & section headings — SemiBold */
.t-title-xl { font-size: var(--font-size-title-xl); font-weight: 600; line-height: 1.2;  letter-spacing: -0.02em;  color: var(--color-text-heading); }
.t-title-lg { font-size: var(--font-size-title-lg); font-weight: 600; line-height: 1.25; letter-spacing: -0.015em; color: var(--color-text-heading); }
.t-title-md { font-size: var(--font-size-title-md); font-weight: 600; line-height: 1.3;  letter-spacing: -0.01em;  color: var(--color-text-heading); }
.t-title-sm { font-size: var(--font-size-title-sm); font-weight: 600; line-height: 1.35; letter-spacing: -0.005em; color: var(--color-text-heading); }
.t-title-xs { font-size: var(--font-size-title-xs); font-weight: 600; line-height: 1.4;  letter-spacing: 0;        color: var(--color-text-heading); }

/* Body tier · running text — Regular */
.t-body-xl { font-size: var(--font-size-body-xl); font-weight: 400; line-height: 1.6;  letter-spacing: -0.01em; color: var(--color-text-body); }
.t-body-lg { font-size: var(--font-size-body-lg); font-weight: 400; line-height: 1.65; letter-spacing: 0;       color: var(--color-text-body); }
.t-body-md { font-size: var(--font-size-body-md); font-weight: 400; line-height: 1.65; letter-spacing: 0;       color: var(--color-text-body); }
.t-body-sm { font-size: var(--font-size-body-sm); font-weight: 400; line-height: 1.6;  letter-spacing: 0;       color: var(--color-text-body); }
.t-body-xs { font-size: var(--font-size-body-xs); font-weight: 400; line-height: 1.55; letter-spacing: 0;       color: var(--color-text-body); }

/* Label tier · UI controls, form fields — Medium */
.t-label-lg { font-size: var(--font-size-label-lg); font-weight: 500; line-height: 1.4;  letter-spacing: 0;      color: var(--color-text-heading); }
.t-label-md { font-size: var(--font-size-label-md); font-weight: 500; line-height: 1.4;  letter-spacing: 0;      color: var(--color-text-heading); }
.t-label-sm { font-size: var(--font-size-label-sm); font-weight: 500; line-height: 1.35; letter-spacing: 0.01em; color: var(--color-text-heading); }
/* .t-label-xs · DEPRECATED 2026-05-10 (typography-master audit pass).
   Now matches .t-label-sm exactly (12 / 500 / line-height 1.35 / tracking 0.01em).
   Existing consumers see a slight visual bump from 11 → 12 px and a softer tracking
   (0.02em → 0.01em). Migrate consumers to .t-label-sm directly; remove this rule
   in v2.2. New consumers MUST use .t-label-sm. */
.t-label-xs { font-size: var(--font-size-label-sm); font-weight: 500; line-height: 1.35; letter-spacing: 0.01em; color: var(--color-text-heading); }

/* Caption · metadata, helpers. bh-500 (7.13:1 on white); was --color-text-muted bh-300 (2.92:1, failed 1.4.3) until 2026-10-24. */
.t-caption  { font-size: var(--font-size-caption);  font-weight: 400; line-height: 1.5; letter-spacing: 0;     color: var(--color-text-muted-on-white); }

/* Overline · uppercase eyebrows */
.t-overline { font-size: var(--font-size-overline); font-weight: 600; line-height: 1.3; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-text-overline); }

/* Code · monospace */
.t-code-md  { font-family: var(--font-mono); font-size: var(--font-size-code-md); font-weight: 400; line-height: 1.55; letter-spacing: 0; }
.t-code-sm  { font-family: var(--font-mono); font-size: var(--font-size-code-sm); font-weight: 400; line-height: 1.55; letter-spacing: 0; }

/* ─── Entry-family typography · .ac__* paired pattern ───
   Promoted to canonical 2026-05-11 after four consumers shipped with
   identical specs across:
     · preview/layouts-welcome-entry.html
     · preview/layouts-login-entry.html
     · preview/layouts-create-password.html
     · preview/components-result-screen.html (absorbed the original
       layouts-account-created-success content in eadu PR 2026-05-15)

   DISTINCT from the open-ended .t-* utility family — .ac__title +
   .ac__body are a PAIRED surface-context pattern (h1 + lead paragraph)
   used on auth-centred / account-flow entry surfaces. Consumers compose
   the pair; one rarely ships without the other.

   The "ac" prefix originated in account-created-success but the family
   now spans login (returning users) and welcome (first-time invitees)
   too. Read it as "auth-centred / account-flow" rather than literally
   "account-created". Rename to .entry-* / .t-display-entry remains on
   the table for v2.1 deprecation cycle (logged in FOLLOWUPS).

   Title responsive scale matches .page-header__title:
     40 px desktop → 32 px ≤ 768 → 28 px ≤ 480.

   Body width capped at 440 px so the line length stays readable when
   the parent column is the entry-family canonical 480 px. */
.ac__title {
  font-size: var(--size-40);
  font-weight: var(--weight-bold);
  color: var(--color-text-heading);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0;
  text-wrap: balance;
}
.ac__body {
  font-size: var(--size-15);
  font-weight: var(--weight-regular);
  color: var(--color-text-body);
  line-height: 1.55;
  margin: 0;
  text-wrap: pretty;
  max-width: 440px;
}
/* Token-emphasis treatment for inline <b> inside .ac__body — used for
   personalisation tokens (patient name, physician name, practice name,
   email address). Heading colour + semibold weight lifts the token out
   of the body copy without colour-shifting. Promoted to canonical
   2026-05-15 (eadu PR) after independent implementations in
   apps/patient/account-created and apps/patient/welcome both rolled
   the same rule. */
.ac__body b {
  color: var(--color-text-heading);
  font-weight: var(--weight-semibold);
}
@media (max-width: 768px) {
  .ac__title { font-size: var(--size-32); }
}
@media (max-width: 480px) {
  .ac__title { font-size: var(--size-28); }
}

/* ─── Text roles ─── (canonical 2026-10-24 · approved by Justin)
   Pick text by what it does, never by size. Each role locks five values:
   --text-<role>-size · -weight · -leading · -tracking · -color.
   Components compose the tokens inside their own rules; pages use the
   .text-<role> class. The .t-* scale above stays as the raw ladder the
   roles are built from — do not reach for it in product UI.
   Heading ladder: page-title 28 → section-title 18 → card-title 16 → item-title 14.
   Leadings are 4px line boxes (--line-*, rev. 2026-10-30 · approved by Justin;
   were unitless 1.15–1.65, which landed on fractions such as 26.4 and 22.4).
   Canonical reference: preview/typography-master.html § 1. */
:root {
  --text-page-title-size: var(--size-28);    --text-page-title-weight: var(--weight-bold);       --text-page-title-leading: var(--line-36);  --text-page-title-tracking: -0.01em; --text-page-title-color: var(--color-text-heading);
  --text-page-lead-size: var(--size-15);     --text-page-lead-weight: var(--weight-regular);     --text-page-lead-leading: var(--line-24);  --text-page-lead-tracking: 0;        --text-page-lead-color: var(--color-text-body);
  --text-section-title-size: var(--size-18); --text-section-title-weight: var(--weight-semibold); --text-section-title-leading: var(--line-24); --text-section-title-tracking: -0.01em; --text-section-title-color: var(--color-text-heading);
  --text-card-title-size: var(--size-16);    --text-card-title-weight: var(--weight-semibold);   --text-card-title-leading: var(--line-24);  --text-card-title-tracking: 0;      --text-card-title-color: var(--color-text-heading);
  --text-item-title-size: var(--size-14);    --text-item-title-weight: var(--weight-semibold);   --text-item-title-leading: var(--line-20);  --text-item-title-tracking: 0;      --text-item-title-color: var(--color-text-heading);
  --text-body-size: var(--size-16);          --text-body-weight: var(--weight-regular);          --text-body-leading: var(--line-24);       --text-body-tracking: 0;            --text-body-color: var(--color-text-body);
  --text-body-sm-size: var(--size-14);       --text-body-sm-weight: var(--weight-regular);       --text-body-sm-leading: var(--line-20);     --text-body-sm-tracking: 0;         --text-body-sm-color: var(--color-text-body);
  --text-label-size: var(--size-13);         --text-label-weight: var(--weight-medium);          --text-label-leading: var(--line-20);       --text-label-tracking: 0;           --text-label-color: var(--color-text-heading);
  --text-data-label-size: var(--size-13);    --text-data-label-weight: var(--weight-regular);    --text-data-label-leading: var(--line-20);  --text-data-label-tracking: 0;      --text-data-label-color: var(--color-text-muted-on-white);
  --text-data-value-size: var(--size-13);    --text-data-value-weight: var(--weight-semibold);   --text-data-value-leading: var(--line-20);  --text-data-value-tracking: 0;      --text-data-value-color: var(--color-text-heading);
  --text-meta-size: var(--size-12);          --text-meta-weight: var(--weight-regular);          --text-meta-leading: var(--line-16);        --text-meta-tracking: 0;            --text-meta-color: var(--color-text-muted-on-white);
  --text-overline-size: var(--size-11);      --text-overline-weight: var(--weight-semibold);     --text-overline-leading: var(--line-16);    --text-overline-tracking: 0.1em;    --text-overline-color: var(--color-text-overline);
  --text-figure-size: var(--size-28);        --text-figure-weight: var(--weight-bold);           --text-figure-leading: var(--line-32);     --text-figure-tracking: -0.02em;    --text-figure-color: var(--color-text-heading);
  /* Button labels (added 2026-11-03 · DES-137). No colour slot: the variant sets the ink. */
  --text-button-size: var(--size-16);        --text-button-weight: var(--weight-semibold);       --text-button-leading: var(--line-24);      --text-button-tracking: var(--tracking-wide);
  --text-button-sm-size: var(--size-13);     --text-button-sm-weight: var(--weight-semibold);    --text-button-sm-leading: var(--line-20);   --text-button-sm-tracking: var(--tracking-wide);
  --text-button-xs-size: var(--size-12);     --text-button-xs-weight: var(--weight-semibold);    --text-button-xs-leading: var(--line-16);   --text-button-xs-tracking: var(--tracking-wide);
}
/* Small and extra-small button labels step up one size below 640 px (--bp-mobile), where those buttons grow to the 44 px touch floor. */
@media (max-width: 639px) { :root { --text-button-sm-size: var(--size-14); --text-button-xs-size: var(--size-13); } }
/* The page title steps with the viewport through its token, so every consumer steps together. */
@media (max-width: 768px) { :root { --text-page-title-size: var(--size-24); --text-page-title-leading: var(--line-32); } }
@media (max-width: 480px) { :root { --text-page-title-size: 1.375rem; /* 22 · off-scale, matches .page-header__title today */ --text-page-title-leading: var(--line-28); } }

.text-page-title, .text-page-lead, .text-section-title, .text-card-title, .text-item-title, .text-body, .text-body-sm,
.text-label, .text-data-label, .text-data-value, .text-meta, .text-overline, .text-figure { margin: 0; }
.text-page-title    { font-size: var(--text-page-title-size);    font-weight: var(--text-page-title-weight);    line-height: var(--text-page-title-leading);    letter-spacing: var(--text-page-title-tracking);    color: var(--text-page-title-color); text-wrap: balance; }
.text-page-lead     { font-size: var(--text-page-lead-size);     font-weight: var(--text-page-lead-weight);     line-height: var(--text-page-lead-leading);     letter-spacing: var(--text-page-lead-tracking);     color: var(--text-page-lead-color); }
.text-section-title { font-size: var(--text-section-title-size); font-weight: var(--text-section-title-weight); line-height: var(--text-section-title-leading); letter-spacing: var(--text-section-title-tracking); color: var(--text-section-title-color); }
.text-card-title    { font-size: var(--text-card-title-size);    font-weight: var(--text-card-title-weight);    line-height: var(--text-card-title-leading);    letter-spacing: var(--text-card-title-tracking);    color: var(--text-card-title-color); }
.text-item-title    { font-size: var(--text-item-title-size);    font-weight: var(--text-item-title-weight);    line-height: var(--text-item-title-leading);    letter-spacing: var(--text-item-title-tracking);    color: var(--text-item-title-color); }
.text-body          { font-size: var(--text-body-size);          font-weight: var(--text-body-weight);          line-height: var(--text-body-leading);          letter-spacing: var(--text-body-tracking);          color: var(--text-body-color); }
.text-body-sm       { font-size: var(--text-body-sm-size);       font-weight: var(--text-body-sm-weight);       line-height: var(--text-body-sm-leading);       letter-spacing: var(--text-body-sm-tracking);       color: var(--text-body-sm-color); }
.text-label         { font-size: var(--text-label-size);         font-weight: var(--text-label-weight);         line-height: var(--text-label-leading);         letter-spacing: var(--text-label-tracking);         color: var(--text-label-color); }
.text-data-label    { font-size: var(--text-data-label-size);    font-weight: var(--text-data-label-weight);    line-height: var(--text-data-label-leading);    letter-spacing: var(--text-data-label-tracking);    color: var(--text-data-label-color); }
.text-data-value    { font-size: var(--text-data-value-size);    font-weight: var(--text-data-value-weight);    line-height: var(--text-data-value-leading);    letter-spacing: var(--text-data-value-tracking);    color: var(--text-data-value-color); font-variant-numeric: tabular-nums lining-nums; }
.text-meta          { font-size: var(--text-meta-size);          font-weight: var(--text-meta-weight);          line-height: var(--text-meta-leading);          letter-spacing: var(--text-meta-tracking);          color: var(--text-meta-color); }
.text-overline      { font-size: var(--text-overline-size);      font-weight: var(--text-overline-weight);      line-height: var(--text-overline-leading);      letter-spacing: var(--text-overline-tracking);      color: var(--text-overline-color); text-transform: uppercase; }
.text-figure        { font-size: var(--text-figure-size);        font-weight: var(--text-figure-weight);        line-height: var(--text-figure-leading);        letter-spacing: var(--text-figure-tracking);        color: var(--text-figure-color); font-variant-numeric: tabular-nums lining-nums; }

/* ═══ design-system/tokens/colors.css ═══ */
/* Docnote Design System — Colour tokens
   Blue Hour · Daybreak · status · validation
   v2.0 · 2026 */

:root {

  /* ─── Blue Hour Scale ─── */
  --color-bh-50:   #EEF3F9;
  --color-bh-100:  #D4E1EE;
  --color-bh-200:  #AABFD9;
  --color-bh-300:  #7A9ABF;
  --color-bh-400:  #4F78A5;
  --color-bh-500:  #2E5A8A;
  --color-bh-600:  #163F6D;
  --color-bh-700:  #082C54;
  --color-bh-800:  #051E3A;
  --color-bh-900:  #031221;
  --color-bh-950:  #010810;

  /* ─── Daybreak Scale ─── */
  --color-db-50:   #EAF6FE;
  --color-db-100:  #C7E9FC;
  --color-db-200:  #90D3F9;
  --color-db-300:  #52BBF4;
  --color-db-400:  #1B99E8;
  --color-db-500:  #1280C7;
  --color-db-600:  #0D63A0;
  --color-db-700:  #094B7A;
  --color-db-800:  #062E5A;
  --color-db-900:  #031A36;
  /* Vivid Daybreak (added 2026-11-03 · DES-137 · approved by Justin): the
     Daybreak hue (203°) at full saturation, stepped by contrast with white.
     db-400 is 3.10:1 and db-500 4.25:1, so neither can carry a white label;
     vivid-500 is the brightest step on the hue that passes 1.4.3. Used by the
     accent button fill (components.css § Buttons) only. */
  --color-db-vivid-500: #0079C5;   /* white 4.62:1 · accent button rest */
  --color-db-vivid-600: #006BAE;   /* white 5.65:1 · hover */
  --color-db-vivid-700: #005C96;   /* white 7.07:1 · active */

  /* ─── Base ─── */
  --color-white: #FFFFFF;

  /* ─── White Alpha ─── */
  --color-wa-8:  rgba(255,255,255,0.08);
  --color-wa-12: rgba(255,255,255,0.12);
  --color-wa-16: rgba(255,255,255,0.16);
  --color-wa-24: rgba(255,255,255,0.24);
  --color-wa-32: rgba(255,255,255,0.32);

  /* ─── Daybreak Alpha ─── */
  --color-da-4:  rgba(27,153,232,0.04);
  --color-da-8:  rgba(27,153,232,0.08);
  --color-da-12: rgba(27,153,232,0.12);
  --color-da-16: rgba(27,153,232,0.16);
  --color-da-24: rgba(27,153,232,0.24);

  /* ─── Scrim ─── shared by .modal-scrim and .drawer-scrim
     Blue Hour 700 at 32% — cool, precise, never grey-overlay. */
  --color-scrim: rgba(8, 44, 84, 0.32);

  /* ─── Semantic Text ───
     Every semantic token below points at the scale (var()), so a palette
     change can never leave a stale copy behind (linked 2026-10-24). */
  --color-text-heading:        var(--color-bh-700);  /* 14.01:1 on white */
  --color-text-body:           var(--color-bh-600);  /* 10.68:1 */
  --color-text-muted:          var(--color-bh-500);  /* 7.13:1 on white · 6.39:1 on Cloud. Repointed 2026-10-24 (approved by Justin) from bh-300, which was 2.92:1 / 2.61:1 and failed WCAG 1.4.3 + 1.4.11. Muted text and icons on white or Cloud. */
  --color-text-muted-on-white: var(--color-text-muted); /* DEPRECATED 2026-10-24 · alias of --color-text-muted (now the same bh-500); migrate, removed next cycle */
  --color-text-overline:       var(--color-bh-500);  /* uppercase eyebrow / section header (.t-overline, .nav-cat) · 7.13:1 on white, 6.39:1 on Cloud */
  --color-text-link:           var(--color-db-600);  /* 6.24:1 */
  --color-text-link-dark:      var(--color-db-300);  /* links on on-brand dark (bh-700+) surfaces, e.g. the snackbar */
  --color-text-link-secondary: var(--color-bh-700);  /* utility / escape-hatch links · paired with underline; keeps Daybreak budget for primary actions */
  --color-text-inverse:        var(--color-white);
  --color-text-inverse-muted:  var(--color-bh-200);  /* [DS REVIEW] used only by the dark-stage demos in preview/components-divider · -avatar · -badges (dark-mode leftovers) */
  --color-placeholder:         var(--color-bh-400);  /* 4.60:1 on white · input placeholders and the floating-label resting colour only (raised from bh-300 2026-08-12 · kamino) */
  --color-selection-bg:        var(--color-db-100);
  --color-selection-text:      var(--color-bh-700);

  /* ─── Semantic Surfaces ───
     Chrome is light by default. Cloud (bh-50) for top-nav and sidebar
     creates a recessed feel against the white content surface.
     Light only — dark mode was removed 2026-10-23. */
  --color-surface-page:         var(--color-white);           /* default content surface */
  --color-surface-recessed:     var(--color-bh-50);           /* Cloud chrome: nav, sidebar, page recess */
  --color-surface-neutral:      #F5F5F7;                      /* neutral grey — soft content fills that sit ON the white surface (info pill · summary panels); not chrome. Off-scale by design. Added 2026-10-09. */
  --color-surface-nav:          var(--color-surface-recessed); /* DEPRECATED 2026-10-24 · merged into --color-surface-recessed; removed next cycle */
  --color-surface-sidebar:      var(--color-surface-recessed); /* DEPRECATED 2026-10-24 · merged into --color-surface-recessed; removed next cycle */

  /* ─── Semantic Fills ─── (canonical 2026-10-24 · approved by Justin)
     The fill behind a thing. Components use these, never a raw scale step.
     Fills sit below 3:1 against white by design, so a selected state never
     relies on fill alone (pair it with weight, an accent bar or a check). */
  --color-fill-hover:          var(--color-bh-50);   /* hover on a white surface: row, ghost button, menu item */
  --color-fill-hover-recessed: var(--color-bh-100);  /* hover on a Cloud surface, where bh-50 would not show: sidebar row, chrome control */
  --color-fill-subtle:         var(--color-bh-100);  /* resting soft fill: progress track, count pill, skeleton, chip */
  --color-fill-accent:         var(--color-db-50);   /* Daybreak tint for selection: selected row, focus halo, highlighted chart period */
  --color-fill-strong:         var(--color-bh-700);  /* solid Blue Hour: active nav pill, pressed quick filter or choice pill, avatar · white text 14.01:1 */

  /* ─── Scroll rail ─── (canonical 2026-10-27 · approved by Justin · components.css § Scroll rail)
     The one in-component scroll indicator: a hairline rail with a Blue Hour pill that
     deepens while it scrolls, is hovered or dragged. Chrome, not interaction, so never Daybreak. */
  --color-scroll-rail:         var(--color-bh-100);  /* the 2px rail · 1.33:1, decorative (the pill carries the position; exempt from 1.4.11) */
  --color-scroll-thumb:        var(--color-bh-400);  /* the pill at rest · 4.60:1 on white · 3.46:1 on the rail (1.4.11 ≥ 3:1) */
  --color-scroll-thumb-active: var(--color-bh-500);  /* scrolling · hover · drag · 7.13:1 on white · 5.37:1 on the rail */

  /* ─── Semantic Borders ─── */
  --color-border:           var(--color-bh-200);  /* dividers, decorative outlines (exempt from 1.4.11) */
  --color-border-control:   var(--color-bh-400);  /* bh-400 — resting boundary for form CONTROLS (input · select · checkbox · radio · toggle-off). 4.60:1 on white, clears WCAG 1.4.11 (3:1) for a control boundary; bh-200 (--color-border) is only 1.88:1 and stays for dividers / decorative outlines, which are exempt. Added 2026-07-07 (jedha). */
  --color-card-border:      var(--color-bh-500);  /* bh-500 — the unified card-family border (.card · .summary-list · .file-card). A deliberately stronger boundary than bh-200 dividers, paired with --shadow-soft as the card elevation treatment. Added 2026-08-04 (ilum). */
  --color-border-subtle:    var(--color-bh-100);  /* hairlines, row dividers */

  /* ─── Interactive ───
     Primary CTA fill restored to the original Daybreak db-400 (#1B99E8) on
     2026-05-20 — brand-first decision documented in
     design-system/preview/components-buttons.html § Decision log 2026-05-20.
     Reverts the 2026-05-08 darkening (db-600 → db-700 → db-800) that had
     been introduced to clear WCAG 1.4.3 AA contrast for normal text on
     .btn-accent. Accessibility trade-off — known + deliberate: white-on-
     fill contrast is ~3.09:1, which FAILS WCAG 1.4.3 AA for normal text
     (4.5:1) but passes the AA "large text" floor (3:1) at btn-touch
     16 px / 600 button labels. Brand vibrancy preserved at the cost of
     this gap on the button-fill text. Focus-visible ring stays db-400
     (unchanged) — vivid against white. Hover / active step one and
     two shades darker per the canonical shade-step rule in CLAUDE.md
     (db-400 → db-500 → db-600). */
  --color-interactive-accent:   var(--color-db-400);  /* db-400 — original Daybreak; brand-first revert 2026-05-20 (was db-600 #0D63A0). White-on-fill ~3.09:1 — fails WCAG 1.4.3 AA normal-text; passes "large text" 3:1 at touch size */
  --color-interactive-primary:  var(--color-bh-700);
  --color-interactive-hover:    var(--color-db-500);  /* db-500 — one shade darker than the db-400 anchor */
  --color-interactive-active:   var(--color-db-600);  /* db-600 — two shades darker than the db-400 anchor */
  --color-focus-ring:           var(--color-db-400);  /* unchanged — db-400 remains for focus rings & focus-ring alphas; non-text-contrast on white = 3.09:1 (passes 3:1 for non-text) */

  /* ─── Status Critical ─── */
  --status-critical-bg:     #FDDDD9;
  --status-critical-border: #E57373;
  --status-critical-icon:   #C0392B;
  --status-critical-text:   #7B1A10;
  /* Shade-step tokens for .btn-destructive hover/active.
     Derived from --status-critical-icon (#C0392B) at 88% / 76% sRGB → black.
     Replaces the color-mix() inline calls in components.css (added 2026-05-08). */
  --status-critical-hover:  #A93226;
  --status-critical-active: #922B21;

  /* ─── Status Pending ─── */
  --status-pending-bg:     #FEF3C7;
  --status-pending-border: #F59E0B;
  --status-pending-icon:   #D97706;
  --status-pending-text:   #78350F;
  --status-pending-fill:   #B45309;  /* solid (filled) Amber badge fill — white label 5.02:1 (AA). The icon amber #D97706 is only 3.19:1 with white, too light for an 11px label. Added 2026-10-13. */

  /* ─── Status Resolved ─── */
  --status-resolved-bg:     #DCFCE7;
  --status-resolved-border: #4ADE80;
  --status-resolved-icon:   #1A7C4A;
  --status-resolved-text:   #14532D;

  /* ─── Status Info ─── */
  --status-info-bg:     #EFF8FF;
  --status-info-border: #60C8F5;
  --status-info-icon:   #1B99E8;
  --status-info-text:   #0C4A6E;

  /* ─── Status Scheduled ─── */
  --status-scheduled-bg:     #EDE9FE;
  --status-scheduled-border: #A78BFA;
  --status-scheduled-icon:   #6D28D9;
  --status-scheduled-text:   #3B0764;

  /* ─── Status Due · work owed ─── [DS REVIEW]
     Sixth status family (2026-06-12 · kashyyyk): prepaid work the office
     still owes — payment is collected but the document/form is not yet
     delivered (Prepaid + "Awaiting document"). True orange, deliberately
     distinct from BOTH Amber pending (#D97706 — "patient owes money")
     and Validation Orange (#EA580C — reserved exclusively for form
     errors; the icon tier here is orange-700 #C2410C so the two never
     read as the same colour). Same 4-tier recipe as the other families.
     Flagged for design-lead ratification + a colors-status.html card. */
  --status-due-bg:     #FFEDD5;
  --status-due-border: #FB923C;
  --status-due-icon:   #C2410C;
  --status-due-text:   #9A3412;

  /* ─── Validation ─── */
  --color-validation-warning: #EA580C;
  --color-validation-bg:      #FFF7ED;
  --color-validation-border:  #FED7AA;
  --color-validation-text:    #7C2D12;

  /* ─── Data visualisation · categorical series ───
     Approved 2026-10-21 (Justin) from the palette study, Candidate 1
     (preview/foundations-data-viz.html). Blue Hour anchors slot 1; six
     complementary hues follow. Every series ≥ 3:1 on white (WCAG 1.4.11)
     and ≥ 7 ΔE from every status icon colour. The order is deliberate:
     neighbours stay distinct under deuteranopia, protanopia and tritanopia,
     so assign slots in order and never re-sort them by value.
     Use ONLY inside chart plot areas and their legends (see CLAUDE.md →
     Colour → Data visualisation). Never on chrome, text or controls.
     Clay (slot 6) never shares a chart with status-coloured marks: it sits
     in the red-orange family, 8.7 ΔE from Critical Scarlet. */
  --viz-series-1: var(--color-bh-500);  /* Blue Hour (= bh-500) · 7.13:1 */
  --viz-series-2: #946B3E;  /* ochre · 4.74:1 */
  --viz-series-3: #19A0A0;  /* teal  · 3.19:1 */
  --viz-series-4: #704060;  /* plum  · 8.10:1 */
  --viz-series-5: #81955E;  /* olive · 3.28:1 */
  --viz-series-6: #944123;  /* clay  · 6.91:1 */
  --viz-series-7: #7466B5;  /* iris  · 4.87:1 */

  /* ─── Data visualisation · single-series roles ───
     Approved 2026-10-21 (Justin). Aliases only, no new values. Most
     dashboard charts carry one series: primary for the series / selected
     mark, muted for everything it is compared against. Muted (bh-300,
     2.92:1) relies on the WCAG 1.4.11 exception: value labels and the
     chart's table view carry the same data. Highlight is the Daybreak
     selection halo (interaction, not a data fill). */
  --viz-primary:   var(--color-bh-500);       /* = --viz-series-1 · 7.13:1 */
  --viz-muted:     var(--color-bh-300);       /* non-selected, comparison, fee steps */
  --viz-highlight: var(--color-db-50);        /* selected-period halo · highlighted column */
  --viz-grid:      var(--color-border-subtle); /* gridlines */
  --viz-baseline:  var(--color-border);       /* zero line · axis */
  --viz-emphasis:  var(--color-bh-700);       /* totals (e.g. waterfall Net to bank) · 12.6:1 */

  /* ─── Data visualisation · gain / loss ───
     Added 2026-10-21 (Justin · Finance Dashboard). A scoped exception to
     "colour is reserved for consequence": used ONLY on delta indicators
     (.delta). Derived from the resolved + critical status families so they
     stay on-palette. Sentiment is decided by meaning, not sign: refunds
     going up is a loss. */
  --color-gain-text:          var(--status-resolved-text);   /* #14532D */
  --color-gain-bg:            var(--status-resolved-bg);
  --color-loss-text:          var(--status-critical-text);   /* #7B1A10 */
  --color-loss-bg:            var(--status-critical-bg);
  --color-delta-neutral-text: var(--color-text-muted);
  --color-delta-neutral-bg:   var(--color-surface-neutral);
}

/* ═══ design-system/tokens/spacing.css ═══ */
/* Docnote Design System — Spacing
   4px baseline · three semantic levels · spacing roles
   v2.0 · 2026 */

:root {

  /* ─── Spacing scale (4px baseline) ───
     L1 component-internal · L2 component-external · L3 section
     Defaults lean generous — 24, 32, 48 are the workhorses, not 8, 12, 16.
     Use these raw steps inside a component (icon gaps, control padding);
     for layout between and around things, use the roles below. */
  --space-0-5: 2px;   /* tight internals · a value and its sub-line, hairline offsets (added 2026-10-24) */
  --space-1:  4px;
  --space-1-5: 6px;   /* tight internals · chip and pill gaps, small icon gaps (added 2026-10-24) */
  --space-2:  8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* ─── Spacing roles ─── (canonical 2026-10-24 · approved by Justin)
     Pick space by the job it does, never by the number. Each role steps
     down once at the 640px mobile edge (below), so components inherit
     mobile spacing and never write their own @media spacing override.
     Replaces the never-used --space-v-* vertical-rhythm tokens.
     Canonical reference: preview/spacing-scale.html § 1. */

  /* Inset · padding inside a container */
  --space-inset-compact: var(--space-4);   /* 16 · dense: bottom sheet, menu panel */
  --space-inset-default: var(--space-5);   /* 20 · panels and tiles: panel card, inset card, metric tile */
  --space-inset-roomy:   var(--space-6);   /* 24 · primary surfaces: card, modal, drawer, callout, metric hero */

  /* Stack · gap between things in a column */
  --space-stack-tight:   var(--space-2);   /*  8 · inside one unit: title and body, label and control */
  --space-stack-default: var(--space-4);   /* 16 · between siblings: list items, comments, tiles, facts */
  --space-stack-loose:   var(--space-6);   /* 24 · between larger units: cards in a column, form fields */

  /* Section · gap between blocks of a page or drawer */
  --space-section:       var(--space-8);   /* 32 · between sections on a page, in a drawer, in a long form */
  --space-section-lg:    var(--space-12);  /* 48 · major breaks: around the page header, empty states */

  /* Relationship ladder (rev. 2026-10-30 · approved by Justin). Space grows
     with distance in meaning, one step per relationship, never a number:
       related   4 · 8   --space-1 · stack-tight  a value and its sub-line; label and control; a title and its body
       item      16      stack-default            siblings in a list; a section title and its content
       field     24      stack-loose              form fields; cards in a column
       section   32      section                  sections of a page, drawer or long form
       major     48      section-lg               around the page header; empty states
     A heading sits closer to what it names than to what came before it. */
}

/* Mobile step · the one place spacing changes with the viewport (--bp-mobile). */
@media (max-width: 640px) {
  :root {
    --space-inset-compact: var(--space-3);  /* 16 → 12 */
    --space-inset-default: var(--space-4);  /* 20 → 16 */
    --space-inset-roomy:   var(--space-5);  /* 24 → 20 */
    --space-stack-loose:   var(--space-5);  /* 24 → 20 */
    --space-section:       var(--space-6);  /* 32 → 24 */
    --space-section-lg:    var(--space-8);  /* 48 → 32 */
  }
}

/* ═══ design-system/tokens/radius.css ═══ */
/* Docnote Design System — Border Radius
   Radius is intent, not decoration.
   v2.0 · 2026 */

:root {

  /* ─── Border Radius · scale ───
     The raw steps. Components never use these directly; they use the
     roles below. The scale exists to define roles.
     inset sits 2px tighter than md so a child reads as contained, not flush. */
  --radius-none:  0px;
  --radius-sm:    4px;
  --radius-inset: 6px;
  --radius-md:    8px;
  --radius-lg:   14px;
  --radius-pill: 999px;

  /* ─── Radius roles ─── (canonical 2026-10-24 · approved by Justin)
     Pick a corner by the kind of thing, never by size. A container that
     should feel more important gets a title or a stronger shadow, never
     a rounder corner. Canonical reference: preview/radius.html § 1. */
  --radius-surface:   var(--radius-md);    /*   8 · every container: card, summary list, file card, callout, modal, bottom sheet, menu, tooltip, banner */
  --radius-control:   var(--radius-md);    /*   8 · inputs, selects, icon buttons, folder tabs, profile link */
  --radius-action:    var(--radius-lg);    /*  14 · buttons at every size, nav rows */
  --radius-label:     var(--radius-pill);  /* 999 · badges, tags, counts, avatars, pill controls (search field, quick filters, choice pills, segmented) */
  --radius-authority: var(--radius-none);  /*   0 · status cards, toasts, form error summaries */
  --radius-nested:    var(--radius-inset); /*   6 · a child inside a surface: menu items, file-type icons, validation messages, search results */
  --radius-utility:   var(--radius-sm);    /*   4 · small marks: checkbox, pager button, copy button, skeleton, code chip */
}

/* ═══ design-system/tokens/layout.css ═══ */
/* Docnote Design System — Layout
   Breakpoints · bar heights · touch primitives · sidebar · content · grid · icons · avatars · nav rows
   v2.0 · 2026

   Note: this file folds together the breakpoints, container sizes and the
   layout primitives because they are tightly coupled to the layout
   decisions that surround them. */

:root {

  /* ─── Breakpoints ─── universal across B2B physician + B2C patient.
     One set of tokens · 6 tiers · the 640 px mobile tier is the major
     transformation point (app-bar replaces topnav · tab-bar replaces sidebar
     · touch density auto-engages · drawer becomes sheet).

     EDGE RULE (canonical 2026-10-24 · approved by Justin): a breakpoint's
     width belongs to the tier named after it.
       · Ceilings (480 · 640 · 768): write  @media (max-width: Npx)
         and the other side                 @media (min-width: N+1px).
         640 px itself is mobile; 768 px itself is tablet.
       · Floor (1280):                      @media (min-width: 1280px)
         and the other side                 @media (max-width: 1279px).
         A 1280 px laptop gets laptop chrome.
       · Targets (1440 · 1680): design widths only — no queries use them.
     Never write the edge the other way (639 · 641-for-768 · 767 · 1281) and
     never invent a breakpoint between tiers.

     CSS cannot read custom properties inside @media / @container, so these
     tokens document the values; queries hard-code the number and
     design-system/lib/breakpoints.ts is the single source for JavaScript. */
  --bp-mobile-sm:  480px;   /* ceiling · smallest phones — page title 22 px */
  --bp-mobile:     640px;   /* ceiling · MOBILE TRANSFORMATION — chrome swap + touch auto-engage */
  --bp-tablet:     768px;   /* ceiling · tablet adjustments — page title 24 px, chrome stays desktop */
  --bp-laptop:    1280px;   /* floor · sidebar expanded 240 px from here up; collapsed to 64 px with a hamburger below */
  --bp-desktop:   1440px;   /* target · primary design width (no queries) */
  --bp-wide:      1680px;   /* target · ceiling — content max-width centred (no queries) */

  /* ─── Container sizes ─── (canonical 2026-10-24 · approved by Justin)
     For layout that depends on its own width, not the window — a card, a
     tile row, a detail page's main column — use a container query at one of
     these sizes, in the ceiling form: @container (max-width: Npx) means
     "N or narrower" (never N−1). The element's parent sets
     container-type: inline-size. Same values in lib/breakpoints.ts → CQ.
     Named exception: the metric tile's 180 / 200 px type-fit steps. */
  --cq-xs:  360px;   /* narrow card or rail: two-column grids stack */
  --cq-sm:  480px;   /* phone-width card or drawer: bars drop below labels, captions wrap */
  --cq-md:  600px;   /* two-up becomes one-up: card grid, metric tiles, dashboard bands */
  --cq-lg:  720px;   /* four-up becomes two-up; toolbars wrap */
  --cq-xl:  900px;   /* a detail page's main column collapses from two columns to one */

  /* ─── Bar heights ─── (v2.1 · 2026-04 unified token system)
     A single token system describes every horizontal bar in the product, on
     every surface. The desktop top nav, the mobile app-bar, the auth brand-bar,
     and the bottom tab-bar all draw from these. Two heights cover all current
     bars; --nav-height is preserved as an alias of --bar-height-md for backward
     compatibility. */
  --bar-height-sm:      56px;   /* mobile app-bar, brand-bar, sheet header */
  --bar-height-md:      64px;   /* desktop top nav, bottom tab-bar */
  --bar-height-lg:      96px;   /* onboarding-header bar (+ patient sticky-header offsets).
                                   Fits a full-width logo at natural aspect with generous
                                   vertical padding. (Was also the brand-nav topnav height;
                                   the Blue Hour brand variant was removed 2026-07-09 · naboo.) */
  --nav-height:         var(--bar-height-md);  /* alias — desktop top nav */
  /* B2B floating shell header row (rev. 2026-10-24): --space-4 + the 48px
     utilities row + --space-4. The sidebar brand row shares it, so the logo
     and the workspace switcher sit on one centre line. */
  --shell-header-height: calc(var(--space-4) * 2 + var(--space-12));  /* 80px */
  --tab-bar-height:     var(--bar-height-md);  /* alias — mobile bottom nav */

  /* ─── Touch & safe-area primitives ───
     Surface-agnostic. These describe input-modality rules (touch density)
     and platform env() values, not a "mobile vs desktop" dichotomy.
     Any surface where touch dominates — phone, tablet kiosk, Surface in
     tablet mode — engages the .--touch sizing variants. */
  --touch-target-min:   44px;   /* WCAG 2.5.5 floor — minimum hit area */
  --safe-area-bottom:   env(safe-area-inset-bottom, 0px);
  --safe-area-top:      env(safe-area-inset-top, 0px);

  /* ─── Form layout ───
     Governs how form inputs size and collapse across page column structures.
     --form-col-max caps a single-column form (primary data entry) — matches
     the reviewed /login + /welcome width. --input-min-w is the usable floor a
     column may shrink to before it drops to fewer columns; it feeds the
     .form-grid repeat(auto-fit, minmax(--input-min-w, 1fr)) collapse (4→2→1).
     Decided by interview 2026-07-07 (jedha) — see
     preview/components-form-inputs-primitives.html § Column layout. */
  --form-col-max:      480px;   /* single-column form cap · primary data entry */
  --input-min-w:       200px;   /* min usable input width · columns collapse below this */
  --input-height:       56px;   /* control height · .input + .select (was 40px · raised 2026-08-12
                                   for a comfortable touch target — clears the 44px --touch-target-min
                                   floor with margin. Trailing icons centre on top:50% so they follow.
                                   Auth keeps its own explicit .auth__field .input override.) */
  --control-height-sm:  40px;   /* compact control · toolbar <SearchField size="sm">, .tbl__btn,
                                   .tbl__filter-select (added 2026-10-23 · matches .btn-sm) */
  --control-height-lg:  64px;   /* hero control · <SearchField size="hero"> (added 2026-10-23) */

  /* ─── Sidebar / content / grid ─── */
  --sidebar-expanded:  240px;
  --sidebar-collapsed:  64px;
  --content-max-width:1200px;   /* the content box, inside the margins */
  --content-margin-xs:  var(--space-4);  /* 16 · ≤ 640px */
  --content-margin-sm:  32px;   /* 641–1279px */
  --content-margin-md:  48px;   /* ≥ 1280px */
  --grid-columns:        12;
  --grid-gutter-sm:     20px;   /* DEPRECATED 2026-10-30 · unused (.card-grid moved to --grid-gutter); removed next cycle */
  --grid-gutter-md:     24px;

  /* ─── Page grid ─── (canonical 2026-10-30 · approved by Justin)
     One 12-column grid at every tier, laid on the content column (the window
     minus the sidebar). Only the gutter and the outer margin step; the
     margin is the same on both sides. Read these, never a tier's number:
       --grid-margin   48 ≥ 1280 · 32 at 641–1279 · 16 ≤ 640
       --grid-gutter   24 above 640 · 16 ≤ 640
       --grid-max      1200, the content box between the margins
     At the 1200 cap a column is 78px (--grid-col-max), so the split asides
     below land on whole columns: 8 + 4 = 792 + 24 + 384.
     Canonical reference: preview/layouts-grid.html. */
  --grid-margin:   var(--content-margin-md);
  --grid-gutter:   var(--grid-gutter-md);
  --grid-max:      var(--content-max-width);
  --grid-col-max:  calc((var(--content-max-width) - (var(--grid-columns) - 1) * var(--grid-gutter-md)) / var(--grid-columns));  /* 78 */

  /* ─── Column widths · single column and split ───
     A single column caps at a measure: 720 for reading (detail, settings,
     help) and 480 for a form (--form-col-max). A split pairs a fluid main
     column with a fixed aside on whole columns of the 1200 grid. */
  --measure-reading: var(--cq-lg);        /* 720 · a reading column of blocks (cards, tables) */
  --measure-form:    var(--form-col-max); /* 480 */
  /* Running text caps at a line length, not a column: 34em is about 75
     characters of Inter at any size (body 16 → 544px), inside any column.
     Not ch — Inter's zero is wide, so 68ch measures about 95 a line. */
  --measure-text:    34em;

  /* ─── Page anatomy ───
     The content column's own top and bottom space: from the header row to
     the first block (crumbs or page header), and from the last block to the
     footer. Section-lg, so 48, then 32 at ≤ 640px. */
  --page-block-start: var(--space-section-lg);
  --page-block-end:   var(--space-section-lg);
  --aside-width-sm:  calc(3 * var(--grid-col-max) + 2 * var(--grid-gutter-md));  /* 282 · 3 columns · step nav, filters */
  --aside-width-md:  calc(4 * var(--grid-col-max) + 3 * var(--grid-gutter-md));  /* 384 · 4 columns · summary, totals */

  /* ─── Overlay widths ─── (tokens 2026-10-30; the values are unchanged)
     Modal sizes (rev. 2026-10-26 modal audit) and drawer sizes. */
  --modal-width-sm: 400px;
  --modal-width-md: 480px;   /* default */
  --modal-width-lg: 640px;
  --modal-width-xl: 920px;
  --drawer-width-sm: 400px;
  --drawer-width-md: 560px;  /* default */
  --drawer-width-lg: 720px;
  --drawer-width-xl: 1120px; /* live-preview drawer · form + preview side by side from --bp-laptop */
  --drawer-form-width: 440px; /* the form column inside the xl drawer */

  /* ─── Hanging indent ───
     Where a row leads with an icon, its sub-lines and wrapped lines start at
     the text edge, not under the icon: icon + its gap. */
  --indent-icon-inline:  calc(var(--icon-inline) + var(--space-2));   /* 24 · 16 icon + 8 */
  --indent-icon-control: calc(var(--icon-control) + var(--space-3));  /* 32 · 20 icon + 12 */

  /* ─── Nav row heights & indentation ───
     The chrome navigation contract: section headers and flat items share
     one row height; nested children sit one notch tighter. The child
     indent (44px) comes from sidebar-padding (16) + lead-icon-track (18)
     + gap (10) — locking it as a token means a future redesign of any
     of those three values can be reflected here in one place. */
  --nav-row-h:          36px;   /* .nav-section-h, .nav-flat, .nav-item */
  --nav-row-h-child:    32px;   /* .nav-child */
  --nav-child-indent:   44px;   /* left padding for .nav-child — visually aligns child label with parent label */

  /* ─── Icon sizes ───
     The scale (icon audit 2026-10-29 added 2xs 14, the size the status pill,
     breadcrumbs, xs buttons and field messages already used). Components pick
     a role below by the job; the steps define the roles. React `size` props
     use ICON_PX from lib/icons.ts, which mirrors these. */
  --icon-2xs: 14px;
  --icon-xs: 16px;
  --icon-sm: 20px;
  --icon-md: 24px;
  --icon-lg: 32px;
  --icon-xl: 48px;

  /* ─── Icon roles ─── */
  --icon-meta:    var(--icon-2xs); /* beside label / data / meta text (11–13): status pill, crumbs, btn-xs, field message */
  --icon-inline:  var(--icon-xs);  /* beside body / body-sm text; sm and default buttons, inputs, menu rows, nav rows */
  --icon-control: var(--icon-sm);  /* alone in a control: icon button md, input adornment, search field, nav list */
  --icon-feature: var(--icon-md);  /* standalone glyph in a tile or disc: file tile, callout, drop zone, tab bar */
  --icon-display: var(--icon-lg);  /* a section's lead glyph: help topic, notification row */
  --icon-hero:    var(--icon-xl);  /* empty states, topic cards */

  /* ─── Brand logo sizes ───
     Visible height of the tight-cropped wordmark or symbol
     (assets/logos/wordmark-* · symbol-*, no built-in whitespace). Shared by
     both kinds; never below --brand-logo-min. Own tokens so an icon or avatar
     change never resizes the brand (brand-logo rebuild 2026-10-26). */
  --brand-logo-min: 16px;  /* legibility floor */
  --brand-logo-sm:  20px;  /* mobile bars · onboarding mobile · public fee page */
  --brand-logo-md:  24px;  /* default chrome: sidebar, topnav, drawers, footer symbol */
  --brand-logo-lg:  32px;  /* onboarding / auth desktop · patient sidebar */
  --brand-logo-xl:  48px;  /* hero and marketing placements */

  /* ─── Chrome icon-button size ───
     Single canonical size for chrome utility buttons (help, bell, gear).
     36px reads as confident-but-not-loud at brand-anchored chrome density
     (--bar-height-lg) and remains a comfortable 8px+ tap target outside
     touch contexts. */
  --size-icon-btn: 36px;

  /* ─── Avatar sizes ───
     Used by .avatar--sm/md/lg/xl. The tokens lock the contract so the
     avatar component, sidebar footer, topnav user identity, and any
     future avatar usage all draw from the same scale. */
  --avatar-sm: 24px;   /* compact lists, dense rows */
  --avatar-md: 32px;   /* topnav inline user identity, default */
  --avatar-lg: 40px;   /* sidebar footer, profile chrome */
  --avatar-xl: 56px;   /* profile pages, settings panels */

  /* ─── Workspace switcher ───
     The header pill's width is content-driven between these: never narrower
     than --ws-switch-min, grows with the clinic name, truncates past
     --ws-switch-max (its menu shares the max). Added 2026-10-23 ·
     workspace-switcher-header; max raised 320 → 400 with the pill. */
  --ws-switch-min: 240px;
  --ws-switch-max: 400px;
}

/* Page grid steps · the margin narrows below the laptop floor and again at
   the mobile edge; the gutter tightens once, at the mobile edge. */
@media (max-width: 1279px) {
  :root { --grid-margin: var(--content-margin-sm); }
}
@media (max-width: 640px) {
  :root {
    --grid-margin: var(--content-margin-xs);
    --grid-gutter: var(--space-4);
  }
}

/* ═══ design-system/tokens/elevation.css ═══ */
/* Docnote Design System — Elevation
   Shadow tokens · ring tokens · z-index layer scale.
   v2.0 · 2026

   Cards carry a soft shadow. The card family (.card · .summary-list ·
   .file-card) uses --shadow-soft — a quiet two-step lift paired with the
   --color-card-border (bh-500) boundary — as its resting elevation
   (2026-08-04 · ilum; this supersedes the prior "cards are flat — no
   shadow" rule). --shadow-elevated remains the stronger lift for overlay
   surfaces (modals / dropdowns / popovers) AND for the tappable-card link
   affordance (.card--tappable); --shadow-accent* covers the primary CTA
   exception (a matched-colour glow tied to the brand fill, 2026-05-20).
   Semantic status/tinted cards (.status-card · .callout) stay flat —
   their accent bar / tint carries separation, not a shadow.
   .card--flat opts a card back out to no shadow. */

:root {

  /* ─── Shadow · base ─── */
  --shadow-elevated: 0 8px 32px rgba(5,30,58,0.18);

  /* ─── Shadow · soft (card family resting lift) ───
     A quiet two-step Blue-Hour (rgba 8,44,84) shadow — the resting
     elevation for .card / .summary-list / .file-card, paired with the
     --color-card-border boundary. Added 2026-08-04 (ilum). Dark override
     in colors.css. */
  --shadow-soft: 0 1px 2px rgba(8,44,84,0.06), 0 2px 8px rgba(8,44,84,0.08);

  /* ─── Shadow · accent CTA (matched-colour glow, .btn-accent only) ───
     Tinted Daybreak shadow that visually anchors the primary CTA. Tied
     to the same db-400 hue (#1B99E8 → rgb(27,153,232)) as
     --color-interactive-accent; alpha is precomputed (not color-mix())
     to match the precomputation pattern used by --status-critical-hover
     / -active (the 2026-05-08 color-mix() retirement sweep). When
     --color-interactive-accent changes hue in the future, these tokens
     MUST be re-derived by hand. Document the new alpha trio in the
     decision log of components-buttons.html when that happens. */
  --shadow-accent:         0 8px 24px -4px rgba(27,153,232,0.40);  /* default · soft anchor */
  --shadow-accent-hover:   0 12px 32px -4px rgba(27,153,232,0.50); /* hover · lifted + intensified */
  --shadow-accent-pressed: 0 2px 8px -2px rgba(27,153,232,0.35);   /* :active · compressed · button "depressed" */

  /* ─── Shadow · raised + sticky ─── (canonical 2026-10-24 · approved by Justin) */
  --shadow-raised: 0 1px 2px rgba(8,44,84,0.06);    /* a control lifted from its track: segmented thumb, active option (= --shadow-soft's first layer) */
  --shadow-sticky: 0 -2px 12px rgba(8,44,84,0.08);  /* upward shadow under a bar stuck to the bottom: docks, mobile action footers */

  /* ─── Rings ─── (canonical 2026-10-24 · approved by Justin)
     Focus is always the outline. Every other ring is a box-shadow named by
     meaning, never by look: halo / error = "you are typing here", strong =
     the search field and workspace switcher's sanctioned focus edge,
     selected = "this option is chosen", current = "you are on this step". */
  --ring-focus:        3px solid var(--color-focus-ring);   /* outline: var(--ring-focus); with --ring-focus-offset · :focus-visible only */
  --ring-focus-offset: 2px;
  --ring-halo:     0 0 0 3px var(--color-da-16);             /* focus halo on an input: OTP cell, auth field, AI prompt, price input */
  --ring-error:    0 0 0 3px rgba(234,88,12,0.14);           /* validation orange (--color-validation-warning) at 14% · an invalid input while focused */
  --ring-strong:   0 0 0 1.5px var(--color-text-heading);    /* dark Blue Hour edge · search field and workspace switcher focus / open */
  --ring-selected: inset 0 0 0 1px var(--color-db-400);      /* a chosen option: radio card, type pill, today / current in the date picker */
  --ring-current:  0 0 0 4px var(--color-da-12);             /* the current step: stepper circles, active payment card, consent gate */

  /* ─── Z-Index · layer scale ─── (rebuilt 2026-10-24 · approved by Justin)
     One token per real layer, in the order things stack. Pick the token for
     the job, never a number. Modals sit above drawers (a drawer can open the
     discard-guard modal); anything portalled to <body> that floats (menu,
     date picker, tooltip) uses --z-popover so it shows above an open drawer
     or modal; toasts sit above every overlay. For a second local level use
     calc(var(--z-raised) + 1). */
  --z-base:            0;
  --z-raised:          1;    /* a local lift inside one component: active tab, sticky first column, stepper circle */
  --z-sticky:        100;    /* in-page sticky bars: docks, sticky period navigator, action footer */
  --z-chrome:        150;    /* app chrome: topnav, app bar, tab bar, sticky onboarding header · above in-page sticky bars */
  --z-dropdown:      200;    /* menus and popups in the page flow */
  --z-drawer-scrim:  300;    /* drawer and sheet scrims */
  --z-drawer:        310;    /* drawer, bottom sheet, nav drawer, notification drawer */
  --z-modal-scrim:   400;    /* modal scrim */
  --z-modal:         410;    /* modal, document preview */
  --z-popover:       500;    /* anything portalled that floats: menus, date pickers, tooltips · the focused skip link */
  --z-toast:         600;    /* toasts and snackbars · always visible */
  --z-devtools:      900;    /* demo-only tools: state tweaks, mobile quick view */
  --z-modal-overlay: var(--z-drawer-scrim);  /* DEPRECATED 2026-10-24 · its consumers (bottom sheet, nav drawer scrims) are drawer-layer; migrate, removed next cycle */
  /* Overlays migrated 2026-10-24: .drawer-scrim / .drawer use --z-drawer-scrim /
     --z-drawer and .modal-scrim / .modal / .doc-preview use --z-modal-scrim /
     --z-modal, so toasts (600) show above an open drawer or modal. Remaining
     raw layers are listed in preview/shadow.html § 1 table F. */
}

/* ═══ design-system/tokens/motion.css ═══ */
/* Docnote Design System — Motion tokens
   Added 2026-10-21 (data-visualisation motion) · rev. 2026-10-26: the motion
   foundation (CLAUDE.md → Motion · preview/foundations-motion.html).

   Motion is purposeful and restrained: it explains a change (something
   arrived, opened, moved, is loading or updated) and nothing else. Every
   motion uses these tokens and belongs to one of seven themes — Feedback ·
   Surfaces · Disclosure · Selection · Flow · Data · Brand. Stillness stays
   the default; nothing moves to decorate.

   Budget: nothing in product UI runs longer than --motion-duration-slow
   (360ms) except Data draw-in (--motion-duration-chart) and Brand; one
   region moves at a time; only Feedback and Brand loop.

   Reduced motion: the spatial tokens (distance, scale) collapse to none and
   durations cap at --motion-duration-fast, so a component written on these
   tokens keeps a short opacity fade and loses all movement with no extra
   CSS. Loops (spin, pulse, Brand) and JS motion (count-up, smooth scroll)
   still check the media query themselves and stop. */

:root {
  /* Duration — pick by the size of the change, not by taste. */
  --motion-duration-instant: 0ms;     /* a state that must not animate */
  --motion-duration-fast:    120ms;   /* colour, hover and press, small glyphs, a toast, an exit */
  --motion-duration-base:    240ms;   /* a surface or step arriving, a thumb or tab slide */
  --motion-duration-slow:    360ms;   /* a region opening (reveal), the longest product motion */
  --motion-duration-chart:   600ms;   /* Data theme only: chart draw-in, count-up */

  /* Loops — Feedback only. */
  --motion-duration-spin:    900ms;   /* one spinner turn */
  --motion-duration-pulse:   1600ms;  /* one skeleton pulse */

  /* Easing — pick by direction. */
  --motion-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);  /* moving between two states on screen */
  --motion-ease-enter:    cubic-bezier(0.2, 0.8, 0.2, 1);  /* arriving: fast start, soft landing */
  --motion-ease-exit:     cubic-bezier(0.4, 0, 1, 1);    /* leaving: gentle start, quick finish */
  --motion-ease-linear:   linear;                        /* loops and progress */

  /* Sequence — Data and grids only. */
  --motion-stagger:     80ms;   /* between items */
  --motion-stagger-max: 640ms;  /* the last item never waits longer than this */

  /* Space — how far things travel. Collapsed under reduced motion. */
  --motion-distance-xs:  4px;   /* a menu dropping from its trigger (added 2026-10-26) */
  --motion-distance-sm:  8px;   /* a step or tile rising in */
  --motion-distance-md:  16px;  /* a toast or drawer-edge nudge */
  --motion-scale-enter:  0.98;  /* a modal panel arriving */
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-duration-base:  var(--motion-duration-fast);
    --motion-duration-slow:  var(--motion-duration-fast);
    --motion-duration-chart: 0ms;
    --motion-stagger:        0ms;
    --motion-distance-xs:    0px;
    --motion-distance-sm:    0px;
    --motion-distance-md:    0px;
    --motion-scale-enter:    1;
  }
}

/* ═══ design-system/tokens/base.css ═══ */
/* Docnote Design System — Base global styles
   Document defaults that consume the token layer.
   These are NOT tokens (they are styled rules), but they ship with the
   token bundle so any consumer that imports tokens/index.css gets the
   intended baseline.
   v2.0 · 2026 */

/* ─── Global box-sizing reset ───
   Every element computes its width to include padding and border by
   default. Closes the entire class of "width: 100% + padding overflows
   parent" bugs without having to declare box-sizing on every component.
   Standard practice in modern CSS systems; harmless for inline elements.
   Added 2026-05-06 after audit found four affected components. */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  font-family: var(--font-inter);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: var(--color-text-body);
  background: var(--color-surface-page);
}

::selection { background: var(--color-selection-bg); color: var(--color-selection-text); }
::placeholder { color: var(--color-placeholder); opacity: 1; }

/* Tabular-numeral utility · mandatory on vitals, doses, IDs, times, lab values */
.data-numerals {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* Monospace data utility · for canonical-record IDs where character-level
   column alignment carries scanability (health-card numbers, payer reference
   codes, MRN, masked card digits, OTP echo). Composes with .data-numerals
   when the string is purely numeric; on its own when the string mixes
   letters + digits + separators (e.g. PO-2026-0431). Promoted to canonical
   2026-05-14 after .src__mono surfaced as a preview-local one-off in
   service-request-create and the same shape was requested for summary-list
   ID rows. Tabular-nums baked in so digit columns line up even when the
   mono fallback chain falls back to a proportional system font. */
.data-mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* ─── Visually hidden, accessible to assistive tech · .sr-only ───
   Industry-standard "visually hidden but still announced" utility. Use
   when meaning is carried visually by an icon, shape, or position but
   needs to be spoken to a screen-reader user. Common consumers:
     · stepper state copy ("Completed", "Current step, N of M",
       "Processing", "Error on this step")
     · the textual label paired with an icon-only button
     · descriptive context for an ARIA element where the visible text
       would visually pollute the surface

   NOT a chrome-display class — never use this to "hide" content you'd
   like to keep around for later. The CSS removes the element from the
   visual flow AND from layout calculations, so it cannot be revealed
   without overriding every property below.

   Pattern details:
     · position: absolute + 1×1 px box — removes from flow without
       triggering display:none (which hides from AT too)
     · clip-path: inset(50%) — modern alternative to legacy `clip: rect(...)`;
       both are kept for older AT compatibility
     · white-space: nowrap — prevents the 1 px box from wrapping content
       across lines (some old screen readers re-read wrapped text)
     · border: 0 + padding: 0 — strips any inherited chrome
     · margin: -1 px — pulls the box into the parent so it doesn't shift
       layout when focused (paired with .sr-only-focusable variant, future)

   Promoted to canonical 2026-05-11 after the stepper migration
   surfaced 23 silently-broken consumers across 3 files. ──────────── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
