/* Lambs Ferry Tool Shed shell — copied by NOBODY; imported.
   Token values differ from Bama Dirt’s; token names do not.
   Every class name and every token NAME below is identical to the UI
   contract’s toolshed.css, because the contract’s rules reference them by
   name. Exactly seven token VALUES changed, all of them the paper, the ink
   and the brand browns; the accent green / warn amber / notify blue /
   danger red button law is untouched. */
/* ============================================================================
   UI CONTRACT v2, re-themed for Lambs Ferry. IMPORTED from @lambs/shell by
   every app; copied into none. DO NOT EDIT LOCALLY. A change is ruled at HQ
   (Lambs Ferry Building Company), then made once, here.
   ============================================================================

   "Field Journal" design system.
   Direction: a warm, crisp field tool. Stone-paper neutrals, the dirt-brown
   brand kept as the identity, translucent chrome the content scrolls under,
   and a small, strict motion vocabulary (below) used everywhere.

   MOTION VOCABULARY — the whole app speaks these tokens and nothing else:
     durations   --dur-press 120ms   press/hover feedback
                 --dur-quick 160ms   micro feedback, exits
                 --dur-view  200ms   view/tab content settle
                 --dur-enter 240ms   list rows, toasts, banners entering
                 --dur-sheet 320ms   the one big surface (dialog sheet)
     easings     --ease-out    strong ease-out — every entrance + feedback
                 --ease-in-out strong in-out — things moving while on screen
                 --ease-drawer iOS-like drawer curve — the sheet only
     stagger     --stagger 30ms per row, capped at 8 rows (set in JS as --i)
   Rules: animate transform + opacity only; entrances never start at scale(0);
   exits are faster than entrances; rapid/dynamic UI uses transitions (never
   keyframes) so it retargets mid-flight; reduced motion collapses movement
   but keeps opacity feedback — and zeroes DELAYS as well as durations.
   JS-driven motion (FLIP, WAAPI, fallback timers) reads these tokens via
   getComputedStyle(document.documentElement) — never hardcodes them.
   ========================================================================== */

