/* ============================================================
   TheDentist.ai — Design System v3 (2026-06-25)
   Canonical file: cloudflare-pages/design-system/tokens-v2.css
   Source: Claude Design project (index.html, theme.css)
   Editorial: calm, positive, outcome-focused. "Outcomes, not opinions."
   ============================================================ */

/* ── Fonts ────────────────────────────────────────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,900;1,400&family=DM+Sans:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&display=swap');

/* ── Tokens (light theme canonical, dark theme explicit opt-in) ─── */
:root {
  color-scheme: light;

  /* LIGHT THEME (canonical, classic-skin default). W2/F-2026-09-23-G:
     light is the prescribed default platform-wide (founder ruling); dark
     is an explicit user opt-in via html[data-theme="dark"] below — no OS
     prefers-color-scheme auto-dark. This is the same full token set that
     used to be the dark-canonical :root, with the tokens that used to
     live under html[data-theme="light"] swapped in as the default value;
     every token name and every invariant (non-flipping) value — spacing,
     type scale, EDU-303 exports, print tokens, etc. — is unchanged. */
  --td-ink:     #F5F2EB;
  --td-ink-0:   #F0EDE5;
  --td-paper:   #0F1820;
  /* DES-008 (F2 = A, F-2026-09-18-I): was #FAF8F3 — the SECOND cream that made
     chairside.css's --td-paper-2 ground (its .cs-card/.cs-tabbar surfaces,
     which fall back to --td-paper when unset) read as a hair darker than
     --td-paper itself. One cream platform-wide: same value as --td-paper.
     The two-tone "card on shell" depth chairside relied on this pair for is
     gone by design — see design_one_cream + its bite suite. */
  --td-paper-2: #0A0A12;
  --td-bone:    #FFFFFF;
  --td-bone-2:  #F5F2EB;
  --td-bone-3:  #EDE9DE;

  --td-seal:    #9E7426;
  --td-seal-2:  #B8862A;
  --td-sage:    #3A6B5A;
  --td-sage-2:  #4D8C74;
  --td-blush:   #C4615A;

  --td-ink-1:   #0F1820;
  --td-ink-2:   #3D4A55;
  --td-ink-3:   #6E7B85;
  --td-ink-4:   #A0ABB5;
  --td-ink-5:   rgba(10, 10, 18, 0.10);
  --td-ink-6:   rgba(10, 10, 18, 0.04);

  /* Semantic colors (mapped to new palette) */
  --td-primary:        var(--td-seal);
  --td-primary-dark:   #A67A2C;
  --td-primary-light:  var(--td-seal-tint, rgba(200, 151, 58, 0.14));
  --td-primary-mid:    var(--td-seal-2);

  --td-success:        var(--td-sage);
  --td-success-light:  var(--td-sage-tint, rgba(58, 107, 90, 0.15));
  --td-danger:         var(--td-blush);
  --td-danger-light:   var(--td-alert-tint, rgba(196, 97, 90, 0.18));
  --td-warning:        var(--td-seal-2);

  /* ── EMERGENCY EXIT CHIP ("Is this an emergency?") ─────────────────────
     DES-005 (F-2026-09-18-G), DOCS/DESIGN_AUDIT_2026-09-18.md row P6.

     WHAT WAS WRONG, MEASURED IN A REAL BROWSER (CLAUDE.md rule 16 — a
     declaration's presence says nothing about what it RESOLVES to). The chip
     painted `color: var(--alert)` on `background: var(--alert-tint, …)`, and
     --alert-tint is TRANSLUCENT (dark rgba(196,97,90,.18), light
     rgba(162,78,71,.12)). A translucent fill has no contrast of its own — it
     inherits whatever is behind it — so the SAME two declarations measured a
     DIFFERENT ratio on every host the chip is mounted over:

       dark  over --bone    #0F1820  →  fill rgb(48,37,42)  → 3.65:1  (#directory)
       dark  over --ink     #0A0A12  →  fill rgb(43,26,31)  → 4.10:1  (#triage, cost/)
       dark  over --bone-2  #0F1E28  →  fill rgb(48,42,49)  → 3.47:1  (worse still)
       light over --bone    #FFFFFF  →  fill rgb(244,234,233) → 4.78:1
       light over --ink     #FAF8F3  →  fill rgb(239,228,222) → 4.52:1

     Three of those are below the 4.5:1 AA floor for normal text, and the two
     that clear it do so by 0.02–0.28 — i.e. the next surface this chip is
     mounted on re-breaks it silently. This is the JM-008 failure class
     (`--alert: var(--bone)` drew the emergency exit at 1:1 with every gate
     green): the colours were "right" in source and wrong on screen.

     THE FIX IS STRUCTURAL, NOT A NUDGE. --td-emergency-bg is OPAQUE, so the
     chip's contrast is a property of the chip and not of whatever it happens
     to be sitting on. A frightened person reading this control under stress
     gets the same legibility on every surface, present and future.

     Verified via gates/lib/contrast.mjs and asserted by computation in
     gates/tests/emergency-chip-contrast.test.mjs on three real surfaces ×
     two viewports × both themes:
       dark   fg #EFA39C on bg #2E2024            = 7.71:1  (text, AA 4.5:1)
              border #C4615A on its own fill      = 3.86:1  (AA 3:1 non-text)
              border #C4615A on every dark host   = 3.94–4.89:1
       light  fg #8F3129 on bg #FDEFEC            = 7.10:1
              border #A24E47 on its own fill      = 5.04:1
              border #A24E47 on every light host  = 4.66–5.65:1

     --td-emergency-bg is chosen as exactly what the old tint composited to
     over --bone (the commonest host), so the chip looks as it did — this
     ticket changes what it MEASURES, not where it sits, what it says, or the
     emergency-guard logic behind it.

     The -strong pair is the hover/focus-visible state, and it is
     THEME-INVARIANT for the same reason teaser/theme.css's --alert-solid is:
     an always-solid safety-path surface should not change meaning when a
     visitor toggles theme. It also fixes a second, unaudited failure on the
     same control — the old hover painted --paper on --alert, which measures
     3.61:1 in dark and 3.17:1 in light, so the chip became LESS readable the
     moment a keyboard user focused it. #FFFFFF on #9E3830 = 6.86:1.
     The border is deliberately NOT restyled on hover: at #9E3830 it would
     measure 2.61:1 against a dark host, losing the component boundary that
     WCAG 1.4.11 asks for. Consumers: Shared.jsx's EmergencyExitLink (SPA)
     and static-shell.css's .emergency-exit-link (static pages). */
  --td-emergency-fg:     #8F3129;
  --td-emergency-bg:     #FDEFEC;
  --td-emergency-border: #A24E47;
  --td-emergency-fg-strong: #FFFFFF;
  --td-emergency-bg-strong: #9E3830;

  --td-teal:           var(--td-sage-2);
  --td-teal-dark:      var(--td-sage);
  --td-teal-light:     var(--td-sage-tint, rgba(58, 107, 90, 0.15));

  --td-amber:          var(--td-seal);
  --td-amber-light:    var(--td-seal-tint, rgba(200, 151, 58, 0.14));
  --td-warning-light:  #FEF3C7;

  /* Neutral scale — dark-primary (Design System Dark; light override below) */
  --td-white:          #FFFFFF;
  --td-bg:             #F8F9FC;
  --td-surface:        #FFFFFF;
  --td-border:         #E8EDF5;
  --td-border-strong:  #C8D2E4;

  --td-text-primary:   #0A1628;
  --td-text-secondary: #475569;
  --td-text-muted:     #94A3B8;
  --td-text-inverse:   #FFFFFF;

  /* EDU-303: theme-INVARIANT primary ink/bg exports.
     --td-bg / --td-text-primary above flip value under [data-theme="light"]
     (this file's own convention: unattributed = dark canonical, the
     attribute opts INTO light). The education vertical's own screens
     (Academy shell, Instructor Console, education-review) default to LIGHT
     with no attribute and opt INTO dark via [data-theme="dark"] — the
     opposite attribute convention — so they cannot simply inherit
     --td-bg/--td-text-primary and land on the right value for their own
     default state. These four names hold the SAME hex already declared
     above (dark) and in the html[data-theme="light"] block below (light),
     just addressable without depending on which attribute is set, so a
     page with its own light/dark switch can pick the right one explicitly
     per state instead of hand-rolling its own hex. Keep in sync with the
     light block below if either changes. */
  --td-bg-dark:            var(--td-ink);   /* #0A0A12, same value as --td-bg above */
  --td-text-primary-dark:  var(--td-ink-1); /* #F5F2EB, same value as --td-text-primary above */
  --td-bg-light:           #F8F9FC;         /* == --td-bg inside html[data-theme="light"] below */
  --td-text-primary-light: #0A1628;         /* == --td-text-primary inside html[data-theme="light"] below */

  /* Dark tokens (system prefers-color-scheme: dark — matches primary theme) */
  --td-dark-bg:        var(--td-ink);
  --td-dark-surface:   var(--td-bone);
  --td-dark-border:    var(--td-ink-5);
  --td-dark-text:      var(--td-ink-1);
  --td-dark-text2:     var(--td-ink-3);

  /* Tint variants for dark theme */
  --td-seal-tint: rgba(158, 116, 38, 0.10);
  --td-sage-tint:      rgba(58, 107, 90, 0.15);
  --td-alert-tint:     rgba(196, 97, 90, 0.18);

  /* Typography — DM Sans for UI; Playfair Display for headings; DM Mono for code */
  /* ===== PRINT SURFACE — deliberately theme-INDEPENDENT =====
     A print card is paper. It must be white with black ink whether the operator
     had the app in light or dark mode, so these are the one set of values that
     must NOT be redefined under [data-theme] or [data-skin] — a themed token here
     would print a black page and empty a toner cartridge.
     They live in the design system, rather than as literals at the call site,
     because `suite_token_source` (a1) forbids palette hex inside the suite and it
     is right to: a literal there is indistinguishable from a forked palette, and
     the reviewer cannot tell "this is print, it is meant to be absolute" from
     "someone hardcoded a grey". Naming them says which one it is. */
  --td-print-paper:  #FFFFFF;
  --td-print-ink:    #111111;
  --td-print-ink-2:  #333333;
  --td-print-ink-3:  #555555;
  --td-print-ink-4:  #777777;

  --td-font:           'DM Sans', -apple-system, system-ui, sans-serif;
  --td-font-display:   'Playfair Display', Georgia, serif;
  --td-font-mono:      'DM Mono', 'JetBrains Mono', 'Fira Code', monospace;

  /* Type scale */
  --td-text-xs:   11px;
  --td-text-sm:   13px;
  --td-text-base: 15px;
  --td-text-md:   17px;
  --td-text-lg:   20px;
  --td-text-xl:   24px;
  --td-text-2xl:  32px;
  --td-text-3xl:  40px;
  --td-text-4xl:  52px;

  /* Radii (updated to match design-system) */
  --td-radius-sm:  4px;
  --td-radius-md:  8px;
  --td-radius-lg:  12px;
  --td-radius-xl:  16px;
  --td-radius-pill: 999px;

  /* Shadows (updated for dark theme primary) */
  --td-shadow-soft:  0 1px 2px rgba(15,24,32,0.06), 0 4px 16px rgba(15,24,32,0.06);
  --td-shadow-lift:  0 8px 24px rgba(15,24,32,0.10), 0 32px 64px rgba(15,24,32,0.10);
  --td-shadow-glow:  0 0 0 1px rgba(158,116,38,0.25), 0 8px 32px rgba(158,116,38,0.14);
  --td-shadow-focus: 0 0 0 2px var(--td-ink), 0 0 0 4px var(--td-seal);

  /* Backward-compatible aliases */
  --td-shadow-sm:   var(--td-shadow-soft);
  --td-shadow-md:   var(--td-shadow-soft);
  --td-shadow-lg:   var(--td-shadow-lift);
  --td-shadow-xl:   var(--td-shadow-lift);
  --td-shadow-blue: 0 4px 16px rgba(200,151,58,.20);

  /* Spacing (4px grid) */
  --td-sp-1:   4px;
  --td-sp-2:   8px;
  --td-sp-3:   12px;
  --td-sp-4:   16px;
  --td-sp-5:   20px;
  --td-sp-6:   24px;
  --td-sp-8:   32px;
  --td-sp-10:  40px;
  --td-sp-12:  48px;
  --td-sp-16:  64px;
  --td-sp-20:  80px;
  --td-sp-24:  96px;

  /* Layout */
  --td-max-w:       1200px;
  --td-max-w-narrow: 780px;

  /* Transitions (updated to match design-system) */
  --td-ease:       cubic-bezier(0.2, 0.8, 0.2, 1);
  --td-ease-out:   cubic-bezier(0.2, 0.8, 0.2, 1);
  --td-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --td-dur:        240ms;
  --td-t-fast:     120ms;
  --td-t-base:     240ms;
  --td-t-slow:     600ms;

  /* Visual effects */
  --td-grain-opacity: 0.018;
  --td-hero-grad-1: rgba(58,107,90,0.18);
  --td-hero-grad-2: rgba(200,151,58,0.10);

  /* DES-002/013 (F-2026-09-18, DOCS/DESIGN_AUDIT_2026-09-18.md §4 item 4):
     minimum tap-box floors for interactive controls, so a fix never hardcodes
     44/40 inline — it references one of these two tokens. Two tiers because
     the audit's own findings are two tiers: patient-facing routes are held to
     the WCAG 2.5.5/2.5.8-aligned 44px floor (DES-002); ops/console pages
     (hitl-rounds, the design gallery, the Strategy Command Center) are held
     to the lighter 40px floor already established for suite chrome (DES-013).
     Never hand-typed — SCRIPTS/tap_target_census.mjs and
     gates/tests/tap-target-census.test.mjs both assert against the COMPUTED
     box, never against these token strings (rule 16). */
  --td-tap-min:     44px;  /* patient-facing floor */
  --td-tap-min-ops: 40px;  /* ops/console floor */
}

