/* ============================================================
   V2 DESIGN TOKENS + COMPONENT CLASSES
   Verbatim port of wireframes/local_v2_app.html — slice 17.
   Loaded by base.html alongside the Tailwind CDN. Classes here
   live in their own namespace (kebab-case semantic names like
   .btn / .card / .pill) and don't collide with Tailwind utilities,
   so per-page templates can opt into v2 styling incrementally.
   ============================================================ */

/* Fragment-anchor scrolling. The event detail page links between
   tabs via `#billing`, `#contracts`, `#documents` etc. Without
   `scroll-padding-top` the anchored element lands at viewport y=0,
   which clips the heading behind any header strip / floating
   element rendered above it. 60px is enough to clear the page-head
   strip without leaving a visible gap. */
html { scroll-padding-top: 60px; }

:root {
  --bg: #F4EDDF; --paper: #FFFDF8; --paper-2: #FAF4E6;
  --ink: #1F1B18; --ink-soft: #6B5F54; --ink-faint: #9B8E80;
  --line: #2A2520; --line-soft: #D7CCB9; --line-faint: #EBE2D0;
  --navy: #21303F; --teal: #4A8590; --teal-soft: #DDEAEC;
  --coral: #C36B5A; --coral-soft: #F3DCD4; --coral-deep: #8B2E1F;
  --gold: #B08A4A; --green: #5B7B4F; --red: #8B2A2A;
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;
  --display: 'Cormorant Garamond', serif;
  --sans: 'DM Sans', system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, monospace;
  --r-sm: 4px; --r-md: 10px;
}

/* ============================================================
   PAGE HEAD + BREADCRUMB
   ============================================================ */
.page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-5); padding-bottom: var(--s-3); margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--line-faint);
  flex-wrap: wrap;
}
.page-head h1 {
  font-family: var(--display); font-weight: 500;
  font-size: 30px; line-height: 1.15; color: var(--ink);
}
.page-head .meta { font-size: 12px; color: var(--ink-soft); margin-top: var(--s-1); }
.breadcrumb {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  color: var(--ink-faint); text-transform: uppercase;
  margin-bottom: var(--s-1);
}
.breadcrumb a { color: var(--ink-faint); }
.breadcrumb a:hover { color: var(--coral-deep); }
.action-group { display: flex; align-items: center; gap: var(--s-2); }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--sans);
  font-size: 13px; font-weight: 500; padding: 8px 14px;
  border-radius: var(--r-sm); border: 1px solid transparent;
  cursor: pointer; background: transparent; color: var(--ink);
  transition: all 0.15s; text-decoration: none; white-space: nowrap;
  line-height: 1.3;
}
.btn:hover { color: var(--coral-deep); }
.btn--primary { background: var(--navy); color: var(--paper); border-color: var(--navy); }
.btn--primary:hover { background: var(--coral-deep); border-color: var(--coral-deep); color: var(--paper); }
.btn--secondary { background: var(--paper); color: var(--ink); border-color: var(--line-soft); }
.btn--secondary:hover { border-color: var(--coral); color: var(--coral-deep); }
.btn--small { font-size: 11px; padding: 4px 10px; }
.btn--ghost { padding: 4px 8px; font-size: 12px; }
.btn--danger { color: var(--red); }
.btn--danger:hover { background: var(--red); color: var(--paper); border-color: var(--red); }
/* Solid-red destructive variant — for primary destructive actions
   (Delete setup, etc.) where the operator needs an unambiguous
   "this is dangerous" signal at rest, not just on hover. */
.btn--danger-solid {
  background: var(--red);
  color: var(--paper);
  border-color: var(--red);
}
.btn--danger-solid:hover { background: #a02323; border-color: #a02323; }
.btn--danger-solid:disabled,
.btn--danger-solid[disabled],
.btn--danger-solid[aria-disabled="true"] {
  background: var(--paper-2);
  color: var(--ink-faint);
  border-color: var(--line-soft);
  cursor: not-allowed;
}
.setup-remove-events { display: inline-block; }
.setup-remove-events__menu {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 4px;
  min-width: 220px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  z-index: 20;
  padding: 4px 0;
}
.setup-remove-events__item {
  width: 100%;
  text-align: left;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 12px;
}
.setup-remove-events__item:hover { background: var(--paper-2); }

/* ============================================================
   PILLS
   ============================================================ */
.pill {
  display: inline-block; font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.1em; padding: 2px 8px; border-radius: var(--r-sm);
  text-transform: uppercase; font-weight: 500;
  background: var(--bg); color: var(--ink-soft);
  border: 1px solid var(--line-soft);
}
.pill--draft   { background: var(--bg); color: var(--ink-soft); }
.pill--pending { background: var(--coral-soft); color: var(--coral-deep); border-color: var(--coral); }
.pill--good    { background: rgba(91,123,79,0.12); color: var(--green); border-color: var(--green); }
.pill--bad     { background: rgba(139,42,42,0.10); color: var(--red); border-color: var(--red); }
.pill--info    { background: var(--teal-soft); color: var(--teal); border-color: var(--teal); }
.pill--gold    { background: rgba(176,138,74,0.10); color: var(--gold); border-color: var(--gold); }

/* ============================================================
   CARDS
   ============================================================ */
.card {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s-5);
  color: var(--ink);
  text-decoration: none;
  display: block;
}
a.card { transition: border-color 0.15s, transform 0.15s; }
a.card:hover { border-color: var(--coral); }
.card--sand { background: var(--paper-2); }
.card--danger {
  background: rgba(139, 42, 42, 0.06);
  border-color: rgba(139, 42, 42, 0.35);
}
.card--danger h2,
.card--danger h3,
.card--danger .strong { color: var(--red); }
.card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-3);
}
.card-head h3 { font-family: var(--display); font-weight: 500; font-size: 19px; line-height: 1.2; }

/* ============================================================
   STAT CARDS (with top-left coral accent stripe)
   ============================================================ */
.stat {
  background: var(--paper); border: 1px solid var(--line-soft);
  border-radius: var(--r-md); padding: var(--s-4);
  position: relative;
}
.stat .label {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.15em;
  color: var(--ink-faint); text-transform: uppercase;
  margin-bottom: var(--s-2);
}
.stat .value {
  font-family: var(--display); font-weight: 500;
  font-size: 32px; line-height: 1; color: var(--ink);
}
.stat .meta { font-size: 11px; color: var(--ink-soft); margin-top: 4px; }
.stat .accent {
  position: absolute; top: 0; left: 0;
  width: 24px; height: 3px; background: var(--coral);
  border-top-left-radius: var(--r-md);
}

/* ============================================================
   LAYOUT HELPERS
   ============================================================ */
.row { display: grid; gap: var(--s-3); }
.row-2 { grid-template-columns: 1fr 1fr; }
.row-3 { grid-template-columns: repeat(3, 1fr); }
.row-4 { grid-template-columns: repeat(4, 1fr); }
.row-side-r { grid-template-columns: 1fr 280px; }
.row-side-l { grid-template-columns: 240px 1fr; }
.stack { display: flex; flex-direction: column; gap: var(--s-3); }
.inline { display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; }

/* ============================================================
   TYPOGRAPHY UTILITIES
   ============================================================ */
.muted { color: var(--ink-faint); }
.soft  { color: var(--ink-soft); }
.strong { font-weight: 500; }
.mono { font-family: var(--mono); font-size: 11px; }
.num  { font-variant-numeric: tabular-nums; }
.text-right { text-align: right; }
.section-label {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  color: var(--ink-faint); text-transform: uppercase;
  font-weight: 600; margin-bottom: var(--s-2);
}

/* ============================================================
   SPACING UTILITIES (mirror wireframe)
   ============================================================ */
.mt-1 { margin-top: var(--s-1); } .mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); } .mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); } .mt-6 { margin-top: var(--s-6); }
.mb-1 { margin-bottom: var(--s-1); } .mb-2 { margin-bottom: var(--s-2); }
.mb-3 { margin-bottom: var(--s-3); } .mb-4 { margin-bottom: var(--s-4); }
.mb-5 { margin-bottom: var(--s-5); } .mb-6 { margin-bottom: var(--s-6); }

/* ============================================================
   TABLES
   ============================================================ */
table.v2 { width: 100%; border-collapse: collapse; font-size: 13px; color: var(--ink); }
table.v2 th {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em;
  color: var(--ink-faint); text-transform: uppercase; font-weight: 600;
  text-align: left; padding: 8px 12px;
  background: var(--paper-2); border-bottom: 1px solid var(--line-soft);
}
table.v2 td { padding: 10px 12px; border-bottom: 1px solid var(--line-faint); vertical-align: top; }
table.v2 tr:last-child td { border-bottom: none; }
table.v2 tbody tr:hover { background: var(--paper-2); cursor: pointer; }

/* Wireframe alias for table.v2 — wireframes/local_v2_app.html uses
   `<table class="v2-table">` while the live convention is class="v2".
   Aliased here so wireframe-faithful ports of cards stay terse. */
table.v2-table { width: 100%; border-collapse: collapse; font-size: 13px; color: var(--ink); }
table.v2-table th {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em;
  color: var(--ink-faint); text-transform: uppercase; font-weight: 600;
  text-align: left; padding: 8px 12px;
  background: var(--paper-2); border-bottom: 1px solid var(--line-soft);
}
table.v2-table td { padding: 10px 12px; border-bottom: 1px solid var(--line-faint); vertical-align: top; }
table.v2-table tr:last-child td { border-bottom: none; }
table.v2-table tbody tr:hover { background: var(--paper-2); }

/* `<th class="text-right">` was getting overridden by the base
   table.v2 / table.v2-table th rule (text-align: left), so numeric
   headers ended up left-aligned while their <td>s were right-aligned —
   visually disconnected. Bump specificity so the class wins. */
table.v2 th.text-right,
table.v2-table th.text-right { text-align: right; }
table.v2 th.text-center,
table.v2-table th.text-center { text-align: center; }

/* =====================================================================
   SEATING SHELL — floor-plan-app Slice C.2-full
   Ports the wireframe's #page-seating chrome (lines 1300-1376) to v2.
   - .seating-shell — 1fr + 300px grid wrapping the canvas + rail
   - .mode-seating modifier on the shell flips toolbar buttons +
     content blocks between Layout and Seating modes (operator-driven
     via the mode tabs, server-rendered with active CSS class).
   - The drag/resize/rotate JS targets #seating-canvas by id, which
     lives unchanged inside the new shell — no JS preservation work
     needed in C.2-full (full pass in C.5).
   ===================================================================== */
.seating-shell {
  display: grid;
  grid-template-columns: 1fr 300px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}
.seating-toolbar {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-soft);
  flex-wrap: wrap;
}
.tool-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--r-sm);
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  color: var(--ink-soft);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1;
}
.tool-btn:hover:not(:disabled) {
  background: var(--paper);
  border-color: var(--line-soft);
  color: var(--coral-deep);
}
.tool-btn.active {
  background: var(--coral-soft);
  color: var(--coral-deep);
  border-color: var(--coral);
}
.tool-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.tool-btn.label-btn {
  width: auto;
  padding: 0 var(--s-3);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink);
  background: var(--paper);
  border-color: var(--line-soft);
  white-space: nowrap;
  height: 30px;
}
.tool-btn.label-btn:hover:not(:disabled) {
  background: var(--coral-soft);
  border-color: var(--coral);
  color: var(--coral-deep);
}
.tool-sep {
  width: 1px;
  height: 16px;
  background: var(--line-soft);
  margin: 0 4px;
}
.toolbar-stats {
  margin-left: auto;
  display: flex;
  gap: var(--s-4);
  align-items: center;
}
.toolbar-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.toolbar-stat .num {
  font-family: var(--display);
  font-weight: 500;
  font-size: 20px;
}
.toolbar-stat .label {
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: 0.15em;
  color: var(--ink-faint);
  text-transform: uppercase;
}
.canvas-wrap {
  position: relative;
  background: var(--paper-2);
  overflow: auto;
}
.seating-rail {
  background: var(--paper);
  border-left: 1px solid var(--line-soft);
  overflow-y: auto;
  min-height: 520px;
  min-width: 0;
  /* Long monospace tokens (file paths, slice names) in placeholder copy
     overflow the 300px column otherwise — .seating-shell has overflow:
     hidden so they get clipped instead of wrapping. */
  overflow-wrap: anywhere;
}
/* Same min-width: 0 dance on the left grid item so its canvas-wrap can
   shrink narrower than its intrinsic content (long toolbar without
   wrap, e.g.) instead of forcing the rail off-screen. */
.seating-shell > div:first-child { min-width: 0; }

/* Mode-aware visibility — the .mode-seating CSS class on the shell
   wrapper drives which toolbar buttons + stats + main-area content
   are visible. Server-rendered: route attaches the class based on
   resolved mode; client doesn't toggle it (each Layout/Seating tab
   click is a full page nav per C.2-lite).

   Rules are split by element type so `[data-layout-mode]` and
   `[data-seating-mode]` wrappers fall back to `display: block` (their
   natural intrinsic) instead of inheriting a flex value meant for
   .toolbar-stat. Earlier version of this rule block forced
   `display: flex` on any [data-layout-mode] element, which made the
   rail's tab-strip wrapper into a flex container — its .rail-tabs
   child collapsed to minimum width and each tab text broke one
   letter per line. */

/* Toolbar buttons — inline-flex when their mode matches, hidden
   otherwise. */
.seating-shell .tool-btn.layout-only { display: inline-flex; }
.seating-shell .tool-btn.seating-only { display: none; }
.seating-shell.mode-seating .tool-btn.layout-only { display: none; }
.seating-shell.mode-seating .tool-btn.seating-only { display: inline-flex; }

/* Same mode-aware visibility for FORM wrappers — the Auto-seat
   button lives inside a <form> with .seating-only, so the
   .tool-btn rule above can't hide it on layout mode. Without
   this rule the form leaks through. */
.seating-shell form.layout-only { display: inline-block; }
.seating-shell form.seating-only { display: none; }
.seating-shell.mode-seating form.layout-only { display: none; }
.seating-shell.mode-seating form.seating-only { display: inline-block; }

/* Toolbar separators — match the button display semantics. */
.seating-shell .tool-sep.layout-only { display: inline-block; }
.seating-shell .tool-sep.seating-only { display: none; }
.seating-shell.mode-seating .tool-sep.layout-only { display: none; }
.seating-shell.mode-seating .tool-sep.seating-only { display: inline-block; }

/* Stats blocks — flex (column layout for num + label). */
.seating-shell .toolbar-stat.layout-only { display: flex; }
.seating-shell .toolbar-stat.seating-only { display: none; }
.seating-shell.mode-seating .toolbar-stat.layout-only { display: none; }
.seating-shell.mode-seating .toolbar-stat.seating-only { display: flex; }

/* Mode-only content WRAPPERS (rail sections, etc.) — block-level
   blocks. Distinct from the toolbar elements above; we never want
   to coerce them to a flex value. */
.seating-shell [data-layout-mode] { display: block; }
.seating-shell [data-seating-mode] { display: none; }
.seating-shell.mode-seating [data-layout-mode] { display: none; }
.seating-shell.mode-seating [data-seating-mode] { display: block; }

