@charset "UTF-8";
/* =============================================================
   NORA APP — Milestone 1
   V21 post-quiz workspace · three-zone layout (chat / dash / drawer)
   Tokens duplicated from V19 SHC-Match canonical (lemon.css theme G)
   ============================================================= */

/* --- design tokens (V23 canonical SHC-Match — white/gold/teal/navy) ---
   These mirror lemon.css :root values so nora-app surfaces inherit the
   exact same palette as the homepage. Variable names are historical;
   actual values are SHC-Match (--pink → teal #00B5D8, NOT coral). */
:root {
  --ink:          #0E2A3A;       /* deep navy */
  --cream:        #FFFFFF;       /* true white */
  --blush:        #FFFFFF;       /* white surface */
  --blush-2:      #EAF6F8;       /* icy teal-tinted secondary */
  --lemon:        #FFC857;       /* warm gold (primary highlight) */
  --lemon-2:      #FFE2A0;       /* light gold */
  --pink:         #00B5D8;       /* teal-cyan (primary CTA) */
  --pink-dark:    #007A93;       /* deep teal (hover/pressed) */
  --mute:         #4A6678;       /* slate · WCAG AA on white (was #5D7C8E pre-Tier-7) */
  --rule:         #DDE9EE;       /* rule lines */
  --mute-bg:      rgba(14,42,58,0.04);
  --mint:         #61C9A8;       /* success/accent */
  --plum:         #001F2E;       /* deepest navy */
  --sky:          #BFE9F0;       /* light sky teal */

  --shadow-2:     2px 2px 0 var(--ink);
  --shadow-3:     3px 3px 0 var(--ink);
  --shadow-4:     4px 4px 0 var(--ink);
  --shadow-6:     6px 6px 0 var(--ink);

  /* V24 Tier 5 motion DNA — ONE motion vocabulary across all surfaces */
  --ease:         cubic-bezier(.22,.61,.36,1);   /* swift settle */
  --t-quick:      180ms var(--ease);             /* hover, micro-interactions */
  --t-small:      280ms var(--ease);             /* state changes, fades */
  --t-medium:     460ms var(--ease);             /* reveals, entrances */
  --t-large:      720ms var(--ease);             /* big reveals, lock celebration */
  --t-state:      .9s var(--ease);               /* legacy alias for big stage transitions */
  --stagger:      60ms;                          /* cascade between items */
}

/* --- global ------------------------------------------------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { background: var(--cream); }

/* V27 F10 — universal `[hidden]` attribute respect. Without this, author CSS
   like `.nx-account { display: grid }` overrides the UA `[hidden] { display: none }`
   stylesheet (specificity tie + cascade order), which renders elements that
   were marked hidden in markup. Forcing `!important` keeps the contract. */
[hidden] { display: none !important; }
body {
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--cream);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
  min-height: 100svh;
  overflow-x: hidden;
}
.serif { font-family: 'Fraunces', Georgia, serif; }
.mono  { font-family: 'JetBrains Mono', ui-monospace, monospace; }
b, strong { font-weight: 600; }
button { font: inherit; cursor: pointer; }
input, button { font-family: inherit; }
/* V24 Tier 6 · Brand-aligned keyboard focus.
   Gold ring (--lemon) at 3px is highly visible and on-brand. Uses
   :focus-visible (NOT :focus) so mouse users don't get the ring on click,
   but keyboard users always do. 2px outer ink for contrast on light bg. */
:focus-visible {
  outline: 3px solid var(--lemon);
  outline-offset: 2px;
  border-radius: 4px;
  box-shadow: 0 0 0 1px var(--ink);
}
/* High-contrast surfaces (e.g. ink-bg buttons) want the ring inverted —
   ink ring inside, gold halo outside */
.nx-send:focus-visible,
.nx-tab:focus-visible {
  outline: 3px solid var(--lemon);
  outline-offset: 2px;
  box-shadow: 0 0 0 1px var(--ink), 0 0 0 5px rgba(255, 200, 87, 0.25);
}

.nx-mute { color: var(--mute); }

/* =============================================================
   V24 TIER 5 · APP-LOAD CHOREOGRAPHY (stage curtain rising)
   Sequence:
     T+0ms     top bar fades in        (180ms)
     T+120ms   trail fades in          (280ms)
     T+280ms   chat zone slides in     (460ms swift-settle)
     T+460ms   dashboard zone fades in (460ms)
     T+720ms   drawer empty fades in   (460ms)
   Calm, intentional, premium — sets the tone before Nora speaks.
   prefers-reduced-motion: skips entirely. Active when body has .is-loading
   class (set on init, dropped after final stage settles).
   ============================================================= */
@keyframes nx-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes nx-slide-in-left {
  from { opacity: 0; transform: translateX(-12px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes nx-fade-up {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
body.nx-is-booting .nx-top {
  animation: nx-fade-in 180ms cubic-bezier(.22,.61,.36,1) 0ms both;
}
body.nx-is-booting #zone-chat {
  animation: nx-slide-in-left 460ms cubic-bezier(.22,.61,.36,1) 280ms both;
}
body.nx-is-booting #zone-dash {
  animation: nx-fade-up 460ms cubic-bezier(.22,.61,.36,1) 460ms both;
}
body.nx-is-booting #zone-drawer .nx-drawer-empty {
  animation: nx-fade-up 460ms cubic-bezier(.22,.61,.36,1) 720ms both;
}
@media (prefers-reduced-motion: reduce) {
  body.nx-is-booting .nx-top,
  body.nx-is-booting #zone-chat,
  body.nx-is-booting #zone-dash,
  body.nx-is-booting #zone-drawer .nx-drawer-empty {
    animation: none;
  }
}

/* =============================================================
   TOP BAR
   ============================================================= */
.nx-top {
  display: flex; align-items: center; gap: 16px;
  padding: 14px 28px;
  background: var(--cream);
  border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; z-index: 50;
}
.nx-logo {
  display: flex; align-items: center; gap: 10px;
  text-decoration: none; color: var(--ink);
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 700; font-size: 19px;
  letter-spacing: -.01em;
  /* V27 F14 — desktop + tablet bump to ≥44 px tap area (passes WCAG AAA
     2.5.5). Mobile keeps the 26 px height (AA pass; bumping to 44 caused
     27 px doc overflow on mobile-360 / 3 px on tablet-1024). The mobile
     override below pins min-height back to 0 so topbar layout doesn't
     bloat and mobile remains under viewport. */
  min-height: 44px;
}
.nx-mark {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--lemon);
  border: 1.5px solid var(--ink);
  position: relative; flex: none;
}
.nx-mark::after {
  content: ""; position: absolute;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--pink);
}
.nx-status {
  margin: 0 auto;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px;
  background: var(--cream);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-2);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
}
.nx-status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 0 3px rgba(74,222,128,0.22);
  animation: nx-pulse 2.2s ease-in-out infinite;
}
.nx-account {
  background: var(--cream); color: var(--ink);
  border: 1.5px solid var(--ink); border-radius: 999px;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  box-shadow: var(--shadow-2);
}

@keyframes nx-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%       { opacity: .6; transform: scale(1.12); }
}

/* =============================================================
   MOBILE TAB NAV
   ============================================================= */
.nx-tabs {
  display: none;
  background: var(--cream);
  border-bottom: 1px solid var(--rule);
  padding: 6px 12px;
  gap: 6px;
  position: sticky; top: 60px; z-index: 49;
}
.nx-tab {
  flex: 1; padding: 10px 12px;
  background: transparent;
  border: 1.5px solid transparent;
  border-radius: 10px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
  transition: background var(--t-quick), color var(--t-quick), border-color var(--t-quick);
}
.nx-tab.is-active {
  background: var(--lemon); color: var(--ink);
  border-color: var(--ink);
  box-shadow: var(--shadow-2);
}

/* =============================================================
   SHELL — single ink-bordered three-zone container
   ============================================================= */
.nx-shell {
  max-width: 1440px;
  margin: 24px auto 40px;
  padding: 0;
  background: var(--cream);
  border: 2px solid var(--ink);
  border-radius: 22px;
  box-shadow: var(--shadow-6);
  display: grid;
  grid-template-columns: 36% 38% 26%;
  align-items: stretch;
  min-height: calc(100vh - 60px - 24px - 40px);
  min-height: calc(100svh - 60px - 24px - 40px);
  overflow: hidden;
}

.nx-zone {
  position: relative;
  display: flex; flex-direction: column;
  min-height: 0;
}
.nx-zone + .nx-zone {
  border-left: 1px dashed var(--rule);
}

