/* ==========================================================
   TRU COMPONENTS — shared control & surface classes.
   Canonical spec: truflow-mobile's 2026 Luminous Dark Glass
   button rules, rewritten onto the L2 semantic tokens.
   Consumes ONLY L2 tokens — never an L1 primitive, never a
   raw hex. Loaded right after tru-tokens.css in every app;
   the per-app copies of these rules were retired in favour
   of this file (fix/shared-ui-kit, Phase 1).
   ========================================================== */

/* ---------- Buttons ----------
   One vocabulary everywhere, and it is OPAQUE BY CONTRACT: a button
   never shows the photo or card behind it (translucent fills turn the
   cyan muddy). Solid --btn-bg, 1px cyan edge, cyan label. Tiers differ
   only in elevation: primary = solid + cyan edge + glow on interaction,
   secondary = raised titanium with a physical bottom edge, ghost = flat,
   danger = solid red push-button. Colours come ONLY from the --btn-*
   tokens in tru-tokens.css — tools/ci/button_style_guard.mjs fails the
   build on rgba()/alpha/hard-coded colour inside a button rule.
   Density is a per-app token (--btn-h); the visual language is not. */

.btn-primary {
  position: relative;
  background: var(--btn-bg);
  color: var(--btn-text);
  border: 1px solid var(--btn-border);
  border-top-color: var(--btn-edge-top);
  border-radius: var(--radius-btn);
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  font-size: var(--text-base);
  box-shadow:
    inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.16),
    inset 0 -2px 0 rgb(var(--tru-ink-900-rgb) / 0.65),
    0 10px 20px -8px rgb(0 0 0 / 0.9),
    0 0 14px -2px var(--btn-glow);
  transition: background var(--dur-fast) ease, box-shadow var(--dur-fast) ease, border-color var(--dur-fast) ease, color var(--dur-fast) ease, transform 90ms ease;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.btn-primary svg { color: var(--btn-text); }
.btn-primary:hover:not(:disabled) {
  background: var(--btn-bg-hover);
  border-color: var(--btn-border-hot);
  color: var(--btn-text-hot);
  transform: translateY(-1px);
  box-shadow:
    inset 0 0 16px rgb(var(--accent-rgb) / 0.18),
    inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.25),
    inset 0 -2px 0 rgb(var(--tru-ink-900-rgb) / 0.65),
    0 14px 24px -8px rgb(0 0 0 / 0.95),
    0 0 20px rgb(var(--accent-rgb) / 0.22);
}
/* Press = the button physically sinks and the glow flares — on a handset
   you can feel the tap even before the action fires. */
.btn-primary:active:not(:disabled) {
  transform: translateY(1px) scale(0.97);
  background: var(--btn-bg-press);
  border-color: var(--btn-border-hot);
  color: var(--btn-text-hot);
  box-shadow:
    inset 0 3px 6px rgb(0 0 0 / 0.75),
    0 2px 6px rgb(0 0 0 / 0.8);
}
.btn-primary:disabled {
  background: var(--btn-disabled-bg);
  color: var(--btn-disabled-text);
  border-color: var(--btn-disabled-border);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
  opacity: 1 !important;
}
.btn-primary:disabled svg { color: var(--btn-disabled-text); }

/* Danger — the one solid fill. Raised like the metered controls. */
.btn-danger {
  position: relative;
  background: var(--state-error);
  color: var(--text-primary);
  border: none;
  border-radius: var(--radius-btn);
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  font-size: var(--text-base);
  box-shadow: inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.24), 0 4px 0 var(--state-error-deep), 0 10px 22px -8px rgb(var(--state-error-rgb) / 0.5);
  transition: background var(--dur-fast) ease, box-shadow 90ms ease, transform 90ms ease;
}
.btn-danger:hover:not(:disabled) { background: var(--state-error-hover); }
.btn-danger:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.15), 0 1px 0 var(--state-error-deep), 0 2px 4px -2px rgb(0 0 0 / 0.9);
}
.btn-danger:disabled {
  background: var(--btn-disabled-bg);
  color: var(--btn-disabled-text);
  border: 1px solid var(--btn-disabled-border);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
  opacity: 1 !important;
}

/* Secondary — raised dark titanium with a physical bottom edge. Same
   opaque body + cyan edge as primary; the 3D lip is the tier marker. */
