/* ============================================================================
   app.css - MIL app-specific components, built on ClayKit (framework.css)
   tokens. Only things the framework doesn't ship live here: the workflow
   node-graph editor, its inspector, the auth screen, and a couple of dashboard
   compositions. Never redefines framework primitives (.btn/.card/.badge/…).
   ========================================================================== */

/* ---- One extra semantic tone: the agent/review violet (single allowed) --- */
:root { --review: #6D4AAD; --review-soft: #F1ECF9; }
.badge-review { background: var(--review-soft); color: var(--review); }

/* ---- Status health pills (app tones + the living pulse) ------------------ */
/* Extra .health tones beyond the framework's pos/warn/neg, from tokens only. */
.health.info { color: var(--info); }
.health.review { color: var(--review); }
.health .dot { width: 7px; height: 7px; }
/* A slow breathe on pulse icons so live/active states feel alive. Killed by
   the appearance panel's motion toggle ([data-motion=off]) and reduced-motion. */
@keyframes ck-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
.health .ph-pulse, .status .dot-pos { animation: ck-breathe 2.6s ease-in-out infinite; }
.health.accent { color: var(--accent-ink); }

/* Step type on a workflow node: a quiet mono eyebrow, not a filled chip. */
.wf-type { font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3); }
.wf-type.review { color: var(--review); }

/* ---- Motion: entrance choreography ---------------------------------------
   One cascade per completed navigation. The animation ONLY runs while <html>
   carries .ck-enter, which motion.js adds after Turbo finishes rendering the
   final page (turbo:load) and removes ~1s later. Cached snapshots and previews
   therefore always render settled - CSS can't tell a preview/restore render
   from a fresh one, so the trigger has to be event-driven, not selector-driven
   (a [data-turbo-preview] guard still double-fired on revisits).
   Killed by [data-motion=off] (!important) and reduced-motion (motion.js). */
@keyframes rise-in { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
html.ck-enter .page-head, html.ck-enter .statstrip > div, html.ck-enter .qlist > *,
html.ck-enter .card, html.ck-enter .wf-node-row, html.ck-enter .wf-inspector {
  animation: rise-in .42s cubic-bezier(.22, .9, .32, 1) backwards;
}
.statstrip > div:nth-child(2), .qlist > *:nth-child(2), .wf-node-row:nth-child(2) { animation-delay: .05s; }
.statstrip > div:nth-child(3), .qlist > *:nth-child(3), .wf-node-row:nth-child(3) { animation-delay: .10s; }
.statstrip > div:nth-child(4), .qlist > *:nth-child(4), .wf-node-row:nth-child(4) { animation-delay: .15s; }
.statstrip > div:nth-child(5), .qlist > *:nth-child(5), .wf-node-row:nth-child(5) { animation-delay: .20s; }
.statstrip > div:nth-child(6), .qlist > *:nth-child(6), .wf-node-row:nth-child(6) { animation-delay: .25s; }
.qlist > *:nth-child(7), .wf-node-row:nth-child(7) { animation-delay: .30s; }
.qlist > *:nth-child(8), .wf-node-row:nth-child(8) { animation-delay: .35s; }
.qlist > *:nth-child(n+9), .wf-node-row:nth-child(n+9) { animation-delay: .40s; }
.wf-inspector { animation-delay: .18s; }
.card { animation-delay: .08s; }

@media (prefers-reduced-motion: reduce) {
  .health .ph-pulse, .status .dot-pos, .page-head, .statstrip > div,
  .qlist > *, .card, .wf-node-row, .wf-inspector, .sys-tile,
  .cap-card.is-saved { animation: none; }
}

/* Dashboard KPI tiles are buttons (each opens the list behind its number) -
   mirror the framework's .statstrip > div look on the button element. */
.statstrip > .dash-stat { padding: 1px 28px; border-left: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  background: none; border-top: none; border-right: none; border-bottom: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer; }
.statstrip > .dash-stat:first-child { padding-left: 0; border-left: none; }
.statstrip > .dash-stat:hover .ss-label { color: var(--ink); }
.statstrip > .dash-stat .ph-caret-down { opacity: 0; transition: opacity .15s var(--ease); }
.statstrip > .dash-stat:hover .ph-caret-down { opacity: 1; }

/* Grayed-out nav for roles that can't go there (visible, honest, locked). */
.nav-item.is-disabled { opacity: .38; cursor: not-allowed; }
.nav-item.is-disabled:hover { background: none; }

/* The platform assistant's floating presence - every screen, bottom right.
   A quiet icon-only circle; the violet glyph is the agent identity. */
.assistant-btn { position: fixed; right: 22px; bottom: 22px; z-index: 1100;
  width: 46px; height: 46px; border-radius: 999px; cursor: pointer;
  display: grid; place-items: center; padding: 0;
  background: var(--surface); color: var(--review);
  border: 1px solid var(--line-strong); box-shadow: var(--shadow-lg);
  transition: transform var(--fast), border-color var(--fast), box-shadow var(--fast); }
.assistant-btn:hover { transform: translateY(-1.5px);
  border-color: color-mix(in srgb, var(--review) 45%, var(--line-strong)); }
.assistant-btn:active { transform: none; }
.assistant-btn .ph-duotone { font-size: 22px; }
/* The launcher yields while ANY drawer or popover is open (it sat on the
   composer, and the nudge floated over the system picker). */
body:has(.drawer.open) .assistant-btn, body:has(.sys-pop.open) .assistant-btn { opacity: 0; pointer-events: none; }

/* The ambient nudge: this screen's agent move, POPPING OUT of the launcher.
   The animation lives on the element itself (fresh DOM every render, so it
   plays on every page entry - no dependence on the ck-enter window); a springy
   overshoot from the button's corner sells the "out of the button" read.
   Previews stay settled; [data-motion=off] and reduced-motion kill it. */
/* The floating "On this page" nudge is retired (no ambient popups,
   2026-08-26): its to-dos live in the assistant drawer instead. */
/* ---- The simulation theater: a quote token travels the track while each
   station lights as it's reached - the run as a mini movie. Timing derives
   from --steps (1.2s per station); Replay re-adds .sim-anim. -------------- */
.simviz { position: relative; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); padding: 26px 28px 18px; margin-bottom: 20px; overflow: hidden; }
.simviz-track { position: relative; height: 3px; background: var(--line);
  border-radius: 99px; margin: 0 26px 18px; }
.simviz-progress { position: absolute; inset: 0 auto 0 0; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #fff)); }
.simviz.sim-anim .simviz-progress { animation: simviz-fill calc(var(--steps) * 1.2s) linear forwards .3s; }
@keyframes simviz-fill { to { width: 100%; } }
.simviz-token { position: absolute; top: 50%; left: 0; transform: translate(-50%, -50%);
  width: 26px; height: 26px; border-radius: 99px; display: grid; place-items: center;
  background: var(--accent); color: #fff; font-size: 13px; box-shadow: var(--shadow);
  z-index: 2; }
.simviz.sim-anim .simviz-token { animation: simviz-ride calc(var(--steps) * 1.2s) linear forwards .3s; }
@keyframes simviz-ride { to { left: 100%; } }
.simviz-nodes { display: flex; justify-content: space-between; gap: 8px; }
.simviz-node { position: relative; display: flex; flex-direction: column; align-items: center;
  gap: 6px; flex: 1; min-width: 0; text-align: center; }
.simviz-dot { width: 34px; height: 34px; border-radius: 99px; display: grid; place-items: center;
  font-size: 16px; color: var(--ink-3); background: var(--surface);
  border: 1.5px solid var(--line-strong); }
.simviz-name { font-size: 11px; color: var(--ink-3); max-width: 110px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.simviz-zap { position: absolute; top: -7px; right: calc(50% - 26px); font-size: 11px;
  color: var(--accent); opacity: 0; }
/* Each station wakes exactly when the token arrives. */
.simviz.sim-anim .simviz-dot { animation: simviz-arrive .5s var(--ease) forwards;
  animation-delay: calc(var(--i) * 1.2s + .3s); }
@keyframes simviz-arrive { 40% { transform: scale(1.18); }
  100% { color: var(--accent); border-color: var(--accent);
         box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 14%, transparent); } }
.simviz.sim-anim .simviz-name { animation: simviz-label .4s var(--ease) forwards;
  animation-delay: calc(var(--i) * 1.2s + .45s); }
@keyframes simviz-label { to { color: var(--ink); font-weight: 550; } }
.simviz.sim-anim .simviz-zap { animation: simviz-spark .6s var(--ease) forwards;
  animation-delay: calc(var(--i) * 1.2s + .7s); }
@keyframes simviz-spark { 0% { opacity: 0; transform: translateY(4px) scale(.6); }
  50% { opacity: 1; transform: translateY(-2px) scale(1.25); }
  100% { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .simviz .simviz-progress { width: 100%; animation: none; }
  .simviz .simviz-token { left: 100%; animation: none; }
  .simviz .simviz-dot, .simviz .simviz-name, .simviz .simviz-zap { animation: none; opacity: 1; }
}

/* ---- Test run playback: the simulated quote WALKS the steps - each row
   wakes in sequence, its dot flaring as the run reaches it. Replay re-adds
   .sim-anim. Killed by data-motion=off / reduced-motion like everything. */
@keyframes sim-wake { from { opacity: .22; transform: translateX(-6px); }
  to { opacity: 1; transform: none; } }
@keyframes sim-flare { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }
  100% { box-shadow: 0 0 0 12px transparent; } }
.sim-anim .sim-row { opacity: .22; animation: sim-wake .5s var(--ease) forwards; }
.sim-anim .sim-row .sim-dot { animation: sim-flare .9s var(--ease) 1; animation-delay: inherit; }
.sim-anim .sim-row:nth-child(1) { animation-delay: .2s; }
.sim-anim .sim-row:nth-child(2) { animation-delay: 1.1s; }
.sim-anim .sim-row:nth-child(3) { animation-delay: 2.0s; }
.sim-anim .sim-row:nth-child(4) { animation-delay: 2.9s; }
.sim-anim .sim-row:nth-child(5) { animation-delay: 3.8s; }
.sim-anim .sim-row:nth-child(6) { animation-delay: 4.7s; }
.sim-anim .sim-row:nth-child(7) { animation-delay: 5.6s; }
.sim-anim .sim-row:nth-child(n+8) { animation-delay: 6.5s; }
@media (prefers-reduced-motion: reduce) { .sim-anim .sim-row { animation: none; opacity: 1; } }

/* ---- Connected-system mini viewers (the job as Monday/CompanyCam see it) - */
.mv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; max-width: 760px; }
.mv-card { display: flex; flex-direction: column; gap: 9px; border: 1px solid var(--line);
  border-radius: var(--r); background: var(--surface); padding: 12px 14px; }
.mv-live { display: inline-block; width: 6px; height: 6px; border-radius: 99px;
  background: var(--pos); margin-right: 6px; animation: ck-breathe 2.2s ease-in-out infinite; }
/* Their look: a filled colored status label. */
.mv-status { flex: none; font-size: 11px; font-weight: 600; color: #fff;
  padding: 3px 10px; border-radius: 5px; }