/* Seating page Layout/Seating mode toggle pill. Ported from the
   wireframe (#page-seating, .canvas-mode-tabs at lines 1428-1454).
   Floor-plan-app Slice C.2-lite: lands the operator-visible toggle
   ahead of the full shell rebuild so /events/{id}/seating has a
   visible mode-switch instead of requiring URL-typing of ?mode=.
   C.2-full moves the pill INSIDE the canvas-wrap as an absolute-
   positioned floating element per the wireframe. */
.canvas-mode-tabs {
  position: absolute;
  top: var(--s-3);
  left: 50%;
  transform: translateX(-50%);
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  padding: 3px;
  display: flex;
  gap: 2px;
  box-shadow: 0 1px 3px rgba(31,27,24,0.06);
  z-index: 10;
}
.canvas-mode-tabs a,
.canvas-mode-tabs .canvas-mode-tab--disabled {
  padding: 5px var(--s-3);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  border-radius: 3px;
  font-weight: 600;
  cursor: pointer;
  line-height: 1;
  display: inline-block;
}
.canvas-mode-tabs a.active { background: var(--navy); color: var(--paper); }
.canvas-mode-tabs a:not(.active):hover { background: var(--paper-2); }
.canvas-mode-tabs .canvas-mode-tab--disabled { opacity: 0.45; cursor: not-allowed; }

/* D-ruler.3 — show/hide the calibration reference line on the
   canvas. Default visible; the Canvas-tab checkbox toggles
   .hide-calibration-ruler on the canvas wrap via localStorage. */
#seating-canvas-wrap.hide-calibration-ruler .calibration-ruler { display: none; }

/* Grid pattern toggle — the ▦ toolbar button flips this class. */
#seating-canvas-wrap.hide-grid .grid-rect { display: none; }
#grid-btn.active { background: var(--paper-2); }
#pan-btn.active { background: var(--paper-2); }
#affix-bg-btn.active { background: var(--paper-2); }

/* Background-mode primitives (aerial-trace shapes) — paint behind
   tables + regular primitives, and click-passthrough by default so
   the operator can place things on top of them. The 🔓 toolbar
   button toggles .bg-select on the wrap to re-enable interaction. */
.bg-primitive { pointer-events: none; }
#seating-canvas-wrap.bg-select .bg-primitive { pointer-events: auto; }
#bg-select-toggle.active { background: var(--paper-2); }

/* Canvas drop-target highlight when the operator drags an unseated
   guest over a seating table. The .drag-over class is added by the
   seating-mode dragover handler and removed on dragleave/drop. */
.seating-table.drag-over .table-shape {
  stroke: var(--teal) !important;
  stroke-width: 3 !important;
}
/* While the 🎨 drawing tool is armed, hide any existing background
   primitives so the operator can trace directly on the underlying
   image without their previous sculpting fighting for visibility. */
#seating-canvas-wrap.drawing-active .bg-primitive { display: none; }

/* Shape palette for the 🎨 drawing tool — class-toggled by JS so
   inline-style display rules can't fight it. Hidden by default;
   .is-armed flips to flex. !important defeats any future visibility
   rule (`.layout-only` etc.) from accidentally winning. */
.draw-bg-palette { display: none !important; }
.draw-bg-palette.is-armed { display: flex !important; }

/* Canvas legend overlay — shares the 🔒/🔓 gate with bg-primitives.
   Default: click-passthrough so the operator can drop shapes under
   it. Unlocked: cursor changes to grab + the JS drag handler kicks
   in. The inner <text> elements always set pointer-events: none so
   text-selection drag doesn't fight the legend-move drag. */
.canvas-legend { pointer-events: none; }
#seating-canvas-wrap.bg-select .canvas-legend { pointer-events: auto; cursor: grab; }
#seating-canvas-wrap.bg-select .canvas-legend:active { cursor: grabbing; }

/* D-ruler.4 — endpoint handles + length pill are only interactive
   while the ⤧ Scale tool is armed. JS adds .editable to the ruler
   group when the toolbar button has .active. */
.calibration-ruler .ruler-handle-hit { cursor: default; }
.calibration-ruler .ruler-length-label { cursor: default; }
.calibration-ruler.editable .ruler-handle-hit { cursor: grab; }
.calibration-ruler.editable .ruler-length-label { cursor: text; }

/* Chunk B — right-click shape context popover. Floats over the
   canvas at the cursor; auto-saves field changes on input
   (debounced) to the existing /position / /move JSON endpoints. */
.shape-context-popover {
  position: absolute;
  z-index: 20;
  width: 240px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  box-shadow: 0 8px 24px rgba(31, 27, 24, 0.15);
  padding: var(--s-3);
  display: none;
}
.shape-context-popover.is-open { display: block; }
.shape-context-popover__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--s-2);
}
.shape-context-popover__row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 0;
  font-size: 12px;
}
.shape-context-popover__row .prop-label { width: 54px; }
.shape-context-popover__row .input {
  flex: 1;
  min-width: 0;
  padding: 4px 6px;
  font-size: 12px;
  height: 26px;
}
.shape-context-popover__row .input.is-locked,
.shape-context-popover__row .input:disabled {
  background: var(--bg-faint, #f3f0eb);
  color: var(--text-soft, #888);
  cursor: not-allowed;
}

/* Chunk (6d) — thumbnail mini-gallery in the popover. */
.shape-context-popover__thumbs {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--line-faint);
}
.shape-context-popover__thumbs-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: 11px;
  margin-bottom: 4px;
}
.shape-context-popover__thumb-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  min-height: 20px;
}
.shape-context-popover__thumb-chip {
  position: relative;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  overflow: hidden;
  background: var(--paper);
  padding: 0;
  cursor: zoom-in;
}
.shape-context-popover__thumb-chip img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.shape-context-popover__thumb-chip__remove {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--coral-deep);
  color: white;
  border: 1px solid white;
  font-size: 9px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: none;
}
.shape-context-popover__thumb-chip__warn {
  position: absolute;
  top: -6px;
  left: -6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--coral-deep, #c0392b);
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  line-height: 16px;
  text-align: center;
  text-decoration: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  z-index: 2;
}
.shape-context-popover__thumb-chip__warn:hover { transform: scale(1.15); }
.shape-context-popover__thumb-chip.is-over-assigned {
  outline: 1px solid var(--coral-deep, #c0392b);
}
.shape-context-popover__thumb-chip:hover .shape-context-popover__thumb-chip__remove {
  display: block;
}
.shape-context-popover__thumb-picker {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
}

/* =====================================================================
   SEATING RAIL PANELS — floor-plan-app Slice C.4
   Dual stacked tab groups inside the .seating-rail (top + bottom),
   ported from the wireframe (lines 1467-1531). Each group is
   independent: top group is Shape/Style/Canvas (Layout-mode only) or
   a single seat-prompt card (Seating mode); bottom group is Library
   /Venues/Guests in both modes.
   ===================================================================== */
.rail-tabs {
  display: flex;
  border-bottom: 1px solid var(--line-soft);
  background: var(--paper-2);
}
.rail-tab {
  flex: 1;
  padding: 9px 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
  text-transform: uppercase;
  cursor: pointer;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  font-weight: 600;
}
.rail-tab.active {
  color: var(--coral-deep);
  border-bottom-color: var(--coral);
  background: var(--paper);
}
.rail-tab:hover:not(.active) {
  color: var(--ink-soft);
}
.rail-section { padding: var(--s-3); }
.rail-section + .rail-section { border-top: 1px solid var(--line-faint); }
.rail-panel { display: none; }
.rail-panel.active { display: block; }
/* The .seating-shell [data-layout-mode] mode-gate rule (above) has
   higher specificity than .rail-panel, so it forces every layout-
   mode panel to display: block — which broke tab switching once two
   layout-mode panels (Canvas + Items) shared the bottom group. This
   override re-asserts the inactive-panel hide for layout-mode AND
   seating-mode panels without affecting the cross-mode gating. */
[data-layout-mode].rail-panel:not(.active),
[data-seating-mode].rail-panel:not(.active) { display: none; }
.rail-search {
  width: 100%;
  padding: 6px 8px;
  font-size: 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper);
  margin-bottom: var(--s-2);
  font-family: var(--sans);
}

/* Library category sections — collapsible via <details>. First
   section auto-expanded; others closed by default. */
.lib-cat { margin-bottom: var(--s-3); }
.lib-cat > summary {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
  text-transform: uppercase;
  padding: 2px 4px;
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.lib-cat > summary::-webkit-details-marker { display: none; }
.lib-cat > summary::before {
  content: "▾";
  font-size: 8px;
  color: var(--ink-soft);
  transition: transform 120ms;
}
.lib-cat:not([open]) > summary::before { transform: rotate(-90deg); }
.lib-cat-count {
  font-size: 9px;
  color: var(--ink-faint);
  margin-left: auto;
  font-weight: 400;
  letter-spacing: 0;
}
.lib-cat-items {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.lib-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px;
  border-radius: var(--r-sm);
  cursor: grab;
  font-size: 10px;
  line-height: 1.2;
  text-align: center;
  color: var(--ink-soft);
  background: var(--paper);
  border: 1px solid var(--line-faint);
  transition: all 120ms;
}
.lib-item:hover {
  background: var(--paper-2);
  border-color: var(--teal);
  color: var(--navy);
}
.lib-item:active { cursor: grabbing; }
.lib-thumb {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper-2);
  border: 1px dashed var(--line-soft);
  border-radius: 2px;
  color: var(--ink-faint);
  font-size: 14px;
}
.lib-seats {
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: 0.05em;
  color: var(--ink-faint);
}

.items-rows { display: flex; flex-direction: column; gap: 4px; }
.items-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-faint);
  background: var(--paper);
  cursor: grab;
  font-size: 11px;
}
.items-row:hover { background: var(--paper-2); border-color: var(--teal); }
.items-row:active { cursor: grabbing; }
.items-row.is-over-assigned {
  border-color: var(--coral-deep, #c0392b);
  background: var(--coral-soft);
}
.items-row.is-nested {
  margin-left: 16px;
  border-left: 2px solid var(--line-faint);
  padding-left: 8px;
}
.items-row__grip {
  flex: 0 0 12px;
  font-family: monospace;
  font-size: 11px;
  line-height: 1;
  color: var(--ink-faint);
  cursor: grab;
  user-select: none;
  letter-spacing: -2px;
}
.items-row__grip:active { cursor: grabbing; }
.lib-cat.is-setup-group > summary {
  display: flex;
  align-items: center;
  gap: 6px;
}
.lib-cat.is-setup-group > summary[draggable="true"] {
  cursor: grab;
}
.lib-cat.is-setup-group > summary[draggable="true"]:active { cursor: grabbing; }
.is-setup-children { margin-left: 4px; }
.items-row__fraction.is-over {
  color: var(--coral-deep, #c0392b);
  font-weight: bold;
}
.items-row__warn-icon { margin-right: 2px; }
.items-row__warn {
  font-size: 9px;
  font-style: italic;
  color: var(--coral-deep, #c0392b);
  margin-top: 2px;
}
.items-row__bump.is-prompt {
  background: var(--coral-deep, #c0392b);
  color: #fff;
  border-color: var(--coral-deep, #c0392b);
  animation: items-bump-pulse 1.5s ease-in-out infinite;
}
.items-row__bump.is-prompt:hover {
  background: var(--coral, #d35a4a);
  color: #fff;
}
@keyframes items-bump-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

/* Cart-group flash when a Proposal is sent. The send_to_client redirect
   bounces the operator to /events/{id}?highlight=<key>#carts; the JS in
   detail.html adds .is-just-sent to the matching [data-cart-anchor] so
   the affected vendor row visibly registers the PROPOSAL_SENT badge
   landing. Auto-removed after ~3.5s. */
[data-cart-anchor].is-just-sent {
  animation: cart-anchor-flash 3.4s ease-out 1;
  border-radius: var(--r-md, 6px);
}
@keyframes cart-anchor-flash {
  0%   { background: rgba(195, 107, 90, 0.32); box-shadow: 0 0 0 3px rgba(195, 107, 90, 0.6); }
  20%  { background: rgba(195, 107, 90, 0.22); box-shadow: 0 0 0 3px rgba(195, 107, 90, 0.45); }
  100% { background: transparent; box-shadow: 0 0 0 0 transparent; }
}
.items-row__thumb {
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  border-radius: 2px;
  background: var(--paper-2);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.items-row__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.items-row__body { flex: 1; min-width: 0; }
.items-row__name {
  font-weight: 500;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.items-row__sub {
  font-size: 9px;
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  align-items: center;
}
.items-row__bump {
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  border: 1px solid var(--line-soft);
  background: var(--paper);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: var(--coral-deep, #c0392b);
}
.items-row__bump:hover {
  background: var(--coral-soft);
  border-color: var(--coral-deep, #c0392b);
}
.items-row.is-drag-target { background: var(--coral-soft); }

/* ============================================================ */
/* BUDGET BAR — restored from the prior cart UI, ported in       */
/* parallel to wireframes/portal_v3_mockup.html#cart so the      */
/* portal and local look match.                                  */
/* Segments (left → right): paid · committed (extra) ·            */
/* estimated (extra) · remainder. Over-budget overflows to a     */
/* coral segment past 100%.                                      */
/* ============================================================ */
.budget-bar-card {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s-4);
  margin-bottom: var(--s-4);
}
.budget-bar-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--s-3);
  flex-wrap: wrap;
  gap: var(--s-2);
}
.budget-bar-card__head h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 20px;
  color: var(--navy);
}
.budget-bar {
  position: relative;
  height: 28px;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  overflow: hidden;
  display: flex;
}
.budget-bar__seg { height: 100%; position: relative; }
.budget-bar__seg.paid       { background: var(--green); }
.budget-bar__seg.committed  { background: var(--teal); }
.budget-bar__seg.estimated  {
  background: repeating-linear-gradient(
    45deg,
    var(--gold) 0,
    var(--gold) 6px,
    rgba(176, 138, 74, 0.6) 6px,
    rgba(176, 138, 74, 0.6) 12px
  );
}
.budget-bar__seg.over-budget {
  background: var(--coral-deep);
  border-left: 2px solid var(--red);
}
.budget-bar::before,
.budget-bar::after {
  content: ''; position: absolute; top: 0; bottom: 0;
  width: 1px; background: rgba(255, 255, 255, 0.35);
  pointer-events: none;
}
.budget-bar::before { left: 50%; }
.budget-bar::after  { left: 75%; }
.budget-bar-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-3);
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-faint);
}
@media (max-width: 600px) {
  .budget-bar-stats { grid-template-columns: 1fr 1fr; }
}
.budget-bar-stats__cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.budget-bar-stats__label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.15em;
  color: var(--ink-faint);
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.budget-bar-stats__label::before {
  content: ''; width: 10px; height: 10px; border-radius: 2px;
  border: 1px solid var(--line-soft);
}
.budget-bar-stats__cell.paid .budget-bar-stats__label::before      { background: var(--green); border-color: var(--green); }
.budget-bar-stats__cell.committed .budget-bar-stats__label::before { background: var(--teal); border-color: var(--teal); }
.budget-bar-stats__cell.estimated .budget-bar-stats__label::before {
  background: repeating-linear-gradient(45deg, var(--gold) 0 3px, rgba(176, 138, 74, 0.6) 3px 6px);
  border-color: var(--gold);
}
.budget-bar-stats__cell.budget .budget-bar-stats__label::before    { background: var(--paper-2); }
.budget-bar-stats__value {
  font-family: var(--display);
  font-weight: 500;
  font-size: 22px;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.budget-bar-stats__value.is-pending {
  font-style: italic;
  color: var(--ink-faint);
  font-size: 18px;
}
.budget-bar-stats__sub {
  font-size: 11px;
  color: var(--ink-soft);
}

.unit-toggle-opt {
  border: 1px solid var(--line-soft);
  background: var(--paper);
  margin-left: -1px;
}
.unit-toggle-opt:first-of-type {
  margin-left: 0;
  border-top-left-radius: var(--r-sm);
  border-bottom-left-radius: var(--r-sm);
}
.unit-toggle-opt:last-of-type {
  border-top-right-radius: var(--r-sm);
  border-bottom-right-radius: var(--r-sm);
}
.unit-toggle-opt.is-active {
  background: var(--coral);
  color: #fff;
  border-color: var(--coral);
  z-index: 1;
}
.items-row__fixed-pill {
  display: inline-block;
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: 0.1em;
  padding: 1px 4px;
  margin-left: 4px;
  background: var(--teal);
  color: #fff;
  border-radius: 2px;
  vertical-align: middle;
}

.rental-modal-head-thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-soft);
  background: var(--paper-2);
  flex: 0 0 64px;
  cursor: zoom-in;
}

.rental-modal-siblings { margin: var(--s-2) 0; }
.rental-modal-siblings__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.rental-modal-siblings__chip {
  width: 40px;
  height: 40px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--paper-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.rental-modal-siblings__chip img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rental-modal-siblings__chip-noimg {
  font-size: 16px;
  color: var(--ink-faint);
}

/* Venue tile (rail-side, bottom Venues panel) */
.venue-tile {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 8px;
  padding: 6px;
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
  background: var(--paper);
  margin-bottom: 6px;
  align-items: center;
}
.venue-tile.active {
  border-color: var(--coral);
  box-shadow: 0 0 0 2px rgba(195,107,90,0.15);
}
.venue-tile-thumb {
  width: 56px;
  height: 42px;
  background: var(--paper-2);
  border: 1px solid var(--line-faint);
  border-radius: 2px;
}
.venue-tile-name {
  font-size: 11px;
  color: var(--navy);
  font-weight: 500;
  line-height: 1.2;
}
.venue-tile-meta {
  font-size: 9px;
  color: var(--ink-faint);
  font-family: var(--mono);
  margin-top: 2px;
}

/* Property rows (Shape / Style / Canvas tabs in Layout mode) */
.prop-row {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 0;
  font-size: 12px;
  flex-wrap: wrap;
}
.prop-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  width: 60px;
  flex-shrink: 0;
  text-transform: uppercase;
}
.prop-input {
  padding: 3px 6px;
  font-size: 12px;
  height: 26px;
  min-width: 0;
  flex: 1 1 auto;
}
.prop-input[type="range"] { height: auto; padding: 0; }

/* Unseated-guest row in the bottom Guests panel — cursor:grab in
   Seating mode. Drag wiring lands in C.5d (Guest → table-card drop). */
.unseated-guest {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  border: 1px dashed var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper);
  margin-bottom: 4px;
  font-size: 11px;
}
.seating-shell.mode-seating .unseated-guest { cursor: grab; }
.seating-shell.mode-seating .unseated-guest:active { cursor: grabbing; }
.unseated-guest .grip {
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: 10px;
  flex-shrink: 0;
}

/* Drop-target highlight on table cards while a guest is being
   dragged over. .drag-over is added by the C.5d JS in dragenter and
   removed in dragleave / drop. */
.table-drop-target.drag-over {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
  background: rgba(74, 133, 144, 0.04);
}
.table-drop-target.is-full {
  opacity: 0.65;
}

/* ============================================================
   FORM FIELDS
   ============================================================ */
.field { display: block; margin-bottom: var(--s-3); }
.field .label {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  color: var(--ink-faint); text-transform: uppercase; font-weight: 600;
  display: block; margin-bottom: 4px;
}
.field .label .req { color: var(--coral); }
.input {
  width: 100%; padding: 9px 12px;
  border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  background: var(--paper-2); font-size: 13px; color: var(--ink);
  font-family: var(--sans);
  transition: border-color 0.15s, background 0.15s;
}
.input:focus { outline: none; border-color: var(--coral); background: var(--paper); }
.input::placeholder { color: var(--ink-faint); }
.help { font-size: 11px; color: var(--ink-faint); margin-top: 4px; }

/* ============================================================
   CHIPS, TABS, FILTERS
   ============================================================ */
.chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; padding: 2px 8px;
  background: var(--teal-soft); color: var(--teal);
  border-radius: 999px;
  border: 1px solid rgba(74,133,144,0.25);
}
.chip--ghost { background: transparent; color: var(--ink-soft); border-color: var(--line-soft); }