.tru-btn-secondary,
.btn-secondary {
  position: relative;
  background: var(--btn-bg);
  border: 1px solid var(--btn-border);
  border-top-color: var(--btn-edge-top);
  box-shadow: inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.10), 0 4px 0 var(--bg-base), 0 10px 20px -6px rgb(0 0 0 / 0.85);
  color: var(--text-primary);
  border-radius: var(--radius-btn);
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  font-size: var(--text-base);
  transition: background var(--dur-fast) ease, box-shadow 90ms ease, transform 90ms ease, border-color var(--dur-fast) ease, color var(--dur-fast) ease;
}
.tru-btn-secondary:hover:not(:disabled),
.btn-secondary:hover:not(:disabled) {
  background: var(--btn-bg-hover);
  border-color: var(--btn-border-hot);
  color: var(--text-primary);
  box-shadow: inset 0 0 16px rgb(var(--accent-rgb) / 0.10), inset 0 1px 0 rgb(var(--tru-paper-rgb) / 0.14), 0 4px 0 var(--bg-base), 0 10px 20px -6px rgb(0 0 0 / 0.85), 0 0 14px var(--btn-glow);
}
.tru-btn-secondary:active:not(:disabled),
.btn-secondary:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--bg-base), 0 2px 4px -2px rgb(0 0 0 / 0.9), 0 0 14px var(--btn-glow);
}
.tru-btn-secondary:disabled,
.btn-secondary:disabled {
  background: var(--btn-disabled-bg);
  color: var(--btn-disabled-text);
  border-color: var(--btn-disabled-border);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
  opacity: 1 !important;
}

/* Ghost — quietest tier, still opaque. Flat solid body, hairline edge;
   the cyan only surfaces on hover. Never see-through. */
