/* =============================================================
   Enstructure ICC — Component CSS
   =============================================================
   Reusable, token-driven component styles shared across every
   ICC document (console + Modal/Icon/Variations specs). Load
   AFTER tokens.css. Contains ONLY component-level chrome that
   can't be expressed as a Tailwind utility — never raw colors.
   ============================================================= */

/* ── Scrollbars — single source (supersedes the old tokens.css copy) ── */
.thin-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.thin-scroll::-webkit-scrollbar-track { background: transparent; }
.thin-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-full);
  border: 3px solid transparent;
  background-clip: content-box;
}
.thin-scroll::-webkit-scrollbar-thumb:hover { background: var(--content-subtle); }

/* ── Utility shims that map to tokens ──────────────────────────────── */
.border-strong { border-color: var(--border-strong) !important; }
.text-interactive-primary { color: var(--interactive-primary); }

/* ── Default border colour — brand hairline, NOT Tailwind Play's grey #e5e7eb.
   Preflight defaults every border to grey; the brand wants the navy-tinted
   --border. Set it once globally so any `border` utility is on-brand without a
   per-element inline override. Tailwind border-* utilities (border-subtle /
   -strong / status borders) and inline styles still win via specificity. ── */
*, *::before, *::after { border-color: var(--border); }

/* ════════════════════════════════════════════════════════════════════
   LAYERED-OS SURFACE LANGUAGE (L0–L5)
   ════════════════════════════════════════════════════════════════════ */

/* L0 — Desktop backbone: faint blueprint grid + edge-masked bloom behind
   the whole shell. Sits at z-(-1); chrome/content planes ride above it. */
