    @font-face {
      font-family: 'JetBrains Mono';
      src: url('/static/fonts/JetBrainsMono-SemiBold.woff2') format('woff2');
      font-weight: 600;
      font-style: normal;
      font-display: swap;
    }

    :root {
      --slate-950: #1a1a18;
      --slate-900: #2C2C2A;
      --slate-800: #444441;
      --slate-600: #5F5E5A;
      --slate-500: #6F6E69;
      --slate-400: #888780;
      --slate-200: #B4B2A9;
      --slate-100: #D3D1C7;
      --slate-50:  #F1EFE8;
      --ember-600: #854F0B;
      --ember-500: #BA7517;
      --ember-400: #EF9F27;
      --ember-100: #FAC775;
      --ember-50:  #FAEEDA;
      --teal-600:  #0F6E56;
      --teal-400:  #1D9E75;
      --teal-50:   #E1F5EE;
      --red-500:   #EF4444;
      --red-400:   #E24B4A;
      --red-50:    #FCEBEB;
      --emerald-500: #10B981;
      --blue-600:  #185FA5;
      --blue-50:   #E6F1FB;
      --cyan-400:  #22d3ee;
      --white:     #ffffff;
      --font-sans: -apple-system, 'Segoe UI', sans-serif;

      /* z-index scale */
      --z-sticky:   1;
      --z-raised:   2;
      --z-drawer:   5;
      --z-fixed:   50;
      --z-dropdown: 100;
      --z-overlay: 200;
      --z-tooltip: 150;
      --z-modal:  1000;

      /* Spacing scale — use these tokens, never ad-hoc px values */
      --space-1: 4px;
      --space-2: 8px;
      --space-3: 12px;
      --space-4: 16px;
      --space-5: 24px;
      --space-6: 32px;

      /* Shadow scale */
      --shadow-card:   0 1px 3px rgba(0,0,0,0.06);
      --shadow-raised: 0 2px 8px rgba(0,0,0,0.08);
      --shadow-modal:  0 4px 12px rgba(0,0,0,0.12);
    }

    * { margin: 0; padding: 0; box-sizing: border-box; }
    html, body { height: 100%; overflow: hidden; }
    body { font-family: var(--font-sans); background: var(--slate-50); color: var(--slate-800); }

    .hidden { display: none !important; }
    .clickable { cursor: pointer; }
    .pmc-value-title { font-size: 16px; }

    /* ── Utility classes ─────────────────────────────────────────────── */

    /* Buttons — base + variants */
    .btn {
      font-family: var(--font-sans);
      font-size: 14px;
      font-weight: 500;
      border: none;
      border-radius: 8px;
      min-height: 44px;
      padding: 0 var(--space-4);
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
    }
    .btn:disabled { opacity: 0.5; cursor: not-allowed; }
    .btn--primary { background: var(--slate-900); color: var(--ember-400); }
    .btn--primary:hover { opacity: 0.9; }
    .btn--accent { background: var(--ember-500); color: var(--white); }
    .btn--accent:hover { opacity: 0.9; }
    .btn--accent:disabled { opacity: 0.6; cursor: not-allowed; }
    .btn--secondary {
      background: var(--white);
      color: var(--slate-800);
      border: 1px solid var(--slate-200);
    }
    .btn--secondary:hover { background: var(--slate-50); }
    .btn--ghost { background: none; color: var(--slate-600); }
    .btn--ghost:hover { color: var(--slate-800); }
    .btn--danger { background: var(--red-50); color: var(--red-400); }
    .btn--danger:hover { opacity: 0.9; }
    .btn--sm {
      min-height: 36px;
      font-size: 12px;
      border-radius: 4px;
      padding: 0 var(--space-3);
    }

    /* Cards — base + variants */
    .card {
      background: var(--white);
      border: 0.5px solid var(--slate-100);
      border-radius: 10px;
    }
    .card--accent { background: var(--ember-50); border-color: var(--ember-100); }
    .card--muted { background: var(--slate-50); border-color: transparent; }

    /* Form inputs */
    .input {
      font-size: 16px;
      padding: var(--space-2) 10px;
      border: 0.5px solid var(--slate-100);
      border-radius: 4px;
      background: var(--white);
      color: var(--slate-800);
      font-family: inherit;
      box-sizing: border-box;
      width: 100%;
    }
    .input:focus {
      outline: none;
      border-color: var(--ember-400);
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--ember-400) 20%, transparent);
    }
    .input:focus-visible {
      outline: 2px solid var(--ember-400);
      outline-offset: 2px;
    }

    /* Labels — uppercase micro text */
    .label {
      font-size: 11px;
      font-weight: 500;
      color: var(--slate-600);
      text-transform: uppercase;
      letter-spacing: 0.06em;
    }

    /* ── Wordmark ─────────────────────────────────────────────────────
       The "why am i slow?" brand lockup. JetBrains Mono 600, self-hosted
       from /static/fonts/ via the @font-face declared above.
       `font-display: swap` falls back to system mono until the web font
       arrives, so the layout never blocks. The ember question mark is
       the signature accent — do not recolour. */
    .wordmark {
      font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-weight: 600;
      letter-spacing: -0.04em;
    }
    .wordmark__mark { color: var(--ember-400); }

    /* Inline wordmark — tab bar, 44px dark chrome. */
    .app-name.wordmark--inline {
      font-size: 15px;
      line-height: 1;
      color: var(--slate-50);
    }

    /* Stacked wordmark — login / splash hero. Block is centred within
       its parent; lines inside are left-aligned so "why", "am i", "slow?"
       share a common left edge. */
    h2.wordmark--stacked {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      width: fit-content;
      font-size: 44px;
      line-height: 0.92;
      color: var(--slate-900);
      text-align: left;
      margin: 0 auto var(--space-4);
    }
    h2.wordmark--stacked .wordmark__line { display: block; }

    /* ── Tab bar ── */
    .tab-bar {
      background: var(--slate-900);
      height: 44px;
      display: flex;
      align-items: center;
      padding: 0 var(--space-4);
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      z-index: var(--z-fixed);
    }
    .tab-bar .app-name {
      margin-right: auto;
    }
    .tab-bar .tabs {
      display: flex;
      gap: 0;
    }
    .tab-bar .tab {
      font-size: 14px;
      font-weight: 500;
      color: var(--slate-400);
      background: none;
      border: none;
      border-bottom: 2px solid transparent;
      padding: 10px var(--space-4);
      cursor: pointer;
      line-height: 22px;
    }
    .tab-bar .tab.active {
      color: var(--ember-400);
      background: rgba(239, 159, 39, 0.1);
      border-bottom-color: transparent;
      border-radius: 4px;
    }
    .tab-bar .tab:hover:not(.active) {
      color: var(--slate-200);
    }
    .beta-marker {
      color: var(--ember-400);
      font-size: 9px;
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      background: var(--slate-800);
      border-radius: 4px;
      padding: 1px var(--space-1);
      margin-left: var(--space-1);
      position: relative;
      top: -1px;
      cursor: help;
    }
    .athlete-pill {
      display: flex;
      align-items: center;
      gap: 6px;
      background: var(--slate-800);
      border-radius: 20px;
      padding: var(--space-1) var(--space-3) var(--space-1) var(--space-2);
      margin-left: var(--space-4);
      cursor: pointer;
      border: none;
    }
    .athlete-pill .dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--ember-400);
    }
    .athlete-pill .name {
      font-size: 14px;
      color: var(--slate-200);
      font-weight: 400;
    }
    .athlete-pill .chevron {
      font-size: 12px;
      color: var(--slate-400);
      margin-left: var(--space-1);
    }
    .athlete-menu { position: relative; margin-left: var(--space-4); }
    .athlete-pill { margin-left: 0; }
    .athlete-dropdown {
      position: absolute;
      top: calc(100% + 4px);
      right: 0;
      background: var(--white);
      border: 0.5px solid var(--slate-100);
      border-radius: 8px;
      min-width: 120px;
      z-index: var(--z-dropdown);
      opacity: 1;
      transform: translateY(0) scale(1);
      transition: opacity 0.15s ease, transform 0.15s ease;
    }
    .athlete-dropdown[data-state="closed"] {
      opacity: 0;
      transform: translateY(-4px) scale(0.97);
      pointer-events: none;
    }
    .athlete-dropdown button,
    .athlete-dropdown a {
      display: block;
      width: 100%;
      padding: 10px 14px;
      border: none;
      background: none;
      font-size: 14px;
      font-family: var(--font-sans);
      color: var(--slate-800);
      cursor: pointer;
      text-align: left;
      border-radius: 8px;
      text-decoration: none;
      box-sizing: border-box;
    }
    .athlete-dropdown button:hover,
    .athlete-dropdown a:hover { background: var(--slate-50); }
    .athlete-dropdown .dropdown-separator {
      border: none;
      border-top: 0.5px solid var(--slate-100);
      margin: var(--space-1) 0;
    }
    .athlete-dropdown button + button,
    .athlete-dropdown button + a,
    .athlete-dropdown a + button,
    .athlete-dropdown a + a {
      border-top: 0.5px solid var(--slate-100);
      border-radius: 0 0 8px 8px;
    }
    .athlete-dropdown :first-child {
      border-radius: 8px 8px 0 0;
    }
    .athlete-dropdown :last-child {
      border-radius: 0 0 8px 8px;
    }
    .athlete-dropdown :only-child {
      border-radius: 8px;
    }
    .oura-status-line {
      display: block;
      padding: 10px 14px;
      font-size: 14px;
      color: var(--slate-600);
    }
    .oura-dot { color: var(--ember-400); }

    /* ── Login ── */
    .login-screen {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      min-height: 100dvh;
      padding-top: 44px;
      padding-bottom: env(safe-area-inset-bottom);
      background: var(--slate-50);
    }
    .login-footer {
      margin-top: var(--space-4);
      font-size: 12px;
      color: var(--slate-400);
      text-align: center;
    }
    .login-footer a {
      color: var(--slate-400);
      text-decoration: none;
    }
    .login-footer a:hover { color: var(--slate-600); }
    .login-footer .strava-attribution {
      margin-top: var(--space-3);
    }
    .strava-badge {
      height: 14px;
      width: auto;
      opacity: 0.6;
      transition: opacity 0.15s ease;
    }
    .strava-badge:hover {
      opacity: 1;
    }
    .app-footer {
      text-align: center;
      padding: var(--space-3) 0;
      font-size: 12px;
      color: var(--slate-400);
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
    }
    .app-footer a { color: var(--slate-400); text-decoration: none; }
    .app-footer a:hover { color: var(--slate-600); }
    .app-footer-sep { color: var(--slate-300); }
    .login-card {
      background: var(--white);
      border-radius: 12px;
      border: 0.5px solid var(--slate-100);
      padding: var(--space-6);
      width: 100%;
      max-width: 360px;
    }
    .login-card h2 {
      font-size: 22px;
      font-weight: 500;
      color: var(--slate-900);
      text-align: center;
      margin-bottom: var(--space-1);
    }
    .login-card .subtitle {
      font-size: 14px;
      color: var(--slate-400);
      text-align: center;
      margin-bottom: var(--space-5);
      min-height: 2.6em;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .login-card .subtitle--typing::after {
      content: '|';
      display: inline-block;
      margin-left: 2px;
      color: var(--slate-400);
      animation: subtitle-cursor 0.9s steps(1) infinite;
    }
    @keyframes subtitle-cursor {
      50% { opacity: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .login-card .subtitle--typing::after { display: none; }
    }
    .login-card input:not([type="checkbox"]),
    .login-card select {
      width: 100%;
      padding: var(--space-2) var(--space-3);
      margin-bottom: var(--space-3);
      border: 0.5px solid var(--slate-200);
      border-radius: 6px;
      font-size: 16px;
      font-family: var(--font-sans);
      min-height: 44px;
      color: var(--slate-800);
      background: var(--white);
    }
    .login-card input::placeholder {
      color: var(--slate-400);
    }
    .login-card .login-btn { width: 100%; }
    .login-error {
      color: var(--red-400);
      font-size: 13px;
      margin-top: var(--space-2);
      text-align: center;
    }
    .login-spinner {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-2);
      margin-top: var(--space-3);
      font-size: 13px;
      color: var(--slate-400);
    }
    .mini-spinner {
      width: 16px;
      height: 16px;
      border: 2px solid var(--slate-100);
      border-top-color: var(--slate-900);
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }

    /* ── Signup form (consent checkboxes + toggle link) ── */
    /* Headings inside signup/pending views need breathing room because there's
       no .subtitle below them (unlike the main login view), so the default
       tight margin-bottom on .login-card h2 leaves the heading crammed
       against the first input. */
    #signupView h2,
    #signupPendingPanel h2 {
      margin-top: var(--space-2);
      margin-bottom: 20px;
    }
    .checkbox-row {
      display: flex;
      align-items: flex-start;
      gap: var(--space-2);
      margin: var(--space-2) 0;
      text-align: left;
      font-size: 14px;
      line-height: 1.4;
      color: var(--slate-800);
    }
    .checkbox-row input[type="checkbox"] {
      width: 18px;
      height: 18px;
      margin: 2px 0 0 0;
      flex-shrink: 0;
      accent-color: var(--slate-900);
      cursor: pointer;
    }
    .login-signup-prompt {
      margin-top: var(--space-3);
      font-size: 14px;
      text-align: center;
      color: var(--slate-600);
    }

    /* ── Error banner ── */
    .error-banner {
      position: fixed;
      top: 48px;
      left: 50%;
      transform: translateX(-50%);
      background: var(--red-50);
      border: 0.5px solid var(--red-400);
      color: var(--red-400);
      font-size: 12px;
      padding: var(--space-2) var(--space-4);
      border-radius: 8px;
      z-index: var(--z-dropdown);
      max-width: 90%;
    }

    /* ── Inline load-error state (renderLoadError) ── */
    .load-error {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: var(--space-3);
      padding: var(--space-6) var(--space-4);
      background: var(--white);
      border: 0.5px solid var(--red-400);
      border-radius: 12px;
      color: var(--slate-600);
      text-align: center;
    }
    .load-error-message {
      margin: 0;
      font-size: 13px;
      color: var(--red-400);
    }
    .load-error-retry {
      appearance: none;
      background: var(--red-400);
      color: var(--white);
      border: none;
      border-radius: 6px;
      font-size: 12px;
      font-weight: 500;
      padding: var(--space-2) var(--space-4);
      cursor: pointer;
      transition: background-color 150ms ease;
    }
    .load-error-retry:hover { background: var(--red-500); }
    .load-error-retry:focus-visible {
      outline: 2px solid var(--red-400);
      outline-offset: 2px;
    }

    /* ── Empty state component ──
       Canonical "nothing to show here" surface. Per DESIGN.md and issue
       #2168, the anatomy is icon (optional) → title → body → action
       (optional). Slate palette only — empty states are not calls to
       action, so no ember accent. Title contrast uses --slate-600 (passes
       WCAG AA at 14px); body uses --slate-400 for the muted hierarchy.

       Sizes:
         (default) — surface-level empty (Plan list, Data activity table)
         --sm      — widget-level empty (sport balance, sidebar lists)
         --lg      — full-surface empty (Chat first session, first-time load)

       Icon: optional, 32px, drawn with mask-image so it inherits the
       --slate-400 colour via background-color. Surfaces add icons by
       composing a modifier class (e.g. `.empty-state__icon--footprints`)
       that sets `mask-image`; absent that, the icon slot collapses.

       Replaces per-tab patterns (.sb-empty, .notes-empty, .plan-empty-state,
       .table-empty-cell, .trends-empty, .dash-sport-empty). Aliases remain
       in styles.css for one cycle so existing markup keeps working. */
    .empty-state {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: var(--space-2);
      padding: var(--space-5) var(--space-4);
      text-align: center;
      color: var(--slate-600);
    }
    .empty-state--sm {
      gap: var(--space-1);
      padding: var(--space-3) var(--space-2);
    }
    .empty-state--lg {
      gap: var(--space-3);
      padding: 48px var(--space-4);
    }
    .empty-state__icon {
      width: 32px;
      height: 32px;
      background-color: var(--slate-400);
      -webkit-mask-repeat: no-repeat;
              mask-repeat: no-repeat;
      -webkit-mask-size: contain;
              mask-size: contain;
      -webkit-mask-position: center;
              mask-position: center;
      flex-shrink: 0;
    }
    .empty-state--sm .empty-state__icon { width: 24px; height: 24px; }
    .empty-state__icon--footprints {
      -webkit-mask-image: url('/static/icons/footprints.svg');
              mask-image: url('/static/icons/footprints.svg');
    }
    .empty-state__title {
      margin: 0;
      font-size: 14px;
      font-weight: 500;
      color: var(--slate-600);
    }
    .empty-state--sm .empty-state__title { font-size: 13px; }
    .empty-state__body {
      margin: 0;
      font-size: 13px;
      font-weight: 400;
      /* --slate-400 per DESIGN.md spec. The body is secondary
         supporting detail; the title carries the meaning at AA contrast. */
      color: var(--slate-400);
      max-width: 32ch;
      line-height: 1.5;
    }
    .empty-state--sm .empty-state__body { font-size: 12px; }
    .empty-state__action {
      margin-top: var(--space-2);
    }

    /* ── Re-auth banner (Oura refresh-token expired) ── */
    .reauth-banner {
      position: fixed;
      top: 48px;
      left: 50%;
      transform: translateX(-50%);
      background: var(--ember-50);
      border: 0.5px solid var(--ember-400);
      color: var(--ember-600);
      font-size: 12px;
      padding: var(--space-2) var(--space-4);
      border-radius: 8px;
      z-index: var(--z-dropdown);
      max-width: 90%;
      display: flex;
      align-items: center;
      gap: var(--space-2);
    }
    .reauth-banner-link {
      background: transparent;
      border: none;
      color: var(--ember-600);
      font-size: 12px;
      font-weight: 600;
      padding: 0;
      cursor: pointer;
      text-decoration: underline;
    }
    .reauth-banner-link:hover,
    .reauth-banner-link:focus-visible {
      color: var(--ember-500);
    }
    /* Info variant (issue #1202) — lower severity, neutral slate palette */
    .reauth-banner--info {
      background: var(--slate-50);
      border-color: var(--slate-200);
      color: var(--slate-600);
    }

    /* ══════════════════════════════════════════
       TOAST — queued notification system (issue #2186)
       ══════════════════════════════════════════
       Top-centre stack. Multiple toasts queue vertically; each enters and
       leaves independently. `data-state` drives the transitions:
         "entering" → "visible" → "leaving"
       Variants: info (default), success, error, loading (with spinner).

       JS: api/static/js/toast.js (`Toast.show(message, opts)`). The
       container is auto-created on first show. Reduced-motion swaps the
       slide for a cross-fade. ══════════════════════════════════════════ */
    .toast-container {
      position: fixed;
      top: 52px;
      left: 50%;
      transform: translateX(-50%);
      z-index: var(--z-dropdown);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: var(--space-2);
      pointer-events: none;
      max-width: min(360px, calc(100vw - 2 * var(--space-4)));
    }
    .toast {
      background: var(--white);
      border: 0.5px solid var(--slate-100);
      border-radius: 8px;
      padding: var(--space-2) var(--space-4);
      display: flex;
      align-items: center;
      gap: var(--space-2);
      font-size: 13px;
      color: var(--slate-600);
      box-shadow: var(--shadow-card);
      pointer-events: auto;
      opacity: 0;
      transform: translateY(-8px);
      transition: opacity 200ms ease, transform 200ms ease;
    }
    .toast[data-state="visible"] {
      opacity: 1;
      transform: translateY(0);
    }
    .toast[data-state="leaving"] {
      opacity: 0;
      transform: translateY(-8px);
    }
    .toast--success {
      border-color: var(--ember-400);
      color: var(--ember-600);
    }
    .toast--error {
      border-color: var(--red-400);
      color: var(--red-400);
    }
    .toast__spinner {
      flex-shrink: 0;
    }
    .toast__message {
      flex: 1;
      min-width: 0;
    }
    @media (prefers-reduced-motion: reduce) {
      .toast {
        transform: none;
        transition: opacity 120ms ease;
      }
      .toast[data-state="leaving"] { transform: none; }
    }

    /* ══════════════════════════════════════════
       SHEET — unified drawer/sheet base (issue #2187)
       ══════════════════════════════════════════
       Slide-over surface with three side variants:
         .sheet--right   — slides in from the right (desktop drawers)
         .sheet--left    — slides in from the left
         .sheet--bottom  — slides up from the bottom (mobile sheets)

       State driven by `data-state="open" | "closed"` (shadcn/ui pattern,
       see DESIGN.md "Vanilla JS implementation pattern").

       Anatomy:
         <div class="sheet sheet--right" data-state="closed">
           <div class="sheet__backdrop"></div>
           <div class="sheet__panel" role="dialog" aria-modal="true"
                aria-labelledby="...">
             <header class="sheet__header">
               <h2 class="sheet__title" id="...">Title</h2>
               <button class="sheet__close" aria-label="Close">×</button>
             </header>
             <div class="sheet__body">...</div>
             <footer class="sheet__footer">...</footer>  <!-- optional -->
           </div>
         </div>

       JS contract — see api/static/js/sheet.js (`Sheet.bind(el, opts)`).
       Surfaces migrate their per-instance drawers to this base during
       overhaul PRs (#2170–#2175 — see migration table on issue #2187).
       ══════════════════════════════════════════ */
    .sheet {
      position: fixed;
      inset: 0;
      z-index: var(--z-modal);
      pointer-events: none;
    }
    .sheet__backdrop {
      position: absolute;
      inset: 0;
      background: rgba(0, 0, 0, 0.4);
      opacity: 0;
      transition: opacity 200ms ease;
      pointer-events: none;
    }
    .sheet__panel {
      position: absolute;
      background: var(--white);
      box-shadow: var(--shadow-modal);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      transition: transform 250ms ease;
      pointer-events: none;
    }

    /* ── Side variants ── */
    .sheet--right .sheet__panel {
      top: 0; right: 0; bottom: 0;
      width: 340px;
      max-width: 90vw;
      transform: translateX(100%);
    }
    .sheet--left .sheet__panel {
      top: 0; left: 0; bottom: 0;
      width: 340px;
      max-width: 90vw;
      transform: translateX(-100%);
    }
    .sheet--bottom .sheet__panel {
      left: 0; right: 0; bottom: 0;
      max-height: 70vh;
      border-radius: 12px 12px 0 0;
      padding-bottom: env(safe-area-inset-bottom);
      transform: translateY(100%);
    }

    /* ── Open state ── */
    .sheet[data-state="open"] {
      pointer-events: auto;
    }
    .sheet[data-state="open"] .sheet__backdrop {
      opacity: 1;
      pointer-events: auto;
    }
    .sheet[data-state="open"] .sheet__panel {
      transform: translate(0, 0);
      pointer-events: auto;
    }

    /* ── Anatomy ── */
    .sheet__header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-3);
      padding: 14px 20px 10px;
      border-bottom: 0.5px solid var(--slate-100);
      flex-shrink: 0;
    }
    .sheet__title {
      font-size: 15px;
      font-weight: 500;
      color: var(--slate-900);
      margin: 0;
    }
    .sheet__body {
      flex: 1;
      overflow-y: auto;
      padding: var(--space-4) 20px;
    }
    .sheet__footer {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      gap: var(--space-2);
      padding: var(--space-3) 20px;
      border-top: 0.5px solid var(--slate-100);
    }
    .sheet__close {
      background: none;
      border: none;
      font-size: 20px;
      line-height: 1;
      color: var(--slate-400);
      cursor: pointer;
      padding: var(--space-1);
      border-radius: 4px;
      position: relative;
      transition: color 120ms ease;
    }
    .sheet__close::after {
      content: '';
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      min-width: 44px;
      min-height: 44px;
    }
    .sheet__close:hover { color: var(--slate-700); }
    .sheet__close:focus-visible {
      outline: 2px solid var(--ember-400);
      outline-offset: 2px;
    }

    /* ── Reduced motion: skip slide, cross-fade panel instead ── */
    @media (prefers-reduced-motion: reduce) {
      .sheet__backdrop { transition: opacity 120ms ease; }
      .sheet__panel { transition: opacity 120ms ease; }
      .sheet--right .sheet__panel,
      .sheet--left .sheet__panel,
      .sheet--bottom .sheet__panel {
        transform: none;
        opacity: 0;
      }
      .sheet[data-state="open"] .sheet__panel { opacity: 1; }
    }

