/* ═══════════════════════════════════════════════════════════════════════
   NEXUS Design System  •  v2
   ───────────────────────────
   Single source of truth for visual language across NEXUS. v2 incorporates
   patterns from best-in-class AI interfaces (ChatGPT, Claude.ai, Linear,
   Notion AI, Arc) — adapted to the editorial-pill language established
   by the login screen.
   
   Key v2 changes from v1:
     • Bumped base type scale (15.5 → 16) for one-handed legibility
     • Added .nx-prompt — the big tappable suggestion card primitive
     • Added .nx-card--elevated — soft-shadow card variant
     • Refined .nx-section to feel like a real heading, not a divider
     • Added .nx-ask — the hero AI-input bar primitive
     • Touch target floor enforced at 44px (Apple HIG / WCAG)
     • Tightened motion timing — 120ms presses feel snappier than 150ms
   
   USAGE PRINCIPLES
   ────────────────
   • One primary action per screen (gold gradient pill).
   • Secondary actions = outlined gold pill.
   • Tertiary actions = quiet text-link (faint grey).
   • Section headings use spacing for hierarchy, not horizontal rules.
   • Touch targets ≥ 44px tall. Pills are easy on a thumb.
   • Editorial restraint: no extra decoration, no shadow stacks, no
     gradient noise. Gold accents earn their place.
   • Soft shadows beat hairline borders for depth on dark backgrounds.
   
   When adding a new surface, REACH FOR THESE PRIMITIVES FIRST.
   Custom one-off styles are a smell. Add to this file or use what's here.
   
   Load order in index.html:
     nexus.css → nx-system.css → feature CSS files

   ═══════════════════════════════════════════════════════════════════════
   TABLE OF CONTENTS
   ═══════════════════════════════════════════════════════════════════════

       37   1. DESIGN TOKENS — :root (dark) declarations
              · color (gold ladder, red ladder, highlights, labels, semantic)
              · type (font families, size scale)
              · spacing (4px base, 1–12 multipliers)
              · motion (press 120ms, flow 220ms, slow 350ms, cinematic 700ms)
              · radii, shadows, surface stack
      197   1.B  Light-theme overrides ([data-theme="light"])
      309   1.C  Theme-invariant tokens (carry across both themes)
      442   1.D  Surface tokens (translucent stack with theme-aware tints)

      483   2. PRIMITIVES
      483   2.A  .nx-pill family (buttons — primary/secondary/ghost/danger + sm/xs)
       —    .nx-card  / .nx-card--elevated
       —    .nx-section / .nx-section-row
       —    .nx-stat / .nx-stats
       —    .nx-input (text/textarea/select)
       —    .nx-ask  / .nx-ask-input  / .nx-ask-send
       —    .nx-prompt
       —    .nx-alert family (info/warn/danger/neutral + 7 legacy aliases)
       —    .nx-toast v2 (success/info/error/warn + 4 legacy aliases)
       —    .nx-sheet (full/medium/compact, mobile-bottom-anchored)
       —    .nx-speed-dial (FAB-style action menu)
       —    Icon classes (25 context-specific size + color rules)
       —    Status dots (clock-in/out, ticket priority, tab pending)
       —    .eq-modal visual aliasing (modernizes 13 equipment modals)

     1931   3. UTILITIES (.nx-display, .nx-h1, .nx-h2, .nx-faint, etc.)
     1946   4. ACCESSIBILITY (focus-visible, skip-link, sr-only)
     1970   5. THEME AWARENESS (transitions on theme switch)
     1990   6. UNIFIED MASTHEAD (coin + NEXUS wordmark + date row)
     2215   7. BOTTOM NAV REDESIGN (5-button mobile primary nav)

   ═══════════════════════════════════════════════════════════════════════ */


/* ═══ 1. DESIGN TOKENS ════════════════════════════════════════════════ */
:root {
  /* ─── Color: gold accent system ─── */
  --nx-gold:          #d4a44e;
  --nx-gold-deep:     #b88830;
  --nx-gold-darker:   #9a7026;
  --nx-gold-on:       #1c1814;
  /* v523 — gold as TEXT, as distinct from gold as surface/border. On dark
     the accent itself already measures 7.3:1 on our surfaces, so the two
     are the same colour here. In light they cannot be: see the light
     block below. Use --nx-gold-ink for any gold that carries words. */
  --nx-gold-ink:      #d4a44e;

  /* ── THE OTHER THREE ACCENTS, AS INK (v589) ──────────────────────────
     v523 minted --nx-gold-ink because gold that carries WORDS needs a
     different value from gold that carries a surface. The same is true of
     the other three, and nobody had measured them: on the board's tinted
     chips the raw accents came back at 2.87:1 (red on red-soft, "URGENT"),
     2.48:1 (plum, the Este badge) and 3.60:1 (verdigris, Toti) — the
     loudest labels on the card were the hardest to read.
     Computed, not eyeballed: each is its own hue lifted in lightness until
     it clears 4.6:1 on BOTH its own tint over a card and the bare card
     (dark) — measured with scripts/board-legibility-check.cjs.
     Surfaces, borders and strips keep the raw accent. */
  /* A GOLD SOLID FILL THAT --nx-gold-on CAN ACTUALLY SIT ON (v589).
     v451 is owner law — "dark ink on gold, both themes" — but in the light
     theme the gold IS the dark thing (#a8742a), so his dark ink measures
     3.99:1 on it and no darker ink can clear AA. v523 hit this on Home's Ask
     bar and went cream, breaking the law to satisfy the number. This keeps
     BOTH: the fill moves one step lighter in daylight (4.92:1 with the same
     dark ink) and stays exactly --nx-gold in dark, where nothing was wrong.
     Use it for a SOLID gold surface carrying text; --nx-gold stays the
     border, strip and tint colour. */
  --nx-gold-fill:     #d4a44e;
  --nx-red-ink:       #e08585;   /* oxblood lifted — 4.63:1 on red-soft over a card */
  --nx-green-ink:     #48b7a4;   /* verdigris lifted — 4.67:1 on its own tint */
  --nx-purple-ink:    #b4a1c1;   /* plum lifted — 4.61:1 on its own tint */
  --nx-gold-faint:    rgba(212, 164, 78, 0.08);
  --nx-gold-soft:     rgba(212, 164, 78, 0.18);
  --nx-gold-line:     rgba(212, 164, 78, 0.22);
  --nx-gold-line-2:   rgba(212, 164, 78, 0.40);
  --nx-gold-line-3:   rgba(212, 164, 78, 0.62);
  --nx-gold-glow:     rgba(212, 164, 78, 0.18);
  /* Additional opacity steps to absorb the long tail of inline gold rgba
     usage scattered across feature CSS files. Each maps to the nearest
     existing rgba(212,164,78,...) value used in the wild. */
  --nx-gold-tint:     rgba(212, 164, 78, 0.04);   /* very subtle wash */
  --nx-gold-mist:     rgba(212, 164, 78, 0.12);   /* gentle hover */
  --nx-gold-haze:     rgba(212, 164, 78, 0.20);   /* mid-strength fill */
  --nx-gold-aura:     rgba(212, 164, 78, 0.30);   /* strong glow */

  /* ─── Color: text hierarchy ─── */
  --nx-text:          #ece4d4;
  --nx-text-strong:   #f6f0e2;
  --nx-muted:         #9aa3b2;
  --nx-faint:         #6c7585;
  --nx-faintest:      #4e5666;

  /* ─── Color: surface system ─── */
  /* v553 — SOLID surfaces (Alfredo: "remove this glass look", the third
     glass complaint after the bottom nav and the parts sheet). These were
     alpha layers from the backdrop-blur era; over the flat page background
     they composite to a constant color anyway, but on overlays they let
     content bleed through. Each value below is the OLD rgba pre-composited
     against --nx-bg — identical look on backgrounds, opaque everywhere. */
  --nx-bg:            #0e1320;
  --nx-surface-0:     #141b2b;
  --nx-surface-1:     #161e30;
  --nx-surface-2:     #141c2e;
  --nx-surface-3:     #222e45;
  --nx-surface-press: #1f293f;
  --nx-surface:       var(--nx-surface-1);  /* v1 alias */

  /* ─── Color: solid surfaces (for components that don't want translucency) ─── */
  --nx-surface-solid: #161d2e;
  --nx-elevated:      #1f2940;

  /* Bottom nav bar — translucent so the masthead's gold halo bleeds
     through. Dark warm to anchor the editorial × terminal feel. */
  --nx-bottom-nav-bg: rgba(16, 22, 36, 0.92);

  /* ─── Color: borders ─── */
  --nx-border:        rgba(212, 164, 78, 0.10);
  --nx-border-strong: rgba(63, 120, 181, 0.28);

  /* ─── Color: cool accents (non-status — links, info chips) ─── */
  --nx-blue:          #3f78b5;
  --nx-purple:        #8a6c9e;

  /* ─── Color: highlight overlays ─────────────────────────────────────
     Subtle tints used to LIFT an element above its parent. In dark mode
     they're translucent white (which lightens the dark BG); in light
     mode they swap to translucent black (which darkens the cream BG).
     Same property, opposite direction — the visual effect (this thing
     is above the BG) reads correctly in both themes. */
  --nx-highlight-tint:   rgba(255, 255, 255, 0.03);
  --nx-highlight-faint:  rgba(255, 255, 255, 0.05);
  --nx-highlight-soft:   rgba(255, 255, 255, 0.08);
  --nx-highlight-line:   rgba(255, 255, 255, 0.10);
  --nx-highlight-mid:    rgba(255, 255, 255, 0.15);

  /* ─── Color: label palette ──────────────────────────────────────────
     8 distinct tokens for user-pickable label colors (used in Board
     custom labels and similar). Each has a dedicated light-mode value
     so labels stay distinguishable on both themes. The dark values
     are saturated; light values are deeper for AA contrast on cream. */
  --nx-label-1: #d45555;   /* oxblood — v593, lifted 2.4% in lightness so the chip
                              can carry 10.5px text: 4.28:1 → 4.61:1 with the
                              espresso ink. Alfredo approved the move. */
  --nx-label-2: #d4a44e;   /* brand gold */
  --nx-label-3: #3fa08f;   /* verdigris (bronze patina) */
  --nx-label-4: #6b6258;   /* graphite */
  --nx-label-5: #9074a3;   /* muted plum — v593, same reason: 4.16:1 → 4.60:1 */
  --nx-label-6: #d4c8a5;   /* parchment */
  --nx-label-7: #7a8db8;   /* denim slate */
  --nx-label-8: #a49c94;   /* taupe */

  /* ─── Color: semantic ─────────────────────────────────────────────
     Editorial palette — desaturated, warm-shifted to live alongside
     the gold accent without competing with it. The previous bright
     kelly green (#5bba5f) and saturated red (#d45858) were carried
     over from the v0 prototype palette; replaced with olive bronze
     and oxblood that read naturally against the cream/charcoal
     surfaces NEXUS uses. The :soft and :line variants stay token-
     derived so they automatically follow when the base shifts.   */
  --nx-red:           #d24b4b;                          /* oxblood */
  --nx-red-soft:      rgba(210, 75, 75, 0.12);
  --nx-red-line:      rgba(210, 75, 75, 0.38);
  /* Additional opacity steps for red — same pattern as gold */
  --nx-red-tint:      rgba(210, 75, 75, 0.05);
  --nx-red-faint:     rgba(210, 75, 75, 0.10);
  --nx-red-mist:      rgba(210, 75, 75, 0.14);
  --nx-red-haze:      rgba(210, 75, 75, 0.22);
  --nx-red-aura:      rgba(210, 75, 75, 0.32);
  --nx-amber:         #d4a44e;                          /* same as gold — warning ≈ attention */
  --nx-green:         #3fa08f;                          /* verdigris (bronze patina) */
  --nx-green-soft:    rgba(63, 160, 143, 0.14);
  --nx-green-line:    rgba(63, 160, 143, 0.38);

  /* ─── Spacing scale ─── */
  --nx-space-1:       4px;
  --nx-space-2:       8px;
  --nx-space-3:       12px;
  --nx-space-4:       16px;
  --nx-space-5:       20px;
  --nx-space-6:       24px;
  --nx-space-7:       32px;
  --nx-space-8:       40px;
  --nx-space-9:       48px;
  --nx-space-10:      56px;
  --nx-space-12:      72px;

  /* ─── Layout chrome heights ───
     Single source of truth so popups/sheets that anchor to chrome
     (speed-dials over the bottom nav, etc.) don't need to guess. */
  --nx-bottom-nav-h:  65px;  /* v451 — matches the REAL .bottom-nav height (v404); was 56 and unused */

  /* ─── Radii ─── */
  --nx-radius-pill:   999px;
  --nx-radius-card:   22px;
  --nx-radius-tight:  14px;
  --nx-radius-tighter: 10px;

  /* ─── Pill component sizes ─── */
  --nx-pill-pad:      14px 22px;
  --nx-pill-pad-sm:   11px 18px;
  --nx-pill-pad-xs:   8px 14px;

  /* ─── Touch target floor ─── */
  --nx-touch-min:     44px;

  /* ─── Type ─── */
  --nx-font-display:  'Outfit', 'DM Sans', system-ui, sans-serif;
  --nx-font-body:     'DM Sans', 'Outfit', system-ui, sans-serif;
  --nx-font-mono:     'JetBrains Mono', 'Courier New', monospace;

  /* Type scale */
  --nx-text-xs:       12px;
  --nx-text-sm:       13.5px;
  --nx-text-base:     16px;
  --nx-text-md:       17px;
  --nx-text-lg:       19px;
  --nx-text-xl:       22px;
  --nx-text-2xl:      28px;
  --nx-text-3xl:      40px;
  --nx-text-4xl:      48px;

  /* ─── Shadows ─── */
  --nx-shadow-soft:   0 1px 2px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.18);
  --nx-shadow-card:   0 2px 4px rgba(0,0,0,0.20), 0 8px 24px rgba(0,0,0,0.30);
  --nx-shadow-pill:   0 4px 14px rgba(212, 164, 78, 0.22);
  --nx-shadow-fab:    0 8px 28px rgba(0, 0, 0, 0.45);

  /* ─── Motion ─── */
  --nx-ease:          cubic-bezier(0.4, 0, 0.2, 1);
  --nx-press:         0.12s var(--nx-ease);
  --nx-flow:          0.22s var(--nx-ease);
  --nx-slow:          0.35s var(--nx-ease);
  --nx-cinematic:     0.70s var(--nx-ease);
}


/* ════════════════════════════════════════════════════════════════════
   LIGHT-THEME TOKEN OVERRIDES
   --------------------------------------------------------------------
   THE root-cause fix for light-mode coverage: every feature CSS file
   (board-system, admin-system, views-system, ingest-system, composer,
   galaxy, chat, home) references --nx-surface-*, --nx-text, --nx-muted,
   --nx-faint, --nx-bg, --nx-gold-* — but the original :root block
   defined those with no light variant. Redefining tokens here means
   every component using them auto-corrects with no per-selector edits.

   When something looks wrong in light mode, FIRST CHECK if the offender
   is using a token. If yes, the token is the fix. Only add a per-
   selector light rule if the offender uses literal colors that bypass
   tokens.

   Mapping rationale:
     - Surfaces: warm white / off-white with cream undertones. Mirrors
       the dark palette's slight warm cast (oil-lamp, not surgical).
     - Text: warm near-black, not pure black. Reduces eye strain on cream.
     - Muted/faint: gold-tinted browns instead of grey. Keeps the
       "editorial × terminal" voice consistent across themes.
     - Gold: shifted to deeper olive-gold (var(--accent) vs dark's var(--accent))
       so gold borders read as borders on cream backgrounds. The dark
       gold at 22% opacity disappears on light surfaces.
   ═════════════════════════════════════════════════════════════════ */