.tru-btn-ghost {
  position: relative;
  background: var(--btn-bg);
  border: 1px solid var(--btn-disabled-border);
  box-shadow: none;
  color: var(--text-secondary);
  border-radius: var(--radius-btn);
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  box-sizing: border-box;
  font-size: var(--text-base);
  transition: background var(--dur-fast) ease, color var(--dur-fast) ease, border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.tru-btn-ghost:hover:not(:disabled) {
  background: var(--btn-bg-hover);
  border-color: var(--btn-border);
  color: var(--btn-text);
}
.tru-btn-ghost:active:not(:disabled) {
  background: var(--btn-bg-press);
  border-color: var(--btn-border-hot);
  color: var(--btn-text-hot);
}
.tru-btn-ghost:disabled {
  background: var(--btn-disabled-bg);
  color: var(--btn-disabled-text);
  border-color: var(--btn-disabled-border);
  cursor: not-allowed;
  transform: none;
  opacity: 1 !important;
}
.tru-btn-ghost.danger {
  color: var(--state-error);
  background: var(--btn-bg);
  border-color: var(--state-error-border);
}
.tru-btn-ghost.danger:hover:not(:disabled) { background: var(--btn-bg-hover); border-color: var(--state-error); color: var(--state-error-hover); }
.tru-btn-ghost.danger:active:not(:disabled) { background: var(--btn-bg-press); }

/* Loading — spinner inside, same footprint, no layout jump. Add
   .is-loading to any button tier while its action is in flight. */
.btn-primary.is-loading,
.btn-danger.is-loading,
.tru-btn-secondary.is-loading,
.btn-secondary.is-loading,
.tru-btn-ghost.is-loading {
  pointer-events: none;
  visibility: hidden;
}
:is(.btn-primary, .btn-danger, .tru-btn-secondary, .btn-secondary, .tru-btn-ghost).is-loading::after {
  visibility: visible;
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  border: 2px solid rgb(var(--accent-rgb) / 0.35);
  border-top-color: var(--accent-bright);
  animation: tru-spin 0.7s linear infinite;
}
.btn-danger.is-loading::after {
  border-color: rgb(var(--tru-paper-rgb) / 0.35);
  border-top-color: var(--text-primary);
}
@keyframes tru-spin { to { transform: rotate(360deg); } }

/* ---------- Opacity invariants ----------
   A disabled button is never see-through — not via its own rule and not
   via a disabled:opacity-* utility class in markup. */
button:disabled,
[role="button"][aria-disabled="true"],
:is(.btn-primary, .btn-danger, .tru-btn-secondary, .btn-secondary, .tru-btn-ghost, .market-btn, .mvbtn, .assist-btn, .assist-chip):disabled {
  opacity: 1 !important;
}

/* Tailwind alpha fills on <button> are demoted to the opaque --btn-bg:
   a utility like bg-white/5 exists to tint a surface, but on a button it
   shows the backdrop through and muddies the accent. Specificity of
   button[attr] beats the utility class; !important also wins against
   disabled:-prefixed variants. */
:is(button, [role="button"])[class*="bg-white/"],
:is(button, [role="button"])[class*="bg-black/"],
:is(button, [role="button"])[class*="bg-slate-500/"],
:is(button, [role="button"])[class*="bg-slate-600/"],
:is(button, [role="button"])[class*="bg-slate-700/"],
:is(button, [role="button"])[class*="bg-slate-800/"],
:is(button, [role="button"])[class*="bg-slate-900/"],
:is(button, [role="button"])[class*="bg-gray-500/"],
:is(button, [role="button"])[class*="bg-gray-600/"],
:is(button, [role="button"])[class*="bg-gray-700/"],
:is(button, [role="button"])[class*="bg-gray-800/"],
:is(button, [role="button"])[class*="bg-cyan-300/"],
:is(button, [role="button"])[class*="bg-cyan-400/"],
:is(button, [role="button"])[class*="bg-cyan-500/"],
:is(button, [role="button"])[class*="bg-cyan-600/"],
:is(button, [role="button"])[class*="var(--glass)"] {
  background: var(--btn-bg) !important;
}

/* ---------- Text on an accent fill ----------
   Any element whose fill is a light accent gets .on-fill so its
   label resolves to ink, not washed-out white. truinspect used the
   class without ever defining it; this is now the only definition. */
.on-fill {
  color: var(--text-on-accent) !important;
  text-shadow: none;
  font-weight: 600;
}
.on-fill svg {
  color: var(--text-on-accent);
  filter: none;
}

/* .on-fill was coined for the OLD translucent-cyan .btn-primary — ink text
   on a light fill. The primary face is now dark titanium, so ink would be
   invisible: every `btn-primary on-fill` (Shoot, + Add, Keep & next, Retake)
   resolves to the button text token instead. Bare `.on-fill` on a real light
   accent fill (premium's bg-[color:var(--cyan)]) keeps ink. */
.btn-primary.on-fill,
.btn-primary.on-fill svg,
.btn-primary.on-fill:hover:not(:disabled),
.btn-primary.on-fill:active:not(:disabled) {
  color: var(--btn-text) !important;
}
.btn-primary.on-fill:hover:not(:disabled),
.btn-primary.on-fill:active:not(:disabled) {
  color: var(--btn-text-hot) !important;
}
.btn-primary.on-fill:disabled {
  color: var(--btn-disabled-text) !important;
}

/* ---------- Surfaces ----------
   Five elevation levels, consumed through semantic classes:
     .surface-page    L0 — page base (solid, no shadow)
     .surface-card    L1 — cards and panels (also .tru-card)
     .surface-popover L2 — raised cards, popovers, dropdowns, menus
     .surface-sheet   L3 — sheets and drawers
     .surface-modal   L4 — modals and dialogs
   Every level is a SOLID fill — the cool frost is a gradient painted on
   top, so a button on the surface never sees photo colour through it.
   Levels 2–4 carry the 1px inner top-edge highlight inside their shadow
   tokens (inset 0 1px 0), brightening slightly with elevation. */
.surface-page {
  background: var(--surface-0);
}
.tru-card,
.surface-card,
/* The one shared card — every per-app card class resolves here. */
.card, .v-card, .pipeline-card, .stat-card, .glass-panel,
.ti-card, .ti-card-quiet, .tl-glass, .tile, .card-titanium {
  position: relative;
  isolation: isolate;
  background-color: var(--surface-1);
  background-image: var(--glass-card);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--surface-1-border);
  border-top-color: rgb(var(--tru-paper-rgb) / 0.24);
  border-bottom-color: rgb(0 0 0 / 0.7);
  border-radius: var(--radius-card);
  box-shadow: var(--surface-1-shadow);
  animation: tru-rise var(--dur-med) var(--ease) both;
}
.tru-card-float { box-shadow: var(--shadow-float); }

