@import url("core/text-entry-contract.css?v=20261008-4");

/**
 * Global desktop design tokens — SPA Admin / Ollira
 *
 * Apple 2026 CALM palette — calibrated against apple.com / macOS Sequoia
 * system chrome, not the saturated iOS app palette.
 *
 * Design principles:
 *   - White + gray ladder + ink are the language. 92% of chrome is monochrome.
 *   - A single muted interactive blue (#0066cc) — Apple.com link / CTA blue —
 *     used ONLY for truly important moments: primary CTAs, links, focus rings,
 *     selected date in mini-calendar. Never for decoration, never for category.
 *   - Two muted semantic anchors — forest green (#2d7a3e) for success / completed,
 *     calm crimson (#c41e3a) for danger / cancel. Used only for genuine state.
 *   - No orange, no teal, no indigo, no coral, no rainbow staff palette.
 *   - All grays are slightly cool (Apple systemGray ladder) — warmth would
 *     feel editorial; cool-neutral reads "software".
 *   - Dark mode uses Apple's elevated-background pattern: #161617 page,
 *     #1d1d1f canvas, #2c2c2e subtle — not pure-black, which feels harsh.
 *
 * All legacy token names are preserved so module CSS keeps compiling.
 * Modules consume via var(--ds-*); never introduce page-local hex for
 * shared UI chrome.
 */