[data-theme="light"] {
  /* Page + surfaces */
  --nx-bg:            #e3d9c4;
  /* v553 — solid (pre-composited against the linen bg; see dark block). */
  --nx-surface-0:     #f0eadd;
  --nx-surface-1:     #f4f1e7;
  --nx-surface-2:     #f9f7f1;
  --nx-surface-3:     #fdfcf8;
  --nx-surface-press: #d6cbb5;
  --nx-surface:       var(--nx-surface-1);

  /* Solid surfaces (non-translucent variant) */
  --nx-surface-solid: #fbf8f1;
  --nx-elevated:      #fffefb;

  /* Bottom nav — cream-tinted to match the page so the bar feels
     integrated with the daylit-ledger aesthetic, not pasted on. */
  --nx-bottom-nav-bg: rgba(233, 225, 207, 0.92);

  /* Borders — warm brown-tinted, visible but gentle on cream.
     v571: the comment above always said "warm brown-tinted" but the
     values were steel-blue rgba(31,79,125,…) — every token hairline on
     the cream page read faintly cold. Now they match the words. */
  --nx-border:        rgba(93, 72, 48, 0.16);
  --nx-border-strong: rgba(93, 72, 48, 0.30);

  /* Cool accents — saturated on light, AA-contrast against cream */
  --nx-blue:          #1f4f7d;
  --nx-purple:        #6D28D9;

  /* Highlight overlays — flip white→dark so "lifted" reads as darker on
     cream. v571: espresso-tinted, not pure black — rgba(0,0,0,…) reads
     BLUE by contrast on a warm page (house shadow law). */
  --nx-highlight-tint:   rgba(59, 44, 26, 0.025);
  --nx-highlight-faint:  rgba(59, 44, 26, 0.04);
  --nx-highlight-soft:   rgba(59, 44, 26, 0.06);
  --nx-highlight-line:   rgba(59, 44, 26, 0.08);
  --nx-highlight-mid:    rgba(59, 44, 26, 0.12);

  /* Label palette — deeper variants for cream-bg AA contrast */
  --nx-label-1: #b83a2e;   /* oxblood deeper */
  --nx-label-2: #8b6914;   /* brand gold deeper */
  --nx-label-3: #2b7a69;   /* olive deeper — v593, deepened 0.9% so the cream ink
                              clears on it: 4.45:1 → 4.64:1 */
  --nx-label-4: #4a4a44;   /* graphite deeper */
  --nx-label-5: #5d4880;   /* plum deeper */
  --nx-label-6: #8b7e58;   /* parchment deeper (visible on cream) */
  --nx-label-7: #4a6090;   /* denim deeper */
  --nx-label-8: #6b6258;   /* taupe deeper */

  /* Text scale — v571 THE WARM-INK CONVERSION. These were cool blue-gray
     navy (#1e2a3c / #14202f / #5b6573) on the warm cream page — exactly
     the ink class the house palette law forbids, and the single biggest
     cause of light theme reading as "beige mush" (Alfredo, Home revamp).
     Espresso family now: 13.3:1 / 15.6:1 / 6.4:1 on #f0eadd. */
  --nx-text:          #2b2018;
  --nx-text-strong:   #1c130c;
  --nx-muted:         #5e5040;
  --nx-faint:         #6b6156;  /* v451 — was #857D72 (2.9:1 on cream); ~4.5:1 now. Masthead date + bottom-nav labels live here. */
  --nx-faintest:      rgba(133, 125, 114, 0.55);

  /* Gold tonality — deeper olive-gold for daylight contrast */
  --nx-gold:          #a8742a;
  --nx-gold-deep:     #8f5f1f;
  --nx-gold-darker:   #6f4a18;
  /* v523 — gold as TEXT. --nx-gold (#a8742a) measures 3.65:1 on our cream
     card and 2.96:1 on the gold-tinted decisions card: every gold word in
     light theme was failing AA, including all the section headings. This
     is the same hue two steps deeper — 5.1:1 on the page ground, 4.9:1 on
     the gold tint, 6.0:1 on a card. Surfaces and borders keep --nx-gold. */
  --nx-gold-ink:      #7d5416;
  /* v589 — the same three as ink, darkened for daylight. Red and plum
     already cleared at their raw values, so the tokens simply name them;
     verdigris did not (3.72:1 on the Toti chip) and is two steps deeper. */
  --nx-gold-fill:     #b8843a;   /* v589 — one step lighter so --nx-gold-on clears 4.5 */
  --nx-red-ink:       #973026;   /* 4.65:1 on red-soft over a card AND over the
                                    gold-tinted controls panel, which is the
                                    darker of the two grounds — tune to the worst */
  --nx-green-ink:     #226053;   /* 4.61:1 on both, same reason — was 3.72 raw */
  --nx-purple-ink:    #5d4880;   /* 5.95:1 on its own tint */
  --nx-gold-on:       #2a1f10;  /* v451 — was #FAFAF6 (white-on-gold, against house law). Dark ink on gold, both themes, matching v448. */
  --nx-gold-faint:    rgba(168, 116, 42, 0.08);
  --nx-gold-soft:     rgba(168, 116, 42, 0.15);
  --nx-gold-line:     rgba(168, 116, 42, 0.24);
  --nx-gold-line-2:   rgba(168, 116, 42, 0.42);
  --nx-gold-line-3:   rgba(168, 116, 42, 0.60);
  --nx-gold-glow:     rgba(212, 164, 78, 0.22);
  /* Light-mode counterparts for the new opacity steps */
  --nx-gold-tint:     rgba(168, 116, 42, 0.04);
  --nx-gold-mist:     rgba(168, 116, 42, 0.12);
  --nx-gold-haze:     rgba(168, 116, 42, 0.20);
  --nx-gold-aura:     rgba(168, 116, 42, 0.28);

  /* Semantic colors — deeper for cream-bg AA contrast */
  --nx-red:           #b83a2e;
  --nx-red-soft:      rgba(184, 58, 46, 0.12);
  --nx-red-line:      rgba(184, 58, 46, 0.32);
  --nx-red-tint:      rgba(184, 58, 46, 0.04);
  --nx-red-faint:     rgba(184, 58, 46, 0.08);
  --nx-red-mist:      rgba(184, 58, 46, 0.12);
  --nx-red-haze:      rgba(184, 58, 46, 0.20);
  --nx-red-aura:      rgba(184, 58, 46, 0.28);
  --nx-amber:         #a8742a;             /* same as gold — warning ≈ attention */
  --nx-green:         #2c7d6c;
  --nx-green-soft:    rgba(44, 125, 108, 0.12);
  --nx-green-line:    rgba(44, 125, 108, 0.32);

  /* Shadows — softer on linen, less black */
  --nx-shadow-soft:   0 1px 2px rgba(60, 50, 30, 0.08), 0 4px 12px rgba(60, 50, 30, 0.06);
  --nx-shadow-card:   0 2px 4px rgba(60, 50, 30, 0.10), 0 8px 24px rgba(60, 50, 30, 0.10);
  --nx-shadow-pill:   0 2px 6px rgba(139, 105, 20, 0.18);
  --nx-shadow-fab:    0 8px 28px rgba(60, 50, 30, 0.18);
}


/* ════════════════════════════════════════════════════════════════════
   SYSTEM PRIMITIVES — apply globally regardless of theme
   --------------------------------------------------------------------
   Cross-cutting rules that affect every interactive surface. Live
   here so they're impossible to miss when reading the design system.
   ═════════════════════════════════════════════════════════════════ */

/* ─── Suppress iOS tap highlight everywhere ─── */
* {
  -webkit-tap-highlight-color: transparent;
}

/* ─── Universal rounding for native form controls ───
   Bumps default 2-4px corners on form elements and buttons up to 12px.
   :where() keeps specificity zero so per-component rules always win. */
:where(button, .btn, input[type="text"], input[type="number"],
       input[type="email"], input[type="password"], input[type="tel"],
       textarea, select) {
  border-radius: 16px;
}

/* ─── Tap target minimums for primary interactive surfaces ───
   Below the WCAG 2.5.5 / Apple HIG / Material 44px floor causes
   missed taps on phones, especially with one-hand use. Minimums
   only — components can be larger but never smaller. */
:where(.bnav-btn, .cv-icon-btn, .cv-back, .util-btn,
       .nx-mast-coin, .cv-plus, .cv-send) {
  min-width: 44px;
  min-height: 44px;
}

/* ─── Universal press feedback ───
   Subtle scale on :active for surfaces that don't define their own
   active state. :where() keeps specificity zero. Skipped during
   reduced-motion (see below). */
:where(.nx-pill,
       .home-glance-item,
       .home-cal-day,
       .home-priority,
       .util-btn,
       .cv-icon-btn,
       .cv-back,
       .cv-plus,
       .cv-send,
       .cv-action-chip,
       .chat-action-btn,
       .prefs-persona-btn,
       .prefs-theme-btn,
       .prefs-tone-btn,
       .prefs-lang-btn):active {
  transform: scale(0.97);
  transition: transform 80ms ease;
}

/* ─── Disabled state primitive ───
   Standard look for disabled interactive elements across the app.
   Two patterns supported: native [disabled] attribute and .is-disabled
   class (for non-form elements like divs styled as buttons). */
:where(button, input, textarea, select)[disabled],
:where(button, input, textarea, select).is-disabled,
.is-disabled {
  opacity: 0.45;
  cursor: not-allowed !important;
  pointer-events: none;
}

/* ─── Focus states (keyboard only) ───
   :focus-visible fires on Tab navigation but not mouse/touch, so
   this gives accessibility users a clear target without polluting
   the touch UX. WCAG 2.4.7 compliant — 2px outline at 2px offset. */
:where(button, a, input, textarea, select, [tabindex]):focus {
  outline: none;
}
:where(button, a, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--nx-gold);
  outline-offset: 2px;
  /* v451 — no border-radius here: forcing 12px made elements visibly change
     shape the moment they received keyboard focus. The ring follows the
     element's own radius. */
}

/* ─── Selection + caret ───
   Browser-default selection blue is jarring against the warm palette.
   Match the active gold accent for both themes. */
::selection {
  background: var(--nx-gold-soft);
  color: inherit;
}
input, textarea {
  caret-color: var(--nx-gold);
}

/* ─── Scrollbars ───
   Theme-aware, thin, gold-tinted. Firefox via scrollbar-* properties,
   WebKit/Chromium via ::-webkit-scrollbar pseudo-elements. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--nx-gold-soft) transparent;
}
*::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background: var(--nx-gold-soft);
  border-radius: 4px;
}
*::-webkit-scrollbar-thumb:hover {
  background: var(--nx-gold-line-2);
}

/* ─── Skeleton loading shimmer ───
   Gold-tinted gradient that reads on both themes. The :root token
   override above takes care of the shift; this rule defines the
   shape once. */
.home-skeleton,
.skeleton,
.cv-skeleton {
  background: linear-gradient(90deg,
    var(--nx-gold-faint) 0%,
    var(--nx-gold-soft) 50%,
    var(--nx-gold-faint) 100%);
  background-size: 200% 100%;
  animation: nx-skeleton-shimmer 1.6s ease-in-out infinite;
  border-radius: 16px;
}
@keyframes nx-skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}


/* ════════════════════════════════════════════════════════════════════
   REDUCED MOTION — system-wide accessibility
   --------------------------------------------------------------------
   When the OS reports prefers-reduced-motion, we don't disable all
   feedback (people still need to see something happened) — we shorten
   transitions and disable specific dramatic animations (cinematic
   coin flip, persona glitch, skeleton shimmer, scale-on-press). Theme
   cross-fades stay enabled at reduced duration so theme changes are
   still legible.
   ═════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* But keep theme cross-fades visible — short, not snapped, so the
     change reads. Without this exception, theme swaps look broken. */
  :root.theme-transitioning,
  :root.theme-transitioning *,
  :root.theme-transitioning *::before,
  :root.theme-transitioning *::after {
    transition-duration: 200ms !important;
  }
  /* Disable the universal :active scale — the press feedback survives
     via outline/color changes, which don't depend on motion. */
  :where(.nx-pill, .home-glance-item, .home-cal-day, .home-priority,
         .util-btn, .cv-icon-btn, .cv-back, .cv-plus, .cv-send,
         .cv-action-chip, .chat-action-btn, .prefs-persona-btn,
         .prefs-theme-btn, .prefs-tone-btn, .prefs-lang-btn):active {
    transform: none !important;
  }
}

/* Bump the base font-size so the whole app reads a hair larger. */
html {
  font-size: var(--nx-text-base);
}


/* ═══ 2. PRIMITIVES ══════════════════════════════════════════════════ */

/* ─── PILL BUTTON ─────────────────────────────────────────────────── */
.nx-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nx-space-2);
  padding: var(--nx-pill-pad);
  min-height: var(--nx-touch-min);
  border-radius: var(--nx-radius-pill);
  border: 1px solid transparent;
  background: transparent;
  color: var(--nx-text);
  font-family: var(--nx-font-display);
  font-size: var(--nx-text-base);
  font-weight: 600;
  letter-spacing: 0.5px;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--nx-press),
    border-color var(--nx-press),
    color var(--nx-press),
    transform var(--nx-press),
    box-shadow var(--nx-press);
  white-space: nowrap;
  user-select: none;
  -webkit-user-select: none;
}
.nx-pill:active { transform: scale(0.97); }
.nx-pill:disabled,
.nx-pill[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none !important;
}

.nx-pill--primary {
  background: linear-gradient(180deg, var(--nx-gold) 0%, var(--nx-gold-deep) 100%);
  color: var(--nx-gold-on);
  border-color: transparent;
  font-weight: 700;
  letter-spacing: 0.6px;
  box-shadow: var(--nx-shadow-pill);
}
.nx-pill--primary:active {
  background: linear-gradient(180deg, var(--nx-gold-deep) 0%, var(--nx-gold-darker) 100%);
}

.nx-pill--secondary {
  background: transparent;
  border-color: var(--nx-gold-line-2);
  color: var(--nx-gold);
  font-weight: 600;
}
.nx-pill--secondary:active {
  background: var(--nx-gold-faint);
  border-color: var(--nx-gold-line-3);
}

.nx-pill--ghost {
  background: var(--nx-gold-faint);
  border-color: var(--nx-gold-line);
  color: var(--nx-gold);
  font-weight: 500;
}
.nx-pill--ghost:active {
  background: var(--nx-gold-soft);
}

.nx-pill--quiet {
  background: transparent;
  border: none;
  color: var(--nx-faint);
  font-weight: 500;
  letter-spacing: 0.3px;
  padding: var(--nx-space-2) var(--nx-space-4);
  min-height: 0;
}
.nx-pill--quiet:active {
  color: var(--nx-gold);
}

.nx-pill--danger {
  background: var(--nx-red-soft);
  border-color: var(--nx-red-line);
  color: var(--nx-red);
  font-weight: 600;
}
.nx-pill--danger:active {
  background: rgba(180, 80, 80, 0.22);
}

.nx-pill--sm {
  padding: var(--nx-pill-pad-sm);
  font-size: var(--nx-text-sm);
  letter-spacing: 0.4px;
  min-height: 40px;
}
.nx-pill--xs {
  padding: var(--nx-pill-pad-xs);
  font-size: var(--nx-text-xs);
  letter-spacing: 0.3px;
  min-height: 32px;
}

.nx-pill--block { width: 100%; }