.os-desktop {
  position: relative;
  background-color: var(--desktop);
}
.os-desktop::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: var(--pattern-grid);
  background-size: var(--pat-size) var(--pat-size);
  -webkit-mask-image: radial-gradient(135% 115% at 85% -12%, #000 32%, transparent 82%);
  mask-image: radial-gradient(135% 115% at 85% -12%, #000 32%, transparent 82%);
  opacity: 0.9;
}
.os-desktop > * { position: relative; z-index: 1; }

/* ── Enstructure OS shell — the app fills the viewport edge-to-edge ──────
   (Full-bleed: no desktop margin, no window radius/shadow framing.) */
.os-shell {
  position: fixed; inset: 0; display: flex; overflow: hidden;
  padding: 0;
  background: var(--mat-content);
}

.os-window {
  position: relative; z-index: 1; flex: 1; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--mat-content);
  border-radius: 0;
  box-shadow: none;
}
.os-lights { display: inline-flex; align-items: center; gap: 7px; }
.os-lights > span { width: 11px; height: 11px; border-radius: var(--radius-full); display: block; }
.os-lights > .tl-r { background: #ED6A5E; box-shadow: inset 0 0 0 0.5px rgb(0 0 0 / 0.12); }
.os-lights > .tl-y { background: #F4BE4F; box-shadow: inset 0 0 0 0.5px rgb(0 0 0 / 0.12); }
.os-lights > .tl-g { background: #61C554; box-shadow: inset 0 0 0 0.5px rgb(0 0 0 / 0.12); }

/* L1 — Chrome glass: header + sidebar vibrancy. Degrades gracefully — the
   fill is opaque enough to stay legible if backdrop-filter is unsupported. */
.mat-header {
  background: var(--mat-header);
  backdrop-filter: var(--mat-header-blur);
  -webkit-backdrop-filter: var(--mat-header-blur);
  border-bottom: 1px solid rgb(255 255 255 / 0.12);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.10), 0 1px 0 rgb(0 0 0 / 0.10);
  position: relative;
  color: #fff;
}
/* Enstructure brand pattern — a faint blueprint grid riding over the gradient,
   fading in from the right (the brighter azure side). */
.mat-header::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgb(255 255 255 / 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 0.05) 1px, transparent 1px);
  background-size: 26px 26px;
  -webkit-mask-image: linear-gradient(95deg, transparent 12%, #000 78%);
  mask-image: linear-gradient(95deg, transparent 12%, #000 78%);
}
.mat-header > * { position: relative; z-index: 1; }
/* The bar is a dark brand surface, so its chrome flips to the on-brand light
   ramp. (Dark mode is already light-on-dark, so it's scoped to light mode.) */
html:not(.dark) .mat-header {
  --content-strong: #fff;
  --content: rgb(255 255 255 / 0.92);
  --content-muted: rgb(255 255 255 / 0.70);
  --content-subtle: rgb(255 255 255 / 0.55);
  --border: rgb(255 255 255 / 0.18);
  --border-strong: rgb(255 255 255 / 0.32);
  --secondary: rgb(255 255 255 / 0.12);
  --surface-sunken: rgb(255 255 255 / 0.12);
  --surface-raised: rgb(255 255 255 / 0.16);
}
/* Overlays anchored inside the header (account menu, alerts bell, popovers)
   reset to the app's light tokens so they stay dark-text-on-white + readable. */
html:not(.dark) .mat-header :is(.mat-thick, [role="menu"], [role="listbox"], [role="dialog"]) {
  color: var(--color-navy-800);
  --content-strong: var(--color-navy-900);
  --content: var(--color-navy-800);
  --content-muted: var(--color-warm-600);
  --content-subtle: var(--color-warm-500);
  --border: rgb(11 30 51 / 0.10);
  --border-strong: rgb(11 30 51 / 0.18);
  --secondary: var(--color-warm-100);
  --surface-sunken: var(--color-warm-200);
  --surface-raised: #FFFFFF;
}
.mat-sidebar {
  background: var(--mat-sidebar);
  backdrop-filter: var(--mat-sidebar-blur);
  -webkit-backdrop-filter: var(--mat-sidebar-blur);
  box-shadow: var(--glass-spec);
}
/* L5 — Thick glass: overlays, drawers, ⌘K. One consistent hairline edge +
   float shadow on EVERY floating surface (modal · drawer · popover · menu). */
.mat-thick {
  background: var(--mat-thick);
  backdrop-filter: var(--mat-thick-blur, blur(24px) saturate(180%));
  -webkit-backdrop-filter: var(--mat-thick-blur, blur(24px) saturate(180%));
  border: 1px solid var(--mat-thick-border, var(--border));
  box-shadow: var(--shadow-float);
}

/* L3 — Block elevation: resting card lift + defining hairline ring, and the
   floating variant for high overlays. The hairline ring inside --shadow-card
   IS the card edge — so the redundant hard border is neutralized here (matches
   the reference's borderless cards). Intentional colored borders set inline
   (success / danger accents) still win over this default. */
.elev-card  { box-shadow: var(--shadow-card); border-color: transparent; }
.elev-float { box-shadow: var(--shadow-float); border-color: transparent; }

/* ── Sunken well — the layer BELOW a card. Tuning controls / credential editors
   drop into it so elevation reads card (raised) → well (inset). Expresses the
   layered-OS depth model on detail screens (Detection Point, Camera). */
.os-well { background: var(--surface-sunken); border-radius: var(--radius-panel); box-shadow: inset 0 0 0 1px var(--border-subtle), inset 0 1px 2px rgb(22 34 76 / 0.05); }

/* ── Token-driven radius/shadow utilities ─────────────────────────────────
   Defined as plain CSS because the Tailwind CDN does NOT register the custom
   borderRadius / boxShadow theme keys at runtime — only its built-in scale
   generates. Without these, .rounded-panel / .rounded-window / .rounded-control
   / .rounded-pill resolve to 0px, so cards, buttons, and class-based overlays
   render with square corners. This maps each class to its design token, the
   same way .os-window already consumes var(--radius-window). */
.rounded-control { border-radius: var(--radius-control); }
.rounded-panel   { border-radius: var(--radius-panel); }
.rounded-window  { border-radius: var(--radius-window); }
.rounded-pill    { border-radius: var(--radius-pill); }
.shadow-card     { box-shadow: var(--shadow-card); }
.shadow-float    { box-shadow: var(--shadow-float); }

/* Scene fills — gradient + blueprint grid for heroes / empty states / wall. */
.scene-hero {
  background-color: var(--color-navy-900);
  background-image: var(--pattern-grid), var(--gradient-aurora);
  background-size: var(--pat-size) var(--pat-size), cover;
}
.scene-mesh { background: var(--gradient-mesh); }

/* Blueprint grid overlay for dark brand/hero panels (rides over photography). */
.grid-overlay-light {
  background-image:
    linear-gradient(rgb(255 255 255 / 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 0.06) 1px, transparent 1px);
  background-size: var(--pat-size) var(--pat-size);
  -webkit-mask-image: radial-gradient(120% 100% at 80% 0%, #000 40%, transparent 85%);
  mask-image: radial-gradient(120% 100% at 80% 0%, #000 40%, transparent 85%);
}

/* ── RZM tuning sliders ────────────────────────────────────────────── */
.icc-range { -webkit-appearance: none; appearance: none; height: 4px; border-radius: var(--radius-full); background: var(--surface-sunken); outline: none; }
.icc-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; border-radius: var(--radius-full); background: var(--interactive-primary); cursor: pointer; box-shadow: 0 0 0 3px var(--interactive-primary-surface); }
.icc-range::-moz-range-thumb { width: 16px; height: 16px; border: none; border-radius: var(--radius-full); background: var(--interactive-primary); cursor: pointer; }

/* ── Selected ring — THE reusable "this card/tile is chosen" treatment ──
   Replaces the scattered inline `boxShadow:'0 0 0 2px var(--interactive-primary)'`
   + `borderColor:'transparent'` pairs (PointDetail, CameraPicker, GeofenceEditor,
   ui2 StatCard…). Toggle with `cn(..., selected && 'ring-selected')`. */
.ring-selected { box-shadow: 0 0 0 2px var(--interactive-primary); border-color: transparent !important; }

/* ── Active state for inline filter / view / tool segmented controls ──
   (GroupToggle "By area", ViewToggle grid/list, editor tool switches.)
   Quiet accent-TINT, deliberately NOT the solid --interactive-primary fill —
   that weight is reserved for true CTAs so each view keeps exactly ONE
   solid-primary affordance. Matches the dominant "selected = accent tint"
   pattern already used by chips, selected rows, focus pills, Stacklights. */
.ctrl-on { background: var(--interactive-primary-surface); color: var(--interactive-primary); border-color: var(--interactive-primary); }
.ctrl-on:hover { background: var(--interactive-primary-surface-hover); }

/* ── Quick-Ack — the per-row queue action (LOW/MEDIUM) ───────────────
   Quiet outline so a long queue doesn't read as a column of solid azure
   (the one solid CTA lives in the detail pane's "No issue"). Hover lifts
   to the primary edge + text — NOT a fill, because the host's button
   reset forces button backgrounds, so we emphasize via border/colour. */
.ack-quiet { background: transparent; color: var(--content-muted); box-shadow: inset 0 0 0 1px var(--border); }
.ack-quiet:hover { color: var(--interactive-primary); box-shadow: inset 0 0 0 1px var(--interactive-primary); }

/* ── Interactive card lift — navy-tinted elevation on hover ────────── */
.card-lift { transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out), border-color var(--dur-base); }
.card-lift:hover { box-shadow: var(--shadow-float); transform: translateY(-2px); border-color: transparent; }
@media (prefers-reduced-motion: reduce) { .card-lift:hover { transform: none; } }

/* ── Segmented control — filters / view toggles ────────────────────── */
.seg { display: inline-flex; align-items: center; gap: 2px; padding: 3px; border-radius: var(--radius-md); background: var(--surface-sunken); border: 1px solid var(--border); }
.seg > button { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: var(--radius-sm); font-size: var(--font-size-xs); font-weight: 500; color: var(--content-muted); background: transparent; box-shadow: none; white-space: nowrap; }
.seg > button:hover { color: var(--content-strong); }
.seg > button[data-active="true"] { background: var(--surface-raised); color: var(--content-strong); box-shadow: var(--shadow-raised); }

/* ── Compact segmented filter — sunken track + lifting pill ──────────
   The inline filter / density / lens rows (Operator Wall, etc.) all shared
   the same active-pill style object. Centralized here; screens set state with
   `data-on` and stop re-inlining the lift + shadow. Distinct from .seg above
   (a fixed-height standalone control); this variant keeps each row's own
   layout utilities (flex-1, padding) and only owns the segment look. */
.seg-track { background: var(--surface-sunken); }
.seg-pill { color: var(--content-muted); transition: color var(--dur-fast) var(--ease-out); }
.seg-pill:hover { color: var(--content-strong); }
.seg-pill[data-on="true"] { color: var(--content-strong); background: var(--surface-raised); box-shadow: var(--shadow-raised); }

/* ── Sidebar nav items — palette via CSS vars set per treatment ──────
   Resting bg/shadow are declared EXPLICITLY (not left to initial) so the
   cascade always has a concrete value for both states. State changes are NOT
   transitioned: animating ANY property (background, box-shadow, OR color) on
   the de-activated item left it stuck on SPA nav (Chromium paint-invalidation
   bug — the transition never repaints the previous item, so two items read as
   active at once). The active pill + color snap instantly, which is crisp and
   on-brand for a command console (the DS calls for restrained motion). */
.nav-item { position: relative; color: var(--nav-fg); background: transparent; box-shadow: none; border-radius: var(--radius-pill);
  transition: background-color var(--dur-fast) var(--ease-out); }
.nav-item:not([data-active="true"]):hover { background: var(--nav-hover-bg); }
/* Active item = colour + weight ONLY. The moving .nav-pill (a sibling element,
   transform-animated) supplies the pill bg + shadow, so no per-item
   shadow/colour is ever transitioned on SPA nav — which is exactly what
   stranded two items as "active" before (Chromium paint-invalidation). Only
   background-color transitions, and only ever for hover on a NON-active row
   (the active row has no bg), so the de-activated item never animates anything. */
.nav-item[data-active="true"] { color: var(--nav-active-fg); font-weight: 600; }
/* CSS fallback indicator — drawn by the active ROW itself, shown only until the
   JS pill reports ready (data-pill-ready) or if JS never runs at all. Guarantees
   the active row is always marked (no flash, no stranded "coloured text only"
   state). Once the pill is ready it takes over and this hides. Not transitioned,
   so it never triggers the per-item paint-invalidation bug. */
.nav-rail:not([data-pill-ready]) .nav-item[data-active="true"] {
  background: var(--nav-active-bg);
  box-shadow: inset 0 0 0 1px var(--sidebar-item-border-active), var(--shadow-raised);
}

/* ── Animated selection rail — one shared indicator that slides between rows ──
   The pill sits at z-index:-1 inside the rail's own stacking context: above the
   vibrancy glass (parent background), below the row text. Only transform/size/
   opacity animate. */
.nav-rail { position: relative; z-index: 0; }
.nav-pill {
  position: absolute; top: 0; left: 0; z-index: -1; pointer-events: none; opacity: 0;
  border-radius: var(--radius-pill);
  /* Blue info-tint pill with an azure hairline (design.md). Lifts off the warm
     rail without the old glass cast. */
  background: var(--nav-active-bg);
  /* Seated, not floating: the shared --shadow-raised recipe (edge ring +
     a small ambient) + the azure hairline border. */
  box-shadow: inset 0 0 0 1px var(--sidebar-item-border-active), var(--shadow-raised);
  transition: transform var(--dur-fast) var(--ease-out),
              width     var(--dur-fast) var(--ease-out),
              height    var(--dur-fast) var(--ease-out),
              opacity   var(--dur-xfast) var(--ease-out);
  will-change: transform, width;
}
/* Redundant (non-colour) active cue: a short azure capsule in the rail's LEFT
   GUTTER — separated from the pill, not crowding the icon (matches the Option A
   reference). It rides the pill via ::before, so it tracks the active row's Y;
   its negative left sits it in the nav's 12px padding gutter (x≈2px), clear of
   the horizontal-overflow clip edge so it never triggers a scrollbar. */
.nav-pill::before {
  content: ""; position: absolute; left: -10px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 20px; border-radius: var(--radius-full);
  background: var(--nav-active-fg);
}
/* Tweak A/B: "Plain pill" reverts to the opaque floating pill, no tint/bar. */
[data-nav-indicator="plain"] .nav-pill {
  background: var(--nav-active-bg);
  box-shadow: var(--shadow-card);
}
[data-nav-indicator="plain"] .nav-pill::before { display: none; }
@media (prefers-reduced-motion: reduce) {
  .nav-pill  { transition: opacity var(--dur-xfast) linear; }
  .nav-item  { transition: none; }
}

/* ── Sidebar treatments — replaces the inlined `P` palette object ──── */
.sidebar--paper {
  --sidebar-bg: var(--color-warm-50); --sidebar-fg: var(--content-strong); --sidebar-edge: var(--sidebar-border);
  --sidebar-brand-bg: var(--color-navy-900); --sidebar-brand-fg: #FFFFFF;
  --sidebar-eyebrow: var(--content-subtle); --sidebar-sub: var(--content-muted);
  /* Nav link palette is sourced entirely from the theme sidebar tokens —
     single source of truth (light + dark + shadcn aliases all agree). */
  --nav-fg: var(--sidebar-item-fg); --nav-active-fg: var(--sidebar-item-fg-active);
  --nav-active-bg: var(--sidebar-item-bg-active); --nav-hover-bg: var(--sidebar-item-bg-hover);
  --sidebar-track: var(--surface-sunken); --sidebar-cap-label: var(--content-muted);
  --sidebar-user-bg: var(--surface-sunken); --sidebar-user-fg: var(--content-muted);
  --sidebar-user-sub: var(--content-subtle); --sidebar-switch: var(--interactive-primary);
}
/* Active nav = solid brand-navy pill (chosen redesign): white label, gold fill-icon,
   NO left indicator bar. Scoped to .nav-rail (the Configure rail — the one with the
   sliding pill) so the operator rail's plain <nav> doesn't inherit white-on-paper text.
   The operator rail gets the same language via .op-rail below. (.sidebar--navy is a
   parked, currently-unused dark treatment.) */
.sidebar--paper .nav-rail {
  --nav-active-bg: var(--surface-brand);
  --nav-active-fg: #FFFFFF;
  --sidebar-item-border-active: transparent;
}
.sidebar--paper .nav-rail .nav-pill::before { display: none; }
.sidebar--paper .nav-rail .nav-item[data-active="true"] :is(i, svg) { color: var(--interactive-accent); }
/* Operator (Wall) rail — a plain <nav> with no sliding pill, so paint the active row
   navy directly. Same selected-state language as the Configure rail: white label, gold icon. */
.op-rail .nav-item[data-active="true"] {
  background: var(--surface-brand); color: #FFFFFF;
  box-shadow: 0 4px 12px -4px rgb(16 24 40 / 0.45);
}
.op-rail .nav-item[data-active="true"] :is(i, svg) { color: var(--interactive-accent); }
.sidebar--navy {
  --sidebar-bg: var(--surface-brand); --sidebar-fg: #FFFFFF; --sidebar-edge: var(--border-inverse);
  --sidebar-brand-bg: var(--interactive-accent); --sidebar-brand-fg: var(--interactive-accent-fg);
  --sidebar-eyebrow: rgb(255 255 255 / 0.5); --sidebar-sub: rgb(255 255 255 / 0.5);
  --nav-fg: rgb(255 255 255 / 0.62); --nav-active-fg: #FFFFFF;
  --nav-active-bg: rgb(255 255 255 / 0.08); --nav-hover-bg: rgb(255 255 255 / 0.04);
  --sidebar-track: rgb(255 255 255 / 0.12); --sidebar-cap-label: rgb(255 255 255 / 0.6);
  --sidebar-user-bg: rgb(255 255 255 / 0.12); --sidebar-user-fg: #FFFFFF;
  --sidebar-user-sub: rgb(255 255 255 / 0.55); --sidebar-switch: var(--interactive-accent);
}

/* ── Surface motion — calm & precise, token-driven ──────────────────────
   Durations come from the semantic --dur-*-in/out tokens (primitives.css),
   overridable globally via html[data-motion] (calm·snappy·off). Easing:
   enter = --ease-out, exit = --ease-in. No spring on chrome (reserved for
   confirmation glyphs). Exit classes (.icc-*-out) are driven by the shared
   Modal / Toast / SlideOver components via useExitTransition. */
@keyframes icc-backdrop-in  { from { opacity: 0 } to { opacity: 1 } }
@keyframes icc-backdrop-out { from { opacity: 1 } to { opacity: 0 } }
@keyframes icc-modal-pop    { from { opacity: 0; transform: translateY(8px) scale(.985) } to { opacity: 1; transform: translateY(0) scale(1) } }
@keyframes icc-modal-out    { from { opacity: 1; transform: translateY(0) scale(1) } to { opacity: 0; transform: translateY(6px) scale(.99) } }
@keyframes icc-drawer-in    { from { transform: translateX(100%) } to { transform: translateX(0) } }
@keyframes icc-drawer-out   { from { transform: translateX(0) } to { transform: translateX(100%) } }
@keyframes icc-toast-in     { from { opacity: 0; transform: translateX(12px) } to { opacity: 1; transform: translateX(0) } }
@keyframes icc-toast-out    { from { opacity: 1; transform: translateX(0) } to { opacity: 0; transform: translateX(12px) } }
@keyframes icc-fade-in      { from { opacity: 0 } to { opacity: 1 } }
@keyframes icc-fade-out     { from { opacity: 1 } to { opacity: 0 } }

.icc-backdrop-in  { animation: icc-backdrop-in  var(--dur-backdrop-in)  var(--ease-out) both; }
.icc-backdrop-out { animation: icc-backdrop-out var(--dur-backdrop-out) var(--ease-in)  both; }
.icc-modal-pop    { animation: icc-modal-pop    var(--dur-modal-in)     var(--ease-out) both; will-change: transform, opacity; }
.icc-modal-out    { animation: icc-modal-out    var(--dur-modal-out)    var(--ease-in)  both; will-change: transform, opacity; }
.icc-drawer-in    { animation: icc-drawer-in    var(--dur-drawer-in)    var(--ease-out) both; will-change: transform; }
.icc-drawer-out   { animation: icc-drawer-out   var(--dur-drawer-out)   var(--ease-in)  both; will-change: transform; }
.icc-toast-in     { animation: icc-toast-in     var(--dur-toast-in)     var(--ease-out) both; }
.icc-toast-out    { animation: icc-toast-out    var(--dur-toast-out)    var(--ease-in)  both; }

/* Reduced motion → honest opacity-only crossfade (no transform), same cadence. */
@media (prefers-reduced-motion: reduce) {
  .icc-modal-pop, .icc-drawer-in,  .icc-toast-in,  .icc-backdrop-in  { animation: icc-fade-in  120ms var(--ease-out) both; }
  .icc-modal-out, .icc-drawer-out, .icc-toast-out, .icc-backdrop-out { animation: icc-fade-out 120ms var(--ease-in)  both; }
}

/* ── u-stagger — list / table entrance. Children rise in sequence, capped at
   8 so long tables stay instant. Opt-in: one class on the row container.
   TRANSFORM-ONLY (no opacity) by design: persistent list content must never
   be gated on an animation completing — a frozen/te-mid frame still shows the
   row (at most 8px low), never invisible. Fades are reserved for transient
   overlays (modals/toasts) where completion is guaranteed. ── */
@keyframes icc-row-in { from { transform: translateY(8px) } to { transform: translateY(0) } }
/* ── u-stagger — DISABLED globally (per request). Lists render instantly:
   no entrance, no stagger. Kept as a no-op so the class can stay on
   containers app-wide and be re-enabled from this one place later. ── */
.u-stagger > * { animation: none !important; opacity: 1 !important; transform: none !important; }

/* ── u-skeleton — directional shimmer (replaces the opacity blink). ──── */
.u-skeleton { position: relative; overflow: hidden; background: var(--surface-sunken); }
.u-skeleton::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in oklch, var(--surface-raised) 70%, transparent), transparent);
  animation: icc-shimmer 1.25s var(--ease-in-out) infinite; }