/* Only clickable cards respond to hover — a hover state is a claim about
   behaviour; putting it on static panels is what makes real affordances
   stop meaning anything. */
:is(.tru-card, .card, .v-card, .pipeline-card, .stat-card, .glass-panel, .ti-card, .tile):is(button, a, [role="button"]) {
  transition: border-color var(--dur-med) var(--ease-enter), box-shadow var(--dur-med) var(--ease-enter), transform var(--dur-med) var(--ease-enter);
}
:is(.tru-card, .card, .v-card, .pipeline-card, .stat-card, .glass-panel, .ti-card, .tile):is(button, a, [role="button"]):hover {
  border-color: var(--btn-border);
  box-shadow: var(--shadow-lift);
  transform: translateY(-2px);
}
.surface-popover {
  position: relative;
  isolation: isolate;
  background-color: var(--surface-2);
  background-image: var(--glass-card);
  backdrop-filter: var(--surface-2-blur);
  -webkit-backdrop-filter: var(--surface-2-blur);
  border: 1px solid var(--surface-2-border);
  border-radius: var(--radius-card);
  box-shadow: var(--surface-2-shadow);
}
.surface-sheet {
  position: relative;
  isolation: isolate;
  background-color: var(--surface-3);
  background-image: var(--glass-card);
  backdrop-filter: var(--surface-3-blur);
  -webkit-backdrop-filter: var(--surface-3-blur);
  border: 1px solid var(--surface-3-border);
  border-radius: var(--radius-card);
  box-shadow: var(--surface-3-shadow);
}
.surface-modal {
  position: relative;
  isolation: isolate;
  background-color: var(--surface-4);
  background-image: var(--glass-card);
  backdrop-filter: var(--surface-4-blur);
  -webkit-backdrop-filter: var(--surface-4-blur);
  border: 1px solid var(--surface-4-border);
  border-radius: var(--radius-card);
  box-shadow: var(--surface-4-shadow);
}
/* Dialog fallback: a role="dialog" panel with no explicit bg utility still
   lands on L4 — explicit surface/bg classes keep their own level. */
[role="dialog"]:not([class*="bg-"]) {
  background-color: var(--surface-4);
  box-shadow: var(--surface-4-shadow);
}
@keyframes tru-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---------- Motion ----------
   Four standard movements, all on the token easings:
     .tru-page     — page fade-up (enter, slow)
     .surface-sheet— sheet slide-up (enter, slow)  [wired automatically]
     .tru-stagger  — children appear in sequence   [opt-in list wrapper]
     card hover    — clickable cards lift          [wired automatically] */
.tru-page {
  animation: tru-rise var(--dur-slow) var(--ease-enter) both;
}
@keyframes tru-sheet-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
.surface-sheet {
  animation: tru-sheet-in var(--dur-slow) var(--ease-spring) both;
}
.tru-stagger > * {
  animation: tru-rise var(--dur-med) var(--ease-enter) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
.tru-stagger > :nth-child(2)  { --i: 1; }
.tru-stagger > :nth-child(3)  { --i: 2; }
.tru-stagger > :nth-child(4)  { --i: 3; }
.tru-stagger > :nth-child(5)  { --i: 4; }
.tru-stagger > :nth-child(6)  { --i: 5; }
.tru-stagger > :nth-child(7)  { --i: 6; }
.tru-stagger > :nth-child(8)  { --i: 7; }
.tru-stagger > :nth-child(n+9){ --i: 8; }
@keyframes tru-out {
  to { opacity: 0; transform: translateY(6px); }
}
.tru-exit { animation: tru-out var(--dur-med) var(--ease-exit) both; }

/* ---------- Attention ----------
   Warnings breathe instead of shout: a slow amber ring that expands and
   fades, so "overdue / needs you" reads at a glance without a neon pill.
   Pair with --state-attention-text for the label colour. */
@keyframes tru-attn-pulse {
  0%   { box-shadow: 0 0 0 0 rgb(var(--state-attention-rgb) / 0.32); }
  70%  { box-shadow: 0 0 0 5px rgb(var(--state-attention-rgb) / 0); }
  100% { box-shadow: 0 0 0 0 rgb(var(--state-attention-rgb) / 0); }
}
.tru-attn-pulse { animation: tru-attn-pulse 2.4s var(--ease) infinite; }
.tru-attn-text { color: var(--state-attention-text); }

/* ---------- Inputs ---------- */
.tru-input {
  background: rgb(var(--tru-paper-rgb) / 0.05);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-btn);
  color: var(--text-primary);
  font: inherit;
  min-height: 44px;
  padding: 0 14px;
  transition: border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease, background var(--dur-fast) ease;
}
.tru-input::placeholder { color: var(--text-muted); }
.tru-input:focus {
  outline: none;
  border-color: rgb(var(--accent-rgb) / 0.55);
  box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.16), 0 0 14px rgb(var(--accent-rgb) / 0.12);
}
/* Icon-in-field variants. This file is deliberately unlayered so it can be
   shared with the vanilla PWAs — that also means Tailwind utilities never
   beat it, so padding overrides for icons live here, not as utilities. */