/* ─── CARD ─────────────────────────────────────────────────────────  */
.nx-card {
  background: var(--nx-surface-1);
  border: 1px solid rgba(212, 164, 78, 0.14);
  border-radius: var(--nx-radius-card);
  padding: var(--nx-space-5) var(--nx-space-4);
  display: flex;
  flex-direction: column;
}
.nx-card--elevated {
  background: var(--nx-surface-1);
  border: 1px solid rgba(255, 255, 255, 0.04);
  box-shadow: var(--nx-shadow-soft);
}


/* ─── SECTION HEADING ────────────────────────────────────────────── */
.nx-section {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--nx-space-4);
  margin: var(--nx-space-9) 0 var(--nx-space-5);
  font-family: var(--nx-font-display);
  font-weight: 500;
  font-size: var(--nx-text-xl);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--nx-text);
  border: none;
  padding: 0;
}
.nx-section-title {
  flex: 1;
  min-width: 0;
}
.nx-section-meta {
  flex-shrink: 0;
  font-family: var(--nx-font-mono);
  font-size: var(--nx-text-xs);
  font-weight: 500;
  letter-spacing: 1px;
  color: var(--nx-faint);
  text-transform: uppercase;
}
.nx-section--first { margin-top: var(--nx-space-6); }


/* ─── PROMPT CARD ────────────────────────────────────────────────── 
   Big tappable suggestion card. The canonical "tap to start" surface
   used in chat empty state, equipment quick actions, etc. */
.nx-prompt {
  display: flex;
  flex-direction: column;
  gap: var(--nx-space-2);
  padding: var(--nx-space-5);
  min-height: 88px;
  background: var(--nx-surface-1);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--nx-radius-card);
  color: var(--nx-text);
  font-family: var(--nx-font-body);
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--nx-press),
    border-color var(--nx-press),
    transform var(--nx-press),
    box-shadow var(--nx-press);
  box-shadow: var(--nx-shadow-soft);
  width: 100%;
}
.nx-prompt:hover {
  background: var(--nx-surface-3);
  border-color: var(--nx-gold-line);
  transform: translateY(-1px);
}
.nx-prompt:active {
  transform: translateY(0) scale(0.99);
  border-color: var(--nx-gold-line-2);
  background: var(--nx-surface-3);
}
.nx-prompt-kicker {
  font-family: var(--nx-font-mono);
  font-size: var(--nx-text-xs);
  font-weight: 600;
  letter-spacing: 1.6px;
  color: var(--nx-gold);
  text-transform: uppercase;
}
.nx-prompt-text {
  font-size: var(--nx-text-md);
  line-height: 1.4;
  color: var(--nx-text);
  font-weight: 400;
  letter-spacing: 0.05px;
}


/* ─── LIST ROW ───────────────────────────────────────────────────── */
.nx-listrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--nx-space-3);
  padding: var(--nx-space-3) var(--nx-space-1);
  min-height: var(--nx-touch-min);
  border-bottom: 1px solid rgba(212, 164, 78, 0.08);
  background: transparent;
  border-radius: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--nx-press);
  border-left: none;
  border-right: none;
  border-top: none;
  width: 100%;
  font-family: inherit;
  color: inherit;
  text-align: left;
}
.nx-listrow:last-child { border-bottom: none; }
.nx-listrow:active { background: var(--nx-gold-faint); }

.nx-listrow-primary {
  flex: 1;
  min-width: 0;
  font-size: var(--nx-text-base);
  color: var(--nx-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.nx-listrow-meta {
  flex-shrink: 0;
  font-family: var(--nx-font-mono);
  font-size: var(--nx-text-xs);
  letter-spacing: 0.5px;
  color: var(--nx-faint);
}


/* ─── STAT CELL ──────────────────────────────────────────────────── */
.nx-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--nx-space-2);
  padding: var(--nx-space-5) var(--nx-space-3) var(--nx-space-4);
  border-radius: var(--nx-radius-pill);
  border: 1px solid var(--nx-gold-line);
  background: var(--nx-gold-faint);
  cursor: pointer;
  font-family: inherit;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--nx-press),
    border-color var(--nx-press),
    transform var(--nx-press);
  text-align: center;
  min-height: 92px;
}
.nx-stat:active {
  transform: scale(0.97);
  background: var(--nx-gold-soft);
  border-color: var(--nx-gold-line-3);
}
.nx-stat-num {
  font-family: var(--nx-font-mono);
  font-size: var(--nx-text-2xl);
  font-weight: 500;
  color: var(--nx-text);
  line-height: 1;
  letter-spacing: -0.5px;
  font-variant-numeric: tabular-nums;
}
.nx-stat-num.is-loading {
  color: var(--nx-faint);
  opacity: 0.5;
}
.nx-stat-num.is-alert {
  color: var(--nx-red);
}
.nx-stat-label {
  font-family: var(--nx-font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 1.6px;
  color: var(--nx-faint);
  text-transform: uppercase;
}


/* ─── INPUT (text + select) ──────────────────────────────────────── */
.nx-input,
.nx-select {
  width: 100%;
  background: var(--nx-surface-2);
  border: 1px solid var(--nx-gold-line);
  border-radius: var(--nx-radius-tight);
  color: var(--nx-text);
  padding: 13px 16px;
  min-height: var(--nx-touch-min);
  font-family: var(--nx-font-body);
  font-size: var(--nx-text-base);
  -webkit-appearance: none;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
  transition: border-color var(--nx-press);
}
.nx-input:focus,
.nx-select:focus {
  outline: none;
  border-color: var(--nx-gold-line-3);
}
.nx-select {
  text-align: center;
  text-align-last: center;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23857f75' stroke-width='2'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 14px;
  padding-right: 38px;
}


/* ─── ASK BAR — the hero AI input ────────────────────────────────── 
   The big primary AI-input bar on home and chat empty state. Solid
   gold gradient, sticky-fixed at bottom. The screen's primary action. */
.nx-ask {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nx-space-2);
  width: 100%;
  padding: 18px 26px;
  min-height: 56px;
  border-radius: var(--nx-radius-pill);
  border: none;
  background: linear-gradient(180deg, var(--nx-gold) 0%, var(--nx-gold-deep) 100%);
  color: var(--nx-gold-on);
  font-family: var(--nx-font-display);
  font-size: var(--nx-text-md);
  font-weight: 700;
  letter-spacing: 0.6px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  box-shadow:
    0 8px 28px rgba(0, 0, 0, 0.45),
    0 4px 16px rgba(212, 164, 78, 0.30);
  transition:
    background var(--nx-press),
    transform var(--nx-press),
    box-shadow var(--nx-press);
}
.nx-ask:active {
  background: linear-gradient(180deg, var(--nx-gold-deep) 0%, var(--nx-gold-darker) 100%);
  transform: scale(0.985);
}


/* ─── 2.X  ICON CLASSES ──────────────────────────────────────────────
   Lucide icons get inserted via <i data-lucide="..."> across the app.
   These classes give each *context* a sensible default (size, color,
   alignment) so icons don't render as 1em cubes by accident. They're
   all designed to inherit currentColor so the icon adopts whatever
   text color the parent uses — this means a button gets a gold icon,
   a faint link gets a faint icon, etc.

   Sizing convention:
     - In a pill / button:   14px
     - In a card / section:  18-20px
     - As a section header:  24px
     - Inline meta chip:     12px

   Special cases at the bottom (status dots, kicker chips). */

/* Generic in-button icon — for primary/secondary pill buttons */
.btn-icon, .b-btn-icon, .eq-btn-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
}

/* Notes panel icons (edit, attach) */
.np-edit-icon, .np-add-file-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
}

/* Ingest tool buttons & admin tabs */
.ig-tool-icon, .at-tab-icon {
  width: 16px; height: 16px;
  vertical-align: -3px;
  flex-shrink: 0;
  stroke-width: 1.75;
  margin-right: 2px;
}

/* Calendar event icons */
.cal-event-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
  color: var(--nx-faint);
  margin-right: 2px;
}
.cal-event-icon-fallback {
  display: inline-block;
  width: 14px;
  text-align: center;
  color: var(--nx-faintest);
}

/* Log feed icons (task status, system events) */
.feed-task-icon, .feed-sys-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
}
.feed-sys-icon {
  /* Render container — the inner <i data-lucide=...> picks up these attrs */
  display: inline-flex;
  align-items: center;
  width: 16px; height: 16px;
  color: var(--nx-faint);
}
.feed-sys-icon i {
  width: 14px; height: 14px;
  stroke-width: 1.75;
}

/* AI chat action icon (in tool-action chip) */
.action-icon, .agenda-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
  color: var(--nx-gold);
  margin-right: 4px;
}

/* Inline hint, meta, badge icons (smallest size) */
.hint-icon, .meta-icon, .badge-icon {
  width: 12px; height: 12px;
  vertical-align: -1px;
  flex-shrink: 0;
  stroke-width: 2;
  margin-right: 2px;
}

/* Ordering view warn icon (inline with text) */
.ord-warn-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  color: var(--nx-amber);
  flex-shrink: 0;
  stroke-width: 1.75;
}

/* Ticket form icons */
.ticket-cam-icon {
  width: 16px; height: 16px;
  vertical-align: -3px;
  flex-shrink: 0;
  stroke-width: 1.75;
  margin-right: 4px;
}

/* Galaxy attachment-type icon */
.galaxy-attach-icon {
  width: 14px; height: 14px;
  vertical-align: -2px;
  flex-shrink: 0;
  stroke-width: 1.75;
  color: var(--nx-faint);
}

/* ─── Status dots ──────────────────────────────────────────────
   Token-driven colored dots that replace emoji presence/priority
   indicators (clock-in/out, ticket priority). 8px circles with a
   subtle outer ring so they read as states, not decoration. */
.feed-clock-dot, .ticket-pri-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  vertical-align: 1px;
  margin-right: 4px;
  flex-shrink: 0;
}

.feed-clock-in    { background: var(--nx-green); box-shadow: 0 0 0 2px var(--nx-green-soft); }
.feed-clock-out   { background: var(--nx-red);   box-shadow: 0 0 0 2px var(--nx-red-soft); }

.ticket-pri-dot                       { background: var(--nx-faintest); }
.ticket-pri-btn[data-pri="low"]    .ticket-pri-dot { background: var(--nx-green); }
.ticket-pri-btn[data-pri="normal"] .ticket-pri-dot { background: var(--nx-amber); }
.ticket-pri-btn[data-pri="urgent"] .ticket-pri-dot { background: var(--nx-red);   }

/* ─── Kicker chip ──────────────────────────────────────────────
   Mono-caps label that prefixes status text in alerts/banners.
   Replaces the emoji prefix (🚨 OVERDUE → "OVERDUE" with kicker
   styling). Color inherits from the alert variant. */