@keyframes icc-shimmer { 100% { transform: translateX(100%) } }
@media (prefers-reduced-motion: reduce) { .u-skeleton::after { animation: none } }

/* ── u-fade-up — tab / panel content switch (minimal page transition). ── */
@keyframes icc-fade-up { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: translateY(0) } }
.u-fade-up { animation: icc-fade-up var(--dur-tab) var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) { .u-fade-up { animation: icc-fade-in 120ms var(--ease-out) both; } }

/* ── icc-pop — dropdown / popover open-close (Select · Combobox · Menu).
   Calm scale-fade, no overshoot; flips transform-origin with data-side. ── */
@keyframes icc-pop-in  { from { opacity: 0; transform: scale(.97) translateY(-3px) } to { opacity: 1; transform: none } }
@keyframes icc-pop-out { from { opacity: 1; transform: none } to { opacity: 0; transform: scale(.98) translateY(-2px) } }
.icc-pop     { animation: icc-pop-in  var(--dur-fast) var(--ease-out) both; }
.icc-pop-out { animation: icc-pop-out var(--dur-xfast, 90ms) var(--ease-in) both; }
@media (prefers-reduced-motion: reduce) { .icc-pop, .icc-pop-out { animation: icc-fade-in 100ms var(--ease-out) both; } }

