/* Host inside V1's _tableGrid: position:fixed below the ~60px billing navbar; z-index above V1's legacy canvas controls. Always mounted (listens for tab changes) but invisible + click-through until V2 is actively rendering. */
.bs1-tlv2-runtime-host {
  position: fixed;
  left: 0;
  right: 0;
  top: 60px;
  bottom: 0;
  background: transparent;
  pointer-events: none;
  z-index: 10;
}

/* The shared legend bar (overflow order-type tabs + search + day-sale/unsettled)
 * is shown above the canvas in V2 table mode. Drop the fixed host below it so the
 * canvas never covers the bar: navbar (60px) + legend (60px) = 120px. The host's
 * ResizeObserver catches this height change and re-fits the canvas. Toggled by
 * _tableGrid.html only when V2 is rendered (enabled && !loading && !error) — during
 * loading/error the legend is hidden and the host stays at top:60px (full surface). */
.bs1-tlv2-runtime-host.bs1-tlv2-host-below-legend {
  top: 120px;
}
/* Both breakpoints that shrink navbar + legend to 52px each — short viewports
 * (billingSkin1.css @media max-height:700px) AND narrow viewports
 * (@media max-width:480px) → 52 + 52 = 104px. Comma = OR, so either fires;
 * keep both terms in sync with those two billingSkin1.css blocks. */
@media (max-height: 700px), (max-width: 480px) {
  .bs1-tlv2-runtime-host.bs1-tlv2-host-below-legend {
    top: 104px;
  }
}

/* When V2 is actively rendering a designed floor, opaque the host + restore
 * pointer events so the canvas owns the billing surface and intercepts clicks. */