.alert-kicker {
  font-family: var(--nx-font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 3px;
  background: rgba(0,0,0,0.18);
  margin-right: 8px;
  vertical-align: 1px;
}

/* Calendar urgent status — token-driven, replaces inline #e88 */
.cal-status-urgent {
  color: var(--nx-red) !important;
  border-color: var(--nx-red-line) !important;
  background: var(--nx-red-soft) !important;
  font-weight: 700;
  letter-spacing: 0.6px;
}


/* ─── 2.Y  ALERTS — .nx-alert ────────────────────────────────────────
   Banner-style alerts that sit beneath the masthead. Replaces the
   four legacy systems (offline-banner, alert-banner, api-banner,
   clean-edit-banner) plus the inline alert-contractor/alert-overdue
   blocks. Single primitive, four tonal variants, theme-aware.

   Markup pattern:
     <div class="nx-alert nx-alert--warn">
       <span class="alert-kicker">WARNING</span>
       <span class="nx-alert-body">Some message text here.</span>
       <button class="nx-alert-dismiss" aria-label="Dismiss">✕</button>
     </div>

   Variants: --info (gold), --warn (amber), --danger (red), --neutral (muted)

   The legacy classes (.offline-banner, .alert-banner, .api-banner,
   .alert-contractor, .alert-overdue, .clean-edit-banner) are aliased
   to .nx-alert variants further down in this file so existing markup
   keeps working until call sites are migrated. */
.nx-alert {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-family: var(--nx-font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.2px;
  background: var(--nx-surface-2);
  border-bottom: 1px solid var(--nx-border-strong);
  color: var(--nx-text);
  
  
  animation: nxAlertIn var(--nx-flow);
}
@keyframes nxAlertIn {
  from { transform: translateY(-100%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
.nx-alert-body {
  flex: 1;
  min-width: 0;
  line-height: 1.4;
  /* Inline links inside an alert get the variant's color */
}
.nx-alert-body a,
.nx-alert-body button:not(.nx-alert-dismiss) {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.nx-alert-dismiss {
  flex-shrink: 0;
  width: 26px; height: 26px;
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity var(--nx-press), background var(--nx-press);
}
.nx-alert-dismiss:hover { opacity: 1; background: rgba(0,0,0,0.10); }

/* ── Variants ── */
.nx-alert--info {
  background: var(--nx-gold-soft);
  border-bottom-color: var(--nx-gold-line-2);
  color: var(--nx-gold);
}
.nx-alert--info .alert-kicker { background: var(--nx-gold); color: var(--nx-bg); }

.nx-alert--warn {
  background: var(--nx-gold-soft);
  border-bottom-color: var(--nx-gold-line-2);
  color: var(--nx-amber);
}
.nx-alert--warn .alert-kicker { background: var(--nx-amber); color: var(--nx-bg); }

.nx-alert--danger {
  background: var(--nx-red-soft);
  border-bottom-color: var(--nx-red-line);
  color: var(--nx-red);
}
.nx-alert--danger .alert-kicker { background: var(--nx-red); color: #fff; }

.nx-alert--neutral {
  background: var(--nx-surface-2);
  border-bottom-color: var(--nx-border-strong);
  color: var(--nx-muted);
}
.nx-alert--neutral .alert-kicker { background: var(--nx-faint); color: var(--nx-bg); }

/* ── Legacy aliases — map old banner classes to .nx-alert variants ── */
.offline-banner,
.api-banner,
.alert-banner {
  /* shape behaves like .nx-alert; tone via the per-class variant below */
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-family: var(--nx-font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.2px;
  background: var(--nx-red-soft);
  border-bottom: 1px solid var(--nx-red-line);
  color: var(--nx-red);
  text-align: left;       /* override the legacy text-align:center */
  
  
}
.alert-contractor {
  background: var(--nx-surface-2);
  border-bottom: 1px solid var(--nx-border-strong);
  color: var(--nx-text);
}
.alert-overdue {
  background: var(--nx-red-soft);
  border-bottom: 1px solid var(--nx-red-line);
  color: var(--nx-red);
}
.clean-edit-banner {
  background: var(--nx-gold-soft);
  border-bottom: 1px solid var(--nx-gold-line-2);
  color: var(--nx-gold);
}
/* The legacy ✕ dismiss button used inside the alerts — re-style to
   match .nx-alert-dismiss so legacy markup looks consistent. */
.alert-dismiss {
  flex-shrink: 0;
  width: 26px; height: 26px;
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  transition: opacity var(--nx-press), background var(--nx-press);
}
.alert-dismiss:hover { opacity: 1; background: rgba(0,0,0,0.10); }


/* ─── 2.Z  TOAST v2 — .nx-toast ─────────────────────────────────────
   Floating notification at the bottom of the viewport. Replaces the
   legacy .toast classes in nexus.css. The existing NX.toast() API
   creates DOM with classes like .toast and .toast-success — we keep
   those classnames working by redefining them here using tokens, AND
   we add a new .nx-toast class that future call sites can use.

   Markup pattern (existing):
     <div class="toast toast-success">Saved</div>
   Markup pattern (new):
     <div class="nx-toast nx-toast--success">Saved</div>

   Both render identically. */
.nx-toast,
.toast {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: var(--nx-radius-pill);
  font-family: var(--nx-font-body);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.1px;
  pointer-events: auto;
  max-width: 320px;
  text-align: center;
  background: var(--nx-surface-3);
  border: 1px solid var(--nx-border-strong);
  color: var(--nx-text);
  
  
  box-shadow: var(--nx-shadow-card);
  animation: nxToastIn 0.3s var(--nx-ease);
}
@keyframes nxToastIn {
  from { transform: translateY(40px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.nx-toast--success, .toast-success {
  background: var(--nx-green-soft);
  border-color: var(--nx-green-line);
  color: var(--nx-green);
}
.nx-toast--info, .toast-info {
  background: var(--nx-gold-soft);
  border-color: var(--nx-gold-line-2);
  color: var(--nx-gold);
}
.nx-toast--error, .toast-error {
  background: var(--nx-red-soft);
  border-color: var(--nx-red-line);
  color: var(--nx-red);
}
.nx-toast--warn {
  background: var(--nx-gold-soft);
  border-color: var(--nx-gold-line-2);
  color: var(--nx-amber);
}


/* ─── 2.W  SHEET — .nx-sheet ───────────────────────────────────────
   Bottom-anchored modal sheet. Replaces the .eq-detail-bg pattern
   used in equipment.js. On mobile it slides up from below; on
   tablet/desktop it centers as a card. Token-driven styling so it
   themes correctly.

   Markup pattern:
     <div class="nx-sheet-backdrop" data-dismiss>
       <div class="nx-sheet" role="dialog" aria-modal="true">
         <div class="nx-sheet-handle"></div>
         <div class="nx-sheet-header">
           <h2 class="nx-sheet-title">Title</h2>
           <button class="nx-sheet-close" aria-label="Close">✕</button>
         </div>
         <div class="nx-sheet-body">
           ...content...
         </div>
       </div>
     </div>

   Variants:
     .nx-sheet--full       full-height
     .nx-sheet--medium     ~70vh height with handle (default)
     .nx-sheet--compact    fits content, centered on tablet+

   Click the backdrop or any element with [data-dismiss] to close.
   Wire dismissal in JS — this primitive handles only the visuals. */
.nx-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.58);
  
  
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: nxSheetBackdropIn var(--nx-flow);
}
@keyframes nxSheetBackdropIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.nx-sheet {
  position: relative;
  width: 100%;
  max-width: 720px;
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
  background: var(--nx-surface-solid);
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  border: 1px solid var(--nx-border);
  border-bottom: none;
  box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.45);
  animation: nxSheetIn var(--nx-flow);
  overflow: hidden;
}
@keyframes nxSheetIn {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.nx-sheet-handle {
  width: 40px; height: 4px;
  background: var(--nx-border-strong);
  border-radius: 2px;
  margin: 8px auto 0;
  flex-shrink: 0;
}

.nx-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  border-bottom: 1px solid var(--nx-border);
  flex-shrink: 0;
}
.nx-sheet-title {
  font-family: var(--nx-font-display);
  font-weight: 500;
  font-size: var(--nx-text-md);
  color: var(--nx-text-strong);
  margin: 0;
}
.nx-sheet-close {
  width: 32px; height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--nx-surface-1);
  color: var(--nx-faint);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--nx-press), color var(--nx-press);
}
.nx-sheet-close:hover {
  background: var(--nx-surface-2);
  color: var(--nx-text);
}

.nx-sheet-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 18px;
  padding-bottom: max(16px, calc(env(safe-area-inset-bottom) + 16px));
  scroll-padding-bottom: 80px;
}

/* Variants */
.nx-sheet--full {
  max-height: calc(100dvh - 20px);
  height: calc(100dvh - 20px);
}
.nx-sheet--medium {
  max-height: 70dvh;
}
.nx-sheet--compact {
  max-height: max-content;
}

/* Tablet/desktop — center the sheet, drop the handle, soften the slide */
@media (min-width: 720px) {
  .nx-sheet-backdrop { align-items: center; }
  .nx-sheet {
    border-radius: 24px;
    border-bottom: 1px solid var(--nx-border);
    max-width: 600px;
    width: calc(100% - 80px);
    box-shadow: var(--nx-shadow-card);
    animation: nxSheetFadeIn var(--nx-flow);
  }
  .nx-sheet-handle { display: none; }
  @keyframes nxSheetFadeIn {
    from { opacity: 0; transform: translateY(20px) scale(0.985); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
  }
}


/* ─── 2.W  SPEED-DIAL (FAB-style action menu) ──────────────────────
   Pattern: tap the Duties bottom-nav button, the screen dims and a
   vertically-stacked list of labeled actions slides up from above
   the nav. Each row is "label chip + circular icon button". Tap any
   action to navigate, tap the backdrop or the rotated bnav button
   to dismiss.

   Used by: app.js to surface Duties → Cleaning / Ordering as direct
   destinations rather than always landing on the last-used pane.

   Markup:
     <div class="nx-speed-dial" id="dutiesDial" aria-hidden="true">
       <div class="nx-speed-dial-backdrop" data-dial-close></div>
       <div class="nx-speed-dial-stack">
         <button class="nx-speed-dial-action" data-target="cleaning">
           <span class="nx-speed-dial-label">Cleaning</span>
           <span class="nx-speed-dial-icon"><i data-lucide="..."></i></span>
         </button>
         ...
       </div>
     </div>
*/

.nx-speed-dial {
  position: fixed;
  inset: 0;
  z-index: 950;          /* above content, below modals (1000+) */
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--nx-flow) ease;
}
.nx-speed-dial.is-open {
  opacity: 1;
  pointer-events: auto;
}

.nx-speed-dial-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.50);
  
  
}

.nx-speed-dial-stack {
  position: absolute;
  /* Anchor flush against the top edge of the bottom nav. The nav is
     ~72px tall (60 min-h + 12 padding) plus safe-area-inset-bottom.
     bottom: 60px + safe-area lands the bottom-most dial action right
     at the nav's upper edge, so the dial reads as an extension of
     the button that spawned it rather than floating mid-screen. */
  bottom: calc(60px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-end;
  padding: 0 16px;
  max-width: calc(100% - 32px);
}

.nx-speed-dial-action {
  display: flex;
  align-items: center;
  gap: 12px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  font: inherit;
  color: inherit;

  /* Each row slides up when the dial opens. Using transition (not
     animation) so it triggers reliably from the .is-open class change
     instead of depending on display:none → display:block, which
     suppresses CSS animations across browsers. */
  transform: translateY(20px);
  transition: transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.nx-speed-dial.is-open .nx-speed-dial-action {
  transform: translateY(0);
}
.nx-speed-dial.is-open .nx-speed-dial-action:nth-child(1) { transition-delay: 30ms; }
.nx-speed-dial.is-open .nx-speed-dial-action:nth-child(2) { transition-delay: 80ms; }
.nx-speed-dial.is-open .nx-speed-dial-action:nth-child(3) { transition-delay: 130ms; }
.nx-speed-dial.is-open .nx-speed-dial-action:nth-child(4) { transition-delay: 180ms; }

.nx-speed-dial-label {
  background: var(--nx-surface-solid);
  color: var(--nx-text-strong);
  border: 1px solid var(--nx-border);
  border-radius: 14px;
  padding: 9px 14px;
  font-family: var(--nx-font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.2px;
  white-space: nowrap;
  box-shadow: var(--nx-shadow-card);
}

.nx-speed-dial-icon {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--nx-gold);
  color: var(--nx-gold-on);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 4px 16px var(--nx-gold-aura),
              0 2px 6px rgba(0, 0, 0, 0.25);
  transition: transform var(--nx-press), box-shadow var(--nx-press);
}
.nx-speed-dial-icon i,
.nx-speed-dial-icon svg {
  width: 22px;
  height: 22px;
  stroke-width: 2;
}

.nx-speed-dial-action:active .nx-speed-dial-icon {
  transform: scale(0.94);
  box-shadow: 0 2px 8px var(--nx-gold-haze);
}
.nx-speed-dial-action:active .nx-speed-dial-label {
  background: var(--nx-surface-2);
}

/* When the dial is open, the duties bnav-btn crossfades from its
   normal icon to a real X icon. The button has both SVGs in its
   markup (.bnav-icon + .bnav-icon-alt) and we toggle opacity via
   the .is-dial-open state class. */
.bnav-btn-has-alt {
  position: relative;
}
.bnav-btn-has-alt .bnav-icon-alt {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, calc(-50% - 9px));   /* match .bnav-icon's vertical position */
  width: 24px;
  height: 24px;
  opacity: 0;
  transition: opacity var(--nx-flow), transform var(--nx-flow);
  pointer-events: none;
}
.bnav-btn-has-alt .bnav-icon {
  transition: opacity var(--nx-flow);
}
.bnav-btn.is-dial-open {
  color: var(--nx-gold);
}
.bnav-btn.is-dial-open .bnav-icon {
  opacity: 0;
}
.bnav-btn.is-dial-open .bnav-icon-alt {
  opacity: 1;
  transform: translate(-50%, calc(-50% - 9px)) rotate(90deg);   /* tiny entrance flourish */
}

/* Reduced-motion users skip the slide-in transitions */
@media (prefers-reduced-motion: reduce) {
  .nx-speed-dial,
  .nx-speed-dial-action {
    transition: none;
  }
  .nx-speed-dial-action {
    transform: none;
  }
  .bnav-btn-has-alt .bnav-icon,
  .bnav-btn-has-alt .bnav-icon-alt {
    transition: none;
  }
  .bnav-btn.is-dial-open .bnav-icon-alt {
    transform: translate(-50%, calc(-50% - 9px));
  }
}


/* ─── 2.V  EQ-MODAL VISUAL ALIASING ──────────────────────────────────
   The equipment view has 13 separate modal instances (detail, edit,
   service, parts, prepop, AI creator, describe, location prompt,
   confirm, zebra print, full edit, plus two tiny dialogs). All use
   the legacy markup pattern:

     <div id="eqModal" class="eq-modal active">      ← fixed full-screen, centers panel
       <div class="eq-detail-bg" onclick="..."></div> ← dark backdrop overlay
       <div class="eq-detail">                       ← the panel itself
         <div class="eq-detail-head">...</div>
         <div class="eq-detail-tabs">...</div>       (some modals)
         <div class="eq-detail-body">...</div>
         <div class="eq-detail-actions">...</div>    (some modals)
       </div>
     </div>

   Rather than migrating 13 markup sites to .nx-sheet (which would
   change layout from centered-modal to bottom-sheet), this block
   modernizes the existing pattern in place — token-driven surfaces,
   modern radii, proper backdrop blur, theme-aware shadows. All 13
   modals get the visual upgrade with zero markup changes.

   Layout positioning (position:fixed, centered alignment, z-index)
   stays in equipment.css. We only override visual properties here. */

.eq-detail-bg {
  /* The dark backdrop sibling — stays absolute-positioned by equipment.css.
     Just modernize the backdrop tint and add a stronger blur. */
  background: rgba(0, 0, 0, 0.58) !important;
  
  
}

.eq-detail {
  /* The panel itself — token-driven surface, modern radii, soft shadow.
     equipment.css sets max-width / max-height / display:flex / column —
     those layout properties stay; we override appearance only. */
  background: var(--nx-surface-solid) !important;
  border: 1px solid var(--nx-border) !important;
  border-radius: 22px !important;
  box-shadow: var(--nx-shadow-card) !important;
  overflow: hidden !important;
}

.eq-detail-head {
  /* Header band — quieter divider, more breathing room */
  background: transparent !important;
  border-bottom: 1px solid var(--nx-border) !important;
  padding: 14px 18px !important;
  gap: 12px !important;
}
.eq-detail-head h2 {
  font-family: var(--nx-font-display) !important;
  font-size: var(--nx-text-md) !important;
  font-weight: 500 !important;
  color: var(--nx-text-strong) !important;
  letter-spacing: -0.01em !important;
  margin: 0 !important;
}
.eq-detail-sub {
  font-size: 12px !important;
  /* v603 — was --nx-faint, a DECORATION token used as ink: 3.62:1. This is
     the line under the equipment's name that says which restaurant and which
     station it is in. Corrected here rather than out-specified from
     equipment-system.css, because an `!important` at source can only be beaten
     by another `!important`, and stacking those is how a stylesheet becomes
     unreadable. These `.eq-*` selectors live in nx-system.css but belong to
     the equipment module alone. */
  color: var(--nx-text-secondary) !important;
  margin-top: 2px !important;
}

.eq-detail-body {
  /* Scrolling content area */
  padding: 16px 18px !important;
  padding-bottom: max(16px, calc(env(safe-area-inset-bottom) + 16px)) !important;
  -webkit-overflow-scrolling: touch !important;
}

/* The tab strip in the detail modal */
.eq-detail-tabs {
  background: var(--nx-surface-1) !important;
  border-bottom: 1px solid var(--nx-border) !important;
  padding: 0 12px !important;
  gap: 2px !important;
}
.eq-detail-tabs .eq-tab {
  background: transparent !important;
  /* v603 — 3.58:1 as --nx-faint. These are the tab labels on the detail
     modal; "Timeline (1)" and "Parts (4)" carry counts a person reads. */
  color: var(--nx-text-secondary) !important;
  border: none !important;
  border-bottom: 2px solid transparent !important;
  padding: 10px 12px !important;
  font-size: 12px !important;
  font-weight: 500 !important;
  letter-spacing: 0.2px !important;
  border-radius: 0 !important;
  transition: color var(--nx-press), border-color var(--nx-press) !important;
}
.eq-detail-tabs .eq-tab.active {
  /* v603 — raw gold measured 3.58:1 on the modal ground. The UNDERLINE keeps
     the raw accent (a border is a surface, not a word); only the label moves
     to the measured ink. v589's rule, applied to the last place it was
     missing. */
  color: var(--nx-gold-ink) !important;
  border-bottom-color: var(--nx-gold) !important;
  background: transparent !important;
}
.eq-detail-tabs .eq-tab:active {
  background: var(--nx-highlight-faint) !important;
}

/* The action bar at the bottom of some modals */
.eq-detail-actions {
  background: var(--nx-surface-1) !important;
  border-top: 1px solid var(--nx-border) !important;
  padding: 12px 16px !important;
  padding-bottom: max(12px, calc(env(safe-area-inset-bottom) + 12px)) !important;
  gap: 8px !important;
}

/* Close button (the ✕ in the top corner) — token-styled hover */
.eq-modal .eq-close,
.eq-detail .eq-close {
  width: 36px !important;
  height: 36px !important;
  border-radius: 50% !important;
  background: var(--nx-surface-1) !important;
  border: 1px solid var(--nx-border) !important;
  color: var(--nx-faint) !important;
  transition: background var(--nx-press), color var(--nx-press), border-color var(--nx-press) !important;
}
.eq-modal .eq-close:hover,
.eq-detail .eq-close:hover {
  background: var(--nx-surface-2) !important;
  color: var(--nx-text) !important;
  border-color: var(--nx-border-strong) !important;
}

/* On mobile, full-bleed the modal for native iOS feel */
@media (max-width: 600px) {
  .eq-detail {
    border-radius: 0 !important;
    border-left: none !important;
    border-right: none !important;
  }
}


/* ═══ 3. UTILITIES ════════════════════════════════════════════════ */

.nx-display { font-family: var(--nx-font-display); font-weight: 300; font-size: clamp(36px, 9vw, 56px); line-height: 1.04; letter-spacing: -0.025em; }
.nx-h1      { font-family: var(--nx-font-display); font-weight: 400; font-size: var(--nx-text-2xl); line-height: 1.25; letter-spacing: -0.01em; }
.nx-h2      { font-family: var(--nx-font-display); font-weight: 500; font-size: var(--nx-text-xl); line-height: 1.3; letter-spacing: -0.01em; }
.nx-body    { font-family: var(--nx-font-body); font-size: var(--nx-text-base); line-height: 1.55; color: var(--nx-text); }
.nx-meta    { font-family: var(--nx-font-mono); font-size: var(--nx-text-xs); font-weight: 500; letter-spacing: 1.4px; text-transform: uppercase; color: var(--nx-faint); }

.nx-stack-2 > * + * { margin-top: var(--nx-space-2); }
.nx-stack-3 > * + * { margin-top: var(--nx-space-3); }
.nx-stack-4 > * + * { margin-top: var(--nx-space-4); }
.nx-stack-5 > * + * { margin-top: var(--nx-space-5); }
.nx-stack-6 > * + * { margin-top: var(--nx-space-6); }


/* ═══ 4. ACCESSIBILITY ════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  .nx-pill, .nx-listrow, .nx-stat, .nx-prompt, .nx-ask {
    transition: background var(--nx-press), border-color var(--nx-press), color var(--nx-press);
  }
  .nx-pill:active, .nx-stat:active, .nx-prompt:active, .nx-ask:active,
  .nx-prompt:hover {
    transform: none;
  }
}

.nx-pill:focus-visible,
.nx-prompt:focus-visible,
.nx-stat:focus-visible,
.nx-listrow:focus-visible,
.nx-ask:focus-visible,
.nx-input:focus-visible,
.nx-select:focus-visible {
  outline: 2px solid var(--nx-gold);
  outline-offset: 2px;
}


/* ═══ 5. THEME AWARENESS ════════════════════════════════════════════
   The canonical [data-theme="light"] block lives near the top of this
   file, immediately after the :root token block. It overrides every
   --nx-* token with its light-mode value (deeper olive-gold, warm
   charcoal text, cream surfaces, softer shadows) so any component
   using --nx-* tokens auto-corrects without per-selector edits.

   The dependency direction is:
     nx-system.css :root            ← dark hex source
     nx-system.css [data-theme=light] ← light hex source
     nexus.css     :root            ← legacy aliases (--accent: var(--nx-gold), etc.)
     component CSS                  ← uses either --nx-* or legacy tokens

   This block used to alias --nx-* TO --accent / --text / --bg etc. (the
   OPPOSITE direction). Removed because that produced circular references
   once the alias layer was added to nexus.css. If you need a light-only
   override for a property nx-system.css doesn't already cover, add it
   to the canonical light block above, NOT here. */


/* ═══ 6. UNIFIED MASTHEAD ═════════════════════════════════════════
   The top .nav becomes THE single masthead for the entire app —
   replaces both the legacy nav-nexus pill AND any duplicate
   "NEXUS / date" rows in individual views (like home's old
   .home-mast block, which is now removed).
   
   Layout:
   ┌─────────────────────────────────────────────────────────┐
   │ [coin]  ● NEXUS    SAT MAY 2 · 1:23 PM           ☰      │
   │ ─────────────────────────────────────────────────────── │
   └─────────────────────────────────────────────────────────┘
   
   The coin (28px) is the visual anchor on the left.
   NEXUS wordmark + live date sit center-left in mono caps.
   Hamburger menu on the right.
   Single hairline divider beneath. */
.nav {
  background: var(--nx-bg) !important;
  border-bottom: 1px solid var(--nx-gold-line) !important;
  /* Top padding respects the iOS status-bar safe area so the coin and
     NEXUS wordmark are never clipped by the translucent status bar
     (apple-mobile-web-app-status-bar-style="black-translucent" makes the
     bar overlay the page). On devices/browsers without notches,
     env(safe-area-inset-top) resolves to 0 and the 10px floor applies.
     Sides + bottom unchanged. */
  padding: max(10px, env(safe-area-inset-top, 10px)) 16px 10px !important;
  gap: 14px !important;
  /* Belt-and-suspenders sticky — already declared as position: sticky in
     nexus.css, but re-asserted here so the rule isn't accidentally lost
     by a later `.nav` override. -webkit-sticky covers older iOS WebKit. */
  position: -webkit-sticky !important;
  position: sticky !important;
  top: 0 !important;
  z-index: 50 !important;
  /* Align to the COIN's center (not midpoints) so the persona label
     hanging below the coin doesn't pull the row visually off-center.
     The coin is 34px tall + 11px label = 45px total wrap height. We
     align everything to the coin's midpoint (17px from top of wrap). */
  align-items: flex-start !important;
}

/* Items that should sit on the coin's midline (not the wrap's midline):
   pull them down by half the label-slot height so they line up with
   the coin's vertical center, not with the entire coin+label stack. */
.nav .nav-nexus,
.nav .nx-mast-date,
.nav .nav-more,
.nav .nav-tabs {
  margin-top: 6px;   /* pulls these onto the coin's midline */
}

/* The masthead coin — visual anchor for the persona system.
   Uses same image assets as login + home. Idle wobble, tap to flip.

   FALLBACK: a radial-gradient gold disc background renders *underneath*
   the <img> tags, so if the PNG assets fail to load (e.g. cache miss,
   bad path) we still see a recognizable gold coin shape — not nothing. */

/* ═══ COIN WRAPPER — vertical stack: coin on top, persona name below ═══
   The coin remains the visual anchor, but it now has a small persona
   name label sitting directly under it. The wrap is sized to the coin
   width so the masthead row stays balanced, with the label rendering
   in a fixed-height slot below the coin (won't push other elements
   when text changes between PROVIDENTIA and TRAJAN). */
.nx-mast-coin-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  flex-shrink: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Reserve label height so the row doesn't reflow when text swaps */
  padding-bottom: 0;
  position: relative;
}

/* The persona name itself — quiet but legible. Updated by JS on
   nx-persona-change. Tone differs slightly per persona so each
   advisor has its own visual signature.

   Sits in a fixed-height slot directly below the coin so the row
   stays balanced when toggling between PROVIDENTIA and TRAJAN. */
.nx-mast-persona {
  font-family: var(--nx-font-mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--nx-gold);
  opacity: 0.85;
  transition: color 0.3s, opacity 0.3s;
  line-height: 1;
  height: 11px;          /* fixed slot — text can't push siblings */
  margin-top: 2px;
  white-space: nowrap;
  pointer-events: none;
  text-align: center;
}
.nx-mast-persona[data-persona="trajan"]      { color: #e6b157; }
.nx-mast-persona[data-persona="providentia"] { color: #d6b380; }

.nx-mast-coin {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  cursor: pointer;
  position: relative;
  display: inline-block;
  perspective: 200px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 35% 35%,
      #e9bf6c 0%,
      var(--accent) 35%,
      #a07a28 70%,
      #6a5018 100%);
  -webkit-tap-highlight-color: transparent;
  /* Brief greeting wobble on mount, then still. No constant motion in
     peripheral vision — that gets annoying after a few minutes. */
  animation: nxMastCoinGreet 1.4s ease-in-out 1;
}
@keyframes nxMastCoinGreet {
  0%, 100% { transform: rotate(0deg); }
  30%      { transform: rotate(-2deg); }
  60%      { transform: rotate(1.5deg); }
}
.nx-mast-coin-flip {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  -webkit-transform-style: preserve-3d;
  transition: transform 0.6s cubic-bezier(0.4, 0.05, 0.3, 1);
}
.nx-mast-coin-flip.flipped { transform: rotateY(180deg); }
.nx-mast-coin-face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  display: block;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}
.nx-mast-coin-back { transform: rotateY(180deg); }

/* Gold rim glow when AI active (pulsing). Same pattern as
   .home-coin-mini.pulsing. */
.nx-mast-coin::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: 0 0 0 0 rgba(212, 164, 78, 0);
  transition: box-shadow 0.3s ease;
  z-index: -1;
}
.nx-mast-coin.pulsing::before {
  animation: nxMastCoinPulse 1.4s cubic-bezier(0.4, 0, 0.6, 1) 1;
}
@keyframes nxMastCoinPulse {
  0%   { box-shadow: 0 0 0 0 rgba(212, 164, 78, 0); }
  35%  { box-shadow: 0 0 8px 2px rgba(212, 164, 78, 0.6), 0 0 16px 4px rgba(212, 164, 78, 0.25); }
  70%  { box-shadow: 0 0 12px 3px rgba(212, 164, 78, 0.45), 0 0 24px 8px rgba(212, 164, 78, 0.18); }
  100% { box-shadow: 0 0 0 0 rgba(212, 164, 78, 0); }
}
.nx-mast-coin.listening::before {
  animation: nxMastCoinListening 3s ease-in-out infinite;
}
@keyframes nxMastCoinListening {
  0%, 100% { box-shadow: 0 0 4px 1px rgba(212, 164, 78, 0.15), 0 0 8px 2px rgba(212, 164, 78, 0.06); }
  50%      { box-shadow: 0 0 6px 1px rgba(212, 164, 78, 0.25), 0 0 12px 3px rgba(212, 164, 78, 0.10); }
}

/* nav-nexus — keep the dot + NEXUS wordmark, but unify the styling
   with the masthead. Drop the gold-bordered pill chrome — the coin
   now carries the brand presence. */
.nav-nexus {
  border: none !important;
  background: transparent !important;
  padding: 0 !important;
  color: var(--nx-gold) !important;
  font-family: var(--nx-font-mono) !important;
  font-size: 12px !important;
  letter-spacing: 3px !important;
  font-weight: 500 !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 7px !important;
  min-height: 0 !important;
  flex-shrink: 0 !important;
}
.nav-nexus.active {
  color: var(--nx-gold) !important;
  background: transparent !important;
  box-shadow: none !important;
}
.nav-nexus .nav-dot {
  width: 6px !important;
  height: 6px !important;
  background: var(--nx-gold) !important;
  box-shadow: 0 0 8px rgba(212, 164, 78, 0.4) !important;
}

/* Live date/time — mono caps, faint, sits centered in the available
   space between the coin and the menu so the masthead reads as a
   balanced editorial header: brand-left, date-center, menu-right.
   Filled by app.js with "SAT · MAY 2 · 1:23 PM" format.

   v18.25 — was text-align: left with padding-left:6px which jammed
   the date text up against the coin and stranded the menu on the
   right. Centering the text inside its flex:1 container puts the
   date in the visual middle of the row. */
.nx-mast-date {
  flex: 1;
  min-width: 0;
  font-family: var(--nx-font-mono);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--nx-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  padding: 0;
}

.nav-more {
  color: var(--nx-faint) !important;
  flex-shrink: 0;
}
.nav-more .nav-icon { width: 22px; height: 22px; }

/* On ultra-narrow screens, drop only the weather CONDITION WORD —
   the date + temp always survive (v393; the old rule hid the whole
   dateline, which read as a broken feature). */
@media (max-width: 360px) {
  .nx-mast-date .nx-wx-word { display: none; }
}
/* v394 collision ratchet — JS adds this class if the dateline would
   ever reach the bell (long date + 3-digit temp + long word). */
.nx-mast-date.nx-date-tight .nx-wx-word { display: none; }


/* ═══ 7. BOTTOM NAV REDESIGN ═════════════════════════════════════
   Match the editorial-pill design language used everywhere else.
   The active state changes from "thin gold underline at top" to
   "soft gold pill behind the icon" — a more contemporary tap-target
   feedback that aligns with .nx-pill semantics.
   
   Touch targets bumped to 60px tall, icons to 24px, label
   typography tightened. */
.bottom-nav {
  background: var(--nx-bottom-nav-bg) !important;
  
  
  /* v404 — Alfredo: "squeeze it down to 65px matching the top masthead."
     The bar itself is a fixed 65px (mirrors the masthead's 10+44+10+1),
     with the home-indicator safe-area added BELOW it — the exact inverse
     of the masthead adding the notch safe-area ABOVE its 65px. */
  height: calc(65px + env(safe-area-inset-bottom)) !important;
  min-height: 0 !important;
  padding: 0 0 env(safe-area-inset-bottom) !important;
}
/* v398 — replace the flat border-top with the masthead's fading gold
   hairline. The flat border is re-added !important by several files
   (home-soft.css, theme rules) at class specificity, so use the id to win
   outright (#bottomNav 1,0,0 beats any .bottom-nav rule). */
#bottomNav { border-top: 0 !important; }
/* Fading gold hairline along the TOP edge of the bottom nav — bright at
   center, dying at the margins. Matches the masthead divider. */
.bottom-nav::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(212, 164, 78, 0)    0%,
    rgba(212, 164, 78, .50) 15%,
    rgba(230, 177, 87, .85) 50%,
    rgba(212, 164, 78, .50) 85%,
    rgba(212, 164, 78, 0)  100%);
  pointer-events: none;
}
/* Desktop left rail (≥900px): the same hairline, vertical, along its RIGHT
   edge — replaces the flat border-right so the rail divider matches the
   masthead + bottom-nav. */
@media (min-width: 900px) {
  #navTabs { border-right: 0 !important; }   /* id beats index.html's .nav-tabs border-right */
  .nav-tabs::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0; right: 0;
    width: 1px;
    background: linear-gradient(180deg,
      rgba(212, 164, 78, 0)    0%,
      rgba(212, 164, 78, .50) 15%,
      rgba(230, 177, 87, .85) 50%,
      rgba(212, 164, 78, .50) 85%,
      rgba(212, 164, 78, 0)  100%);
    pointer-events: none;
  }
}
/* v400 — LIGHT theme: the divider hairlines are tuned to GLOW on OLED navy
   (bright #d4a44e/#e6b157); on cream that reads faint. Swap to the light
   theme's deeper olive-gold at higher alpha so the rule stays "crafted". */