/* =============================================================
   ZONE 1 — NORA CHAT
   ============================================================= */
.nx-chat {
  background: linear-gradient(180deg, rgba(255,232,110,0.10) 0%, rgba(255,250,234,0) 220px);
}
.nx-chat-head {
  display: flex; align-items: center; gap: 14px;
  padding: 22px 24px 16px;
  border-bottom: 1px dashed var(--rule);
  background: var(--cream);
  position: sticky; top: 0; z-index: 5;
}

/* DAV-74 — Baseline chip (chat-head right rail). Hidden until
   baseline-chip.js confirms window.NORA_OCR_BASELINE is set, then
   filled with carrier + plan (+ premium when known). Ink surface,
   lemon dot for Vision-only, cyan dot for Stedi-verified. */
.nx-baseline-chip {
  margin-left: auto;
  display: none; /* hidden until populated */
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  background: var(--ink, #0E2A3A);
  color: var(--cream, #FFFAEA);
  border: 1px solid var(--ink, #0E2A3A);
  border-radius: 999px;
  box-shadow: 2px 2px 0 var(--ink, #0E2A3A), 2px 2px 0 1px rgba(0,0,0,.08);
  max-width: 360px;
  font-family: 'Inter', system-ui, sans-serif;
}
.nx-baseline-chip[data-visible="1"] { display: inline-flex; }
.nx-baseline-chip .bc-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--lemon, #FFC857);
  flex: none;
  box-shadow: 0 0 0 2px rgba(255,200,87,.18);
}
.nx-baseline-chip .bc-dot--verified {
  background: var(--pink, #00B5D8); /* "pink" CSS var is actually our cyan */
  box-shadow: 0 0 0 2px rgba(0,181,216,.18);
}
.nx-baseline-chip .bc-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(255,250,234,.62);
  flex: none;
}
.nx-baseline-chip .bc-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--cream, #FFFAEA);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: -.005em;
}
.nx-baseline-chip .bc-sep {
  color: rgba(255,250,234,.42);
  font-weight: 400;
}
.nx-baseline-chip .bc-price {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--lemon, #FFC857);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  flex: none;
}
.nx-baseline-chip .bc-suffix {
  font-size: 10px;
  font-weight: 500;
  color: rgba(255,250,234,.6);
  margin-left: 1px;
}
/* Mobile — the chip drops below the avatar block so it doesn't crush
   the header on narrow widths. */
@media (max-width: 640px) {
  .nx-chat-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .nx-baseline-chip {
    margin-left: 0;
    flex-basis: 100%;
    justify-content: flex-start;
  }
}
.nx-avatar {
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--lemon); color: var(--ink);
  border: 2px solid var(--ink);
  display: grid; place-items: center;
  font-family: 'Fraunces', serif; font-weight: 700; font-size: 22px;
  flex: none;
  box-shadow: var(--shadow-3);
  animation: nx-breath 4s ease-in-out infinite;
}
@keyframes nx-breath {
  0%, 100% { box-shadow: var(--shadow-3); }
  50%       { box-shadow: 5px 5px 0 var(--ink); }
}
.nx-chat-name {
  font-family: 'Fraunces', serif; font-weight: 700;
  font-size: 19px; line-height: 1.1; color: var(--ink);
}
.nx-chat-credential {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--mute); font-weight: 700;
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px;
  margin-top: 3px;
}
.nx-chat-sep { opacity: .5; }
.nx-online {
  display: inline-block;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 0 3px rgba(74,222,128,0.22);
  animation: nx-pulse 2.2s ease-in-out infinite;
  margin-right: 2px;
}

/* --- chat scroll area + bubbles ----------------------------- */
.nx-chat-scroll {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 22px 24px 12px;
  display: flex; flex-direction: column; gap: 14px;
  scroll-behavior: smooth;
}
.nx-chat-scroll::-webkit-scrollbar { width: 6px; }
.nx-chat-scroll::-webkit-scrollbar-thumb { background: rgba(14,42,58,.18); border-radius: 4px; }

.nx-msg {
  display: flex; gap: 10px;
  align-items: flex-end;
  max-width: 92%;
  opacity: 0; transform: translateY(6px);
  animation: nx-msg-in .4s var(--ease) forwards;
}
@keyframes nx-msg-in {
  to { opacity: 1; transform: translateY(0); }
}
.nx-msg.from-nora { align-self: flex-start; }
.nx-msg.from-user { align-self: flex-end; flex-direction: row-reverse; }

.nx-msg-avatar {
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: 'Fraunces', serif; font-weight: 700; font-size: 14px;
  border: 1.5px solid var(--ink);
  flex: none;
}
.nx-msg.from-nora .nx-msg-avatar { background: var(--lemon); color: var(--ink); }
.nx-msg.from-user .nx-msg-avatar { background: var(--ink); color: var(--cream); }

.nx-bub {
  background: var(--cream);
  color: var(--ink);
  border: 1.5px solid var(--ink);
  border-radius: 18px;
  padding: 13px 17px;
  font-size: 14.5px; line-height: 1.5;
  box-shadow: var(--shadow-2);
  max-width: 100%;
}
.nx-msg.from-nora .nx-bub { border-bottom-left-radius: 6px; }
.nx-msg.from-user .nx-bub {
  background: var(--lemon);
  border-bottom-right-radius: 6px;
}
.nx-bub.is-aha {
  background: linear-gradient(135deg, var(--lemon) 0%, #FFF1A6 100%);
  border-color: var(--ink);
}
.nx-bub.is-aha .aha-num {
  font-family: 'Fraunces', serif; font-style: italic; font-weight: 700;
  color: var(--pink-dark);
  font-size: 1.05em;
}
.nx-bub p { margin: 0 0 6px; }
.nx-bub p:last-child { margin: 0; }
.nx-ts {
  display: block; margin-top: 4px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase;
  opacity: .5;
}

/* --- typing indicator --------------------------------------- */
.nx-typing { display: inline-flex; gap: 4px; align-items: center; padding: 4px 0; }
.nx-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink); opacity: .3;
  animation: nx-typing-dot 1.2s ease-in-out infinite;
}
.nx-typing span:nth-child(2) { animation-delay: .15s; }
.nx-typing span:nth-child(3) { animation-delay: .3s; }
@keyframes nx-typing-dot {
  0%, 60%, 100% { opacity: .3; transform: translateY(0); }
  30%            { opacity: 1; transform: translateY(-3px); }
}

/* --- quick replies + input row ------------------------------ */
.nx-chat-input-wrap {
  border-top: 1px dashed var(--rule);
  padding: 14px 22px 16px;
  background: var(--cream);
}
.nx-quick-replies {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: 10px;
}
.nx-quick-replies:empty { display: none; }
.nx-qr {
  background: var(--cream);
  color: var(--ink);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 13.5px; font-weight: 500;
  box-shadow: var(--shadow-2);
  transition: transform var(--t-quick), box-shadow var(--t-quick), background var(--t-quick);
}
.nx-qr:hover { background: var(--lemon); transform: translate(-1px, -1px); box-shadow: var(--shadow-3); }
.nx-qr:active { transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--ink); }

.nx-input-row {
  display: flex; gap: 8px; align-items: stretch;
  background: var(--cream);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  padding: 4px 4px 4px 16px;
  box-shadow: var(--shadow-2);
  transition: box-shadow var(--t-quick);
}
.nx-input-row:focus-within { box-shadow: var(--shadow-3); }
.nx-input {
  flex: 1; min-width: 0;
  border: 0; outline: 0; background: transparent;
  padding: 8px 0;
  font-size: 14.5px; color: var(--ink);
}
.nx-input::placeholder { color: var(--mute); }
.nx-send {
  /* V27 F11 — was 38×38 base + 44×44 mobile @media. Bumped base universally
     to 44×44 so desktop also passes WCAG 2.5.5 (Target Size, AAA, 44 px) and
     2.5.8 (AA, 24 px). Mobile rule below still applies but is now no-op. */
  background: var(--lemon); color: var(--ink);
  border: 1.5px solid var(--ink); border-radius: 999px;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  flex: none;
  transition: transform var(--t-quick), background var(--t-quick);
}
.nx-send:hover { background: var(--pink); color: var(--cream); transform: translate(-1px, -1px); }
.nx-send:active { transform: translate(0, 0); }
.nx-send:disabled { opacity: .45; cursor: not-allowed; }

/* =============================================================
   ZONE 2 — DASHBOARD (M1 scaffold; live updates land in M2)
   ============================================================= */