/* ---------- TOKENS ---------- */
:root {
  color-scheme: light;

  /* Brand = dirt brown. The identity — kept, refined. */
  --brand: #8B5029;
  --brand-strong: #6E3F20;
  --brand-press: #5A3219;
  --brand-tint: #EFE4D6;
  --brand-ghost: rgba(139, 80, 41, .10);

  /* Accent = ONE green. Save, positive. */
  --accent: #2e7d32;

  /* Status — one each. */
  --danger: #b3261e;
  --warn:   #9a6206;
  --notify: #2563c7;

  /* Status tints. */
  --green-tint: #e7f2e7;
  --red-tint:   #f9e9e7;
  --amber-tint: #f7efdc;
  --azure-tint: #e9f0fb;

  /* Warm stone neutrals. */
  --bg:        #F4F1EA;
  --surface:   #ffffff;
  --surface-2: #efece7;
  --border:    #e3dfd8;
  --border-strong: #cfc9bf;
  --text:      #1F211F;
  --muted:     #6b6357;

  /* Type ramp. Base 16 locked (inputs never smaller — iOS zoom). */
  --fs-display: 28px;
  --fs-h1: 23px;
  --fs-h2: 17px;
  --fs-lg: 17px;
  --fs-base: 16px;
  --fs-sm: 14px;
  --fs-xs: 13px;
  --fs-2xs: 11px;
  --lh: 1.45;

  /* Tracking is size-specific: tight when large, open when tiny. */
  --track-display: -0.02em;
  --track-h1: -0.017em;
  --track-h2: -0.01em;
  --track-2xs: 0.07em;

  /* Spacing (6 steps). */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;

  /* Touch target — the 44px glove minimum. */
  --touch-min: 44px;

  /* Radii. */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-pill: 999px;

  /* Elevation — soft, warm, layered. */
  --shadow-card: 0 1px 2px rgba(43, 33, 20, .04), 0 2px 6px rgba(43, 33, 20, .05);
  --shadow-pop:  0 4px 12px rgba(43, 33, 20, .09), 0 16px 40px rgba(43, 33, 20, .16);
  --shadow-sheet: 0 -8px 40px rgba(43, 33, 20, .20);
  --shadow-toast: 0 6px 16px rgba(43, 33, 20, .22), 0 2px 6px rgba(43, 33, 20, .14);

  /* Stacking ladder. */
  --z-bar: 10;
  --z-pop: 40;
  --z-dialog: 60;
  --z-toast: 70;

  /* MOTION — the vocabulary (see header comment). */
  --dur-press: 120ms;
  --dur-quick: 160ms;
  --dur-view:  200ms;
  --dur-enter: 240ms;
  --dur-sheet: 320ms;
  --ease-out:    cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
  --stagger: 30ms;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* ---------- BASE ---------- */
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: var(--lh);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1 {
  font-size: var(--fs-h1); font-weight: 800; line-height: 1.2;
  letter-spacing: var(--track-h1); margin: 0 0 var(--sp-3);
  overflow-wrap: anywhere;
}
h2 {
  font-size: var(--fs-h2); font-weight: 700; letter-spacing: var(--track-h2);
  margin: 0 0 var(--sp-2);
}
p  { margin: 0 0 var(--sp-3); }
a  { color: var(--brand-strong); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }
button, [role="button"] { -webkit-tap-highlight-color: transparent; }

@media (hover: hover) and (pointer: fine) {
  a:hover { color: var(--brand); }
}

/* Every number that can sit above another number is tabular. */
.num { font-variant-numeric: tabular-nums; }

.page { max-width: 960px; margin: 0 auto; padding: var(--sp-4); }

.eyebrow {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: var(--track-2xs);
  text-transform: uppercase; color: var(--muted);
}

/* ---------- CHROME (sticky translucent header + tab bar) ----------
   One material layer; the page scrolls underneath it. The hairline + shadow
   only appear once content actually slides under ([data-scrolled], set in
   app JS from a rAF-throttled scroll listener) — a scroll edge effect, not
   a permanent divider. */
.chrome {
  position: sticky; top: 0; z-index: var(--z-bar);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.6);
  backdrop-filter: blur(14px) saturate(1.6);
  box-shadow: 0 1px 0 transparent;
  transition: box-shadow var(--dur-quick) var(--ease-out);
}
.chrome[data-scrolled] {
  box-shadow: 0 1px 0 var(--border), 0 4px 16px rgba(43, 33, 20, .06);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .chrome { background: var(--bg); }
}
@media (prefers-reduced-transparency: reduce) {
  .chrome { background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

.topbar {
  display: flex; align-items: center; gap: var(--sp-3);
  max-width: 960px; margin: 0 auto;
  padding: var(--sp-2) var(--sp-4) 0;
  min-height: var(--touch-min);
}
.topbar .app-name {
  font-weight: 800; font-size: var(--fs-lg); letter-spacing: -0.01em;
  color: var(--brand-strong); white-space: nowrap;
}
.topbar .crumb {
  color: var(--muted); font-size: var(--fs-sm); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar .crumb:not(:empty)::before { content: "/"; margin-right: var(--sp-2); color: var(--border-strong); }

/* The signed-in partner's name — always visible, pushed right (ratified
   ruling: partners see whose seat is active before they write anything). */
.topbar .who {
  margin-left: auto; color: var(--muted); font-size: var(--fs-sm);
  font-weight: 650; white-space: nowrap;
}

/* Icon buttons in the chrome (Search). Round, 44px, aria-label mandatory.
   GRADER FIX (b): the selector is TWO classes (.btn.btn-icon) so the pill
   radius outranks .btn's --r-sm. A one-class .btn-icon ties with .btn and
   loses to source order — the round buttons shipped square once. Never
   weaken this selector. */
.btn.btn-icon {
  min-width: var(--touch-min); width: var(--touch-min); padding: 0;
  border-radius: var(--r-pill); font-size: var(--fs-base); flex: none;
}
.btn-icon svg { width: 19px; height: 19px; display: block; }

/* The Help control — a LABELED pill reading "Help" (ratified ruling: never
   a bare "?" icon). Use: <button class="btn btn-ghost btn-pill">Help</button> */
.btn.btn-pill { border-radius: var(--r-pill); flex: none; }

/* ---------- TAB BAR ----------
   Quiet text tabs + one sliding brand underline. GRADER FIX (c): the
   indicator moves by TRANSFORM ONLY — translateX for position, scaleX for
   width — never by transitioning `width` (a layout property). The bar has a
   fixed 100px base width; the positioning helper writes
     indicator.style.transform = `translateX(${x}px) scaleX(${w / 100})`
   with transform-origin left. (Radius distortion under scaleX is
   imperceptible on a 3px bar.) The slide only enables after first placement
   ([data-ready], set two rAFs after positioning) so load never animates. */
.tabbar {
  position: relative;
  display: flex; align-items: stretch; gap: var(--sp-1);
  max-width: 960px; margin: 0 auto; padding: 0 var(--sp-3);
  overflow-x: auto; scrollbar-width: none;
}
.tabbar::-webkit-scrollbar { display: none; }
.tab {
  font: inherit; font-size: var(--fs-sm); font-weight: 650; color: var(--muted);
  background: none; border: 0; border-radius: var(--r-sm) var(--r-sm) 0 0;
  padding: var(--sp-3) var(--sp-3); cursor: pointer;
  min-height: var(--touch-min); white-space: nowrap;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  transition: color var(--dur-press) ease, opacity var(--dur-press) ease;
}
@media (hover: hover) and (pointer: fine) {
  .tab:hover { color: var(--text); }
}
.tab:active { opacity: .6; }
.tab.on { color: var(--brand-strong); }
.tab-indicator {
  position: absolute; bottom: 0; left: 0; height: 3px;
  width: 100px;                      /* fixed base — JS scales it, never resizes it */
  transform-origin: left center;
  transform: translateX(0) scaleX(0);
  border-radius: var(--r-pill) var(--r-pill) 0 0;
  background: var(--brand);
  opacity: 0;
  will-change: transform;
  pointer-events: none;
}
.tabbar[data-ready] .tab-indicator {
  transition: transform var(--dur-view) var(--ease-out),
              opacity var(--dur-quick) var(--ease-out);
}

/* ---------- BUTTONS ----------
   Color law kept: green = save (one per view) · brand = navigate/open ·
   red = destructive confirm only · ghost = everything secondary.
   Feedback lives on the press and it is instant: scale(0.97) on :active. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font: inherit; font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0;
  padding: 10px var(--sp-4); border-radius: var(--r-sm);
  border: 1px solid transparent; cursor: pointer;
  min-height: var(--touch-min);
  transition: background var(--dur-press) ease,
              border-color var(--dur-press) ease,
              transform var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) ease;
}
.btn svg { width: 16px; height: 16px; flex: none; }
.btn-primary { background: var(--accent); color: #fff; box-shadow: 0 1px 2px rgba(46,125,50,.35); }
.btn-brand   { background: var(--brand); color: #fff; box-shadow: 0 1px 2px rgba(111,71,32,.35); }
.btn-danger  { background: var(--danger); color: #fff; }
.btn-ghost   { background: var(--surface); color: var(--text); border-color: var(--border-strong); box-shadow: 0 1px 2px rgba(43,33,20,.05); }

@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover { background: color-mix(in srgb, var(--accent), #000 8%); }
  .btn-brand:hover   { background: color-mix(in srgb, var(--brand), #000 8%); }
  .btn-danger:hover  { background: color-mix(in srgb, var(--danger), #000 8%); }
  .btn-ghost:hover   { background: var(--surface-2); }
}
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn-primary:active:not(:disabled) { background: color-mix(in srgb, var(--accent), #000 16%); }
.btn-brand:active:not(:disabled)   { background: var(--brand-press); }
.btn-danger:active:not(:disabled)  { background: color-mix(in srgb, var(--danger), #000 16%); }
.btn-ghost:active:not(:disabled)   { background: var(--surface-2); }

.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* Loading — spinner joins the label, width holds, taps ignored. */
.btn.is-loading { pointer-events: none; }
.btn.is-loading::before {
  content: ""; width: 15px; height: 15px; flex: none;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: ts-spin .6s linear infinite;
}
@keyframes ts-spin { to { transform: rotate(360deg); } }

/* ---------- INPUTS & FIELDS ---------- */
label {
  display: block; font-size: var(--fs-xs); font-weight: 650; color: var(--muted);
  margin-bottom: var(--sp-1);
}
.input, select.input, textarea.input {
  width: 100%; font: inherit; font-size: var(--fs-base); /* ≥16px, always */
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  padding: 10px var(--sp-3); min-height: var(--touch-min);
  box-shadow: 0 1px 2px rgba(43, 33, 20, .04) inset;
  transition: border-color var(--dur-press) ease, box-shadow var(--dur-press) ease;
}
textarea.input { line-height: var(--lh); }
.input::placeholder { color: color-mix(in srgb, var(--muted) 72%, transparent); }
.input:focus {
  border-color: var(--brand); outline: none;
  box-shadow: 0 0 0 3px var(--brand-ghost);
}
.field { margin-bottom: var(--sp-4); }

/* Error — danger border + plain-English line; clears the moment it's valid. */
.input[aria-invalid="true"] { border-color: var(--danger); }
.input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(179, 38, 30, .12); }
.field-error { font-size: var(--fs-xs); font-weight: 650; color: var(--danger); margin-top: var(--sp-1); }

.input:disabled { background: var(--surface-2); color: var(--muted); cursor: not-allowed; }
.input[readonly] { background: var(--surface-2); }

input[type="checkbox"], input[type="radio"] { accent-color: var(--brand); }

/* Status text (e.g. "Saved ✓") — its own element, OUTSIDE the label, with
   role="status". JS toggles its TEXT CONTENT, never its opacity: a node
   that is always present but faded is read by screen readers as always
   there and its change is never announced (grader-corrected law). Reserve
   the width so the layout never shifts. */
.save-status {
  display: inline-block; margin-left: var(--sp-2); min-width: 4em;
  font-size: var(--fs-2xs); font-weight: 700; color: var(--accent);
}

/* ---------- CARDS ---------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-card);
  padding: var(--sp-4);
}
.card + .card { margin-top: var(--sp-4); }

/* ---------- PILLS · BADGES · CHIPS ---------- */
.pill {
  display: inline-block; font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: 0.02em;
  padding: 2px 9px; border-radius: var(--r-pill);
  background: var(--brand-tint); color: var(--brand-strong);
}
.pill-green  { background: var(--green-tint); color: var(--accent); }
.pill-red    { background: var(--red-tint);   color: var(--danger); }
.pill-amber  { background: var(--amber-tint); color: var(--warn); }
.pill-azure  { background: var(--azure-tint); color: var(--notify); }

/* Badge — a counted pill. Pops in gently when it appears (occasional). */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-2xs); font-weight: 700; font-variant-numeric: tabular-nums;
  padding: 2px 9px; border-radius: var(--r-pill);
  background: var(--brand-tint); color: var(--brand-strong);
  transition: transform var(--dur-enter) var(--ease-out), opacity var(--dur-enter) var(--ease-out);
  @starting-style { transform: scale(0.9); opacity: 0; }
}
.badge.pill-green  { background: var(--green-tint); color: var(--accent); }
.badge.pill-red    { background: var(--red-tint);   color: var(--danger); }
.badge.pill-amber  { background: var(--amber-tint); color: var(--warn); }
.badge.pill-azure  { background: var(--azure-tint); color: var(--notify); }
.badge .dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }

.chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-sm); font-weight: 650;
  padding: 6px var(--sp-3); border-radius: var(--r-pill);
  background: var(--surface); border: 1px solid var(--border-strong); cursor: pointer;
  min-height: var(--touch-min);
  transition: background var(--dur-press) ease, transform var(--dur-press) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .chip:hover { background: var(--surface-2); }
}
.chip:active { transform: scale(0.97); }

/* ---------- SEGMENTED CONTROL ----------
   iOS-style: a recessed track and one sliding thumb (positioned in app JS:
   width + translateX on an absolutely-positioned child; slide enabled after
   first paint via [data-ready]).

   EXEMPTION (documented, deliberate — the ONE sanctioned width transition
   in the system): the seg thumb transitions `width`. The transform/opacity
   law exists to avoid layout work; this element is absolutely positioned
   with ZERO layout dependents, so its width change lays out nothing but
   itself. scaleX here would visibly stretch the thumb's border-radius and
   shadow (a smeared box, not a sliding one) — worse craft for no measurable
   win. Do NOT copy this pattern anywhere else without adding an exemption
   block like this one; the sliding underline above proves the scaleX way. */
.seg {
  position: relative;
  display: inline-flex; gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 3px;
}
.seg-thumb {
  position: absolute; top: 3px; left: 0; height: calc(100% - 6px); width: 0;
  background: var(--surface); border-radius: calc(var(--r-sm) - 3px);
  box-shadow: 0 1px 3px rgba(43, 33, 20, .14), 0 0 0 1px rgba(43, 33, 20, .03);
  will-change: transform;
  pointer-events: none;
}
.seg[data-ready] .seg-thumb {
  transition: transform var(--dur-view) var(--ease-out), width var(--dur-view) var(--ease-out);
}
.seg button {
  position: relative; /* above the thumb */
  font: inherit; font-size: var(--fs-sm); font-weight: 650; color: var(--muted);
  padding: 6px var(--sp-4); border: 0; background: none; cursor: pointer;
  min-height: var(--touch-min); border-radius: calc(var(--r-sm) - 3px);
  transition: color var(--dur-press) ease;
}
.seg button.on { color: var(--text); font-weight: 700; }
.seg button:active:not(.on) { color: var(--text); }