:root {
  /* ═══════════════════════════════════════════════════════════════════
   * BLUE BRAND SYSTEM v1.0  (Booking OS · Color System)
   *
   * Canonical single-hue system. One brand blue (H≈255) derives the
   * whole palette; cool neutrals carry a whisper of the same hue so the
   * UI reads as one family (never pure gray). Statuses are the only
   * other saturated moments, all balanced to the same lightness so no
   * one dominates. Colour is never the sole signal — always paired with
   * a label. 60 / 30 / 10 discipline: 60% neutral ground, 30% surface +
   * text, 10% brand blue. When everything is blue, nothing is.
   *
   * These raw scales are the source; the semantic --ds-* aliases below
   * resolve to them. Surface CSS consumes ONLY the aliases, never these
   * raw steps directly (except decorative charts, per spec §01).
   * ─────────────────────────────────────────────────────────────────── */
  --brand-50:  #f2f7fe;
  --brand-100: #d8e8fe;
  --brand-200: #b4d2fd;
  --brand-300: #85b8fb;
  --brand-400: #4e97f6;
  --brand-500: #1a7bf0;
  --brand-600: #006de9;   /* BASE — canonical action colour, 4.8:1 on white */
  --brand-700: #005ac4;   /* hover */
  --brand-800: #00489c;   /* pressed */
  --brand-900: #003a7d;
  --brand-950: #002450;

  /* ══ THE NEUTRAL LADDER — the ONLY grey in the product ═══════════════════
   *
   * THE LAW: colour means STATE, never identity. One blue for what you can
   * do, this one ladder for everything you read, and the closed status set
   * below for what a booking IS. Anything else is greyscale.
   *
   * Neutrals are 60–70% of every screen, so a second grey system does not
   * read as "a different grey" — it reads as every surface being slightly
   * wrong next to every other, with nobody able to say why. The product used
   * to carry three of them at once (Apple's, ours, and Tailwind's slate/gray,
   * the last at 763 occurrences across 24 files). Tailwind slate was the
   * worst offender and not on taste grounds: it sits at ~220° hue, so it is a
   * SECOND BLUE pretending to be grey, and it fought --brand at 211°.
   *
   * This ladder is theme-INDEPENDENT (see the dark block: only the semantic
   * aliases below re-point to different rungs). That is what makes migrating
   * a hardcoded grey safe — swapping a literal for its rung changes nothing
   * in either theme, it only moves the value onto the ladder.
   *
   * MIGRATION MAP — matched by luminance, not by name:
   *   slate-50  #f8fafc → 25    gray-50  #f9fafb → 25
   *   slate-100 #f1f5f9 → 50    gray-100 #f3f4f6 → 50
   *   slate-200 #e2e8f0 → 100   gray-200 #e5e7eb → 100
   *   slate-300 #cbd5e1 → 200   gray-300 #d1d5db → 200
   *   slate-400 #94a3b8 → 400   gray-400 #9ca3af → 400
   *   slate-500 #64748b → 500   gray-500 #6b7280 → 500
   *   slate-600 #475569 → 600   gray-600 #4b5563 → 600
   *   slate-700 #334155 → 600   gray-700 #374151 → 600
   *   slate-800 #1e293b → 700   gray-800 #1f2937 → 700
   *   slate-900 #0f172a → 800   gray-900 #111827 → 800
   */
  --gray-0:   #ffffff;
  --gray-25:  #f7f8fb;
  --gray-50:  #f2f2f7;
  --gray-100: #e8eaf0;
  --gray-200: #d3d6df;
  --gray-300: #aeb2bf;
  --gray-400: #8a8d99;
  --gray-500: #62656f;
  --gray-600: #3c3e45;
  --gray-700: #24262b;
  --gray-800: #14161a;
  --gray-900: #0b0c0f;

  /* Booking status trio: dot colour · tint background · ink (on tint). */
  --status-confirmed: #00a96b; --status-confirmed-bg: #e4f8ef; --status-confirmed-ink: #00714a;
  --status-pending:   #e38a00; --status-pending-bg:   #fdf1dc; --status-pending-ink:   #8c5400;
  --status-cancelled: #e0322b; --status-cancelled-bg: #fce9e8; --status-cancelled-ink: #a4211c;
  --status-waitlist:  #00a0b4; --status-waitlist-bg:  #dff5f8; --status-waitlist-ink:  #006b79;
  --status-noshow:    #7a5af8; --status-noshow-bg:    #eeeafe; --status-noshow-ink:    #4b33c4;
  --status-completed: #62656f; --status-completed-bg: #e8eaf0; --status-completed-ink: #3c3e45;

  /* The fourth rung: the EDGE of a status surface. The trio had dot/fill/ink
   * but no border, so every surface that needed one reached outside the system
   * for it — which is how Tailwind's red-200/green-200/amber-200 got in. It is
   * DERIVED, not chosen: the status hue mixed into the surface it sits on, so
   * a border can never drift away from the fill it belongs to. */
  --status-confirmed-border: color-mix(in srgb, var(--status-confirmed) 34%, var(--gray-0));
  --status-pending-border:   color-mix(in srgb, var(--status-pending)   34%, var(--gray-0));
  --status-cancelled-border: color-mix(in srgb, var(--status-cancelled) 34%, var(--gray-0));
  --status-waitlist-border:  color-mix(in srgb, var(--status-waitlist)  34%, var(--gray-0));
  --status-noshow-border:    color-mix(in srgb, var(--status-noshow)    34%, var(--gray-0));
  --status-completed-border: color-mix(in srgb, var(--status-completed) 34%, var(--gray-0));

  /* Semantic surface/text/accent aliases (dark overrides in the dark block). */
  --bg-canvas:  var(--gray-50);
  --bg-surface: var(--gray-0);
  --bg-raised:  var(--gray-25);
  --border-hairline: #e1e3ea;
  --text-primary:   var(--gray-700);
  --text-secondary: var(--gray-500);
  --accent:      var(--brand-600);
  --accent-hover: var(--brand-700);
  --accent-soft: var(--brand-50);
  --focus-ring:  var(--brand-300);

  /* ── Strict app aliases ─────────────────────────────────────────── */
  --primary-bg: var(--bg-canvas);
  --card-bg: var(--bg-surface);
  /* Legacy token name kept; value is brand blue (was neutral ink). */
  --accent-sapphire: var(--brand-600);
  --text-main: var(--text-primary);
  --text-muted: var(--text-secondary);

  /* ── Ollira light surfaces — v26.58 Apple 2026 4-tier neutral ladder ──
   *
   * Ship history:
   *   v26.55 — first Apple-tuned ladder: page #fbfbfd → #f2f3f6,
   *     chrome #ececf0 → #e9eaee, toolbar-band #f3f3f7 → #f6f7f9,
   *     canvas-app #ffffff. v26.55 reduced backdrop+chrome closeness
   *     but all four tiers were still perceptually too tight (the
   *     v26.55 page-vs-chrome delta of ~3% lightness was below the
   *     threshold a casual viewer registers as separate surfaces).
   *   v26.56 — ownership cleanup atop the v26.55 values, no value
   *     changes. Verifier checks F + H asserted the v26.55 token
   *     values; check P asserted topbar source ownership.
   *   v26.57 — live cascade winner fix on the day-only premium
   *     toolbar selector, no value changes.
   *   v26.58 — STRONGER 4-tier contrast so the v26.55 ladder
   *     actually reads:
   *     page          v26.55 #f2f3f6 → v26.58 #eef0f4 (deeper backdrop)
   *     chrome        v26.55 #e9eaee → v26.58 #e4e6eb (deeper chrome)
   *     toolbar-band  v26.55 #f6f7f9 → v26.58 #f4f5f7 (slightly deeper)
   *     canvas-app    v26.55 #ffffff → v26.58 #ffffff (unchanged —
   *                                       focal canvas must remain
   *                                       brightest)
   *     The v26.58 toolbar-band stays only just below canvas so the
   *     toolbar/grid hairline still reads as a divider, but the
   *     overall ladder backdrop ↔ chrome ↔ toolbar ↔ canvas now has
   *     visible deltas at every step (vs v26.55 where chrome and
   *     toolbar were ~7% lightness apart but page and chrome were
   *     ~3% — the bottom of the ladder was too tight).
   *   v26.58 also retunes panel border/shadow (slightly deeper than
   *     v26.55 values to match the deeper backdrop without going
   *     heavy).
   *
   *   Legacy aliases kept (no value change in this ship, identical
   *   to v26.55 layout):
   *     #ffffff  --ds-color-bg-canvas      (raw white alias)
   *     #f5f5f7  --ds-color-bg-subtle      (hover/inner accent)
   *     #eeeeef  --ds-color-bg-muted       (muted inner controls)
   *
   * The 4-tier law lives below in the v26.52 block (--ds-color-bg-
   * chrome / -toolbar-band / -canvas-app / -focus-surface). v26.55
   * first tuned those for Apple parity; v26.58 tunes them to the
   * final Apple-2026 ladder values. */
  --ds-color-brand: var(--gray-700);
  /* Blue System v1.0 — page backdrop is the cool canvas tone; all-white
   * panels lift as cards above it. (Was Apple flat-canvas #f2f3f5.) */
  --ds-color-bg-page: var(--bg-canvas);
  --ds-color-bg-canvas: var(--bg-surface);
  --ds-color-bg-subtle: var(--bg-raised);
  --ds-color-bg-muted: var(--gray-100);
  /* v28.5.41 — Surface aliases requested by booking-cards.css §669
   * (.ops-block-appt dark variant) and the legacy hover paths.
   * `--ds-color-bg-surface` is the elevated card body (same as
   * canvas in light theme; one notch lighter than canvas in dark
   * theme so cards read as raised against the slightly darker page
   * background). `--ds-color-bg-surface-hover` is the same surface
   * one notch deeper, paired with the rest token for consistent
   * hover lift across all card surfaces. Pre-v28.5.41 these tokens
   * were referenced via var() with stale Tailwind-slate fallbacks
   * (`#111827` / `#1f2937`) that drifted hard from the Apple
   * cool-gray palette. */
  --ds-color-bg-surface: var(--bg-surface);
  --ds-color-bg-surface-hover: var(--gray-50);

  --ds-color-text: var(--text-primary);
  --ds-color-text-secondary: var(--text-secondary);
  --ds-color-text-tertiary: var(--gray-400);
  --ds-color-text-quaternary: var(--gray-300);
  /* v28.5.41 — Alias used by calendar-toolbar.css §1704 dark-only
   * rules. Resolves to the canonical text token in dark mode (set
   * below in the dark theme block). In light theme it falls
   * through to --ds-color-text via var() chain — light callers
   * should typically use --ds-color-text directly. Kept for
   * parity with the legacy fallback chain. */
  --ds-color-text-on-dark: #f5f5f7;

  /* ── COLOR LAW (2026-05) ─────────────────────────────────────
   * Aliases below MUST be used by ALL surface CSS — sidebar,
   * drawers, popovers, calendar, dialog, dropdown, modal, toast,
   * etc. Hardcoded hex/rgb values for `color:` and `background-color:`
   * outside design-tokens.css / design-system.css are forbidden.
   * The static verifier scans for violations on every ship.
   * Dark mode is OPAQUE to consumers — change tokens here only;
   * never branch on `[data-app-theme="dark"]` in surface CSS for
   * pure color-flip purposes.
   * ─────────────────────────────────────────────────────────── */

  /* Primary alias — many surfaces (drawer, modal) expect this exact name. */
  --ds-color-text-primary: var(--ds-color-text);
  /* Muted ink — secondary text alias for legibility hierarchies. */
  --ds-color-text-muted: var(--ds-color-text-secondary);

  /* Surface state backgrounds — hover/pressed/active for ANY interactive
   * surface (nav links, buttons, list rows, popover items). The hover
   * is calibrated to be just visible on white without competing with
   * the focus ring. */
  --ds-color-bg-hover: rgba(11, 12, 15, 0.04);
  --ds-color-bg-pressed: rgba(11, 12, 15, 0.08);
  --ds-color-bg-selected: rgba(0, 109, 233, 0.06);

  /* Subtle border — for hairline separators that need to whisper
   * (table rows, popover divider lines, drawer field underlines).
   * NOT the same as --ds-color-divider (which is strongest separator). */
  --ds-color-border-subtle: rgba(11, 12, 15, 0.08);

  --ds-color-border: var(--gray-200);
  --ds-color-border-strong: var(--gray-300);
  --ds-color-border-light: var(--border-hairline);
  --ds-color-divider: var(--border-hairline);

  /* Legacy accent tokens — now brand blue. */
  --ds-color-sapphire: var(--brand-600);
  --ds-color-sapphire-black: var(--brand-800);

  /* Brand mark (logo tile + bolt) — blue bolt on soft-blue tint tile. */
  --ds-color-brand-logo-bg: var(--brand-50);
  --ds-color-brand-bolt: var(--brand-600);

  /* Tenant main column max width. */
  --ds-content-max-width: 1320px;

  /* Accent chrome — primary nav text, key emphasis. Deliberately kept
   * NEUTRAL INK (not blue): per the 60/30/10 law, nav text and headers
   * stay ink so the brand blue reads as the single 10% action moment. */
  --ds-color-accent: var(--gray-700);
  --ds-color-accent-hover: var(--gray-900);
  --ds-color-accent-muted: rgba(36, 38, 43, 0.06);
  --ds-color-accent-border: rgba(36, 38, 43, 0.18);

  /* ── Interactive blue — THE brand action colour (#006de9) ────────
   * Used for: primary action buttons, links, focus rings, active
   * date/slot, selected states. This is the 10%. Never decoration. */
  --ds-color-interactive: var(--brand-600);
  --ds-color-interactive-hover: var(--brand-700);
  --ds-color-interactive-muted: rgba(0, 109, 233, 0.08);
  --ds-color-interactive-border: rgba(0, 109, 233, 0.24);
  --ds-color-primary-command-bg: var(--ds-color-interactive);
  --ds-color-primary-command-bg-hover: var(--ds-color-interactive-hover);

  /* Disabled commands keep their contrast without fading the whole control. */
  --ds-color-button-disabled-bg: var(--ds-color-bg-muted);
  --ds-color-button-disabled-text: var(--ds-color-text-secondary);
  --ds-color-button-disabled-border: transparent;

  /* Focus ring — brand blue (important for a11y visibility). */
  --ds-color-focus-ring: rgba(0, 109, 233, 0.32);
  --ds-color-focus-ring-ui: var(--brand-600);

  /* ── Semantic (calm, muted — never iOS-vibrant) ─────────────────── */
  /* Danger / cancel / destructive action — status red. */
  --ds-color-danger: var(--status-cancelled);
  --ds-color-danger-bg: rgba(224, 50, 43, 0.06);
  /* Neutral at 28% — iOS toggle off-state track. */
  --ds-color-toggle-off: rgba(120, 120, 128, 0.28);
  /* iOS toggle active state — Apple green (orthogonal to brand). */
  --ds-color-toggle-on: #34c759;
  /* Success / completed / checked-in — status green. */
  --ds-color-success: var(--status-confirmed);
  --ds-color-success-bg: rgba(0, 169, 107, 0.06);
  --ds-color-success-muted: var(--gray-500);
  /* Warning → monochrome (no orange per calm palette). Contrast via weight,
   * not hue. Legacy token names preserved. */
  --ds-color-warning: #424245;
  --ds-color-warning-bg: #eeeeef;
  --ds-color-warning-text: #6e6e73;
  /* Rating stars ONLY — calm brass, never iOS-vivid orange. The one warm hue in
   * the palette; a rating is data, and gold is its universal material. */
  --ds-color-star: #a8781d;

  /* Availability dot on staff avatars — status green. */
  --ds-color-ios-system-green: var(--status-confirmed);
  /* Primary UI toggles / active chrome — legacy name, neutral ink. */
  --ds-color-apple-system-green: var(--gray-700);

  /* ── Appointment lifecycle status ────────────────────────────────
   * Blue System v1.0 status hues. No-show is now VIOLET, deliberately
   * distinct from cancelled RED (the spec separates "didn't come" from
   * "was cancelled"). "in-progress" carries the brand blue as the one
   * live/now state. Colour is never the sole signal — always + label. */
  --ds-color-status-scheduled: var(--gray-400);
  --ds-color-status-confirmed: var(--status-confirmed);
  --ds-color-status-checked-in: var(--status-confirmed);
  --ds-color-status-in-progress: var(--brand-600);
  --ds-color-status-completed: var(--status-completed);
  --ds-color-status-cancelled: var(--status-cancelled);
  --ds-color-status-no-show: var(--status-noshow);

  /* ── Typography ─────────────────────────────────────────────────── */
  --ds-font-sans: "Inter", system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Tabular numerics for prices, time slots, stat readouts. Inter ships
   * tnum + cv11; the system stack falls through to SF / Segoe UI tabular. */
  --ds-font-numeric: var(--ds-font-sans);

  /* Raw size primitives — kept for backward compatibility with the few
   * call sites that already use them. New code SHOULD reach for the
   * composite --ds-type-* tokens below instead, which bundle size +
   * weight + line-height + letter-spacing per semantic role. */
  --ds-text-2xs: 0.625rem;       /* 10px */
  --ds-text-xs: 0.6875rem;       /* 11px */
  --ds-text-sm: 0.8125rem;       /* 13px */
  --ds-text-base: 0.875rem;      /* 14px */
  --ds-text-md: 0.875rem;        /* 14px alias */
  --ds-text-lg: 1rem;            /* 16px */
  --ds-text-xl: 1.125rem;        /* 18px */
  --ds-text-2xl: 1.25rem;        /* 20px */
  --ds-text-display: 1.5rem;     /* 24px */
  --ds-text-hero: 2.5rem;        /* 40px */
  --ds-text-page-title: clamp(1.25rem, 2vw, 1.5rem);

  --ds-font-normal: 400;
  --ds-font-regular: 400;     /* alias used by .ds-text-* helpers */
  --ds-font-medium: 500;
  --ds-font-semibold: 600;
  --ds-font-bold: 700;
  --ds-font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --ds-font-feature-text: "ss01", "cv11", "calt";
  --ds-font-feature-tnum: "tnum", "lnum";

  --ds-leading-none: 1;
  --ds-leading-2xs: 1.15;
  --ds-leading-xs: 1.4;
  --ds-leading-sm: 1.45;
  --ds-leading-base: 1.5;
  --ds-leading-md: 1.5;
  --ds-leading-lg: 1.4;
  --ds-leading-xl: 1.3;
  --ds-leading-2xl: 1.25;
  --ds-leading-display: 1.2;
  --ds-leading-hero: 1.1;
  --ds-leading-tight: 1.25;
  --ds-leading-snug: 1.35;
  --ds-leading-normal: 1.5;
  --ds-leading-relaxed: 1.6;

  /* Letter-spacing scale — Apple-HIG style: optical compensation only,
   * larger sizes get tighter tracking, body sizes neutral, captions
   * slightly looser for readability. */
  --ds-tracking-tightest: -0.022em;
  --ds-tracking-tighter:  -0.014em;
  --ds-tracking-tight:    -0.008em;
  --ds-tracking-normal:    0;
  --ds-tracking-wide:      0.012em;
  --ds-tracking-widest:    0.04em;     /* uppercase eyebrows / overlines */

  /* Per-step trackings consumed by the existing .ds-text-* helpers in
   * design-system.css. Optical pairing: small text gets neutral or
   * slightly positive tracking for legibility, large text gets tighter. */
  --ds-tracking-eyebrow:  0.06em;     /* uppercase eyebrows */
  --ds-tracking-xs:       0.005em;
  --ds-tracking-sm:       0;
  --ds-tracking-base:    -0.005em;
  --ds-tracking-md:      -0.008em;
  --ds-tracking-lg:      -0.012em;
  --ds-tracking-xl:      -0.016em;
  --ds-tracking-2xl:     -0.02em;
  --ds-tracking-display: -0.022em;
  --ds-tracking-hero:    -0.028em;
  --ds-tracking-tabular:  0;

  /* ── Type scale (composite roles) ────────────────────────────────
   * The canonical surface for product typography. Each token is a
   * `font:` shorthand bundling weight, size, and line-height; pair
   * with the matching `--ds-tracking-*` and an explicit `color:` from
   * the text-color tokens (`--ds-color-text` / -secondary / -tertiary
   * / -quaternary). Use the .ds-type-* helper classes from
   * design-system.css for the most concise application.
   *
   * Apple HIG-inspired scale, compressed for app density vs. marketing
   * surfaces. Sizes step ~1.125 ratio at the small end, ~1.25 at the
   * display end for visual hierarchy under dense layouts. */

  /* Display — page titles, dashboard headlines */
  --ds-type-display-lg: 700 1.75rem/1.15 var(--ds-font-sans);   /* 28/32 */
  --ds-type-display-md: 700 1.5rem/1.2 var(--ds-font-sans);     /* 24/29 */
  --ds-type-display-sm: 700 1.25rem/1.25 var(--ds-font-sans);   /* 20/25 */

  /* Heading — section headers, card titles, drawer titles */
  --ds-type-heading-lg: 600 1.125rem/1.3 var(--ds-font-sans);   /* 18/23 */
  --ds-type-heading-md: 600 1rem/1.35 var(--ds-font-sans);      /* 16/22 */
  --ds-type-heading-sm: 600 0.9375rem/1.35 var(--ds-font-sans); /* 15/20 */

  /* Body — primary readable copy, list rows, cell content */
  --ds-type-body-lg: 400 1rem/1.5 var(--ds-font-sans);          /* 16/24 */
  --ds-type-body-md: 400 0.875rem/1.5 var(--ds-font-sans);      /* 14/21 */
  --ds-type-body-sm: 400 0.8125rem/1.45 var(--ds-font-sans);    /* 13/19 */

  /* Body-strong — emphasized inline body (names, key facts) */
  --ds-type-body-md-strong: 500 0.875rem/1.5 var(--ds-font-sans);
  --ds-type-body-sm-strong: 500 0.8125rem/1.45 var(--ds-font-sans);

  /* Label — buttons, form labels, UI controls. Tighter line-height
   * because labels almost never wrap, keeps button heights tight. */
  --ds-type-label-lg: 600 0.9375rem/1.2 var(--ds-font-sans);    /* 15/18 */
  --ds-type-label-md: 600 0.875rem/1.2 var(--ds-font-sans);     /* 14/17 */
  --ds-type-label-sm: 600 0.8125rem/1.15 var(--ds-font-sans);   /* 13/15 */
  --ds-type-label-xs: 600 0.75rem/1.15 var(--ds-font-sans);     /* 12/14 */

  /* Caption — eyebrow text, timestamps, meta info, helper text */
  --ds-type-caption-md: 500 0.75rem/1.4 var(--ds-font-sans);    /* 12/17 */
  --ds-type-caption-sm: 500 0.6875rem/1.4 var(--ds-font-sans);  /* 11/15 */

  /* Eyebrow / overline — uppercase section labels (TIME, STAFF, GROWTH).
   * Pair with --ds-tracking-widest. */
  --ds-type-eyebrow: 600 0.6875rem/1.2 var(--ds-font-sans);     /* 11/13 */

  /* Numeric — calendar time slots, prices, stats. Tabular-nums via
   * font-feature-settings applied in the helper class below. */
  --ds-type-numeric-lg: 600 1rem/1.2 var(--ds-font-numeric);    /* 16/19 */
  --ds-type-numeric-md: 500 0.875rem/1.2 var(--ds-font-numeric);/* 14/17 */
  --ds-type-numeric-sm: 500 0.75rem/1.2 var(--ds-font-numeric); /* 12/14 */

  /* ── Spacing (8px grid) ─────────────────────────────────────────── */
  --ds-space-1: 0.25rem;
  --ds-space-2: 0.5rem;
  --ds-space-3: 0.75rem;
  --ds-space-4: 1rem;
  --ds-space-5: 1.25rem;
  --ds-space-6: 1.5rem;
  --ds-space-8: 2rem;
  --ds-space-10: 2.5rem;

  /* ── Radius ─────────────────────────────────────────────────────── */
  --ds-radius-sm: 6px;
  --ds-radius-md: 10px;
  --ds-radius-lg: 14px;
  --ds-radius-xl: 20px;
  --ds-radius-2xl: 24px;
  --ds-radius-pill: 999px;

  /* Commands, icon buttons and segmented controls share the compact corner
     used by the New appointment drawer. Larger radii remain for surfaces. */
  --ds-button-radius: var(--ds-radius-sm);
  --ds-button-radius-icon: var(--ds-radius-sm);
  --ds-button-radius-segmented: var(--ds-radius-sm);
  --ds-disclosure-radius: var(--ds-radius-sm);
  --ds-field-control-radius: var(--ds-radius-sm);
  --ds-option-radius: 0px;
  --ds-group-row-radius: 0px;
  --ds-interactive-surface-radius: var(--ds-radius-sm);

  /* ── Shadows (soft, single-cast ink) ────────────────────────────── */
  --ds-shadow-xs: 0 1px 0 rgba(0, 0, 0, 0.04);
  --ds-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
  --ds-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 1px 4px rgba(0, 0, 0, 0.04);
  --ds-shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04);
  --ds-shadow-drawer: -8px 0 32px rgba(0, 0, 0, 0.12);
  --ds-shadow-control-rest: 0 1px 2px rgba(0, 0, 0, 0.03);
  --ds-shadow-control-hover: 0 4px 14px rgba(0, 0, 0, 0.1);
  --ds-shadow-segmented-active: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
  --ds-shadow-segmented-thumb: 0 0 1px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.08),
    0 3px 10px rgba(0, 0, 0, 0.1);
  --ds-color-segmented-track: #eeeeef;
  --ds-color-segmented-track-strong: #e5e5ea;
  --ds-segmented-track-padding: 4px;
  --ds-segmented-thumb-inset: 2px;
  --ds-segmented-thumb-inset-pill: 4px;
  /* Split pill — neutral ink gradient. */
  --ds-split-pill-border: rgba(36, 38, 43, 0.08);
  --ds-split-pill-body-bg: var(--bg-surface);
  --ds-split-pill-accent-from: var(--gray-600);
  --ds-split-pill-accent-to: var(--gray-700);
  /* Today CTA depth — ink cast. */
  --ds-shadow-toolbar-cta: 0 2px 10px rgba(36, 38, 43, 0.18);
  --ds-shadow-toolbar-cta-hover: 0 8px 22px rgba(36, 38, 43, 0.18);
  --ds-shadow-toolbar-cta-active: 0 2px 8px rgba(36, 38, 43, 0.14);

  /* Floating UI surfaces. */
  --ds-surface-canvas: var(--ds-color-bg-canvas);
  --ds-surface-card: var(--ds-color-bg-canvas);
  --ds-surface-raised: var(--ds-color-bg-page);
  --ds-surface-tint: #f5f5f7;
  --ds-surface-floating-strong: #ffffff; /* ODS v1.2 Phase 1 — opaque; was rgba(255,255,255,0.97) */

  /* ── ODS v1.2 Phase 1 bridge tokens ─────────────────────────────────
   * Champagne Bronze accent family + focus ring + --ds-color-primary bridge.
   * Full --ods-bg-* semantic surface matrix is deferred to Phase 2. */
  /* Blue System v1.0 — the ODS accent family is now the brand blue
   * (was Champagne Bronze). Every consumer of --ods-accent / focus
   * colour / --ds-color-primary now resolves to blue for one coherent
   * identity across the whole app. */
  --ods-accent:             var(--brand-600);
  --ods-accent-dark:        var(--brand-400);
  --ods-accent-bg:          var(--brand-50);
  --ods-accent-bg-dark:     #10243d;
  --ods-accent-border:      var(--brand-200);
  --ods-accent-border-dark: var(--brand-700);
  --ods-focus-color:        var(--brand-700);
  --ods-focus-ring-outline: 2px solid var(--ods-focus-color);
  --ods-focus-ring-shadow:  0 0 0 3px rgba(0, 109, 233, 0.22);
  --ods-focus-ring-offset:  2px;
  --ds-color-primary:       var(--ods-accent); /* bridges booking-cards draft color-mix → brand blue */
  --ds-surface-border-subtle: rgba(29, 29, 31, 0.08);
  --ds-surface-border-strong: rgba(29, 29, 31, 0.15);
  --ds-surface-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.045);
  --ds-surface-shadow-md: var(--ds-shadow-glass);

  /* ── Control sizes (desktop) ────────────────────────────────────── */
  --ds-control-height: 2.25rem;
  --ds-control-height-sm: 2rem;
  --ds-control-height-lg: 2.5rem;
  --ds-control-padding-x: 0.75rem;
  --ds-app-header-rail-height: 3.5rem;
  --ds-workspace-control-height: var(--ds-control-height-lg);

  /* ── Layout ─────────────────────────────────────────────────────── */
  --ds-shell-pad-x: var(--ds-space-5);
  --ds-shell-pad-y: var(--ds-space-4);
  --ds-workspace-max-width: none;
  --ds-z-app-header: 200;
  --ds-z-account-popover: calc(var(--ds-z-app-header) + 30);
  --ds-z-drawer: calc(var(--ds-z-app-header) + 250);
  /* Toasts float ABOVE every overlay so a notification is never hidden. The old
   * value (drawer + 80 ≈ 530) sat UNDER the V4 calendar drawer panels (local
   * z-index ~1001), the action menu (~10100) and the staging drawer (~10090) —
   * so a status-update toast fell behind an open drawer (Armen 2026-07-15). Flat
   * value that clears all of them, still BELOW the blocking modal layer (20000). */
  --ds-z-toast: 15000;
  --ds-z-context-menu: 400;
  /* ── Modal z-index ─────────────────────────────────────────────────
   * AppModal is a blocking confirmation layer. It must sit above
   * EVERYTHING in the app: drawers (including V4 drawer panels which
   * use local z-index ~1001), drawer backdrops, context menus, toasts,
   * popovers, and any utility panes. A flat high value (20000) is used
   * intentionally so no future component can accidentally win the
   * stacking contest by adding a small offset. */
  --ds-z-modal: 20000;

  /* ── Motion (functional only) ───────────────────────────────────── */
  --ds-duration-fast: 150ms;
  --ds-duration-drawer: 180ms;
  --ds-duration-toolbar-entrance: 350ms;
  --ds-ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
  --ds-ease-spring: cubic-bezier(0.22, 1, 0.36, 1);

  /* ── Ollira motion system (shell / panel / popover) ─────────────────
   * Single source of truth for every open/close animation across the
   * application shell: account popover, view-settings drawer, sidebar
   * collapse, future right-edge sidecars. Goal is one perceived rhythm.
   *
   *   --ollira-motion-micro  140ms  labels, chevrons, opacity crossfades
   *   --ollira-motion-fast   200ms  panels, popovers, drawers, sidebar
   *   --ollira-ease                  decel-out curve (Linear / Vercel style)
   *
   * Reference: Linear inspector ~200ms, Figma right-panel ~180ms, Notion
   * sidebar ~200ms, Vercel dashboard ~160–200ms. Below this window motion
   * feels snappy but harsh; above it feels sluggish. 200ms with a steep
   * decel-out is the SaaS sweet spot.
   * ───────────────────────────────────────────────────────────────── */
  --ollira-motion-micro: 140ms;
  --ollira-motion-fast: 200ms;
  --ollira-ease: cubic-bezier(0.2, 0, 0, 1);

  /* ── Glass / backdrop ───────────────────────────────────────────── */
  --ds-glass-bg: rgba(255, 255, 255, 0.72);
  --ds-glass-bg-strong: rgba(255, 255, 255, 0.88);
  --ds-glass-border: rgba(255, 255, 255, 0.55);
  --ds-glass-blur: blur(24px) saturate(140%);
  --ds-glass-blur-strong: blur(32px) saturate(160%);

  /* Today CTA — ink fill, white text (neutral anchor, keeps blue as the
   * single primary-action moment rather than doubling it on navigation). */
  --ds-color-toolbar-cta-bg: var(--ds-color-interactive);
  --ds-color-toolbar-cta-border: var(--ds-color-interactive);
  --ds-color-toolbar-cta-bg-hover: var(--ds-color-interactive-hover);
  --ds-color-toolbar-cta-border-hover: var(--ds-color-interactive-hover);
  --ds-color-toolbar-cta-text: #ffffff;
  /* Mini-calendar selected day — brand blue (truly important moment). */
  --ds-color-selection-fill: var(--brand-600);
  --ds-color-selection-shadow: rgba(0, 109, 233, 0.28);

  /* ── Appointments calendar specific tokens ────────────────────────
   * Current-time indicator: uses the canonical danger token so it reads
   * as a live/critical moment, NOT raw hardcoded red.
   * Any module consuming the now-line must reference ONLY this token. */
  --ds-color-current-time: var(--ds-color-danger);          /* light: #e0322b */
  --ds-color-current-time-glow: rgba(224, 50, 43, 0.12);   /* soft halo */

  /* Appointments workspace header — near-colorless glass. */
  --ds-appts-header-control-height: var(--ds-workspace-control-height);
  --ds-appts-header-pad: 4px;
  --ds-appts-header-font-size: 0.9375rem;
  --ds-appts-header-tracking: -0.032em;
  --ds-appts-header-blur: blur(22px) saturate(114%);

  /* Day calendar workspace — monochrome ash atmosphere, no hue. */
  --ds-cal-atmos-bg-top: #fbfbfd;
  --ds-cal-atmos-bg-mid: #f5f5f7;
  --ds-cal-atmos-bg-bottom: #eeeeef;
  --ds-cal-atmos-orb-a: radial-gradient(circle, rgba(29, 29, 31, 0.04) 0%, rgba(29, 29, 31, 0.015) 100%);
  --ds-cal-atmos-orb-b: radial-gradient(circle, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0.22) 100%);
  --ds-cal-atmos-body-surface: rgba(255, 255, 255, 0.78);
  --ds-cal-atmos-body-blur: blur(18px) saturate(142%);

  /* Day calendar — liquid glass panels (low-alpha stack). */
  --ds-cal-liquid-fill: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.34) 0%,
    rgba(255, 255, 255, 0.16) 48%,
    rgba(255, 255, 255, 0.1) 100%
  );
  --ds-cal-liquid-fill-lifted: linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, transparent 38%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.34) 0%, rgba(255, 255, 255, 0.15) 52%, rgba(255, 255, 255, 0.09) 100%);
  --ds-cal-liquid-action-fill: linear-gradient(180deg, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0.14) 100%);
  --ds-cal-liquid-action-fill-hover: linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.18) 100%);
  --ds-cal-liquid-stroke-soft: rgba(255, 255, 255, 0.2);
  --ds-cal-liquid-blur: blur(22px) saturate(180%) brightness(1.06);
  --ds-cal-liquid-outer-shadow: 0 12px 32px rgba(0, 0, 0, 0.08), 0 3px 10px rgba(0, 0, 0, 0.05);
  --ds-cal-liquid-inset-rim: inset 0 1px 0 rgba(255, 255, 255, 0.64), inset 0 -1px 0 rgba(255, 255, 255, 0.1);

  /* ── Shell-frame tokens (Ollira 2026 dual layout) ─────────────────────
   *
   * Single source of truth for the shell's panel geometry. All three
   * panel surfaces — sidebar (left), workspace card (center), drawer
   * (right) — derive their top/bottom/radius/glass values from these
   * tokens, so they read as a unified panel system: same vertical
   * extents, same corner treatment, same surface chrome.
   *
   * The user-facing rule the tokens enforce:
   *   • header sits at the very top, full width
   *   • below the header: gutter on every outer edge + gutter between
   *     each pair of adjacent panels
   *   • all panels start at the SAME top line (header bottom + gutter)
   *     and end at the SAME bottom line (viewport bottom - gutter)
   *
   * Override any single token to adjust globally — there is no
   * per-component duplication of these values anywhere else in the
   * codebase.
   */
  --ds-shell-gutter: 12px;
  --ds-shell-header-height: 64px;
  /* ──────────────────────────────────────────────────────────────────
   * v26.50 — APPOINTMENTS TOPBAR ALIGNMENT LAW.
   *
   * One token drives the height of all five top-band surfaces on the
   * desktop attached-shell calendar:
   *   - sidebar `.logo-row`
   *   - day calendar toolbar `.appts-calendar-control-surface--premium`
   *   - booking drawer header `.appt-drawer__hd`
   *   - View-settings drawer header `.cal-view-settings__header`
   *   - Utility drawer header `.cal-utility-drawer__header`
   * Adjusting --ds-app-frame-topbar-height rebalances the entire row.
   * ────────────────────────────────────────────────────────────────── */
  --ds-app-frame-topbar-height: var(--ds-shell-header-height, 64px);

  /* ──────────────────────────────────────────────────────────────────
   * v26.52 — APPOINTMENTS SURFACE HIERARCHY (5-TIER).
   *
   * Five distinct surface tiers across the attached-shell calendar
   * page. The ladder is Apple-style cool-neutral and gives the user's
   * eye an automatic focus path: chrome surfaces (sidebar + drawer
   * header) recede; canvas (calendar grid) is the brightest focal
   * surface; toolbar + drawer body sit in the middle.
   *
   *  Tier 1 — chrome           sidebar + drawer header
   *  Tier 2 — toolbar band     calendar topbar + drawer body
   *  Tier 3 — canvas (focal)   calendar grid
   *
   * The 5-tier story:
   *   sidebar          (chrome)        ◀── recedes
   *   toolbar          (toolbar band)
   *   calendar grid    (canvas)         ◀── BRIGHTEST in light, focal
   *   drawer header    (chrome)
   *   drawer body      (toolbar band)
   *
   * Surface chrome (sidebar = drawer header) is one tier; toolbar
   * band (calendar toolbar = drawer body) is one tier; calendar
   * grid is its own focal tier. This keeps the visual delta between
   * adjacent surfaces tight enough to feel coherent (no garish
   * boundary) while large enough to read as separate surfaces (not
   * one flat wash).
   *
   * Three named tokens are exposed; all consumers read these only,
   * never the raw hex.
   * ────────────────────────────────────────────────────────────────── */
  /* ──────────────────────────────────────────────────────────────────
   * v27.0 — FLAT-CANVAS CANON (supersedes v26.52 5-tier hierarchy).
   *
   * The previous 3-tier surface ladder (chrome / toolbar-band /
   * canvas-app) was deliberately tonally separated so the user's eye
   * could trace sidebar (recessed) → toolbar (mid) → grid (focal).
   * v27.0 collapses all three tiers to pure white in light mode and
   * pushes ALL visual differentiation onto the page backdrop alone:
   * each panel reads as a single white card lifted above a calm
   * gray ground (--ds-color-bg-page = #f2f3f5).
   *
   * Light-mode contract:
   *   sidebar         (chrome)        ⟶ #ffffff
   *   calendar tools  (toolbar-band)  ⟶ #ffffff
   *   calendar grid   (canvas-app)    ⟶ #ffffff
   *   drawer header   (chrome)        ⟶ #ffffff
   *   drawer body     (toolbar-band)  ⟶ #ffffff
   *
   * Dark mode is UNCHANGED — the 5-tier ladder still applies there
   * (see html[data-app-theme="dark"] block below). The tokens are
   * still distinct names so dark-mode consumers keep their tonal
   * differentiation.
   *
   * Companion change at the bottom of this file: the three module-
   * level tier-divider hairlines (calendar toolbar bottom, drawer
   * header bottom, booking-drawer header ::after) are silenced in
   * light mode because they would otherwise render as visible
   * #e5e5ea lines on identical-white surfaces. Dark mode keeps
   * the dividers because the tonal ladder still wants them.
   * ────────────────────────────────────────────────────────────────── */
  --ds-color-bg-chrome:        #ffffff;  /* v27.0 (was #e4e6eb) — sidebar + drawer headers */
  --ds-color-bg-toolbar-band:  #ffffff;  /* v27.0 (was #f4f5f7) — calendar toolbar + drawer body */
  --ds-color-bg-canvas-app:    #ffffff;  /* unchanged — calendar grid */

  /* Legacy alias — kept for compat with v26.51 consumers. The drawer
   * panel uses the focus-surface name; in the v26.52 5-tier model the
   * drawer is composed of TWO surfaces (header = chrome, body =
   * toolbar-band), so focus-surface defaults to the toolbar-band
   * value (which paints the drawer body). The booking drawer's local
   * --_surface alias picks this up. */
  --ds-color-bg-focus-surface: var(--ds-color-bg-toolbar-band);

  /* Accent — muted-blue chip for "Today / Date" toolbar pill and
   * other accent affordances. Deliberately quiet (10% blue fill +
   * 28% blue border) so it reads as an accent, not a primary CTA. */
  --ds-color-accent-blue-bg:    rgba(0, 109, 233, 0.10);
  --ds-color-accent-blue-border: rgba(0, 109, 233, 0.28);
  --ds-color-accent-blue-text:   var(--brand-600);

  --ds-shell-panel-radius: 14px;
  /* Light-mode panel chrome: matches the workspace card. Sidebar +
   * drawer pull the same 4 properties so all three surfaces share one
   * visual language. Updated v26.45 to match premium sidebar design,
   * then v26.55 retuned border + shadow, then v26.58 retuned again
   * (border slightly stronger 0.085, shadow slightly larger 20px blur
   * + 0.08 alpha — reads as proper card lift over the deeper
   * #eef0f4 backdrop without feeling heavy). */
  --ds-shell-panel-bg:
    linear-gradient(180deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0.78) 100%);
  --ds-shell-panel-bg-solid: #ffffff;
  --ds-shell-panel-border: 0.5px solid rgba(15, 18, 28, 0.085);
  --ds-shell-panel-shadow:
    0 20px 48px rgba(15, 18, 28, 0.08),
    0 2px 8px rgba(15, 18, 28, 0.045);
  --ds-shell-panel-blur: blur(28px) saturate(150%);

  /* v26.53 — APPOINTMENTS FLOATING SHELL FRAME (peer-card geometry).
   * Consumed by calendar-floating-shell.css which loads as the final
   * calendar CSS include (after calendar-day-2026.css). Tokens wire
   * through existing surface tokens — no raw hex. */
  /* 2026-06-19 — Jony-Ive "unified surface" language: the app is ONE continuous
     workspace divided by hairlines, not a scatter of floating islands. The outer
     margin, inter-panel gap, island radius and shadow collapse to 0/none; the
     only structural divider is the sidebar↔content seam hairline (sidebar-base.css).
     Recovers the wasted voids and calms the surface for all-day operational use.
     Mobile (≤900px) already rendered edge-to-edge — desktop now matches. */
  --ollira-frame-outer: 0px;
  --ollira-frame-gap: 0px;
  --ollira-frame-radius: 0px;
  --ollira-frame-bg: var(--ds-color-bg-page);
  --ollira-frame-border: 0;
  --ollira-frame-shadow: none;
}