.nx-dash {
  padding: 30px 30px 28px;
  overflow-y: auto;
  background:
    radial-gradient(circle at 88% 8%, rgba(255,232,110,0.18) 0, transparent 36%),
    var(--cream);
}
.nx-dash-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--pink-dark);
  margin-bottom: 14px;
}
.nx-dash-headline {
  font-family: 'Fraunces', serif; font-weight: 600;
  font-size: clamp(32px, 3.6vw, 44px); line-height: 1.05; letter-spacing: -.025em;
  margin: 0 0 22px;
  color: var(--ink);
}
.nx-dash-headline em { font-style: italic; color: var(--pink); font-weight: 400; }

/* V23 · Persona reinforce paragraph beneath the dashboard headline.
   Mirrors V20 quiz reveal layout. */
.nx-dash-reinforce {
  font-family: 'Inter', sans-serif;
  font-size: clamp(14px, 1.05vw, 16px);
  line-height: 1.55;
  letter-spacing: -.005em;
  color: var(--ink);
  opacity: .82;
  margin: -10px 0 22px;
  max-width: 64ch;
}
.nx-dash-reinforce:empty { display: none; }

/* V23 · AuthGate dashboard skeleton — keeps the middle column alive
   while the gate is mid-flight. */
.nx-dash-authskel {
  margin-top: 14px;
  background: rgba(14,42,58,0.04);
  border: 1.5px dashed var(--rule);
  border-radius: 14px;
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.nx-dash-authskel[hidden] { display: none; }
.nx-dash-authskel-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--mute);
}
.nx-dash-authskel-status {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  margin: 0;
  opacity: .8;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-dash-authskel.is-cycling .nx-dash-authskel-status::after {
  content: ' …';
  opacity: .55;
}

.nx-dash-divider {
  height: 0; border: 0;
  border-top: 1px dashed var(--rule);
  margin: 22px 0;
}

/* V24 · Pre-name-bind ValueStack placeholder.
   Renders inside #dashValueStack until the user types their first name. */
.nx-vs-pre {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 36px 12px;
  min-height: 200px;
}
.nx-vs-pre-pip {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  height: 12px;
}
.nx-vs-pre-pip span {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ink);
  opacity: 0.35;
  animation: nx-vs-pre-bounce 1.2s ease-in-out infinite;
}
.nx-vs-pre-pip span:nth-child(2) { animation-delay: 0.18s; }
.nx-vs-pre-pip span:nth-child(3) { animation-delay: 0.36s; }
@keyframes nx-vs-pre-bounce {
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
  40%           { opacity: 1;    transform: translateY(-4px); }
}
.nx-vs-pre-label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--ink);
  opacity: 0.7;
}
@media (prefers-reduced-motion: reduce) {
  .nx-vs-pre-pip span { animation: none; opacity: 0.5; }
}

.nx-bignum-wrap {
  background: linear-gradient(135deg, var(--lemon) 0%, #FFF1A6 100%);
  border: 1.5px solid var(--ink);
  border-radius: 18px;
  padding: 24px 26px;
  box-shadow: var(--shadow-3);
  margin-bottom: 14px;
  /* Task 4.3 — Dashboard stability. Reserve min-height so the BigNum
     container doesn't shift layout as Value Stack mounts/remounts or
     copy length changes between Nora replies. */
  min-height: 220px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.nx-bignum {
  font-family: 'Fraunces', serif; font-weight: 600; font-style: italic;
  font-size: clamp(48px, 6vw, 72px);
  line-height: .95; letter-spacing: -.035em;
  color: var(--ink);
  /* V24 Tier 5: 460ms swift-settle */
  transition:
    transform 460ms cubic-bezier(.22,.61,.36,1),
    opacity 460ms cubic-bezier(.22,.61,.36,1);
}
.nx-bignum-unit {
  font-family: 'Inter', sans-serif; font-style: normal; font-weight: 600;
  font-size: 0.4em; letter-spacing: 0; opacity: .8;
  margin-left: 4px;
}
.nx-bignum-label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--ink);
  margin-top: 6px;
}

.nx-dash-compare { display: flex; flex-direction: column; gap: 6px; }
.nx-compare-row {
  display: flex; gap: 10px; align-items: baseline;
  font-size: 14px;
}
.nx-compare-label { color: var(--mute); flex: none; }
.nx-compare-val { color: var(--ink); font-weight: 600; }
.nx-savings .nx-compare-val { color: var(--pink-dark); }

/* "What we know so far" --- */
.nx-known-label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
  margin-bottom: 12px;
}
.nx-known-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.known-item {
  display: flex; align-items: center; gap: 10px;
  font-size: 14px;
  /* V24 Tier 5: 280ms swift-settle on color shift when fact confirms */
  transition: color 280ms cubic-bezier(.22,.61,.36,1);
}
.known-item .nx-known-mark {
  display: inline-grid; place-items: center;
  width: 20px; height: 20px; border-radius: 50%;
  font-size: 13px; font-weight: 700;
  flex: none;
}
.known-item.is-confirmed .nx-known-mark {
  background: var(--ink); color: var(--lemon);
}
.known-item.is-pending .nx-known-mark {
  background: transparent; color: var(--mute);
  border: 1.5px solid var(--rule);
  font-size: 11px;
}
.known-item.is-pending .nx-known-text { color: var(--mute); }
.known-item .nx-known-text b { font-weight: 600; }

/* M2 stub callout */
.nx-dash-stub {
  margin-top: 22px;
  padding: 16px 18px;
  background: rgba(14,42,58,0.04);
  border: 1px dashed var(--rule);
  border-radius: 12px;
  font-size: 13px; color: var(--mute);
}
.nx-dash-stub .nx-stub-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--pink-dark);
  margin-bottom: 6px;
}
.nx-dash-stub p { margin: 0; line-height: 1.5; }

/* V27.1 — trust footer relocated from inside dash zone to below shell.
   See index.html footer.nx-trust-footer rationale comment. */
.nx-trust-footer {
  max-width: 1440px;
  margin: 0 auto;
  padding: 14px 28px 24px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mute);
  line-height: 1.7;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.nx-trust-footer a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  display: inline-block;
  padding: 4px 4px;
  min-height: 24px;
}
@media (max-width: 767px) {
  .nx-trust-footer {
    padding: 12px 16px 80px; /* extra bottom for mobile sticky tab nav */
    font-size: 10px;
  }
}

/* =============================================================
   ZONE 3 — REPORT DRAWER (M5 placeholder)
   ============================================================= */
.nx-drawer {
  padding: 30px 26px;
  background:
    linear-gradient(180deg, rgba(255,232,110,0.05) 0%, rgba(255,250,234,0) 200px),
    var(--cream);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
}
.nx-drawer-empty {
  /* V24 · Ghost-card feel — no longer competes with dashboard headline.
     Wrap the empty state in a subtle dashed-rule container so it reads
     as a placeholder slot, not a primary heading. */
  max-width: 280px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 28px 24px;
  border: 1px dashed var(--rule);
  border-radius: 14px;
  background: rgba(14,42,58,0.025);
  opacity: 0.78;
}
.nx-drawer-illo {
  /* V24 · Smaller, quieter — supports the ghost-card framing */
  color: var(--ink); opacity: .28;
  width: 48px; height: 48px;
}
.nx-drawer-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
}
.nx-drawer-title {
  /* V24 · Demoted from 22px → 17px Fraunces — no longer competes with
     dash-headline (clamp 32-44px) for the eye.
     V27 F16 · Was opacity 0.7 + var(--ink). Under forced-colors that drops
     contrast below WCAG 2.1 AA (effective contrast collapses when system
     compositor doesn't honor opacity). Replaced with var(--mute) which is
     #4A6678 (5.3:1 on white, AA-pass), and a forced-colors override below. */
  font-family: 'Fraunces', serif; font-weight: 500;
  font-size: 17px; line-height: 1.25; letter-spacing: -.005em;
  color: var(--mute);
}
@media (forced-colors: active) {
  /* V27 F16 — axe needs explicit Canvas background to resolve color-contrast.
     Without it, the title's transparent bg + opacity:0 ancestor confuses the
     check. forced-color-adjust:none locks system colors at this node. Same
     pattern applied to .nx-drawer-eyebrow which was the residual final-audit
     violation in fc-SP1-read-1440. */
  .nx-drawer-title,
  .nx-drawer-eyebrow {
    color: CanvasText;
    background: Canvas;
    forced-color-adjust: none;
  }
}
.nx-drawer-copy {
  font-size: 13.5px; line-height: 1.55;
  color: var(--mute);
  margin: 0;
}
.nx-drawer-meta {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--mute);
  margin: 10px 0 0;
  line-height: 1.5;
}