/* ---------- LIST ROWS (questions, SOPs, lessons, ideas, search hits) ----------
   Rows enter with a short staggered rise (only the first screenful staggers —
   --i is capped at 8 in app JS). CSS animation is fine here: a list renders
   once per navigation, it is not a rapidly-retriggered surface. */
.q-list-card { padding: 0; overflow: hidden; }
.q-row {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); cursor: pointer;
  transition: background var(--dur-press) ease;
}
.q-row:active { background: var(--surface-2); }
@media (hover: hover) and (pointer: fine) {
  .q-row:hover { background: var(--surface-2); }
  .q-row:hover .q-row-arrow { transform: translateX(2px); color: var(--brand); }
}
.q-row + .q-row { border-top: 1px solid var(--border); }
.q-row .q-main { flex: 1 1 220px; min-width: 0; }
.q-row .q-text { font-weight: 650; overflow-wrap: anywhere; }
.q-row .q-meta { color: var(--muted); font-size: var(--fs-xs); margin-top: 2px; }
.q-row-arrow {
  flex: none; color: var(--border-strong); display: inline-flex;
  transition: transform var(--dur-press) var(--ease-out), color var(--dur-press) ease;
}
.q-row-arrow svg { width: 16px; height: 16px; }
.q-row.is-static { cursor: default; }
.q-row.is-static:active { background: none; }