:root[data-theme="light"] .nav::after,
:root[data-theme="light"] .bottom-nav::before {
  background: linear-gradient(90deg,
    rgba(168,116,42,0) 0%, rgba(168,116,42,.55) 15%,
    rgba(143,95,31,.90) 50%, rgba(168,116,42,.55) 85%,
    rgba(168,116,42,0) 100%) !important;
}
@media (min-width: 900px) {
  :root[data-theme="light"] .nav-tabs::after {
    background: linear-gradient(180deg,
      rgba(168,116,42,0) 0%, rgba(168,116,42,.55) 15%,
      rgba(143,95,31,.90) 50%, rgba(168,116,42,.55) 85%,
      rgba(168,116,42,0) 100%) !important;
  }
}
.bnav-btn {
  flex: 1 !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 4px !important;
  padding: 6px 4px !important;
  min-height: 56px !important;
  background: none !important;
  border: none !important;
  color: var(--nx-faint) !important;
  cursor: pointer !important;
  position: relative !important;
  font-family: var(--nx-font-body) !important;
  -webkit-tap-highlight-color: transparent !important;
  transition: color var(--nx-press), transform var(--nx-press) !important;
}
/* Icon container — soft gold pill behind icon when active.
   We wrap the icon in a 36×36 invisible pill that fills with gold
   tint and goes solid on the active button. */
.bnav-btn .bnav-icon {
  width: 24px !important;
  height: 24px !important;
  stroke-width: 1.7 !important;
  flex-shrink: 0;
  transition: transform var(--nx-press);
  position: relative;
  z-index: 1;
}
.bnav-btn::before {
  /* The gold-pill behind-icon — sized to fit the icon area only,
     positioned to sit centered above the label. */
  content: '';
  position: absolute;
  top: 4px;
  left: 50%;
  transform: translateX(-50%);
  width: 56px;
  height: 32px;
  border-radius: var(--nx-radius-pill);
  background: transparent;
  transition:
    background var(--nx-press),
    border-color var(--nx-press);
  border: 1px solid transparent;
  z-index: 0;
}
.bnav-btn span {
  font-size: 11px !important;
  letter-spacing: 0.3px !important;
  font-weight: 500 !important;
  color: inherit;
  position: relative;
  z-index: 1;
  margin-top: 2px;
}