html[data-theme="dark"] {
  color-scheme: dark;

  /* DARK THEME (explicit opt-in, classic-skin). Redeclares only the
     tokens that differ from the light :root above — same set, same
     values, that used to be the canonical dark declarations before this
     restructure (F-2026-09-23-G-B/-G-C: light is now the default). */
  --td-ink:     #0A0A12;
  --td-ink-0:   #060810;
  --td-paper:   #F5F2EB;
  --td-paper-2: #F5F2EB;
  --td-bone:    #0F1820;
  --td-bone-2:  #0F1E28;
  --td-bone-3:  #13242F;
  --td-seal:    #C8973A;
  --td-seal-2:  #E8B84B;
  --td-ink-1:   #F5F2EB;
  --td-ink-2:   #B7C3CC;
  --td-ink-3:   #8FA3B1;
  --td-ink-4:   #5A6B78;
  --td-ink-5:   rgba(245, 242, 235, 0.10);
  --td-ink-6:   rgba(245, 242, 235, 0.05);
  --td-emergency-fg:        #EFA39C;
  --td-emergency-bg:        #2E2024;
  --td-emergency-border:    #C4615A;
  --td-bg:             var(--td-ink);
  --td-surface:        var(--td-bone);
  --td-border:         var(--td-ink-5);
  --td-border-strong:  color-mix(in srgb, var(--td-ink-3) 45%, transparent);
  --td-text-primary:   var(--td-ink-1);
  --td-text-secondary: var(--td-ink-2);
  --td-text-muted:     var(--td-ink-3);
  --td-text-inverse:   var(--td-ink);
  --td-seal-tint:      rgba(200, 151, 58, 0.14);
  --td-shadow-soft:  0 1px 2px rgba(0,0,0,0.35), 0 4px 16px rgba(0,0,0,0.30);
  --td-shadow-lift:  0 8px 24px rgba(0,0,0,0.40), 0 32px 64px rgba(0,0,0,0.45);
  --td-shadow-glow:  0 0 0 1px rgba(200,151,58,0.22), 0 8px 32px rgba(200,151,58,0.10);
}