body.tlv2-rt-active .bs1-tlv2-runtime-host {
  background: var(--tlv2-canvas-bg-override, var(--floor-bg, #FFFFFF));
  pointer-events: auto;
}

/* Also hide V1's legacy canvas while V2 is actively rendering. */
body.tlv2-rt-active #diningStructureBox {
  display: none !important;
}

/* <tlv2-billing-canvas> runtime canvas embedded in V1 billing. Scoped under .tlv2-rt-shell so nothing leaks to V1; reuses editor classes from design.css. Maps V1 bill-status vocabulary onto readable canvas colors. */

.tlv2-rt-shell {
  /* Runtime canvas mounts in V1 billing OUTSIDE any .tlv2-app/.tlv2-home ancestor, so it carries its own
     palette tokens. These are a deliberate runtime slate grade — NOT required to byte-match the editor
     (.tlv2-app) or floor-list (.tlv2-fl) tokens (those are intentionally distinct palette generations). */
  --accent:        #4185F4;
  --accent-dark:   #2C6BD9;
  --accent-light:  rgba(65, 133, 244, 0.15);
  --text-primary:  #2C3E50;
  --text-muted:    #7F8C8D;
  --border:        #E2E5EA;
  /* Canvas tokens consumed by shared design.css .tlv2-pattern-* / .tlv2-floor-canvas rules; without them the pattern bg-image vars are invalid and the grid disappears in billing. */
  --floor-bg:      #ECEEF2;
  --grid-color:    rgba(0, 0, 0, 0.06);

  position: relative;
  width: 100%;
  height: 100%;
  min-height: 480px;
  /* Themed fill — matches the canvas so scroll gutters blend in. */
  background: var(--tlv2-canvas-bg-override, var(--floor-bg, #FFFFFF));
  overflow: hidden;
  font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
  color: var(--text-primary);
}

.tlv2-rt-loading,
.tlv2-rt-fallback-hint {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: var(--text-muted);
}

/* Error state (transient fetch failure) — floats as a banner so the V1 navbar stays reachable and the cashier can retry. */
.tlv2-rt-error {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(255, 255, 255, 0.88);
}
.tlv2-rt-error-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  background: #FFFFFF;
  border: 1px solid rgba(231, 76, 60, 0.32);
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(4, 9, 17, 0.10);
  color: var(--text-primary);
  max-width: 560px;
}
.tlv2-rt-error-inner > svg { color: #E74C3C; flex-shrink: 0; }
.tlv2-rt-error-text {
  flex: 1;
  font-size: 13px;
  line-height: 1.45;
}
.tlv2-rt-error-text strong { margin-right: 4px; font-weight: 700; }
.tlv2-rt-error-retry {
  flex: none;
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid #E74C3C;
  background: #FFFFFF;
  color: #E74C3C;
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.12s;
}
.tlv2-rt-error-retry:hover { background: #FBE8E5; }
/* Focus ring: :focus base paints on the Chrome ~85 POS webview (which doesn't know :focus-visible, so a
   bare :focus-visible rule would be dropped entirely). @supports demotes it to keyboard-only :focus-visible
   on Chrome >=86 so a tap doesn't flash the ring. */
.tlv2-rt-error-retry:focus { outline: 2px solid #E74C3C; outline-offset: 2px; }
@supports selector(:focus-visible) {
  .tlv2-rt-error-retry:focus { outline: none; }
  .tlv2-rt-error-retry:focus-visible { outline: 2px solid #E74C3C; outline-offset: 2px; }
}

/* Viewport + sizer + canvas (same as editor). Viewport adopts the theme canvas color so aspect-ratio gutters read as canvas, not a white frame; falls back to white when no theme override. */
.tlv2-rt-viewport {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  /* overflow:hidden — fit() always sizes canvas to fit this box, so no scrollbars; locking overflow blocks touch-scroll from interfering with table selection. */
  overflow: hidden;
  display: flex;
  /* PIN TOP-LEFT (2026-06-17): match the EDITOR exactly, which pins the layout to the top-left corner
     (design.controller.js fitToViewport panX/panY=0 for non-negative coords). With BILLING_AUTOFILL off, billing
     is now pure WYSIWYG, so it must also place the fitted block in the SAME corner — centring shifted the whole
     floor right+down vs the editor and read as "doesn't match what I built". Any residual letterbox from the
     uniform fit now lands at the bottom/right, exactly like the editor's droppable-grid margin. (Reverses the
     2026-06-13 "centre the block" choice, which only made sense while auto-fill was on.) */
  align-items: flex-start;
  justify-content: flex-start;
  /* EDGE PAD — uniform breathing margin on all 4 sides (billing only); MUST equal TLV2_FIT.RT_EDGE_PAD (tableLayoutV2.js), which fit() subtracts from the measured avail so the floor fits inside the padded box. The floor pattern paints across the padding, so the margin reads as floor, not a frame. */
  padding: 14px;
  /* background-COLOR (not the shorthand) so the .tlv2-pattern-* grid background-image on this element isn't reset to none. */
  background-color: var(--tlv2-canvas-bg-override, var(--floor-bg, #FFFFFF));
  /* Block iOS rubber-band / pull-to-refresh — touch means "select a table", never a navigation gesture. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: auto;
  touch-action: manipulation;
}
.tlv2-rt-sizer {
  position: relative;
  flex: none;
  /* Smooth size change when zoom % updates (slider drag, +/−, reset). */
  transition: width 0.16s ease-out, height 0.16s ease-out;
}
.tlv2-rt-canvas {
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 0.16s ease-out;
  /* GPU promotion (will-change + translateZ) is GATED behind .tlv2-rt-zooming (toggled by the zoom slider/buttons while interacting): a permanently promoted layer rasterizes text at scale 1 and GPU-minifies it at the fractional fit scale → soft/fuzzy glyphs on every POS whose fit ≠ 1 (the "font not looking nice" half of the complaint). Unpromoted, Chrome re-rasters at the effective scale → crisp text when idle; during a zoom gesture the layer is promoted so the 0.16s transition stays smooth (blur during the gesture is acceptable). `contain: paint` deliberately NOT included — it would clip off-canvas decor; the viewport's overflow:hidden is the real clipper. */
  contain: layout style;
}
.tlv2-rt-canvas.tlv2-rt-zooming {
  /* will-change ALONE does the promoting — the element's transform is an inline ng-style (translate+scale), so a class-level translateZ could never win the cascade anyway. */
  will-change: transform;
}
/* Runtime billing canvas is a read-only view — NOTHING on it should be text-selectable. Applying this at
   the canvas root (not just .tlv2-rt-table) stops a touch tap-drag, or any text that overflows a small
   cell, from triggering the native blue selection highlight that reads as "selected/highlighted" text.
   -webkit-/-moz- prefixes required for the Chrome ~85 POS webview. Scoped to .tlv2-rt-canvas so it can
   never leak into the editor (design.css owns its own user-select rules). */
.tlv2-rt-canvas,
.tlv2-rt-canvas * {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
@media (prefers-reduced-motion: reduce) {
  .tlv2-rt-sizer,
  .tlv2-rt-canvas { transition: none; }
}
/* Suppress transitions during initial mount so the first paint doesn't flash through transition states. Class added at link, dropped via $timeout(...,0) after first rebuildDerived. */
.tlv2-rt-shell.is-mounting,
.tlv2-rt-shell.is-mounting * {
  transition: none !important;
  animation: none !important;
}

/* ── Zones ────────────────────────────────────────────────────────────── */
.tlv2-rt-zone {
  position: absolute;
  box-sizing: border-box;
  border-width: 2px;
  /* No host background — the .tlv2-zone-fill child carries the colour + shape clip; a host bg would paint a rectangle behind circle/polygon zones. Matches editor's .tlv2-zone-area. */
  pointer-events: none;
  /* Bound layout/paint so neighbour items don't recalc when this one moves. */
  contain: layout style;
}
/* Runtime zone label — override only positioning + click-through; inherits typography/pill chrome from the editor rule .tlv2-zone-label in design.css so editor + billing read identically. */
.tlv2-rt-zone .tlv2-zone-label {
  position: absolute;
  top: 2px;
  left: 2px;
  pointer-events: none;
}

/* Per-shape border-radius — runtime mirror of the editor's .tlv2-zone-area.tlv2-zone-shape-* border-radius rules in design.css so circle/ellipse/pill match editor; !important overrides the template's inline borderRadius. */
.tlv2-rt-zone.tlv2-zone-shape-circle,
.tlv2-rt-zone.tlv2-zone-shape-ellipse,
.tlv2-rt-zone.tlv2-zone-shape-circle .tlv2-zone-fill,
.tlv2-rt-zone.tlv2-zone-shape-ellipse .tlv2-zone-fill { border-radius: 50% !important; }
.tlv2-rt-zone.tlv2-zone-shape-pill,
.tlv2-rt-zone.tlv2-zone-shape-pill .tlv2-zone-fill { border-radius: 9999px !important; }
/* Polygon shapes — outline painted by SVG polyline in the host; host border suppressed (CSS border can't trace concave inner-step edges). Clip the FILL not the host (clipping the host hid the top-left zone label). Mirror of design.css; keep editor + runtime in lockstep. */
.tlv2-rt-zone.tlv2-zone-shape-diamond,
.tlv2-rt-zone.tlv2-zone-shape-triangle,
.tlv2-rt-zone.tlv2-zone-shape-hexagon,
.tlv2-rt-zone.tlv2-zone-shape-octagon,
.tlv2-rt-zone.tlv2-zone-shape-lshape {
  border: 0 !important;
  border-radius: 0 !important;
}
.tlv2-rt-zone.tlv2-zone-shape-diamond  .tlv2-zone-fill { clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
.tlv2-rt-zone.tlv2-zone-shape-triangle .tlv2-zone-fill { clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.tlv2-rt-zone.tlv2-zone-shape-hexagon  .tlv2-zone-fill { clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%); }
.tlv2-rt-zone.tlv2-zone-shape-octagon  .tlv2-zone-fill { clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%); }
.tlv2-rt-zone.tlv2-zone-shape-lshape   .tlv2-zone-fill { clip-path: polygon(0 0, 50% 0, 50% 50%, 100% 50%, 100% 100%, 0 100%); }
.tlv2-rt-zone .tlv2-zone-polygon-outline {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0; /* inset shorthand is Chrome 87+; POS floor is ~85 */
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

/* Runtime zone fill — inner div in .tlv2-rt-zone painting translucent tint at zone.opacity. Mirror of editor's .tlv2-zone-area .tlv2-zone-fill. */
.tlv2-rt-zone .tlv2-zone-fill {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0; /* inset shorthand is Chrome 87+; POS floor is ~85 */
  pointer-events: none;
  z-index: 0;
}

/* ── Text overlays ────────────────────────────────────────────────────── */
.tlv2-rt-text {
  position: absolute;
  /* content-box: stored w/h are the text CONTENT size (editor's fitTextOverlaySize), so padding renders outside
     and text never clips. Mirrors editor .tlv2-text-overlay; both share the global border-box reset otherwise. */
  box-sizing: content-box;
  /* Column flex + justify-content:center vertically centres the text block (mirrors editor .tlv2-text-overlay). */
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 6px;
  pointer-events: none;
  user-select: none;
  contain: layout style;
  /* Clip overflow like editor's .tlv2-text-overlay so long text can't escape; the width:100% inner below centers + wraps. */
  overflow: hidden;
  /* Mirrors editor: labels stay above tables/zones/decor so they read clearly when placed over a table. */
  z-index: 60;
}
/* Mirrors editor's .tlv2-text-overlay-inner: full-width (text-align takes effect), pre-wrap (newlines survive), break-word (no horizontal overflow). Keep in lockstep with design.css. */
.tlv2-rt-text-inner {
  width: 100%;
  line-height: 1.2;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Decorations ──────────────────────────────────────────────────────────
   Runtime mirrors editor markup. Rules MUST be scoped under .tlv2-rt-canvas — the .tlv2-decor* classes are shared with the editor (design.css); an unscoped rule here leaked into the editor and disabled drag/resize. */
.tlv2-rt-canvas .tlv2-decor {
  position: absolute;
  pointer-events: none;
  border-radius: 4px;
  box-sizing: border-box;
  contain: layout style;
}
/* Emoji-rendered decoration body (mirrors design.css). Scoped under .tlv2-rt-canvas so it doesn't leak into the editor. font-size set inline per decoration. */
.tlv2-rt-canvas .tlv2-decor-wall-body {
  width: 100%;
  height: 100%;
  border-radius: 3px;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.18);
}
/* Curved + round wall renderers — scoped under .tlv2-rt-canvas so the runtime rule doesn't leak into the editor. non-scaling-stroke keeps thickness constant across zoom + host W:H ratio. */
.tlv2-rt-canvas .tlv2-decor-wall-curved-svg,
.tlv2-rt-canvas .tlv2-decor-wall-round-svg,
.tlv2-rt-canvas .tlv2-decor-wall-semi-svg,
.tlv2-rt-canvas .tlv2-decor-door-double-svg,
.tlv2-rt-canvas .tlv2-decor-door-single-svg,
.tlv2-rt-canvas .tlv2-decor-door-single-right-svg,
.tlv2-rt-canvas .tlv2-decor-door-svg,
.tlv2-rt-canvas .tlv2-decor-reception-desk-svg {
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  overflow: visible;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.14));
}
/* New decor types — keep visuals in lockstep with design.css. */
.tlv2-rt-canvas .tlv2-decor-pillar-svg {
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.22));
}
.tlv2-rt-canvas .tlv2-decor-window-body {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 2px;
  background:
    linear-gradient(
      to bottom,
      rgba(0, 0, 0, 0.24) 0%,
      rgba(0, 0, 0, 0.24) 18%,
      rgba(255, 255, 255, 0.55) 18%,
      rgba(255, 255, 255, 0.55) 82%,
      rgba(0, 0, 0, 0.24) 82%,
      rgba(0, 0, 0, 0.24) 100%
    ),
    var(--tlv2-decor-window-tint, #8FB3DC);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.20),
    inset 0 1px 0 rgba(255, 255, 255, 0.30);
  display: flex;
  align-items: stretch;
  justify-content: space-evenly;
  overflow: hidden;
}
.tlv2-rt-canvas .tlv2-decor-window-glass {
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  height: 1.5px;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.35);
  pointer-events: none;
}
.tlv2-rt-canvas .tlv2-decor-window-mullion {
  width: 2px;
  background: rgba(0, 0, 0, 0.22);
  align-self: stretch;
  z-index: 1;
}

/* ── Tables ───────────────────────────────────────────────────────────── */
.tlv2-rt-table {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border: 2px solid #D0D4DB;
  background: #FFFFFF;
  color: var(--text-primary);
  border-radius: 14px;
  cursor: pointer;
  /* Touch tap-target floor — POS touch screen, so a table never renders below a tappable size (mirrors editor). */
  min-width: 48px;
  min-height: 48px;
  /* Smooth status-colour flips. V1 attaches bills async after the layout fetch, so cells briefly paint 'empty' before their real colour; the 140ms transition fades that instead of flickering. */
  transition: background-color 0.14s ease, border-color 0.14s ease,
              color 0.14s ease, transform 0.12s, box-shadow 0.14s, filter 0.14s;
  -webkit-user-select: none;
  user-select: none;
  contain: layout style;
}
.tlv2-rt-table:hover {
  transform: scale(1.02);
  box-shadow: 0 4px 14px rgba(4, 9, 17, 0.15);
  z-index: 2;
}
.tlv2-rt-table:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(65, 133, 244, 0.40);
  z-index: 3;
}
@supports selector(:focus-visible) {
  .tlv2-rt-table:focus { box-shadow: none; }
  .tlv2-rt-table:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(65, 133, 244, 0.40);
    z-index: 3;
  }
}
/* Runtime corner radii — kept in lockstep with editor (.tlv2-table-shape.tlv2-* in design.css) so a designed table renders with identical curvature in billing. */
.tlv2-rt-table.tlv2-round  { border-radius: 50%; }
.tlv2-rt-table.tlv2-square { border-radius: 12px; }
.tlv2-rt-table.tlv2-rect   { border-radius: 14px; }
.tlv2-rt-table.tlv2-long   { border-radius: 14px; }

.tlv2-rt-table-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.15;
  /* Tightened padding 4→2 + gap 2→1 (2026-06-09) to reclaim vertical room so one more bill line fits on
     short cells WITHOUT shrinking the number. MUST stay in lockstep with fitTableText (H = h-8, GAP = 1) or
     the no-overlap guarantee breaks. */
  padding: 2px;
  gap: 1px;
  text-align: center;
  pointer-events: none;
  /* Final safety net: constrain the number+label stack to the cell and clip any residual so data NEVER paints
     outside the table (fitTableText already scales the stack to fit; this guarantees it for extreme cells). */
  box-sizing: border-box;
  max-width: 100%;
  max-height: 100%;
  overflow: hidden;
}
.tlv2-rt-num {
  font-weight: 700;
  font-size: 1em;
  /* Table number always BLACK (user 2026-06-09) — overrides the inherited status-tinted text colour; bill status
     is still conveyed by the cell background / border / chair dots. Cells stay light (incl. dark-canvas), so it reads. */
  color: #1A1A1A;
  /* Clip + ellipsis so a multi-digit number on a small cell truncates inside the cell border instead of
     painting out onto the canvas (which read as a stray highlighted text sliver). */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tlv2-rt-prefix {
  font-weight: 500;
  font-size: 0.65em;
  margin-right: 2px;
  opacity: 0.85;
}
.tlv2-rt-customer {
  font-size: 0.55em;
  font-weight: 600;
  color: rgba(44, 62, 80, 0.78);
  letter-spacing: 0.02em;
  max-width: 90%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Covers + APC pill (TL). Layout: <covers> | ₹<APC>. Each half gated on its floor flag; the | separator only renders when both are present. */
.tlv2-rt-covers {
  position: absolute;
  /* Badge sits just INSIDE the cell's top-left (was top:-9px overhanging the border, which poked into a neighbouring
     table when tables are placed close together). Inside-the-bounds keeps it within the table's own footprint; the
     fit logic (numTop clearance) keeps it off the number. fit-content hugs the full "covers | APC" text. */
  top: 2px;
  left: 2px;
  width: -moz-fit-content;
  width: fit-content;
  /* No cell-relative max-width. The old `calc(100% - 6px)` cap chopped a long APC
     off the right of the dark bg — there is no overflow/ellipsis here, so the
     value spilled past the pill background and read as "cut". fitTableText already
     width-gates this chip (drops APC, then covers, when the cell is too narrow), so
     when it IS shown the content fits the cell; sizing the pill to that content
     guarantees the background always wraps the whole "covers | APC". */
  max-width: none;
  min-width: 16px;
  min-height: 17px;   /* was height:17px — grow with the (now table-scaled) chip font on big tables */
  padding: 0 0.6em;   /* em so the badge chrome scales with the inline font-size (was 0 7px) */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;         /* em (was 5px) */
  font-size: 10px;
  font-weight: 700;
  border-radius: 999px;
  background: rgba(4, 9, 17, 0.64);
  color: #FFFFFF;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tlv2-rt-covers-num,
.tlv2-rt-covers-apc {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}
.tlv2-rt-covers-sep {
  opacity: 0.50;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  line-height: 1;
}


/* Chair dots — absolute via inline left/top from the directive. Sits OUTSIDE the table bounds (border is the reference edge). */
.tlv2-rt-chair {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #9DA4AF;
  border: 2.5px solid #FFFFFF;
  box-shadow: 0 1px 3px rgba(4, 9, 17, 0.18), 0 0 0 0.5px rgba(4, 9, 17, 0.08);
  pointer-events: none;
  z-index: 1;
}
/* Chair dots inherit the table's status colour via the per-status --rt-status custom property, so seat indicators stay in the same family as the cell. */
.tlv2-rt-running  .tlv2-rt-chair,
.tlv2-rt-printed  .tlv2-rt-chair,
.tlv2-rt-reserved .tlv2-rt-chair,
.tlv2-rt-online   .tlv2-rt-chair,
.tlv2-rt-hold     .tlv2-rt-chair,
.tlv2-rt-split    .tlv2-rt-chair {
  background: var(--rt-status);
}

/* Partial-occupancy override: chairs at index ≥ cover-count render as "open seats at an occupied table". Specificity (0,3,0) beats the per-status rule (0,2,0). Grey + 60% opacity gives three states: occupied / open-at-occupied / empty. */
.tlv2-rt-table .tlv2-rt-chair.tlv2-rt-chair-empty {
  background: #C8CDD4;
  opacity: 0.62;
}

/* Sofa partial-occupancy overlay — sofas have no chair dots (cushions are the seats), so partial coverage = white-tinted overlays on empty cushions. Positioned over the seat area (between armrests, below back-band). */
.tlv2-rt-table.tlv2-sofa .tlv2-rt-sofa-overlay {
  position: absolute;
  /* Coords mirror the DOM seat-row in design.css (top 27% / bottom 6% / left+right 6%) so the wash lines up with the cushion strip. */
  top: 27%;
  bottom: 6%;
  left: 6%;
  right: 6%;
  display: flex;
  gap: 2px;          /* match seat-row gap so seams align */
  pointer-events: none;
  z-index: 1;        /* sits ABOVE sofa-art (z:0), BELOW table-inner (z:2) */
  border-radius: 8px;
  overflow: hidden;
}
.tlv2-rt-table.tlv2-sofa .tlv2-rt-sofa-cushion-overlay {
  flex: 1;
  background: transparent;
  transition: background 140ms ease;
}
.tlv2-rt-table.tlv2-sofa .tlv2-rt-sofa-cushion-overlay.is-empty {
  background: rgba(255, 255, 255, 0.55);
}

/* Sofa — same status palette as other tables; distinguished by asymmetric border-radius + DOM cushion render via .tlv2-sofa-art (legacy ::before gradient divider retired 2026-05-11, see `content: none` below). */
.tlv2-rt-table.tlv2-sofa,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa {
  border-radius: 10px;
}
/* Sofa floor-plan render — DOM-based (mirrors editor tableShape.template.html 47-53); base geometry from design.css 4680+. Billing overrides below keep back-band + cushions TRANSLUCENT so status tint reads through. Cushion count via the tlv2-sofa-c<N> class. */
.tlv2-rt-table.tlv2-sofa .tlv2-sofa-art,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa .tlv2-sofa-art {
  /* The sofa-art layer's box geometry comes from design.css (`inset:0`), but the runtime page does NOT
     load design.css and `inset` shorthand is Chrome 87+ (POS floor is ~85) — so re-declare the offsets
     here as longhand or the cushions collapse to content size and mis-position on every sofa table. */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: hidden;
  border-radius: inherit;
  z-index: 0;
}
.tlv2-rt-table.tlv2-sofa .tlv2-sofa-back-band,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa .tlv2-sofa-back-band {
  /* Geometry — design.css isn't loaded on runtime, so re-declare it here (longhand, Chrome-85-safe);
     without this the backrest collapses to a 0x0 inline span and the sofa shows as a flat rect. Mirrors design.css:4665. */
  display: block;
  position: absolute;
  top: 5%;
  left: 6%;
  right: 6%;
  height: 18%;
  border-radius: 7px;
  /* Darken-over (not solid #DDE0E5) so status tint bleeds through and the backrest reads as a raised cushion of the same colour. */
  background:
    radial-gradient(ellipse at 50% 28%, rgba(255, 255, 255, 0.18), transparent 70%),
    rgba(0, 0, 0, 0.10);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -2px 4px rgba(0, 0, 0, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.05);
}
.tlv2-rt-table.tlv2-sofa .tlv2-sofa-seat-row,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa .tlv2-sofa-seat-row {
  /* Geometry — re-declared for runtime (mirrors design.css:4685); else the seat row + cushions collapse. */
  display: flex;
  position: absolute;
  top: 27%;
  bottom: 6%;
  left: 6%;
  right: 6%;
  gap: 2px;
  border-radius: 8px;
  /* Slightly darker seam between cushions (status tint shows through). */
  background: rgba(0, 0, 0, 0.08);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04);
}
.tlv2-rt-table.tlv2-sofa .tlv2-sofa-seat-cushion,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa .tlv2-sofa-seat-cushion {
  /* Geometry — re-declared for runtime (mirrors design.css:4698). */
  display: block;
  flex: 1;
  min-width: 0;
  border-radius: 6px;
  /* Translucent white lift — status fill blends through; empty sofas show the pale grey body, occupied sofas tint with the status colour. */
  background:
    radial-gradient(ellipse at 50% 28%, rgba(255, 255, 255, 0.45), transparent 70%),
    rgba(255, 255, 255, 0.22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.50),
    0 1px 1px rgba(0, 0, 0, 0.04);
}

/* Empty sofa — cushions render pure white so it reads "available" at a glance (matches editor's untinted #F8F9FB cushion); radial highlight keeps the plump look. */
.tlv2-rt-table.tlv2-sofa.tlv2-rt-empty .tlv2-sofa-seat-cushion {
  background:
    radial-gradient(ellipse at 50% 28%, rgba(255, 255, 255, 0.55), transparent 70%),
    #FFFFFF;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.70),
    0 1px 1px rgba(0, 0, 0, 0.04);
}

/* Disable the legacy ::before cushion-divider (replaced by DOM .tlv2-sofa-art); content:none drops the pseudo, so per-N tlv2-sofa-c<N> ::before styling is retired (inert gradient blocks removed 2026-06-11). */
.tlv2-rt-table.tlv2-sofa::before,
.tlv2-rt-split-tile-shape.tlv2-shape-sofa::before {
  content: none;
}

/* Number + status text above sofa-art (z:0) and partial-occupancy overlay (z:1). */
.tlv2-rt-table.tlv2-sofa .tlv2-rt-table-inner { position: relative; z-index: 2; }

/* (Earlier `.tlv2-long { border-radius: 40px }` removed — it made long tables render as pills; base rule above governs both.) */

/* ── Zoom controls (bottom-right) ───────────────────────────── */
/* Glassmorphic horizontal zoom pill: [−] [slider] [+] │ [100%], divider before the % reset. */
.tlv2-rt-zoombar {
  position: absolute;
  right: 10px;
  bottom: 10px;
  /* Compact pill tucked into the corner — slider fills remaining space via flex:1 below. */
  width: 226px;
  box-sizing: border-box;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  height: 32px;
  z-index: 5;

  /* Dark glass — translucent black with backdrop blur. */
  background: linear-gradient(135deg,
    rgba(20, 20, 32, 0.62) 0%,
    rgba(10, 10, 18, 0.80) 100%);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 6px 20px rgba(0, 0, 0, 0.40);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  color: #f0f2f6;
}

/* Drag grip — the ONLY handle that moves the pill (slider / ± / % stay
   interactive). touch-action:none lets us own the touch drag; the JS locks
   vertical, so it only slides along the bottom row. */
.tlv2-rt-zoom-grip {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 24px;
  margin-left: -2px;
  border-radius: 6px;
  color: rgba(240, 242, 246, 0.55);
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  transition: background 0.12s, color 0.12s;
}
.tlv2-rt-zoom-grip svg { fill: currentColor; display: block; }
.tlv2-rt-zoom-grip:hover { color: #ffffff; background: rgba(255, 255, 255, 0.12); }
.tlv2-rt-zoom-grip:focus { outline: none; color: #ffffff; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.45); }
@supports selector(:focus-visible) {
  .tlv2-rt-zoom-grip:focus { box-shadow: none; }
  .tlv2-rt-zoom-grip:focus-visible { outline: none; color: #ffffff; box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.45); }
}
/* While dragging — grabbing cursor across the whole pill + brighten the grip. */
.tlv2-rt-zoombar.is-dragging { cursor: grabbing; }
.tlv2-rt-zoombar.is-dragging .tlv2-rt-zoom-grip { cursor: grabbing; color: #ffffff; }

/* Thin vertical divider between the slider/+/− cluster and the % display */
.tlv2-rt-zoom-divider {
  width: 1px;
  height: 14px;
  background: rgba(255, 255, 255, 0.16);
  margin: 0 2px;
  flex-shrink: 0;
}

/* Horizontal slider — flexes into remaining pill space, no fixed width. */
.tlv2-rt-zoom-slider {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  flex: 1 1 auto;
  min-width: 0;
  height: 20px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: ew-resize;
  outline: none;
  box-sizing: border-box;
}
/* Track — chromium/webkit. Thicker (6px) for a smoother pillow shape. */
.tlv2-rt-zoom-slider::-webkit-slider-runnable-track {
  height: 6px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 3px;
  transition: background 0.14s;
}
.tlv2-rt-zoom-slider:hover::-webkit-slider-runnable-track,
.tlv2-rt-zoom-slider:focus::-webkit-slider-runnable-track {
  background: rgba(255, 255, 255, 0.28);
}
/* Track — firefox */
.tlv2-rt-zoom-slider::-moz-range-track {
  height: 6px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 3px;
}
.tlv2-rt-zoom-slider:hover::-moz-range-track,
.tlv2-rt-zoom-slider:focus::-moz-range-track {
  background: rgba(255, 255, 255, 0.28);
}
/* Filled portion before the thumb — Firefox-only progress fill (Chrome uses the bare track). */
.tlv2-rt-zoom-slider::-moz-range-progress {
  height: 6px;
  background: linear-gradient(90deg, var(--accent), #6BA4FF);
  border-radius: 3px;
}

/* Thumb — chromium/webkit. Slightly bigger + soft glow. */
.tlv2-rt-zoom-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #FFFFFF;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35), 0 0 0 0 rgba(65, 133, 244, 0);
  margin-top: -4.5px;
  cursor: ew-resize;
  transition: transform 0.14s ease, background 0.14s, box-shadow 0.14s;
}
.tlv2-rt-zoom-slider:hover::-webkit-slider-thumb {
  transform: scale(1.18);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35), 0 0 0 6px rgba(65, 133, 244, 0.25);
}
.tlv2-rt-zoom-slider:active::-webkit-slider-thumb {
  background: var(--accent-dark);
  transform: scale(1.10);
}
/* Thumb — firefox */
.tlv2-rt-zoom-slider::-moz-range-thumb {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #FFFFFF;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  cursor: ew-resize;
  transition: transform 0.14s ease, background 0.14s;
}
.tlv2-rt-zoom-slider:hover::-moz-range-thumb { transform: scale(1.18); }

/* (Dark-canvas variant removed — base pill is already dark glass, consistent across themes.) */
/* Compact glass-pill children — bare buttons inherit the pill's dark surface; hover paints a light disk so it reads as interactive. */
.tlv2-rt-zoom-btn {
  width: 22px;
  height: 22px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: #f0f2f6;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 0.12s, color 0.12s, transform 0.10s;
  font-family: inherit;
}
.tlv2-rt-zoom-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #ffffff;
}
.tlv2-rt-zoom-btn:active { transform: scale(0.94); }
.tlv2-rt-zoom-btn:focus {
  outline: none;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.50);
}
@supports selector(:focus-visible) {
  .tlv2-rt-zoom-btn:focus { box-shadow: none; }
  .tlv2-rt-zoom-btn:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.50);
  }
}

/* % display — text-style, slightly wider to fit "100%" / "250%". */
.tlv2-rt-zoom-pct {
  width: auto;
  min-width: 36px;
  padding: 0 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  border-radius: 13px;
}

/* Status palette — mirrors V1 bill-state vocabulary (empty→white, running→violet, printed→emerald, reserved→teal, online→coral, hold→amber), saturation tuned to V1 billingSkin1 for POS glare. Three layered tones per status: --rt-status (border/chair/pill), --rt-status-bg (pastel cell fill), --rt-status-text (dark, WCAG AA on fill). Empty = white card with dashed grey outline. */
.tlv2-rt-empty {
  --rt-status: #D5D9E0;
  --rt-status-bg: #FFFFFF;
  --rt-status-text: #A8AEB8;
  border: 2px dashed var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-running {
  --rt-status: #7C4DFF;
  --rt-status-bg: #ECE3FF;
  --rt-status-text: #2A1F66;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-printed {
  --rt-status: #34A853;
  --rt-status-bg: #DAEDD9;
  --rt-status-text: #1A4D2C;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-reserved {
  --rt-status: #DBAD22;
  --rt-status-bg: #FFF4D2;
  --rt-status-text: #5C4100;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-online {
  --rt-status: #D85565;
  --rt-status-bg: #FBDADD;
  --rt-status-text: #5C1438;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-hold {
  --rt-status: #E8915C;
  --rt-status-bg: #F8DDC9;
  --rt-status-text: #4A2300;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}
.tlv2-rt-split {
  --rt-status: #D89578;
  --rt-status-bg: #F8E2D0;
  --rt-status-text: #5A2E14;
  border: 2px solid var(--rt-status);
  background: var(--rt-status-bg);
  color: var(--rt-status-text);
}

/* Number / customer pick up the chip's status-text grade — number heaviest (primary glance target), customer a muted form. */
.tlv2-rt-table:not(.tlv2-rt-empty) .tlv2-rt-num     { font-weight: 800; }
.tlv2-rt-table:not(.tlv2-rt-empty) .tlv2-rt-customer { font-weight: 600; color: inherit; opacity: 0.72; }

/* Dark-canvas overrides for non-chip text — loading + fallback greys fail AA on dark presets (blueprint #1A2744, dark #15181E). Zone label excluded: design.css .tlv2-zone-label is glassmorphic and already reads on dark. */
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-loading,
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-fallback-hint {
  color: rgba(255, 255, 255, 0.78);
}

/* ── Label stack (always-on) ─────────────────────────────────────────────
   Scoped under .tlv2-rt-canvas so it doesn't leak into the editor. Adaptive: inner sets per-cell px font-size (clamped 9–13px), labels use em so the stack scales together; ellipsis truncates overflow. */
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-label {
  display: block;
  font-size: 1em;
  line-height: 1.2;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  padding: 0 4px;
}
/* Centre-stack labels, visual order: waiter > amount > time > customer. ALL render when present (no priority drop); JS (fitTableText) scales the number+stack to fit the cell; CSS handles visuals + ellipsis. */
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-customer { font-weight: 600; }
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-amount   {
  font-weight: 800;            /* still emphasised by weight; size kept at 1em so it packs to one row height */
  letter-spacing: -0.2px;
}

/* Timer pill — bg follows the cell's --rt-status, white text for AA, tabular-nums so the seconds digit doesn't jitter. Clock icon hidden by default (defined below for opt-in templates). */
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-timer {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 9px;            /* slim pill so the timer row matches the others (no overlap with the tighter budget) */
  border-radius: 999px;
  /* Outlined pill (white fill + status outline + clock icon) to match legacy billing timer. The outline is an
     INSET box-shadow, not a border, so it adds 0 to the box height — keeping the pill within the row budget
     (border would make the row ~3px taller than rowH and clip/overlap). Redesigned 2026-05-27. */
  background: rgba(255, 255, 255, 0.88);
  box-shadow: inset 0 0 0 1.5px var(--rt-status, #6BB494);
  color: var(--rt-status-text, #0E5A2C);
  font-weight: 700;
  /* Floor at 11 SCREEN px: the canvas is CSS-scaled by S, so the model-px floor must be 11px × (1/S) to stay 11px
     on screen under zoom — else zoom-out renders it tiny and zoom-in inflates it past the fit's row budget →
     overlap. --tlv2-rt-inv-scale = 1/S (set on .tlv2-rt-canvas); fallback 1 = no zoom. (max() Chrome 79+.) */
  font-size: max(calc(11px * var(--tlv2-rt-inv-scale, 1)), 0.85em);
  letter-spacing: -0.2px;
  font-variant-numeric: tabular-nums;
  line-height: 1.0;
}
/* Icon hidden by default — bare time string reads cleaner; templates add `.tlv2-rt-timer.has-icon` to opt in. */
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-timer-icon {
  display: none;
}
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-timer.has-icon .tlv2-rt-timer-icon {
  display: inline-block;
  flex-shrink: 0;
  vertical-align: middle;
  width: 0.9em;
  height: 0.9em;
}
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-waiter {
  opacity: 0.7;
  font-style: italic;
  font-weight: 600;
  /* Floor at 11 SCREEN px under zoom (see .tlv2-rt-timer note): model floor = 11px × 1/S. */
  font-size: max(calc(11px * var(--tlv2-rt-inv-scale, 1)), 0.9em);
}
/* Merge tag (V1 parity) — muted line: "M : a, b" on primary, "Merged In : X" on secondary. */
.tlv2-rt-canvas .tlv2-rt-table-inner .tlv2-rt-merge-tag {
  /* Floor at 11 SCREEN px under zoom (see .tlv2-rt-timer note): model floor = 11px × 1/S. */
  font-size: max(calc(11px * var(--tlv2-rt-inv-scale, 1)), 0.78em);
  font-weight: 700;
  opacity: 0.85;
  letter-spacing: -0.2px;
  white-space: nowrap;
}
/* Secondary of a merged table — bill lives on the primary, so it's inert. Rendered as a SOLID light-grey card
   with black text (user 2026-06-10), NOT a dimmed status cell: the old opacity:0.55 over the dark canvas read as
   muddy grey AND faded the text. Flat grey (no status colour) signals "not actionable"; full-opacity black text
   stays legible. cursor not-allowed.
   pointer-events:none MATCHES the normal billing flow (_billing.html: a merged table gets .disabledButton =
   pointer-events:none) — so the merged secondary is fully inert: NO tap, NO right-click menu (user 2026-06-14
   "follow how normal table works"). Unmerge is done from the PRIMARY/master table (its menu carries "Unmerge"),
   exactly as in the normal flow. (Was pointer-events:ON to allow right-click-unmerge on the secondary; removed.) */
.tlv2-rt-canvas .tlv2-rt-merged-secondary {
  background: #DDE1E6;
  border: 2px solid #C2C8D0;
  color: #1A1A1A;
  opacity: 1;
  cursor: not-allowed;
  pointer-events: none;
}
/* Force the number + "Merged In : X" tag to solid black on the grey card (overrides status-tinted colour and the
   merge-tag's own 0.85 mute). Number heaviest by size; the tag sits below it. */
.tlv2-rt-canvas .tlv2-rt-merged-secondary .tlv2-rt-num,
.tlv2-rt-canvas .tlv2-rt-merged-secondary .tlv2-rt-merge-tag {
  color: #1A1A1A;
  opacity: 1;
}

/* ── 3-dot arc button ────────────────────────────────────────────────────
   Bottom-right of the cell; rendered only when enable_menu_options_via_arc=true AND t.w >= 80 (gated in template). */
.tlv2-rt-canvas .tlv2-rt-arc-btn {
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 32px;
  height: 32px;
  padding: 0;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 16px;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  color: #111;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.tlv2-rt-canvas .tlv2-rt-arc-btn:hover {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.35);
}
.tlv2-rt-canvas .tlv2-rt-arc-btn:focus {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.35);
  outline: none;
  box-shadow: 0 0 0 2px rgba(65, 133, 244, 0.50);
}
@supports selector(:focus-visible) {
  .tlv2-rt-canvas .tlv2-rt-arc-btn:focus { box-shadow: none; }
  .tlv2-rt-canvas .tlv2-rt-arc-btn:focus-visible {
    background: #fff;
    border-color: rgba(0, 0, 0, 0.35);
    outline: none;
    box-shadow: 0 0 0 2px rgba(65, 133, 244, 0.50);
  }
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-arc-btn {
  background: rgba(20, 20, 30, 0.85);
  border-color: rgba(255, 255, 255, 0.2);
  color: #fff;
}

/* ── GM View panel — canvas-theme aware glassmorphic surface ─────────────
 * Adapts to theme via base surface (.is-dark-canvas → dark glass) + accent tint (var(--tlv2-accent-override-rgb,…) into rgba chrome). Default below = LIGHT canvas; dark overrides at section bottom. Slide-out from left of .tlv2-rt-shell; closed = translateX(-100%) so it doesn't intercept clicks. */
.tlv2-rt-shell .tlv2-rt-gm-panel {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 280px;
  /* Light glass — translucent white + hairline accent wash so it reads as canvas family. accent-rgb fallback (65,133,244) = light-theme blue. */
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 0.78) 0%,
    rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.06) 100%);
  -webkit-backdrop-filter: blur(22px) saturate(170%);
  backdrop-filter: blur(22px) saturate(170%);
  color: #1F2937;
  border-right: 1px solid rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.18);
  box-shadow:
    4px 0 30px rgba(0, 0, 0, 0.10),
    inset 1px 0 0 rgba(255, 255, 255, 0.40);
  z-index: 4;
  transform: translateX(-100%);
  transition: transform 200ms ease-out;
  overflow-y: auto;
  font-size: 12px;
}
/* Dark-canvas override — back to dark navy glass + light text. */
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-panel {
  background: linear-gradient(135deg,
    rgba(28, 28, 42, 0.62) 0%,
    rgba(20, 20, 32, 0.74) 100%);
  color: #FFFFFF;
  border-right-color: rgba(255, 255, 255, 0.10);
  box-shadow:
    4px 0 30px rgba(0, 0, 0, 0.35),
    inset 1px 0 0 rgba(255, 255, 255, 0.06);
}
.tlv2-rt-shell .tlv2-rt-gm-panel.is-open { transform: translateX(0); }

.tlv2-rt-shell .tlv2-rt-gm-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 14px 10px 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.tlv2-rt-shell .tlv2-rt-gm-header h3 {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: #1F2937;
}
.tlv2-rt-shell .tlv2-rt-gm-floor {
  font-size: 11px;
  color: rgba(31, 41, 55, 0.55);
  font-weight: 500;
  flex: 1 1 auto;
}
.tlv2-rt-shell .tlv2-rt-gm-close {
  border: 0;
  background: transparent;
  color: rgba(31, 41, 55, 0.55);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  min-width: 28px;
  min-height: 32px;
  transition: color 0.12s;
}
.tlv2-rt-shell .tlv2-rt-gm-close:hover { color: #1F2937; }
/* Dark-canvas overrides — back to white text + translucent borders. */
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-header {
  border-bottom-color: rgba(255, 255, 255, 0.10);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-header h3 { color: #FFFFFF; }
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-floor { color: rgba(255, 255, 255, 0.6); }
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-close { color: rgba(255, 255, 255, 0.7); }
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-close:hover { color: #FFFFFF; }

/* Tab / payment-mode dropdown — V1 dark select */
.tlv2-rt-shell .tlv2-rt-gm-tabsel {
  margin: 12px 14px 6px;
  position: relative;
}
.tlv2-rt-shell .tlv2-rt-gm-tabsel select {
  width: 100%;
  height: 36px;
  padding: 0 32px 0 12px;
  background: rgba(255, 255, 255, 0.55);
  color: #1F2937;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 8px;
  font: inherit;
  font-size: 12.5px;
  outline: none;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  transition: background 0.14s, border-color 0.14s;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.tlv2-rt-shell .tlv2-rt-gm-tabsel select:hover {
  background: rgba(255, 255, 255, 0.75);
  border-color: rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.40);
}
.tlv2-rt-shell .tlv2-rt-gm-tabsel select option {
  background: #FFFFFF;
  color: #1F2937;
}
.tlv2-rt-shell .tlv2-rt-gm-tabsel::after {
  content: '';
  position: absolute;
  right: 12px;
  top: 50%;
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  /* Chevron picks up the canvas theme accent. */
  border-top: 6px solid var(--tlv2-accent-override, var(--accent));
  transform: translateY(-50%);
  pointer-events: none;
}

.tlv2-rt-shell .tlv2-rt-gm-section-title {
  margin: 14px 14px 6px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(31, 41, 55, 0.55);
}
/* Dark-canvas overrides for select + section title. */
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-tabsel select {
  background: rgba(255, 255, 255, 0.06);
  color: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.10);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-tabsel select:hover {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.18);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-tabsel select option {
  background: #1a1a26;
  color: #FFFFFF;
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-section-title {
  color: rgba(255, 255, 255, 0.6);
}

/* GM panel empty state — no loader, no error, no analytics payload yet. */
.tlv2-rt-shell .tlv2-rt-gm-empty {
  margin: 14px;
  font-size: 12px;
  color: rgba(31, 41, 55, 0.6);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-empty {
  color: rgba(255, 255, 255, 0.6);
}

/* Stat cards — glassmorphic: translucent accent tint over the blurred backdrop + 1px top-left highlight border. */
.tlv2-rt-shell .tlv2-rt-gm-card {
  margin: 8px 14px 0;
  padding: 12px 14px;
  border-radius: 12px;
  color: #FFFFFF;
  border: 1px solid rgba(0, 0, 0, 0.06);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    0 4px 16px rgba(0, 0, 0, 0.10);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
/* Settled — semantic green ("money in"), identical gradient across themes. */
.tlv2-rt-shell .tlv2-rt-gm-card-settled  {
  background: linear-gradient(135deg,
    rgba(29, 188, 149, 0.85) 0%,
    rgba(29, 188, 149, 0.62) 100%);
  border-color: rgba(29, 188, 149, 0.50);
}
/* Unsettled — uses the canvas theme accent (brand colour) so the panel feels cohesive instead of a fixed purple. */
.tlv2-rt-shell .tlv2-rt-gm-card-unsettled {
  background: linear-gradient(135deg,
    rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.85) 0%,
    rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.60) 100%);
  border-color: rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.50);
}
/* Stat cards (APC / APB / Avg time) — neutral light-glass (dark-glass on dark canvas), subtle theme-tinted border. */
.tlv2-rt-shell .tlv2-rt-gm-card-stat {
  background: rgba(255, 255, 255, 0.50);
  border-color: rgba(var(--tlv2-accent-override-rgb, 65, 133, 244), 0.18);
  color: #1F2937;
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card {
  border-color: rgba(255, 255, 255, 0.08);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 4px 16px rgba(0, 0, 0, 0.18);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card-settled {
  background: linear-gradient(135deg,
    rgba(29, 188, 149, 0.55) 0%,
    rgba(29, 188, 149, 0.35) 100%);
  border-color: rgba(29, 188, 149, 0.40);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card-unsettled {
  background: linear-gradient(135deg,
    rgba(var(--tlv2-accent-override-rgb, 148, 103, 255), 0.55) 0%,
    rgba(var(--tlv2-accent-override-rgb, 148, 103, 255), 0.35) 100%);
  border-color: rgba(var(--tlv2-accent-override-rgb, 148, 103, 255), 0.40);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card-stat {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.10);
  color: #FFFFFF;
}

.tlv2-rt-shell .tlv2-rt-gm-card-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12.5px;
  font-weight: 600;
  opacity: 0.92;
}
.tlv2-rt-shell .tlv2-rt-gm-card-row strong { font-weight: 800; font-size: 14px; }
.tlv2-rt-shell .tlv2-rt-gm-card-row small  { font-weight: 600; opacity: 0.85; }
.tlv2-rt-shell .tlv2-rt-gm-card-amount {
  margin-top: 4px;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.2px;
}
.tlv2-rt-shell .tlv2-rt-gm-card-amount small {
  font-size: 12px;
  font-weight: 600;
  opacity: 0.85;
  margin-left: 2px;
}
.tlv2-rt-shell .tlv2-rt-gm-icon {
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.30);
  border: 1px solid rgba(255, 255, 255, 0.25);
  flex-shrink: 0;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-icon {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.10);
}

.tlv2-rt-shell .tlv2-rt-gm-card-btn {
  margin-top: 10px;
  width: 100%;
  height: 32px;
  border: 1px solid rgba(255, 255, 255, 0.30);
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: #FFFFFF;
  /* Buttons stay legible: white-tinted glass on the saturated card backgrounds. */
  background: rgba(255, 255, 255, 0.20);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background 0.14s, border-color 0.14s;
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn:hover {
  background: rgba(255, 255, 255, 0.32);
  border-color: rgba(255, 255, 255, 0.45);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card-btn {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.16);
}
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-gm-card-btn:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.28);
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn-settled {
  background: rgba(17, 144, 144, 0.55);
  border-color: rgba(17, 144, 144, 0.55);
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn-settled:hover {
  background: rgba(17, 144, 144, 0.78);
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn-unsettled {
  background: rgba(112, 71, 209, 0.55);
  border-color: rgba(112, 71, 209, 0.55);
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn-unsettled:hover {
  background: rgba(112, 71, 209, 0.78);
}
/* Disabled (decorative "Analytic" button) — default cursor, no hover, dimmed; keeps purple so it doesn't look broken. */
.tlv2-rt-shell .tlv2-rt-gm-card-btn[disabled] {
  cursor: default;
  opacity: 0.8;
}
.tlv2-rt-shell .tlv2-rt-gm-card-btn-unsettled[disabled]:hover {
  background: rgba(112, 71, 209, 0.55);
  border-color: rgba(112, 71, 209, 0.55);
}

.tlv2-rt-shell .tlv2-rt-gm-loader {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 32px 14px;
  color: rgba(255, 255, 255, 0.7);
  font-size: 12px;
}
.tlv2-rt-shell .tlv2-rt-gm-spinner {
  width: 18px; height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-top-color: #ffffff;
  border-radius: 50%;
  animation: tlv2-rt-gm-spin 0.85s linear infinite;
}
@keyframes tlv2-rt-gm-spin { to { transform: rotate(360deg); } }

.tlv2-rt-shell .tlv2-rt-gm-error {
  margin: 14px;
  padding: 12px 14px;
  border-radius: 8px;
  /* Light-pink fill + dark-red text — readable on the white GM panel (old #ffd2d8 was for dark bg, invisible here). */
  background: rgba(220, 53, 69, 0.10);
  border: 1px solid rgba(220, 53, 69, 0.32);
  color: #B0212F;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.45;
}

.tlv2-rt-shell .tlv2-rt-gm-body { padding-bottom: 18px; }

/* ---- GM View launcher (bottom-left) -----------------------------------------
   Small round accent button; a tap opens the GM View side panel directly (no fan
   / no arc). Glassmorphic accent disc with a "GM" monogram; light + dark canvases. */
.tlv2-rt-shell .tlv2-rt-gm-btn {
  position: absolute;
  left: 14px;
  bottom: 14px;
  z-index: 6;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 50%;
  background: linear-gradient(135deg, #4f95ff 0%, #2c6bd9 100%);
  color: #ffffff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.03em;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    0 4px 14px rgba(28, 70, 150, 0.40);
  -webkit-backdrop-filter: blur(8px) saturate(150%);
  backdrop-filter: blur(8px) saturate(150%);
  transition: transform 0.10s ease, box-shadow 0.16s, filter 0.16s;
}
.tlv2-rt-shell .tlv2-rt-gm-btn:hover {
  filter: brightness(1.05);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.40),
    0 6px 18px rgba(28, 70, 150, 0.48);
}
.tlv2-rt-shell .tlv2-rt-gm-btn:active { transform: scale(0.94); }
.tlv2-rt-shell .tlv2-rt-gm-btn:focus {
  outline: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    0 0 0 3px rgba(65, 133, 244, 0.45),
    0 4px 14px rgba(28, 70, 150, 0.40);
}
@supports selector(:focus-visible) {
  .tlv2-rt-shell .tlv2-rt-gm-btn:focus {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.35),
      0 4px 14px rgba(28, 70, 150, 0.40);
  }
  .tlv2-rt-shell .tlv2-rt-gm-btn:focus-visible {
    outline: none;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.35),
      0 0 0 3px rgba(65, 133, 244, 0.45),
      0 4px 14px rgba(28, 70, 150, 0.40);
  }
}

/* Dark-canvas variant keeps the same accent disc (reads on both light + dark). */
.tlv2-rt-shell .tlv2-rt-gm-btn-label { line-height: 1; }

/* ── Split-bill cell treatment + picker modal ─────────────────────────────
   When V1 splits a table into N children, V2 renders the parent as ONE cell with the .tlv2-rt-split status class (V1 peach palette) + a ×N badge TR. Tap → vanilla-DOM picker modal (one card per split child) → V1 startNewTableBill. */

.tlv2-rt-canvas .tlv2-rt-split-badge {
  position: absolute;
  top: 3px;
  right: 3px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 5px 6px;
  background: rgba(255, 255, 255, 0.92);
  color: #1a1a1a;
  border: 1px solid rgba(0, 0, 0, 0.20);
  border-radius: 8px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  z-index: 2;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}
.tlv2-rt-canvas .tlv2-rt-split-badge svg { display: block; }
.tlv2-rt-canvas .tlv2-rt-split-badge-count { letter-spacing: -0.2px; }
.tlv2-rt-shell.is-dark-canvas .tlv2-rt-canvas .tlv2-rt-split-badge {
  background: rgba(20, 20, 30, 0.85);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.20);
}
/* ROUND tables: show the SPLIT badge OUTSIDE the circle (user 2026-06-13: "show split number outside"). It
   overhangs the top-right edge so it can never cover the centred number, while keeping the count visible. The
   negative top floats it just above the circle's top-right arc; pointer-events:none so the overhang never eats
   a tap meant for a neighbouring table. (The COVERS/APC chip stays at the top-left corner — user-requested.) */
.tlv2-rt-canvas .tlv2-rt-table.tlv2-round .tlv2-rt-split-badge {
  top: -10px;
  right: 4px;
  pointer-events: none;
}

/* ── Split picker modal (vanilla DOM, opened from clickTable) ──────────── */

.tlv2-rt-split-picker-backdrop {
  background: rgba(15, 22, 40, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
  animation: tlv2-split-fadein 0.12s ease-out;
}
@keyframes tlv2-split-fadein { from { opacity: 0; } to { opacity: 1; } }

.tlv2-rt-split-picker {
  background: #FFFFFF;
  color: #1a1a1a;
  border-radius: 14px;
  box-shadow: 0 18px 48px rgba(4, 9, 17, 0.25);
  width: 100%;
  /* max-width is set per-open in JS by the split count (sizeBySplitCount) so the modal grows for many bills;
     this is the fallback ceiling. */
  max-width: 720px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: tlv2-split-popin 0.16s ease-out;
}
@keyframes tlv2-split-popin {
  from { transform: translateY(8px) scale(0.98); opacity: 0; }
  to   { transform: translateY(0) scale(1); opacity: 1; }
}

.tlv2-rt-split-picker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 22px;
  border-bottom: 1px solid #E0E4EA;
  flex-shrink: 0;
}
.tlv2-rt-split-picker-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -0.2px;
  color: #1a1a1a;
}
.tlv2-rt-split-picker-close {
  width: 36px;
  height: 36px;
  border: 0;
  background: rgba(0, 0, 0, 0.04);
  color: #5A6577;
  border-radius: 18px;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
}
.tlv2-rt-split-picker-close:hover { background: rgba(0, 0, 0, 0.08); }
.tlv2-rt-split-picker-close:focus {
  outline: none;
  box-shadow: 0 0 0 2px rgba(65, 133, 244, 0.50);
}
@supports selector(:focus-visible) {
  .tlv2-rt-split-picker-close:focus { box-shadow: none; }
  .tlv2-rt-split-picker-close:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px rgba(65, 133, 244, 0.50);
  }
}

/* ── Dark-theme chrome ────────────────────────────────────────────────────
 * openSplitPicker copies the floor's is-dark-canvas class onto the backdrop (modal mounts on body, outside the themed root); flip picker chrome to dark to match. Grid interior follows theme vars; status tiles stay as-is. */
.tlv2-rt-split-picker-backdrop.is-dark-canvas .tlv2-rt-split-picker {
  background: #1A1D24;
  color: #E8EAED;
}
.tlv2-rt-split-picker-backdrop.is-dark-canvas .tlv2-rt-split-picker-header {
  border-bottom-color: rgba(255, 255, 255, 0.12);
}
.tlv2-rt-split-picker-backdrop.is-dark-canvas .tlv2-rt-split-picker-header h3 {
  color: #E8EAED;
}
.tlv2-rt-split-picker-backdrop.is-dark-canvas .tlv2-rt-split-picker-close {
  background: rgba(255, 255, 255, 0.08);
  color: #C5CAD3;
}
.tlv2-rt-split-picker-backdrop.is-dark-canvas .tlv2-rt-split-picker-close:hover {
  background: rgba(255, 255, 255, 0.16);
}

/* Modal interior — canvas-styled backdrop + grid so the picker reads as a slice of the floor; tiles reuse the canvas shape + chair + status DOM. */
.tlv2-rt-split-picker-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* flex-start, NOT center: with center, an overflowing grid centres its rows and clips BOTH the first and last
     row out of view (the "A-7/A-8 cut off" report). flex-start stacks from the top so overflow-y:auto scrolls
     cleanly through every card. */
  align-content: flex-start;
  gap: 32px;
  padding: 36px 28px;
  overflow-y: auto;
  /* Background PATTERN is applied dynamically via a tlv2-pattern-<floor.pattern> class (added in
     openSplitPicker) so the picker matches the floor canvas (dots / grid / lines / none) instead of a
     hardcoded grid. --grid-color local default lets the shared design.css .tlv2-pattern-* rules resolve here:
     the modal is teleported to <body>, outside .tlv2-rt-canvas where --grid-color normally lives. Surface
     colour still honours the floor theme var (carried onto the backdrop in openSplitPicker). */
  --grid-color: rgba(0, 0, 0, 0.06);
  background-color: var(--tlv2-canvas-bg-override, #ECEEF2);
  min-height: 220px;
}

/* Each split = canvas-table replica; whole tile is the click target. Hover/active mirror canvas-table affordances so it feels like tapping a table. */
.tlv2-rt-split-tile {
  position: relative;
  flex-shrink: 0;
  border: none;
  background: transparent;
  padding: 0;
  font: inherit;
  cursor: pointer;
  transition: transform 0.14s ease-out, filter 0.14s ease-out;
}
.tlv2-rt-split-tile:hover  { transform: translateY(-3px); filter: brightness(1.03); }
.tlv2-rt-split-tile:active { transform: scale(0.97); }
.tlv2-rt-split-tile:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(65, 133, 244, 0.45);
  border-radius: 14px;
}
@supports selector(:focus-visible) {
  .tlv2-rt-split-tile:focus { box-shadow: none; }
  .tlv2-rt-split-tile:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(65, 133, 244, 0.45);
    border-radius: 14px;
  }
}

/* Stage at parent.w × parent.h + transform:scale() — chair offsets land in display px without per-chair math. transform-origin:0 0 anchors to top-left. */
.tlv2-rt-split-tile-stage {
  position: absolute;
  transform-origin: 0 0;
  pointer-events: none;
}
.tlv2-rt-split-tile-shape {
  position: absolute;
  top: 0;
  left: 0;
  border-width: 2px;
  border-style: solid;
  box-sizing: border-box;
  /* Soft shadow so the table sits on the canvas — same 0.18 / 0.08 layered shadow as the canvas-table base. */
  box-shadow: 0 1px 3px rgba(4, 9, 17, 0.18), 0 0 0 0.5px rgba(4, 9, 17, 0.08);
}
.tlv2-rt-split-tile-shape.tlv2-shape-round  { border-radius: 50%; }
.tlv2-rt-split-tile-shape.tlv2-shape-square { border-radius: 12px; }
.tlv2-rt-split-tile-shape.tlv2-shape-rect   { border-radius: 14px; }
.tlv2-rt-split-tile-shape.tlv2-shape-long   { border-radius: 14px; }
/* Sofa border-radius + DOM .tlv2-sofa-art cushion render inherited from the canvas rules above (multi-selector), so picker sofa matches the canvas table. */
.tlv2-rt-split-tile-shape.tlv2-rt-empty { border-style: dashed; }

/* Center label stack — fixed display sizes so metadata stays readable on small tables (60×60 round still fits serial + timer + ₹). */
.tlv2-rt-split-tile-labels {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  max-width: 94%;
  pointer-events: none;
  /* Soft halo so labels stay legible if a chair dot sits behind them. */
  text-shadow: 0 0 5px rgba(255, 255, 255, 0.85);
}
/* (.tlv2-rt-split-tile-num removed 2026-05-27 — serial chip is the identifier now.) */
/* Bill serial chip — outline like V1 split view; topmost element on the tile. */
.tlv2-rt-split-tile-serial {
  display: inline-block;
  max-width: 100%;
  padding: 2px 8px;
  /* Chip colour follows the split's STATUS via --rt-status / --rt-status-text (running→purple, printed→green, reserved→amber, etc.), not fixed green; translucent-white fill keeps it readable on any pastel card. */
  border: 1.5px solid var(--rt-status, #6BB494);
  border-radius: 8px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.12;
  color: var(--rt-status-text, #0E5A2C);
  background: rgba(255, 255, 255, 0.55);
  text-align: center;
  word-break: break-word;
  font-variant-numeric: tabular-nums;
}
/* Amount — the HERO of the tile (the figure the cashier settles): largest + boldest. Redesigned 2026-05-27. */
.tlv2-rt-split-tile-amount {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.5px;
  line-height: 1.05;
  color: var(--rt-status-text, #1A1D24);
  font-variant-numeric: tabular-nums;
}
/* Customer name — secondary line under the serial, mirrors the table mini's .tlv2-rt-customer (slightly muted, readable). */
.tlv2-rt-split-tile-customer {
  max-width: 100%;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.15;
  color: var(--rt-status-text, #1A1D24);
  opacity: 0.82;
  text-align: center;
  word-break: break-word;
}
/* Waiter — muted trailing line, mirrors the table mini's .tlv2-rt-waiter. */
.tlv2-rt-split-tile-waiter {
  max-width: 100%;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--rt-status-text, #1A1D24);
  opacity: 0.62;
  text-align: center;
  word-break: break-word;
}
/* Covers · APC — TL dark corner badge mirroring the table mini's .tlv2-rt-covers pill. Redesigned 2026-05-27. */
.tlv2-rt-split-tile-covers {
  position: absolute;
  /* top/left set inline in JS (anchored to the card, inset by padScaled). */
  height: 20px;
  padding: 0 9px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: rgba(4, 9, 17, 0.64);
  color: #FFFFFF;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
/* Timer — outlined clock pill identical to the table mini's .tlv2-rt-timer (white fill + status border + clock icon). */
.tlv2-rt-split-tile-timer {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.88);
  border: 1.5px solid var(--rt-status, #6BB494);
  color: var(--rt-status-text, #0E5A2C);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.2px;
  font-variant-numeric: tabular-nums;
}
.tlv2-rt-split-tile-timer-icon {
  width: 11px;
  height: 11px;
  flex-shrink: 0;
}
/* Chair dots — siblings of the scaled stage so they stay 16px native regardless of stage scale; position pre-applied in JS, dot unscaled. Status colour cascades from the tile's status class. */
.tlv2-rt-split-tile-chair {
  pointer-events: none;
}
/* a11y-only status text — visually hidden but read by screen readers. */
.tlv2-rt-split-tile-sr-status {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}




/* ── Asset cache fallback placeholder ──────────────────────────────
 * Mounted by tlv2-cached-src when an uploaded asset (custom SVG decor / CAD bg) can't be fetched and isn't cached; replaces the hidden <img>, inherits parent positioning. Dotted muted border + neutral fill + small label — visible but not jarring. */
.tlv2-rt-canvas .tlv2-decor-fallback {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0; /* inset shorthand is Chrome 87+; POS floor is ~85 */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: rgba(0, 0, 0, 0.04);
  border: 1.5px dotted rgba(0, 0, 0, 0.22);
  border-radius: 6px;
  color: rgba(0, 0, 0, 0.55);
  font-size: 11px;
  line-height: 1.2;
  text-align: center;
  padding: 4px;
  pointer-events: none;
  user-select: none;
}
.tlv2-rt-canvas .tlv2-decor-fallback-icon {
  font-size: 14px;
  opacity: 0.6;
}
.tlv2-rt-canvas .tlv2-decor-fallback-label {
  font-weight: 500;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ============================================================================
   TLV2 PASSCODE GLASS — light frosted glassmorphism for the table / GM / waiter
   passcode modals, applied ONLY when enable_table_layout_v2 is on (the opener adds
   windowClass 'tlv2-passcode-glass' + backdropClass 'tlv2-passcode-glass-backdrop').
   AngularUI teleports these modals to <body>, OUTSIDE .tlv2-rt-canvas, so this block
   is intentionally UNSCOPED — the tlv2-passcode-glass class is the gate; with the
   flag off no class is added and V1 styling is byte-for-byte intact. Covers BOTH
   templates: _passCode.html (.rw-numpad-btn) and _passCodeTable.html (.bs-glyphicons
   -list li / .passCode*). backdrop-filter is Chrome 76+ (-webkit- included); POS 85+.
   ============================================================================ */
.modal-backdrop.tlv2-passcode-glass-backdrop,
.tlv2-passcode-glass-backdrop {
  background-color: rgba(226, 232, 240, 0.30);   /* light veil over the blurred canvas */
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  backdrop-filter: blur(10px) saturate(120%);
  opacity: 1 !important;                          /* AngularUI sets .modal-backdrop.in { opacity:.5 } */
}
.tlv2-passcode-glass .modal-dialog { border-radius: 24px; }
.tlv2-passcode-glass .modal-content {
  background-color: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: blur(28px) saturate(150%);
  backdrop-filter: blur(28px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 24px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.65);
  color: #1f2a44;
  overflow: hidden;
}
/* Strip the dark V1 surfaces → transparent on glass, dark legible text. */
.tlv2-passcode-glass .modal-header,
.tlv2-passcode-glass .modal-body,
.tlv2-passcode-glass .modal-footer,
.tlv2-passcode-glass .rw-modal,
.tlv2-passcode-glass .rw-modal-header,
.tlv2-passcode-glass .rw-modal-body,
.tlv2-passcode-glass .rw-modal-footer,
.tlv2-passcode-glass .passCode {
  background: transparent !important;
  border-color: rgba(15, 23, 42, 0.08) !important;
  color: #1f2a44 !important;
}
.tlv2-passcode-glass .modal-title,
.tlv2-passcode-glass .rw-modal-header h3,
.tlv2-passcode-glass h5,
.tlv2-passcode-glass .passCodetext { color: #1f2a44 !important; }
.tlv2-passcode-glass .rw-modal-close { color: #1f2a44 !important; }
/* Passcode input — frosted field. */
.tlv2-passcode-glass .form-control,
.tlv2-passcode-glass .rw-passcode-input {
  background-color: rgba(255, 255, 255, 0.78) !important;
  border: 1px solid rgba(15, 23, 42, 0.12) !important;
  border-radius: 12px !important;
  color: #1f2a44 !important;
  box-shadow: inset 0 1px 3px rgba(15, 23, 42, 0.06) !important;
}
/* Keys + action tiles — frosted white glass (both templates). */
.tlv2-passcode-glass .rw-numpad-btn,
.tlv2-passcode-glass .passCodeNumberDiv li,
.tlv2-passcode-glass .bs-glyphicons-list li {
  background-color: rgba(255, 255, 255, 0.55) !important;
  border: 1px solid rgba(255, 255, 255, 0.6) !important;
  border-radius: 14px !important;
  color: #1f2a44 !important;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background-color 0.15s ease, transform 0.06s ease;
}
.tlv2-passcode-glass .bs-glyphicons-list li h3,
.tlv2-passcode-glass .passCodeNumberDiv li h3 { color: #1f2a44 !important; }
.tlv2-passcode-glass .rw-numpad-btn:hover,
.tlv2-passcode-glass .passCodeNumberDiv li:hover,
.tlv2-passcode-glass .bs-glyphicons-list li:hover {
  background-color: rgba(255, 255, 255, 0.88) !important;
}
.tlv2-passcode-glass .rw-numpad-btn:active,
.tlv2-passcode-glass .passCodeNumberDiv li:active,
.tlv2-passcode-glass .bs-glyphicons-list li:active { transform: translateY(1px); }
/* Focus ring (a11y) — :focus fallback for Chrome 85, :focus-visible for 86+. */
.tlv2-passcode-glass .rw-numpad-btn:focus,
.tlv2-passcode-glass .bs-glyphicons-list li:focus {
  outline: 2px solid #2D7DD2;
  outline-offset: 2px;
}
@supports selector(:focus-visible) {
  .tlv2-passcode-glass .rw-numpad-btn:focus,
  .tlv2-passcode-glass .bs-glyphicons-list li:focus { outline: none; }
  .tlv2-passcode-glass .rw-numpad-btn:focus-visible,
  .tlv2-passcode-glass .bs-glyphicons-list li:focus-visible {
    outline: 2px solid #2D7DD2;
    outline-offset: 2px;
  }
}
/* Cancel button (legacy _passCode.html footer = .btn.btn-danger). The frosted footer is forced transparent
   above, which stripped the default button fill so Cancel read as faint low-contrast text (user 2026-06-10).
   Give it an explicit visible glass face — opaque white + dark ink + clear border — matching the table-passcode
   modal's .tlv2-pc-cancel. */
.tlv2-passcode-glass .rw-modal-footer .btn,
.tlv2-passcode-glass .modal-footer .btn-danger {
  background-color: rgba(255, 255, 255, 0.88) !important;
  border: 1.5px solid rgba(15, 23, 42, 0.20) !important;
  border-radius: 14px !important;
  color: #1f2a44 !important;
  font-weight: 600 !important;
  padding: 10px 28px !important;
  text-shadow: none !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9) !important;
  opacity: 1 !important;
}
.tlv2-passcode-glass .rw-modal-footer .btn:hover,
.tlv2-passcode-glass .modal-footer .btn-danger:hover {
  background-color: rgba(255, 255, 255, 0.96) !important;
  border-color: rgba(15, 23, 42, 0.32) !important;
}
.tlv2-passcode-glass .rw-modal-footer .btn:active,
.tlv2-passcode-glass .modal-footer .btn-danger:active {
  background-color: rgba(244, 248, 253, 0.96) !important;
  transform: translateY(1px);
}
.tlv2-passcode-glass .rw-modal-footer .btn:focus {
  outline: 2px solid #2D7DD2 !important;
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .tlv2-passcode-glass .rw-numpad-btn,
  .tlv2-passcode-glass .passCodeNumberDiv li,
  .tlv2-passcode-glass .bs-glyphicons-list li { transition: none !important; }
}