/* Pill-style boolean checkbox used in venue detail edit mode — wraps
   a native <input type="checkbox"> + label so the operator can toggle
   policy / staff / amenities flags inline. The .checked modifier is
   applied via JS on change so the chip itself looks "selected" rather
   than relying on the native checkbox affordance. */
.region-check {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  background: var(--paper); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  font-size: 12px; cursor: pointer; user-select: none;
}
/* `:has(input:checked)` reads live state so the checked styling stays
   in sync with the checkbox without a JS toggle. Modern browsers
   only — the operator runs Chromium-based browsers on Windows so this
   is fine for the local-only tool. `.checked` is kept as a static
   fallback for templates that pre-render the selected state server-side. */
.region-check:has(input:checked),
.region-check.checked {
  background: var(--teal-soft); border-color: var(--teal); color: var(--teal);
  font-weight: 500;
}
.region-check input[type="checkbox"] { accent-color: var(--teal); }

.tabs {
  display: flex; gap: var(--s-1);
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: var(--s-4);
}
.tab {
  padding: 8px var(--s-3); font-size: 13px; color: var(--ink-soft);
  border-bottom: 2px solid transparent; cursor: pointer; margin-bottom: -1px;
}
.tab:hover { color: var(--coral-deep); }
.tab.active { color: var(--ink); border-bottom-color: var(--coral); font-weight: 500; }
.tab .count { margin-left: 4px; font-family: var(--mono); font-size: 10px; color: var(--ink-faint); }

.tab-bar {
  display: inline-flex; background: var(--paper-2);
  border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  padding: 3px; gap: 2px;
}
.tab-bar a {
  padding: 5px 14px; font-size: 12px; font-weight: 500;
  color: var(--ink-soft); border-radius: 3px; cursor: pointer;
}
.tab-bar a.active { background: var(--navy); color: var(--paper); }

.filter-bar {
  display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
  padding: var(--s-3); background: var(--paper-2);
  border: 1px solid var(--line-soft); border-radius: var(--r-md);
  margin-bottom: var(--s-3);
}
.filter-select {
  background: var(--paper); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: 7px 10px;
  font-size: 12px; cursor: pointer; font-family: var(--sans);
}

/* ============================================================
   EMPTY STATE
   ============================================================ */
.empty {
  text-align: center; padding: var(--s-7) var(--s-5);
  background: var(--paper-2); border: 1px dashed var(--line-soft);
  border-radius: var(--r-md);
}
.empty .glyph {
  font-family: var(--display); font-size: 36px; color: var(--ink-faint);
  margin-bottom: var(--s-3); display: inline-block;
  width: 56px; height: 56px; line-height: 56px;
  border-radius: 50%; background: var(--paper); border: 1px solid var(--line-soft);
}
.empty h3 { font-family: var(--display); font-weight: 500; font-size: 20px; margin-bottom: var(--s-1); }
.empty p { color: var(--ink-soft); font-size: 13px; max-width: 42ch; margin: 0 auto var(--s-3); }

/* ============================================================
   PENDING-ITEM ROW (used on /pending list)
   Class is referenced in the wireframe but its CSS was missing —
   defined here based on the visual intent of the markup.
   ============================================================ */
.proposal-row {
  display: grid;
  grid-template-columns: 1fr auto auto 16px;
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line-faint);
  text-decoration: none;
  color: var(--ink);
  background: var(--paper);
  transition: background 0.12s;
}
.proposal-row:last-child { border-bottom: none; }
.proposal-row:hover { background: var(--paper-2); }
.proposal-row .ptitle { font-size: 14px; font-weight: 500; color: var(--ink); }
.proposal-row .pvendor { font-size: 11px; color: var(--ink-soft); margin-top: 2px; }
.proposal-row .ptotal {
  font-family: var(--display); font-weight: 500; font-size: 17px;
  color: var(--ink); text-align: right; line-height: 1.1;
}
.proposal-row .ptotal .meta {
  display: block; font-family: var(--mono); font-size: 9px;
  letter-spacing: 0.12em; color: var(--ink-faint);
  text-transform: uppercase; font-weight: 500; margin-top: 2px;
}

/* ============================================================
   APP SHELL — sidebar + main (replaces base.html's Tailwind shell)
   ============================================================ */
body.v2-shell {
  display: flex; min-height: 100vh; overflow: hidden;
  font-family: var(--sans); background: var(--bg); color: var(--ink);
  font-size: 14px; line-height: 1.5;
}
/* Embedded mode (?embedded=1) — sidebar is omitted from the markup,
   main grows to fill the iframe. Used by the custom-primitive editor
   modal launched from the seating-canvas rail. */
body.v2-shell--embedded { background: var(--paper); }
body.v2-shell--embedded .v2-main {
  width: 100%; padding: var(--s-3) var(--s-4);
}
.v2-sidebar {
  background: var(--paper-2);
  border-right: 1px solid var(--line-soft);
  padding: var(--s-5) 0;
  width: 230px; flex-shrink: 0;
  overflow-y: auto; height: 100vh;
  display: flex; flex-direction: column;
}
.v2-sidebar .brand {
  padding: 0 var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--line-faint);
  margin-bottom: var(--s-3);
}
.v2-sidebar .brand h1 {
  font-family: var(--display); font-weight: 500;
  font-size: 22px; letter-spacing: 0.02em; line-height: 1.1;
  color: var(--ink);
}
.v2-sidebar .brand .sub {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.22em;
  color: var(--ink-faint); text-transform: uppercase; margin-top: 2px;
}
.v2-sidebar .nav-section { padding: var(--s-3) 0 var(--s-1); }
.v2-sidebar .nav-label {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em;
  color: var(--ink-faint); text-transform: uppercase;
  padding: 0 var(--s-5) var(--s-2); font-weight: 600;
}
.v2-sidebar .nav-item {
  display: flex; align-items: center; gap: var(--s-2);
  padding: 7px var(--s-5); font-size: 13px; color: var(--ink);
  cursor: pointer; border-left: 3px solid transparent;
  transition: background 0.15s, border-color 0.15s;
  text-decoration: none;
}
.v2-sidebar .nav-item:hover { background: var(--bg); }
.v2-sidebar .nav-item.active {
  background: var(--paper); border-left-color: var(--coral);
  font-weight: 500;
}
.v2-sidebar .nav-item .count {
  margin-left: auto; font-family: var(--mono);
  font-size: 10px; color: var(--ink-faint);
  background: var(--bg); padding: 1px 5px; border-radius: var(--r-sm);
}
.v2-sidebar .nav-item.active .count { background: var(--coral-soft); color: var(--coral-deep); }
.v2-sidebar .nav-icon {
  width: 12px; height: 12px;
  border: 1.5px solid currentColor;
  flex-shrink: 0;
}
.v2-sidebar .nav-icon.circle { border-radius: 50%; }
.v2-sidebar .nav-icon.diamond { transform: rotate(45deg); }
.v2-sidebar .sidebar-footer {
  padding: var(--s-3) var(--s-5); margin-top: auto;
  border-top: 1px solid var(--line-faint);
  font-size: 11px; color: var(--ink-faint);
  display: flex; align-items: center; gap: var(--s-2);
}
.v2-sidebar .sidebar-footer .avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--coral-soft); border: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--display); color: var(--coral-deep); font-size: 13px;
}

.v2-main {
  flex: 1; padding: var(--s-5) var(--s-6);
  background: var(--paper); overflow-y: auto; height: 100vh;
}

/* ============================================================
   AUDIT-ROW TONES — slice 23
   One class per tone; applies left-border + text color in one
   declaration so the audit macro stays declarative and never
   needs Tailwind utility classes. Tokens are pulled from :root.
   ============================================================ */
.audit-row {
  border-left: 2px solid var(--line-soft);
  padding: 2px 0 2px var(--s-2);
  list-style: none;
}
.audit-row .audit-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex-shrink: 0;
  font-size: 12px; line-height: 1;
}
.audit-row--navy   { border-left-color: var(--navy); }
.audit-row--navy   .audit-text, .audit-row--navy   .audit-glyph { color: var(--ink); }
.audit-row--teal   { border-left-color: var(--teal); }
.audit-row--teal   .audit-text, .audit-row--teal   .audit-glyph { color: var(--teal); }
.audit-row--coral  { border-left-color: var(--coral); }
.audit-row--coral  .audit-text, .audit-row--coral  .audit-glyph { color: var(--coral-deep); }
.audit-row--green  { border-left-color: var(--green); }
.audit-row--green  .audit-text, .audit-row--green  .audit-glyph { color: var(--green); }
.audit-row--red    { border-left-color: var(--red); }
.audit-row--red    .audit-text, .audit-row--red    .audit-glyph { color: var(--red); }
.audit-row--muted  { border-left-color: var(--line-soft); }
.audit-row--muted  .audit-text, .audit-row--muted  .audit-glyph { color: var(--ink-soft); }

/* ============================================================
   MODAL PRIMITIVE — slice 23
   Single v2 pattern for every dialog. Toggle .is-open on the
   .modal-overlay element to show/hide; the existing template
   JS already uses classList.toggle on the modal wrapper.
   ============================================================ */
.modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(33, 48, 63, 0.6); /* same hue as --navy at 60% alpha */
  display: none;
  align-items: center; justify-content: center;
  padding: var(--s-4);
}
.modal-overlay.is-open { display: flex; }
/* Legacy compat: existing JS uses .hidden + classList.remove('hidden')
   on the wrapper. Treat absence of .is-open + .hidden the same. */
.modal-overlay:not(.is-open) { display: none; }
.modal-body {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: 0 20px 50px rgba(31, 27, 24, 0.25);
  max-width: 520px; width: 100%;
  max-height: 90vh; overflow-y: auto;
  padding: var(--s-5);
}
.modal-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-3);
}
.modal-head h2 {
  font-family: var(--display); font-weight: 500; font-size: 22px;
  line-height: 1.2; color: var(--ink);
}
.modal-footer {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--s-3); padding-top: var(--s-3); margin-top: var(--s-3);
  border-top: 1px solid var(--line-faint);
}
.modal-footer.has-split { justify-content: space-between; }