.row-in {
  animation: row-in var(--dur-enter) var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}
@keyframes row-in {
  from { opacity: 0; transform: translateY(6px); }
}

/* ---------- TABLE-COUNT (list meta line) ---------- */
.table-count { font-size: var(--fs-xs); color: var(--muted); margin: var(--sp-2) 0; display: block; }

/* ---------- BANNERS ----------
   A standing fact about the record on screen. Enters with a soft rise. */
.banner {
  display: flex; align-items: center; gap: var(--sp-2);
  border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm); font-weight: 600; border: 1px solid;
  transition: opacity var(--dur-enter) var(--ease-out), transform var(--dur-enter) var(--ease-out);
  @starting-style { opacity: 0; transform: translateY(-4px); }
}
.banner-warn   { background: var(--amber-tint); border-color: color-mix(in srgb, var(--warn) 38%, transparent);   color: var(--warn); }
.banner-danger { background: var(--red-tint);   border-color: color-mix(in srgb, var(--danger) 38%, transparent); color: var(--danger); }
.banner-notify { background: var(--azure-tint); border-color: color-mix(in srgb, var(--notify) 38%, transparent); color: var(--notify); }
.banner .dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }

/* ---------- DIALOG → SHEET ----------
   One surface, two postures: a bottom sheet on phones (slides up on the
   drawer curve, dismisses back down the same path — spatial consistency),
   a centered dialog at ≥640px (scale 0.96 + fade; modals stay
   center-origin). Backdrop and panel use transitions, not keyframes, so a
   fast open/close retargets instead of restarting.
   JS LAW (grader-corrected): the close-fallback timer must be guarded — a
   `closing` flag plus a check that the timer still owns the dialog root —
   so dismissing dialog A can never wipe a dialog B opened moments later. */