.mv-status.is-good { background: #00A96C; }
.mv-status.is-wip  { background: #E8A33D; }
.mv-status.is-bad  { background: #D83A52; }
.mv-status.is-info { background: #579BFC; }
.mv-update { display: flex; flex-direction: column; gap: 1px; padding-top: 7px;
  border-top: 1px solid var(--line); }
.mv-photos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.mv-photos img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 6px; }
/* The card PLAYS when it arrives: status pops, updates land one after
   another, photos deal in like a hand of cards - a mini movie of the system. */
.mv-status { animation: mv-pop .5s var(--ease) .45s backwards; }
@keyframes mv-pop { 0% { opacity: 0; transform: scale(.6); }
  60% { transform: scale(1.12); } 100% { opacity: 1; transform: none; } }
.mv-update { animation: rise-in .45s var(--ease) backwards; }
.mv-update:nth-child(1) { animation-delay: .7s; }
.mv-update:nth-child(2) { animation-delay: 1.05s; }
.mv-update:nth-child(3) { animation-delay: 1.4s; }
.mv-update:nth-child(n+4) { animation-delay: 1.75s; }
.mv-photos a { animation: mv-deal .5s var(--ease) backwards; }
.mv-photos a:nth-child(1) { animation-delay: .45s; } .mv-photos a:nth-child(2) { animation-delay: .6s; }
.mv-photos a:nth-child(3) { animation-delay: .75s; } .mv-photos a:nth-child(4) { animation-delay: .9s; }
.mv-photos a:nth-child(5) { animation-delay: 1.05s; } .mv-photos a:nth-child(n+6) { animation-delay: 1.2s; }
@keyframes mv-deal { from { opacity: 0; transform: translateY(8px) rotate(-2deg) scale(.9); }
  to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .mv-status, .mv-update, .mv-photos a { animation: none; }
}

/* Tap-to-talk: the mic button breathes red while listening. */
[data-controller="voicenote"].is-recording .btn { color: var(--neg); border-color: var(--neg); }
[data-controller="voicenote"].is-recording .ph-microphone { animation: ck-breathe 1.6s ease-in-out infinite; }
/* The live transcript bubble: what the mic hears, as you speak. */
.voice-live { position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 8px);
  z-index: 1200; max-width: min(560px, calc(100vw - 40px));
  background: var(--ink); color: #fff; font-size: 13px; line-height: 1.5;
  padding: 10px 16px; border-radius: 12px; box-shadow: var(--shadow-lg);
  opacity: 0; transition: opacity .2s var(--ease), transform .25s var(--ease); }
.voice-live.open { opacity: 1; transform: translate(-50%, 0); }

/* Thinking state while the model answers (Turbo marks the frame busy). */
#assistant_answer[busy] > * { opacity: .25; }
#assistant_answer[busy]::before { content: "Thinking"; display: inline-flex; align-items: center;
  gap: 6px; font-size: 12.5px; color: var(--review); font-weight: 540;
  animation: ck-think 1.2s ease-in-out infinite; }
@keyframes ck-think { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }

/* The workflow library hero: a glanceable mini-flow on the dotted canvas. */
.wf-hero-board { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 24px 20px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background-color: var(--surface-2);
  background-image: radial-gradient(color-mix(in srgb, var(--ink) 12%, transparent) 1px, transparent 1px);
  background-size: 22px 22px; }
.wf-hero-node { display: inline-flex; align-items: center; gap: 8px; padding: 9px 13px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--shadow-sm); font-size: 13px; font-weight: 540; white-space: nowrap; }
.wf-hero-node .ph-duotone { font-size: 16px; color: var(--accent); }
.wf-hero-link { width: 24px; height: 2px; background: var(--line-strong); border-radius: 2px; flex-shrink: 0; }

/* The Test run timeline: a visual walk, not a wall of text. */
.sim { display: flex; flex-direction: column; }
.sim-row { display: flex; gap: 16px; }
.sim-rail { position: relative; width: 36px; flex-shrink: 0; display: flex; justify-content: center; }
.sim-rail::before { content: ""; position: absolute; top: 0; bottom: 0; left: 50%;
  width: 2px; transform: translateX(-50%); background: var(--line); }
.sim-row:first-child .sim-rail::before { top: 18px; }
.sim-row:last-child .sim-rail::before { bottom: auto; height: 18px; }
.sim-dot { position: relative; z-index: 1; width: 36px; height: 36px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--line-strong); box-shadow: var(--shadow-sm);
  display: flex; align-items: center; justify-content: center; }
.sim-dot .ph-duotone { font-size: 17px; color: var(--accent); }
.sim-body { padding: 6px 0 26px; display: flex; flex-direction: column; gap: 6px; min-width: 0; }

/* ---- Workflow editor: split ---------------------------------------------- */
.wf-editor { display: grid; grid-template-columns: minmax(0,1fr) 348px; gap: 26px; align-items: start; }

/* ── The flow board: dotted canvas, pan/zoom world, draggable nodes, SVG edges ── */
.wfb { position: relative; height: 640px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background-color: var(--surface-2);
  background-image: radial-gradient(color-mix(in srgb, var(--ink) 13%, transparent) 1px, transparent 1px);
  background-size: 22px 22px; overflow: hidden; cursor: grab; user-select: none; touch-action: none; }
.wfb.is-panning { cursor: grabbing; }
.wfb-world { position: absolute; inset: 0; transform-origin: 0 0; will-change: transform; }
.wfb-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.wfb-edge { fill: none; stroke: var(--line-strong); stroke-width: 1.75; }
.wfb-edge-dot { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.5; }
.wfb-node { position: absolute; width: 330px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 13px 14px; box-shadow: var(--shadow-sm); cursor: grab; }
.wfb-node:hover { border-color: var(--line-strong); box-shadow: var(--shadow); }
.wfb-node:active { cursor: grabbing; }
.wfb-node.is-selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), var(--shadow); }
.wfb-node:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.wfb-branch { width: 250px; padding: 10px 12px; background: var(--surface-2);
  border-style: dashed; box-shadow: none; cursor: default; }
.wfb-add { width: 330px; background: color-mix(in srgb, var(--surface) 72%, transparent);
  border-style: dashed; border-color: var(--line-strong); box-shadow: none; cursor: default; }
.wfb-tools { position: absolute; right: 14px; bottom: 14px; display: flex; gap: 6px;
  padding: 5px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow-sm); }
.wfb-hint { position: absolute; right: 14px; top: 16px; font-size: 11.5px; color: var(--ink-3);
  background: color-mix(in srgb, var(--surface) 85%, transparent); padding: 3px 8px; border-radius: var(--r-sm); }
.wf-editor.is-full .wfb-hint { right: 352px; }
.wfb-auto { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }

/* The toolkit: a floating Add pill that unfolds a palette of pluggable pieces. */
/* The picker's type cards: icon, name, one-line meaning; picked = accent ring. */
.wfb-kit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.wfb-kit-card { position: relative; display: grid; grid-template-columns: 26px 1fr;
  grid-template-rows: auto auto; column-gap: 9px; row-gap: 3px; align-items: center;
  padding: 12px 13px; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r); cursor: pointer; overflow: hidden;
  transition: border-color .15s var(--ease), transform .15s var(--ease), box-shadow .15s var(--ease); }
.wfb-kit-card:hover { transform: translateY(-1px); border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
.wfb-kit-card:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: var(--surface); }
.wfb-kit-card input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; }
.wfb-kit-card .ph-duotone { font-size: 20px; color: var(--accent); grid-row: 1 / 3; align-self: start; margin-top: 1px; }
.wfb-kit-card .text-sm { line-height: 1.2; }
.wfb-kit-card .text-xs { grid-column: 2; line-height: 1.4; }

/* The Test run auto-plays: steps light up in sequence like an execution. */
html.ck-enter .sim-row { animation: rise-in .5s cubic-bezier(.22, .9, .32, 1) backwards; }
html.ck-enter .sim-row:nth-child(2) { animation-delay: .35s; }
html.ck-enter .sim-row:nth-child(3) { animation-delay: .7s; }
html.ck-enter .sim-row:nth-child(4) { animation-delay: 1.05s; }
html.ck-enter .sim-row:nth-child(5) { animation-delay: 1.4s; }
html.ck-enter .sim-row:nth-child(6) { animation-delay: 1.75s; }
html.ck-enter .sim-row:nth-child(n+7) { animation-delay: 2.1s; }
@media (prefers-reduced-motion: reduce) { .sim-row { animation: none !important; } }

/* The builder's in-room agent suggestions collapse out of the way. */
.wf-agent-details > summary { list-style: none; cursor: pointer; display: inline-flex;
  align-items: center; gap: 7px; font-size: 12.5px; color: var(--review); font-weight: 540;
  padding: 4px 0; margin-bottom: 8px; }
.wf-agent-details > summary::-webkit-details-marker { display: none; }
.wf-agent-details > summary .ph-caret-right { transition: transform .18s var(--ease); font-size: 12px; }
.wf-agent-details[open] > summary .ph-caret-right { transform: rotate(90deg); }

.wfb-kit { position: absolute; left: 50%; top: 50%; width: 420px; max-width: calc(100% - 24px);
  transform: translate(-50%, -46%) scale(.95); transform-origin: 50% 50%;
  opacity: 0; pointer-events: none; visibility: hidden;
  transition: transform .28s var(--ease), opacity .22s var(--ease), visibility 0s .28s;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg); padding: 16px; z-index: 70; cursor: default; }
.wfb-kit.is-open { transform: translate(-50%, -50%) scale(1); opacity: 1;
  pointer-events: auto; visibility: visible; transition: transform .28s var(--ease), opacity .22s var(--ease); }
/* The dial: the four step types fan out around the Add pill on a springy arc
   (game-dial feel; --a is each item's polar angle). */

/* Fullscreen: the board pins to the viewport (below the topbar) so the tools,
   Add pill, and collapse control are ALWAYS in view; the inspector floats over
   it as a compact glassy panel - the nodes are the focus. Esc exits. */
.wf-editor.is-full .wfb { position: fixed; top: var(--topbar-h); left: 0; right: 0; bottom: 0;
  height: auto; z-index: 60; border-radius: 0; border-width: 1px 0 0 0; }
.wf-editor.is-full .wf-inspector { position: fixed; top: calc(var(--topbar-h) + 14px); right: 14px;
  width: 324px; max-height: calc(100vh - var(--topbar-h) - 28px); overflow-y: auto; z-index: 61; margin: 0;
  background: color-mix(in srgb, var(--surface) 84%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--shadow-lg); border-color: color-mix(in srgb, var(--line) 70%, transparent);
  font-size: 13px; }
.wf-editor.is-full .wf-inspector .h2 { font-size: 17px; }
/* Keep the board tools clear of the floating inspector. */
.wf-editor.is-full .wfb-tools { right: 352px; }

/* Connected step nodes down a hairline rail with dots. */
.wf-canvas { display: flex; flex-direction: column; }
.wf-node-row { display: flex; gap: 14px; }
.wf-rail { position: relative; width: 12px; flex-shrink: 0; }
.wf-rail::before { content: ""; position: absolute; left: 50%; transform: translateX(-50%);
  top: 0; bottom: 0; width: 2px; background: var(--line-strong); }
.wf-node-row:first-child .wf-rail::before { top: 18px; }
.wf-node-row:last-child .wf-rail::before { height: 18px; bottom: auto; }
.wf-dot { position: absolute; left: 50%; top: 12px; transform: translateX(-50%);
  width: 11px; height: 11px; border-radius: 999px; background: var(--surface);
  border: 2px solid var(--line-strong); z-index: 1; }
.wf-dot.is-active { background: var(--accent); border-color: var(--accent); }

.wf-node-wrap { flex: 1; min-width: 0; padding-bottom: 14px; }
.wf-node { display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 12px 14px; transition: border-color var(--fast), box-shadow var(--fast); }
.wf-node:hover { border-color: var(--line-strong); }
.wf-node.is-selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.wf-node-top { display: flex; align-items: center; gap: 10px; }
.wf-pos { font-family: var(--mono); font-size: 12px; color: var(--ink-3); width: 16px; text-align: center; }
.wf-name { font-size: 14px; font-weight: 560; color: var(--ink); flex: 1; letter-spacing: -.01em; }
.wf-node-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }

.mini-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 2px 8px; }
.mini-chip .ph { font-size: 13px; }
.mini-chip b { font-weight: 520; color: var(--ink); }
/* Agent chips read like every other chip - hairline, quiet - the violet lives
   in the ICON only (no filled pill; filled soft-violet read as off-framework). */
.mini-agent { color: var(--ink-2); background: var(--surface); border-color: var(--line); }
.mini-agent .ph { color: var(--review); }

/* Branch offshoot (dashed = secondary path). */
.wf-branch { display: flex; margin: 10px 0 2px 4px; }
.wf-elbow { width: 20px; border-left: 2px solid var(--line-strong); border-bottom: 2px solid var(--line-strong);
  border-bottom-left-radius: 9px; margin-bottom: 15px; flex-shrink: 0; }
.wf-branch-node { flex: 1; display: flex; gap: 9px; align-items: flex-start;
  background: var(--surface-2); border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm); padding: 9px 12px; }
.wf-branch-node .wf-name { font-size: 13px; }

/* ---- Live tables: hover-revealed affordances (inspo1's feel, not its
   filled chips) - the row answers "this is clickable" the moment you touch
   it: an accent arrow slides in and the row key warms to accent. */