/* Native controls: ink for checkboxes/radios. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--ds-color-interactive);
}

/*
 * Dark theme (operator preference). Applied via html[data-app-theme="dark"]
 * from account menu + head script.
 *
 * Apple dark — Sequoia-aligned:
 *   - Page: #161617 (not pure black — harsh in long sessions)
 *   - Canvas: #1d1d1f (elevated one step)
 *   - Subtle:  #2c2c2e
 *   - Muted:   #3a3a3c
 *   - Text:    #f5f5f7 → #aeaeb2 → #8e8e93 → #636366
 *   - Interactive blue brighter (#2997ff — Apple.com dark link)
 *   - Muted green / red shifted one stop brighter
 */
html[data-app-theme="dark"] {
  color-scheme: dark;

  /* ── Blue System v1.0 — dark semantic overrides ───────────────────
   * The raw --brand-* / --gray-* ladders are theme-independent; only
   * the semantic aliases flip. On dark, 600 mutes so the brand lifts to
   * #3b92ff (5.8:1); status hues step one notch brighter, tint bgs go
   * translucent. */
  --bg-canvas:  var(--gray-900);
  --bg-surface: var(--gray-800);
  --bg-raised:  #1f2228;
  --border-hairline: #2a2e36;
  --text-primary:   #f5f6f8;
  --text-secondary: #9ba0ac;
  --accent:      #3b92ff;
  --accent-hover: #5aa6ff;
  --accent-soft: #10243d;
  --focus-ring:  #5aa6ff;

  --status-confirmed: #22c58a; --status-confirmed-bg: rgba(34,197,138,0.14);  --status-confirmed-ink: #5be0ac;
  --status-pending:   #ffae30; --status-pending-bg:   rgba(255,174,48,0.14);  --status-pending-ink:   #ffc66b;
  --status-cancelled: #ff5c54; --status-cancelled-bg: rgba(255,92,84,0.14);   --status-cancelled-ink: #ff8b85;
  --status-waitlist:  #3fc9db; --status-waitlist-bg:  rgba(63,201,219,0.14);  --status-waitlist-ink:  #7fe0ec;
  --status-noshow:    #9b85ff; --status-noshow-bg:    rgba(155,133,255,0.14); --status-noshow-ink:    #c0b2ff;
  --status-completed: #6e7480; --status-completed-bg: rgba(110,116,128,0.18); --status-completed-ink: #aeb2bf;

  /* Same derivation, mixed toward the DARK surface instead of white — so the
   * edge stays a step out of the fill in both themes rather than glowing. */
  --status-confirmed-border: color-mix(in srgb, var(--status-confirmed) 40%, var(--bg-surface));
  --status-pending-border:   color-mix(in srgb, var(--status-pending)   40%, var(--bg-surface));
  --status-cancelled-border: color-mix(in srgb, var(--status-cancelled) 40%, var(--bg-surface));
  --status-waitlist-border:  color-mix(in srgb, var(--status-waitlist)  40%, var(--bg-surface));
  --status-noshow-border:    color-mix(in srgb, var(--status-noshow)    40%, var(--bg-surface));
  --status-completed-border: color-mix(in srgb, var(--status-completed) 40%, var(--bg-surface));

  --ds-color-brand: var(--text-primary);
  /* v27.0.3 — page is the DEEPEST surface in dark mode, mirroring
   * the light canon where #f2f3f5 page sits beneath #ffffff panels.
   * Predecessor: #161617 — that was correct under the v26.52 5-tier
   * ladder where sidebar/chrome was even deeper at #0a0a0c, but the
   * v27 flat-canvas canon flips the relationship: panels sit ABOVE
   * the page. #0e0e10 is roughly the same perceptual gap below
   * #1d1d1f panels (~5% lightness) as #f2f3f5 below #ffffff in light. */
  --ds-color-bg-page: var(--bg-canvas);
  --ds-color-bg-canvas: var(--bg-surface);
  --ds-color-bg-subtle: var(--bg-raised);
  --ds-color-bg-muted: #2a2e36;

  /* v27.0.3 — DARK FLAT-CANVAS PARITY. The v26.52 5-tier ladder
   * (chrome #0a0a0c < toolbar-band #161618 < canvas-app #1d1d1f)
   * had the sidebar painted DARKER than the page — the "recessed
   * sidebar pit" feel that reads as a hole punched into the page.
   * In light mode v27.0 already flipped this by unifying chrome /
   * toolbar-band / canvas-app to #ffffff and pushing the page down
   * to #f2f3f5 (panels lifted, page recessed). v27.0.3 brings dark
   * mode to canon parity by unifying the same three surfaces to a
   * single lifted tone (#1d1d1f) that sits ABOVE the deeper page
   * (#0e0e10).
   *
   * Net visual: in BOTH themes, sidebar / calendar toolbar / calendar
   * grid / drawer panels read as ONE coherent lifted card surface,
   * floating above a slightly deeper backdrop. The 5-tier ladder
   * survives only in the secondary tier tokens (--ds-color-bg-canvas,
   * --ds-color-bg-subtle, --ds-color-bg-muted) which are still used
   * by inset elements like form controls, code blocks, and chips. */
  --ds-color-bg-chrome:         var(--bg-surface);  /* sidebar + drawer header */
  --ds-color-bg-toolbar-band:   var(--bg-surface);  /* calendar toolbar + drawer body */
  --ds-color-bg-canvas-app:     var(--bg-surface);  /* calendar grid (focal canvas) */

  --ds-color-bg-focus-surface:  var(--ds-color-bg-toolbar-band);

  /* Accent — brand-blue chip in dark mode. */
  --ds-color-accent-blue-bg:     rgba(59, 146, 255, 0.14);
  --ds-color-accent-blue-border: rgba(59, 146, 255, 0.34);
  --ds-color-accent-blue-text:   var(--accent);
  --ds-color-text: var(--text-primary);
  --ds-color-text-secondary: var(--text-secondary);
  /* v28.5.41 — Surface aliases for elevated cards on dark theme.
   * #2c2c2e is the standard "subtle" surface; #3a3a3c is one notch
   * up for hover lift. Both come from Apple's dark palette ladder
   * (matches --ds-color-bg-subtle / --ds-color-bg-muted). Replaces
   * the booking-cards.css L670/673 stale `#111827` / `#1f2937`
   * Tailwind-slate fallbacks. */
  --ds-color-bg-surface:        var(--bg-raised);
  --ds-color-bg-surface-hover:  #2a2e36;
  /* v28.5.41 — Alias for legacy callers. Same as --ds-color-text in
   * dark mode (#f5f5f7) but exposed under the on-dark name so
   * existing callers don't need to be rewritten. */
  --ds-color-text-on-dark:      var(--text-primary);
  --ds-color-text-tertiary: #8e8e93;
  --ds-color-text-quaternary: #636366;

  /* COLOR LAW aliases (must mirror light-mode) */
  --ds-color-text-primary: var(--ds-color-text);
  --ds-color-text-muted: var(--ds-color-text-secondary);
  --ds-color-bg-hover: rgba(255, 255, 255, 0.06);
  --ds-color-bg-pressed: rgba(255, 255, 255, 0.10);
  --ds-color-bg-selected: rgba(255, 255, 255, 0.08);
  --ds-color-border-subtle: rgba(255, 255, 255, 0.10);
  --ds-color-border: rgba(84, 84, 88, 0.65);
  --ds-color-border-strong: rgba(84, 84, 88, 0.88);
  --ds-color-border-light: var(--border-hairline);
  --ds-color-divider: var(--border-hairline);
  --ds-color-accent: var(--text-primary);
  --ds-color-accent-hover: #ffffff;
  --ds-color-accent-muted: rgba(255, 255, 255, 0.1);
  --ds-color-accent-border: rgba(255, 255, 255, 0.22);

  --ds-color-sapphire: var(--accent);
  --ds-color-sapphire-black: var(--accent-hover);

  /* Brand blue brighter on dark (#3b92ff, 5.8:1). */
  --ds-color-interactive: var(--accent);
  --ds-color-interactive-hover: var(--accent-hover);
  --ds-color-interactive-muted: rgba(59, 146, 255, 0.14);
  --ds-color-interactive-border: rgba(59, 146, 255, 0.32);
  --ds-color-focus-ring: rgba(59, 146, 255, 0.38);
  --ds-color-focus-ring-ui: var(--accent);
  /* Filled commands need white-label contrast; bright accent remains for links/focus on dark surfaces. */
  --ds-color-primary-command-bg: #075fad;
  --ds-color-primary-command-bg-hover: #004f9f;

  /* In dark mode the track uses the same alpha — works on dark surfaces. */
  --ds-color-toggle-off: rgba(120, 120, 128, 0.32);
  /* iOS toggle active dark — slightly brighter green. */
  --ds-color-toggle-on: #30d158;
  /* Semantic in dark — status hues one stop brighter. */
  --ds-color-danger: var(--status-cancelled);
  --ds-color-danger-bg: var(--status-cancelled-bg);
  --ds-color-success: var(--status-confirmed);
  --ds-color-success-bg: var(--status-confirmed-bg);
  --ds-color-success-muted: #aeaeb2;
  --ds-color-warning: #aeaeb2;
  --ds-color-warning-bg: rgba(255, 255, 255, 0.06);
  --ds-color-warning-text: #aeaeb2;
  /* Rating stars — one stop brighter brass for dark surfaces. */
  --ds-color-star: #d2a244;

  --ds-color-status-scheduled: var(--gray-400);
  --ds-color-status-confirmed: var(--status-confirmed);
  --ds-color-status-checked-in: var(--status-confirmed);
  --ds-color-status-in-progress: var(--accent);
  --ds-color-status-completed: var(--status-completed);
  --ds-color-status-cancelled: var(--status-cancelled);
  --ds-color-status-no-show: var(--status-noshow);

  --ds-color-current-time: var(--ds-color-danger);         /* dark: #ff5c54 */
  --ds-color-current-time-glow: rgba(255, 92, 84, 0.16);

  --ds-color-ios-system-green: var(--status-confirmed);
  --ds-color-apple-system-green: var(--text-primary);

  --ds-color-segmented-track: #2c2c2e;
  --ds-color-segmented-track-strong: #3a3a3c;
  --ds-shadow-segmented-thumb: 0 0 1px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.35),
    0 4px 14px rgba(0, 0, 0, 0.35);
  --ds-glass-bg: rgba(28, 28, 32, 0.82);
  --ds-glass-bg-strong: rgba(22, 22, 26, 0.92);
  --ds-glass-border: rgba(255, 255, 255, 0.08);

  --ds-color-brand-logo-bg: var(--accent-soft);
  --ds-color-brand-bolt: var(--accent);

  /* Today CTA in dark: light fill on dark (monochrome invert). */
  --ds-color-toolbar-cta-bg: var(--ds-color-interactive);
  --ds-color-toolbar-cta-border: var(--ds-color-interactive);
  --ds-color-toolbar-cta-bg-hover: var(--ds-color-interactive-hover);
  --ds-color-toolbar-cta-border-hover: var(--ds-color-interactive-hover);
  --ds-color-toolbar-cta-text: #ffffff;
  --ds-shadow-toolbar-cta: 0 2px 14px rgba(0, 0, 0, 0.4);

  /* Mini-calendar selected day — brand blue, bright for dark bg. */
  --ds-color-selection-fill: var(--accent);
  --ds-color-selection-shadow: rgba(59, 146, 255, 0.38);

  --ds-split-pill-border: rgba(255, 255, 255, 0.12);
  --ds-split-pill-body-bg: var(--ds-color-bg-canvas);
  --ds-split-pill-accent-from: #aeaeb2;
  --ds-split-pill-accent-to: #f5f5f7;

  --ds-appts-header-blur: blur(22px) saturate(108%);

  --ds-surface-canvas: var(--ds-color-bg-canvas);
  --ds-surface-card: var(--ds-color-bg-canvas);
  --ds-surface-raised: #1d1d1f;
  --ds-surface-tint: #2c2c2e;
  --ds-surface-floating-strong: #1c1c20; /* ODS v1.2 Phase 1 — opaque; was rgba(28,28,32,0.94) */

  /* ── ODS v1.2 Phase 1 dark overrides ────────────────────────────── */
  --ods-focus-color:        var(--accent);
  --ods-focus-ring-outline: 2px solid var(--ods-focus-color);
  --ods-focus-ring-shadow:  0 0 0 3px rgba(59, 146, 255, 0.22);
  --ds-color-primary:       var(--ods-accent-dark); /* bridges draft color-mix → brand blue on dark */
  --ds-surface-border-subtle: rgba(255, 255, 255, 0.1);
  --ds-surface-border-strong: rgba(255, 255, 255, 0.18);
  --ds-surface-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --ds-surface-shadow-md: 0 10px 36px rgba(0, 0, 0, 0.48), 0 2px 10px rgba(0, 0, 0, 0.32);

  /* Dark liquid atmosphere — monochrome ink shadow field. */
  --ds-cal-atmos-bg-top: #0f0f10;
  --ds-cal-atmos-bg-mid: #161617;
  --ds-cal-atmos-bg-bottom: #1d1d1f;
  --ds-cal-atmos-orb-a: radial-gradient(circle, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.01) 100%);
  --ds-cal-atmos-orb-b: radial-gradient(circle, rgba(29, 29, 31, 0.6) 0%, rgba(29, 29, 31, 0.18) 100%);
  --ds-cal-liquid-outer-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 3px 10px rgba(0, 0, 0, 0.3);

  /* Shell-frame dark variants (sidebar + workspace card + drawer all
   * read these, kept synchronised with the light values above so the
   * three panels share one dark visual language). v26.45: sidebar
   * background overridden to #0e0e10 in shell.css — workspace card
   * keeps canvas color for contrast separation. */
  --ds-shell-panel-bg: var(--ds-color-bg-canvas);
  --ds-shell-panel-bg-solid: var(--ds-color-bg-canvas);
  --ds-shell-panel-border: 0.5px solid rgba(255, 255, 255, 0.05);
  --ds-shell-panel-shadow:
    0 20px 50px rgba(0, 0, 0, 0.45),
    0 1px 2px rgba(0, 0, 0, 0.25);

  /* v26.53 — FLOATING SHELL FRAME (dark). Token indirection makes
   * --ollira-frame-bg / -border / -shadow track the dark redefinitions
   * of --ds-color-bg-page / --ds-shell-panel-border / --ds-shell-panel-shadow.
   * Re-declared at literal level so the floating-shell verifier detects
   * dark coverage without walking var() chains. */
  --ollira-frame-bg: var(--ds-color-bg-page);
  --ollira-frame-border: 0;
  --ollira-frame-shadow: none;
}