/* ============================================================
   TABLEWARE ANATOMY MODAL — docket #3 polish
   Custom Tableware-identity chrome (navy header strip + 960px
   body + floating bottom-right trigger button). The wrapper uses
   the standard .modal-overlay primitive; only the body chrome
   diverges from the default 520px dialog shape.
   ============================================================ */
.tw-anatomy-trigger {
  position: fixed;
  bottom: var(--s-5); right: var(--s-5);
  z-index: 40;
  width: 48px; height: 48px;
  border-radius: 50%;
  background: var(--navy); color: var(--paper);
  border: 2px solid var(--coral);
  box-shadow: 0 8px 24px rgba(31, 27, 24, 0.25);
  font-family: var(--display);
  font-style: italic; font-size: 22px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: transform 120ms;
}
.tw-anatomy-trigger:hover { transform: scale(1.05); }

.tw-anatomy-header {
  padding: var(--s-4) var(--s-5);
  background: var(--navy);
  color: var(--paper);
  position: relative;
}
.tw-anatomy-subtitle {
  font-family: var(--display);
  font-style: italic;
  font-size: 13px;
  margin: 6px 0 0;
  /* Lighter, slightly-warmer gold than var(--gold)'s #B08A4A — picks
     out the italic subtitle against the navy header at higher
     contrast than the gold token reads at this point size. */
  color: #D4B681;
}
.tw-anatomy-header-rule {
  position: absolute;
  bottom: 0; left: var(--s-5); right: var(--s-5);
  height: 1px;
  background: var(--coral);
}
.tw-anatomy-close {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: transparent;
  color: var(--paper);
  border: 1px solid var(--paper);
  cursor: pointer;
  font-size: 18px; line-height: 1;
  transition: background 120ms, border-color 120ms;
}
.tw-anatomy-close:hover {
  background: var(--coral);
  border-color: var(--coral);
}

/* ============================================================
   EVENT-DETAIL PRIMITIVES — slice 24α(i)
   Wireframe-sourced (local_v2_app.html lines 1962-2200+). The
   per-cart primitives (.cart-row, .thumb, .setup-group, etc.)
   land in slice 24α(ii) when the cart sections are rebuilt.
   ============================================================ */

/* Always-visible money strip below the event-detail tab nav. Lays
   each stat-cell as a flex column with generous gap so labels and
   values don't visually mash together at narrow widths. */
.summary-strip {
  padding: var(--s-4) var(--s-5);
  margin-bottom: var(--s-4);
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  display: flex;
  align-items: flex-start;
  gap: var(--s-6);
  flex-wrap: wrap;
}
.summary-strip > .stack-cell + .stack-cell {
  margin-left: 0;
}
.summary-strip .stack-cell {
  display: flex; flex-direction: column;
  gap: 6px; min-width: 100px;
}
.summary-strip .stack-cell.setup-cell {
  margin-left: auto;
  text-align: right;
}
.money-large {
  font-family: var(--display); font-weight: 500;
  font-size: 22px; line-height: 1.1; color: var(--ink);
}

/* Vendor-decline / alert banner — sits above the Carts tab content
   when an action is required (e.g. vendor declined a proposal). */
.banner {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--coral-soft);
  border: 1px solid var(--coral);
  border-left: 4px solid var(--coral);
  border-radius: var(--r-md);
}
.banner .icon {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: var(--coral);
  color: var(--paper);
  border-radius: 50%;
  font-family: var(--display); font-size: 18px; font-weight: 500;
}
.banner .body h4 {
  font-family: var(--display); font-weight: 500;
  font-size: 16px; color: var(--ink); margin-bottom: 2px;
}
.banner .body p {
  font-size: 12px; color: var(--ink-soft);
}

/* In-page tab nav (extends .tabs for event-detail use). The
   .tab-pane wrapper hides every pane that isn't .active so the
   single-page tab UX matches the wireframe exactly. */
.tab-pane { display: none; }
.tab-pane.active { display: block; animation: fadeIn 0.18s ease; }
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(2px); }
  to { opacity: 1; transform: translateY(0); }
}

/* "Open in full page" CTA used inside shell tabs. Cleaner than
   a bare link — uses .btn--secondary visual treatment. */
.shell-cta {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  padding: var(--s-7) var(--s-5);
  background: var(--paper-2);
  border: 1px dashed var(--line-soft);
  border-radius: var(--r-md);
  gap: var(--s-3);
}
.shell-cta h3 {
  font-family: var(--display); font-weight: 500;
  font-size: 20px; color: var(--ink);
}
.shell-cta p {
  font-size: 13px; color: var(--ink-soft);
  max-width: 42ch;
}

/* ============================================================
   CART PRIMITIVES — slice 24α(ii)
   Promoted verbatim from wireframes/local_v2_app.html cart
   sections (event-detail Carts tab). Used by venue / rental /
   service carts in pages/events/detail.html.
   ============================================================ */

/* Generic cart row — grid: thumb / body / status / total (column 4
   collapses when used inside a single-column layout). */
.cart-row {
  display: grid;
  grid-template-columns: 56px 1fr 100px 120px;
  gap: var(--s-3);
  padding: var(--s-3);
  border-bottom: 1px solid var(--line-faint);
  align-items: center;
}
.cart-row:last-child { border-bottom: none; }

/* 48px square placeholder. .has-image swaps the dashed-mono fallback
   for a teal→coral gradient so an actual <img> can render on top. */
.thumb {
  width: 48px; height: 48px;
  border-radius: var(--r-sm);
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 9px;
  color: var(--ink-faint);
  letter-spacing: 0.15em; text-transform: uppercase;
  overflow: hidden;
  flex-shrink: 0;
}
.thumb.has-image {
  background: linear-gradient(135deg, var(--teal-soft), var(--coral-soft));
}
.thumb img { width: 100%; height: 100%; object-fit: cover; }

/* Rental cart table with a thumbnail column. */
.cart-with-thumbs { width: 100%; border-collapse: collapse; font-size: 13px; }
.cart-with-thumbs th {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em;
  color: var(--ink-faint); text-transform: uppercase; font-weight: 600;
  text-align: left; padding: 8px 12px;
  background: var(--paper-2); border-bottom: 1px solid var(--line-soft);
}
.cart-with-thumbs td {
  padding: 8px 12px; border-bottom: 1px solid var(--line-faint);
  vertical-align: top;
}
.cart-with-thumbs tr:last-child td { border-bottom: none; }
.cart-with-thumbs tbody tr:hover { background: var(--paper-2); }
.cart-with-thumbs th.col-thumb,
.cart-with-thumbs td.col-thumb { width: 56px; padding-left: 8px; padding-right: 4px; }
.cart-thumb-img {
  width: 44px; height: 44px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-soft);
  background: var(--paper-2);
  flex-shrink: 0;
}

/* Rental cart grouped-by-setup: wraps an optional setup name + a
   .cart-with-thumbs sub-table for that setup's items. A coral
   left-border ties the group head to the Monarch palette. */
.setup-group {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  margin-bottom: var(--s-3);
  /* No overflow: hidden — would clip the kebab/status-pill popovers that
     open downward from the last row. Inner head + table border-radius
     handle the rounded-corner appearance below. */
}
.setup-group .cart-with-thumbs { margin: 0; }
/* Round the head's top corners so it stays flush with the group's
   rounded border. */
.setup-group > .setup-group-head:first-child {
  border-top-left-radius: calc(var(--r-md) - 1px);
  border-top-right-radius: calc(var(--r-md) - 1px);
}
/* Round the table's bottom corners by clipping its last row's bg. */
.setup-group > .cart-with-thumbs tfoot tr:last-child td:first-child {
  border-bottom-left-radius: calc(var(--r-md) - 1px);
}
.setup-group > .cart-with-thumbs tfoot tr:last-child td:last-child {
  border-bottom-right-radius: calc(var(--r-md) - 1px);
}
.setup-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 10px var(--s-3);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-faint);
  border-left: 3px solid var(--coral);
}
.setup-group-marker {
  width: 18px; height: 18px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
  border: 1px solid var(--line-soft);
}

/* Rental cart grand total — dark navy block at the bottom of the
   rental cart, summing all setup groups + misc. */
.cart-grand-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--s-3) var(--s-4);
  margin-top: var(--s-3);
  background: var(--navy);
  color: var(--paper);
  border-radius: var(--r-md);
}
.cart-grand-total .section-label { color: rgba(255, 253, 248, 0.7); }
.cart-grand-total .money-large { color: var(--paper); }
.cart-grand-total .muted { color: rgba(255, 253, 248, 0.5); }

/* ============================================================
   MEAL LINEUP — slice 24α(ii)
   Nested block inside a catering service's cart-row body. Sign-off
   wireframe at static/wireframes/meal_lineup_v2.html shows all
   read/edit/add states; canonical integration is at
   wireframes/local_v2_app.html#page-event-detail Service providers.
   JS handlers in base.html pattern-match on .meal-row-display /
   .meal-row-edit / .meal-name / .meal-price / .meal-sub-event-badge.
   ============================================================ */
.meal-lineup {
  margin-top: var(--s-3);
  padding: var(--s-3);
  background: var(--paper-2);
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
}
.meal-lineup-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-2);
  padding-bottom: var(--s-2); border-bottom: 1px solid var(--line-faint);
}
.meal-lineup-head .meal-lineup-total {
  font-family: var(--mono); font-size: 11px; color: var(--ink-soft);
}
.meal-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.meal-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto auto;
  align-items: baseline;
  gap: var(--s-2);
  padding: 6px 4px;
  font-size: 13px;
  color: var(--ink);
  border-radius: var(--r-sm);
}
.meal-row:hover { background: var(--paper); }
.meal-row .meal-name { font-weight: 500; min-width: 0; }
.meal-row .meal-price { font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.meal-row .meal-price--quote { color: var(--coral-deep); font-style: italic; font-size: 11px; }
.meal-row .meal-headcount {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--ink-faint);
}
.meal-row .meal-headcount.is-attending { color: var(--teal); }
.meal-row .meal-actions {
  display: inline-flex; align-items: center; gap: 2px;
  opacity: 0; transition: opacity 0.12s;
}
.meal-row:hover .meal-actions,
.meal-row:focus-within .meal-actions { opacity: 1; }
.meal-row.is-buffet { grid-template-rows: auto auto; }
.meal-row.is-buffet .meal-description {
  grid-column: 1 / -1;
  font-size: 11px; color: var(--ink-soft); font-style: italic;
  padding-top: 2px; margin-left: 4px;
}
.meal-row.is-editing .meal-row-display { display: none; }
.meal-row:not(.is-editing) .meal-row-edit { display: none; }
.meal-row-edit {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-3); margin: 4px 0;
  background: var(--paper);
  border: 1px solid var(--coral);
  border-radius: var(--r-sm);
}
.meal-row-edit .field { margin-bottom: 0; }
.meal-add {
  margin-top: var(--s-2); padding-top: var(--s-2);
  border-top: 1px solid var(--line-faint);
}
.meal-add summary {
  cursor: pointer; user-select: none; list-style: none;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--coral-deep);
  padding: 4px 8px; border-radius: var(--r-sm);
}
.meal-add summary::-webkit-details-marker { display: none; }
.meal-add summary:hover { background: var(--coral-soft); }
.meal-add[open] summary { color: var(--ink-soft); }
.meal-add-form {
  margin-top: var(--s-2); padding: var(--s-3);
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  display: flex; flex-direction: column; gap: var(--s-2);
}

/* ============================================================
   CHECKLIST  —  v2-native task rows + bulk bar
   Mirrors wireframes/local_v2_app.html #page-checklist.
   ============================================================ */
.task-row {
  display: grid;
  grid-template-columns: 24px 1fr auto auto auto;
  gap: var(--s-3);
  padding: var(--s-3);
  align-items: center;
  border-bottom: 1px solid var(--line-faint);
  font-size: 13px;
  cursor: default;
  transition: background 120ms;
}
.task-row:last-child { border-bottom: none; }
.task-row.selected { background: rgba(91,138,138,0.06); }
.task-row.completed { opacity: 0.65; }
.task-row.completed .task-title { text-decoration: line-through; color: var(--ink-soft); }
.task-row.focused .focus-marker { color: var(--coral); transform: scale(1.15); }
.task-row.editing { background: rgba(195,107,90,0.06); border-bottom: 1px dashed var(--coral); }
.task-row.editing + .task-edit-form { display: block; }

.task-complete {
  width: 18px; height: 18px;
  border: 1.5px solid var(--ink-soft);
  border-radius: 3px;
  cursor: pointer;
  display: inline-flex;
  align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: all 120ms;
  background: var(--paper);
}
.task-complete:hover { border-color: var(--teal); }
.task-row.completed .task-complete {
  background: var(--teal); border-color: var(--teal); color: var(--paper);
}
.task-row.completed .task-complete::before { content: "\2713"; font-size: 12px; font-weight: 700; }

.task-body { min-width: 0; }
.task-title { font-weight: 600; color: var(--ink); }
.task-meta {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 4px;
}

.task-pill-cycle { cursor: pointer; user-select: none; transition: all 120ms; }
.task-pill-cycle:hover { filter: brightness(0.95); }

.focus-marker {
  color: var(--ink-faint);
  cursor: pointer;
  user-select: none;
  transition: color 120ms, transform 120ms;
  font-size: 13px;
}
.focus-marker:hover { color: var(--coral); }
.task-row.focused .focus-marker { color: var(--coral); }

.task-edit-form {
  display: none;
  padding: var(--s-3);
  background: var(--paper-2);
  border-top: 1px solid var(--line-faint);
}
.task-edit-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--ink-faint);
  font-size: 12px;
  padding: 2px 6px;
}
.task-edit-btn:hover { color: var(--teal); }

.bulk-select { cursor: pointer; accent-color: var(--teal); width: 16px; height: 16px; margin: 0; }

/* Sticky dark action bar above the task list. */
.bulk-bar {
  background: var(--navy);
  color: var(--paper);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  position: sticky;
  top: 0;
  z-index: 5;
  box-shadow: 0 2px 8px rgba(31,27,24,0.18);
}
.bulk-bar .count {
  font-family: var(--display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1;
}
.bulk-bar .actions { display: flex; gap: var(--s-2); margin-left: auto; flex-wrap: wrap; }
.bulk-bar .clear {
  color: rgba(255,253,248,0.55);
  font-size: 11px;
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
}
.bulk-bar .clear:hover { color: var(--paper); }

.btn-on-dark {
  background: rgba(255,253,248,0.10);
  color: var(--paper);
  border: 1px solid rgba(255,253,248,0.25);
  padding: 6px 12px;
  border-radius: var(--r-sm);
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
}
.btn-on-dark:hover { background: var(--paper); color: var(--navy); }
.btn-on-dark[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn-on-dark[disabled]:hover { background: rgba(255,253,248,0.10); color: var(--paper); }

/* ============================================================
   STATS — 4-up KPI cards above the guest table.
   Mirrors wireframes/local_v2_app.html .stat.
   ============================================================ */
.stat {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s-4);
  position: relative;
}
.stat .label {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.15em;
  color: var(--ink-faint);
  text-transform: uppercase;
  margin-bottom: var(--s-2);
}
.stat .value {
  font-family: var(--display);
  font-weight: 500;
  font-size: 32px;
  line-height: 1;
}
.stat .meta { font-size: 11px; color: var(--ink-soft); margin-top: 4px; }
.stat .accent {
  position: absolute; top: 0; left: 0;
  width: 24px; height: 3px;
  background: var(--coral);
  border-top-left-radius: var(--r-md);
}

/* ============================================================
   GUEST TABLE  —  per-(guest × sub-event) RSVP cells + meal pills.
   Mirrors wireframes/local_v2_app.html #page-guests.
   ============================================================ */
.guest-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}
.guest-table thead {
  background: var(--paper-2);
}
.guest-table th {
  text-align: left;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 500;
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--line-soft);
  vertical-align: bottom;
}
.guest-table td {
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--line-faint);
  vertical-align: middle;
}
.guest-table tbody tr:last-child td { border-bottom: none; }
.guest-table tbody tr:hover { background: var(--paper-2); }
.guest-table th.rsvp-col { text-align: center; min-width: 68px; }
.guest-table td.rsvp-col { vertical-align: top; padding-top: 10px; text-align: center; }
.guest-table td.num { text-align: right; font-variant-numeric: tabular-nums; }