.qlist-live .qrow { position: relative; }
.qlist-live .qrow > div:first-child { transition: color var(--fast); }
.qlist-live .qrow:hover > div:first-child { color: var(--accent); }
.qlist-live .qrow::after { content: "→"; position: absolute; right: 13px; top: 50%;
  transform: translate(-5px, -50%); opacity: 0; color: var(--accent); font-size: 14px;
  transition: opacity var(--fast), transform var(--fast); }
.qlist-live .qrow:hover::after { opacity: 1; transform: translate(0, -50%); }

/* ---- Event timeline (quote history): icon rail + quiet rows; rows carrying
   a diff/context expand into labeled key-value detail (inspo2 + inspo4). */
.tl { display: flex; flex-direction: column; }
.tl-item { position: relative; }
.tl-item::before { content: ""; position: absolute; left: 19px; top: 0; bottom: 0;
  width: 1.5px; background: var(--line); }
.tl-item:first-child::before { top: 14px; }
.tl-item:last-child::before { height: 14px; bottom: auto; }
.tl-row { display: flex; align-items: center; gap: 11px; padding: 7px 6px;
  border-radius: var(--r-sm); list-style: none; transition: background var(--fast); }
.tl-row::-webkit-details-marker { display: none; }
details .tl-row { cursor: pointer; }
details .tl-row:hover { background: var(--hover); }
.tl-dot { width: 27px; height: 27px; border-radius: 999px; flex: none; z-index: 1;
  display: grid; place-items: center; font-size: 13px; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line-strong); }
.tl-text { font-size: 13px; color: var(--ink); font-weight: 490; min-width: 0; }
.tl-meta { margin-left: auto; font-size: 11.5px; color: var(--ink-3); white-space: nowrap; }
.tl-caret { font-size: 12px; color: var(--ink-4); flex: none; transition: transform var(--fast); }
details[open] .tl-caret { transform: rotate(90deg); }
.tl-detail { margin: 2px 0 10px 44px; padding: 10px 12px; display: flex;
  flex-direction: column; gap: 7px; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface-2); }
.tl-kv { display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  font-size: 12px; }
.tl-kv .k { color: var(--ink-3); flex: none; }
.tl-kv .v { color: var(--ink); text-align: right; overflow-wrap: anywhere; }

/* ---- Workflow library (template cards + instance door) ------------------- */
.wft-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.wft-card { display: flex; flex-direction: column; gap: 13px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 15px; transition: border-color var(--fast), box-shadow var(--fast); }
.wft-card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
/* Naked duotone glyph - no filled tile (the framework's field note: filled
   icon squares read as generic-dashboard). */
.wft-icon { flex: none; display: grid; place-items: center;
  font-size: 25px; color: var(--accent); }
.wft-card .wft-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; min-width: 0; }
.wft-count { display: flex; align-items: center; gap: 8px; font-size: 12.5px;
  color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 7px 10px; transition: background var(--fast), border-color var(--fast), color var(--fast); }
.wft-count:hover { background: var(--hover); border-color: var(--line-strong); color: var(--ink); }
.wft-count .ph { font-size: 14px; }

/* ---- System-of-record picker (the binding modal's app tiles) ------------- */
.sys-note { display: flex; align-items: flex-start; gap: 8px; font-size: 12px;
  color: var(--ink-2); padding: 9px 11px; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface-2); }
.sys-note .ph { font-size: 15px; color: var(--accent); flex: none; margin-top: 1px; }
/* A compact popover springing FROM the button - no scrim, no screen takeover.
   Fixed-positioned by JS next to the trigger; the body scrolls inside. */
.sys-pop { position: fixed; z-index: 1200; width: 330px; display: flex; flex-direction: column;
  max-height: min(540px, calc(100vh - 24px));
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  opacity: 0; pointer-events: none; visibility: hidden;
  transform: scale(.9) translate(8px, -6px); transform-origin: top right;
  transition: opacity .16s var(--ease), transform .24s cubic-bezier(.3, 1.3, .45, 1), visibility .16s; }
.sys-pop.open { opacity: 1; pointer-events: auto; visibility: visible; transform: none; }
.sys-pop-head { display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; border-bottom: 1px solid var(--line); flex: none; }
.sys-pop-body { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 10px; }
.sys-pop-foot { padding: 10px 14px; border-top: 1px solid var(--line); flex: none; }
.sys-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 2px; }
/* Inside the popover: pills WRAP (nothing cut off), and inputs go filled -
   quiet surface-2 fills that sharpen on focus, no boxy default chrome. */
.sys-pop .pillbar { flex-wrap: wrap; overflow: visible; margin: 0; padding: 0; }
.sys-pop .search-slim { height: 32px; border-color: transparent; background: var(--surface-2); }
.sys-pop .search-slim:focus-within { background: var(--surface); border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
.sys-pop .input { background: var(--surface-2); border-color: transparent; border-radius: 9px; }
.sys-pop .input:focus { background: var(--surface); border-color: var(--line-strong); box-shadow: none; }
@keyframes sys-tile-in { from { opacity: 0; transform: scale(.86) translateY(5px); }
  to { opacity: 1; transform: none; } }
.sys-tile { position: relative; display: flex; flex-direction: column; align-items: center;
  gap: 6px; padding: 13px 8px 11px; text-align: center; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface); cursor: pointer;
  animation: sys-tile-in .3s cubic-bezier(.25, 1, .4, 1) backwards;
  transition: border-color var(--fast), box-shadow var(--fast), background var(--fast); }
.sys-tile.is-hidden { display: none; }
.sys-tile .ph-duotone { font-size: 22px; color: var(--ink-2); }
.sys-tile:hover { border-color: var(--line-strong); background: var(--hover); }
.sys-tile input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.sys-tile:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.sys-tile:has(input:checked) .ph-duotone { color: var(--accent); }
.sys-tile.is-locked { opacity: .45; cursor: not-allowed; }
.sys-tile.is-locked:hover { border-color: var(--line); background: var(--surface); }
.sys-lock { position: absolute; top: 6px; right: 7px; font-size: 11px; color: var(--ink-3); }

/* Inspector controls go filled-slim (the popover's language): quiet surface-2
   fills, no boxy chrome, sharpening on focus. */
.wf-inspector .input, .wf-inspector .select {
  height: 29px; font-size: 12.5px; padding: 0 9px; border-radius: 8px;
  background: var(--surface-2); border-color: transparent; }
.wf-inspector .input:hover, .wf-inspector .select:hover { border-color: var(--line); }
.wf-inspector .input:focus, .wf-inspector .select:focus {
  background: var(--surface); border-color: var(--line-strong); box-shadow: none; }

/* Field rows read as soft CARDS (enterprise builder feel): hairline border,
   grip dots waking on hover - obviously grabbable. */
.insp-field { border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 10px 12px; margin-bottom: 8px; background: var(--surface);
  transition: border-color var(--fast), box-shadow var(--fast); }
.insp-field:hover { border-color: var(--line-strong); }
.fgrip { display: inline-flex; align-items: center; cursor: grab; color: var(--ink-4);
  font-size: 15px; touch-action: none; opacity: .45;
  transition: color var(--fast), opacity var(--fast); }
.insp-field:hover .fgrip { opacity: 1; color: var(--ink-2); }
.fgrip:active { cursor: grabbing; }

/* Drag-in-flight: the card LIFTS OUT (fixed, shadowed, slightly tilted) and
   follows the pointer; a dashed slot marks where it will land. */
.insp-field.is-dragging { box-shadow: var(--shadow-lg); border-color: var(--accent);
  transform: rotate(1.5deg) scale(1.02); cursor: grabbing; }
.fsort-placeholder { border: 1.5px dashed var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface-2); margin-bottom: 8px; }

/* ---- Workflow inspector (a framework .card) ------------------------------ */
/* Sticky panel capped to the viewport: content taller than the screen scrolls
   inside the panel instead of being clipped out of reach. */
.wf-inspector { position: sticky; top: calc(var(--topbar-h) + 20px);
  max-height: calc(100vh - var(--topbar-h) - 40px); overflow-y: auto; overflow-x: hidden; }
.insp-head { padding: 16px 18px; border-bottom: 1px solid var(--line); }
.insp-section { padding: 14px 18px; border-bottom: 1px solid var(--line); }
.insp-label { display: flex; align-items: center; justify-content: space-between;
  font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 10px; }
.insp-row, .insp-bind, .insp-auto { padding: 8px 0; border-bottom: 1px solid var(--line); }
.insp-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.insp-row:last-child, .insp-bind:last-child, .insp-auto:last-child { border-bottom: none; padding-bottom: 0; }
.insp-bind { display: flex; flex-direction: column; gap: 2px; }
.insp-auto { display: flex; align-items: center; gap: 8px; }
.insp-foot { padding: 12px 18px; font-size: 12px; color: var(--ink-3);
  background: var(--surface-2); display: flex; align-items: center; gap: 7px; }

/* ---- Dashboard compositions ---------------------------------------------- */
.pipeline { display: grid; grid-template-columns: repeat(6, 1fr); }
.pipe-stage { display: flex; flex-direction: column; gap: 3px; padding: 14px 16px;
  border-right: 1px solid var(--line); }
.pipe-stage:last-child { border-right: none; }
/* Stages with a list behind them are click-throughs to that filter. */
.pipe-link { color: inherit; transition: background var(--fast); }
.pipe-link:hover { background: var(--hover); }
.pipe-link:hover .num { color: var(--accent); }
.dash-grid { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr);
  gap: 26px; align-items: start; }
/* Let grid children shrink below their content so wide lists never force
   horizontal page scroll (min-width:auto is the usual overflow culprit). */
.dash-grid > * { min-width: 0; }

/* The app shell has a fixed sidebar, so the usable content width is ~248px less
   than the viewport. Stack the two-column layouts well before the content itself
   gets cramped on 13-14" laptops. */
@media (max-width: 1440px) {
  .dash-grid { grid-template-columns: 1fr; }
}
@media (max-width: 1200px) {
  .wf-editor { grid-template-columns: 1fr; }
  .wf-inspector { position: static; }
}
@media (max-width: 720px) {
  .pipeline { grid-template-columns: repeat(3, 1fr); }
}

/* ---- Mobile pass: usable one-handed. Data grids scroll sideways instead of
   crushing; heads stack; chrome tightens. On phones the sidebar leaves the
   layout entirely and the topbar toggle summons it as an overlay. --------- */
@media (max-width: 640px) {
  .app { grid-template-columns: 1fr; }
  .app > .sidebar { display: none; }
  /* The framework toggle flips .app.collapsed; on phones that means SHOW. */
  .app.collapsed { grid-template-columns: 1fr; }
  .app.collapsed > .sidebar { display: flex; position: fixed; inset: 0 22vw 0 0;
    z-index: 95; width: auto; box-shadow: var(--shadow-lg); }
  .app.collapsed .brand-text, .app.collapsed .nav-section, .app.collapsed .nav-label,
  .app.collapsed .nav-tail, .app.collapsed .user-text { display: initial; }
  .app.collapsed .nav-item { justify-content: flex-start; padding: 0 10px; }
  .content { padding: 12px; }
  .topbar [data-cmdk] { display: none; }
  .crumbs .here { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .page-head { flex-direction: column; align-items: flex-start !important; gap: 12px; }
  .page-title { font-size: 22px; }
  .statstrip > div, .statstrip > .dash-stat { padding: 1px 14px; }
  .statstrip .ss-val { font-size: 20px; }
  .qlist { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .qlist > .qrow, .qlist > .qhead, .qlist > form.qrow { min-width: 560px; }
  .pipeline { grid-template-columns: 1fr 1fr; }
  .wfb { height: 68vh; }
  .wfb-node { width: 270px; }
  .wfb-kit { width: calc(100vw - 24px); }
  .assistant-btn { right: 12px; bottom: 12px; padding-inline: 12px; }
  .drawer { width: 100vw; max-width: 100vw; border-left: none; }
  .wf-editor.is-full .wf-inspector { width: calc(100vw - 28px); top: auto; bottom: 14px; max-height: 46vh; }
  .wf-editor.is-full .wfb-tools { right: 14px; bottom: calc(46vh + 24px); }
  .photo-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }

  /* The workflow builder on a phone: board first, inspector below, controls
     within thumb reach, no clutter. */
  .utabs { overflow-x: auto; -webkit-overflow-scrolling: touch; flex-wrap: nowrap; }
  .wfb { height: 56vh; }
  .wfb-node { width: 240px; padding: 11px 12px; }
  .wfb-branch { width: 200px; }
  .wfb-hint { display: none; }
  .wfb-tools { left: 12px; right: auto; bottom: 12px; }
  .wfb-tools .btn:not(.btn-icon) { padding-inline: 8px; }
  .wfb-kit { width: calc(100vw - 24px); }
  .wf-inspector { margin-top: 14px; }
  .insp-head, .insp-section { padding-left: 14px; padding-right: 14px; }
  .wf-hero-board { overflow-x: auto; flex-wrap: nowrap; padding: 16px 12px; }
  .sim-body { padding-bottom: 20px; }
  .wf-editor.is-full .wf-inspector { display: none; } /* fullscreen = the board, period */
  .wf-editor.is-full .wfb-tools { bottom: 12px; right: auto; left: 12px; }
}

/* ---- Auth screen --------------------------------------------------------- */
.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: var(--bg); }
.auth-card { width: 100%; max-width: 380px; }
.auth-brand { display: flex; align-items: center; gap: 10px; justify-content: center; margin-bottom: 22px; }
.auth-title { font-size: 22px; font-weight: 580; letter-spacing: -.02em; text-align: center; }
.auth-sub { text-align: center; color: var(--ink-2); font-size: 13px; margin: 6px 0 22px; }
.auth-panel { padding: 24px; }
.auth-demo { display: flex; align-items: center; gap: 8px; margin-top: 16px; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: 12px; color: var(--ink-2); }
.auth-foot { text-align: center; font-size: 12px; color: var(--ink-3); margin-top: 16px; }

