
@font-face {
  font-family: "Inter";
  src: url("/fonts/InterVariable.woff2") format("woff2-variations"),
       url("/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #0b0b0d;
  --card: #16161a;
  --text: #f4f4f5;
  --muted: #a1a1aa;
  --accent: #ef4444;
  --border: #26272b;
  --pill-off: #2f3138;
  --pill-on: #16a34a;
  /* Stage 4.7.1 atom 2: promoted from .capture-banner-link literal per
     Droplet/CLAUDE.md deferred-promotion rule. Second consumer (.accordion-success)
     fires the promotion. .chip-live (status-on green) is a separate semantic
     and stays as a literal pending a future cleanup pass. */
  --success: #86efac;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: Inter, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}
.container { max-width: 1320px; margin: 0 auto; padding: 24px; }
.narrow { max-width: 520px; }
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 20px;
  margin-bottom: 20px;
}
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.grid-libraries { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
/* Stage 4.7.86 atom-0: Slide Text band — per-role plain-text columns, inline
   enable toggle, and disabled-input greying (house style mirrors
   .media-swap-upload:disabled / .refresh-icon:disabled). Canvas-side element
   hide is NOT a CSS class — renderCanvas owns block display (inline style),
   driven by the enable flags in getPreviewModelFromForm (see dashboard.js). */
.slide-text-field { display: flex; flex-direction: column; gap: 6px; }
/* 4.7.86 polish-1: the per-role enable controls now reuse the canonical
   .toggle-row/.toggle-pill switch (4.7.79); the atom-0 .form-enable-toggle rule
   was removed as orphaned. */
#form-title-input:disabled,
#form-body-input:disabled,
#form-tagline1-input:disabled,
#form-tagline2-input:disabled { opacity: 0.5; cursor: not-allowed; }
/* editor-grid removed — preview is now full-width below the editor */
.stack { display: flex; flex-direction: column; gap: 12px; }
.row { display: flex; gap: 10px; align-items: center; }
.wrap { flex-wrap: wrap; }
.between { justify-content: space-between; }
label { display: flex; flex-direction: column; gap: 6px; color: var(--muted); }
.label-text { display: flex; align-items: center; gap: 4px; }
input, textarea, select, button, a.button {
  border-radius: 12px;
  border: 1px solid var(--border);
  background: #111217;
  color: var(--text);
  padding: 12px 14px;
  text-decoration: none;
}
button, a.button {
  background: var(--accent);
  border-color: transparent;
  color: white;
  cursor: pointer;
  font: inherit;
}
button.secondary, a.button.secondary { background: #23252b; }
button.danger { background: #7f1d1d; }
#kiosk-reset-btn.is-resetting,
#kiosk-reset-btn.is-resetting:disabled {
  background: #fbbf24;
  color: #0b0b0d;
  border-color: #d97706;
  cursor: progress;
  opacity: 1;
}
/* 4.7.85 atom-0: dirty-state Save-button shift (mirrors .is-resetting amber).
   polish-2: #canvas-save-btn lives inside #rich-text-toolbar, whose canvas-shell
   rule `#rich-text-toolbar #canvas-save-btn` (2,0,0) outranks a bare
   `#canvas-save-btn.is-dirty` (1,1,0) — so the amber never painted. Raise the
   canvas selector to `#id #id.class` (2,1,0) to beat the base, plus a :hover
   variant (2,2,0) to beat canvas-shell's `#rich-text-toolbar #canvas-save-btn:hover`
   (2,1,0). #band-save-btn has no competing rule, so it stays at (1,1,0). The
   competing rule is in a stamped shared file — fixing it here keeps the stage
   Droplet-only (no shared/ touch). */
#band-save-btn.is-dirty { background: #fbbf24; color: #0b0b0d; }
#rich-text-toolbar #canvas-save-btn.is-dirty,
#rich-text-toolbar #canvas-save-btn.is-dirty:hover { background: #fbbf24; color: #0b0b0d; }
#kiosk-reset-btn .dot {
  display: inline;
}
#kiosk-reset-btn .dot.dot-on {
  visibility: visible;
}
#kiosk-reset-btn .dot.dot-off {
  visibility: hidden;
}
#screenshot-capture-btn.is-capturing,
#screenshot-capture-btn.is-capturing:disabled {
  background: #fbbf24;
  color: #0b0b0d;
  border-color: #d97706;
  cursor: progress;
  opacity: 1;
}
#screenshot-capture-btn .dot {
  display: inline;
}
#screenshot-capture-btn .dot.dot-on {
  visibility: visible;
}
#screenshot-capture-btn .dot.dot-off {
  visibility: hidden;
}
.capture-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  margin: 0 0 16px 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 4px solid #22c55e;
  border-radius: 12px;
}
.capture-banner.error { border-left-color: #ef4444; }
.capture-banner-text { flex: 1; color: var(--text); }
.capture-banner-link {
  color: var(--success);
  text-decoration: underline;
  font-weight: 600;
}
.capture-banner-close {
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 8px;
}
/* Generic system-message banner (session-expired et al). Top-of-page, full
   width, high-visibility amber treatment distinct from the green/red
   #capture-banner. NOTE: the explicit [hidden] override below is REQUIRED —
   author `display:flex` defeats the UA `[hidden]{display:none}` rule (same
   trap documented at .permanent-slide-accordion / .modal-overlay / #hero-slide-
   accordion). Without it the banner would be permanently visible. */
.system-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  margin: 0 0 16px 0;
  background: #2a1f10;
  border: 1px solid #f59e0b;
  border-left: 4px solid #f59e0b;
  border-radius: 12px;
}
.system-banner[hidden] { display: none; }
.system-banner-text { flex: 1; color: var(--text); font-weight: 600; }
.system-banner-cta {
  color: #fbbf24;
  text-decoration: underline;
  font-weight: 700;
}
.system-banner-close {
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 8px;
}
/* Session-expired re-anchor pulse (4.7.75 atom-0). id-scoped so it cannot leak
   to the green #capture-banner that also carries .system-banner. Amber
   rgba(245,158,11,…) = #f59e0b per the .system-banner border palette. Outward
   box-shadow ripple only — no transform/background change, so no layout shift
   and the banner stays readable mid-pulse. */
#session-banner.pulse {
  animation: session-banner-pulse 600ms ease-out;
}
@keyframes session-banner-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.55); }
  60%  { box-shadow: 0 0 0 10px rgba(245, 158, 11, 0); }
  100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
}
.muted { color: var(--muted); }
.compact-list { max-height: 320px; overflow: auto; }
.screenshots-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
}
.screenshot-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: #101114;
  border: 1px solid var(--border);
  border-radius: 12px;
}
.screenshot-card img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
  background: #000;
}
.screenshot-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
}
.screenshot-time { font-weight: 600; }
.screenshot-user { font-size: 12px; }
.screenshot-note {
  font-size: 12px;
  color: var(--muted);
  font-style: italic;
}
.screenshot-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}
.hint {
  display: block;
  font-size: 0.85em;
  color: var(--muted);
  margin-top: 4px;
}

.list-item {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 12px 14px;
  background: #101114;
}
/* 4.7.73 atom-2 polish-1: flex-wrap lets the open permanent accordion drop to its own
   full-width line (see .permanent-slide-accordion.is-open). No-op for regular rows —
   no accordion sibling to wrap. */