.dialog-backdrop {
  position: fixed; inset: 0; z-index: var(--z-dialog);
  background: rgba(33, 25, 15, .42);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0;
  opacity: 1;
  transition: opacity var(--dur-view) var(--ease-out);
  @starting-style { opacity: 0; }
}
.dialog {
  background: var(--surface); width: 100%; max-width: 560px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-sheet);
  padding: var(--sp-5) var(--sp-4) calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  transform: translateY(0);
  transition: transform var(--dur-sheet) var(--ease-drawer);
  @starting-style { transform: translateY(100%); }
}
.dialog::before { /* sheet grabber */
  content: ""; display: block; width: 36px; height: 4px; margin: 0 auto var(--sp-4);
  border-radius: var(--r-pill); background: var(--border-strong);
}
.dialog-backdrop[data-closing] { opacity: 0; pointer-events: none; }
.dialog-backdrop[data-closing] .dialog {
  transform: translateY(100%);
  transition-duration: var(--dur-enter); /* exit a beat faster than enter */
}
.dialog-actions {
  display: flex; justify-content: flex-end; gap: var(--sp-2);
  margin-top: var(--sp-4); flex-wrap: wrap;
}
@media (min-width: 640px) {
  .dialog-backdrop { align-items: center; padding: var(--sp-4); }
  .dialog {
    max-width: 480px; border-radius: var(--r-lg);
    padding: var(--sp-5); box-shadow: var(--shadow-pop);
    transform: none;
    transition: transform var(--dur-view) var(--ease-out), opacity var(--dur-view) var(--ease-out);
    @starting-style { transform: scale(0.96) translateY(8px); opacity: 0; }
  }
  .dialog::before { display: none; }
  .dialog-backdrop[data-closing] .dialog {
    transform: scale(0.97) translateY(4px); opacity: 0;
    transition-duration: var(--dur-quick);
  }
}

/* ---------- TOASTS ----------
   Bottom-center, above everything. Enters from below (its dismiss path
   would be the same edge), exits faster than it entered. Transitions, not
   keyframes: a new toast replacing an old one never stutters. */
.toast {
  position: fixed; bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  left: 0; right: 0; margin-inline: auto; width: fit-content; /* centered without transform, so reduced-motion can zero transforms safely */
  z-index: var(--z-toast);
  display: flex; align-items: center; gap: var(--sp-2);
  background: #2a2116; color: #fff;
  font-size: var(--fs-sm); font-weight: 600;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
  box-shadow: var(--shadow-toast), 0 0 0 1px rgba(255,255,255,.06) inset;
  max-width: calc(100vw - 32px);
  transform: translateY(0) scale(1);
  opacity: 1;
  transition: transform var(--dur-enter) var(--ease-out), opacity var(--dur-enter) var(--ease-out);
  @starting-style { transform: translateY(12px) scale(0.97); opacity: 0; }
}
.toast[data-leaving] {
  transform: translateY(8px) scale(0.98); opacity: 0;
  transition-duration: var(--dur-quick);
}
.toast-icon {
  flex: none; width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff;
}
.toast-icon svg { width: 11px; height: 11px; }
.toast-danger { background: var(--danger); }
.toast .toast-x {
  font: inherit; background: none; border: 0; color: #fff; cursor: pointer;
  font-weight: 800; margin-left: var(--sp-1); padding: 0 var(--sp-1);
}