/* ===========================================================================
   STRATA SKIN  —  opt-in, scoped, additive
   ===========================================================================
   Founder ruling F-2026-09-03-H: "go with the strata theme from the strata
   artefact." Strata is the platform's ratified visual language
   (DOCS/DESIGN_TEMPLATE_STRATA/TEMPLATE.md v1.7.0, ratified F-2026-08-31-J)
   and these are ITS values, copied verbatim from the generated Strata surface
   `command/architecture/index.html` -- not a reinterpretation.

   WHY A SCOPED SKIN RATHER THAN A REWRITE OF THE LIGHT THEME ABOVE.
   The light theme above is the WHOLE PLATFORM's -- landing, CFO, Counsel,
   Cockpit, every console. Rewriting it would restyle all of them in one
   unreviewed step, and the standing scope ruling (F-2026-09-01-O) is
   "Practice Suite only, for now". So Strata is an opt-in skin: a surface
   takes it by setting `data-skin="strata"` on <html>, and every surface that
   does not is byte-for-byte unaffected. Nothing above this comment changes.

   WHAT WAS ACTUALLY WRONG, MEASURED RATHER THAN ASSERTED.
   The design system's own identity is warm -- `--td-paper #F5F2EB`,
   `--td-ink #0A0A12`, `--td-seal #C8973A` -- and the light theme above
   overrides that semantic layer with a COOL SLATE set: `--td-bg #F8F9FC`,
   `--td-border #E8EDF5`, `--td-text-primary #0A1628`, `--td-text-secondary
   #475569`. Read in a browser on the Suite, that is what "the suite still
   uses the old styling" resolves to: warm brand, cool blue-grey chrome. The
   hues below put the chrome back on the brand.

   THE ROLE INVERSION IS DELIBERATE AND MATCHES THE LIGHT BLOCK ABOVE.
   In a light context `--td-ink` is the GROUND and `--td-ink-1` is the text --
   the light theme above already inverts them that way, and consumers (the
   Suite aliases `--bg: var(--td-ink)`) depend on it. Breaking that convention
   here would invert every consuming surface, so it is kept exactly.
   =========================================================================== */