/* ── Reduced-motion: neutralize interaction transforms for sensitive users ── */
@media (prefers-reduced-motion: reduce) { .icc-press { transition: none; } }

/* ── icc-progress-indet — Progress (indeterminate). A token-tinted comet
   sweeps the track; reduced-motion freezes it as a calm partial fill. ── */
@keyframes icc-progress-indet { 0% { left: -40% } 100% { left: 100% } }
.icc-progress-indet { position: absolute; top: 0; bottom: 0; width: 40%; border-radius: inherit;
  animation: icc-progress-indet 1.15s var(--ease-in-out) infinite; }
@media (prefers-reduced-motion: reduce) {
  .icc-progress-indet { animation: none; left: 0; width: 60%; opacity: .7; }
}

/* ── icc-accordion — height transition via grid-template-rows (no max-height
   guessing). Honors reduced-motion by snapping. ── */
.icc-acc-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-base) var(--ease-out); }
.icc-acc-panel[data-open="true"] { grid-template-rows: 1fr; }
.icc-acc-panel > .icc-acc-inner { overflow: hidden; min-height: 0; }
@media (prefers-reduced-motion: reduce) { .icc-acc-panel { transition: none; } }

/* ── u-chip — THE reusable interactive-chip pattern. Replaces scattered
   `transition: all` on toggle/filter pills with one tokenized definition,
   and adds consistent press feedback (matches Button's active:scale). ── */