/* ════════════════════════════════════════════════════════════════════
 * v27.0 — FLAT-CANVAS TIER DIVIDERS (suppression).
 *
 * Three hairlines were calibrated for the v26.52 5-tier ladder where
 * chrome and toolbar-band tiers were tonally different and a 1px
 * hairline at the divider color read as a refined seam between them.
 * v27.0 flattened both tiers in LIGHT mode to #ffffff (see top of
 * file), so those same hairlines rendered as a visible gray line
 * floating on identical-white surfaces — the exact "extra strokes"
 * v27.0 was removing.
 *
 * v27.0.3 — extended to DARK mode. Dark now also unifies chrome /
 * toolbar-band / canvas-app at #1d1d1f (see html[data-app-theme="dark"]
 * block above), so the same hairlines also read as "extra strokes
 * on identical-dark surfaces" in dark mode. The suppression block
 * below now applies in BOTH themes (selectors changed from
 * `:root:not([data-app-theme="dark"])` to plain `:root`).
 *
 * Three suppression targets:
 *   1. .appts-calendar-control-surface--premium  (calendar toolbar
 *      bottom border — separates toolbar-band from canvas-app, both
 *      now unified)
 *   2. .app-drawer__header / __footer           (overlay-drawer
 *      header bottom + footer top — separates chrome from
 *      toolbar-band inside the drawer panel, both now unified)
 *   3. .appt-drawer__hd::after                  (booking-drawer
 *      header bottom hairline — same role as above for the booking
 *      flow's custom in-panel header)
 *
 * Single-file revert: `git revert` this commit restores the v26.58
 * tier values + the hairlines; no module CSS was touched.
 * ════════════════════════════════════════════════════════════════════ */