/* Strata is now the DEFAULT skin (F-2026-09-23-G-C): applies to every
   surface that does not opt into data-skin="classic". data-skin="strata"
   is kept as an explicit, redundant alias for pages that set it by name. */
html:not([data-skin="classic"]):not([data-theme="dark"]),
html[data-skin="strata"][data-theme="light"] {
  color-scheme: light;

  /* Ground and surfaces — warm paper, white cards, hairline separations. */
  --td-ink:     #F5F2EB;
  --td-ink-0:   #FAF8F3;
  --td-paper:   #14181F;
  --td-paper-2: #0A0A12;
  --td-bone:    #FFFFFF;
  --td-bone-2:  #F0EBE0;
  --td-bone-3:  #EDE7DA;

  /* Text ramp — neutral, not navy. */
  --td-ink-1:   #14181F;
  --td-ink-2:   #4A5560;
  --td-ink-3:   #616D79;
  --td-ink-4:   #8A939C;
  --td-ink-5:   rgba(10, 10, 18, 0.13);
  --td-ink-6:   rgba(10, 10, 18, 0.06);

  /* Seal gold is Strata's single accent. The light-theme value is the darker
     step (#8B6723) because the accent is used as TEXT and as a button fill on
     white here; #C8973A is the identity hue but does not clear AA as 12-13px
     text on paper. Same trade the Strata surface itself makes. */
  --td-seal:      #8B6723;
  --td-seal-2:    #A87C2A;
  --td-seal-tint: rgba(200, 151, 58, 0.16);

  --td-bg:             #F5F2EB;
  --td-surface:        #FFFFFF;
  --td-border:         rgba(10, 10, 18, 0.13);
  --td-border-strong:  rgba(10, 10, 18, 0.28);
  --td-text-primary:   #14181F;
  --td-text-secondary: #4A5560;
  --td-text-muted:     #616D79;
  --td-text-inverse:   #F5F2EB;

  --td-shadow-soft:  0 1px 2px rgba(10,10,18,0.05), 0 12px 32px -12px rgba(10,10,18,0.16);
  --td-shadow-lift:  0 2px 6px rgba(10,10,18,0.06), 0 28px 64px -20px rgba(10,10,18,0.22);
  --td-shadow-glow:  0 0 0 1px rgba(200,151,58,0.28), 0 8px 32px rgba(200,151,58,0.16);
}