.rsvp-stack {
  display: flex; flex-direction: column;
  align-items: center; gap: 3px;
}
.rsvp-stack .meal-pick {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  padding: 1px 4px;
  background: rgba(91,138,138,0.08);
  border-radius: 2px;
  white-space: nowrap;
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rsvp-stack .meal-pick.empty { background: transparent; color: var(--line-soft); }

.rsvp-cell {
  width: 22px; height: 22px;
  margin: 0 auto;
  display: inline-flex;
  align-items: center; justify-content: center;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  background: var(--paper-2);
  color: var(--ink-faint);
  transition: all 120ms;
  font-family: inherit;
}
.rsvp-cell:hover { color: var(--ink); }
.rsvp-cell.yes      { background: rgba(91,123,79,0.18); color: var(--green); }
.rsvp-cell.no       { background: rgba(139,42,42,0.15); color: var(--red); }
.rsvp-cell.awaiting { background: var(--bg); color: var(--ink-faint); }
.rsvp-cell.na       { color: var(--line-soft); background: transparent; cursor: default; }
.rsvp-cell.active   { font-weight: 700; box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px currentColor; }
.rsvp-toggle {
  display: inline-flex; gap: 2px; align-items: center;
}

/* Plus-ones cell. − / + buttons reveal on hover; quiet otherwise. */
.plus-ones-cell {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
}
.plus-ones-cell .pm-btn {
  width: 20px; height: 20px;
  display: inline-flex;
  align-items: center; justify-content: center;
  background: var(--paper-2);
  color: var(--ink-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  font-size: 12px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms, color 120ms;
  font-family: inherit;
}
.plus-ones-cell:hover .pm-btn,
.plus-ones-cell:focus-within .pm-btn { opacity: 1; }
.plus-ones-cell .pm-btn:hover { color: var(--ink); border-color: var(--ink-faint); }
.plus-ones-cell .pm-btn[disabled] { opacity: 0 !important; pointer-events: none; }
.plus-ones-cell .num { font-variant-numeric: tabular-nums; min-width: 14px; text-align: center; color: var(--ink-soft); }

/* Inline "select" used in cell forms (small, integrates with table). */
.cell-select {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  font-size: 10px;
  padding: 2px 4px;
  font-family: var(--mono);
  color: var(--ink);
  max-width: 110px;
}

/* ============================================================
   LOOKBOOK  —  curated palette/style/setup cards + reactions.
   Mirrors wireframes/local_v2_app.html #page-lookbook.
   ============================================================ */
.publish-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-family: var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid;
}
.publish-status.draft     { background: var(--bg); color: var(--ink-soft); border-color: var(--line-soft); }
.publish-status.published { background: rgba(91,138,138,0.10); color: var(--teal); border-color: var(--teal); }
.publish-status.retracted { background: var(--paper-2); color: var(--ink-soft); border-color: var(--line-soft); }
.publish-status.loved     { background: rgba(195,107,90,0.10); color: var(--coral-deep); border-color: var(--coral); }
.publish-status.curious   { background: rgba(176,138,74,0.10); color: var(--gold); border-color: var(--gold); }
.publish-status.passed    { background: rgba(180,180,180,0.10); color: var(--ink-soft); border-color: var(--line-soft); }
.publish-status.accepted  { background: rgba(141,168,108,0.18); color: #2e4f1c; border-color: var(--green); font-weight: 700; }
.publish-status.declined  { background: rgba(180,180,180,0.18); color: var(--ink-soft); border-color: var(--line-soft); }

.lookbook-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.lookbook-card-thumb {
  height: 300px;
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding: var(--s-3);
}

/* Palette/style thumbnail — wide vertical color strips matching the
   Style Library cards. Strips fill the full thumbnail height + width
   (display: grid is set inline with grid-template-columns per
   swatch-count). Overlay pills (.signature-badge, .style-name-pill,
   .status-row) sit on top via absolute positioning. */
.lookbook-strips {
  position: absolute;
  inset: 0;
  display: grid;
  gap: 0;
  background: var(--paper-2);
  z-index: 0;
}
.lookbook-strip { height: 100%; }

/* Style-library parity pills for palette_style lookbook cards.
   Mirrors .style-card-badge-tl / .style-card-name-pill on the
   /themes/styles cards so palette setups read the same in both
   places. */
.lookbook-card-thumb .signature-badge {
  position: absolute;
  top: var(--s-3);
  left: var(--s-3);
  background: var(--paper);
  border: 1px solid var(--coral);
  color: var(--coral-deep);
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  padding: 3px 8px;
  border-radius: var(--r-sm);
  text-transform: uppercase;
}
.lookbook-card-thumb .style-name-pill {
  position: absolute;
  left: var(--s-3);
  bottom: var(--s-3);
  background: var(--navy);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  text-transform: uppercase;
}

/* ============================================================
   THUMB COLLAGE — slice 29
   Mosaic of rental-item thumbnails for setup cards (lookbook +
   themes list). Layout adapts to item count: 1 fills the cell,
   2 splits horizontally, 3 splits in thirds, 4 forms a 2×2,
   5+ uses a 3×N grid (max 9 visible, +N badge on the last cell
   if there are more).
   ============================================================ */
.thumb-collage {
  position: absolute; inset: 0;
  display: grid;
  gap: 1px;
  background: var(--paper-2);
  z-index: 0;
}
.thumb-collage > .tc-cell {
  background-size: cover;
  background-position: center;
  background-color: var(--paper);
  position: relative;
}
.thumb-collage > .tc-cell.tc-empty {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--display); font-style: italic;
  color: var(--ink-faint); font-size: 22px;
}
.thumb-collage > .tc-cell .tc-more {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(31, 27, 24, 0.55);
  color: var(--paper);
  font-family: var(--display); font-weight: 500;
  font-size: 20px;
}
.thumb-collage.n-1 { grid-template-columns: 1fr; grid-template-rows: 1fr; }
.thumb-collage.n-2 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; }
.thumb-collage.n-3 { grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr; }
.thumb-collage.n-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.thumb-collage.n-many { grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; }
.lookbook-card-thumb .status-row { display: flex; gap: 6px; flex-wrap: wrap; }
.lookbook-card-body {
  padding: var(--s-3);
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.lookbook-card-name { font-family: var(--display); font-size: 17px; font-weight: 500; color: var(--navy); }
.lookbook-card-meta { font-size: 11px; color: var(--ink-soft); }
.lookbook-card-foot {
  padding: var(--s-2) var(--s-3);
  background: var(--paper-2);
  border-top: 1px solid var(--line-faint);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

/* Palette swatch tile — used inside lookbook cards + style detail rows. */
.ps {
  display: inline-block;
  width: 18px;
  height: 18px;
  border-radius: 3px;
  border: 1px solid rgba(31,27,24,0.10);
}

.reaction-bar {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  background: rgba(91,138,138,0.05);
  border-top: 1px dashed var(--line-faint);
  flex-wrap: wrap;
}
.reaction-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  font-size: 12px;
  border-radius: var(--r-sm);
  background: var(--paper);
  border: 1px solid var(--line-soft);
  cursor: pointer;
  font-family: var(--body);
  color: var(--ink);
  transition: all 120ms;
}
.reaction-btn:hover { border-color: var(--teal); }
.reaction-btn.active.love    { background: rgba(195,107,90,0.12); border-color: var(--coral); color: var(--coral-deep); font-weight: 600; }
.reaction-btn.active.curious { background: rgba(176,138,74,0.12); border-color: var(--gold); color: #7a5a2a; font-weight: 600; }
.reaction-btn.active.pass    { background: rgba(180,180,180,0.15); border-color: var(--ink-soft); color: var(--ink-soft); font-weight: 600; }
.reaction-btn[disabled],
.reaction-btn.inert { cursor: default; }
.reaction-btn[disabled]:hover,
.reaction-btn.inert:hover { border-color: var(--line-soft); }
.reaction-btn .glyph { font-size: 13px; }
.reaction-btn .count { font-family: var(--mono); font-size: 10px; color: var(--ink-faint); margin-left: 2px; }

.client-reaction-strip {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  background: var(--paper);
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
  margin: 6px var(--s-3) 0;
  align-items: center;
}
.client-reaction-strip:last-child { margin-bottom: var(--s-3); }
.client-reaction-strip .avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--teal-soft);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--display);
  font-size: 13px;
  color: var(--teal);
}
.client-reaction-strip .quote { font-size: 12px; color: var(--ink); font-style: italic; }
.client-reaction-strip .who { font-size: 10px; color: var(--ink-faint); font-family: var(--mono); margin-top: 2px; }

/* Per-card decision strip — Accept/Decline state with action row. */
.decision-strip {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  font-size: 11px;
  border-top: 1px solid var(--line-faint);
  border-bottom: 1px solid var(--line-faint);
  flex-wrap: wrap;
}
.decision-strip .dec-status {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 3px 8px;
  border-radius: 999px;
}
.decision-strip .dec-actions { display: flex; gap: 4px; margin-left: auto; }
.decision-strip.accepted { background: rgba(141,168,108,0.06); border-color: var(--green); }
.decision-strip.accepted .dec-status { background: rgba(141,168,108,0.20); color: #2e4f1c; border: 1px solid var(--green); }
.decision-strip.awaiting { background: rgba(176,138,74,0.06); border-color: var(--gold); }
.decision-strip.awaiting .dec-status { background: rgba(176,138,74,0.18); color: #7a5a2a; border: 1px solid var(--gold); }
.decision-strip.declined { background: rgba(180,180,180,0.08); }
.decision-strip.declined .dec-status { background: rgba(180,180,180,0.25); color: var(--ink-soft); border: 1px solid var(--line-soft); }
.decision-strip.draft { background: var(--paper-2); }
.decision-strip.draft .dec-status { background: var(--paper); color: var(--ink-soft); border: 1px dashed var(--line-soft); }

/* ============================================================
   LINE-ITEM TABLE  —  compact editable rows for milestone schedules.
   Used on the Billing tab inside Monarch service cards + each vendor
   billing card. Cells host .input controls inline; the table stays
   readable when those inputs are sparse + when they're all filled.
   ============================================================ */
.line-item-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--s-2);
  font-size: 12px;
}
.line-item-table th {
  text-align: left;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 400;
  padding: 4px 8px;
  border-bottom: 1px solid var(--line-faint);
}
.line-item-table th.text-right { text-align: right; }
.line-item-table td {
  padding: 4px 8px;
  border-bottom: 1px solid var(--line-faint);
  vertical-align: middle;
}
.line-item-table td.text-right { text-align: right; }
.line-item-table tbody tr:last-child td { border-bottom: 1px solid var(--line-soft); }
.line-item-table tfoot td {
  padding: 6px 8px;
  border-top: 1px solid var(--line-soft);
  background: var(--paper-2);
}
.line-item-table .input {
  padding: 4px 8px;
  font-size: 12px;
  background: var(--paper);
  border: 1px solid var(--line-faint);
}
.line-item-table .input:focus { background: var(--paper); border-color: var(--coral); }
.meal-add-form .field { margin-bottom: 0; }

/* ============================================================
   DAY-OF TIMELINE  —  vertical time + card list on event detail.
   Mirrors wireframes/local_v2_app.html .tab-pane[data-tab-pane="day-of"].

   Each row is a 96px time column + 1fr card; the card's left
   border colors by kind:
     - .day-of-row.setup     → gold
     - .day-of-row.guest     → coral (sub-events too)
     - .day-of-row.breakdown → ink-faint
     - .day-of-row.planner   → ink-soft (default)
   ============================================================ */
.day-of-timeline {
  display: grid;
  gap: var(--s-3);
  padding: var(--s-3);
}
.day-of-row {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: var(--s-3);
  align-items: start;
}
.day-of-time {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--coral-deep);
  padding-top: 10px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.day-of-card {
  padding: var(--s-3);
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--ink-soft);
  border-radius: var(--r-sm);
  transition: background 120ms;
}
.day-of-row.setup .day-of-card     { border-left-color: var(--gold); }
.day-of-row.guest .day-of-card     { border-left-color: var(--coral); }
.day-of-row.breakdown .day-of-card { border-left-color: var(--ink-faint); }
.day-of-row.planner .day-of-card   { border-left-color: var(--ink-soft); }
.day-of-row.guest.is-sub-event .day-of-card {
  background: var(--paper);
}
.day-of-row.editing .day-of-card { background: rgba(195,107,90,0.06); border-left-style: dashed; }
.day-of-row .day-of-edit-form { display: none; padding-top: var(--s-2); border-top: 1px dashed var(--line-faint); margin-top: var(--s-2); }
.day-of-row.editing .day-of-edit-form { display: block; }

/* ============================================================
   SCAFFOLD MODALS  —  page-level UI primitives mounted in base.html.
   Live alongside the body-level .modal-overlay used by content
   modals, but cover full-viewport concerns (save-confirmation
   toast at top, image-zoom lightbox).
   ============================================================ */

/* Save-confirmation toast — slides in at the top after a server
   redirect carries ?saved=<label>. Hidden by default; .is-open
   makes it visible + fades out via the .is-fading helper class. */
.save-toast {
  position: fixed;
  top: var(--s-3);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  background: var(--teal);
  color: var(--paper);
  font-size: 13px;
  padding: 8px 16px;
  border-radius: var(--r-sm);
  box-shadow: 0 8px 24px rgba(31,27,24,0.18);
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease;
}
.save-toast.is-open  { opacity: 1; }
.save-toast.is-fading { opacity: 0; }

/* Full-viewport image-zoom lightbox. Triggered by clicking any
   element with [data-img-zoom="<url>"]. Hidden by default; .is-open
   shows it as a centered flex layout against a dark backdrop. */