/* =============================================================
   RESPONSIVE
   ============================================================= */

/* Laptop (1024–1279px) — slightly compressed grid (33/40/27) */
@media (max-width: 1279px) {
  .nx-shell { grid-template-columns: 33% 40% 27%; }
}

/* V27 F02 — tablet-landscape vertical chrome trim (e.g. 1024×768) */
/* Without this, header(65) + trail-gap(16) + trail(106) + shell-margin-top(24)
   + shell(580) + shell-margin-bottom(40) = 831px overshoots 768px viewport by 63px.
   Targeted at desktop-grid widths with constrained heights so we don't affect
   iPad Pro portrait (1024×1366) or any tall display. */
@media (min-width: 1024px) and (max-width: 1279px) and (max-height: 820px) {
  /* nx-top padding 4px (was 6) — F14 logo bumped link 29→44 (+15 px) which
     pushed tablet-1024 doc 3 px over viewport even with the original F02
     trims; reclaiming the padding here gives the budget back. */
  .nx-top { padding: 4px 28px; }
  .nx-shell { margin: 2px auto 0; }

  /* V27 F18 — Plan cards naturally render ~486 px tall but the dash zone at
     LOCK on tablet-1024 only has ~428 px to give them. Reuse the COMPARE-phase
     compact override at LOCK on this breakpoint so cards fit without clipping;
     also hide the rationale paragraph (the long descriptive copy) which saves
     ~80 px and brings cards under 400 px without sacrificing the price + tier
     summary the user needs at lock-in. */
  .nx-shell[data-layout-phase="lock"] .plan-card {
    padding: 14px 14px 12px;
    gap: 8px;
    min-height: 0 !important; /* override plan-cards.css default 480px */
  }
  .nx-shell[data-layout-phase="lock"] .plan-card__short { font-size: 18px; }
  .nx-shell[data-layout-phase="lock"] .plan-card__rationale { display: none; }
  /* V27 F18 sibling — F18 compaction made carrier-text container ~38 px wide
     which clipped "Regence BCBS" / "U of U Health" with ellipsis. Allow wrap
     at this breakpoint+phase so the full carrier name reads. */
  .nx-shell[data-layout-phase="lock"] .plan-card__carrier-text {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.15;
  }
}

/* Tablet (768–1023px) — drawer collapses */
@media (max-width: 1023px) {
  .nx-shell { grid-template-columns: 42% 58%; margin: 16px 16px 24px; }
  .nx-drawer { display: none; }
}

/* Mobile (<768px) — single-zone tab navigation */
@media (max-width: 767px) {
  .nx-tabs { display: flex; }
  .nx-shell {
    grid-template-columns: 1fr;
    margin: 12px 12px 24px;
    border-radius: 16px;
    box-shadow: var(--shadow-4);
    min-height: calc(100vh - 60px - 52px - 12px - 24px);
    min-height: calc(100svh - 60px - 52px - 12px - 24px);
  }
  .nx-zone { display: none; }
  .nx-zone.is-active { display: flex; }
  .nx-zone + .nx-zone { border-left: none; border-top: 1px dashed var(--rule); }
  .nx-top { padding: 12px 18px; }
  .nx-status { font-size: 10px; padding: 5px 10px; }
  .nx-dash { padding: 22px 20px; }
  .nx-dash-headline { font-size: clamp(28px, 6.5vw, 36px); }
  .nx-bignum { font-size: clamp(48px, 12vw, 64px); }
  .nx-bignum-wrap { padding: 20px 22px; }
  .nx-chat-head { padding: 18px 22px 14px; }
  .nx-chat-scroll { padding: 18px 22px 8px; }
  .nx-chat-input-wrap { padding: 12px 18px 14px; }
  .nx-qr { font-size: 14px; padding: 10px 16px; min-height: 40px; }
  .nx-input { font-size: 16px; } /* prevent iOS zoom-on-focus */
  .nx-msg { max-width: 96%; }
}

/* =============================================================
   V24 Tier 4 — Percentile framing post-plan-lock (Spotify Wrapped)
   Slides in 1.5s after PLAN LOCKED caption fades; stays visible
   as identity anchor.
   ============================================================= */