html:not([data-skin="classic"])[data-theme="dark"],
html[data-skin="strata"][data-theme="dark"] {
  color-scheme: dark;

  --td-ink:     #0A0A12;
  --td-ink-0:   #0C0F17;
  --td-paper:   #F5F2EB;
  --td-paper-2: #FAF8F3;
  --td-bone:    #111A22;
  --td-bone-2:  #16232D;
  --td-bone-3:  #1B2A35;

  --td-ink-1:   #F2EFE8;
  --td-ink-2:   #B7C3CC;
  --td-ink-3:   #93A6B3;
  --td-ink-4:   #748694;
  --td-ink-5:   rgba(245, 242, 235, 0.12);
  --td-ink-6:   rgba(245, 242, 235, 0.06);

  --td-seal:      #E8B84B;
  --td-seal-2:    #E8B84B;
  --td-seal-tint: rgba(232, 184, 75, 0.15);

  --td-bg:             #0A0A12;
  --td-surface:        #111A22;
  --td-border:         rgba(245, 242, 235, 0.12);
  --td-border-strong:  rgba(245, 242, 235, 0.26);
  --td-text-primary:   #F2EFE8;
  --td-text-secondary: #B7C3CC;
  --td-text-muted:     #93A6B3;
  --td-text-inverse:   #0A0A12;

  --td-shadow-soft:  0 1px 2px rgba(0,0,0,0.4), 0 16px 40px -14px rgba(0,0,0,0.6);
  --td-shadow-lift:  0 2px 6px rgba(0,0,0,0.5), 0 32px 72px -20px rgba(0,0,0,0.7);
  --td-shadow-glow:  0 0 0 1px rgba(232,184,75,0.30), 0 8px 32px rgba(232,184,75,0.18);
}

/* (removed) light is canonical at root — no color-scheme assertion needed
   here; html[data-theme="dark"] above and the strata dark block below
   cover the explicit-dark case. See F-2026-09-23-G-B/-G-C. */

/* Nested components — light is canonical, dark is the explicit override */
body {
  background: var(--td-ink);
  color: var(--td-ink-1);
}

.td-nav {
  background: color-mix(in srgb, var(--td-bone) 92%, transparent);
  border-bottom-color: var(--td-border);
}

html[data-theme="dark"] .td-nav {
  background: color-mix(in srgb, var(--td-ink) 88%, transparent);
  border-bottom-color: transparent;
}

.td-wordmark {
  color: var(--td-paper-2);
}

.td-btn-primary {
  color: var(--td-paper-2);
}

.td-btn-secondary {
  background: var(--td-bone);
  color: var(--td-text-primary);
  border-color: var(--td-border);
}

.td-card {
  background: var(--td-bone);
  border-color: var(--td-border);
}

/* Removed bare @media (prefers-color-scheme: dark) — data-theme is canonical */

/* ── Reset (v2 surfaces only — does not replace colors_and_type editorial base) ── */
.td-v2-root *, .td-v2-root *::before, .td-v2-root *::after { box-sizing: border-box; }

/* ── Layout Utilities ─────────────────────────────────────── */
.td-container { max-width: var(--td-max-w); margin: 0 auto; padding: 0 var(--td-sp-6); }
.td-container-narrow { max-width: var(--td-max-w-narrow); margin: 0 auto; padding: 0 var(--td-sp-6); }
.td-section { padding: var(--td-sp-16) 0; }
.td-section-sm { padding: var(--td-sp-8) 0; }

/* ── Typography ───────────────────────────────────────────── */
.td-display { font-size: var(--td-text-4xl); font-weight: 600; line-height: 1.1; letter-spacing: -.02em; }
.td-h1 { font-size: var(--td-text-3xl); font-weight: 600; line-height: 1.15; letter-spacing: -.018em; }
.td-h2 { font-size: var(--td-text-2xl); font-weight: 600; line-height: 1.2; letter-spacing: -.014em; }
.td-h3 { font-size: var(--td-text-xl); font-weight: 500; line-height: 1.3; }
.td-h4 { font-size: var(--td-text-lg); font-weight: 500; line-height: 1.4; }
.td-body { font-size: var(--td-text-base); font-weight: 400; line-height: 1.7; }
.td-body-sm { font-size: var(--td-text-sm); font-weight: 400; line-height: 1.65; }
.td-label { font-size: var(--td-text-xs); font-weight: 500; text-transform: uppercase; letter-spacing: .07em; color: var(--td-text-muted); }