.img-modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--s-4);
  background: rgba(31,27,24,0.85);
}
.img-modal.is-open { display: flex; }
.img-modal-close {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--paper);
  background: transparent;
  border: 0;
  font-size: 28px;
  cursor: pointer;
  transition: color 120ms;
}
.img-modal-close:hover { color: var(--coral); }
.img-modal-img {
  max-height: 90vh;
  max-width: 90vw;
  border-radius: var(--r-md);
  box-shadow: 0 16px 48px rgba(0,0,0,0.5);
  background: var(--paper);
}
.img-modal-caption {
  position: absolute;
  bottom: var(--s-4);
  left: 0;
  right: 0;
  text-align: center;
  color: rgba(255,253,248,0.85);
  font-size: 13px;
  font-family: var(--display);
  font-style: italic;
  padding: 0 var(--s-4);
  pointer-events: none;
}

/* ============================================================
   UTILITIES  —  small primitives used across v2 partials.
   ============================================================ */

/* `.hidden` is toggled by inline JS in several v2 partials (region
   checkboxes, sub-event picker, etc.) to show/hide popovers and
   inline error rows. Owning this rule explicitly in v2.css lets the
   pattern survive the eventual removal of the Tailwind CDN. */
.hidden { display: none !important; }

/* ============================================================
   BUDGET BAR  —  cart-breakdown widget on event detail
   Two overlapping fills inside a single track:
     .budget-bar-entered → light teal (current total)
     .budget-bar-paid    → solid teal (contract-signed)
   The track caps each width at 100% in the template so over-budget
   renders fully filled (the entered VALUE carries the over-budget
   color via budget_color()).
   ============================================================ */
.budget-bar {
  position: relative;
  height: 14px;
  background: var(--paper-2);
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.budget-bar-entered,
.budget-bar-paid {
  position: absolute;
  top: 0; left: 0;
  height: 100%;
  transition: width 0.18s ease;
}
.budget-bar-entered { background: rgba(74, 133, 144, 0.28); }
.budget-bar-paid    { background: var(--teal); }

/* Submit-proposal hover-revealed icon — sits to the left of a vendor
   name on cart-row titles. Click POSTs to /proposals/quick-create
   which creates a draft + redirects to the editor. Wraps in a tiny
   form so the click is a real POST (preserving CSRF-style guarantees
   from FastAPI's Form handling). */
.submit-proposal-form {
  display: inline-block;
  margin: 0;
  vertical-align: baseline;
}
.submit-proposal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--ink-faint);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 14px; line-height: 1;
  opacity: 0;
  transition: opacity 0.12s, color 0.12s, background 0.12s;
  vertical-align: middle;
  margin-right: 2px;
}
.cart-row:hover .submit-proposal-btn,
.setup-group-head:hover .submit-proposal-btn,
.submit-proposal-btn:focus { opacity: 1; }
.submit-proposal-btn:hover {
  color: var(--paper);
  background: var(--coral);
}

/* ============================================================
   ROW-POPOVER + KEBAB  —  slice 24p
   Cart-row pattern: status pill opens a status-options popover,
   kebab opens an action menu. Both share <details data-row-popover>
   markup so the toggle/outside-click JS (base.html) is one handler.
   ============================================================ */

/* Wrapper. The summary's native marker is suppressed; the body
   floats absolutely so it doesn't displace row layout. */
details.row-popover {
  position: relative;
  display: inline-block;
}
details.row-popover > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}
details.row-popover > summary::-webkit-details-marker { display: none; }
details.row-popover[open] > summary { outline: none; }

/* Pill-flavored trigger — wraps a .pill so the open state can
   show a thin ring without disturbing pill colors. */
.row-popover-trigger.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.row-popover-trigger.pill .caret {
  font-size: 8px;
  opacity: 0.65;
  line-height: 1;
}
details.row-popover[open] > .row-popover-trigger.pill {
  box-shadow: 0 0 0 2px rgba(91, 123, 79, 0.18);
}

/* Kebab trigger — icon-only button. Mostly invisible at rest, ink
   on hover/open. Keeps row baseline aligned. */
.kebab-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: var(--r-sm);
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-faint);
  font-size: 18px; line-height: 1;
  letter-spacing: 0;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.kebab-btn:hover,
details.row-popover[open] > .kebab-btn {
  background: var(--paper-2);
  border-color: var(--line-soft);
  color: var(--ink);
}

/* The floating body. Anchored to the trigger, opens downward and
   either right- or left-aligned via modifier. z-index above cart
   rows but below the modal overlay (200). */
.row-popover-body {
  position: absolute;
  top: calc(100% + 4px);
  z-index: 50;
  min-width: 180px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  box-shadow: 0 10px 28px rgba(31, 27, 24, 0.14);
  padding: 4px;
}
.row-popover-body--right { right: 0; }
.row-popover-body--left { left: 0; }

/* ── Vendor / service / venue documents panel ────────────────────
   Compact pill-row layout used by components/{vendor,service}_documents_panel.html
   and the matching block in pages/venues/detail.html.

   Header: "Documents⁺" — the "+" trigger sits as a small superscript
   coral pill next to the heading, opening a popover form. Rows:
   [type pill] [truncated filename] [size] [×] all on one line, the
   "×" superscript-sized so it doesn't dominate the row. */
.doc-panel__head {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  margin: 0 0 var(--s-3);
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.doc-panel__add { position: relative; display: inline-block; }
.doc-panel__add-trigger {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  background: var(--coral);
  color: var(--paper);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  vertical-align: super;
  transition: background 120ms;
}
.doc-panel__add-trigger::-webkit-details-marker { display: none; }
.doc-panel__add-trigger::marker { content: ''; }
.doc-panel__add-trigger:hover { background: var(--coral-deep, #8b2e1f); }

.doc-panel__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.doc-panel__group-head {
  font-family: var(--mono, monospace);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint, #9b8e80);
  margin: var(--s-2, 8px) 0 4px;
  padding: 0;
  font-weight: 500;
}
.doc-panel__group-head:first-of-type { margin-top: 0; }
.doc-row {
  display: flex;
  align-items: center;
  gap: var(--s-2, 8px);
  font-size: 13px;
  flex-wrap: nowrap;
  padding: 5px 10px;
  margin: 0 -10px;
  border-radius: var(--r-sm, 4px);
  border: 1px solid transparent;
  transition: background 120ms, border-color 120ms, transform 120ms;
}
.doc-row:hover {
  background: var(--paper, #fff);
  border-color: var(--coral, #c36b5a);
  transform: translateX(2px);
}
.doc-row:hover .doc-row__name { color: var(--coral-deep, #8b2e1f); }
.doc-row:hover .doc-row__delete { color: var(--coral, #c36b5a); }
.doc-row__type {
  font-size: 9px;
  flex-shrink: 0;
}
.doc-row__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.doc-row__size {
  font-size: 10px;
  flex-shrink: 0;
}
.doc-row__delete-form {
  margin: 0;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.doc-row__delete {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--ink-faint, #9b8e80);
  font-size: 11px;
  line-height: 1;
  vertical-align: super;
  transition: color 120ms;
}
.doc-row__delete:hover { color: var(--coral, #c36b5a); }

/* Vertical menu — used by kebab. Each item is a button or link
   styled as a row in the list. */
.kebab-menu {
  display: flex; flex-direction: column;
  min-width: 200px;
  gap: 0;
}
.kebab-menu > a,
.kebab-menu > button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  width: 100%;
  padding: 7px 10px;
  font-size: 13px;
  font-family: var(--sans);
  font-weight: 400;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.kebab-menu > a:hover,
.kebab-menu > button:hover { background: var(--paper-2); }
.kebab-menu > .kebab-menu-sep {
  height: 1px;
  margin: 4px 6px;
  background: var(--line-faint);
}
.kebab-menu > .danger { color: var(--red); }
.kebab-menu > .danger:hover { background: rgba(139, 42, 42, 0.06); }
.kebab-menu .badge {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--paper);
  background: var(--coral);
  padding: 1px 6px;
  border-radius: 999px;
  min-width: 18px;
  text-align: center;
}

/* Status-options popover — vertical list of buttons that submit a
   status change. Each option carries a pill swatch on the left and
   the human-readable label. */
.status-options-form {
  display: flex; flex-direction: column;
  margin: 0;
  min-width: 200px;
}
.status-options-form button {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  padding: 6px 10px;
  font-size: 12px;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  text-align: left;
  cursor: pointer;
  color: var(--ink);
}
.status-options-form button:hover { background: var(--paper-2); }
.status-options-form button .pill { pointer-events: none; }
.status-options-form button[aria-current="true"] {
  background: var(--paper-2);
  font-weight: 500;
}

/* ============================================================
   RENTAL CART — slice 24q (+ slice 40C kebab modal cleanup)
   Read-mostly table. Slice 40C retired the legacy "Manage" toggle
   in favor of a per-row Edit kebab modal — the kebab now lives in
   the Actions column unconditionally. The .manage-toggle /
   .setup-group.is-managing / .cell-swap CSS that used to gate
   inline-edit visibility was removed in the docket #2 cleanup
   (those classes have no template references anywhere; their dead
   rules were silently hiding the kebab column too).
   table-layout: fixed gives consistent column widths across
   independent vendor groups (fixes the cross-group misalignment
   issue from the v1-era variable-width tables).
   ============================================================ */

.cart-with-thumbs.cart-with-thumbs--locked {
  table-layout: fixed;
}
.cart-with-thumbs--locked th,
.cart-with-thumbs--locked td {
  overflow: hidden;
  text-overflow: ellipsis;
}
.cart-with-thumbs--locked th.col-item,
.cart-with-thumbs--locked td.col-item { width: auto; }
.cart-with-thumbs--locked th.col-subevents,
.cart-with-thumbs--locked td.col-subevents { width: 28%; overflow: visible; }
.cart-with-thumbs--locked th.col-qty,
.cart-with-thumbs--locked td.col-qty { width: 72px; text-align: right; }
.cart-with-thumbs--locked th.col-unit,
.cart-with-thumbs--locked td.col-unit { width: 96px; text-align: right; }
.cart-with-thumbs--locked th.col-line,
.cart-with-thumbs--locked td.col-line { width: 112px; text-align: right; }
.cart-with-thumbs--locked th.col-actions,
.cart-with-thumbs--locked td.col-actions { width: 44px; text-align: right; overflow: visible; }

/* tfoot subtotal strip — keep colspan stable; the last cell
   carries the amount. */
.cart-with-thumbs tfoot td {
  background: var(--paper-2);
  border-top: 1px solid var(--line-soft);
  padding: 8px 12px;
  font-size: 11px;
}

/* Read-mode subevent chips — one chip per linked sub-event,
   wrapping rather than overflowing the cell. */
.subevent-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.subevent-chips .chip {
  font-size: 10px;
  padding: 2px 7px;
}

/* ============================================================
   THEME WORKSPACE SHELL — slice 24s.1
   Three-column layout: catalog (left, sticky) · setup items
   (middle) · sidebar (right). Sidebar collects regions / palette
   / style / setup-details cards. Inner catalog and items grid
   keep their v1 markup for now — 24s.2/24s.3 restyle them.
   ============================================================ */
.ws-shell {
  display: grid;
  grid-template-columns: 320px 1fr 280px;
  gap: var(--s-4);
  align-items: start;
  margin-top: var(--s-4);
}

/* Left column — sticky so the catalog stays visible while the
   middle column scrolls past it. */
.ws-catalog {
  position: sticky;
  top: var(--s-3);
  max-height: calc(100vh - var(--s-5));
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}

/* Middle column — setup items workspace. Header bar pinned at
   top with the section label + drop-here hint; body fills the
   remaining space and is the drop target. */
.ws-workspace {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}
.ws-workspace-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-faint);
}
.ws-workspace-body {
  padding: var(--s-4);
}

/* Action-group kebab on the page-head action row — slightly
   larger hit area than the row-level kebab. */
.page-head-kebab .kebab-btn {
  width: 32px;
  height: 32px;
  font-size: 20px;
}

/* ============================================================
   SETUP CARD GRID — slice 24s.2
   Used by app/templates/components/theme_items_grid.html. The grid
   itself is the drop target; its container picks up .is-drop-target
   while a catalog drag hovers. Each card carries the rental thumb,
   name, vendor, price, and an inline qty + multiplier form.
   ============================================================ */
#theme-items-grid {
  min-height: 200px;
  border-radius: var(--r-sm);
  transition: outline-color 0.15s, background 0.15s;
  outline: 2px solid transparent;
  outline-offset: 4px;
}
#theme-items-grid.is-drop-target {
  outline-color: var(--coral);
  background: rgba(195, 107, 90, 0.04);
}

.setup-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--s-3);
  list-style: none;
  padding: 0;
  margin: 0;
}

.setup-card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.12s;
}
.setup-card:hover { border-color: var(--coral); }
.setup-card.has-oor { border-color: var(--coral); }

.setup-card-thumb {
  aspect-ratio: 1;
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-faint);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}
.setup-card-thumb img { width: 100%; height: 100%; object-fit: cover; }
.setup-card-thumb-placeholder {
  font-family: var(--display); font-style: italic;
  font-size: 28px; color: var(--ink-faint);
}

.setup-card-body {
  padding: var(--s-2) var(--s-3);
  display: flex; flex-direction: column;
  gap: 3px;
  flex: 1;
}
.setup-card-name {
  font-weight: 500;
  font-size: 12.5px;
  color: var(--ink);
  line-height: 1.25;
  cursor: pointer;
  background: none; border: 0; padding: 0;
  text-align: left;
}
.setup-card-name:hover { color: var(--coral-deep); }
.setup-card-vendor {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.setup-card-price {
  font-size: 11px;
  font-weight: 500;
  color: var(--teal);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.setup-card-price .unit { color: var(--ink-faint); font-weight: 400; }
.setup-card-price.is-quote {
  color: var(--coral-deep);
  font-style: italic;
}

.setup-card-qty {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  font-size: 11px;
}
.setup-card-qty > .x { color: var(--ink-faint); }
.setup-card-qty input[type=number] {
  width: 48px;
  padding: 2px 6px;
  font-size: 11px;
  text-align: center;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.setup-card-qty input[type=number]:focus {
  outline: none;
  border-color: var(--coral);
  background: var(--paper);
}
.setup-card-qty select {
  flex: 1;
  font-size: 10px;
  padding: 2px 4px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--sans);
}

/* Out-of-region badge — coral glyph + replace CTA pill, anchored
   top-left of the card. Toggled by recomputeOutOfRegion() in
   workspace.html. */
.setup-card-oor {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 4px;
}
.setup-card-oor-glyph {
  width: 18px; height: 18px;
  background: var(--coral);
  color: var(--paper);
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; line-height: 1;
  cursor: help;
}
.setup-card-oor-replace {
  background: var(--teal); color: var(--paper);
  font-family: var(--mono);
  font-size: 9px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: var(--r-sm);
  text-decoration: none;
}
.setup-card-oor-replace:hover { background: var(--coral-deep); }

/* Legacy "out of event" marker (duplicate-and-flag flow). Distinct
   from the live region check — kept for backwards compat. */
.setup-card-out-of-event {
  position: absolute;
  top: 28px;
  left: var(--s-2);
  z-index: 2;
  background: var(--coral);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 9px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 2px 6px; border-radius: var(--r-sm);
}

/* × delete — hover-revealed top-right. */
.setup-card-delete {
  position: absolute;
  top: 4px; right: 4px;
  z-index: 2;
  opacity: 0;
  transition: opacity 0.12s;
}
.setup-card:hover .setup-card-delete,
.setup-card-delete:focus-within { opacity: 1; }
.setup-card-delete button {
  width: 20px; height: 20px;
  padding: 0;
  border-radius: 50%;
  background: var(--coral);
  color: var(--paper);
  border: 0;
  font-size: 13px; line-height: 1; font-weight: 600;
  cursor: pointer;
}
.setup-card-delete button:hover { background: var(--coral-deep); }

/* Seating-table marker — distinguishes the "active" per_table item
   (drives the table-count math) from passive seating items. */
.seating-marker {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--mono);
  font-size: 9px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 2px 6px;
  border-radius: var(--r-sm);
  align-self: flex-start;
  margin-top: 2px;
}
.seating-marker.is-active {
  background: rgba(195, 107, 90, 0.12);
  color: var(--coral);
}
.seating-marker.is-passive {
  border: 1px solid rgba(195, 107, 90, 0.4);
  color: rgba(195, 107, 90, 0.75);
}
.seating-marker .chair-glyph { font-size: 10px; }

/* Visual preview strip — slice 24s.4. Horizontal row of mini
   thumbnails above the setup grid: the gestalt of the assembled
   setup at a glance. Each thumb opens the rental-preview modal
   (same hook as the setup-card name). Scrolls horizontally when
   the setup has many items. */
.setup-preview-strip {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  padding: var(--s-3);
  background: var(--paper-2);
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
}
.setup-preview-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex-shrink: 0;
}
.setup-preview-imgs {
  display: flex;
  gap: 4px;
  flex: 1;
  align-items: center;
  overflow-x: auto;
  scrollbar-width: thin;
}
.setup-preview-img {
  width: 36px; height: 36px;
  flex-shrink: 0;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-soft);
  background: var(--paper);
  overflow: hidden;
  padding: 0;
  cursor: pointer;
  transition: border-color 0.12s, transform 0.12s;
}
.setup-preview-img:hover {
  border-color: var(--coral);
  transform: translateY(-1px);
}
.setup-preview-img img { width: 100%; height: 100%; object-fit: cover; }
.setup-preview-img-placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--display); font-style: italic;
  font-size: 13px; color: var(--ink-faint);
}