.slide-row { align-items: stretch; padding-bottom: 16px; flex-wrap: wrap; }
.slide-row-main { display: flex; flex-direction: column; gap: 12px; }

.chip {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: #23252b;
  border: 1px solid var(--border);
  padding: 4px 10px;
  font-size: 12px;
  color: var(--muted);
}
.chip-live { background: rgba(22,163,74,.18); color: #86efac; border-color: rgba(22,163,74,.4); }
.chip-off { background: rgba(120,120,120,.18); color: #d4d4d8; }

/* 4.7.85 atom-0: slide-edit "Unsaved changes" pill. Mirrors the chip ratio
   (.18α bg / .4α border / light-tint text) at amber #fbbf24 — matching the
   #band-save-btn.is-dirty / #canvas-save-btn.is-dirty amber Save-button shift.
   Hidden by default; .is-active reveals it. */
.dirty-pill {
  display: none;
  border-radius: 999px;
  border: 1px solid rgba(251,191,36,.4);
  background: rgba(251,191,36,.18);
  color: #fbbf24;
  padding: 4px 10px;
  font-size: 12px;
  align-self: center;
}
.dirty-pill.is-active { display: inline-block; }

.toggle-row { display: inline-flex; flex-direction: row; align-items: center; gap: 10px; color: var(--text); }
.toggle-row.small { gap: 8px; }
.toggle-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}
.toggle-pill {
  position: relative;
  width: 50px;
  height: 28px;
  background: var(--pill-off);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: inset 0 2px 5px 0 #16151c, 0 2px 3px -1px #403f4e;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background .2s ease;
}
.toggle-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 22px;
  height: 22px;
  border-radius: 100%;
  background: linear-gradient(#3b3a4e, #272733);
  box-shadow: inset 0 1px 1px 0 #424151, 0 1px 4px 0 #0f0e17;
  transition: left .3s ease-in;
  z-index: 2;
}
.toggle-indicator {
  position: absolute;
  top: 50%;
  right: 5px;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  border: 2px solid #ef565f;
  border-radius: 50%;
  box-sizing: border-box;
}
.toggle-input:checked + .toggle-pill { background: var(--pill-on); }
.toggle-input:checked + .toggle-pill .toggle-knob { left: 26px; }
.toggle-input:checked + .toggle-pill .toggle-indicator { animation: toggle-indicator 1s forwards; }
@keyframes toggle-indicator {
  0%   { opacity: 1; }
  30%  { opacity: 0; }
  100% { opacity: 1; border: 2px solid var(--pill-on); right: auto; left: 5px; }
}

.preview-panel {
  /* full-width section, no sticky needed */
}
.preview-shell {
  width: 100%;
  max-width: 860px;
  aspect-ratio: var(--kiosk-aspect, 16/9);
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid #31333a;
  background: black;
}
.kiosk-stage {
  width: 100vw;
  height: 100vh;
  background: black;
  overflow: hidden;
}
.preview-page {
  background: black;
}
.slide-card {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  overflow: hidden;
  background: radial-gradient(circle at top, #1d1f26 0%, #09090b 70%);
}
.kiosk-fit { width: 100vw; height: 100vh; }
.media-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.media-contain {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.media-fallback {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  color: #cbd5e1;
  background: linear-gradient(135deg, #111827, #1f2937);
  padding: 24px;
  text-align: center;
}
.overlay-bottom {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 22px;
  background: linear-gradient(to top, rgba(0,0,0,.84), rgba(0,0,0,.15), transparent);
}
.overlay-bottom h2, .text-centered-inner h2, .split-copy h2, .spotlight-copy h2 {
  margin: 0 0 10px;
  font-size: clamp(28px, 4vw, 44px);
  line-height: 1.05;
}
.overlay-bottom p, .text-centered-inner p, .split-copy p, .spotlight-copy p {
  margin: 0;
  font-size: clamp(16px, 2vw, 24px);
  line-height: 1.3;
  color: #e4e4e7;
}
.tpl-text-centered {
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 28px;
}
.text-centered-inner {
  max-width: 86%;
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-items: center;
}
.inline-image {
  max-width: 58%;
  max-height: 34%;
  border-radius: 18px;
  object-fit: contain;
}
.tpl-split-feature {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
}
.split-media {
  background: #0f172a;
}
.split-copy {
  padding: 28px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 16px;
}
.tpl-product-spotlight {
  padding: 26px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.spotlight-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}
.spotlight-thumb {
  max-width: 42%;
  max-height: 260px;
  border-radius: 18px;
  object-fit: contain;
}
.spotlight-copy {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.slide-cta {
  display: inline-flex;
  align-self: center;
  border-radius: 999px;
  padding: 10px 18px;
  font-weight: 700;
  background: var(--accent, #ef4444);
  color: white;
}
.slide-cta.large {
  align-self: flex-start;
  font-size: 20px;
  padding: 14px 22px;
}
.slide-badge {
  display: inline-flex;
  align-self: flex-start;
  border-radius: 999px;
  padding: 8px 12px;
  font-weight: 700;
  background: var(--accent, #ef4444);
  color: white;
  margin-bottom: 8px;
}

@media (max-width: 980px) {
  .grid, .grid2, .grid3, .grid4 { grid-template-columns: 1fr; }
  .grid-libraries { grid-template-columns: 1fr; }
}

.hours-grid { display:flex; flex-direction:column; gap:10px; }
.hours-row { display:grid; grid-template-columns: 110px 140px 1fr 1fr; gap:10px; align-items:center; border:1px solid var(--border); border-radius:14px; padding:10px 12px; background:#101114; }
.hours-day { font-weight:700; color:var(--text); }
.hidden { display:none !important; }
.tpl-image-overlay { position:relative; overflow:hidden; background:#050505; }
.overlay-bottom.strong { position:absolute; left:0; right:0; bottom:0; padding:24px; display:flex; flex-direction:column; gap:12px; background:linear-gradient(to top, rgba(0,0,0,0.88), rgba(0,0,0,0.58), rgba(0,0,0,0)); }
.overlay-bottom.strong h2, .overlay-bottom.strong p { text-shadow:0 2px 14px rgba(0,0,0,0.7); }
.overlay-bottom.strong p { max-width:85%; }
@media (max-width: 980px) { .hours-row { grid-template-columns:1fr; } }

.settings-collapsible {
  border: 1px solid var(--border);
  border-radius: 16px;
  background: #101114;
}
.settings-collapsible > summary {
  cursor: pointer;
  list-style: none;
  padding: 14px 16px;
  font-weight: 800;
  color: var(--text);
}
.settings-collapsible > summary::-webkit-details-marker { display:none; }
.settings-collapsible > summary::after {
  content: "+";
  float: right;
  color: var(--muted);
}
.settings-collapsible[open] > summary::after { content: "–"; }
.settings-collapsible .details-body {
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.hours-row {
  grid-template-columns: 100px 210px minmax(0,1fr) 180px;
}
.hours-state-toggle {
  display: inline-flex;
  background: #1b1c20;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
  gap: 4px;
}
.state-btn {
  border: 0;
  border-radius: 999px;
  padding: 8px 14px;
  background: transparent;
  color: var(--muted);
  font-weight: 700;
}
.state-btn.open.active {
  background: rgba(22,163,74,.28);
  color: #bbf7d0;
}
.state-btn.closed.active {
  background: #3a3b43;
  color: #f4f4f5;
}
.hours-times {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.hours-placeholder {
  color: var(--muted);
  font-weight: 700;
  letter-spacing: 0.06em;
}

@media (max-width: 980px) {
  .hours-row { grid-template-columns: 1fr; }
  .hours-times { grid-template-columns: 1fr; }
}

.hours-status-wrap {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.hours-toggle-row {
  color: var(--text);
}
.hours-status-text {
  min-width: 58px;
  font-size: 14px;
  font-weight: 700;
}
.hours-status-text.open { color: #86efac; }
.hours-status-text.closed { color: #d4d4d8; }
.hours-placeholder {
  color: var(--muted);
  font-weight: 700;
}
.hours-row.is-open .toggle-pill {
  background: var(--pill-on);
}
.hours-row.is-closed .toggle-pill {
  background: var(--pill-off);
}

.settings-collapsible > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 700;
  color: var(--text);
}
.settings-collapsible > summary::-webkit-details-marker { display:none; }
.settings-collapsible > summary::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(45deg);
  transition: transform .2s ease;
  margin-left: 12px;
}
.settings-collapsible[open] > summary::after {
  transform: rotate(225deg);
}
.details-body { padding-top: 14px; }
.hours-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.hours-row {
  display: grid;
  grid-template-columns: 120px 130px 1fr;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #101114;
}
.hours-day {
  color: var(--text);
  font-weight: 700;
}
.hours-toggle-cell {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.hours-state-label {
  min-width: 54px;
  font-size: 14px;
  font-weight: 700;
}
.hours-state-label.open { color: #86efac; }
.hours-state-label.closed { color: #a1a1aa; }
.hours-toggle-row { margin: 0; }
.hours-times {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.hours-times input[type="time"] {
  width: 130px;
}
.hours-sep, .hours-dashes span {
  color: var(--muted);
  font-size: 14px;
  font-weight: 700;
}
.hours-dashes {
  color: var(--muted);
  font-weight: 700;
  letter-spacing: 0.04em;
}
.hidden { display: none !important; }

/* Hours toggle revert v3 */
.settings-collapsible > summary::after {
  content: "";
  float: right;
  width: 10px;
  height: 10px;
  margin-top: 4px;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(45deg);
  transition: transform .2s ease;
}
.settings-collapsible[open] > summary::after {
  transform: rotate(225deg);
}
.hours-row {
  grid-template-columns: 100px 150px 1fr;
  gap: 12px;
}
.hours-toggle-cell {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.hours-state-label {
  min-width: 52px;
  font-size: 13px;
  font-weight: 800;
}
.hours-state-label.open { color: #86efac; }
.hours-state-label.closed { color: #d4d4d8; }

.hours-times,
.hours-placeholder {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.hours-times input[type="time"],
.time-box-mock,
.ampm-mock {
  border-radius: 12px;
  border: 1px solid var(--border);
  background: #111217;
  color: var(--text);
}
.hours-times input[type="time"] {
  padding: 12px 14px;
}
.time-box-mock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 98px;
  padding: 12px 14px;
  color: var(--muted);
}
.ampm-mock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 66px;
  padding: 12px 14px;
  color: var(--muted);
}
.hours-sep {
  color: var(--muted);
  font-weight: 700;
}
.hidden { display:none !important; }
@media (max-width: 980px) {
  .hours-row { grid-template-columns: 1fr; }
}

/* Hours toggle revert v4 - keep closed row on same line as open */
.hours-row {
  grid-template-columns: 100px 150px minmax(0, 1fr);
  gap: 12px;
}
.hours-times,
.hours-placeholder {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  white-space: nowrap;
  min-width: 0;
}
.hours-times input[type="time"] {
  width: 132px;
  min-width: 132px;
}
.time-inline-mock {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: nowrap;
}
.time-box-mock,
.ampm-mock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: #111217;
  color: var(--muted);
  font-weight: 700;
}
.time-box-mock {
  min-width: 96px;
  padding: 0 14px;
  letter-spacing: 0.04em;
}
.ampm-mock {
  min-width: 56px;
  padding: 0 12px;
}
.hours-sep {
  flex: 0 0 auto;
}
@media (max-width: 980px) {
  .hours-row {
    grid-template-columns: 1fr;
  }
  .hours-times,
  .hours-placeholder {
    flex-wrap: wrap;
  }
}

/* Hours toggle revert v5 - closed placeholders use same inline box footprint */
.hours-times input[type="time"],
.time-box-mock {
  width: 132px;
  min-width: 132px;
  height: 44px;
  padding: 0 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: #111217;
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  line-height: 44px;
  text-align: center;
}
.time-box-mock {
  color: var(--muted);
  letter-spacing: 0.02em;
}
.ampm-mock,
.time-inline-mock {
  display: none !important;
}
.hours-placeholder {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.hours-sep {
  flex: 0 0 auto;
}

/* hours fix v6 */
.hours-times,
.hours-placeholder {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
}

.hours-time-input {
  width: 132px;
  height: 44px;
  padding: 0 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: #111217;
  color: var(--text);
  font: 14px Inter, Arial, sans-serif;
  line-height: 44px;
  box-sizing: border-box;
}

.hours-time-input.hours-time-faux,
.hours-time-input.hours-time-faux:disabled {
  opacity: 1;
  color: var(--text);
  -webkit-text-fill-color: var(--text);
  cursor: default;
}

.hours-sep {
  color: var(--muted);
  font-weight: 700;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
}

.hours-time-input::-webkit-calendar-picker-indicator {
  opacity: .9;
}

/* hours display upgrade v8 */
.hours-times,
.hours-placeholder {
  display: inline-flex !important;
  align-items: center !important;
  gap: 10px !important;
  flex-wrap: nowrap !important;
}

.time-box-display-v8 {
  width: 132px !important;
  height: 44px !important;
  padding: 0 14px !important;
  border-radius: 12px !important;
  border: 1px solid var(--border) !important;
  background: #111217 !important;
  color: var(--text) !important;
  font: 14px Inter, Arial, sans-serif !important;
  font-weight: 600 !important;
  line-height: 44px !important;
  box-sizing: border-box !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  text-align: center !important;
  white-space: nowrap !important;
  appearance: none !important;
  cursor: pointer !important;
  box-shadow: none !important;
}

.time-box-display-v8.faux {
  cursor: default !important;
}

.hours-time-native-v8 {
  position: absolute !important;
  opacity: 0 !important;
  pointer-events: none !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
}

.time-box-mock {
  display: none !important;
}

/* hours visibility hotfix */
.hours-times.hidden,
.hours-placeholder.hidden {
  display: none !important;
}

.hours-times:not(.hidden),
.hours-placeholder:not(.hidden) {
  display: inline-flex !important;
}

/* ============================================================
   TEXT OVERLAY POSITIONING (admin preview + Pi kiosk shared logic)
   ============================================================ */
.text-overlay {
  position: absolute;
  padding: 18px 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 90%;
  cursor: grab;
  user-select: none;
  border-radius: 12px;
  transition: outline 0.1s;
}
.text-overlay:hover {
  outline: 2px dashed rgba(255,255,255,0.25);
}
.drag-active .text-overlay {
  outline: 2px dashed rgba(255,255,255,0.5);
  cursor: grabbing;
}
.text-overlay.static {
  position: relative;
  cursor: default;
}
.text-overlay.static:hover { outline: none; }

/* has-scrim: gradient bg for readability on image-overlay */
.text-overlay.has-scrim {
  background: linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.52) 70%, rgba(0,0,0,0) 100%);
  padding: 32px 24px 22px;
  border-radius: 0;
}

/* 9 zone positions */
.pos-top-left     { top: 0;   left: 0;                     }
.pos-top-center   { top: 0;   left: 50%; transform: translateX(-50%); }
.pos-top-right    { top: 0;   right: 0;                    }
.pos-middle-left  { top: 50%; left: 0;   transform: translateY(-50%); }
.pos-middle-center{ top: 50%; left: 50%; transform: translate(-50%,-50%); }
.pos-middle-right { top: 50%; right: 0;  transform: translateY(-50%); }
.pos-bottom-left  { bottom: 0; left: 0;                    }
.pos-bottom-center{ bottom: 0; left: 50%; transform: translateX(-50%); }
.pos-bottom-right { bottom: 0; right: 0;                   }

/* Custom pixel-coordinate positioning — inline styles set left/top/transform */
.text-overlay.pos-custom {
  text-align: left;
}

/* Independent text elements — absolutely positioned title and body */
.text-element {
  position: absolute;
  z-index: 5;
  max-width: 70%;
  box-sizing: border-box;
  padding: 0.5em 0.8em;
  pointer-events: none;
  word-wrap: break-word;
  overflow-wrap: break-word;
}
.text-element-title { text-align: left; }
.text-element-body  { text-align: left; }

/* Draggable handles in admin editor */
.text-element[data-draggable-element] {
  pointer-events: auto;
  cursor: grab;
  outline: 2px dashed rgba(255,255,255,0.35);
  outline-offset: 4px;
  border-radius: 4px;
}
.text-element[data-draggable-element]:hover {
  outline-color: rgba(255,255,255,0.6);
}
.text-element[data-draggable-element] .drag-label {
  position: absolute;
  top: -18px;
  left: 0;
  font-size: 10px;
  color: rgba(255,255,255,0.7);
  background: rgba(0,0,0,0.5);
  padding: 1px 6px;
  border-radius: 3px;
  pointer-events: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* has-scrim overrides: always stretch full width at bottom */
.text-overlay.has-scrim.pos-bottom-left,
.text-overlay.has-scrim.pos-bottom-center,
.text-overlay.has-scrim.pos-bottom-right {
  left: 0; right: 0; bottom: 0;
  transform: none;
  max-width: 100%;
}
.text-overlay.has-scrim.pos-top-left,
.text-overlay.has-scrim.pos-top-center,
.text-overlay.has-scrim.pos-top-right {
  left: 0; right: 0; top: 0;
  bottom: auto; transform: none;
  max-width: 100%;
  background: linear-gradient(to bottom, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.52) 70%, rgba(0,0,0,0) 100%);
}
.text-overlay.has-scrim.pos-middle-left,
.text-overlay.has-scrim.pos-middle-center,
.text-overlay.has-scrim.pos-middle-right {
  top: 50%; left: 0; right: 0;
  transform: translateY(-50%);
  max-width: 100%;
  background: rgba(0,0,0,0.72);
  border-radius: 0;
}

/* ============================================================
   TITLE / BODY SIZE CLASSES
   ============================================================ */
.text-overlay h2, .split-copy h2, .spotlight-copy h2 { margin: 0; line-height: 1.05; }
.text-overlay p,  .split-copy p,  .spotlight-copy p  { margin: 0; line-height: 1.3; color: #e4e4e7; }

/* Title sizes */
.ot-sm { font-size: clamp(18px, 2.5vw, 28px); }
.ot-md { font-size: clamp(26px, 3.5vw, 40px); }
.ot-lg { font-size: clamp(34px, 4.5vw, 56px); }
.ot-xl { font-size: clamp(44px, 6vw,   72px); }

/* Body sizes */
.ob-sm { font-size: clamp(13px, 1.5vw, 17px); }
.ob-md { font-size: clamp(16px, 2vw,   22px); }
.ob-lg { font-size: clamp(20px, 2.5vw, 30px); }
.ob-xl { font-size: clamp(26px, 3vw,   38px); }

/* ============================================================
   TOOLTIPS
   ============================================================ */
.tooltip-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-left: 4px;
  vertical-align: middle;
}
.tooltip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #2e3038;
  border: 1px solid #444;
  color: var(--muted);
  font-size: 10px;
  font-weight: 700;
  cursor: default;
  line-height: 1;
  user-select: none;
  padding: 0;
}
.tooltip-icon:hover {
  background: #3a3d47;
  color: var(--text);
}
.tooltip-box {
  display: none;
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  width: 220px;
  background: #1e2028;
  border: 1px solid #3a3d47;
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.5;
  color: #c4c4cc;
  z-index: 100;
  pointer-events: none;
  box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
.tooltip-box::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #3a3d47;
}
.tooltip-wrap:hover .tooltip-box {
  display: block;
}
.tooltip-wrap.is-open .tooltip-box {
  display: block;
}
.tooltip-wrap.is-open .tooltip-icon {
  background: #3a3d47;
  color: var(--text);
}

/* ============================================================
   CANVAS EDITOR
   ============================================================ */
/* Atom E + D-4: aspect-ratio promoted to wrap so the box self-sizes
   to width × aspect. Stage paints absolute inside via canvas-shell.
   --kiosk-aspect plumbing (set by applyAspectRatio in dashboard.js)
   is unchanged. */
.canvas-wrap {
  width: 100%;
  max-width: 900px;
  aspect-ratio: var(--kiosk-aspect, 16/9);
  position: relative;
  overflow: hidden;
}
/* Polish T2: cap the Canvas Editor header to the SAME 900px as .canvas-wrap above
   so the right-pinned #rich-text-toolbar's right edge aligns to the canvas red
   border instead of overhanging to the panel content edge (~330px overhang). The
   toolbar is flex-shrink:0 (canvas-shell.css) so it keeps full width here. Keep
   this 900 in sync with .canvas-wrap's max-width. */
.preview-panel > .canvas-editor-header { max-width: 900px; }
/* Polish T1: cap the heading block so its <p> description wraps compactly to the
   left of the toolbar (the heading shares the 900px row with the ~715px toolbar).
   text-wrap:balance evens the wrapped lines so the last line is never a single
   orphan word (Chromium-supported; the admin runs in Chromium). */
.canvas-editor-heading { max-width: 176px; }
.canvas-editor-heading p { text-wrap: balance; }
.canvas-stage {
  position: relative;
  background: #09090b;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid #2a2d35;
  cursor: default;
  user-select: none;
}
.canvas-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Text block */
.canvas-text-block {
  position: absolute;
  min-width: 80px;
  max-width: 70%;
  box-sizing: border-box;
  padding: 10px 14px;
  border-radius: 10px;
  cursor: grab;
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: box-shadow 0.12s;
  word-wrap: break-word;
  overflow-wrap: break-word;
}
/* Atom H ss14: thumb wrapper sits beneath other text wrappers so image doesn't cover title/body/tagline1/tagline2. */
#canvas-thumb-block { z-index: 5; }
.canvas-text-block:hover {
  box-shadow: 0 0 0 2px rgba(255,255,255,0.2);
}
.canvas-text-block.canvas-dragging {
  cursor: grabbing;
  box-shadow: 0 0 0 2px rgba(255,255,255,0.45), 0 8px 32px rgba(0,0,0,0.5);
}
.canvas-select-ring {
  display: none;
  position: absolute;
  inset: -3px;
  border: 2px dashed rgba(255,255,255,0.45);
  border-radius: 12px;
  pointer-events: none;
}
.canvas-text-block:hover .canvas-select-ring,
.canvas-text-block.canvas-dragging .canvas-select-ring {
  display: block;
}
.canvas-text-block .drag-label {
  position: absolute;
  top: -18px;
  left: 0;
  font-size: 10px;
  color: rgba(255,255,255,0.6);
  background: rgba(0,0,0,0.5);
  padding: 1px 6px;
  border-radius: 3px;
  pointer-events: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0;
  transition: opacity 0.12s;
}
.canvas-text-block:hover .drag-label,
.canvas-text-block.canvas-dragging .drag-label {
  opacity: 1;
}

/* Resize handle — right edge of each canvas block */
.canvas-resize-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  cursor: ew-resize;
  z-index: 15;
  opacity: 0;
  transition: opacity 0.12s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.canvas-resize-handle::after {
  content: "";
  display: block;
  width: 4px;
  height: 28px;
  background: rgba(99,179,237,0.8);
  border-radius: 2px;
}
.canvas-text-block:hover .canvas-resize-handle,
.canvas-text-block.canvas-dragging .canvas-resize-handle {
  opacity: 1;
}

/* Alignment guides */
.canvas-guide {
  position: absolute;
  background: rgba(99, 179, 237, 0.7);
  opacity: 0;
  pointer-events: none;
  z-index: 20;
  transition: opacity 0.08s;
}
.guide-h {
  left: 0; right: 0;
  height: 1px;
  top: 50%;
}
.guide-v {
  top: 0; bottom: 0;
  width: 1px;
  left: 50%;
}
#guide-h-center { top: 50%; }
#guide-h-top    { top: 4%; }
#guide-h-bottom { top: 96%; }
#guide-v-center { left: 50%; }
#guide-v-left   { left: 4%; }
#guide-v-right  { left: 96%; }

/* --- Typography panel (Session 2) --- */
.typography-subhead {
  margin: 8px 0 4px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Slide list drag-to-reorder */
.slide-row.dragging {
  opacity: 0.4;
}
.slide-row.drag-over {
  outline: 2px solid var(--accent, #f5c542);
  outline-offset: -2px;
}

/* Stage 4.7: Playlist Curation Mode B picker. Sits inside a .card-bearing
   <section>; uses --card for own background (matching parent card surface
   creates seamless integration) and the literal #101114 for tile background
   matching the .hours-row precedent at "background:#101114" earlier in this
   file — between --bg (#0b0b0d) and --card (#16161a), the existing pattern
   for recessed nested elements where the palette lacks a third tier. */
.sentinel-picker {
  margin: 8px 0;
  padding: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.sentinel-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0;
}
.sentinel-tile {
  width: 44px;
  height: 44px;
  font-size: 24px;
  line-height: 1;
  background: #101114;
  border: 2px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: border-color 0.15s, transform 0.05s;
}
.sentinel-tile:hover {
  border-color: var(--muted);
}
.sentinel-tile:active {
  transform: scale(0.96);
}
.sentinel-tile.is-active {
  border-color: #86efac;
  background: rgba(134, 239, 172, 0.1);
}

/* Stage 4.7.1 atom 2: permanent-slide accordion + chip. The accordion lives
   inside the slide-list row for permanent slides; Edit-button click toggles
   its .is-open state. Single-open semantic is managed in JS via
   openAccordionSlideId. The .sentinel-picker / .sentinel-tile rules above
   are LOAD-BEARING on this accordion (lift-and-relocate of the previous
   top-of-page Curation section). */
.permanent-slide-accordion {
  display: none;
  margin-top: 10px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #111217;
}
.permanent-slide-accordion.is-open {
  display: block;
  /* 4.7.73 atom-2 polish-1 — own full-width line (flex sibling wraps below the row).
     Measurement-validated (tools/measure-slide-layout.js): the accordion is a rigid
     317px inline sibling that starved .slide-row-main to 126-238px across two broken
     bands (1080-1200px with .slide-row-actions pinned at 516; 660-800px below the 980
     reflow). flex-basis:100% removes it from the row line; main then gets >=430px across
     660-1320, well above the 335px chip one-line threshold. Pairs with .slide-row's
     flex-wrap:wrap. */
  flex-basis: 100%;
}
.permanent-slide-accordion-inner {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* :not([hidden]) defers to the HTML hidden attribute. Without it,
   this descendant-class selector (specificity 0,2,0) wins over the
   UA stylesheet's [hidden] { display: none } (0,1,0), and elements
   with the hidden attribute render visible anyway. The atom-2 lift-
   and-relocate of .sentinel-picker onto a div also carrying
   .accordion-field is what introduced the conflict; atom-4 fix. */
.permanent-slide-accordion .accordion-field:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--muted);
}
/* Stage 4.7.1 atom 6: pin the conditional-reveal region to Sentinel
   mode's natural default-state height (Advanced disclosure collapsed)
   so toggling between Mode A and Mode B doesn't reflow scroll
   position. Hardcoded value (213px) measured via DevTools in admin
   browser at commit time. Brittle to future Sentinel-block content
   changes — Stage 5b candidate for a JS-measure-and-pin or
   container-query alternative when the UI scales beyond single-
   permanent-slide. */
.accordion-conditional-region {
  min-height: 213px;
}
/* Stage 4.7.1 atom 4: hide Edit + Preview buttons on the permanent
   row while its accordion is open. :has() lets the rule live in pure
   CSS — no JS class-toggling needed because the accordion's own
   .is-open class is the discriminator. Operator-side browsers
   (Baseline-2023 :has() support) are the only consumers; the kiosk
   does not render the admin UI. */
.slide-row:has(.permanent-slide-accordion.is-open) [data-action="edit"],
.slide-row:has(.permanent-slide-accordion.is-open) [data-action="preview"] {
  display: none;
}
.permanent-slide-accordion .accordion-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
}
.accordion-success {
  color: var(--success);
  font-size: 13px;
  font-weight: 600;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.accordion-success.is-visible {
  opacity: 1;
}
.chip-permanent {
  background: rgba(59, 130, 246, 0.18);
  color: #93c5fd;
  border-color: rgba(59, 130, 246, 0.4);
}

/* Stage 4.7.4 atom-5: Last 10 Reviewers attribute pool admin UI.
   Lives inside .permanent-slide-accordion-inner alongside RRR's
   sentinel-picker shape. Clear-all reuses the existing button.danger
   class. Error/destructive accent literals (#fda4af, #ef4444) flagged
   for var-promotion if a third consumer arrives. */
.leaderboard-attr-count-row {
  color: var(--muted);
  font-size: 14px;
}
.leaderboard-attr-add-row,
.leaderboard-attr-upload-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.leaderboard-attr-add-row input[type="text"] {
  flex: 1;
  min-width: 200px;
}
.leaderboard-attr-add-error,
.leaderboard-attr-upload-report {
  font-size: 13px;
  padding: 6px 10px;
  border-radius: 4px;
  margin-top: 4px;
}
.leaderboard-attr-add-error {
  color: #fda4af;
  background: rgba(239, 68, 68, 0.08);
}
.leaderboard-attr-upload-report {
  color: var(--success);
  background: rgba(134, 239, 172, 0.08);
}
.leaderboard-attr-list {
  max-height: 196px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.leaderboard-attr-empty {
  color: var(--muted);
  font-style: italic;
  padding: 16px;
  text-align: center;
}
.leaderboard-attr-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 3px;
}
.leaderboard-attr-text {
  flex: 1;
  word-break: break-word;
}
.leaderboard-attr-delete-btn {
  background: none;
  border: none;
  color: #fda4af;
  cursor: pointer;
  font-size: 18px;
  padding: 0 6px;
  line-height: 1;
}
.leaderboard-attr-delete-btn:hover {
  color: #ef4444;
}
.denied-author-count-row {
  color: var(--muted);
  font-size: 14px;
}
.denied-author-add-row,
.denied-author-upload-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.denied-author-add-row input[type="text"] {
  flex: 1;
  min-width: 200px;
}
.denied-author-add-error,
.denied-author-upload-report {
  font-size: 13px;
  padding: 6px 10px;
  border-radius: 4px;
  margin-top: 4px;
}
.denied-author-add-error {
  color: #fda4af;
  background: rgba(239, 68, 68, 0.08);
}
.denied-author-upload-report {
  color: var(--success);
  background: rgba(134, 239, 172, 0.08);
}
.denied-author-list {
  max-height: 320px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.denied-author-empty {
  color: var(--muted);
  font-style: italic;
  padding: 16px;
  text-align: center;
}
.denied-author-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 3px;
}
.denied-author-name {
  flex: 1;
  word-break: break-word;
}
.denied-author-delete-btn {
  background: none;
  border: none;
  color: #fda4af;
  cursor: pointer;
  font-size: 18px;
  padding: 0 6px;
  line-height: 1;
}
.denied-author-delete-btn:hover {
  color: #ef4444;
}

/* === Atom L — Logo/QR field controls === */
.atomL-field { display: flex; flex-direction: column; gap: 4px; }
.atomL-label-row { display: inline-flex; align-items: center; gap: 6px; }
.atomL-label-row > span:first-child { font-weight: 500; }
.refresh-icon,
.upload-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--border, #444);
  background: var(--bg-elev, #1a1a1a);
  color: var(--fg, #ddd);
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
  line-height: 1;
}
.refresh-icon:hover:not(:disabled),
.upload-icon-btn:hover { background: var(--bg-elev-hover, #2a2a2a); }
.refresh-icon:disabled { opacity: 0.4; cursor: not-allowed; }
/* Frame Border Color swatch-button positioned at the trailing edge, inside the
   input's bounding area (positioned-overlay). The .upload-icon-btn keeps its
   own size/border; its inline background (current value preview) is unaffected.
   padding-right clears the swatch so typed hex never sits under it. */
.accent-input-wrap { position: relative; display: block; }
.accent-input-wrap > input { width: 100%; box-sizing: border-box; padding-right: 40px; }
.accent-input-wrap > .upload-icon-btn { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); }
.atomL-warning {
  display: block;
  color: #ef4444;
  font-size: 12px;
  margin-top: 2px;
}

/* Step 5: Swap Image/Video media picker modal */
.media-swap-backdrop {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-swap-modal {
  background: #15151a;
  border: 1px solid var(--border);
  border-radius: 12px;
  width: min(720px, 92vw);
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}
.media-swap-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  font-weight: 700;
  color: var(--text);
}
.media-swap-close {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.media-swap-close:hover { color: var(--text); }
.media-swap-grid {
  padding: 16px;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}
.media-swap-tile {
  background: var(--bg);
  border: 2px solid var(--border);
  border-radius: 8px;
  padding: 8px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
  text-align: center;
}
.media-swap-tile:hover { border-color: var(--muted); }
.media-swap-tile.is-selected { border-color: var(--accent); }
.media-swap-thumb {
  width: 100%;
  height: 100px;
  object-fit: contain;
  background: #000;
  border-radius: 4px;
  display: block;
}
.media-swap-name {
  color: var(--text);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.media-swap-empty {
  color: var(--muted);
  padding: 24px;
  text-align: center;
  grid-column: 1 / -1;
}
/* T2: upload affordance + inline feedback in the swap modal header/body. */
.media-swap-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.media-swap-upload {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  padding: 6px 12px;
  cursor: pointer;
}
.media-swap-upload:hover { border-color: var(--muted); }
.media-swap-upload:disabled { opacity: 0.5; cursor: default; }
.media-swap-note {
  padding: 8px 18px;
  font-size: 13px;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}
.media-swap-note.is-error { color: var(--accent); }

/* 4.7.58 atom-0: action-button cluster on slide-list rows holds a single row at
   typical viewport width (no @media; admin dashboard is desktop-only de facto). */
.slide-row-actions {
  flex-wrap: nowrap;
  flex-shrink: 0;
  align-items: flex-start;
  /* 4.7.71 polish-2: fixed cluster width = the widest (regular, 6-element) row,
     so the space-between right-anchoring yields a consistent LEFT edge across
     regular + permanent + Hero rows — [Live, Edit, Preview] form vertical columns.
     Reset to 0 in the <=980px reflow block below. */
  min-width: 516px;
}

/* === 4.7.59 atom-0 · mobile-landscape (pair.B: beta + lock.phones-only) === */

/* Rotate-device overlay — base styles, hidden by default; activated by the
   phone-portrait @media further below. Modeled on .media-swap-backdrop @ 1660. */
.rotate-device-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2100;
  background: rgba(20, 20, 20, 0.95);
  color: #f0f0f0;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  font-family: Inter, Arial, sans-serif;
}
.rotate-device-overlay__msg { font-size: 20px; line-height: 1.4; max-width: 320px; font-weight: 500; }

/* Tablet-portrait + narrow-desktop reflow (beta tier — slide-list collapses
   at the same width the existing 980 grid/hours tier does; R2 confirmed
   980 blocks at 409/423/490/689/742 cover .grid* + .hours-* only, so the
   slide-list rule below is net-new). */
@media (max-width: 980px) {
  .slide-row-actions {
    display: grid;
    grid-template-columns: repeat(3, auto);
    gap: 6px;
    min-width: 0; /* polish-2: drop the desktop fixed-width below the 980 reflow */
  }
  .slide-row-main {
    flex: 1 1 auto;
    min-width: 0;
  }
}

/* Phone-tier reflow (<=640px): top header column-stacks; slide-list action
   cluster column-stacks with full-width buttons. dashboard.html-scoped via
   the header element selector — login.html stays in .container.narrow. */
@media (max-width: 640px) {
  main.container > header.row.between.wrap { flex-direction: column; align-items: stretch; gap: 10px; }
  .slide-row-actions { flex-direction: column; align-items: stretch; }
  .slide-row-actions button { width: 100%; }
}

/* Rotate-overlay activation — phones in portrait only (lock.phones-only).
   Tablets in portrait keep their reflowed layout from the 980 tier above. */
@media (orientation: portrait) and (max-width: 640px) {
  .rotate-device-overlay { display: flex; }
}

/* Canvas-editor sub-desktop reflow (4.7.60 atom-0). Header row wraps
   so the toolbar drops to its own row beneath the heading; heading
   max-width relaxes to fill the freed first row. Paired with the
   toolbar-internal wrap in canvas-shell.css at the same breakpoint. */
@media (max-width: 980px) {
  .canvas-editor-header { flex-wrap: wrap; }
  .canvas-editor-heading { max-width: none; }
}

/* 4.7.60 atom-1: snow-container resets for the 3 new canvas Quill
   mounts. Kept admin-side (this file) to preserve the no-shared/
   no-restamp scope — canvas-shell.css carries body's equivalent
   resets (.mixed-body @ canvas-shell.css :157/:158/:173) but
   editing there would require a canonical deploy. The admin
   cascade into #canvas-stage works identically for the new roles. */
#canvas-stage .mixed-title.ql-container,
#canvas-stage .mixed-tagline1.ql-container,
#canvas-stage .mixed-tagline2.ql-container { border: 0; }
/* 4.7.71 polish-3: canvas Quill placeholder text. Quill 2's default
   .ql-blank::before color is near-black (invisible on the dark canvas); use the
   muted token. Render-only (Quill never serializes the placeholder via
   getContents); inherits each role's font-size. */
#canvas-stage .ql-editor.ql-blank::before { color: var(--muted); }
#canvas-stage .mixed-title .ql-editor,
#canvas-stage .mixed-tagline1 .ql-editor,
#canvas-stage .mixed-tagline2 .ql-editor { padding: 0; line-height: inherit; overflow: visible; }

/* 4.7.61 atom-4: hide always-emitted empty .mixed-tagline1 (gold pill from
   canvas-shell.css:108-120) and .mixed-tagline2 (accent shape from canvas-shell.css
   :123-133 + inline background at dashboard.js:3368) chrome in canvas-editor
   mode. Both blocks are emitted with backgrounds regardless of content (atom-1
   of 4.7.60 lineage — "blocks stay visible when mounted" preserves Quill mount
   stability). Quill v2.0.3 auto-toggles `.ql-blank` on `.ql-editor` for empty
   state, and the toggle runs even through `setContents(..., "silent")` (silent
   suppresses only the public text-change event, not the DOM class update).
   Kiosk path conditionally skips empty blocks already (shared/slide-renderers/
   slide-renderers.js), so this rule is canvas-editor-only by construction.
   The `!important` on .mixed-tagline2 is required because its background is set
   inline by the renderer; .mixed-tagline1's background is from CSS, no !important
   needed there. */
#canvas-stage .mixed-tagline1.ql-container:has(> .ql-editor.ql-blank) {
  background: transparent;
  padding: 0;
}
#canvas-stage .mixed-tagline2.ql-container:has(> .ql-editor.ql-blank) {
  background: none !important;
  padding: 0;
}
/* 4.7.71 atom-2: canvas-tagline editor geometry fix — latent since 4.7.60 atom-1.
   The kiosk pill styling (display: inline-flex on .mixed-tagline1/2 in
   canvas-shell.css L109/124) shrink-wraps the hosted Quill .ql-editor to ~1ch
   when empty, making the editor un-clickable AND breaking placeholder rendering
   (vertical letter-stack). Scope-restore block-width geometry only while empty;
   pill returns on first keystroke (matches prior-art :has() pattern @ L1804).
   Kiosk render preserved (canvas-shell.css untouched). */
#canvas-stage .mixed-tagline1.ql-container:has(.ql-editor.ql-blank),
#canvas-stage .mixed-tagline2.ql-container:has(.ql-editor.ql-blank) {
  display: block;
  width: 100%;
}
/* 4.7.71 polish-4: Tagline 1 caret visibility. .mixed-tagline1 sets color:#111
   (dark text for the gold pill, canvas-shell.css:112); the caret inherits
   currentColor, so an EMPTY tagline1 editor (block-width on the dark canvas per
   atom-2, no gold background) shows a near-black caret invisible on the dark
   canvas. tagline2 uses color:#fff so its caret is already visible. Force a light
   caret only in the empty state; once content fills, the gold pill returns and the
   #111 caret is visible on gold again (no filled-state regression). */
#canvas-stage .mixed-tagline1.ql-container:has(.ql-editor.ql-blank) .ql-editor {
  caret-color: var(--text);
}

/* 4.7.63 atom-1: File Library "Show Thumbnails" toggle + per-entry thumbnails */
.show-thumbs-row {
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
}
.media-thumb {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 8px;
  flex-shrink: 0;
  background: var(--bg);
}
.media-thumb-video {
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-thumb-video::before {
  content: "▶";
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
}
/* atom-2 (4.7.63): when .media-thumb is present in .list-item, absorb the gap
   on the name-block side so the name-block sits flush after the thumbnail (was
   getting centered by .list-item's space-between distributing 3 children). */
.list-item > .media-thumb + div { margin-right: auto; }

/* atom-3 (4.7.63): shrink the live-firing Trigger button so it doesn't dominate
   the row; new Preview button sized matching. */
#celebration-trigger-btn, #celebration-preview-btn {
  padding: 6px 10px;
  font-size: 0.85em;
}

/* atom-4 (4.7.63): row container so the Celebration buttons sit side-by-side at
   content width inside the full-width column flow of .details-body (which is
   flex-column/align-items:stretch — that cross-axis stretch was full-widthing
   and stacking the two buttons). The buttons are main-axis items here, so they
   self-size to content; no width override needed. */
.celebration-buttons-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}

/* QR-generator atom: popup wired to the QR URL field. Overlay/panel modeled on
   .media-swap-backdrop @ :1660 (same z-index tier + dark-backdrop idiom).
   .modal-overlay sets display:flex, so [hidden] needs an explicit override to
   stay hidden; JS removes the hidden attr to show. Canvas renders at 512×512
   internally (crisp print scan) but displays at 256×256. */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.modal-overlay[hidden] { display: none; }
.modal-overlay .modal-panel {
  background: #15151a;
  border: 1px solid var(--border);
  border-radius: 12px;
  width: min(600px, 92vw);
  max-height: 90vh;
  overflow-y: auto;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}
.qr-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.qr-modal-header h2 { margin: 0; font-size: 1.2rem; color: var(--text); }
#qr-generator-modal #qr-target-input { width: 100%; box-sizing: border-box; }
.qr-helper { margin: 4px 0 0; font-size: 13px; color: var(--muted); }
.qr-encoded-url {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  color: var(--text);
  word-break: break-all;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  min-height: 1.2em;
}
#qr-canvas {
  width: 256px;
  height: 256px;
  align-self: center;
  background: #fff;
  border-radius: 6px;
}
.qr-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 4px;
}

/* 4.7.65 atom-0 EDIT-4: drop the Canvas Editor controls row below the header.
   Was a 2-col flex row (.canvas-editor-heading | #rich-text-toolbar). Operator
   picked a single-column stack: the h2 + helper <p> read as one full-width
   header block (releases the 176px Polish-T1 narrow-column squeeze @ :1123),
   and the toolbar + Disabled toggle + Save Slide controls row sits on its own
   row above the canvas. The .row gap (10px) becomes the vertical gap between
   header block and controls row; the header's inline margin-bottom:12px spaces
   the controls row off the canvas. The toolbar carries an inline margin-left:auto
   (dashboard.js:2382, set for the OLD 2-col row to right-pin it). In a column
   container that's a CROSS-axis auto margin, which per the flexbox spec PREEMPTS
   align-items:stretch — so the toolbar would shrink to content width and dock
   right instead of filling the row. We reset it to 0 (!important to beat the
   inline style) so stretch fills the toolbar to the 900px header width; the
   .rt-right-col still docks the Disabled toggle + Save right via its OWN internal
   margin-left:auto. Supersedes the @media(max-width:980px) wrap-reflow @ :1828
   (now redundant but harmless under the column stack). Canvas-editor-header-
   scoped only. */
.preview-panel > .canvas-editor-header {
  flex-direction: column;
  align-items: stretch;
}
.preview-panel > .canvas-editor-header > .canvas-editor-heading {
  max-width: none;
}
.preview-panel > .canvas-editor-header > #rich-text-toolbar {
  margin-left: 0 !important;
}

/* 4.7.65 atom-1: persisted swatch library in the Pickr widget — hover-reveal ×
   remove badge on each swatch cell + the Add Swatch button. Pickr swatch cells
   are <button> children of .pcr-swatches; position:relative anchors the badge. */
.pcr-swatches > button { position: relative; overflow: visible; }
.cp-remove-x {
  position: absolute;
  top: -5px;
  right: -5px;
  width: 14px;
  height: 14px;
  line-height: 12px;
  text-align: center;
  font-size: 11px;
  border-radius: 50%;
  background: #1e1e22;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.5);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
  z-index: 2;
}
.pcr-swatches > button:hover > .cp-remove-x,
.pcr-swatches > button:focus-within > .cp-remove-x,
.cp-remove-x:focus { opacity: 1; }
.cp-add-swatch {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 6px 10px;
  font-size: 0.85em;
  background: #2a2a30;
  color: #e5e7eb;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 6px;
  cursor: pointer;
}
.cp-add-swatch:hover { background: #34343c; }
.cp-add-swatch:disabled { opacity: 0.7; cursor: default; }

/* Hero Slide / Review Farmer — faux-permanent card (4.7.67 polish 1).
   Gold border (#f5c542) lives on the OUTER #hero-slide-card; the inner row + accordion
   carry no border of their own. canvas-shell gold token is scoped to #canvas-stage and
   does not resolve in admin chrome (L420 belt-3); hardcoded here. */
.hero-slide-card {
  border: 2px solid #f5c542;
  border-radius: 14px;
  background: var(--card);
  padding: 12px 14px;
  margin-bottom: 16px;
}
.hero-slide-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.hero-slide-pin { font-size: 18px; flex: 0 0 auto; }
#hero-slide-accordion[hidden] { display: none; }
#hero-slide-accordion { margin-top: 12px; }
.hero-slide-accordion-title { margin: 0 0 12px; font-size: 1.1em; color: var(--text); }
.hero-slide-accordion-actions { display: flex; gap: 8px; margin-top: 16px; }

/* chip-hero — mirrors the established .chip-permanent / .chip-live ratio
   (0.18α background, 0.4α border, light-tint text) at gold #f5c542. */
.chip-hero {
  background: rgba(245, 197, 66, 0.18);
  color: #f5c542;
  border-color: rgba(245, 197, 66, 0.4);
}

/* 4.7.68 atom-1: active edit-mode highlight. Inset ring (not outline — taken by
   .drag-over; not a second border — clashes with .list-item's 1px border and
   #hero-slide-card's gold border) sits INSIDE the existing borders. Violet is
   collision-free vs chip-live green / chip-permanent blue / chip-hero gold /
   --accent red / chip-off grey. */
.slide-row.is-editing,
#hero-slide-card.is-editing {
  box-shadow: inset 0 0 0 2px rgba(139, 92, 246, 0.4);
  background-color: rgba(139, 92, 246, 0.08);
}

/* 4.7.69 atom-0 — Canvas Editor header heading-row + right-side actions */
.canvas-editor-heading-row {
  flex: 1 1 auto;
}
.canvas-editor-actions {
  display: flex;
  gap: 8px;
  align-items: flex-start;
}

/* 4.7.73 atom-0 — dashboard brand block */
#dashboard-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}
#dashboard-brand-logo {
  max-height: 96px;
  width: auto;
  object-fit: contain;
}
.dashboard-brand-key {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.02em;
}
#dashboard-actions {
  margin-bottom: 16px;
}
@media (max-width: 640px) {
  #dashboard-brand-logo { display: none; }
  #dashboard-brand { align-items: flex-start; text-align: left; }
}

/* 4.7.73 atom-2 — Last 10 slide explainer (CSS :has() conditional, styles.css:1381 precedent) */
.last-10-explainer {
  display: none;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
  /* 4.7.73 atom-2 polish-2 — breathing room from chips row above */
  margin-top: 16px;
}
.rrr-explainer {
  display: none;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
  /* 4.7.73 atom-2 polish-3 — breathing room from chips row above */
  margin-top: 16px;
}
/* 4.7.73 atom-2 polish-2 — keep .slide-row-actions on row line 1 when accordion open.
   The explainer's long max-content was making main claim line 1 alone; flex:1 1 0
   + min-width:0 sets main's wrap-decision basis to 0 so main + actions fit on line 1,
   then main grows back via grow:1. Scoped to accordion-open so non-permanent rows and
   closed permanent rows keep their prior layout exactly. */
.slide-row:has(.permanent-slide-accordion.is-open) .slide-row-main {
  flex: 1 1 0;
  min-width: 0;
}
.slide-row:has(.permanent-slide-accordion.is-open) .last-10-explainer {
  display: block;
}
.slide-row:has(.permanent-slide-accordion.is-open) .rrr-explainer {
  display: block;
}

/* Canvas Editor monitor frame — intake (7), Stage 4.7.82 */
.canvas-frame {
  position: relative;
  max-width: 900px;
  /* base wrapper; max-width matches .canvas-wrap's 900px cap. Under global border-box,
     visual <= 900px and content area <= 900 - 2*padding (variant-dependent). per-variant
     rules add padding/background/border-radius + stand pseudo. */
}

.canvas-frame[data-frame-style="modern"] {
  padding: 10px;
  background: linear-gradient(180deg, #2a2d35 0%, #1a1c22 100%);
  border-radius: 14px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 4px 12px rgba(0, 0, 0, 0.3);
}

.canvas-frame[data-frame-style="crt"] {
  padding: 18px 24px 22px 24px;
  background: linear-gradient(180deg, #3a2e22 0%, #1f1812 100%);
  border-radius: 22px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 6px 16px rgba(0, 0, 0, 0.4);
}

/* data-frame-style="none" (portrait 9/16): .canvas-frame stays transparent passthrough; no per-variant rules emit */