/* ---------- THE FOUR STATES ---------- */
.state-empty, .state-loading, .state-error {
  text-align: center; padding: var(--sp-6) var(--sp-4); color: var(--muted);
  background: var(--surface); border: 1px dashed var(--border-strong); border-radius: var(--r-md);
}
.state-empty {
  animation: state-in var(--dur-enter) var(--ease-out) backwards;
}
@keyframes state-in {
  from { opacity: 0; transform: translateY(4px); }
}
.state-error { color: var(--danger); border-color: var(--danger); border-style: solid; }

/* Skeletons mirror the real layout. The sheen is a transform-only sweep on
   an overlay — no background-position painting. */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--surface-2); border-radius: var(--r-sm); min-height: 16px;
}
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent 20%, rgba(255,255,255,.55) 50%, transparent 80%);
  transform: translateX(-100%);
  animation: sheen 1.3s linear infinite;
}
@keyframes sheen { to { transform: translateX(100%); } }

/* ---------- VIEW ENTER ----------
   One short settle when the route changes (set in app JS on navigation
   only — never on in-place re-renders). Tabs are a tens-a-day surface, so
   this stays nearly imperceptible: 4px, 200ms. */
.view-enter { animation: view-in var(--dur-view) var(--ease-out); }
@keyframes view-in {
  from { opacity: 0; transform: translateY(4px); }
}