/* Press feedback */
.bnav-btn:active { transform: scale(0.97) !important; }
.bnav-btn:active::before { background: var(--nx-gold-faint); }

/* Active state — gold pill behind icon, gold text, gold icon stroke.
   Override the legacy `.bnav-btn.active::after` underline rule. */
.bnav-btn.active {
  color: var(--nx-gold) !important;
}
.bnav-btn.active::before {
  background: var(--nx-gold-soft);
  border-color: var(--nx-gold-line-2);
}
.bnav-btn.active::after {
  /* Override / hide the legacy gold underline at the top. */
  display: none !important;
}
.bnav-btn.active .bnav-icon {
  /* Slight icon scale-up makes the active state feel tactile */
  transform: scale(1.06);
}

/* Reduce home button's special outlined style — the legacy
   .bnav-btn-home .bnav-icon padding/border treatment looked grafted
   on. Now Home behaves like every other button. */
.bnav-btn-home .bnav-icon {
  padding: 0 !important;
  border: none !important;
}
.bnav-btn-home.active .bnav-icon {
  border-color: transparent !important;
}


/* ═══════════════════════════════════════════════════════════════════════
   PIN screen + clock-in panel — small-iPhone fitting (2026-05-06)

   Problem: the PIN screen stack is rigid — coin (240px) + NEXUS title +
   keypad (~270px) + language row totals ~800px tall. iPhone SE / 8 /
   13 mini have a 667px screen, and Safari's URL bar leaves ~570px of
   visible space — content overflows and the language buttons + bottom
   keypad row get cut off. Worse after PIN auth: the tc-card (280px)
   replaces the keypad and pushes "Enter NEXUS" off-screen too.

   Fix: media-query overrides that shrink the coin + tighten vertical
   rhythm on short viewports. Plus overflow-y: auto as a safety net so
   nothing is unreachable on truly tiny screens (landscape, split-view).
   Inline #pinScreen overrides in index.html don't use !important, so
   these win cleanly via cascade specificity + !important.
   ═══════════════════════════════════════════════════════════════════════ */

@media (max-height: 720px) {
  /* Safety: scrollable if anything still overflows on edge cases */
  .pin-screen {
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
    align-items: flex-start !important;
    padding-top: max(8px, env(safe-area-inset-top, 8px)) !important;
    padding-bottom: max(12px, env(safe-area-inset-bottom, 12px)) !important;
  }

  /* Shrink the hero coin (was 240×240 stage / 200×200 coin) */
  #pinScreen .nx-coin-stage {
    width: 114px !important;
    height: 114px !important;
    margin: 0 auto 4px !important;
  }
  #pinScreen .nx-coin {
    top: 8px !important;
    left: 8px !important;
    width: 98px !important;
    height: 98px !important;
  }
  #pinScreen .nx-coin-info {
    width: 22px !important;
    height: 22px !important;
    font-size: 12px !important;
  }

  /* Tighten the masthead stack (NEXUS title + date) */
  #pinScreen .pin-dot { margin-bottom: 8px !important; }
  #pinScreen .pin-title {
    font-size: 22px !important;
    margin-bottom: 6px !important;
    letter-spacing: 4px !important;
  }
  #pinScreen .pin-hairline { margin: 4px auto !important; }
  #pinScreen .pin-date {
    margin-bottom: 14px !important;
    font-size: 11px !important;
  }

  /* Tighten the keypad area */
  #pinScreen .pin-display {
    margin-bottom: 18px !important;
    gap: 14px !important;
  }
  #pinScreen .pin-pad { margin-bottom: 4px !important; gap: 10px !important; max-width: 288px !important; }
  #pinScreen .pin-pad .pin-key {
    height: 54px !important;
    width: 100% !important;
    font-size: 20px !important;
  }
  #pinScreen .pin-sub { margin: 6px 0 0 !important; font-size: 12px !important; }

  /* Compact tc-panel for clock-in mode */
  #pinScreen .tc-panel { margin-top: 8px !important; gap: 10px !important; }
  #pinScreen .tc-card {
    height: auto !important;
    max-height: 40vh !important;
    padding: 8px 12px 6px !important;
  }
  #pinScreen .tc-time { font-size: 16px !important; margin-bottom: 6px !important; }

  /* Lang row sits closer */
  #pinScreen .pin-lang-row { margin-top: 8px !important; }

  /* pin-box outer padding tighter */
  #pinScreen .pin-box { padding: 6px 24px 8px !important; }
}

/* Extra-short viewports — phones in landscape, split-screen multitasking,
   genuine vintage-small iPhones. Coin gets very small but stays present. */
@media (max-height: 600px) {
  #pinScreen .nx-coin-stage {
    width: 78px !important;
    height: 78px !important;
  }
  #pinScreen .nx-coin {
    top: 5px !important;
    left: 5px !important;
    width: 68px !important;
    height: 68px !important;
  }
  #pinScreen .nx-coin-info {
    width: 18px !important;
    height: 18px !important;
    font-size: 10px !important;
  }
  #pinScreen .pin-title {
    font-size: 18px !important;
    margin-bottom: 4px !important;
    letter-spacing: 3px !important;
  }
  #pinScreen .pin-date {
    margin-bottom: 8px !important;
    font-size: 10px !important;
  }
  #pinScreen .pin-display { margin-bottom: 12px !important; gap: 10px !important; }
  #pinScreen .pin-pad .pin-key { height: 46px !important; width: 100% !important; font-size: 18px !important; }
  #pinScreen .tc-card {
    max-height: 60vh !important;
    padding: 8px 12px 6px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Record editor engine — shared overlay + card + chip styles
   Used by NX.recordEditor (js/record-editor.js). Both the ordering
   module's vendor editor and the equipment module's contractor editor
   render through this. Class prefix "rx-" = record editor.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── overlay shell ───────────────────────────────────────────────────── */
.rx-overlay {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--bg);
  /* No flex, no grid — head/body/foot are positioned absolutely
     inside this container. position:absolute is the most reliable
     way to make a scrollable middle section on iOS Safari, period.
     It doesn't depend on flex/grid math which iOS gets wrong. */
  overflow: hidden;
  animation: rx-overlay-in .22s ease;
}
.rx-overlay-head {
  position: absolute;
  top: env(safe-area-inset-top, 0);
  left: 0;
  right: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  /* Expose the rendered height as a CSS variable so the body knows
     where to start. Browsers don't compute this for us — we set a
     reasonable default and live with rounding. */
  min-height: 60px;
}
.rx-overlay-body {
  /* Absolutely positioned between head and foot. Plenty of margin so
     even if head/foot are taller than expected, content isn't covered. */
  position: absolute;
  top: calc(env(safe-area-inset-top, 0) + 60px);
  left: 0;
  right: 0;
  bottom: 64px;
  overflow-y: scroll;             /* explicit scroll — never "auto" */
  -webkit-overflow-scrolling: touch;
  /* Block layout — cards stack naturally with margin-bottom. No flex
     math means no flex bugs on iOS. */
  padding: 20px 16px calc(28px + env(safe-area-inset-bottom, 0));
}
.rx-overlay-foot {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  display: flex;
  gap: 10px;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0));
  border-top: 1px solid var(--border);
  background: var(--surface);
}
@keyframes rx-overlay-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

.rx-overlay-close {
  flex-shrink: 0;
  width: 36px; height: 36px;
  border-radius: 50%;
  background: var(--elevated);
  border: 1px solid var(--border);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s ease;
}
.rx-overlay-close:hover { background: var(--surface); }
.rx-overlay-title-block {
  flex: 1;
  min-width: 0;
  text-align: center;
}
.rx-overlay-title {
  font-family: 'Outfit', 'Libre Franklin', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rx-overlay-subtitle {
  margin-top: 1px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  /* One line only — a vendor with 7 TO/CC addresses was wrapping to
     4 rows and colliding with the count chip + first card title. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rx-overlay-count {
  flex-shrink: 0;
  min-width: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 600;
  font-size: 16px;
  color: var(--accent);
}
.rx-overlay-count span {
  font-size: 9px;
  color: var(--muted);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
}
.rx-overlay-spacer { flex-shrink: 0; width: 36px; }

.rx-overlay-cancel,
.rx-overlay-save {
  flex: 1;
  height: 44px;
  border-radius: var(--rs);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.rx-overlay-cancel {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.rx-overlay-cancel:hover { background: var(--surface); color: var(--text); }
.rx-overlay-save {
  background: var(--accent);
  color: var(--bg);
  border: 1px solid var(--accent);
  font-weight: 600;
}
.rx-overlay-save:hover { filter: brightness(1.05); }
.rx-overlay-save:disabled { opacity: 0.6; cursor: wait; }

/* ── cards ──────────────────────────────────────────────────────────── */
.rx-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  /* No flex-shrink concerns — body uses block layout now (each card
     stacks via margin-bottom rather than flex-gap). overflow: clip is
     the modern alternative to overflow: hidden — it clips visually
     for the rounded corners but does NOT establish a scroll containment
     context, so it never accidentally constrains child heights. */
  overflow: clip;
  margin-bottom: 20px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.rx-card:last-child {
  margin-bottom: 0;
}
[data-theme="dark"] .rx-card { box-shadow: 0 1px 3px rgba(0,0,0,0.18); }
.rx-card-danger { border-color: rgba(190, 60, 60, 0.35); }
.rx-card-danger .rx-card-head { background: rgba(190, 60, 60, 0.06); }

.rx-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* 56px min-height = comfortable iOS-grade tap target; 18px horizontal
     padding gives room for the title + count subtitle without crowding
     the chevron. */
  padding: 16px 18px;
  min-height: 56px;
  background: var(--nx-gold-faint);
  border: 0;
  color: var(--accent);
  text-align: left;
  cursor: pointer;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s ease, border-color .15s ease;
}
/* Quiet collapsed state: drop the gold tint so a stack of closed cards
   feels calm. The currently-open card pops via its tinted bar. */
.rx-card.is-collapsed .rx-card-head {
  background: transparent;
}
.rx-card-head:hover { background: var(--nx-gold-mist); }
.rx-card-title-wrap {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.rx-card-title {
  flex-shrink: 0;
  font-weight: 600;
}
.rx-card-subtitle {
  font-family: inherit;
  font-size: 10px;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rx-card-chevron {
  flex-shrink: 0;
  width: 24px; height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
}
.rx-card-chevron svg {
  transition: transform .22s cubic-bezier(.4,0,.2,1);
}
.rx-card.is-collapsed .rx-card-chevron svg {
  transform: rotate(-90deg);
}

.rx-card-body {
  /* Generous padding + 16px gap between fields — the card frames them
     so they don't need to fight for space. */
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  border-top: 1px solid var(--border);
}
.rx-card.is-collapsed > .rx-card-body { display: none; }
.rx-card-compact .rx-card-body { padding: 10px 14px; gap: 8px; }

/* ── form fields ────────────────────────────────────────────────────── */
.rx-form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.rx-form-label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.rx-form-hint {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 10.5px;
  letter-spacing: normal;
  text-transform: none;
  color: var(--faint);
  font-weight: 400;
}
.rx-form-input {
  width: 100%;
  height: 42px;
  padding: 0 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--rs);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color .15s ease, background .15s ease;
}
.rx-form-input:focus {
  border-color: var(--accent);
  background: var(--surface);
}
.rx-form-input.rx-input-error {
  border-color: var(--red, #c44);
  background: rgba(190, 60, 60, 0.08);
}

/* ── identity row (avatar + name) ───────────────────────────────────── */
.rx-identity-row {
  display: flex;
  gap: 14px;
  align-items: center;
}
.rx-avatar-btn {
  position: relative;
  flex-shrink: 0;
  width: 64px; height: 64px;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rx-avatar-preview {
  width: 64px; height: 64px;
  border-radius: 50%;
  background: hsl(var(--avatar-hue, 35), 34%, 36%);
  background-size: cover;
  background-position: center;
  border: 2px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0.05em;
}
.rx-avatar-preview.has-photo { background-color: var(--bg); }
.rx-avatar-initials { line-height: 1; }
.rx-avatar-badge {
  position: absolute;
  right: -2px; bottom: -2px;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg);
  border: 2px solid var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
}
.rx-avatar-badge svg { width: 12px; height: 12px; }
.rx-identity-name-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ── photo actions row ──────────────────────────────────────────────── */
.rx-photo-actions {
  display: flex;
  gap: 8px;
}
.rx-photo-action {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 38px;
  border-radius: var(--rs);
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: all .15s ease;
}
.rx-photo-action:hover { border-color: var(--accent); color: var(--accent); }
.rx-photo-action svg { width: 14px; height: 14px; }
.rx-photo-action-clear:hover { border-color: var(--red, #c44); color: var(--red, #c44); }

/* ── color hue picker ───────────────────────────────────────────────── */
.rx-hue-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rx-hue-swatch {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: hsl(var(--avatar-hue, 35), 34%, 40%);
  border: 2px solid transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: all .15s ease;
}
.rx-hue-swatch:hover { transform: scale(1.1); }
.rx-hue-swatch.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
.rx-hue-auto {
  background: var(--surface);
  color: var(--accent);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
  font-size: 12px;
  border-color: var(--border);
}

/* ── toggle (checkbox styled as iOS switch) ─────────────────────────── */
.rx-form-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rx-form-toggle input { display: none; }
.rx-form-toggle-track {
  flex-shrink: 0;
  width: 42px; height: 24px;
  background: var(--border);
  border-radius: 16px;
  position: relative;
  transition: background .2s ease;
}
.rx-form-toggle-thumb {
  position: absolute;
  top: 2px; left: 2px;
  width: 20px; height: 20px;
  background: #fff;
  border-radius: 50%;
  transition: transform .2s cubic-bezier(.4,0,.2,1);
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.rx-form-toggle input:checked + .rx-form-toggle-track {
  background: var(--accent);
}
.rx-form-toggle input:checked + .rx-form-toggle-track .rx-form-toggle-thumb {
  transform: translateX(18px);
}
.rx-form-toggle-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rx-form-toggle-title { font-size: 13px; color: var(--text); }
.rx-form-toggle-sub { font-size: 11px; color: var(--muted); }

/* ── chip groups ────────────────────────────────────────────────────── */
.rx-chip-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--rs);
}
.rx-chip-group-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.rx-chip-pill {
  flex-shrink: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.15em;
  padding: 3px 8px;
  border-radius: 4px;
  background: var(--accent);
  color: var(--bg);
  text-transform: uppercase;
}
.rx-chip-pill-to    { background: var(--accent); color: var(--bg); }                                                       /* primary recipient — solid gold */
.rx-chip-pill-cc    { background: transparent; color: var(--accent); border: 1px solid var(--accent); }                     /* secondary — outlined gold so TO still reads as primary */
.rx-chip-pill-bcc   { background: var(--muted);  color: var(--bg); }
.rx-chip-pill-alt   { background: transparent; color: var(--muted); border: 1px solid var(--border); }
.rx-chip-pill-phone { background: var(--nx-gold-deep, #b88830); color: var(--bg); }                                         /* deeper gold variant — distinct from email but still in palette */
.rx-chip-pill-email { background: var(--accent); color: var(--bg); }
.rx-chip-hint {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--faint);
}

.rx-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.rx-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 6px 5px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--text);
  max-width: 100%;
}
.rx-chip-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}
.rx-chip-meta {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  color: var(--muted);
  padding-left: 4px;
  border-left: 1px solid var(--border);
}
.rx-chip-remove {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: transparent;
  border: 0;
  color: var(--faint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: all .12s ease;
}
.rx-chip-remove:hover { background: var(--red, #c44); color: #fff; }
.rx-chip-remove svg { width: 12px; height: 12px; }

.rx-chip-add {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 10px 5px 8px;
  border-radius: 999px;
  background: transparent;
  border: 1px dashed var(--border);
  color: var(--accent);
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: all .12s ease;
}
.rx-chip-add:hover { border-color: var(--accent); border-style: solid; background: var(--nx-gold-faint); }
.rx-chip-add svg { width: 12px; height: 12px; }

/* ── Chip input wrap — shown when user taps "+ Add CC" etc.
   Re-designed as a clearly labeled mini-card so it's obvious to the
   user what they're looking at. The previous flex-row layout caused
   the input to stretch and squash the Add/Cancel buttons off-screen
   on narrow viewports + iOS keyboard up — this wrap now uses
   flex-wrap so the input takes its own row and buttons stay sized. */
.rx-chip-input-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 12px;
  margin-top: 6px;
  background: var(--surface);
  border: 1.5px solid var(--accent);
  border-radius: var(--rs);
  /* iOS Safari: when this is the focused element area, scroll-margin-bottom
     reserves space below it when scrolled into view, so the keyboard
     doesn't cover it. */
  scroll-margin-bottom: 120px;
}
.rx-chip-input {
  flex: 1 1 100%;       /* full width on its own row */
  min-width: 0;
  height: 40px;
  padding: 0 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--rs);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  outline: none;
}
.rx-chip-input:focus {
  border-color: var(--accent);
}
.rx-chip-input.rx-input-error {
  border-color: var(--red, #c44);
  background: rgba(190, 60, 60, 0.08);
}
.rx-chip-input-save,
.rx-chip-input-cancel {
  flex: 1 1 calc(50% - 4px);   /* two buttons side-by-side, equal width */
  flex-shrink: 0;              /* don't compress on narrow */
  min-width: 88px;
  height: 38px;
  padding: 0 12px;
  border-radius: var(--rs);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rx-chip-input-save {
  background: var(--accent);
  color: var(--bg);
  border: 1px solid var(--accent);
}
.rx-chip-input-save:active {
  transform: scale(0.98);
}
.rx-chip-input-cancel {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.rx-chip-input-cancel:active {
  transform: scale(0.98);
}

/* ── Photo upload status pill ──────────────────────────────────────────
   Shown when photo is stored as a base64 data URL — prevents the giant
   data URL string from being dumped into a visible input field, which
   was causing layout chaos on iOS Safari. The original data is held
   in a sibling hidden input so it still saves correctly. */
.rx-photo-status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  margin-top: 8px;
  background: rgba(212, 164, 78, 0.08);
  border: 1px solid var(--nx-gold-line);
  border-radius: var(--rs);
  font-size: 12px;
  color: var(--text);
  line-height: 1.35;
}
.rx-photo-status-dot {
  flex-shrink: 0;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
}
.rx-photo-status-text {
  flex: 1;
  min-width: 0;
}

/* ── Tall textarea variants for the record-editor form ───────────────
   The bare .rx-form-input is sized for single-line inputs. Textareas
   need more room + vertical resize. These modifiers stack on top of
   .rx-form-input so the bordered/focused styling stays consistent. */
.rx-form-input.rx-form-textarea {
  height: auto;
  min-height: 120px;
  padding: 10px 12px;
  resize: vertical;
}
.rx-form-input.rx-form-textarea.rx-form-textarea-sm {
  min-height: 80px;
}

.rx-form-hint.rx-form-hint--center {
  text-align: center;
}

/* ═══════════════════════════════════════════════════════════════════════
   RX-FORM additions for template editor
   ─────────────────────────────────────────────────────────────────────
   Used by the vendor email template card to show a leading note,
   collapsible default-format preview, legacy-template warning banner,
   and a "Use default" reset link beside each override field. These
   compose with the existing .rx-form-* primitives without disturbing
   them.
   ═══════════════════════════════════════════════════════════════════════ */

.rx-form-note {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--nx-muted, var(--muted));
  background: var(--nx-gold-faint, rgba(212, 164, 78, 0.05));
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.18));
  border-left-width: 3px;
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 16px;
}

/* Collapsible preview block — collapsed shows just a one-line summary,
   expanded reveals the full sample email body. */
.rx-form-preview {
  margin-bottom: 16px;
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.18));
  border-radius: 12px;
  overflow: hidden;
  background: var(--nx-bg-soft, rgba(255, 255, 255, 0.02));
}
.rx-form-preview > summary {
  padding: 10px 14px;
  cursor: pointer;
  font-size: 12.5px;
  color: var(--nx-text);
  font-weight: 500;
  list-style: none;
  -webkit-tap-highlight-color: transparent;
  display: flex;
  align-items: center;
  gap: 8px;
}
.rx-form-preview > summary::-webkit-details-marker { display: none; }
.rx-form-preview > summary::before {
  content: '▸';
  display: inline-block;
  color: var(--nx-gold);
  transition: transform 150ms ease;
}
.rx-form-preview[open] > summary::before { transform: rotate(90deg); }
.rx-form-preview[open] > summary {
  border-bottom: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.18));
}