.nx-percentile {
  display: block;
  margin: 16px auto 0;
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  color: var(--mute, #5D7C8E);
  line-height: 1.5;
  max-width: 460px;
  padding: 8px 14px;
  opacity: 0;
  transform: translateY(6px);
  /* V24 Tier 5: 720ms swift-settle large reveal */
  transition:
    opacity 720ms cubic-bezier(.22,.61,.36,1),
    transform 720ms cubic-bezier(.22,.61,.36,1);
}
.nx-percentile.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.nx-percentile .nx-pct-num {
  font-family: 'Fraunces', serif;
  font-style: italic;
  font-weight: 700;
  font-size: 18px;
  color: var(--pink, #00B5D8);
  padding: 0 1px;
}
.nx-percentile .nx-pct-emph {
  color: var(--ink, #0E2A3A);
  font-weight: 600;
}

/* =============================================================
   V24 TIER 6 · STATE DESIGN — empty / loading / error / confirmation
   Every non-happy-path state designed as deliberately as the happy path.
   ============================================================= */

/* ─── 1a · Drawer route preview (3-dot journey)─────────────── */
.nx-drawer-route {
  list-style: none;
  margin: 6px 0 0;
  padding: 14px 0 4px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: 220px;
  border-top: 1px dashed var(--rule);
}
.nx-route-step {
  display: flex; align-items: center; gap: 12px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
  opacity: 0;
  animation: nx-route-fade 460ms cubic-bezier(.22,.61,.36,1) forwards;
}
.nx-route-step:nth-child(1) { animation-delay: 0ms; }
.nx-route-step:nth-child(2) { animation-delay: 60ms; }
.nx-route-step:nth-child(3) { animation-delay: 120ms; }
@keyframes nx-route-fade {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nx-route-dot {
  flex: none;
  width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid var(--rule);
  background: transparent;
  transition: background-color 280ms cubic-bezier(.22,.61,.36,1),
              border-color 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-route-step.is-active {
  color: var(--ink);
}
.nx-route-step.is-active .nx-route-dot {
  background: var(--ink);
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(255, 200, 87, 0.30);
}
/* The middle (next) dot pulses subtly — anticipation */
.nx-route-step:nth-child(2) .nx-route-dot {
  animation: nx-route-pulse 2.4s ease-in-out infinite;
  animation-delay: 1.2s;
}
@keyframes nx-route-pulse {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  50%      { opacity: 0.9; transform: scale(1.15); }
}
.nx-route-step.is-faded { opacity: 0.45; }
@media (prefers-reduced-motion: reduce) {
  .nx-route-step,
  .nx-route-step:nth-child(2) .nx-route-dot { animation: none; opacity: 1; }
}

/* ─── 1b · Pre-opener "Nora is gathering your context" loader ── */
.nx-pre-opener {
  align-self: flex-start;
  display: flex; align-items: center; gap: 8px;
  margin: 4px 0 0 44px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 700;
  color: var(--mute);
  opacity: 0;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-pre-opener.is-visible { opacity: 0.78; }
.nx-pre-opener.is-fading  { opacity: 0; }
.nx-pre-opener::before {
  content: '';
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--pink);
  animation: nx-pre-opener-pulse 1.4s ease-in-out infinite;
}
@keyframes nx-pre-opener-pulse {
  0%, 100% { opacity: 0.4; transform: scale(0.85); }
  50%      { opacity: 1;   transform: scale(1.15); }
}
@media (prefers-reduced-motion: reduce) {
  .nx-pre-opener::before { animation: none; }
}

/* ─── 1c · "What we know" pending → confirmed animated stroke ── */
.known-item.is-pending .nx-known-text {
  position: relative;
  background-image: linear-gradient(to right, var(--rule) 0%, var(--rule) 100%);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size 720ms cubic-bezier(.22,.61,.36,1);
  padding-bottom: 1px;
}
.known-item.is-confirming .nx-known-text {
  background-size: 100% 1px;
  background-image: linear-gradient(to right, var(--pink) 0%, var(--pink) 100%);
}
.known-item.is-confirming .nx-known-mark {
  background: var(--ink);
  color: var(--lemon);
  border-color: var(--ink);
  transform: scale(1);
  animation: nx-known-confirm 460ms cubic-bezier(.22,.61,.36,1);
}
@keyframes nx-known-confirm {
  0%   { transform: scale(0.7); opacity: 0.5; }
  60%  { transform: scale(1.15); }
  100% { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .known-item.is-confirming .nx-known-text { transition: none; }
  .known-item.is-confirming .nx-known-mark  { animation: none; }
}

/* ─── 1d · Plan-cards locked-preview peek ─────────────────────── */
.nx-plan-cards-host:not(.is-active) {
  display: block; /* override default `display:none` so the peek shows */
}
.nx-plan-cards-host.is-active .nx-plan-peek { display: none; }
/* V24 Tier 8 · "less but better" — peek pill quieted to a tighter,
   single-line strip. Was 16-18px padded card with 22px icon. Now an
   inline strip that whispers anticipation, doesn't shout for attention. */
.nx-plan-peek {
  margin-top: 18px;
  padding: 10px 14px;
  background: transparent;
  border: 1px dashed var(--rule);
  border-radius: 10px;
  display: flex; align-items: center; gap: 8px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
  line-height: 1.4;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-plan-peek-icon {
  flex: none;
  display: inline-grid; place-items: center;
  width: 16px; height: 16px;
  font-size: 10px;
  color: var(--mute);
  opacity: .6;
}
/* Hide the peek when the plan-cards host has been already activated */
.nx-plan-cards-host.is-active + .nx-plan-peek,
.nx-plan-cards-host.is-active .nx-plan-peek { display: none; }

/* ─── 2a · Inline status text on long Nora typing pauses ──────── */
.nx-typing-status {
  display: block;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 700;
  color: var(--mute);
  margin: -2px 0 6px;
  opacity: 0;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-typing-status.is-visible { opacity: 0.75; }

/* ─── 2b · Plan-options fetching loader ───────────────────────── */
.nx-plan-fetching {
  margin-top: 16px;
  padding: 18px 20px;
  background: rgba(0, 181, 216, 0.04);
  border: 1px dashed var(--rule);
  border-radius: 12px;
  display: flex; flex-direction: column; gap: 10px;
}
.nx-plan-fetching-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--pink-dark);
}
.nx-plan-fetching-status {
  font-family: 'Inter', sans-serif;
  font-size: 14px; color: var(--ink);
  margin: 0;
}
.nx-plan-fetching-bars {
  display: flex; gap: 4px; align-items: flex-end; height: 12px;
}
.nx-plan-fetching-bars span {
  flex: 1; max-width: 40px;
  background: var(--pink);
  border-radius: 2px;
  height: 100%;
  opacity: 0.4;
  animation: nx-fetch-bar 1.2s ease-in-out infinite;
}
.nx-plan-fetching-bars span:nth-child(1) { animation-delay: 0ms; }
.nx-plan-fetching-bars span:nth-child(2) { animation-delay: 150ms; }
.nx-plan-fetching-bars span:nth-child(3) { animation-delay: 300ms; }
@keyframes nx-fetch-bar {
  0%, 100% { opacity: 0.3; transform: scaleY(0.5); }
  50%      { opacity: 1;   transform: scaleY(1); }
}
@media (prefers-reduced-motion: reduce) {
  .nx-plan-fetching-bars span { animation: none; opacity: 0.6; }
}

/* ─── 2c · Pre-celebration "Locking your plan…" overlay ───────── */
.nx-locking-overlay {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: saturate(1.05);
  border-radius: 14px;
  z-index: 12;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--ink);
  opacity: 0;
  pointer-events: none;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}
.nx-locking-overlay.is-visible { opacity: 1; pointer-events: auto; }
.nx-locking-overlay::before {
  content: '';
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--pink); margin-right: 9px;
  animation: nx-pre-opener-pulse 1.0s ease-in-out infinite;
}
.nx-bignum-wrap { position: relative; }

/* ─── 3b · Network drop banner ────────────────────────────────── */
.nx-net-banner {
  display: none;
  margin: 0 0 12px;
  padding: 10px 14px;
  background: rgba(0, 181, 216, 0.08);
  border: 1px solid rgba(0, 181, 216, 0.32);
  border-radius: 10px;
  font-size: 13px; line-height: 1.45;
  color: var(--ink);
}
.nx-net-banner.is-visible { display: block; }
.nx-net-banner-eyebrow {
  display: block;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--pink-dark);
  margin-bottom: 2px;
}

/* ─── 5b · Plan-locked "Continue when ready" CTA ──────────────── */
.nx-locked-cta {
  display: block;
  margin: 14px auto 0;
  text-align: center;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--pink-dark);
  text-decoration: none;
  opacity: 0;
  transform: translateY(4px);
  transition:
    opacity 460ms cubic-bezier(.22,.61,.36,1),
    transform 460ms cubic-bezier(.22,.61,.36,1),
    color 180ms cubic-bezier(.22,.61,.36,1);
  padding: 6px 12px;
  border-radius: 8px;
}
.nx-locked-cta.is-visible {
  opacity: 0.82;
  transform: translateY(0);
}
.nx-locked-cta:hover {
  opacity: 1;
  color: var(--ink);
}
.nx-locked-cta::after {
  content: ' →';
  font-family: 'Inter', sans-serif;
  transition: transform 180ms cubic-bezier(.22,.61,.36,1);
  display: inline-block;
}
.nx-locked-cta:hover::after { transform: translateX(2px); }
@media (prefers-reduced-motion: reduce) {
  .nx-locked-cta { transition: opacity 0ms; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .nx-percentile { transition: none; }
  .nx-percentile.is-visible { opacity: 1; transform: none; }
}

/* =============================================================
   prefers-reduced-motion — kill animations
   ============================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .nx-msg { opacity: 1; transform: none; }
  .nx-bignum { transition: none; }
}

/* =============================================================
   V24 TIER 7 — A11Y + MOBILE NATIVE + HIGH CONTRAST
   Pass 1: WCAG 2.1 AA color/keyboard/screen-reader
   Pass 2: iOS Safari + Android — safe-area, 44px tap targets, native feel
   Pass 3: Reduced-motion final audit
   Pass 4: Windows High Contrast Mode + macOS Increase Contrast
   ============================================================= */

/* ─── A11Y · Screen-reader-only utility ───────────────────────── */
.sr-only,
.is-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ─── A11Y · Skip-to-content link ─────────────────────────────── */
.nx-skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  background: var(--lemon);
  color: var(--ink);
  border: 2px solid var(--ink);
  border-radius: 8px;
  padding: 10px 16px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: var(--shadow-3);
  transition: top 180ms cubic-bezier(.22,.61,.36,1);
}
.nx-skip-link:focus,
.nx-skip-link:focus-visible {
  top: 12px;
}

/* ─── A11Y · Color contrast ──
   --mute deepened to #4A6678 in the canonical :root block above
   (was #5D7C8E ~3.8:1 on white; now ~5.3:1 — WCAG AA pass at all body
   sizes). Single source of truth — no override needed here. */

/* ─── A11Y · Keyboard navigation between plan cards (arrow keys) ─
   Cards use [role="group"] container for arrow key handler in JS.
   Visual treatment matches existing card focus-visible. */
.plan-cards__grid:focus { outline: none; }

/* ─── A11Y · Live region announcement zone (off-screen) ─────── */
.nx-aria-live {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* =============================================================
   MOBILE NATIVE FEEL — iPhone SE (375), iPhone 14 (390),
   Plus (414), Android narrow (360)
   ============================================================= */

/* iOS Safari + Android — respect device safe areas (notch + home indicator) */
.nx-top {
  padding-top: max(14px, env(safe-area-inset-top));
}

/* Mobile-specific: top bar compresses to logo only on <768px,
   bottom tab nav becomes sticky-bottom with home indicator inset */
@media (max-width: 767px) {
  /* Logo-only top bar — hide live status (moved into chat header) */
  .nx-top {
    padding: max(10px, env(safe-area-inset-top)) 16px 10px;
    gap: 12px;
  }
  .nx-status {
    display: none; /* Live status visible in chat-head, not duplicate here */
  }
  .nx-logo { font-size: 17px; min-height: 0; } /* V27 F14 — keep AA-only at mobile to avoid topbar bloat */
  .nx-mark { width: 22px; height: 22px; }

  /* 44px minimum tap targets (Apple HIG) */
  .nx-tab {
    min-height: 44px;
    padding: 12px 12px;
  }
  .nx-qr {
    min-height: 44px;
    padding: 12px 18px;
    font-size: 14.5px;
  }
  .nx-send {
    width: 44px; height: 44px;
  }
  .nx-input {
    font-size: 16px; /* prevent iOS input-zoom-on-focus */
    min-height: 44px;
    padding: 11px 0;
  }

  /* Tab nav sticks to bottom on mobile + safe-area for home indicator */
  .nx-tabs {
    position: fixed;
    bottom: 0; left: 0; right: 0;
    top: auto;
    background: var(--cream);
    border-top: 1px solid var(--rule);
    border-bottom: none;
    padding: 6px 12px max(6px, env(safe-area-inset-bottom));
    z-index: 49;
    box-shadow: 0 -1px 8px rgba(14, 42, 58, 0.06);
  }

  /* Compensate shell bottom padding so content doesn't sit under tabs */
  .nx-shell {
    margin-bottom: calc(64px + env(safe-area-inset-bottom));
  }

  /* Tap-active states (no hover on touch — give immediate feedback) */
  .nx-qr:active,
  .nx-tab:active,
  .nx-send:active {
    transform: scale(0.97);
    transition: transform 100ms cubic-bezier(.22,.61,.36,1);
  }

  /* Disable iOS tap-highlight (we provide our own affordances) */
  .nx-qr,
  .nx-tab,
  .nx-send,
  .nx-input,
  button,
  a {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }

  /* Drawer becomes bottom-sheet on mobile when active — pull-down handle */
  .nx-zone.nx-drawer.is-active {
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
  }
  .nx-zone.nx-drawer.is-active::before {
    content: '';
    position: absolute;
    top: 8px; left: 50%;
    transform: translateX(-50%);
    width: 36px; height: 4px;
    background: var(--rule);
    border-radius: 2px;
    pointer-events: none;
  }
}

/* Mobile narrow (<480px) — tighter compression */
@media (max-width: 479px) {
  .nx-dash { padding: 18px 16px; }
  .nx-dash-headline {
    font-size: clamp(24px, 7.5vw, 32px);
    margin-bottom: 16px;
  }
  .nx-dash-reinforce {
    font-size: 14px;
    margin: -6px 0 18px;
  }
  .nx-bignum-wrap { padding: 18px 18px; }
  .nx-bignum { font-size: clamp(42px, 13vw, 56px); }
  .nx-chat-head { padding: 14px 18px 12px; }
  .nx-chat-scroll { padding: 14px 18px 8px; }
  .nx-chat-input-wrap { padding: 10px 14px 12px; }
  .nx-msg { max-width: 98%; }
  .nx-known-list { gap: 7px; }
  .known-item { font-size: 13.5px; }
}

/* =============================================================
   PASS 3 · REDUCED-MOTION FINAL AUDIT
   Comprehensive zero-out of every animated property in the app.
   Already covered above with the global *,*::before reset, but
   we add specific overrides for animations that use properties
   not caught by the universal duration override (transforms held
   via animation, infinite pulses, etc.).
   ============================================================= */
@media (prefers-reduced-motion: reduce) {
  /* Status pulses — freeze in mid-state but visible */
  .nx-status-dot,
  .nx-online,
  .nx-pre-opener::before,
  .nx-locking-overlay::before,
  .nx-typing span {
    animation: none !important;
    opacity: 0.85 !important;
    transform: none !important;
  }

  /* Avatar breath — freeze */
  .nx-avatar { animation: none !important; }

  /* Typing dots — show static three dots, no jump */
  .nx-typing span {
    opacity: 0.5;
  }

  /* Cascade entrance — collapse to simultaneous fade-in (already) */
  .nx-route-step,
  .nx-route-step:nth-child(2) .nx-route-dot {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  /* Lock celebration pulse — freeze, keep caption */
  .value-stack.is-locking::after {
    animation: none !important;
  }

  /* Plan-card lock glow — freeze */
  .plan-card.is-locking {
    animation: none !important;
  }

  /* Stage curtain (boot choreography) — already handled, reinforce */
  body.nx-is-booting * {
    animation: none !important;
  }

  /* App-load route fades — show all immediately */
  .nx-route-step { opacity: 1 !important; }

  /* Pending-fact stroke fill — show as confirmed instantly */
  .known-item.is-confirming .nx-known-text {
    background-image: none !important;
    transition: none !important;
  }
}

/* =============================================================
   PASS 4 · WINDOWS HIGH CONTRAST MODE (forced-colors: active)
   The OS overrides bg/fg colors. Ensure focus/borders/distinguishable.
   ============================================================= */
@media (forced-colors: active) {
  /* Use system colors instead of brand tokens where critical */
  :focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: 2px;
    box-shadow: none; /* drop the gold halo since CanvasText takes over */
  }

  /* Ensure containers + cards still have visible borders */
  .nx-shell,
  .nx-input-row,
  .nx-bub,
  .nx-bignum-wrap,
  .nx-status,
  .nx-account,
  .nx-avatar,
  .plan-card,
  .auth-gate__bubble,
  .auth-gate__field,
  .auth-gate__btn,
  .pcm-panel,
  .pr-toggle {
    border-color: CanvasText !important;
  }

  /* Status dots / pulses — replace with text since high-contrast users
     may not see colored dots. Keep mark for screen-reader users. */
  .nx-status-dot,
  .nx-online,
  .nx-pre-opener::before {
    background: Highlight !important;
    box-shadow: none !important;
  }

  /* Selected/active states — use system Highlight */
  .nx-tab.is-active,
  .nx-qr:hover,
  .nx-qr:focus-visible,
  .plan-card.is-selected {
    background: Highlight !important;
    color: HighlightText !important;
    forced-color-adjust: none;
  }

  /* Drop hard 3D shadows in HCM — they don't render anyway */
  .nx-shell,
  .nx-bignum-wrap,
  .auth-gate__bubble,
  .plan-card {
    box-shadow: none !important;
  }

  /* Disabled state — clearer */
  [aria-disabled="true"],
  button:disabled {
    color: GrayText !important;
    border-color: GrayText !important;
  }

  /* Links — ensure they look like links */
  a {
    color: LinkText !important;
  }
  a:visited { color: VisitedText !important; }
}

/* =============================================================
   PASS 4b · macOS "Increase Contrast" — already works through
   our :focus-visible (gold ring) + ink-bordered components.
   No changes required: the brand IS high-contrast by design.
   ============================================================= */

/* =============================================================
   A11Y · ARROW-KEY NAV INDICATOR ON PLAN CARDS
   When user presses arrow keys to navigate cards, give the keyboard
   user a subtle "this is the focused card" indicator beyond the
   default focus-visible ring.
   ============================================================= */
.plan-card:focus-visible {
  position: relative;
  z-index: 2;
}

/* =============================================================
   A11Y · Drawer collapsible chevron — keyboard activation visual
   Apply :focus-visible to the toggle button so keyboard users see
   the same affordance as click users.
   ============================================================= */
.pr-toggle:focus-visible {
  background: rgba(0, 181, 216, 0.06);
  border-radius: 6px;
}

/* =============================================================
   A11Y · Modal focus-trap visual nothing (focus-trap is JS-only;
   we keep the existing pcm-panel:focus-visible default).
   ============================================================= */
.pcm-panel:focus { outline: none; }
.pcm-panel:focus-visible {
  outline: 3px solid var(--lemon);
  outline-offset: -3px;
}

/* =============================================================
   AUTH-GATE TIER 7 SUPPORT
   - Email field describedby helper text
   - Error state aria-live polite
   ============================================================= */
.auth-gate__field-help {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: var(--mute);
  margin: 4px 0 0 14px;
}

/* =============================================================
   V31 — DESKTOP UX OVERHAUL Task 1.2
   2-zone shell: persistent CHAT column + swappable MAIN content.
   The old V26 phase-aware zone-role grid (chat/dash/drawer) is
   replaced with a `data-main-content` swap inside `.nx-main`.
   PHASE_ROLES JS still runs (Task 1.4 removes it) but no CSS reads
   zone-role classes anymore — those rules are intentionally gone.
   ============================================================= */

/* Base shell: viewport-pinned grid; never grows past visible area.
   Grid template + areas defined per-breakpoint below. */
.nx-shell[data-layout-phase] {
  display: grid;
  align-items: stretch;
  /* Viewport - top bar (64) - trail (~110) - margin (24+40) - safety (8) */
  height: calc(100vh - 64px - 116px - 24px - 40px);
  height: calc(100svh - 64px - 116px - 24px - 40px);
  min-height: 580px;
  max-height: calc(100vh - 64px - 116px - 24px - 40px);
  max-height: calc(100svh - 64px - 116px - 24px - 40px);
  overflow: hidden;
}

/* Universal zone behavior: internal scroll handles overflow */
.nx-shell[data-layout-phase] .nx-zone {
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: opacity 280ms cubic-bezier(.22,.61,.36,1);
}

/* Chat internal scroll — always */
.nx-shell[data-layout-phase] .nx-chat-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* ─────────────────────────────────────────────────────────────
   DESKTOP (≥1024px) — 2-zone grid, fixed 420px chat rail
   Main content swaps via [data-main-content] on .nx-main
   ───────────────────────────────────────────────────────────── */
@media (min-width: 1024px) {
  .nx-shell {
    grid-template-columns: 1fr 420px;
    grid-template-areas: "main chat";
  }
  .nx-main  { grid-area: main;  overflow-y: auto; }
  #zone-chat { grid-area: chat; border-left: 1px dashed var(--rule); }
  #zone-dash, #zone-drawer { display: none; }
}

/* ─────────────────────────────────────────────────────────────
   TABLET (768–1023px) — same 2-zone shape, narrower chat rail
   ───────────────────────────────────────────────────────────── */
@media (min-width: 768px) and (max-width: 1023px) {
  .nx-shell {
    grid-template-columns: 1fr 360px;
    grid-template-areas: "main chat";
  }
  .nx-main  { grid-area: main;  overflow-y: auto; }
  #zone-chat { grid-area: chat; border-left: 1px dashed var(--rule); }
  #zone-dash, #zone-drawer { display: none; }
}

/* ─────────────────────────────────────────────────────────────
   MAIN-CONTENT SWAP — show exactly one host per content mode
   Hosts are added in Task 1.3; rules are inert until then.
   ───────────────────────────────────────────────────────────── */
.nx-main[data-main-content="dashboard"]      .nx-main-host--dashboard { display: block; }
.nx-main[data-main-content="plans-vertical"] .nx-main-host--plans     { display: block; }
.nx-main[data-main-content="plan-report"]    .nx-main-host--report    { display: block; }
.nx-main[data-main-content="enrollment"]     .nx-main-host--enroll    { display: block; }
.nx-main[data-main-content="dashboard"]      :is(.nx-main-host--plans, .nx-main-host--report, .nx-main-host--enroll) { display: none; }
.nx-main[data-main-content="plans-vertical"] :is(.nx-main-host--dashboard, .nx-main-host--report, .nx-main-host--enroll) { display: none; }
.nx-main[data-main-content="plan-report"]    :is(.nx-main-host--dashboard, .nx-main-host--plans, .nx-main-host--enroll) { display: none; }
.nx-main[data-main-content="enrollment"]     :is(.nx-main-host--dashboard, .nx-main-host--plans, .nx-main-host--report) { display: none; }

/* ─────────────────────────────────────────────────────────────
   Mobile (<768px) — single-zone view, tabs auto-pivot to LEAD
   The existing .nx-tabs nav system stays — V26 just listens for
   layoutPhaseChange events to set aria-selected on the lead zone.
   ───────────────────────────────────────────────────────────── */
@media (max-width: 767px) {
  .nx-shell[data-layout-phase] {
    grid-template-columns: 1fr !important;
    grid-template-rows: 1fr !important;
    grid-template-areas: "active" !important;
    height: calc(100vh - 64px - 116px - 16px - 24px) !important;
    height: calc(100svh - 64px - 116px - 16px - 24px) !important;
    margin: 16px 12px 24px;
  }
  .nx-shell[data-layout-phase] .nx-zone {
    grid-area: active;
    border: none !important;
  }
  .nx-shell[data-layout-phase] .nx-zone:not(.is-active-tab) { display: none; }
  .nx-shell[data-layout-phase="enroll"] #zone-drawer {
    display: flex !important;
  }
}

/* Reduced motion — disable FLIP transitions, opacity cross-fade only */
@media (prefers-reduced-motion: reduce) {
  .nx-shell[data-layout-phase] .nx-zone {
    transition: opacity 200ms linear !important;
  }
}

/* Forced colors */
@media (forced-colors: active) {
  .nx-shell[data-layout-phase] {
    border: 2px solid CanvasText !important;
  }
  .nx-shell[data-layout-phase] .nx-zone {
    border-color: CanvasText !important;
  }
}

/* =============================================================
   v0.7 — Sequential focus per phase (item F).
   The phase grid grammar already rearranges zones; these rules add
   content-level suppression so each phase has ONE clear hero.
   ============================================================= */

/* PRICE — dial is hero; mute the "What we know so far" list and trust counter */
.nx-shell[data-layout-phase="price"] .nx-known {
  opacity: .55;
  transition: opacity 320ms cubic-bezier(.22,.61,.36,1);
}
.nx-shell[data-layout-phase="price"] .nx-known:hover,
.nx-shell[data-layout-phase="price"] .nx-known:focus-within { opacity: 1; }

/* COMPARE — plans-grid is hero; the dial collapses to a single sticky number
   strip at the top of the dash zone, so users still know their estimate but
   it doesn't compete for the eye. */
.nx-shell[data-layout-phase="compare"] .value-stack__arc { display: none; }
.nx-shell[data-layout-phase="compare"] .value-stack__cohort { display: none; }
.nx-shell[data-layout-phase="compare"] .value-stack__estimate-note { display: none; }
.nx-shell[data-layout-phase="compare"] .value-stack__primary {
  /* Inline horizontal layout: $X/mo · saving $Y/mo */
  flex-direction: row;
  align-items: baseline;
  gap: 12px;
}
.nx-shell[data-layout-phase="compare"] .value-stack__primary-number .vs-num {
  font-size: 24px;
}
.nx-shell[data-layout-phase="compare"] .value-stack__savings {
  margin-top: 0;
  flex-direction: row;
  gap: 8px;
  align-items: baseline;
}
.nx-shell[data-layout-phase="compare"] .value-stack__savings-primary { font-size: 16px; }
.nx-shell[data-layout-phase="compare"] .value-stack__savings-delta { display: none; }
.nx-shell[data-layout-phase="compare"] .nx-dash-headline,
.nx-shell[data-layout-phase="compare"] .nx-dash-eyebrow { display: none; }
.nx-shell[data-layout-phase="compare"] .nx-known { display: none; }

/* LOCK — selected card is hero; non-selected fade hard, dial mutes more */
.nx-shell[data-layout-phase="lock"] .plan-card:not(.is-selected) {
  opacity: .35;
  filter: grayscale(.4);
  transition: opacity 320ms cubic-bezier(.22,.61,.36,1),
              filter 320ms cubic-bezier(.22,.61,.36,1);
  pointer-events: none;
}
.nx-shell[data-layout-phase="lock"] .nx-bignum-wrap {
  /* Already shrunk by other rules; further fade to defer attention to drawer */
  opacity: .7;
}

/* AUTH-GATE MODAL OPEN — hide background dashboard noise so the modal hero
   (savings number) is the only visible content in the user's field. The
   `.is-auth-modal-open` class is set on body by auth-gate.js when modal mounts
   (item D). */
body.is-auth-modal-open .nx-dash-authskel,
body.is-auth-modal-open #dash-plan-cards-host,
body.is-auth-modal-open .nx-known,
body.is-auth-modal-open .ut-counter {
  visibility: hidden;
}
body.is-auth-modal-open .nx-shell { filter: blur(2px); transition: filter 200ms; }
body.is-auth-modal-open .nx-shell::after {
  content: '';
  position: fixed;
  inset: 0;
  background: rgba(20,30,48,0.5);
  z-index: 9000;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  body.is-auth-modal-open .nx-shell { filter: none; }
  body.is-auth-modal-open .nx-shell::after { background: rgba(20,30,48,0.6); }
}

/* =============================================================
   v0.7 G1 — Email-needed hint at lock time.
   Surfaces under the locked dial when user is in skip mode (no
   email on file). Sets enrollment-step expectation early.
   ============================================================= */
.nx-email-needed-hint {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin: 14px 16px 0;
  padding: 10px 14px;
  background: rgba(255, 200, 87, 0.18);
  border: 1px dashed var(--ink, #0E2A3A);
  border-radius: 10px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.4;
  color: var(--mute, #4A6678);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 380ms cubic-bezier(.22,.61,.36,1),
              transform 380ms cubic-bezier(.22,.61,.36,1);
}
.nx-email-needed-hint.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.nx-email-needed-hint .ne-icon {
  color: var(--ink, #0E2A3A);
  font-weight: 600;
  margin-right: 2px;
}
.nx-email-needed-hint .ne-text {
  flex: 1 1 auto;
  min-width: 200px;
}
.nx-email-needed-hint .ne-link {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink, #0E2A3A);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
  flex-shrink: 0;
}
.nx-email-needed-hint .ne-link:hover {
  color: var(--ink, #0E2A3A);
  text-decoration-thickness: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .nx-email-needed-hint { transition: none; }
}

/* =============================================================
   v1.1 · LOCK-phase layout overhaul
   - Plan cards stack vertically (one column) so they fit the 54%
     dash zone without clipping. Each card reads at full width.
   - Chat zone behaves as a vertical scrollable panel with a usable
     input field instead of a one-message ticker.
   - COMPARE-phase mini ticker (top strip) shows the last 2 messages
     instead of just one.
   ============================================================= */

/* ── B1 · Cards stack vertically at LOCK ── */
.nx-shell[data-layout-phase="lock"] .plan-cards__grid {
  grid-template-columns: 1fr;
  gap: 14px;
}
.nx-shell[data-layout-phase="lock"] .plan-card {
  /* Drop the equal-heights floor (480px). Vertical stacking makes
     comparison happen down the page; cards size to content. */
  min-height: 0;
  padding: 16px 18px 14px;
}
/* Bring back the WHY THIS ONE rationale at LOCK (it's hidden in the old
   tablet-landscape rules earlier in this file). With full card width
   the rationale is the most useful thing on the page. */
.nx-shell[data-layout-phase="lock"] .plan-card__rationale {
  display: block;
}
/* Carrier name has full width to wrap without ellipsis truncation */
.nx-shell[data-layout-phase="lock"] .plan-card__carrier-text {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
/* Keep the chosen card's accent border so the user always sees what's locked */
.nx-shell[data-layout-phase="lock"] .plan-card.is-selected {
  border-color: var(--ink, #0E2A3A);
  box-shadow: 6px 6px 0 var(--ink, #0E2A3A);
}

/* ── B2 + B3 · Compound LOCK/COMPARE chat rules REMOVED (V31 Task 1.2) ──
   Previously these scoped to compound zone-role selectors. The new 2-zone
   shell pins chat at a fixed 420px (desktop) / 360px (tablet) rail, so the
   legacy ticker mode is gone — chat is always full-height. The grid-template-
   rows: 1fr 240px override for LOCK is also obsolete (no more bottom strip).
   Behaviour now matches Task 1.3's main-host swap. */

/* =============================================================
   v31 mobile-ux-overhaul Task 2 — Opt-in zone-pivot toast
   Shows at the bottom of the chat zone when a layoutPhaseChange
   would otherwise yank the user to a different tab. Sits ABOVE the
   bottom-fixed .nx-tabs (z-index 49) so the user sees it before the
   tabs. Hidden on desktop where multi-zone is always visible.
   ============================================================= */
.nx-zone-pivot-toast {
  display: none;
}
@media (max-width: 767px) {
  .nx-zone-pivot-toast {
    display: flex;
    align-items: center;
    gap: 10px;
    position: fixed;
    bottom: calc(64px + env(safe-area-inset-bottom));
    left: 12px;
    right: 12px;
    z-index: 50;
    background: var(--ink, #0E2A3A);
    color: var(--cream, #FFFAEA);
    padding: 12px 14px;
    border-radius: 12px;
    box-shadow: var(--shadow-3, 0 8px 24px rgba(0, 0, 0, 0.18));
    animation: nxZonePivotToastIn 220ms cubic-bezier(.22, .61, .36, 1);
  }
  .nx-zone-pivot-toast__label {
    flex: 1 1 auto;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 12px;
    letter-spacing: 0.04em;
    font-weight: 600;
  }
  .nx-zone-pivot-toast [data-zone-pivot-jump] {
    background: var(--lemon, #FFD84D);
    color: var(--ink, #0E2A3A);
    border: 0;
    padding: 8px 14px;
    border-radius: 8px;
    font-family: 'Inter', system-ui, sans-serif;
    font-weight: 700;
    font-size: 13px;
    min-height: 44px;
    cursor: pointer;
    flex-shrink: 0;
  }
  .nx-zone-pivot-toast [data-zone-pivot-dismiss] {
    background: transparent;
    color: var(--cream, #FFFAEA);
    border: 1px solid rgba(255, 250, 234, 0.25);
    width: 32px;
    height: 32px;
    min-width: 32px;
    padding: 0;
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
    line-height: 1;
    flex-shrink: 0;
  }
}
@keyframes nxZonePivotToastIn {
  from { transform: translateY(40px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* --- V36 UX phase 2 / DAV-N14 --- Chat polish ----------------------------
   Three additive surfaces. None touches existing chat or bubble layout.
   - .nx-thinking            Persistent typing-dots bubble during real LLM
                             fetch (showThinkingBubble / hideThinkingBubble).
                             Reuses the existing .nx-typing dot animation, so
                             only the wrapper class is new — the visual dots
                             match appendTypingIndicator perfectly.
   - .nx-plan-pull-status   Live rotating-status bubble during the 3-7s
                             Quotit plan-options fetch. Italic text + 12px
                             border-spinner. Spinner is .nx-spinner (also
                             reusable elsewhere if we add other live waits).
   - .nora-table            Rendered output of NoraTable.transform (markdown
                             pipe-table parser). Horizontal scroll on
                             narrow screens; tight uppercase headers; same
                             ink-border + cream surface as nx-bub.
   All animations respect prefers-reduced-motion. ------------------------ */

/* Thinking bubble — reuses existing .nx-typing dots inside .nx-bub */
.nx-msg.nx-thinking .nx-bub {
  /* Tight padding so the wrapper is just the dots, no empty visual space. */
  padding: 11px 16px;
}

/* Plan-pull live status — DAV-160: restyled as a status pill, not a bubble.
   Old .nx-msg.nx-plan-pull-status selector kept for legacy reference only. */

/* New pill indicator — sits between chat bubbles as a progress marker */
.nx-plan-pull-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  margin: 8px auto;
  max-width: 280px;
  font-size: 12px;
  color: #64748B;
  background: #F8FAFC;
  border: 1px dashed #CBD5E1;
  border-radius: 999px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-style: italic;
  letter-spacing: 0.02em;
}
.nx-plan-pull-indicator .spinner {
  display: inline-block;
  width: 10px;
  height: 10px;
  border: 2px solid #94A3B8;
  border-top-color: transparent;
  border-radius: 50%;
  animation: nx-spin 1.2s linear infinite;
  flex: none;
}
.nx-plan-pull-indicator .nx-status-text {
  opacity: 0.85;
  transition: opacity 0.18s ease;
}
.nx-spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: nx-spin 0.9s linear infinite;
  vertical-align: middle;
  opacity: 0.55;
  flex: none;
}
@keyframes nx-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .nx-spinner { animation: none; opacity: 0.3; }
}

/* Markdown table — Nora's pipe-table responses (e.g. plan side-by-sides) */
.nora-table-wrap {
  /* Wrapper provides the scroll container + ink border so the table itself
     stays clean and we don't fight cell padding on overflow. */
  margin: 8px 0;
  max-width: 100%;
  overflow-x: auto;
  border: 1.5px solid var(--ink);
  border-radius: 10px;
  background: var(--cream);
  /* Subtle drop shadow to match nx-bub feel */
  box-shadow: var(--shadow-2);
  /* Soften the scrollbar on macOS so it doesn't visually fight the border */
  -webkit-overflow-scrolling: touch;
}
.nora-table-wrap::-webkit-scrollbar { height: 6px; }
.nora-table-wrap::-webkit-scrollbar-thumb {
  background: rgba(14, 42, 58, 0.18);
  border-radius: 4px;
}
.nora-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
  line-height: 1.45;
  background: transparent;
  color: var(--ink);
}
.nora-table th,
.nora-table td {
  padding: 8px 12px;
  text-align: left;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
  vertical-align: top;
}
.nora-table th {
  background: var(--blush-2);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--mute);
  border-bottom: 1.5px solid var(--ink);
}
.nora-table tr:last-child td { border-bottom: 0; }
.nora-table strong { color: var(--ink); font-weight: 700; }
@media (max-width: 480px) {
  .nora-table { font-size: 12px; }
  .nora-table th,
  .nora-table td { padding: 6px 9px; }
  .nora-table th { font-size: 10px; }
}