/* ---- Quote builder ------------------------------------------------------- */
/* Line rows stack the name OVER its detail line, so a long name truncates in
   its own row instead of fighting the part number, badges, and qty column. */
.qlist .cell-name { overflow: hidden; }
.line-stack { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.line-stack .nm { display: block; }
/* The detail line WRAPS instead of truncating: the department badge and case
   must stay visible on a 13 inch screen, so they take a second row there. */
.line-stack .nm-sub { display: flex; align-items: center; gap: 4px 6px; min-width: 0;
  flex-wrap: wrap; white-space: normal; row-gap: 2px; }
.line-stack .nm-sub > * { white-space: nowrap; }
/* A sheet row that repeats the part number above it (a subpart / the other
   hand): indented with a quiet tag so the relationship reads at a glance. */
.sug-row.sug-sub .cell-name { padding-left: 18px; }
/* In a drag-ordered list every row already pads 18px for its grip, so a
   subpart / assembly member steps in a further 18px to read as nested. */
.sug-row[data-order-key].sug-sub > .cell-name { padding-left: 36px; }
.sug-row.sug-sub .sug-grip { left: 18px; }
.sub-tag { display: inline-flex; align-items: center; gap: 3px; margin-right: 6px; white-space: nowrap;
  font-size: 10px; color: var(--ink-3); border: 1px solid var(--line);
  border-radius: 999px; padding: 0 6px; font-family: var(--sans); }

/* ── Per-part color pick (suggestion rows) ────────────────────────────────
   The pill wears the row's color (swatch + name); the menu is the palette,
   cloned from one template per checklist, with the inline add-a-color. */
.clr-swatch { display: inline-block; width: 13px; height: 13px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--ink-1) 18%, transparent); flex-shrink: 0; }
.clr-pill { display: inline-flex; align-items: center; gap: 5px; margin-left: 8px; white-space: nowrap;
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
  background: var(--surface); font-family: var(--sans); font-size: 11px;
  color: var(--ink-2); cursor: pointer; }
.clr-pill:hover { border-color: var(--ink-3); }
.clr-paint { font-size: 9.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--review); border: 1px solid color-mix(in srgb, var(--review) 40%, transparent);
  border-radius: 999px; padding: 0 5px; }
/* Fixed overlay (see anchor_menu.js): never clipped by the row list, never
   inherits the part-number mono from .nm-sub. Colors scroll; add-a-color
   is a one-line footer until opened. */
.clr-menu { position: fixed; z-index: 90; width: 248px; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--shadow-lg); padding: 4px; font-family: var(--sans); color: var(--ink);
  overflow: hidden; }
.clr-menu .clr-swatch { width: 14px; height: 14px; }
.clr-list { overflow-y: auto; overscroll-behavior: contain; padding: 2px 2px 2px 0;
  min-height: 0; max-height: 240px; }
.clr-list::-webkit-scrollbar { width: 6px; }
.clr-list::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 99px; }
.clr-opt { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 10px; border: 0; background: transparent; border-radius: var(--r-xs);
  font-family: var(--sans); font-size: 13px; color: var(--ink); cursor: pointer; }
.clr-opt:hover { background: var(--hover); }
.clr-opt.is-on { background: var(--surface-3); }
.clr-opt.is-on:hover { background: color-mix(in srgb, var(--ink) 6%, var(--surface-3)); }
.clr-opt .clr-name { flex: 1; min-width: 0; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clr-opt .clr-code { font-size: 11px; color: var(--ink-3); font-weight: 400; }
.clr-foot { border-top: 1px solid var(--line); margin-top: 2px; padding: 2px 2px 0; flex: none; }
.clr-add-toggle { display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 7px 10px; border: 0; background: transparent; border-radius: var(--r-xs);
  font-family: var(--sans); font-size: 12.5px; font-weight: 500; color: var(--ink-2);
  cursor: pointer; }
.clr-add-toggle:hover { background: var(--hover); color: var(--ink); }
.clr-add-toggle .ph { font-size: 14px; color: var(--ink-3); }
.clr-add { display: grid; gap: 6px; padding: 8px 6px 4px; }
.clr-add[hidden], .clr-add-toggle[hidden] { display: none !important; }
.clr-add-fields { display: grid; grid-template-columns: auto minmax(0, 1fr) 52px;
  gap: 6px; align-items: center; min-width: 0; }
.clr-add .input { height: 28px; font-size: 12.5px; min-width: 0; width: 100%; padding: 0 8px; }
.clr-add .input.clr-add-code { font-family: var(--mono); font-size: 12px; padding: 0 6px; }
.clr-add-actions { display: flex; align-items: center; gap: 6px; }
.clr-add-actions .btn-ghost { margin-right: auto; padding-left: 0; }

/* Inline-editing cells: read as values, become inputs on hover/focus.
   Compact (28px) so rows keep list density; mono + tabular for numbers. */
.input-cell {
  height: 28px; padding: 0 8px;
  font-family: var(--mono); font-size: 12.5px; font-feature-settings: "tnum";
  text-align: right;
  background: transparent; border-color: transparent;
}
.input-cell:hover { background: var(--surface); }
.input-cell:focus { background: var(--surface); }
/* hide number spinners; they fight the quiet-list look */
.input-cell::-webkit-outer-spin-button, .input-cell::-webkit-inner-spin-button { appearance: none; margin: 0; }
.input-cell[type="number"] { -moz-appearance: textfield; }

/* The add-a-manual-line row: same grid as the lines above it, quiet until used. */
.line-add { cursor: default; }
.line-add:hover { background: transparent; }
.line-add .input { height: 30px; font-size: 12.5px; }
.line-add .input-cell { border-color: var(--line); background: var(--surface); }

/* ---- Scope site photos (field-docs pulls) --------------------------------- */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; }
.photo-tile { display: block; aspect-ratio: 4 / 3; overflow: hidden;
  border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface-2); }
.photo-tile img { width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--med); }
.photo-tile:hover img { transform: scale(1.04); }
/* The "Show all" tile at the end of the strip: opens the gallery modal. */
.photo-more { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; cursor: pointer; color: var(--ink-3); font-family: var(--sans); font-size: 12px;
  font-weight: 560; border: 1px dashed var(--line); }
.photo-more .ph { font-size: 20px; }
.photo-more:hover { color: var(--ink); border-color: var(--ink-3); }

/* ---- Site-photo gallery modal (browse all, select scope sheets, read) ----- */
.pm-overlay { position: fixed; inset: 0; z-index: 1300; padding: 24px;
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  display: grid; place-items: center; animation: pm-in 200ms ease-out; }
/* Explicit display beats the [hidden] UA default - restore it, or the
   overlay (and its loading/empty lines) renders open on page load. */
.pm-overlay[hidden], .pm-overlay [hidden] { display: none !important; }
@keyframes pm-in { from { opacity: 0; } to { opacity: 1; } }
.pm-box { background: var(--surface); border-radius: 10px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, .18);
  width: min(960px, 100%); max-height: min(86vh, 900px);
  display: flex; flex-direction: column; overflow: hidden; }
.pm-head { display: flex; justify-content: space-between; align-items: flex-start;
  gap: 12px; padding: 18px 20px 12px; border-bottom: 1px solid var(--line); }
.pm-title { font-weight: 650; font-size: 15px; }
.pm-body { overflow-y: auto; padding: 4px 20px 14px; flex: 1; }
.pm-day { font-size: 11px; font-weight: 650; color: var(--ink-3); letter-spacing: .02em;
  text-transform: uppercase; padding: 14px 0 8px; position: sticky; top: 0;
  background: var(--surface); z-index: 1; }
.pm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); gap: 8px; }
.pm-tile { position: relative; aspect-ratio: 4 / 3; border-radius: 8px; overflow: hidden;
  cursor: pointer; border: 2px solid transparent; padding: 0; background: var(--surface-2); }
.pm-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pm-tile:hover { border-color: var(--line); }
.pm-tile.is-picked { border-color: var(--ink); }
.pm-check { position: absolute; top: 6px; left: 6px; width: 20px; height: 20px;
  border-radius: 999px; display: none; place-items: center;
  background: var(--ink); color: var(--surface); font-size: 12px; }
.pm-tile.is-picked .pm-check { display: grid; }
.pm-open { position: absolute; bottom: 6px; right: 6px; width: 24px; height: 24px;
  border-radius: 6px; display: grid; place-items: center; color: var(--ink);
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  opacity: 0; transition: opacity var(--fast, 120ms) ease-out; }
.pm-tile:hover .pm-open, .pm-open:focus-visible { opacity: 1; }
.pm-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 12px 20px; border-top: 1px solid var(--line); }
@media (max-width: 640px) { .pm-overlay { padding: 8px; } .pm-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); } }

/* ---- Workflow progress rail (quote page; mirrors the editor's steps) ------ */
/* ── The guided first-run: one focused card, everything else under a veil ── */
.veiled { filter: blur(2.5px) saturate(.75); opacity: .42; pointer-events: none; user-select: none;
  transition: filter .45s var(--ease), opacity .45s var(--ease); }
.focus-card { background: var(--surface); border: 1.5px solid color-mix(in srgb, var(--accent) 55%, var(--line));
  border-radius: 14px; padding: 16px 18px 14px; margin-bottom: 22px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 9%, transparent), var(--shadow, 0 8px 24px rgba(0,0,0,.06)); }
.setup-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-top: 10px; }
.setup-done { color: var(--pos); font-size: 14px; margin-left: 5px; vertical-align: -2px; }
.subtle-label { color: var(--ink-3); font-weight: 460; }
.setup-more { margin-top: 12px; }
.setup-more > summary { list-style: none; display: inline-flex; }
.setup-more > summary::-webkit-details-marker { display: none; }
.focus-ring { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)) !important;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 9%, transparent); }
/* The Get started moment: setup is done, this is the one thing to press. */
.pulse-btn { animation: pulsebtn 1.7s var(--ease) infinite; }
@keyframes pulsebtn {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 38%, transparent); }
  55% { box-shadow: 0 0 0 9px transparent; }
}
@media (prefers-reduced-motion: reduce) { .pulse-btn { animation: none; } }
/* Boxed sections: work areas get a visible container so nothing floats in a
   void. The catalog search box reveals from the "Add from catalog" button. */
.panel-box { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; background: var(--surface);
  overflow-x: auto; }

/* ── Quote-from list (/quotes/new): pick a job ───────────────────────────
   A section head (label + count left, bulk bar + search right) over one
   quiet list. Each row carries ONE status pill and ONE meta line (source,
   site docs, the rest of the tags), so a tag-heavy job stays one row tall. */