/* ── Tap targets (DES-002/013) ───────────────────────────────
   Reusable utility for any INTERACTIVE control that is not already a
   `.td-btn*` (a plain `<a>` styled as a button, an icon-only control, a form
   input acting as a tap target). Apply `.td-tap-target` on patient-facing
   pages and `.td-tap-target-ops` on ops/console pages — never hardcode
   44/40 inline. Deliberately does NOT touch width/height directly (that
   would force a fixed box on content that should stay content-sized); it
   sets the min- floor and centers box-sizing so padding + border count
   toward it, same contract as `.td-btn`. Never apply to a genuinely
   decorative (non-interactive) glyph — see the per-page audit notes for
   which controls were judged interactive vs decorative. */
.td-tap-target {
  min-height: var(--td-tap-min, 44px);
  min-width: var(--td-tap-min, 44px);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.td-tap-target-ops {
  min-height: var(--td-tap-min-ops, 40px);
  min-width: var(--td-tap-min-ops, 40px);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── Buttons ──────────────────────────────────────────────── */
.td-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--td-sp-2);
  padding: 10px 20px;
  font-size: var(--td-text-sm);
  font-weight: 500;
  border-radius: var(--td-radius-md);
  border: none;
  cursor: pointer;
  transition: all var(--td-dur) var(--td-ease);
  text-decoration: none;
  white-space: nowrap;
  font-family: var(--td-font);
  /* PRO-501 (F-2026-09-12, mobile_shell_census): WCAG 2.5.5/2.5.8 44px tap-target
     floor. .td-btn-sm's tighter padding (6px 14px) computed a 31px box (13px
     under floor) on the front-door "Sign in →" CTA and any shared desk-page
     control drawing this base class — min-height on the shared BASE rule
     covers every .td-btn-* size variant, current and future, without a
     per-variant patch. box-sizing keeps the floor a true content+padding+border
     minimum regardless of a caller's own box-sizing reset. */
  min-height: var(--td-tap-min, 44px);
  box-sizing: border-box;
}
/* F-002 (patient_a11y_contrast_gate) — InsuranceCapture.jsx's photo-upload
   `<label class="td-btn ...">` wraps a visually-hidden `<input type="file">`
   (position:absolute, 1x1, opacity:0 — the standard "real target is the
   label" pattern for styling a native file input) that this gate still
   measures directly, at 1x1. Scoped to exactly that shape via :has() rather
   than a blanket `input[type=file]` override (which would risk enlarging an
   unrelated file input elsewhere into an unpositioned, page-escaping
   invisible click target — see this rule's own PR discussion). `position:
   relative` on the label ensures the enlarged input's containing block is
   the label itself, not some distant positioned ancestor; the input has no
   top/left offset, so its static position — and therefore this expanded
   hit-box — stays anchored at the label's own top-left corner. The 1x1 size
   is set INLINE on the input (React style={{width:1,height:1}}), which no
   external stylesheet rule can outrank without !important — scoped to this
   one exact :has() match, not a global !important on the attribute selector. */
.td-btn:has(> input[type="file"]) {
  position: relative;
  min-height: var(--td-tap-min, 44px);
}
.td-btn:has(> input[type="file"]) > input[type="file"] {
  width: 44px !important;
  height: 44px !important;
}
.td-btn-primary {
  background: var(--td-primary);
  color: var(--td-ink);
  box-shadow: var(--td-shadow-glow);
}
.td-btn-primary:hover { background: var(--td-primary-mid); box-shadow: var(--td-shadow-lift); transform: translateY(-1px); text-decoration: none; }
.td-btn-primary:active { transform: translateY(0); }

.td-btn-secondary {
  background: var(--td-bone);
  color: var(--td-ink-1);
  border: 1px solid var(--td-ink-5);
  box-shadow: var(--td-shadow-soft);
}
.td-btn-secondary:hover { border-color: var(--td-seal); background: var(--td-bone-2); text-decoration: none; }

.td-btn-ghost {
  background: transparent;
  color: var(--td-seal);
  border: 1px solid var(--td-seal-tint);
}
.td-btn-ghost:hover { background: var(--td-seal-tint); text-decoration: none; }

.td-btn-teal {
  background: var(--td-sage);
  color: var(--td-paper-2);
}
.td-btn-teal:hover { background: var(--td-sage-2); transform: translateY(-1px); text-decoration: none; }

.td-btn-lg { padding: 14px 28px; font-size: var(--td-text-base); border-radius: var(--td-radius-lg); }
.td-btn-sm { padding: 6px 14px; font-size: var(--td-text-xs); border-radius: var(--td-radius-sm); }

/* ── Cards ────────────────────────────────────────────────── */
.td-card {
  background: var(--td-bone);
  border: 1px solid var(--td-ink-5);
  border-radius: var(--td-radius-lg);
  padding: var(--td-sp-6);
  box-shadow: var(--td-shadow-soft);
}
.td-card:hover { box-shadow: var(--td-shadow-lift); border-color: var(--td-seal); }
.td-card-sm { padding: var(--td-sp-4); border-radius: var(--td-radius-md); }