/* Bottom subtotal strip — sum of qty × price across all items.
   Quote-pending items add a "+ quote" caveat. */
.setup-subtotal {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--s-3) var(--s-4);
  margin-top: var(--s-3);
  background: var(--paper-2);
  border-top: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
}

/* Empty state — large dashed dropzone with drag hint. */
.setup-empty {
  border: 2px dashed var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s-6) var(--s-4);
  text-align: center;
}
.setup-empty .glyph {
  font-family: var(--display); font-style: italic;
  font-size: 32px; color: var(--ink-faint);
  margin-bottom: var(--s-2);
}
.setup-empty p { margin: 0; }

/* ============================================================
   CATALOG PANEL — slice 24s.3
   Inner of .ws-catalog: header section (search + filters), then a
   scrolling tree of <details> categories with .cat-item rows.
   Each filter popover (color, style) and the JS hooks
   (.catalog-item / data-* attrs / #catalog-search etc.) are
   preserved exactly so the existing workspace JS keeps working.
   ============================================================ */

/* Sticky head — search + filters, doesn't scroll with the tree. */
.ws-catalog-head {
  padding: var(--s-4);
  border-bottom: 1px solid var(--line-faint);
  background: var(--paper);
  flex-shrink: 0;
}
.ws-catalog-head .label-coral {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  color: var(--coral);
  font-weight: 600;
  text-transform: uppercase;
  margin: 0 0 var(--s-1);
}
.ws-catalog-head h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  color: var(--ink);
  margin: 0 0 var(--s-2);
}

/* 2×2 filter grid: Material | Color / Style (col-span-2). Vendor
   lives on its own full-width row below. */
.catalog-filters {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-2);
  margin-top: var(--s-2);
}
.catalog-filters .span-2 { grid-column: span 2; }

/* Custom filter pick — looks like a native <select> but supports
   inline swatch previews. Used for Color + Style popovers and the
   matching <select>s use the same baseline styling. */
.catalog-pick {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  font-size: 12px;
  font-family: var(--sans);
  color: var(--ink);
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  cursor: pointer;
  text-align: left;
  min-height: 30px;
}
.catalog-pick:hover:not(:disabled) { border-color: var(--coral); }
.catalog-pick:focus { outline: none; border-color: var(--coral); }
.catalog-pick:disabled { opacity: 0.5; cursor: not-allowed; }
.catalog-pick .lbl {
  flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.catalog-pick .lbl.muted { color: var(--ink-faint); }
.catalog-pick .caret { color: var(--ink-faint); font-size: 11px; }
.catalog-pick .sw {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 2px;
  border: 1px solid rgba(31, 27, 24, 0.12);
}
.catalog-pick .sw-mini {
  display: inline-flex;
  gap: 1px;
  align-items: center;
}

/* Popover panel anchored under a .catalog-pick trigger. */
.catalog-pick-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0; right: 0;
  min-width: 200px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  box-shadow: 0 10px 28px rgba(31, 27, 24, 0.14);
  padding: 4px;
}
.catalog-pick-panel button {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border: 0;
  background: transparent;
  border-radius: var(--r-sm);
  font-size: 12px;
  font-family: var(--sans);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.catalog-pick-panel button:hover { background: var(--paper-2); }
.catalog-pick-panel button.muted { color: var(--ink-faint); font-style: italic; }
.catalog-pick-panel .style-row {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* In-region / in-setup stats line. */
.ws-catalog-stats {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: var(--s-2) 0 0;
}

/* Scrolling tree body. */
.ws-catalog-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--s-3) var(--s-4);
}

/* Category — nested <details>. depth=0 has a uniform top border;
   .cat-group.sub indents and gets a left border. */
.cat-group {
  border-bottom: 1px solid var(--line-faint);
}
.cat-group:last-child { border-bottom: 0; }
.cat-group.sub {
  margin-left: var(--s-3);
  padding-left: var(--s-2);
  border-left: 1px solid var(--line-faint);
  border-bottom: 0;
}
.cat-group > summary {
  list-style: none;
  cursor: pointer;
  padding: 7px 4px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}