.qsec-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
/* The bulk bar is a .row (display:flex), which would beat the UA [hidden]. */
.qsec-head [hidden] { display: none !important; }
.job-pick { --cols: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1.3fr) 110px 130px 124px; }
.job-pick.has-check { --cols: 30px minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1.3fr) 110px 130px 124px; }
/* The Tags column (the records-table idea): every tag as a hairline chip,
   one line tall; the overflow folds behind "+N more", which opens the cell
   in place (tagwrap controller) so every tag is one click away, never
   hidden in a tooltip. Stage tags carry a flag; the rest are plain. */
.tags-cell { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0;
  max-height: 24px; overflow: hidden; }
.tags-cell.is-open { max-height: none; }
.tag-chip { display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 8px;
  font: inherit; font-size: 11.5px; line-height: 1; color: var(--ink-2); background: var(--surface);
  border: 1px solid var(--line); border-radius: 6px; white-space: nowrap; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; }
.tag-chip .ph { font-size: 11px; flex: none; }
.tag-chip.stage .ph { color: var(--pos); }
.tag-chip.is-cut { display: none; }
.tag-more { border-style: dashed; color: var(--ink-3); cursor: pointer;
  transition: color .12s var(--ease), border-color .12s var(--ease); }
.tag-more:hover { color: var(--ink); border-color: var(--line-strong); }
.tag-more[hidden] { display: none; }
.job-pick .qrow { padding: 12px 8px; align-items: start; }
/* Every cell hangs from the top so an expanded tags cell grows the row
   DOWN and the name, status, checkbox, and button stay on the first line. */
.job-pick .qrow > * { align-self: start !important; }
.job-pick .qrow > .check { margin-top: 2px; }
.job-pick .qrow > .text-sm { margin-top: 2px; }
.job-pick .qrow > .right { margin-top: 0; }
.job-pick .tags-cell { margin-top: 0; }
.job-pick .job-status { margin-top: 2px; }
.job-pick .cell-name .nm-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.job-status { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.job-status .health { font-size: 12.5px; }
.job-meta { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: 12px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.job-meta > span { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.job-meta > span:first-child { flex: none; }
.job-meta .ph { font-size: 12px; }
.job-meta .sep { color: var(--line-strong); }
.job-pick .btn-default.btn-sm .ph { font-size: 13px; }
/* Row-only actions (delete) show on hover or keyboard focus, like a table. */
.job-pick .hover-act { opacity: 0; transition: opacity .12s var(--ease); }
.job-pick .qrow:hover .hover-act, .job-pick .qrow:focus-within .hover-act { opacity: 1; }
@media (hover: none) { .job-pick .hover-act { opacity: 1; } }
/* Laptop widths (a 13" screen is 1280 CSS px with the sidebar out): Stage
   and Group step out so the name, status, and tags keep real room. */
@media (max-width: 1320px) {
  .job-pick { --cols: minmax(0, 1.5fr) minmax(0, 1.1fr) minmax(0, 1fr) 118px; }
  .job-pick.has-check { --cols: 30px minmax(0, 1.5fr) minmax(0, 1.1fr) minmax(0, 1fr) 118px; }
  .job-pick .qhead > div:nth-last-child(2), .job-pick .qhead > div:nth-last-child(3),
  .job-pick .qrow > div:nth-last-child(2), .job-pick .qrow > div:nth-last-child(3) { display: none; }
  .qsec-head { flex-direction: column; align-items: stretch; }
  .qsec-head > .row { justify-content: flex-start; }
}

/* ── Quote workspace: tabs + summary rail (the clean flow) ──────────────── */
/* The rail gives a little back on 13" laptops (1280 CSS px wide with the
   sidebar out, the middle column is what gets cramped), full 300 elsewhere. */
.qwrap { display: grid; grid-template-columns: minmax(0, 1fr) clamp(248px, 22vw, 300px); gap: 24px; align-items: start; }
@media (max-width: 1100px) { .qwrap { grid-template-columns: 1fr; } }
.qtabs { display: flex; gap: 8px; margin-bottom: 20px; overflow-x: auto; scrollbar-width: none; padding: 2px; }
.qtabs::-webkit-scrollbar { display: none; }
.qtab { display: inline-flex; align-items: center; gap: 7px; padding: 8px 14px;
  font-size: 13px; font-weight: 520; color: var(--ink-3); white-space: nowrap; text-decoration: none;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  transition: color .15s var(--ease), border-color .15s var(--ease), background .15s var(--ease); }
.qtab:hover { color: var(--ink); border-color: var(--line-strong); }
.qtab.is-open { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 8%, transparent); }
.qtab .qtab-n { font-size: 11px; color: var(--ink-3); font-weight: 400; }
.qtab.is-open .qtab-n { color: var(--accent); }
.qtab .qtab-done { font-size: 12px; color: var(--pos, var(--accent)); }

/* Department color badge: hue from the name (see department_badge), outline
   style, never a filled block. */
.dept-badge { display: inline-block; font-size: 10.5px; font-weight: 540; line-height: 1;
  padding: 3px 8px; border-radius: 999px; vertical-align: 1px;
  border: 1px solid hsl(var(--dh) 45% 52% / .45); color: hsl(var(--dh) 60% 34%);
  background: hsl(var(--dh) 60% 52% / .07); }

/* Finish named in the catalog description: a real swatch, not a random hue.
   Hairline chip so white/clear still reads on the ivory canvas. */
.finish-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px;
  font-weight: 540; line-height: 1; padding: 3px 8px 3px 5px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-3); white-space: nowrap;
  vertical-align: 1px; margin-left: 8px; }
.finish-swatch { width: 8px; height: 8px; border-radius: 999px; flex: none;
  background: var(--fc); border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); }
.sug-row .finish-chip { margin-left: 8px; margin-top: 0; }

/* The case card's two tools as small tabs. */
.case-tools { margin-top: 10px; border-top: 1px dashed var(--line); padding-top: 8px; }
.ct-tabs { display: flex; gap: 6px; margin-bottom: 2px; }
.ct-tab { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; font-size: 12px;
  font-weight: 520; color: var(--ink-3); background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; cursor: pointer; transition: color .12s var(--ease), border-color .12s var(--ease); }
.ct-tab:hover { color: var(--ink); border-color: var(--line-strong); }
.ct-tab.is-on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
  background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
.case-tools .case-sugg { border-top: none; padding-top: 4px; margin-top: 2px; }

/* The OCR review: the document itself beside compact check-off rows. */
.notes-layout { display: grid; grid-template-columns: minmax(200px, 300px) minmax(0, 1fr); gap: 18px;
  align-items: start; }
.notes-doc { position: sticky; top: 14px; }
.notes-doc img { width: 100%; border-radius: 10px; border: 1px solid var(--line); display: block;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease); }
.notes-doc a:hover img { transform: scale(1.02); box-shadow: 0 10px 30px rgba(0, 0, 0, .18); }
.notes-row { display: flex; gap: 10px; padding: 10px 0; border-top: 1px solid var(--line);
  align-items: flex-start; }
.notes-row:first-of-type { border-top: none; }
@media (max-width: 860px) { .notes-layout { grid-template-columns: 1fr; } .notes-doc { position: static; max-width: 340px; } }

/* The in-case suggested parts + the "generating" beat. */
.case-sugg { margin-top: 10px; border-top: 1px dashed var(--line); padding-top: 8px; }
.sugg-gen { display: flex; align-items: center; gap: 9px; padding: 10px 4px; font-size: 12.5px; color: var(--ink-3); }
/* The little working spinner: lives in the suggestion zone AND inside a
   submitting button (working_controller swaps it in). */
.spin { width: 13px; height: 13px; flex-shrink: 0; border: 2px solid var(--line); border-top-color: var(--review);
  border-radius: 50%; animation: suggspin .7s linear infinite; }
@keyframes suggspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spin { animation: none; } }
/* More fields sits beside Quantity; opening it takes the full row. */
.cap-more > summary { margin-top: 18px; }
.cap-more[open] { grid-column: 1 / -1; }
.cap-more[open] > summary { margin-top: 0; }

/* The synced customer info grid: responsive columns, label over value. */
.info-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 12px 18px; }
.info-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.info-cell .text-sm { overflow: hidden; text-overflow: ellipsis; }
/* Customer info values read left-aligned under their labels (the shared
   .input-cell right-aligns for the number grids). */
.info-cell .input-cell { text-align: left; }
/* A flat address cell that opens the Bill To / Ship To pop-out on click
   (QB-136): one line, truncated like the other cells, pencil on hover. */