/* ── Dentist listing card ─────────────────────────────────── */
.td-dentist-card {
  background: var(--td-surface);
  border: 1px solid var(--td-border);
  border-radius: var(--td-radius-xl);
  padding: var(--td-sp-6);
  box-shadow: var(--td-shadow-sm);
  transition: all var(--td-dur) var(--td-ease);
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: var(--td-sp-5);
  align-items: flex-start;
}
.td-dentist-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--td-shadow-lg);
  border-color: var(--td-border-strong);
}
.td-dentist-avatar {
  width: 72px;
  height: 72px;
  border-radius: var(--td-radius-lg);
  object-fit: cover;
  background: var(--td-bg);
}
.td-dentist-name { font-size: var(--td-text-md); font-weight: 600; color: var(--td-text-primary); line-height: 1.2; margin-bottom: 2px; }
.td-dentist-specialty { font-size: var(--td-text-sm); color: var(--td-text-secondary); margin-bottom: var(--td-sp-3); }
.td-dentist-meta { display: flex; flex-wrap: wrap; gap: var(--td-sp-2); align-items: center; margin-bottom: var(--td-sp-4); }
.td-dentist-actions { display: flex; gap: var(--td-sp-2); }

/* ── VPS Badge (Michelin-style) ───────────────────────────── */
.td-vps-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--td-radius-sm);
  font-size: var(--td-text-xs);
  font-weight: 500;
  background: var(--td-primary-light);
  color: var(--td-primary);
  border: 1px solid rgba(0,87,255,.2);
}
.td-vps-badge svg { width: 12px; height: 12px; }

/* Axis recognition badges */
.td-axis-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: var(--td-radius-sm);
  font-size: 10px;
  font-weight: 500;
  background: var(--td-teal-light);
  color: var(--td-teal-dark);
}

/* ── Tags / Pills ─────────────────────────────────────────── */
.td-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--td-radius-pill);
  font-size: var(--td-text-xs);
  font-weight: 500;
  border: 1px solid var(--td-border);
  color: var(--td-text-secondary);
  background: var(--td-surface);
  cursor: pointer;
  transition: all var(--td-dur) var(--td-ease);
}
.td-pill:hover, .td-pill.active {
  background: var(--td-primary);
  color: var(--td-white);
  border-color: var(--td-primary);
}

/* ── Search bar ───────────────────────────────────────────── */
.td-search-bar {
  display: flex;
  background: var(--td-bone);
  border: 1px solid var(--td-ink-5);
  border-radius: var(--td-radius-xl);
  box-shadow: var(--td-shadow-soft);
  overflow: hidden;
  align-items: center;
  padding: var(--td-sp-1);
  gap: var(--td-sp-1);
}
.td-search-input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  padding: 10px var(--td-sp-4);
  font-size: var(--td-text-base);
  font-family: var(--td-font);
  color: var(--td-ink-1);
}
.td-search-input::placeholder { color: var(--td-ink-4); }
.td-search-divider { width: 1px; height: 24px; background: var(--td-ink-5); }

/* ── Form elements ────────────────────────────────────────── */
.td-input {
  width: 100%;
  padding: 10px var(--td-sp-4);
  min-height: 44px; /* F-002 (patient_a11y_contrast_gate) — measured 40px on #emergency's InsuranceCapture panel */
  border: 1px solid var(--td-ink-5);
  border-radius: var(--td-radius-md);
  font-family: var(--td-font);
  font-size: var(--td-text-base);
  color: var(--td-ink-1);
  background: var(--td-bone);
  transition: border-color var(--td-dur);
  outline: none;
}
.td-input:focus { border-color: var(--td-seal); box-shadow: 0 0 0 3px var(--td-seal-tint); background: var(--td-bone-2); }
.td-input::placeholder { color: var(--td-ink-4); }

.td-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394A3B8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
}