.cat-group > summary::-webkit-details-marker { display: none; }
.cat-group > summary:hover { color: var(--coral-deep); }
.cat-group:not(.sub) > summary > :first-child {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.cat-group.sub > summary > :first-child {
  font-size: 11px;
  font-weight: 400;
  color: var(--ink-soft);
}

/* Item count badges in category headers. */
.cat-badges {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.cat-badge-direct {
  background: var(--navy);
  color: var(--paper);
  font-size: 9.5px;
  font-weight: 500;
  padding: 1px 7px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}
.cat-badge-nested {
  color: var(--ink-faint);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
}

/* Item list. */
.cat-items {
  list-style: none;
  padding: 0 0 var(--s-2);
  margin: 0;
}
.cat-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 6px;
  margin: 1px 0;
  border-radius: var(--r-sm);
  cursor: grab;
}
.cat-item:hover { background: var(--paper-2); }
.cat-item:active { cursor: grabbing; }
/* Out-of-region badge state — the catalog-gate JS toggles this on items
   whose vendor doesn't cover any theme service region. Pairs with the
   inline [data-out-of-region-badge] flex chip the item row renders. */
.cat-item.is-out-of-region { border: 1px solid var(--coral); }

.cat-thumb-small {
  width: 30px; height: 30px;
  flex-shrink: 0;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-faint);
  background: var(--paper-2);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.cat-thumb-small img { width: 100%; height: 100%; object-fit: cover; }
.cat-thumb-small-placeholder {
  font-family: var(--display); font-style: italic;
  font-size: 11px; color: var(--ink-faint);
}

.cat-info {
  flex: 1;
  min-width: 0;
}
.cat-name {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 4px;
}
.cat-vendor {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ⋮⋮ drag handle on each catalog item — visual cue that the row
   is draggable. */
.drag-handle {
  color: var(--ink-faint);
  font-size: 16px;
  letter-spacing: -2px;
  line-height: 1;
  flex-shrink: 0;
  user-select: none;
}

/* Seating-table marker inside .cat-name — same coral chip as the
   setup-card variant. */
.cat-name .seating-marker {
  margin-right: 2px;
  padding: 1px 5px;
  font-size: 8.5px;
}

/* "No matches" hint inside the body. */
.catalog-no-matches {
  font-size: 13px;
  color: var(--ink-soft);
  font-style: italic;
  text-align: center;
  padding: var(--s-5) 0;
}

/* ============================================================
   CATEGORY TREE — slice 24w (used by /categories list page)
   Flat list of rows; nesting is rendered via inline padding-left.
   Hover reveals the per-row actions (+ sub / edit / delete).
   ============================================================ */
.cat-tree-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px var(--s-4);
  border-bottom: 1px solid var(--line-faint);
  transition: background 0.12s;
}
.cat-tree-row:last-child { border-bottom: none; }
.cat-tree-row:hover { background: var(--paper-2); }
.cat-tree-row .name {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
  flex: 1;
}
.cat-tree-row .glyph {
  color: var(--ink-faint);
  font-size: 11px;
  user-select: none;
  flex-shrink: 0;
}
.cat-tree-row a.cat-name-link {
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-tree-row a.cat-name-link:hover { color: var(--coral-deep); }
.cat-tree-row .cat-desc {
  font-size: 11px;
  color: var(--ink-faint);
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-tree-row .cat-empty {
  font-size: 10px;
  color: var(--ink-faint);
  font-style: italic;
  flex-shrink: 0;
}
.cat-tree-row .row-actions {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-shrink: 0;
  opacity: 0;
  transition: opacity 0.12s;
  font-size: 11px;
}
.cat-tree-row:hover .row-actions,
.cat-tree-row:focus-within .row-actions { opacity: 1; }
.cat-tree-row .row-actions a,
.cat-tree-row .row-actions button {
  color: var(--ink-faint);
  text-decoration: none;
  border: 0;
  background: transparent;
  font-size: 11px;
  cursor: pointer;
  padding: 0;
}
.cat-tree-row .row-actions a:hover,
.cat-tree-row .row-actions button:hover { color: var(--coral-deep); }

/* ============================================================
   AI CHIPS — slice 24s.3 (used by catalog rows + setup cards)
   Material pill + small color swatches. .cs is a 8px square.
   ============================================================ */
.ai-chips {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 2px;
}
.material-chip {
  background: var(--navy);
  color: var(--paper);
  font-size: 8.5px;
  font-weight: 500;
  line-height: 1;
  padding: 2px 5px;
  border-radius: var(--r-sm);
  letter-spacing: 0.02em;
}
.color-row {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}
.color-row .cs {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 1.5px;
  border: 1px solid rgba(31, 27, 24, 0.12);
}
/* X5/X7 — Document attachment chip in chat bubbles. Mirrors the
   portal-side styling so operator + client see attachments the same
   way. Rendered by templating.py:render_comment_body when a comment
   body contains `[filename](url)` markdown pointing at the portal's
   comment-attachment endpoint. */
.bill-bubble-doc {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 10px 4px 8px;
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  font-size: 12px;
  color: var(--ink);
  text-decoration: none;
  max-width: 100%;
}
.bill-bubble-doc:hover {
  border-color: var(--coral);
  color: var(--coral-deep);
}
.bill-bubble-doc__icon { font-size: 14px; flex-shrink: 0; }
.bill-bubble-doc__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.bill-bubble-doc:first-child { margin-top: 0; }
.bill-bubble--operator .bill-bubble-doc {
  background: rgba(255, 253, 248, 0.95);
  border-color: rgba(255, 253, 248, 0.4);
}

/* ============================================================
   Rail · Comments tab — operator-side chat log (ClientComment +
   OperatorComment merged) under a tab next to Canvas. Mirrors
   the portal's chat-bubble pattern but compressed for the rail.
   ============================================================ */
.rail-tab__badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 16px; padding: 0 5px;
  margin-left: 6px;
  background: var(--coral, #c36b5a);
  color: #fff;
  border-radius: 9px;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
}

.rail-chat-log { display: flex; flex-direction: column; gap: var(--s-2); }
.rail-chat-row {
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 6px;
  align-items: flex-start;
}
.rail-chat-row--operator { grid-template-columns: 1fr 28px; }
.rail-chat-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 600; font-size: 11px;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  color: var(--ink-soft);
}
.rail-chat-avatar--operator {
  background: var(--coral-soft, rgba(195,107,90,0.18));
  border-color: var(--coral, #c36b5a);
  color: var(--coral-deep, #8a3f30);
}
.rail-chat-row--operator .rail-chat-avatar { grid-column: 2; }
.rail-chat-row--operator .rail-chat-bubble-wrap { grid-column: 1; grid-row: 1; }
.rail-chat-bubble-wrap { display: flex; flex-direction: column; min-width: 0; }
.rail-chat-row--operator .rail-chat-bubble-wrap { align-items: flex-end; }
.rail-chat-meta {
  font-family: var(--mono); font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  text-transform: uppercase;
  margin-bottom: 2px;
  display: flex; gap: 4px; align-items: baseline; flex-wrap: wrap;
}
.rail-chat-meta strong { color: var(--ink-soft); font-weight: 700; }
.rail-chat-tag {
  background: var(--coral-soft, rgba(195,107,90,0.18));
  color: var(--coral-deep, #8a3f30);
  padding: 1px 5px;
  border-radius: 3px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.rail-chat-when { color: var(--ink-faint); }
.rail-chat-bubble {
  padding: 5px 9px;
  font-size: 12px;
  line-height: 1.35;
  white-space: pre-wrap;
  word-break: break-word;
  border-radius: 10px;
  max-width: 100%;
  width: fit-content;
}
.rail-chat-bubble--client {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  color: var(--ink);
  border-radius: 10px 10px 10px 2px;
}
.rail-chat-bubble--operator {
  background: var(--coral, #c36b5a);
  color: #fff;
  border-radius: 10px 10px 2px 10px;
}

/* ============================================================
   Operator-side overall-feedback section — sits below the canvas
   instructions. Mirrors the portal's "Overall feedback on the
   seating chart" card. layout.general thread only.
   ============================================================ */
.layout-general-feedback {
  margin: var(--s-3) var(--s-4) var(--s-4);
  padding: var(--s-4);
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
}
.layout-general-feedback__head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--s-3); flex-wrap: wrap;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-faint);
  margin-bottom: var(--s-3);
}
.layout-general-feedback__head h3 {
  font-family: var(--display); font-weight: 500;
  font-size: 18px; color: var(--navy); margin: 0;
}
.layout-general-feedback__head .mono { font-size: 11px; }
.layout-general-feedback__log {
  display: flex; flex-direction: column; gap: var(--s-3);
  margin-bottom: var(--s-3);
  max-height: 400px;
  overflow-y: auto;
}
.layout-general-feedback__row {
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: var(--s-2);
  align-items: flex-start;
}
.layout-general-feedback__row--operator { grid-template-columns: 1fr 32px; }
.layout-general-feedback__row--operator .layout-general-feedback__avatar { grid-column: 2; }
.layout-general-feedback__row--operator .layout-general-feedback__bubble-wrap { grid-column: 1; grid-row: 1; align-items: flex-end; }
.layout-general-feedback__avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 600; font-size: 11px;
  color: var(--ink-soft);
}
.layout-general-feedback__avatar--operator {
  background: var(--coral-soft, rgba(195,107,90,0.18));
  border-color: var(--coral, #c36b5a);
  color: var(--coral-deep, #8a3f30);
}
.layout-general-feedback__bubble-wrap { display: flex; flex-direction: column; min-width: 0; }
.layout-general-feedback__meta {
  font-family: var(--mono); font-size: 9px;
  letter-spacing: 0.08em; color: var(--ink-faint);
  text-transform: uppercase;
  margin-bottom: 3px;
}
.layout-general-feedback__meta strong { color: var(--ink-soft); }
.layout-general-feedback__bubble {
  padding: 6px 10px;
  font-size: 13px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
  border-radius: 10px;
  max-width: 100%;
  width: fit-content;
}
.layout-general-feedback__bubble--client {
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  color: var(--ink);
  border-radius: 10px 10px 10px 2px;
}
.layout-general-feedback__bubble--operator {
  background: var(--coral, #c36b5a);
  color: #fff;
  border-radius: 10px 10px 2px 10px;
}
.layout-general-feedback__form textarea {
  width: 100%; min-height: 70px;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper);
  font: inherit; font-size: 13px;
  resize: vertical;
}
.layout-general-feedback__post-row {
  display: flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-2);
}
.layout-general-feedback__post-row .mono { flex: 1; font-size: 11px; }

/* Comments rail panel sizing — applied ONLY when the tab is active.
   Inline display: flex on the panel previously won the specificity
   war against `.rail-panel { display: none }`, leaking the Comments
   content into other tabs (Canvas / Table view / Guests / Library /
   Style / Legend / Items). Class-gated rule honors the base hide. */
.rail-panel--comments.active {
  display: flex;
  flex-direction: column;
  max-height: 480px;
  overflow-y: auto;
}

/* ============================================================
   Dashboard · Client-comment inbox section
   ----------
   Mirrors wireframes/operator_inbox_mockup.html · same row anatomy
   (dot · avatar · main · pill), same state colors. Lives between
   the page-head greeting and the stat-card row.
   ============================================================ */
.dashboard-inbox {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s-4);
  margin-bottom: var(--s-5);
}
.dashboard-inbox__head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--s-3); flex-wrap: wrap;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-faint);
  margin-bottom: var(--s-3);
}
.dashboard-inbox__head h2 {
  font-family: var(--display); font-weight: 500; font-size: 22px;
  color: var(--navy); margin: 0;
}
.dashboard-inbox__counts {
  font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.08em; color: var(--ink-faint);
  text-transform: uppercase; margin: 4px 0 0;
}
.dashboard-inbox__counts strong { color: var(--coral-deep, #8a3f30); font-weight: 700; }
.dashboard-inbox__sort {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px;
  cursor: pointer;
}
.dashboard-inbox__sort .mono {
  font-size: 10px; letter-spacing: 0.12em; color: var(--ink-faint);
}
.dashboard-inbox__select {
  padding: 5px 10px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--paper);
  font: inherit; font-size: 12px;
  color: var(--ink);
  cursor: pointer;
}
.dashboard-inbox__select:hover { border-color: var(--coral, #c36b5a); }
.dashboard-inbox__select:focus {
  outline: none;
  border-color: var(--coral, #c36b5a);
  box-shadow: 0 0 0 2px rgba(195,107,90,0.18);
}

.dashboard-inbox__group-header {
  font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.14em; font-weight: 700;
  color: var(--ink-soft); text-transform: uppercase;
  margin: var(--s-3) 0 var(--s-2);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line-faint);
}
.dashboard-inbox__group-header:first-of-type { margin-top: 0; }
.dashboard-inbox__footer {
  text-align: center; margin: var(--s-3) 0 0;
  font-family: var(--mono); font-size: 11px; color: var(--ink-faint);
}

.inbox-list {
  display: flex; flex-direction: column;
  border: 1px solid var(--line-faint);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.inbox-row {
  display: grid;
  grid-template-columns: 12px 40px 1fr auto;
  gap: var(--s-3);
  align-items: center;
  padding: 10px var(--s-3);
  border-bottom: 1px solid var(--line-faint);
  cursor: pointer;
  text-decoration: none;
  color: var(--ink, #1f1b18);
  transition: background 120ms;
}
.inbox-row:last-of-type { border-bottom: 0; }
.inbox-row:hover { background: var(--paper-2, #f5efe2); }
.inbox-row__dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: transparent;
  justify-self: center;
}
.inbox-row[data-state="unread"]   .inbox-row__dot { background: var(--red, #d83a3a); }
.inbox-row[data-state="awaiting"] .inbox-row__dot { background: var(--gold, #b08a4a); }
.inbox-row[data-state="replied"]  .inbox-row__dot { background: transparent; }
.inbox-row__avatar {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--paper-2, #f5efe2); border: 1px solid var(--line-soft, #d7ccb9);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 600; font-size: 13px;
  color: var(--ink-soft, #5a4f44);
}
.inbox-row__main { min-width: 0; }
.inbox-row__head {
  font-size: 13px; margin-bottom: 3px;
  display: flex; gap: 5px; flex-wrap: wrap; align-items: baseline;
}
.inbox-row__head .name { font-weight: 600; color: var(--ink, #1f1b18); }
.inbox-row__head .sep { color: var(--ink-faint, #9b8e80); }
.inbox-row__head .event { color: var(--ink-soft, #5a4f44); }
.inbox-row__head .crumb {
  font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.08em; color: var(--ink-faint, #9b8e80);
}
.inbox-row__body {
  font-size: 13px; color: var(--ink-soft, #5a4f44); line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.inbox-row__foot {
  font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.06em; color: var(--ink-faint, #9b8e80);
  margin-top: 4px;
}
.inbox-row__foot .arrow { color: var(--coral, #c36b5a); margin-left: 6px; }

.pill--unread {
  background: rgba(195,107,90,0.15); color: var(--coral-deep, #8a3f30);
  border: 1px solid var(--coral, #c36b5a);
  padding: 4px 9px; border-radius: 999px;
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.pill--awaiting {
  background: rgba(176,138,74,0.14); color: var(--gold, #b08a4a);
  border: 1px solid var(--gold, #b08a4a);
  padding: 4px 9px; border-radius: 999px;
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.pill--replied {
  background: rgba(91,123,79,0.14); color: var(--green, #5b7b4f);
  border: 1px solid var(--green, #5b7b4f);
  padding: 4px 9px; border-radius: 999px;
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
}

/* ============================================================
   Inline-thread images — bounded so a huge upload doesn't blow
   out the chat bubble width. Was scoped inside _billing_tab.html's
   <style> block, which meant the proposals page (a separate route,
   doesn't include billing tab) had no constraint and rendered
   client uploads at full size. Migrating to v2.css fixes that.
   ============================================================ */
.bill-bubble-img {
  display: block;
  max-width: 100%;
  max-height: 320px;
  margin-top: 6px;
  border-radius: 8px;
  cursor: zoom-in;
}
.bill-bubble-img:first-child { margin-top: 0; }

/* ============================================================
   inline_thread chrome (.bill-thread-* + .bill-bubble-*) — was
   scoped inside _billing_tab.html's <style> block. Migrating to
   v2.css so any page using the inline_thread macro (proposals,
   future surfaces) renders the chat-bubble correctly.

   In particular: the avatar <span class="bill-bubble-avatar"> has
   no size without this CSS, so its child <img style="width:100%">
   blows up to the natural resolution of the profile picture.
   ============================================================ */
.bill-thread-disclosure { margin-top: 4px; }
.bill-thread-summary {
  cursor: pointer;
  font-size: 11px;
  color: var(--ink-soft);
  padding: 4px 8px;
  background: var(--paper-2);
  border: 1px solid var(--line-faint);
  border-radius: 4px;
  list-style: none;
  display: inline-flex; align-items: center; gap: 6px;
  user-select: none;
}
.bill-thread-summary:hover { color: var(--coral-deep); border-color: var(--coral-soft); }
.bill-thread-summary::-webkit-details-marker { display: none; }
.bill-thread-summary .count {
  font-family: var(--mono);
  font-size: 10px;
  background: var(--paper);
  border: 1px solid var(--line-faint);
  border-radius: 9px;
  padding: 1px 6px;
}
.bill-thread-summary__client-pill {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--coral-deep);
  background: var(--coral-soft);
  padding: 1px 5px;
  border-radius: 3px;
}
.bill-thread-summary__unread {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--coral-deep, #c36b5a);
  border-radius: 9px;
}
.bill-thread-summary__unread[hidden] { display: none; }
.bill-thread-chat {
  margin-top: 6px;
  padding: 12px;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  display: flex; flex-direction: column; gap: 12px;
  max-height: 360px;
  overflow-y: auto;
}
.bill-bubble-row {
  display: flex; gap: 8px;
  align-items: flex-end;
}
.bill-bubble-row--right { justify-content: flex-end; }
.bill-bubble-row--left  { justify-content: flex-start; }
.bill-bubble-wrap {
  display: flex; flex-direction: column;
  max-width: 70%;
  min-width: 0;
}
.bill-bubble {
  padding: 6px 12px;
  font-size: 13px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-wrap: break-word;
  word-break: break-word;
}
.bill-bubble--operator {
  background: var(--coral);
  color: #fff;
  border-radius: 14px 14px 4px 14px;
}
.bill-bubble--client {
  background: var(--paper-2);
  color: var(--ink);
  border: 1px solid var(--line-soft);
  border-radius: 14px 14px 14px 4px;
}
.bill-bubble-meta {
  margin: 4px 0 0 6px;
  font-size: 9px;
  letter-spacing: 0.08em;
}
.bill-bubble-meta--right { text-align: right; margin: 4px 6px 0 0; }
.bill-bubble-avatar {
  width: 26px; height: 26px;
  flex-shrink: 0;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px;
  font-weight: 600;
  overflow: hidden;
}
.bill-bubble-avatar--operator {
  background: var(--coral-deep);
  color: #fff;
}
.bill-bubble-avatar--client {
  background: var(--paper-2);
  color: var(--ink);
  border: 1px solid var(--line-soft);
}
.bill-thread-reply {
  margin-top: 8px;
  display: flex; gap: 8px; align-items: flex-end;
  padding: 8px;
  background: var(--paper-2);
  border-radius: 6px;
}
.bill-thread-reply__textarea {
  flex: 1;
  resize: vertical;
  min-height: 36px;
  font-family: inherit;
  font-size: 13px;
}
.bill-thread-reply__submit { flex-shrink: 0; }
.bill-thread-reply__buttons {
  display: flex; align-items: center; gap: 8px; flex-shrink: 0;
}
.bill-thread-reply__file-label {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 6px;
  cursor: pointer;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  font-size: 16px;
  user-select: none;
  transition: background 0.15s;
}
.bill-thread-reply__file-label:hover {
  background: var(--paper-2);
  border-color: var(--coral);
}
.bill-thread-reply__file-label.is-uploading {
  opacity: 0.6; cursor: wait;
}
.bill-thread-reply__textarea.is-dragover {
  border: 2px dashed var(--coral) !important;
  background: var(--coral-soft) !important;
}

/* ── Per-line cart-thread thumbnail (planner-side click target) ──
   Replaces the old .cart-thread-icon button. The thumbnail itself is
   the modal trigger; a red badge in the top-right corner carries the
   unread-count signal. "Img" placeholder when the line has no image.
   --cart-thumb-size set inline per row to scale (56px default, 40px
   for line items, 64px for selections). */
.cart-thread-thumb {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cart-thumb-size, 56px);
  height: var(--cart-thumb-size, 56px);
  padding: 0;
  background: var(--paper-2, #faf4e6);
  border: 1px solid var(--line-soft, #d8cdb8);
  border-radius: var(--r-sm, 4px);
  /* `overflow: visible` so the unread badge can float OUTSIDE the
     thumbnail (per 2026-05-28 UX request). Image clipping is handled
     by the image's own border-radius below — the thumb box still
     reads as a rounded square. */
  overflow: visible;
  cursor: pointer;
  vertical-align: middle;
  transition: border-color 0.12s, transform 0.08s, box-shadow 0.12s;
  flex-shrink: 0;
}
.cart-thread-thumb:hover {
  border-color: var(--coral, #c36b5a);
  box-shadow: 0 0 0 2px rgba(195, 107, 90, 0.15);
}
.cart-thread-thumb:active { transform: scale(0.97); }
.cart-thread-thumb__img {
  width: 100%; height: 100%;
  object-fit: contain; display: block;
  background: var(--paper-2, #faf4e6);
  border-radius: var(--r-sm, 4px);
}
.cart-thread-thumb__placeholder {
  font-family: var(--display, serif);
  font-size: calc(var(--cart-thumb-size, 56px) * 0.32);
  font-weight: 400;
  font-style: italic;
  color: var(--ink-faint, #9b8e80);
  letter-spacing: 0.04em;
  user-select: none;
}
/* Unread state — outline goes coral, plus a red badge corner-pinned. */
.cart-thread-thumb.is-unread {
  border-color: var(--coral, #c36b5a);
  box-shadow: 0 0 0 2px rgba(195, 107, 90, 0.20);
}
.cart-thread-thumb__badge {
  position: absolute;
  top: -6px; right: -6px;
  min-width: 18px; height: 18px;
  padding: 0 5px;
  background: var(--ink, #1f1b18);
  color: var(--paper, #fff);
  border: 2px solid var(--paper, #fff);
  border-radius: 999px;
  font-family: var(--mono, monospace);
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  font-weight: 600;
  line-height: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.cart-thread-thumb__badge--unread {
  background: #d63b3b;
  box-shadow: 0 0 0 1px rgba(214, 59, 59, 0.25);
}

/* ── Two-column thread modal (image left, chat right) ─────────────
   When the thread subject has a full_image_url, the modal body
   splits into a 1:1 row: image panel on the left, chat (existing
   scroll + reply form) on the right. Drops back to single-column
   chat when there's no image. Image col uses object-fit: contain
   so portraits + landscape orientations both display readably. */
.thread-modal-two-col {
  display: flex;
  flex: 1;
  min-height: 0;
  background: var(--paper);
}
.thread-modal-chat-col {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  min-height: 0;
}
.thread-modal-image-col {
  flex: 0 0 45%;
  max-width: 45%;
  background: var(--paper-2, #faf4e6);
  border-right: 1px solid var(--line-soft, #eae0d0);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-3, 12px);
  min-height: 0;
}
.thread-modal-image-col__img {
  max-width: 100%;
  max-height: 60vh;
  object-fit: contain;
  display: block;
  border-radius: var(--r-sm, 4px);
}
@media (max-width: 720px) {
  .thread-modal-two-col { flex-direction: column; }
  .thread-modal-image-col {
    flex: 0 0 auto; max-width: 100%;
    border-right: 0;
    border-bottom: 1px solid var(--line-soft, #eae0d0);
    max-height: 30vh;
  }
}