.u-chip {
  transition-property: color, background-color, border-color, box-shadow, transform;
  transition-duration: var(--dur-fast);
  transition-timing-function: var(--ease-out);
}
.u-chip:active:not(:disabled) { transform: scale(0.96); }

/* ── TABLE CHROME — one token source for EVERY table surface ──────
   The Enstructure read: azure is the "structure / working" colour, so the
   column header is a faint azure wash with strong navy text and an azure-
   tinted rule (scannable, on-brand). Zone bands stay warm-sunken so grouping
   reads as a quieter tier beneath the header. Row hover + selection are azure
   tints (not neutral grey). Flip the direction here once → every table follows.
   All values build on semantic tokens, so dark mode adapts automatically. */
:root {
  /* Quiet header (V2): no azure band — a subtle bottom rule + muted mono labels.
     The single global table change; every DataTable head reads calmer. */
  --table-head-bg:      transparent;
  --table-head-fg:      var(--content-muted);
  --table-head-border:  var(--border);
  --table-zone-bg:      var(--surface-sunken);
  --table-zone-fg:      var(--content);
  --table-row-hover:    color-mix(in oklch, var(--interactive-primary) 6%, transparent);
  --table-row-selected: var(--interactive-primary-surface);
}

/* ── Table column headers — THE reusable table-head row pattern ─── */
/* Applied to a <tr>/<thead> in plain-<table> screens; paints the band too so   */
/* CamTable / BulkRegister match the grid DataTable without extra utilities.    */
.tbl-head { font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--font-size-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: var(--tracking-wider); color: var(--table-head-fg); background: var(--table-head-bg); }