/* ── Nav / Header ─────────────────────────────────────────── */
.td-nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--td-ink) 88%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--td-ink-5);
}
.masthead.td-nav {
  padding: var(--td-sp-4) var(--td-sp-6);
}
.td-nav-inner {
  max-width: var(--td-max-w);
  margin: 0 auto;
  display: flex;
  align-items: center;
  height: 60px;
  gap: var(--td-sp-6);
}
/* Brand wordmark — Playfair Display 700; .ai italic per canon */
.td-wordmark {
  font-family: var(--td-font-display);
  font-size: 22px;
  font-weight: 700;
  color: var(--td-paper-2);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.td-wordmark em.ai,
.td-wordmark .ai {
  font-style: italic;
  color: var(--td-seal);
  font-weight: 600;
}
.td-nav-links { display: flex; gap: var(--td-sp-5); margin-left: auto; }
.td-nav-link { font-size: var(--td-text-sm); color: var(--td-ink-2); transition: color var(--td-dur); }
.td-nav-link:hover { color: var(--td-ink-1); text-decoration: none; }

/* ── Hero variants ────────────────────────────────────────── */
.td-hero-directory {
  background: linear-gradient(160deg, var(--td-primary-light) 0%, var(--td-bg) 55%, var(--td-teal-light) 100%);
  padding: var(--td-sp-20) 0 var(--td-sp-16);
}
.td-hero-dentist {
  background: var(--td-dark-bg);
  color: var(--td-white);
  padding: var(--td-sp-20) 0 var(--td-sp-16);
}

/* ── Trust stripe ─────────────────────────────────────────── */
.td-trust-stripe {
  background: var(--td-bone);
  border-top: 1px solid var(--td-ink-5);
  border-bottom: 1px solid var(--td-ink-5);
  padding: var(--td-sp-4) 0;
}
.td-trust-items { display: flex; gap: var(--td-sp-8); align-items: center; justify-content: center; flex-wrap: wrap; }
.td-trust-item { display: flex; align-items: center; gap: var(--td-sp-2); font-size: var(--td-text-sm); color: var(--td-ink-2); }
.td-trust-item svg { width: 16px; height: 16px; color: var(--td-teal); }

/* ── Dashboard panels ─────────────────────────────────────── */
.td-sidebar {
  background: var(--td-surface);
  border-right: 1px solid var(--td-border);
  min-height: 100vh;
  width: 220px;
  padding: var(--td-sp-6) 0;
  flex-shrink: 0;
}
.td-sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--td-sp-3);
  padding: 10px var(--td-sp-5);
  font-size: var(--td-text-sm);
  color: var(--td-text-secondary);
  border-radius: 0;
  transition: all var(--td-dur);
  border-left: 3px solid transparent;
}
.td-sidebar-link:hover { color: var(--td-text-primary); background: var(--td-bg); text-decoration: none; }
.td-sidebar-link.active { color: var(--td-primary); background: var(--td-primary-light); border-left-color: var(--td-primary); font-weight: 500; }

/* ── Stat card ────────────────────────────────────────────── */
.td-stat-card {
  background: var(--td-surface);
  border: 1px solid var(--td-border);
  border-radius: var(--td-radius-lg);
  padding: var(--td-sp-5) var(--td-sp-6);
}
.td-stat-label { font-size: var(--td-text-xs); color: var(--td-text-muted); text-transform: uppercase; letter-spacing: .06em; margin-bottom: var(--td-sp-2); }
.td-stat-value { font-size: var(--td-text-2xl); font-weight: 600; color: var(--td-text-primary); line-height: 1; }
.td-stat-delta { font-size: var(--td-text-sm); margin-top: var(--td-sp-1); }
.td-stat-delta.pos { color: var(--td-success); }
.td-stat-delta.neg { color: var(--td-danger); }

/* ── Toggle switch ────────────────────────────────────────── */
.td-toggle { position: relative; display: inline-flex; align-items: center; }
.td-toggle input { position: absolute; opacity: 0; width: 0; }
.td-toggle-track {
  width: 40px; height: 22px;
  background: var(--td-border-strong);
  border-radius: var(--td-radius-pill);
  cursor: pointer;
  transition: background var(--td-dur);
}
.td-toggle-track::after {
  content: '';
  position: absolute;
  top: 3px; left: 3px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--td-white);
  box-shadow: var(--td-shadow-sm);
  transition: transform var(--td-dur) var(--td-ease);
}
.td-toggle input:checked ~ .td-toggle-track { background: var(--td-primary); }
.td-toggle input:checked ~ .td-toggle-track::after { transform: translateX(18px); }

/* ── Section divider ──────────────────────────────────────── */
.td-divider { height: 1px; background: var(--td-border); margin: var(--td-sp-6) 0; }

/* ── Avatar stack ─────────────────────────────────────────── */
.td-avatar {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: var(--td-primary-light);
  color: var(--td-primary);
  font-size: 12px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--td-surface);
}

/* ── Responsive ───────────────────────────────────────────── */
@media (max-width: 768px) {
  .td-display { font-size: var(--td-text-3xl); }
  .td-h1 { font-size: var(--td-text-2xl); }
  .td-h2 { font-size: var(--td-text-xl); }
  .td-container { padding: 0 var(--td-sp-4); }
  .td-dentist-card { grid-template-columns: 56px 1fr; gap: var(--td-sp-4); }
  .td-dentist-avatar { width: 56px; height: 56px; }
  .masthead.td-nav .nav-links { display: none; }
  .td-sidebar { display: none; }
}

/* ── Animations ───────────────────────────────────────────── */
@keyframes td-fadein { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.td-fadein { animation: td-fadein .35s var(--td-ease) both; }
@keyframes td-skeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.td-skeleton {
  background: linear-gradient(90deg, var(--td-border) 25%, var(--td-bg) 50%, var(--td-border) 75%);
  background-size: 200% 100%;
  animation: td-skeleton 1.6s infinite;
  border-radius: var(--td-radius-md);
}