.addr-open { position: relative; display: block; width: 100%; min-width: 0;
  padding: 4px 24px 4px 7px; margin: -4px -7px; border: 0; border-radius: 8px;
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.addr-open:hover, .addr-open:focus-visible { background: var(--surface-2); outline: 0; }
.addr-open-icon { position: absolute; top: 50%; right: 7px; transform: translateY(-50%);
  font-size: 12px; color: var(--ink-3); opacity: 0; transition: opacity .12s ease; }
.addr-open:hover .addr-open-icon, .addr-open:focus-visible .addr-open-icon { opacity: 1; }
.qrail { position: sticky; top: 18px; display: flex; flex-direction: column; gap: 12px; }
.rail-card { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.rail-card .rc-h { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
  padding: 11px 14px 0; font-weight: 560; }
.rail-card .rc-body { padding: 9px 14px 13px; }
.rail-total { font-size: 26px; font-weight: 640; letter-spacing: -.015em; color: var(--ink); line-height: 1.1; }
.rail-row { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 8px 14px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--ink-3); }
.rail-row .v { color: var(--ink); font-weight: 520; }
.rail-src { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 12.5px; color: var(--ink); }
.rail-src .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.rail-src .st { margin-left: auto; font-size: 10.5px; color: var(--ink-3); text-transform: uppercase; letter-spacing: .04em; }

/* Suggested parts: Unit Price (static per-unit), Part price (qty × unit),
   Labor, Qty. Add/qty scale Part price only — they must not eat Unit Price.
   Fixed columns from Unit Price onward keep the header lined up; stock is
   `auto` and the name column absorbs the rest. */
.sug-row { display: grid; grid-template-columns: minmax(0, 1fr) auto 76px 76px 76px 78px 128px; gap: 10px; align-items: center;
  padding: 6px 2px; border-bottom: 1px solid var(--line); }
.sug-head { padding: 2px 4px 0; border-bottom: none; }
.sug-head .sh { font-size: 10.5px; color: var(--ink-3); letter-spacing: .01em; text-align: center;
  line-height: 1.2; white-space: normal; }
/* Names WRAP in suggestion rows and pick-a-job lists: never truncated. A long
   part number breaks rather than running under the stock pill. */
/* One line per part (Story, 2026-09-17): the name, then the part number and
   colour pill RIGHT BESIDE it, left to right, clustered at the left of the
   cell (never pushed to the far edge). A two-column grid does that: the
   name column sizes to its text but may shrink to nothing (ellipsis), the
   number + pill column never shrinks or breaks. Link chips take their own
   line under the pair. */
.sug-row .cell-name, .wrap-names .cell-name { display: grid; grid-template-columns: minmax(0, max-content) max-content;
  justify-content: start; align-items: center; column-gap: 10px; row-gap: 4px; overflow: visible; min-width: 0; }
/* The checklist measures ITSELF, not the window: on a narrow middle column
   (13" laptop, rail open) each row wraps to two lines, name + part number on
   top, the money boxes / qty / Add beneath with their own captions. Wide
   screens keep the one-line grid untouched. */
.case-sugg { container-type: inline-size; }
@container (max-width: 900px) {
  .sug-row:not(.sug-head) { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; padding: 10px 6px; }
  /* Line one: name · number · colour on the left, the Add / remove cell on
     the right. Line two: stock, the money boxes and the qty clicker. The
     action cell never spills onto a third line under the money. */
  .sug-row:not(.sug-head) .cell-name { flex: 1 1 calc(100% - 170px); min-width: 0; order: -2; }
  .sug-row:not(.sug-head) > .row:last-child { order: -1; margin-left: auto; align-self: center; flex-shrink: 0; }
  .sug-row .stockpill { padding: 4px 10px; }
  .sug-row .qty-step { gap: 4px; }
  .sug-head { display: none; }
  /* Two-class selectors: .pe-cap / .sug-price are (re)declared further down
     this file and would otherwise win the cascade over this block. */
  .sug-row .pe-cap { display: block; }
  .sug-row .sug-price { text-align: left; }
}
.sug-row .cell-name .nm, .wrap-names .cell-name .nm { grid-column: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.35; }
/* A cut name opens on click: the span is focusable (tabindex -1, never a
   Tab stop) and while it holds focus it wraps out to its full text; a click
   anywhere else folds it back. */
.sug-row .cell-name .nm:focus, .wrap-names .cell-name .nm:focus { white-space: normal; overflow: visible; outline: none; }
.sug-row .cell-name .nm-sub, .wrap-names .cell-name .nm-sub { grid-column: 2; white-space: nowrap; display: inline-flex; align-items: center; gap: 0; }
.sug-row .cell-name .pair-tag { grid-column: 1 / -1; justify-self: start; }
/* The row's quantity clicker: minus, value, plus. */
.qty-step { display: inline-flex; align-items: center; gap: 2px; }
.qty-btn { display: grid; place-items: center; width: 22px; height: 26px; border: 1px solid var(--line);
  border-radius: 7px; background: var(--surface); color: var(--ink-3); font-size: 11px; cursor: pointer;
  transition: border-color .12s var(--ease), color .12s var(--ease); }
.qty-btn:hover { border-color: var(--line-strong); color: var(--ink); }
.qty-btn:disabled { opacity: .4; cursor: default; }
/* A div row driven by rowlink still reads and behaves like the link it was. */
[data-controller~="rowlink"] { cursor: pointer; }

/* The quiet green "Autosaved" whisper beside a background-saving box. */
.autosave-status { color: var(--pos, #177a44); font-weight: 540; }
.autosave-status.is-bad { color: var(--neg); }

/* ---- Document cards: live PDF preview + per-document notes -------------- */
.doc-card { border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
.doc-card > summary { display: flex; align-items: center; gap: 10px; padding: 10px 14px; cursor: pointer; list-style: none; }
.doc-card > summary::-webkit-details-marker { display: none; }
.doc-caret { font-size: 12px; color: var(--ink-3); flex-shrink: 0; transition: transform .15s var(--ease); }
.doc-card[open] .doc-caret { transform: rotate(90deg); }
.doc-body { display: grid; grid-template-columns: minmax(240px, 1fr) minmax(0, 1.6fr); gap: 16px;
  padding: 12px 14px 14px; border-top: 1px solid var(--line); }
.doc-notes { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.doc-notes textarea { resize: vertical; height: auto; font-size: 12.5px; line-height: 1.5; padding: 8px 10px; }
.doc-preview iframe { width: 100%; height: 560px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); }
@media (max-width: 900px) { .doc-body { grid-template-columns: 1fr; } .doc-preview iframe { height: 420px; } }

/* ---- The sandbox banner: impossible to miss while in the Test workspace - */
.sandbox-bar { position: sticky; top: 0; z-index: 220; display: flex; align-items: center; gap: 10px;
  padding: 9px 18px; font-size: 13px; color: #5c4a05;
  background: color-mix(in srgb, var(--warn, #c28a12) 16%, #fffdf5);
  border-bottom: 2px solid var(--warn, #c28a12); }
.sandbox-bar .ph-flask { color: var(--warn, #c28a12); font-size: 16px; }
.sandbox-bar form { margin-left: auto; }
.sandbox-bar .sandbox-back { background: var(--warn, #c28a12); color: #fff; border: none; }
.sandbox-bar .sandbox-back:hover { filter: brightness(1.08); }

/* ---- Paused access: the app is visible but gray and inert --------------- */
.suspend-veil { filter: grayscale(.85) opacity(.55); user-select: none; }
.suspend-bar { position: sticky; top: 0; z-index: 200; display: flex; align-items: center; gap: 10px;
  padding: 10px 18px; font-size: 13px; background: var(--surface); border-bottom: 1px solid var(--line-strong); }
.suspend-bar .ph-lock { color: var(--warn, #c28a12); }
.suspend-bar form { margin-left: auto; }
.qty-val { width: 30px; text-align: center; padding: 3px 2px; height: 26px; font-size: 12.5px; }
.price-val { width: 78px; text-align: center; padding: 3px 7px; height: 26px; font-size: 12.5px;
  font-family: var(--mono); }
.price-val[readonly] { color: var(--ink-3); background: transparent; cursor: default; }
/* Price/labor/qty sit centered under their column headers. The per-box caption
   is hidden on desktop (the header strip labels the column) and only shows on
   narrow screens where that strip is dropped. */
.price-edit, .qty-step, .sug-price { justify-self: center; }
.price-edit { display: inline-flex; flex-direction: column; gap: 2px; align-items: center; }
.pe-cap { display: none; font-size: 9.5px; line-height: 1; letter-spacing: .02em; color: var(--ink-3);
  text-align: center; white-space: nowrap; }
.sug-price { display: inline-flex; flex-direction: column; gap: 2px; align-items: center; }
.price-val::-webkit-outer-spin-button, .price-val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-val::-webkit-outer-spin-button, .qty-val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-val[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.sug-row:last-child { border-bottom: none; }
/* A left/right hand pair: both rows carry the green rail (adjacent rows read
   as one connected block) and the link switch chip. Unlinking drops both. */
.sug-row.pair-linked { border-left: 2.5px solid var(--pos, #1e9e57); padding-left: 8px !important;
  background: color-mix(in srgb, var(--pos, #1e9e57) 4%, transparent); }
.sug-row.pair-linked:hover {
  background-color: color-mix(in srgb, var(--pos, #1e9e57) 4%, var(--hover)); }
.pair-tag { display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 560; white-space: nowrap;
  line-height: 1; padding: 3px 8px; border-radius: 999px; cursor: pointer;
  color: var(--pos, #177a44); background: transparent;
  border: 1px solid color-mix(in srgb, var(--pos, #1e9e57) 45%, var(--line)); }
.pair-tag .ph { font-size: 11px; }
.pair-tag:hover { background: color-mix(in srgb, var(--pos, #1e9e57) 10%, transparent); }
.pair-tag.is-off { color: var(--ink-3); border-color: var(--line); border-style: dashed; }
.pair-tag.is-static { cursor: default; }
/* Added rows quiet the text but keep their live stepper fully usable. */
.sug-row.is-added .cell-name .nm, .sug-row.is-added .cell-name .nm-sub,
.sug-row.is-added .sug-price, .sug-row.is-added .stockpill { opacity: .55; }
/* QB-104: the "Added" pill is a toggle in the Add button's spot — click it to
   un-add. It reads "Added" at rest and flips to a red "Un-add" on hover/focus.
   Un-adding a parent takes its linked children (Track/Heads, hand mate) too. */
.added-toggle { cursor: pointer; }
.added-toggle .added-on, .added-toggle .added-off { display: inline-flex; align-items: center; gap: 4px; }
.added-toggle .added-off { display: none; }
.added-toggle:hover, .added-toggle:focus-visible {
  color: var(--neg); border-color: color-mix(in srgb, var(--neg) 45%, var(--line)); }
.added-toggle:hover .added-on, .added-toggle:focus-visible .added-on { display: none; }
.added-toggle:hover .added-off, .added-toggle:focus-visible .added-off { display: inline-flex; }
/* TICKET-6: a quantity typed but not yet added tints the row until it
   auto-adds, so a populated qty is visibly on its way onto the total, never
   silently excluded. */
.sug-row.qty-pending { background: color-mix(in srgb, var(--warn, #c28a12) 9%, transparent);
  box-shadow: inset 2px 0 0 var(--warn, #c28a12); }
.sug-row.qty-pending:hover {
  background-color: color-mix(in srgb, var(--warn, #c28a12) 9%, var(--hover)); }
.sug-price { font-family: var(--mono, monospace); font-size: 13px; text-align: center; color: var(--ink); }
.stockpill { font-size: 11px; font-weight: 520; border-radius: 6px; padding: 3px 8px; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); color: var(--ink-3); }
.stockpill .d { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); }
.stockpill.in { color: var(--pos, #177a44); border-color: color-mix(in srgb, var(--pos, #1e9e57) 35%, var(--line)); }
.stockpill.in .d { background: var(--pos, #1e9e57); }
.stockpill.short { color: var(--warn, #8a6410); border-color: color-mix(in srgb, var(--warn, #c28a12) 40%, var(--line)); }
.stockpill.short .d { background: var(--warn, #c28a12); }
.stockpill.out { color: var(--neg); border-color: color-mix(in srgb, var(--neg) 35%, var(--line)); }
.stockpill.out .d { background: var(--neg); }

/* An out-of-stock row's in-stock colour options: pick one, paint it to match
   the colour that was wanted. A tinted dashed box indented under the row. */
.alt-callout { margin: 2px 0 8px 18px; padding: 8px 10px 4px;
  border: 1px dashed color-mix(in srgb, var(--warn, #c28a12) 45%, var(--line));
  border-radius: 8px; background: color-mix(in srgb, var(--warn, #c28a12) 8%, var(--surface, #fff)); }
.alt-head { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  color: var(--warn, #8a6410); margin-bottom: 4px; }
.alt-head .ph { font-size: 14px; }
.alt-row.sug-row { border-bottom: none !important; padding: 4px 2px !important; }
.alt-row .clr-pill { margin-left: 0; }
@media (max-width: 640px) { .sug-row { grid-template-columns: minmax(0, 1fr) auto; } .sug-price { text-align: left; } .sug-head { display: none; } .pe-cap { display: block; } }

.wfp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.wfp-step { display: inline-flex; align-items: center; gap: 7px; }
.wfp-dot { width: 18px; height: 18px; border-radius: 999px; flex: none;
  display: grid; place-items: center; font-size: 11px;
  background: var(--surface); border: 1.5px solid var(--line-strong); color: var(--on-accent); }
.wfp-step.is-done .wfp-dot { background: var(--accent); border-color: var(--accent); }
.wfp-step.is-current .wfp-dot { border-color: var(--accent); box-shadow: var(--ring); }
.wfp-name { font-size: 12px; color: var(--ink-3); letter-spacing: -.003em; }
.wfp-step.is-done .wfp-name, .wfp-step.is-current .wfp-name { color: var(--ink); font-weight: 520; }
.wfp-line { width: 22px; height: 1.5px; background: var(--line-strong); flex: none; }
.wfp-line.is-done { background: var(--accent); }
@media (max-width: 1100px) { .wfp-name { display: none; }
  .wfp-step.is-current .wfp-name { display: inline; } }

/* ---- Agent banner (the agent's work, visible on the quote) ---------------- */
.agent-banner { display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--review-soft);
  background: color-mix(in srgb, var(--review-soft) 45%, var(--surface));
  border-radius: var(--r); }
.agent-banner .glyph { font-size: 22px; width: 26px; height: 26px; }
/* Stock counsel is one fold for the whole list, not a bar per line. */
.stock-advice { display: block; margin-bottom: 16px;
  border-color: color-mix(in srgb, var(--review) 28%, var(--line)); }
.stock-advice > summary { display: flex; align-items: center; gap: 10px;
  cursor: pointer; list-style: none; }
.stock-advice > summary::-webkit-details-marker { display: none; }
.stock-advice > summary .cap-caret { margin-left: auto; font-size: 12px;
  transition: transform .15s ease; color: var(--ink-3); }
.stock-advice[open] > summary .cap-caret { transform: rotate(90deg); }
.stock-advice-list { margin-top: 10px; display: flex; flex-direction: column; gap: 12px; }
.stock-advice-item { padding-left: 36px; }

/* ---- Near-miss suggestions under an unmatched line ------------------------ */
.near-miss { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; padding: 4px 8px 12px 40px; }
.near-miss-pick { border: 1px solid var(--line); border-radius: var(--r-sm); gap: 7px; }
.near-miss-pick:hover { border-color: var(--review); }

/* ---- Review queue row actions -------------------------------------------- */
/* button_to wraps in a block <form>; keep actions in one flex row like links. */
.review-queue-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; align-items: flex-start; }
.review-queue-action-form { display: inline; margin: 0; }
/* Top-align the row so a wrapping sentence doesn't shove the pill/buttons off-centre. */
.qrow.rq-row { align-items: start; }
/* The sentence cell: stacked and free to WRAP (unlike .cell-name's one-line ellipsis). */
.rq-what { display: flex; flex-direction: column; gap: 3px; min-width: 0; padding: 1px 0; }
.rq-what .rq-line { font-weight: 500; color: var(--ink); letter-spacing: -.008em; line-height: 1.45; }
.rq-what a.rq-line:hover { text-decoration: underline; }
.rq-what .rq-src { font-size: 12px; color: var(--ink-3); }

/* ── Capture cases: the workflow-driven case capture (grouped, sandbox flow) ── */
.cap-dept { position: relative; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 10px; background: var(--surface); }
.cap-dept > summary { display: flex; align-items: center; gap: 8px; padding: 10px 12px; cursor: pointer; list-style: none; font-weight: 520; font-size: 13.5px; color: var(--ink); }
.cap-dept:has(> .cap-add) > summary { padding-right: 118px; }
.cap-dept > summary::-webkit-details-marker { display: none; }
.cap-dept > summary .cap-caret,
.cap-card > summary .cap-caret { font-size: 12px; transition: transform .15s ease; color: var(--ink-3); }
.cap-dept[open] > summary .cap-caret,
.cap-card[open] > summary .cap-caret { transform: rotate(90deg); }
/* Suggested-parts sections (one per sheet style group) fold like the case cards. */
.sug-group > summary .cap-caret { font-size: 12px; transition: transform .15s ease; color: var(--ink-3); }
.sug-group[open] > summary .cap-caret { transform: rotate(90deg); }
.sug-group > summary { border-bottom: 1px solid var(--line); }
.cap-dept[open] > summary,
.cap-card[open] > summary { border-bottom: 1px solid var(--line); }
.cap-count { margin-left: auto; font-size: 11px; color: var(--ink-3); font-weight: 400; }
.cap-body { padding: 10px 12px; }
/* A case slot: the card plus its Edit / Remove pinned top-right, outside the
   <details> so they work folded or open. Quiet until the card is hovered,
   focused, open, or editing (always on for touch). */
.cap-slot { position: relative; margin-bottom: 8px; }
.cap-actions { position: absolute; top: 6px; right: 8px; z-index: 2; display: flex; gap: 2px; align-items: center;
  opacity: 0; transition: opacity .15s var(--ease); }
.cap-actions form { margin: 0; }
.cap-slot:hover .cap-actions, .cap-slot:focus-within .cap-actions,
.cap-slot:has(.cap-card[open]) .cap-actions, .cap-slot.is-editing .cap-actions { opacity: 1; }
@media (hover: none) { .cap-actions { opacity: 1; } }
.cap-slot.is-editing .cap-act-edit { color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.cap-card { border: 1px solid var(--line); border-radius: 9px;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease); }
.cap-slot .cap-card > summary { padding-right: 76px; }
/* Editing: the card wears the accent ring (same voice as the open tab) and
   the summary grows an "Editing" tag beside the name. */
.cap-slot.is-editing .cap-card { border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 9%, transparent); }
.cap-editing-tag { display: none; align-items: center; gap: 4px; font-size: 10.5px; font-weight: 560;
  line-height: 1; padding: 3px 7px; border-radius: 999px; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line)); white-space: nowrap; }
.cap-slot.is-editing .cap-editing-tag { display: inline-flex; animation: ai-pop .2s var(--ease) both; }
/* The editor sheet: first in the body, revealed by a grid-rows slide (0fr to
   1fr, no measuring) with the sheet easing up into place. Closed it is also
   invisible to the keyboard. Once settled the clip lifts so dropdown menus
   inside can overflow the sheet. */
.cap-edit { display: grid; grid-template-rows: 0fr; visibility: hidden;
  transition: grid-template-rows .32s var(--ease), visibility 0s linear .32s; }
.cap-edit.is-open { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows .32s var(--ease), visibility 0s; }
.cap-edit-in { min-height: 0; overflow: hidden; opacity: 0; transform: translateY(-6px);
  transition: opacity .22s var(--ease), transform .32s var(--ease); }
.cap-edit.is-open .cap-edit-in { opacity: 1; transform: none; }
.cap-edit.is-settled .cap-edit-in { overflow: visible; }
.cap-edit-in > form { margin: 0 0 12px; padding: 12px 12px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 3%, var(--surface)); }
.cap-edit-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.cap-edit-eyebrow { display: inline-flex; align-items: center; gap: 4px; font-family: var(--mono); font-size: 10.5px;
  font-weight: 500; letter-spacing: .07em; text-transform: uppercase; color: var(--accent); }
.cap-edit-title { font-size: 13.5px; font-weight: 560; color: var(--ink); overflow-wrap: anywhere; }
/* While editing, the saved view (chips + parts) and the footer step aside:
   the editor holds this case's full checklist, so parts never show twice. */
.cap-slot.is-editing .cap-view, .cap-slot.is-editing .cap-foot { display: none; }
.cap-foot { display: flex; justify-content: flex-end; margin-top: 10px; }
@media (prefers-reduced-motion: reduce) {
  .cap-edit, .cap-edit-in, .cap-actions, .cap-card { transition: none; }
  .cap-editing-tag { animation: none; }
}
[data-motion="off"] .cap-edit, [data-motion="off"] .cap-edit-in { transition: none; }
[data-motion="off"] .cap-editing-tag { animation: none; }
@keyframes cap-saved { from { background: color-mix(in srgb, var(--accent) 14%, var(--surface)); } }
.cap-card.is-saved { animation: cap-saved .85s var(--ease); }
.cap-card > summary { display: flex; align-items: center; gap: 8px; padding: 10px 12px; cursor: pointer; list-style: none; }
.cap-card > summary::-webkit-details-marker { display: none; }
.cap-card-body { padding: 10px 12px; }
.cap-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.cap-chip { font-size: 11px; line-height: 1; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--line); color: var(--ink); }
.cap-chip .subtle { color: var(--ink-3); }
.cap-form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; margin-bottom: 10px; }
.cap-form-grid .cap-span { grid-column: 1 / -1; }

/* Multi-select dropdown: looks like .select, opens a checkbox menu. */
.multisel { display: block; width: 100%; }
.multisel-trigger {
  display: block; width: 100%; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
}
.multisel-trigger.is-placeholder { color: var(--ink-4); }
/* Required, nothing picked, submit attempted (multisel controller). */
.multisel.is-invalid .multisel-trigger { border-color: var(--neg); }
.multisel-pop {
  min-width: 100%; width: max-content; max-width: min(280px, 90vw);
  padding: 6px; z-index: 40;
}
.multisel-item {
  display: flex; width: 100%; padding: 7px 8px; border-radius: var(--r-xs);
  white-space: normal; gap: 8px;
}
.multisel-item:hover { background: var(--hover); }
.cap-add { display: flex; flex-direction: column; align-items: flex-end; }
.cap-add:not([open]) { position: absolute; top: 6px; right: 12px; z-index: 2; }
.cap-add[open] { margin: 0 12px; padding-top: 10px; align-items: stretch; }
.cap-add[open] > summary { align-self: flex-end; }
.cap-add > summary { list-style: none; }
.case-manual { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 8px; }
.cm-fields { display: contents; }
.cm-del { margin: 0; }
.cm-name { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.cm-add .input { height: 30px; font-size: 12.5px; }
/* Line items rows ARE suggestion rows (.sug-row): the list measures itself
   like the checklist (container-type), so the same container query gives it
   the same two modes - one-line grid under its own header row when wide,
   value-over-caption cells when narrow. No overrides of its own. */
.case-manual { container-type: inline-size; }
.case-manual .cm-line .qty-btn { width: 26px; }
.cm-merged { margin-top: 4px; }
/* The Additional items entry bar: a suggestion row whose cells are inputs. */
.manual-bar .input { height: 30px; font-size: 12.5px; }
.manual-bar .cell-name .input { width: 100%; }
.manual-bar .qty-step .qty-val { width: 52px; }
@media (max-width: 640px) { .cap-form-grid { grid-template-columns: 1fr 1fr; } }

/* The tick-off: a department's usual parts as a check-off-with-qty list, the
   old Excel tool's speed. Rows are labels: click anywhere to tick. */
.cap-common { border: 1px dashed var(--line-strong); border-radius: 9px; margin: 4px 0 10px; }
.cap-common > summary { display: flex; align-items: center; gap: 7px; padding: 8px 11px; cursor: pointer; list-style: none; font-size: 12.5px; font-weight: 520; color: var(--ink); }
.cap-common > summary::-webkit-details-marker { display: none; }
.cap-common-body { padding: 4px 8px 10px; }
.cp-row { display: grid; grid-template-columns: 22px minmax(0, 1fr) 96px 76px 66px; gap: 8px; align-items: center; padding: 5px 5px; border-radius: 7px; cursor: pointer; }
.cp-row:hover { background: var(--hover); }
.cp-row.is-on { opacity: .55; cursor: default; }
.cp-row .cp-desc { font-size: 13px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cp-row input[type="checkbox"] { accent-color: var(--accent); width: 15px; height: 15px; }
.cp-row .input-cell { padding: 3px 7px; height: 26px; font-size: 12.5px; }
@media (max-width: 640px) { .cp-row { grid-template-columns: 22px minmax(0, 1fr) 76px 66px; } .cp-row .cp-part { display: none; } }

/* ---- AI drafting loader (instructions box) --------------------------------
   While the agent writes, the textarea blurs and a pill floats over it:
   a 3x3 pixel grid whose chevron wavefront drives right (delay = (col +
   |row-1|) x 90ms on a 650ms cycle, so two fronts are always in flight),
   a shimmering label, and a live elapsed timer in mono tabular figures.
   Reduced motion freezes the grid dim; the timer still ticks. */
.ai-box { position: relative; }
.ai-box .textarea { transition: filter .25s var(--ease), opacity .25s var(--ease); }
.ai-writing .textarea { filter: blur(4px); opacity: .5; pointer-events: none; }
.ai-veil { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; }
.ai-writing .ai-veil { display: flex; }
.ai-veil-pill { display: inline-flex; align-items: center; gap: 10px; padding: 9px 15px;
  border-radius: 999px; background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow); animation: ai-pop .2s cubic-bezier(.16,1,.3,1) both; }
@keyframes ai-pop { from { opacity: 0; transform: scale(.92) translateY(3px); } }
.ai-grid { display: grid; grid-template-columns: repeat(3, 4px); gap: 1.5px; flex-shrink: 0; }
.ai-grid span { width: 4px; height: 4px; border-radius: 1px; background: var(--ink);
  opacity: .15; animation: ai-pixel-on 650ms ease-in-out infinite; }
@keyframes ai-pixel-on { 50% { opacity: 1; } }
.ai-grid span:nth-child(1) { animation-delay: 90ms; }
.ai-grid span:nth-child(2) { animation-delay: 180ms; }
.ai-grid span:nth-child(3) { animation-delay: 270ms; }
.ai-grid span:nth-child(4) { animation-delay: 0ms; }
.ai-grid span:nth-child(5) { animation-delay: 90ms; }
.ai-grid span:nth-child(6) { animation-delay: 180ms; }
.ai-grid span:nth-child(7) { animation-delay: 90ms; }
.ai-grid span:nth-child(8) { animation-delay: 180ms; }
.ai-grid span:nth-child(9) { animation-delay: 270ms; }
.ai-label { font-size: 13px; font-weight: 500; color: transparent;
  background: linear-gradient(90deg, var(--ink-3) 35%, var(--ink) 50%, var(--ink-3) 65%);
  background-size: 200% 100%; -webkit-background-clip: text; background-clip: text;
  animation: ai-shimmer 1.4s linear infinite; }
@keyframes ai-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.ai-elapsed { font-family: var(--mono); font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) {
  .ai-grid span, .ai-label, .ai-veil-pill { animation: none; }
  .ai-label { color: var(--ink-3); background: none; }
}
[data-motion="off"] .ai-grid span, [data-motion="off"] .ai-label, [data-motion="off"] .ai-veil-pill { animation: none; }
[data-motion="off"] .ai-label { color: var(--ink-3); background: none; }

/* ---- "Always suggest" (QB-101): pin button + confirm modal ---------------- */
/* The pin sits between Add and the X on a catalog match; the accent is the
   only colour, and only on hover, so a row never shouts. */
.pin-btn { color: var(--ink-3); }
.pin-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.pin-modal { max-width: 520px; }
.pin-part { display: flex; align-items: center; gap: 10px; padding: 10px 12px; margin-bottom: 16px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--surface-2); }
.pin-part > .ph { font-size: 18px; color: var(--ink-3); flex: none; }
/* Two dropdowns joined by a link: the line draws in and the link chip wakes
   the moment both are picked (the rule IS the pair), no bounce, 200ms. */
.pin-pair { display: grid; grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1fr); gap: 8px; align-items: end; }
.pin-link { position: relative; height: 36px; display: grid; place-items: center; color: var(--ink-4); }
.pin-link svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.pin-link-line { fill: none; stroke: var(--line-strong); stroke-width: 1.5; stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset .22s var(--ease), stroke .18s var(--ease); }
.pin-link-dot { position: relative; width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;
  font-size: 13px; background: var(--surface); border: 1px solid var(--line); color: var(--ink-4);
  transform: scale(.88); transition: transform .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease),
  background .18s var(--ease); }
.pin-link.is-linked .pin-link-line { stroke-dashoffset: 0; stroke: var(--accent); }
.pin-link.is-linked .pin-link-dot { transform: none; color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
/* Saved: the chip fills, a soft ring leaves it once, the pin becomes a check. */
.pin-scrim.is-done .pin-link-dot { background: var(--accent); border-color: var(--accent); color: #fff;
  animation: pin-ring .5s var(--ease) 1; }
@keyframes pin-ring {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 40%, transparent); }
  100% { box-shadow: 0 0 0 10px transparent; }
}
.pin-hint { margin: 12px 0 0; transition: color .18s var(--ease); }
.pin-scrim.is-done .pin-hint { color: var(--ink); }
.pin-error { margin: 10px 0 0; color: var(--danger, #8f3a32); }
.btn.is-busy { opacity: .7; pointer-events: none; }
@media (max-width: 480px) {
  .pin-pair { grid-template-columns: 1fr; }
  .pin-link { height: 28px; }
  .pin-link svg { display: none; }
}
/* A pinnable list (catalog matches) carries three controls in the action
   cell: Add, the pin, the X. Wide: the last column grows so they stay on one
   line. Narrow: the action cell rides the NAME line like every other row
   (the container query above). */
.case-sugg.has-pin .sug-row { grid-template-columns: minmax(0, 1fr) auto 76px 76px 76px 78px 156px; }
.case-sugg.has-pin .sug-row > .row:last-child { gap: 4px; flex-wrap: nowrap; }
.pin-btn { width: 26px; }

/* ---- "Already added" (QB-102): a typed add raised an existing line ------- */
/* The note sits right where Add was clicked; the line's own row (inside the
   opened Additional items / Paint fold) flashes once, the saved-card way. */
.cm-merged { display: flex; align-items: center; gap: 8px; margin: 6px 0 2px; padding: 7px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 7%, var(--surface)); font-size: 12.5px; color: var(--ink);
  animation: rise-in .3s var(--ease); }
.cm-merged .ph { color: var(--accent); font-size: 15px; flex: none; }
.cm-merged b { font-weight: 560; }
.sug-row.is-merged { animation: cap-saved 1.2s var(--ease); }

/* ---- Suggested parts quick search (QB-103) -------------------------------- */
/* A small, quiet field on its own line under the list's title, left-aligned
   so it is the first thing seen. It grows and gains the accent ring only on
   focus or while a search is on; the count and the clear button appear only
   then. A soft sheen sweeps left to right once when the list lands and
   again on focus (the "this is live" cue), never while typing. */
.sug-search-row { margin: 2px 0 8px; }
.sug-search { position: relative; display: inline-flex; align-items: center; gap: 6px; height: 30px;
  width: 260px; max-width: 100%; padding: 0 8px 0 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--surface); color: var(--ink-3); cursor: text; overflow: hidden; isolation: isolate;
  transition: width .2s var(--ease), border-color .15s var(--ease), box-shadow .15s var(--ease), color .15s var(--ease); }
.sug-search:hover { border-color: var(--ink-4); }
.sug-search:focus-within, .sug-search.is-on { width: 320px; border-color: var(--accent); color: var(--ink-2); }
.sug-search:focus-within { box-shadow: var(--ring); }
.sug-search::after { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--accent) 12%, transparent) 50%, transparent 80%);
  transform: translateX(-110%); animation: sug-sheen .9s var(--ease) .2s 1; }
.sug-search:focus-within::after { animation: sug-sheen .7s var(--ease) 1; }
.sug-search.is-on::after { animation: none; }
@keyframes sug-sheen { to { transform: translateX(110%); } }
@media (prefers-reduced-motion: reduce) { .sug-search::after { animation: none !important; } }
[data-motion="off"] .sug-search::after { animation: none !important; }
.sug-search > .ph { font-size: 14px; flex: none; }
.sug-search-input { flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent;
  font: inherit; font-size: 12.5px; color: var(--ink); padding: 0; }
.sug-search-input::placeholder { color: var(--ink-4); }
.sug-search-input::-webkit-search-cancel-button { display: none; }
.sug-search-count { font-size: 11px; color: var(--ink-3); white-space: nowrap; font-variant-numeric: tabular-nums;
  animation: rise-in .2s var(--ease); }
.sug-search-count:empty { display: none; }
.sug-search-clear { display: grid; place-items: center; width: 18px; height: 18px; flex: none; border: 0;
  border-radius: 4px; background: transparent; color: var(--ink-3); cursor: pointer; font-size: 12px;
  transition: background .12s var(--ease), color .12s var(--ease); }
.sug-search-clear:hover { background: var(--surface-2); color: var(--ink); }
/* Display-only: the class hides, the row and its inputs stay exactly as they were. */
.sug-row.is-off-search { display: none !important; }
/* An out-of-stock row's "Other colors" callout follows its row off screen. */
.sug-row.is-off-search + .alt-callout, .alt-callout[hidden] { display: none !important; }
@container (max-width: 900px) {
  .sug-search, .sug-search:focus-within, .sug-search.is-on { width: 100%; }
}

/* ---- Suggested parts manager (pins + removals per case type / model) ---- */
/* Underline tabs (no pills): the active mark slides between them. */
.spm-tabs { display: flex; gap: 18px; border-bottom: 1px solid var(--line); margin-bottom: 12px; }
.spm-tab { position: relative; display: inline-flex; align-items: center; gap: 6px; padding: 8px 2px 10px;
  font-size: 13px; font-weight: 520; color: var(--ink-2); text-decoration: none; }
.spm-tab .ph { font-size: 14px; }
.spm-tab::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--accent); transform: scaleX(0); transform-origin: left center; transition: transform .16s ease-out; }
.spm-tab.is-on { color: var(--ink); }
.spm-tab.is-on::after { transform: scaleX(1); }
.spm-tab:hover { color: var(--ink); }
.spm-filters { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 12px; }
.spm-filters .input { min-width: 180px; }
/* The records list: the qlist grid, one row per suggestion. */
.spm-list { --cols: minmax(0, 1.6fr) minmax(170px, .9fr) 90px 160px max-content; container-type: inline-size; }
.spm-row { display: grid; grid-template-columns: var(--cols); gap: 10px; align-items: center; }
.qrow.spm-row { animation: spm-in .18s ease-out both; animation-delay: calc(var(--i, 0) * 16ms); }
@keyframes spm-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.qrow.spm-row.is-removed .cell-name, .qrow.spm-row.is-removed .mini-chip { opacity: .55; }
.spm-more { cursor: pointer; text-decoration: none; transition: border-color .12s ease-out, color .12s ease-out; }
.spm-more:hover { border-color: var(--ink-3); color: var(--ink); }
.spm-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 540;
  border-radius: 6px; padding: 2px 8px; width: max-content; border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2); }
.spm-badge .ph { font-size: 12px; }
.spm-badge.is-pinned { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.spm-badge.is-removed { color: var(--ink-3); border-style: dashed; }
/* Actions ride at the row's end; hover lifts them into focus (records-table rule). */
.spm-actions { display: flex; gap: 4px; justify-content: flex-end; align-items: center; opacity: .72; transition: opacity .12s ease-out; }
.qrow.spm-row:hover .spm-actions, .qrow.spm-row:focus-within .spm-actions { opacity: 1; }
.spm-act { margin: 0; }
.spm-move { position: relative; }
.spm-move > summary { list-style: none; cursor: pointer; }
.spm-move > summary::-webkit-details-marker { display: none; }
.spm-move-pop { position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; width: 240px; padding: 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.08);
  animation: spm-in .14s ease-out both; }
.spm-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 28px 8px; border: 1px dashed var(--line); border-radius: 8px; }
.spm-empty .ph { font-size: 22px; color: var(--ink-3); margin-bottom: 4px; }
/* The history drawer: fixed right, slides in on load, out on close. */
.spm-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 92vw); z-index: 60;
  background: var(--surface); border-left: 1px solid var(--line); box-shadow: -12px 0 32px rgba(0,0,0,.08);
  transform: translateX(104%); transition: transform .2s ease-out; display: flex; flex-direction: column; }
.spm-drawer.is-open { transform: none; }
.spm-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.spm-hist { padding: 14px 16px; overflow: auto; }
.spm-hist-title { display: flex; flex-direction: column; gap: 2px; margin-bottom: 12px; }
.spm-trail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.spm-trail-item { position: relative; display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: 8px; padding: 8px 0 12px;
  animation: spm-in .18s ease-out both; animation-delay: calc(var(--i, 0) * 24ms); }
.spm-trail-item::before { content: ""; position: absolute; left: 6px; top: 22px; bottom: -2px; width: 1px; background: var(--line); }
.spm-trail-item:last-child::before { display: none; }
.spm-trail-dot { width: 13px; height: 13px; border-radius: 50%; margin-top: 3px; border: 2px solid var(--ink-3); background: var(--surface); }
.spm-trail-item.catalog-part-pinned .spm-trail-dot, .spm-trail-item.catalog-part-restored .spm-trail-dot { border-color: var(--accent); }
.spm-trail-item.catalog-part-unpinned .spm-trail-dot, .spm-trail-item.catalog-part-removed .spm-trail-dot { border-color: var(--ink-3); border-style: dashed; }
.spm-trail-item.is-origin .spm-trail-dot { background: var(--ink-3); border-color: var(--ink-3); }
/* A row holding an open popover paints above the rows below it (each row is
   its own stacking layer while it animates in). */
.qrow.spm-row:has(.spm-move[open]) { position: relative; z-index: 50; }
/* The list measures itself: on a 13" screen (rail + gutters leave ~900px)
   each row wraps to two lines - part + pair, then style, state and actions. */
.spm-style-chip { display: none; }
@container (max-width: 1200px) {
  /* Two lines: the part (name, number, stock) with its state on the right;
     then the pair, the style chip and the actions. */
  .qrow.spm-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name state" "pair actions";
    gap: 8px 16px; padding: 12px 8px; align-items: start; }
  .qrow.spm-row > .cell-name { grid-area: name; }
  .qrow.spm-row > .spm-state { grid-area: state; align-items: flex-end; text-align: right; }
  .qrow.spm-row > .spm-pair { grid-area: pair; align-self: center; }
  .qrow.spm-row > .spm-style { display: none; }
  .spm-style-chip { display: inline-flex; }
  .qrow.spm-row > .spm-actions { grid-area: actions; justify-self: end; align-self: center; opacity: 1; }
  .qhead.spm-row { display: none; }
}

/* Drag-to-reorder on the suggested parts (sugorder): the grip rides the row's
   left edge, faint until hover; a lifted row travels with the builder's
   drag look (.is-dragging / .fsort-placeholder). */
.sug-row[data-order-key] > .cell-name { position: relative; padding-left: 18px; }
.sug-grip { position: absolute; left: 0; top: 3px; display: inline-flex; align-items: center; cursor: grab;
  color: var(--ink-4); opacity: .35; font-size: 14px; transition: opacity .12s ease-out, color .12s ease-out; touch-action: none; }
.sug-row:hover .sug-grip { opacity: 1; color: var(--ink-2); }
.sug-grip:active { cursor: grabbing; }
.sug-row.is-dragging { background: var(--surface); box-shadow: var(--shadow-lg); border: 1px solid var(--accent); border-radius: var(--r-sm); }
/* A part travels as one row while it drags: its repeats / linked parts fold
   away and this badge on the lifted card says how many come along. */
.sug-drag-more { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); font-size: 11px; font-weight: 560;
  color: var(--ink-2); background: var(--surface-2); border: 1px solid var(--line); border-radius: 6px; padding: 2px 8px; }