/* ── DataTable / Wall column-header band — the grid-based header row ── */
.dt-head-band { background: var(--table-head-bg); color: var(--table-head-fg); border-bottom: 1px solid var(--table-head-border); }

/* ── DataTable zone section bands — grouped rows get their own sticky header ── */
/* Sits flush under the sticky column header (which is ~34px tall). Reads as a   */
/* warm sunken band so the rows above/below float on the card (layered-OS depth) */
.dt-group-head { position: sticky; top: 34px; z-index: 9; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 6px 20px; background: var(--table-zone-bg); color: var(--table-zone-fg); border-bottom: 1px solid var(--border); }

/* ── Shared navigable-row hover — azure tint. ONE rule for table rows (.dt-row)
   AND list / menu rows across screens (.row-hover): every clickable row signals
   "actionable" with the same azure wash. Controls (buttons, menu headers, tabs,
   icon buttons) deliberately stay on the neutral hover:bg-secondary — that split
   is the system: rows = azure (navigate), controls = neutral (act). ── */
.dt-row, .row-hover { transition: background-color var(--dur-fast) var(--ease-out); }
.dt-row:hover, .row-hover:hover { background: var(--table-row-hover); }

/* ── Lift hover (opt-in via DataTable hover="lift") — borderless list rows that
   raise into a soft card on hover. The Areas list uses this on the paper plane;
   default tables keep the azure wash above. Placed AFTER the wash rule so it
   wins at equal specificity. ── */