:root .appts-calendar-control-surface--premium {
  border-bottom-color: transparent;
}
:root .app-drawer__header,
:root .app-drawer__footer {
  border-bottom-color: transparent;
  border-top-color: transparent;
}
:root .appt-drawer__hd::after {
  background: transparent;
}

/* Appointment status ink and badges. Booking-card fills use the separate
   --appt-card-* palette below so all calendar views share the same cards. */
:root {
  --appt-status-booked-bg: #D8E8FE;
  --appt-status-booked-fg: #075FBE;
  --appt-status-confirmed-bg: #BDD7FC;
  --appt-status-confirmed-fg: #153E8F;
  --appt-status-arrived-bg: #FEE4BF;
  --appt-status-arrived-fg: #96530C;
  --appt-status-started-bg: #C8EBCF;
  --appt-status-started-fg: #12692F;
  --appt-status-completed-bg: #EAECEF;
  --appt-status-completed-fg: #565E6B;
  --appt-status-danger-bg: #FBE1DF;
  --appt-status-danger-fg: #B0392C;
  /* Booking-card fills sampled from the six-card reference. Status badges
     retain their semantic colours above; card edges use paired darker hues. */
  --appt-card-booked-bg: #DFF3FE;
  --appt-card-confirmed-bg: #E9E0FD;
  --appt-card-arrived-bg: #FEEECD;
  --appt-card-started-bg: #CCF6DE;
  --appt-card-completed-bg: #CDD4FE;
  --appt-card-pending-bg: #FEEECD;
  --appt-card-danger-bg: #FED6CE;
  --appt-card-ink: #1C2024;
  --appt-card-booked-rail: #87CBEF;
  --appt-card-confirmed-rail: #BBA6EA;
  --appt-card-arrived-rail: #E5C276;
  --appt-card-started-rail: #79D0A8;
  --appt-card-completed-rail: #96A7EA;
  --appt-card-danger-rail: #EAA194;
}