/* ── Chart primitives ──
   Shared by Trends (PMC, HRV, sleep, volume) and Data (detail-row charts) via
   chartUtils.js. Promoted to base.css from styles.css per #2227 so cross-surface
   chart helpers live alongside other shared primitives. */
.chart-axis-line { stroke: var(--slate-100); stroke-width: 1; }
.chart-zero-line { stroke: var(--slate-100); stroke-width: 1; opacity: 0.6; }
.chart-axis-tick text { fill: var(--slate-600); font-size: 10px; font-variant-numeric: tabular-nums; }
.chart-gridline { stroke: var(--slate-100); stroke-width: 1; opacity: 0.4; }
.chart-hairline { stroke: var(--slate-600); stroke-width: 1; stroke-dasharray: 2,2; pointer-events: none; }

.chart-tooltip-host { position: relative; }
.chart-tooltip {
  position: absolute;
  top: 0;
  transform: translate(calc(var(--tt-x, 0px) - 50%), -100%);
  background: var(--slate-50);
  border: 1px solid var(--slate-100);
  border-radius: 6px;
  padding: 0.375rem 0.625rem;
  font-size: 0.8125rem;
  color: var(--slate-800);
  white-space: nowrap;
  pointer-events: none;
  z-index: var(--z-tooltip, 150);
  box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.chart-tooltip--top { transform: translate(-50%, 0); top: -0.5rem; left: 50%; }
.chart-tooltip[hidden] { display: none; }

/* Delta-vs-baseline colour states — used by Home Readiness, Trends sub-lines,
   Data activity TSS hint, Profile season tiles, Plan header cards.
   Inline-friendly — colour only. Panel chrome (background, border) for the
   Home Readiness paragraph lives under .dash-baseline in css/home.css. */
.baseline-good    { color: var(--teal-600); }
.baseline-poor    { color: var(--red-400); }
.baseline-neutral { color: var(--slate-600); }