/* ---------- PRINT ---------- */
@media print {
  .chrome, .toast, .no-print, .state-loading { display: none !important; }
  body { background: #fff; }
  .card { box-shadow: none; }
  a { text-decoration: none; color: inherit; }
}

/* ---------- REDUCED MOTION ----------
   Movement collapses; opacity feedback stays legible. JS-driven motion
   (FLIP, WAAPI) checks the same media query before animating.
   GRADER FIX (a): delays are zeroed alongside durations — a zeroed
   duration with a live animation-delay left staggered rows INVISIBLE for
   their whole delay, then popping, for exactly the users who asked for
   less motion. Both pairs stay zeroed, permanently. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  .skeleton::after { animation: none; }
  .dialog, .toast, .banner, .badge {
    transition: opacity var(--dur-view) ease !important;
    transform: none !important;
  }
  .dialog-backdrop { transition: opacity var(--dur-view) ease !important; }
}

/* ============================================================================
   CHROME — THE WAY HOME, THE ACCOUNT MENU, AND THE BANNER ROW'S CONTROL
   ============================================================================
   Added 2026-09-21 (P0 round 2, defect 4). chrome.js has always printed these
   class names and this stylesheet said nothing about them, which is not a
   cosmetic miss: <details> renders its sheet in the document flow, so an
   unstyled Account menu drops Sign out wherever the paragraph happens to
   fall — the Bama Dirt scar exactly (a Sign out pushed off a 390px screen
   through three reviews that all read the styles and never looked at a page).

   THEY LIVE HERE, IN THE SHARED PLACE, AND NEVER IN AN APP. HQ's ruling: a
   per-app copy is how one bug lived in five Bama Dirt apps at once.

   WHERE THE RULES COME FROM:
     · .way-home / .way-home-chev / .way-home-tile — VERBATIM from HQ's
       canonical `sop-library\ui-contract-v2\way-home.html` (Bama Dirt's own
       copy, read-only). Do not restyle, relabel, move or re-space them.
     · .who-btn, .who inside it, .muted-line — from Bama Dirt's
       `front-door\public\app.css`, the most complete copy on the machine.
       ONE selector is mapped: that file styles `#who` (an id), the Lambs
       chrome renders `class="who"`, so the rule is `.who-btn .who` here.
     · .account, .account-sheet, .who-caret, .menu-line, .menu-item,
       .menu-item-danger, .menu-form, .banner-form — NO canonical Bama Dirt
       copy exists (grepped: nothing on this machine defines .account-sheet or
       .menu-item). They are written here from the tokens above and the
       shapes already in this file (.card's surface, .btn's target, the
       dialog's shadow). NO NEW COLOUR LITERAL: every value is a token. */

/* ---------- THE WAY HOME (canonical, verbatim) ---------- */
.way-home { display:inline-flex; align-items:center; gap:4px; flex:none;
            min-height:44px; padding:0 6px 0 2px; margin-left:-6px;
            color:var(--muted); text-decoration:none;
            -webkit-tap-highlight-color:transparent; }
.way-home:active { opacity:.6; }
.way-home-chev { width:18px; height:18px; display:block; }
.way-home-tile { width:34px; height:34px; border-radius:9px;
                 overflow:hidden; display:block; }
.way-home-tile svg { width:100%; height:100%; display:block; }

/* ---------- THE ACCOUNT MENU (PLACEMENT §2) ----------
   Top-right, always, and it is the ONE thing in the header that never gives
   way. The sheet is a <details>: a real, keyboard-operable control with no
   JavaScript at all, so the menu — and Sign out inside it — still opens on a
   cold start in a dead zone. It is positioned OUT of the flow so opening it
   never pushes the screen down. */
.account { position: relative; margin-left: auto; flex: none; }
.account > summary { list-style: none; cursor: pointer; }
.account > summary::-webkit-details-marker { display: none; }

.who-btn {
  flex: none;
  font-size: var(--fs-sm); font-weight: 650; color: var(--muted);
  padding: 6px var(--sp-3); min-height: 36px;
  max-width: 46vw; overflow: hidden;
}
/* The front door's `#who` rule, mapped to the class the Lambs chrome prints.
   `margin-left: 0` undoes `.topbar .who`'s push-right, which belongs to the
   bare name in an app that has no menu — inside the button it would shove the
   name off its own caret. */
.who-btn .who { margin-left: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-caret { width: 16px; height: 16px; display: block; flex: none; }

/* ---------- .muted-line — A QUIET LINE OF EXPLANATION ----------
   The header above has NAMED this class since P0 round 2 and nothing ever
   defined it, so the signed-out page's one sentence rendered as plain body
   text at full weight and full contrast, shouting exactly as loudly as the
   heading directly above it. The class-list check missed it because its
   matcher searched the whole file and the header's own prose satisfied it;
   that hole is closed in css-in-step.test.mjs.

   It is the quiet line under a heading: the muted ink, a notch down in size,
   and room above it. NO NEW COLOUR LITERAL — --muted is the token this
   stylesheet already uses wherever something is deliberately secondary. */
.muted-line {
  margin: var(--sp-2) 0 0;
  color: var(--muted); font-size: var(--fs-sm); line-height: 1.5;
}

.account-sheet {
  position: absolute; right: 0; top: calc(100% + var(--sp-1));
  z-index: var(--z-pop);
  min-width: 224px; max-width: calc(100vw - 32px);
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-pop);
  padding: var(--sp-2);
}

/* A LINE, NOT A BUTTON (PLACEMENT §2): "Signed in as <first name>". */
.menu-line {
  margin: 0 0 var(--sp-1); padding: var(--sp-2) var(--sp-3);
  color: var(--muted); font-size: var(--fs-xs);
}

/* Every item is a 44px glove target, whatever tag it is — an <a> for a screen
   and a <button> for Sign out must not feel like two different controls. */
.menu-item {
  display: flex; align-items: center; width: 100%;
  min-height: var(--touch-min); padding: 0 var(--sp-3);
  border: 0; border-radius: var(--r-sm); background: none;
  font: inherit; font-size: var(--fs-base); font-weight: 650; text-align: left;
  color: var(--text); text-decoration: none; cursor: pointer;
  transition: background var(--dur-press) ease;
}
.menu-item:active { background: var(--surface-2); }
@media (hover: hover) and (pointer: fine) {
  .menu-item:hover { background: var(--surface-2); }
}
/* Sign out — ALWAYS LAST, and it reads as the destructive one. */
.menu-item-danger { color: var(--danger); font-weight: 700; }
.menu-form { margin: 0; display: block; }

/* ---------- THE BANNER ROW'S ONE CONTROL ----------
   The new-version bar's `Refresh` button sits at the END of the row, away
   from the sentence, so a thumb reaching for it never lands on the words. */
.banner-form { margin: 0 0 0 auto; display: flex; flex: none; }