.dt-row--lift { border-bottom-color: var(--border-subtle); transition: background-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.dt-row--lift:hover { background: var(--surface-raised); border-bottom-color: transparent; border-radius: var(--radius-panel); box-shadow: var(--shadow-sm); }
.u-nudge { transition: transform var(--dur-fast) var(--ease-out); }
.group:hover .u-nudge { transform: translateX(2px); }

@media (prefers-reduced-motion: reduce) {
  .u-chip { transition-property: color, background-color, border-color, box-shadow; }
  .u-chip:active { transform: none; }
  .u-nudge, .group:hover .u-nudge { transition: none; transform: none; }
}

/* ── Layout primitives (lib/layout.jsx · WS-1) ───────────────────────────────
   Cluster's contract: a single-line row that never overlaps. Every direct
   child gets min-width:0 so labels truncate instead of overrunning their
   neighbours — the rule a hand-rolled flex always forgets. Children that must
   keep their size opt out with .shrink-0 (the <Pin> helper). */
.cluster > * { min-width: 0; }
.cluster > .shrink-0 { min-width: max-content; }

/* ── Custom type-scale utilities (typography fix) ────────────────────────────
   The Tailwind Play CDN does not emit the inline `theme.fontSize` remap, so the
   three CUSTOM keys with no Tailwind built-in silently rendered at the inherited
   16px instead of their token size (45+ `text-2xs` nodes were affected). Define
   them explicitly here — deterministic, CDN-independent (WS-4), font-size only so
   line-height stays as authored. (Built-in keys text-xs…text-6xl still resolve
   via Tailwind's own utilities; remapping those to the larger DS values is a
   separate, broader change — see the type-scale note in the refactor plan.) */
.text-2xs      { font-size: var(--font-size-2xs); }
.text-sm-tight { font-size: var(--font-size-sm-tight); }
.text-md       { font-size: var(--font-size-md); }

/* ── Pinned semantic COLOR utilities (CDN-independent safety net) ─────────────
   The Tailwind Play CDN's JIT emits these custom token-mapped color utilities
   from a runtime scan — which can lag a frame after SPA navigation or miss a
   class entirely, leaving buttons/cards/surfaces briefly UNSTYLED (transparent
   bg, fallback text color). Same root cause as the type-scale pins above.

   These rules are a deterministic FALLBACK: Tailwind injects its own <style>
   AFTER this file, so when the JIT DOES emit a utility, Tailwind's identical
   rule wins on source order — these only take effect in the gap. They map 1:1
   to the same tokens the config declares, so the result is pixel-identical.
   Keep in sync with tailwind.config.js `theme.extend.colors`. */

/* Backgrounds — chrome surfaces + button/badge fills */
.bg-background      { background-color: var(--background); }
.bg-foreground      { background-color: var(--foreground); }
.bg-card            { background-color: var(--card); }
.bg-popover         { background-color: var(--popover); }
.bg-primary         { background-color: var(--primary); }
.bg-secondary       { background-color: var(--secondary); }
.bg-muted           { background-color: var(--muted); }
.bg-accent          { background-color: var(--accent); }
.bg-destructive     { background-color: var(--destructive); }
.bg-input           { background-color: var(--input); }
.bg-surface-base    { background-color: var(--surface-base); }
.bg-surface-raised  { background-color: var(--surface-raised); }
.bg-surface-sunken  { background-color: var(--surface-sunken); }
.bg-surface-overlay { background-color: var(--surface-overlay); }
.bg-surface-inverse { background-color: var(--surface-inverse); }
.bg-surface-brand   { background-color: var(--surface-brand); }
.bg-success-surface { background-color: var(--status-success-surface); }
.bg-warning-surface { background-color: var(--status-warning-surface); }
.bg-danger-surface  { background-color: var(--status-danger-surface); }
.bg-info-surface    { background-color: var(--status-info-surface); }

/* Foreground text — semantic content ramp + on-fill labels */
.text-foreground            { color: var(--foreground); }
.text-primary               { color: var(--primary); }
.text-primary-foreground    { color: var(--primary-foreground); }
.text-secondary-foreground  { color: var(--secondary-foreground); }
.text-destructive           { color: var(--destructive); }
.text-destructive-foreground{ color: var(--destructive-foreground); }
.text-accent-foreground     { color: var(--accent-foreground); }
.text-card-foreground       { color: var(--card-foreground); }
.text-popover-foreground    { color: var(--popover-foreground); }
.text-muted-foreground      { color: var(--muted-foreground); }
.text-content-strong        { color: var(--content-strong); }
.text-content               { color: var(--content); }
.text-content-muted         { color: var(--content-muted); }
.text-content-subtle        { color: var(--content-subtle); }
.text-content-disabled      { color: var(--content-disabled); }
.text-content-inverse       { color: var(--content-inverse); }
.text-content-on-brand      { color: var(--content-on-brand); }
.text-content-on-accent     { color: var(--content-on-accent); }
.text-content-on-media      { color: var(--content-on-media); }
.text-success-text          { color: var(--status-success-text); }
.text-warning-text          { color: var(--status-warning-text); }
.text-danger-text           { color: var(--status-danger-text); }
.text-info-text             { color: var(--status-info-text); }

/* Borders */
.border-subtle  { border-color: var(--border-subtle); }
.border-inverse { border-color: var(--border-inverse); }
.border-focus   { border-color: var(--border-focus); }
.border-input   { border-color: var(--input); }

/* Button interaction states (hover/active) — keep feedback if the JIT misses them */
.hover\:bg-primary-hover:hover         { background-color: var(--interactive-primary-hover); }
.active\:bg-primary-press:active       { background-color: var(--interactive-primary-press); }
.hover\:bg-secondary:hover             { background-color: var(--secondary); }
.hover\:bg-secondary-hover:hover       { background-color: var(--interactive-neutral-hover); }
.active\:bg-secondary-hover:active     { background-color: var(--interactive-neutral-hover); }
.active\:bg-secondary-press:active     { background-color: var(--interactive-neutral-press); }
.hover\:text-secondary-foreground:hover{ color: var(--secondary-foreground); }
.hover\:text-foreground:hover          { color: var(--foreground); }
.hover\:bg-destructive-hover:hover     { background-color: var(--interactive-danger-hover); }
.active\:bg-destructive-press:active   { background-color: var(--interactive-danger-press); }