.rx-form-preview-pre {
  margin: 0;
  padding: 14px 16px;
  font-family: ui-monospace, 'JetBrains Mono', SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--nx-text);
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-x: auto;
  max-height: 360px;
  overflow-y: auto;
  background: var(--nx-bg, #0e1320);
}

/* Banner shown when the existing template matches the legacy default.
   Warn variant uses a soft amber tint without screaming for attention. */
.rx-form-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 12px;
  margin-bottom: 14px;
  font-size: 12.5px;
  line-height: 1.5;
}
.rx-form-banner-warn {
  background: rgba(212, 164, 78, 0.1);
  border: 1px solid rgba(212, 164, 78, 0.45);
  color: var(--nx-text);
}
.rx-form-banner-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rx-form-banner-text strong { color: var(--nx-gold); font-weight: 600; }
.rx-form-banner-text em { font-style: normal; color: var(--nx-text); }

/* Row with label on the left, "Use default" link on the right —
   keeps the reset action discoverable without crowding the input. */
.rx-form-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 4px;
}
.rx-form-label-row .rx-form-label {
  margin-bottom: 0;
}
.rx-form-link {
  background: transparent;
  border: none;
  padding: 2px 4px;
  font-family: inherit;
  font-size: 11.5px;
  color: var(--nx-gold, #d4a44e);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 4px;
}
.rx-form-link:hover,
.rx-form-link:active {
  text-decoration: underline;
  background: var(--nx-gold-faint, rgba(212, 164, 78, 0.08));
}

/* ═══ NX MINI LOADER (v390) — small inline "this section is loading" ═══
   The section-scale sibling of the boot screen: a small pulsing gold
   dot + quiet mono label. Use via NX.miniLoad('Loading equipment…')
   (returns an HTML string) wherever a view or panel would otherwise
   sit blank while it fetches. Default block form centers itself with
   breathing room; --inline hugs the text line. */
.nx-mini-load {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 30px 12px;
  /* v400 — was var(--nx-text-soft, ...) but --nx-text-soft is UNDEFINED, so
     the pale-blue fallback always applied → the loading label was invisible on
     the light cream bg. --nx-muted is theme-aware (light #5b6573 / dark). */
  color: var(--nx-muted);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.nx-mini-load--inline {
  display: inline-flex;
  padding: 2px 0;
}
.nx-mini-load-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--nx-gold, #d4a44e);
  box-shadow: 0 0 8px var(--nx-gold-glow, rgba(212, 164, 78, 0.4));
  animation: nxMiniPulse 1.4s ease-in-out infinite;
}
@keyframes nxMiniPulse {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .nx-mini-load-dot { animation: none; opacity: 0.7; }
}

/* ═══ v406 — FULL-VIEW LOADER + ERROR (NX.viewLoad / NX.viewError) ════════
   A proper centered loading screen for a whole view — the boot-curtain
   language (pulsing gold dot + tracked NEXUS + gold sweep bar + label) —
   for slow modules like Equipment, plus a matching error card with a
   Try-again button for when a load fails outright. Theme-aware tokens. */
.nx-view-load, .nx-view-error {
  min-height: 62vh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 16px; padding: 48px 24px; text-align: center;
}
.nx-view-load-mark {
  display: flex; align-items: center; gap: 13px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 21px; font-weight: 500;
  letter-spacing: 0.42em; text-indent: 0.42em;
  color: var(--nx-text-strong, var(--text));
}
.nx-view-load-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--nx-gold, var(--accent));
  box-shadow: 0 0 12px var(--nx-gold-glow, rgba(212, 164, 78, 0.55));
  animation: nxViewPulse 1.6s ease-in-out infinite;
}
.nx-view-load-bar {
  width: 148px; height: 2px; border-radius: 2px;
  overflow: hidden; position: relative;
  background: var(--nx-gold-line, rgba(212, 164, 78, 0.2));
}
.nx-view-load-bar span {
  position: absolute; top: 0; left: -40%;
  width: 40%; height: 100%; border-radius: 2px;
  background: var(--nx-gold, var(--accent));
  animation: nxViewSweep 1.5s ease-in-out infinite;
}
.nx-view-load-label {
  font-size: 12.5px; color: var(--nx-muted, var(--muted));
  letter-spacing: 0.03em;
}
@keyframes nxViewPulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
@keyframes nxViewSweep { 0% { left: -40%; } 60% { left: 100%; } 100% { left: 100%; } }
@media (prefers-reduced-motion: reduce) {
  .nx-view-load-dot, .nx-view-load-bar span { animation: none; }
  .nx-view-load-bar span { left: 30%; }
}
/* Error card */
.nx-view-error-icon {
  width: 42px; height: 42px; color: var(--nx-gold, var(--accent)); opacity: 0.9;
}
.nx-view-error-title { font-size: 16px; font-weight: 700; color: var(--nx-text-strong, var(--text)); }
.nx-view-error-msg { font-size: 13px; color: var(--nx-muted, var(--muted)); max-width: 34ch; line-height: 1.55; margin: -4px 0 4px; }
/* v674 — the technical reason under a failed view. Monospace and selectable,
   because its entire job is to be read back to whoever is debugging it.
   --nx-text-secondary, never --nx-faint: this is the sentence that explains
   why the screen is empty, and the legibility guard holds it to 4.5:1. */
.nx-view-error-detail {
  font-family: var(--nx-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px; line-height: 1.45; color: var(--nx-text-secondary, var(--muted));
  max-width: 38ch; margin: 2px 0 10px; word-break: break-word; user-select: text;
}
.nx-view-error-btn {
  padding: 11px 26px; border-radius: 999px;
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.4));
  background: var(--nx-gold-faint, rgba(212, 164, 78, 0.12));
  color: var(--nx-gold, var(--accent));
  font-family: inherit; font-size: 13.5px; font-weight: 700;
  letter-spacing: 0.3px; cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}
.nx-view-error-btn:hover { border-color: var(--nx-gold, var(--accent)); background: var(--nx-gold-soft, rgba(212, 164, 78, 0.18)); }
.nx-view-error-btn:active { transform: scale(0.97); }

/* ═══ v393 MASTHEAD — "The Folio" ═════════════════════════════════════
   One 44px row (the bell/menu touch floor governs): coin bare top-left,
   a centered two-line brand block — NEXUS wordmark over a tiny dateline
   ("SUN · JUL 27 · 78° CLOUDY") — nested INSIDE the row, bell + menu
   right. Below, a fading gold hairline divides header from content.
   Total ≈ 52px incl. divider (v392's second row cost ~80px).
   Beacon dot + persona lettering stay retired; elements remain in the
   DOM for the JS that addresses them. */
.nav {
  flex-wrap: nowrap !important;
  align-items: center !important;
  /* v395 — Alfredo: "give it more padding. allow 65px header."
     10 + 44 row + 10 + 1 divider = 65px (safe-area replaces the top
     10px on notched phones). */
  padding: max(10px, env(safe-area-inset-top, 10px)) 16px 10px !important;
  border-bottom: 0 !important;
}
/* The divider — an engraved rule, bright at center, dying at the edges.
   (The old flat 1px rgba(...,.22) border was below perceptual threshold
   on OLED navy — this one is unmissable without boxing the screen.) */
.nav::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(212, 164, 78, 0)    0%,
    rgba(212, 164, 78, .50) 15%,
    rgba(230, 177, 87, .85) 50%,
    rgba(212, 164, 78, .50) 85%,
    rgba(212, 164, 78, 0)  100%);
  pointer-events: none;
}
.nx-mast-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-width: 0;
  position: relative;
  height: 44px;
}
.nav-nexus .nav-dot { display: none !important; }   /* beacon retired (v392) */
.nx-mast-persona    { display: none !important; }   /* lettering retired (v392) */
.nav .nav-nexus, .nav .nx-mast-date,
.nav .nav-more, .nav .nav-tabs { margin-top: 0; }   /* midline hack retired with the label */
.nx-mast-row .nav-bell { margin-left: auto; }       /* actions hug the right */
/* v394 — the hamburger was the OLD layout's right-hugger (nexus.css:1766
   margin-left:auto). With the bell now owning the auto margin, a second
   auto splits the row's free space in half and strands the bell mid-row,
   printed over the wordmark (Alfredo's screenshot). One spacer only. */
.nx-mast-row .nav-more { margin-left: 0; }
.nx-mast-row .nav-tabs { margin-left: 0; }          /* same trap on tablet (nexus.css:115) */
/* Coin: keep the 34px face (persona affordance), grow the hit target
   to 44px with padding, optically aligned to the 16px gutter. */
.nx-mast-coin-wrap { padding: 5px; margin-left: -5px; }
/* The folio dateline — mono under the wordmark. v409: 8→9.5px (Alfredo:
   "let the weather and nexus take up a bit more space"). */
.nx-mast-date {
  font-family: var(--nx-font-mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--nx-faint);
  line-height: 1;
  white-space: nowrap;
  overflow: visible;
}
/* v409 — the weather glyph, a touch larger than the mono dateline so it
   reads as an icon, nudged to sit on the text baseline. */
.nx-mast-date .nx-wx-emoji {
  font-size: 1.35em;
  line-height: 1;
  vertical-align: -0.08em;
}
/* Phones: the centered two-line brand block. Wordmark bottom edge sits
   1.5px above the row midline, dateline top edge 1.5px below — a 22px
   stack floating inside the 44px row, smaller than the coin beside it. */
@media (max-width: 768px) {
  .nx-mast-row .nav-nexus {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, calc(-100% - 2px));
    /* v409 — 11→13px wordmark (Alfredo: "a bit more space"). */
    font-size: 13px !important;
    letter-spacing: 3px !important;
    padding-left: 3px !important;   /* tracking compensation — true optical center */
    line-height: 1 !important;
  }
  .nx-mast-row .nx-mast-date {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, 2px);
  }
}
/* Tablet/desktop: brand stays in flow beside the coin (the rail owns
   the middle); the dateline becomes a right-aligned folio before the
   bell. */
@media (min-width: 769px) {
  .nx-mast-date { margin-left: auto; font-size: 10px; }
  .nx-mast-row .nav-bell { margin-left: 14px; }
}