.tru-input.has-leading-icon { padding-left: 42px; }
.tru-input.has-trailing-ctl { padding-right: 46px; }

/* ---------- Skeleton — shimmer for lists and cards, never a
   full-page spinner. ---------- */
.tru-skeleton {
  position: relative;
  overflow: hidden;
  background: rgb(var(--tru-paper-rgb) / 0.06);
  border-radius: var(--radius-sm);
}
.tru-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgb(var(--tru-paper-rgb) / 0.08), transparent);
  animation: tru-skeleton 1.6s ease-in-out infinite;
}
@keyframes tru-skeleton { to { transform: translateX(100%); } }

/* ---------- Scrollbars (canonical: TruFlow Premium) ---------- */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgb(var(--tru-paper-rgb) / 0.10);
  border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover { background: rgb(var(--tru-paper-rgb) / 0.20); }
* { scrollbar-width: thin; scrollbar-color: rgb(var(--tru-paper-rgb) / 0.14) transparent; }

/* ---------- Keyboard focus ----------
   Fallback ring for apps that don't define their own; app-level
   :focus-visible rules (all three React apps have one) still win. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------- Figures ----------
   Table cells and anything tagged .tnum line up digits in equal-width
   columns — prices, stock numbers, mileages and counts stop jittering
   row to row. Inter's proportional figures stay for prose; mono spans
   (IBM Plex) are already fixed-width and unaffected. */
td, th, .tnum {
  font-variant-numeric: tabular-nums;
}

/* ---------- Table ----------
   One shared table. Comfortable by default; .tru-table-compact tightens
   row padding. Sticky header, row hover on surface-2, tabular figures. */
.tru-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
  line-height: var(--text-sm--line-height);
}
.tru-table th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-1);
  font-size: var(--text-xs);
  line-height: var(--text-xs--line-height);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--surface-1-border);
}
.tru-table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--glass-line);
  vertical-align: middle;
}
.tru-table tbody tr {
  transition: background var(--dur-fast) var(--ease-enter);
}
.tru-table tbody tr:hover {
  background: var(--surface-2);
}
.tru-table tbody tr:last-child td {
  border-bottom: none;
}
.tru-table-compact th,
.tru-table-compact td {
  padding: var(--space-1) var(--space-3);
}

/* ---------- Empty state ----------
   One pattern: icon, one line of copy, at most one action. While data
   loads use .tru-skeleton rows — never a spinner page. */
.tru-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-8) var(--space-4);
  color: var(--text-muted);
}
.tru-empty .tru-empty-icon {
  color: var(--text-muted);
  opacity: 0.55;
}
.tru-empty p {
  font-size: var(--text-sm);
  line-height: var(--text-sm--line-height);
  max-width: 34ch;
  margin: 0;
}
.tru-empty .tru-empty-sub {
  font-size: var(--text-xs);
  line-height: var(--text-xs--line-height);
  opacity: 0.75;
}
.tru-empty .tru-btn,
.tru-empty .tru-empty-action {
  margin-top: var(--space-1);
}

/* ---------- Reduced motion ----------
   Global, not per-component: when the OS asks for stillness the whole kit
   goes still — entrances, hovers, spins, pulses, skeleton shimmer. */
@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;
  }
}