html[data-app-theme="dark"], html.dark {
  --appt-status-booked-bg: #1E2E4F;
  --appt-status-booked-fg: #C3D3F2;
  --appt-status-confirmed-bg: #274495;
  --appt-status-confirmed-fg: #C6D6FB;
  --appt-status-arrived-bg: #432B10;
  --appt-status-arrived-fg: #F3C285;
  --appt-status-started-bg: #1F3A2E;
  --appt-status-started-fg: #BFE0CE;
  --appt-status-completed-bg: #262A30;
  --appt-status-completed-fg: #AEB8C6;
  --appt-status-danger-bg: #3A2628;
  --appt-status-danger-fg: #E8B8BC;
  --appt-card-booked-bg: #203C4A;
  --appt-card-confirmed-bg: #39304F;
  --appt-card-arrived-bg: #4B342A;
  --appt-card-started-bg: #2C584D;
  --appt-card-completed-bg: #25453E;
  --appt-card-pending-bg: #4B342A;
  --appt-card-danger-bg: #4B342A;
  --appt-card-ink: #E8EDF0;
  --appt-card-booked-rail: #28739A;
  --appt-card-confirmed-rail: #725BB6;
  --appt-card-arrived-rail: #9F5A36;
  --appt-card-started-rail: #287A61;
  --appt-card-completed-rail: #286650;
  --appt-card-danger-rail: #845154;
}

/* Legacy commands still set local pill/circle radii. Keep their geometry in
   the same 6px button family without changing avatars, badges or switches. */
:where(button:not([role="switch"], [role="checkbox"]), input[type="button"], input[type="submit"], input[type="reset"], [role="button"], a.btn, a[class*="-btn"], a[class*="button"], a[class*="cta"]) {
  --ds-button-radius: var(--ds-radius-sm) !important;
  border-radius: var(--ds-button-radius) !important;
}