/* ═══ v435 — UNTRUSTED-NETWORK GATE (.nx-gate) — full revamp ══════════════
   Shown when the PIN was correct but the network is untrusted and the
   device holds no trust key. Hierarchy: GPS unlock is the hero, Wi-Fi
   retry is secondary, the email hatch is tertiary. Mechanical press on
   every action, focus rings, spinner status states, both themes. */
.nx-gate-overlay {
  position: fixed; inset: 0; z-index: 100002;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background: radial-gradient(120% 90% at 50% 0%, rgba(212, 164, 78, 0.07), transparent 55%), rgba(6, 9, 18, 0.88);
  
  
  animation: nxGateFade .28s ease both;
}
@keyframes nxGateFade { from { opacity: 0; } to { opacity: 1; } }
.nx-gate-card {
  width: 100%; max-width: 400px;
  background: linear-gradient(180deg, #1a2338, #131a2b 60%);
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.3));
  border-radius: 20px;
  padding: 24px 22px 18px;
  text-align: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06),
              0 24px 60px rgba(0, 0, 0, 0.55),
              0 0 0 1px rgba(212, 164, 78, 0.05);
  animation: nxGateRise .32s cubic-bezier(.22, .9, .3, 1) both;
}
@keyframes nxGateRise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.nx-gate-kicker {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px; font-weight: 700; letter-spacing: 3px;
  color: var(--nx-gold, #d4a44e); opacity: 0.85;
  margin-bottom: 14px; text-transform: uppercase;
}
.nx-gate-disc {
  width: 64px; height: 64px; margin: 0 auto 12px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, rgba(212, 164, 78, 0.16), rgba(212, 164, 78, 0.05));
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.3));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.nx-gate-icon { width: 30px; height: 30px; color: var(--nx-gold, #d4a44e); display: block; }
.nx-gate-title {
  font-size: 19px; font-weight: 700; letter-spacing: -0.2px;
  color: var(--nx-text-strong, #f0e9dd);
  margin-bottom: 8px; text-wrap: balance;
}
.nx-gate-msg {
  font-size: 13.5px; line-height: 1.55;
  color: var(--nx-muted, #9aa3b2);
  margin-bottom: 16px; text-wrap: pretty;
}
.nx-gate-es { display: block; margin-top: 4px; font-size: 11.5px; color: var(--nx-faint, #77808f); }
/* Hero action — mechanical press: darker 3px bottom edge collapses on tap.
   Fixed heights + border-box keep the card from reflowing during the press. */
.nx-gate-locate {
  box-sizing: border-box; width: 100%; min-height: 54px; padding: 13px 15px;
  border: 0; border-bottom: 3px solid var(--nx-gold-deep, #8a6a1c);
  border-radius: 14px; margin-bottom: 12px;
  background: linear-gradient(180deg, #e2b45e, var(--nx-gold, #d4a44e) 55%, #c3923a);
  color: #17130a;
  font-family: inherit; font-size: 16px; font-weight: 800;
  letter-spacing: 0.2px; cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 8px 22px rgba(212, 164, 78, 0.25);
  transition: transform .1s ease, box-shadow .12s ease, border-bottom-width .1s ease;
  -webkit-tap-highlight-color: transparent;
}
.nx-gate-locate:active {
  transform: translateY(2px); border-bottom-width: 1px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 3px 10px rgba(212, 164, 78, 0.18);
}
.nx-gate-locate:disabled { opacity: 0.55; cursor: default; transform: none; }
.nx-gate-note {
  font-size: 11.5px; line-height: 1.5; color: var(--nx-gold, #d4a44e);
  background: var(--nx-gold-faint, rgba(212, 164, 78, 0.10));
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.22));
  border-radius: 10px; padding: 9px 11px; margin-bottom: 14px;
}
.nx-gate-retry {
  box-sizing: border-box; width: 100%; min-height: 46px; padding: 12px;
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.30));
  border-radius: 12px; background: rgba(255, 255, 255, 0.03);
  color: var(--nx-text, #e7e0d1);
  font-family: inherit; font-size: 13.5px; font-weight: 600;
  letter-spacing: 0.2px; cursor: pointer;
  transition: transform .1s ease, background .12s ease, border-color .12s ease;
  -webkit-tap-highlight-color: transparent;
}
.nx-gate-retry:active { transform: translateY(1px); background: var(--nx-gold-faint, rgba(212, 164, 78, 0.10)); }
/* Divider with centered label — replaces the "— or —" text row. */
.nx-gate-or {
  display: flex; align-items: center; gap: 10px;
  font-size: 10px; letter-spacing: 2px; text-transform: uppercase;
  color: var(--nx-faint, #77808f); margin: 14px 0 10px;
}
.nx-gate-or::before, .nx-gate-or::after {
  content: ''; flex: 1; height: 1px;
  background: linear-gradient(90deg, transparent, var(--nx-gold-line, rgba(212, 164, 78, 0.25)));
}
.nx-gate-or::after { background: linear-gradient(270deg, transparent, var(--nx-gold-line, rgba(212, 164, 78, 0.25))); }
.nx-gate-email {
  box-sizing: border-box; width: 100%; min-height: 46px; padding: 12px;
  border: 1px solid var(--nx-gold-line, rgba(212, 164, 78, 0.35));
  border-radius: 12px; background: transparent; color: var(--nx-gold, #d4a44e);
  font-family: inherit; font-size: 14px; font-weight: 700; cursor: pointer;
  transition: transform .1s ease, background .12s ease;
  -webkit-tap-highlight-color: transparent;
}
.nx-gate-email:active { transform: translateY(1px); background: var(--nx-gold-faint, rgba(212, 164, 78, 0.08)); }
.nx-gate-email:disabled { opacity: 0.5; cursor: default; transform: none; }
/* Status line — .nx-busy shows a spinning ring, .nx-ok goes green. */
.nx-gate-status {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 12.5px; line-height: 1.5; color: var(--nx-muted, #9aa3b2);
  text-align: center; margin: 12px 2px 4px; min-height: 18px;
}
.nx-gate-status:empty { visibility: hidden; }
.nx-gate-status.nx-busy::before {
  content: ''; flex-shrink: 0; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--nx-gold-line, rgba(212, 164, 78, 0.3));
  border-top-color: var(--nx-gold, #d4a44e);
  animation: nxGateSpin .8s linear infinite;
}
@keyframes nxGateSpin { to { transform: rotate(360deg); } }
.nx-gate-status.nx-ok { color: var(--nx-green, #59b97c); font-weight: 700; }
.nx-gate-back {
  width: 100%; margin-top: 6px; padding: 11px;
  background: none; border: 0; border-radius: 10px;
  color: var(--nx-faint, #77808f); font-family: inherit; font-size: 13px; cursor: pointer;
}
.nx-gate-back:active { color: var(--nx-muted, #9aa3b2); }
.nx-gate-locate:focus-visible, .nx-gate-retry:focus-visible,
.nx-gate-email:focus-visible, .nx-gate-back:focus-visible {
  outline: 2px solid var(--nx-gold, #d4a44e); outline-offset: 2px;
}
/* Light theme — cream card on a warm scrim (never a black wash on cream). */
[data-theme="light"] .nx-gate-overlay {
  background: radial-gradient(120% 90% at 50% 0%, rgba(168, 116, 42, 0.10), transparent 55%), rgba(58, 46, 24, 0.45);
}
[data-theme="light"] .nx-gate-card {
  background: linear-gradient(180deg, #fdfaf2, #f6efdd 65%);
  border-color: rgba(143, 95, 31, 0.35);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 24px 60px rgba(80, 60, 30, 0.32);
}
[data-theme="light"] .nx-gate-kicker { color: #8f5f1f; }
[data-theme="light"] .nx-gate-disc {
  background: radial-gradient(circle at 50% 35%, rgba(168, 116, 42, 0.14), rgba(168, 116, 42, 0.04));
  border-color: rgba(143, 95, 31, 0.3);
}
[data-theme="light"] .nx-gate-icon { color: #8f5f1f; }
[data-theme="light"] .nx-gate-title { color: #241c10; }
[data-theme="light"] .nx-gate-msg { color: #5a4a30; }
[data-theme="light"] .nx-gate-es { color: #8a7a5e; }
[data-theme="light"] .nx-gate-note {
  color: #7a4f12; background: rgba(168, 116, 42, 0.10); border-color: rgba(143, 95, 31, 0.3);
}
[data-theme="light"] .nx-gate-locate {
  background: linear-gradient(180deg, #b8823a, #a8742a 55%, #96661f);
  border-bottom-color: #6d4c12; color: #fff8ec;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 8px 22px rgba(143, 95, 31, 0.28);
}
[data-theme="light"] .nx-gate-retry {
  background: rgba(143, 95, 31, 0.04); color: #4a3a20; border-color: rgba(143, 95, 31, 0.35);
}
[data-theme="light"] .nx-gate-email { color: #8f5f1f; border-color: rgba(143, 95, 31, 0.4); }
[data-theme="light"] .nx-gate-or, [data-theme="light"] .nx-gate-status { color: #7a6a4e; }
[data-theme="light"] .nx-gate-status.nx-busy::before {
  border-color: rgba(143, 95, 31, 0.25); border-top-color: #8f5f1f;
}
[data-theme="light"] .nx-gate-status.nx-ok { color: #2e7d4f; }
[data-theme="light"] .nx-gate-back { color: #7a6a4e; }
@media (prefers-reduced-motion: reduce) {
  .nx-gate-overlay, .nx-gate-card { animation: none; }
  .nx-gate-status.nx-busy::before { animation: none; }
  .nx-gate-locate, .nx-gate-retry, .nx-gate-email { transition: none; }
}
/* v418 — admin secret door: the empty pad key becomes a gold → submit, and
   the NEXUS wordmark breathes gold so the owner knows the door opened. */
#pinScreen .pin-key.pin-key-go {
  visibility: visible !important; opacity: 1 !important; pointer-events: auto !important;
  color: var(--nx-gold, #d4a44e); font-size: 26px; font-weight: 400; cursor: pointer;
}
/* v564 — beacon law, again (Alfredo, on this exact element: "remove nexus
   logo beacons. nexus is glowing beacon"). The breathing gold text-shadow
   is gone; admin mode now announces itself with a STATIC colour shift —
   colour change is how state speaks, motion is not. The gold → key on the
   pad stays as the other tell. */
#pinScreen .pin-title.pin-title-admin { color: #e6b157; }
[data-theme="light"] .nx-gate-email { color: #8f5f1f; border-color: rgba(143, 95, 31, 0.4); }
[data-theme="light"] .nx-gate-or, [data-theme="light"] .nx-gate-status { color: #7a6a4e; }
[data-theme="light"] .nx-gate-back { color: #5a4a30; }

/* ═══ v436 — gate buttons: line-art icons, no emoji ═══ */
.nx-gate-locate, .nx-gate-retry, .nx-gate-email {
  display: flex; align-items: center; justify-content: center; gap: 9px;
}
.nx-gate-btn-ico { width: 18px; height: 18px; flex-shrink: 0; }
.nx-gate-retry .nx-gate-btn-ico, .nx-gate-email .nx-gate-btn-ico { width: 16px; height: 16px; }

/* ═══════════════════════════════════════════════════════════════════════════
   v498 — THE SEVENTEEN UNDEFINED TOKENS (Alfredo: "fix the 17 tokens")
   ═══════════════════════════════════════════════════════════════════════════
   Every name below was REFERENCED across the estate and DEFINED NOWHERE. A
   `var(--x, fallback)` whose token does not exist never errors and never
   warns — it silently freezes at the fallback forever. That is what made the
   Weekly Schedule sheet unreadable in light theme (v497: --nx-panel), and
   `scripts/css-token-check.cjs` found sixteen more of the same.

   TWO RULES FOLLOWED HERE, so this is a repair and not a redesign:

   1. The DARK value equals what the fallback already resolved to, so dark
      theme is unchanged. The only exceptions are called out inline below.
   2. Nothing invents a new colour. Every definition is an alias onto a token
      the palette already carries, which is why these can never drift again:
      change --nx-red once and the danger tokens follow.

   Where call sites disagreed about a token's strength, the call site was
   corrected rather than the token bent to fit — see --nx-amber-soft. */
:root {
  /* Gold, one step stronger. All 24 uses fall back to var(--nx-gold), so dark
     is identical; light is where this earns its name (below). */
  --nx-gold-strong:   var(--nx-gold);

  /* Neutral hairline. Call sites used .08–.14 and TWO used no fallback at all
     (`border: 1px solid var(--nx-line)`), which is invalid CSS — those two
     borders in equipment.css have simply never rendered. 0.09 sits in the
     middle of the range that was in use. */
  --nx-line:          rgba(255, 255, 255, 0.09);

  /* Muted text, three names for one idea inherited from three different
     files. Kept as separate names because they are referenced that way; all
     three now track the palette instead of three hardcoded greys. */
  --nx-text-dim:       #b6ad9a;
  --nx-text-secondary: #b9b3a5;
  --nx-dim:            #5a584f;
  --nx-fainter:        rgba(255, 255, 255, 0.18);
  /* nexus.css's unprefixed legacy pair. */
  --text-dim:          var(--nx-muted);
  --text-faint:        #746c5e;

  /* Semantics. --nx-danger was a hand-typed #d04a4a two units off the
     palette's own oxblood; it is the same colour, so this is a no-op that
     makes it follow the theme. */
  --nx-danger:        var(--nx-red);
  --nx-danger-line:   var(--nx-red-line);
  --nx-amber-soft:    var(--nx-gold-soft);
  /* CHANGE, stated: --nx-warn fell back to --nx-faint, so .ord-vdetail-sub-warn
     has been rendering warnings in grey italic. A token named warn beside a
     class named warn should be amber. One selector in Ordering changes. */
  --nx-warn:          var(--nx-amber);

  /* Surfaces used by equipment-audit.css and one nx-system rule. */
  --nx-bg-soft:       rgba(255, 255, 255, 0.02);
  --nx-bg-elev:       #131316;
  --nx-bg-card:       #16161a;

  /* Fonts — aliases onto the real stacks so a font change lands everywhere. */
  --nx-font-ui:       var(--nx-font-body);
  --font-display:     var(--nx-font-display);
}

[data-theme="light"] {
  /* THIS is the whole point of the exercise. Every one of these was stuck at
     a dark-theme value on cream. */

  /* Deeper than the light gold so "strong" still reads as emphasis — the
     light-theme rules that ask for it are asking for punch, and plain gold on
     cream is already deepened. */
  --nx-gold-strong:   var(--nx-label-2);

  --nx-line:          var(--nx-border);

  --nx-text-dim:       var(--nx-muted);
  --nx-text-secondary: var(--nx-muted);
  --nx-dim:            var(--nx-faint);
  --nx-fainter:        rgba(30, 26, 20, 0.22);
  --text-dim:          var(--nx-muted);
  --text-faint:        var(--nx-faint);

  --nx-danger:        var(--nx-red);
  --nx-danger-line:   var(--nx-red-line);
  --nx-amber-soft:    var(--nx-gold-soft);
  --nx-warn:          var(--nx-amber);

  --nx-bg-soft:       var(--nx-highlight-tint);
  --nx-bg-elev:       var(--nx-surface-solid);
  --nx-bg-card:       var(--nx-elevated);
}

/* v674 — the only thing on screen when NEXUS cannot build its database
   client at all. Deliberately unmissable and theme-independent: at this
   point no data has loaded, so it must read on whatever ground it lands on. */
.nx-boot-failed {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  z-index: 99999; max-width: min(520px, calc(100vw - 32px));
  padding: 14px 18px; border-radius: 12px;
  background: #2a1414; color: #ffd9d9; border: 1px solid #a83e3e;
  font-size: 13px; line-height: 1.5; text-align: center;
}
