:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel-2: #1e222b;
  --border: #2a2f3a;
  --text: #e8eaed;
  --text-dim: #9aa2ad;
  --accent: #4f8cff;
  --accent-2: #2ecf7a;
  --danger: #ef5350;
  --radius: 10px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------------------------------------------------------------------------
   The admin surfaces in the corporate identity — white ground, ink type, the
   amber accent from the marketing site. Matthew, 2026-08-28: the backend should
   look like the front.

   Scoped to html[data-ui="light"] and deliberately NOT applied to :root.
   display.html — the television in a hotel lobby — carries 1,418 lines of its
   own CSS but defines only LAYOUT variables of its own, and reads --bg, --panel
   and --text from this file 69 times. Flipping the root here would turn every
   customer's screen white. So the screen keeps the dark palette above, and only
   the pages a staff member logs into opt in.

   The accent carries three jobs on white where on dark it carried one:
     --accent       fills (a button, a progress dot)
     --accent-ink   type ON those fills — amber needs dark type, never white
     --accent-text  accent-coloured type on the white ground, darkened to stay
                    legible; #ffad00 on #fff is about 1.7:1 and fails outright.
   -------------------------------------------------------------------------- */
html[data-ui="light"] {
  --bg: #ffffff;
  --panel: #f7f7f8;
  /* --panel-2 is the form-field and secondary-button surface. Missing it is what
     left the stat tiles, every input and the log-out button as dark boxes on a
     white page, with dark type inside them. */
  --panel-2: #ffffff;
  --accent-2: #17864a;
  --border: #e4e4e7;
  --text: #0b0b0c;
  --text-dim: #5f6368;
  --accent: #ffad00;
  --accent-ink: #3d2900;
  --accent-text: #8a5c00;
  --danger: #c0392b;
}

/* Type on the accent. The base rules hardcode #fff, unreadable on amber. */
html[data-ui="light"] button,
html[data-ui="light"] .btn,
html[data-ui="light"] .btn-hero,
html[data-ui="light"] .onboard-tabs button.active { color: var(--accent-ink); }

/* Accent-coloured TEXT has to darken on a white ground.
   Not inside the navigation rail: that is dark in BOTH themes, so a colour
   chosen for a white ground is wrong there twice over. Left unexcluded, this
   rule painted the Studio link amber among grey siblings — it out-specifies
   `.side a` because of the attribute selector on <html>. */
html[data-ui="light"] a:not(.side a),
html[data-ui="light"] .link-btn,
html[data-ui="light"] .brand span,
html[data-ui="light"] .onboard-kicker { color: var(--accent-text); }

/* The accent tints are hardcoded blue throughout (rgba(79,140,255,...)) because
   they predate the variable. Restated here in amber rather than rewritten as
   color-mix() at source: this stylesheet is also read by the screen path, and a
   colour function an old TV browser cannot parse drops the declaration outright,
   which is a worse failure than a stale blue. */
html[data-ui="light"] .onboard-icon        { background: rgba(255,173,0,0.16); color: var(--accent-text); }
html[data-ui="light"] .btn-hero            { box-shadow: 0 8px 24px rgba(255,173,0,0.32); }
html[data-ui="light"] .dropzone.dragover   { background: rgba(255,173,0,0.12); }
html[data-ui="light"] .library-item.assigned { box-shadow: 0 0 0 2px rgba(255,173,0,0.45); }
html[data-ui="light"] .voice-preview-block.selected { background: rgba(255,173,0,0.28); }
html[data-ui="light"] .sp-zone-instore     { background: rgba(255,173,0,0.16); }
html[data-ui="light"] .sp-zone-ad          { background: rgba(255,173,0,0.08); }

/* On dark, a field read as a field because it was lighter than the page. On white
   it is the same colour as the page, so the border has to do that work instead. */
html[data-ui="light"] input,
html[data-ui="light"] select,
html[data-ui="light"] textarea { border: 1px solid var(--border); }

/* Keyboard focus. login.html defines an amber focus ring for its own fields and
   the dashboard defined none at all, so the moment a venue tabbed into anything
   past the front door the accent turned Chrome-default blue — the one colour
   this identity does not contain. Same ring on both sides of the login now.
   :focus-visible, not :focus, so a mouse click on a button does not paint one. */
html[data-ui="light"] input:focus-visible,
html[data-ui="light"] select:focus-visible,
html[data-ui="light"] textarea:focus-visible,
html[data-ui="light"] button:focus-visible,
html[data-ui="light"] .btn:focus-visible,
html[data-ui="light"] a:focus-visible,
html[data-ui="light"] summary:focus-visible,
html[data-ui="light"] [tabindex]:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}
html[data-ui="light"] input:focus-visible,
html[data-ui="light"] select:focus-visible,
html[data-ui="light"] textarea:focus-visible {
  border-color: var(--accent-text);
  outline-offset: 0;
}

/* Every form in this product answers through .msg, and both colours were
   picked for a near-black page: #7fe3ac on the 12%-green tint over white is
   about 1.9:1 and #ff8a85 on the red tint about 2.4:1 — the confirmation you
   get for signing up, saving an element or inviting a partner was legible only
   if you already knew what it said. The tints stay (they are the thing that
   makes a result feel like a result); the type darkens onto them. */
html[data-ui="light"] .msg.ok {
  background: #eaf8f0; color: #0f6b3a; border-color: #b6e3c9;
}
html[data-ui="light"] .msg.error {
  background: #fdeceb; color: #a4271b; border-color: #f2c2bd;
}
/* A `code` chip and a `pre` block both painted themselves in --panel-2, which
   on white IS white — an inline SKU or endpoint had no chip at all and the
   curl example was a wall of unbounded text. */
html[data-ui="light"] code { background: var(--panel); border: 1px solid var(--border); }
html[data-ui="light"] pre { background: var(--panel) !important; border: 1px solid var(--border); }
/* White tick on amber is 1.8:1. The tick is the whole point of the checklist. */
html[data-ui="light"] .setup-tick { color: var(--accent-ink); }
/* --line is not defined anywhere on this page, so every printable guest code
   fell back to the hardcoded dark-navy #2a3444 on a white card. */
html[data-ui="light"] .qr-card { border-color: var(--border); }
html[data-ui="light"] .overview-stat { background: var(--panel); border: 1px solid var(--border); }
/* .btn.secondary as well as button.secondary — an <a class="btn secondary">
   (the "Open the guest page" link on a package row) was getting the base rule
   only, so it never picked up the light hover and sat visibly apart from the
   real buttons beside it. */
html[data-ui="light"] button.secondary,
html[data-ui="light"] .btn.secondary { background: #fff; color: var(--text); border: 1px solid var(--border); }
html[data-ui="light"] button.secondary:hover,
html[data-ui="light"] .btn.secondary:hover { background: var(--panel); }


* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  overflow-x: hidden;
}

a { color: var(--accent); }
.link-btn {
  background: none; border: none; padding: 0; margin: 0;
  color: var(--accent); font: inherit; cursor: pointer; text-decoration: underline;
}

.wrap {
  max-width: 1080px;
  margin: 0 auto;
  padding: 32px 20px 80px;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}

.brand {
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.02em;
}

.brand a { display: inline-flex; align-items: center; gap: 8px; }

.brand span { color: var(--accent); }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 20px;
}

.card h2, .card h3 { margin-top: 0; }

input, select, textarea {
  width: 100%;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 0.95rem;
  /* Form controls do not inherit font-family — that is a browser default, not
     an oversight of the cascade. Only font-size was being set here, so every
     <textarea> in this dashboard (the resolution note a guest reads, the room
     list, the layout description, the product specs) rendered in Courier while
     the <input> beside it rendered in the system font. line-height too: a
     textarea's default is `normal`, which is tighter than the body text it
     sits in. */
  font-family: inherit;
  line-height: 1.45;
}

label { display: block; font-size: 0.85rem; color: var(--text-dim); margin: 12px 0 4px; }

button, .btn {
  display: inline-block;
  padding: 10px 16px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 8px;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

button.secondary, .btn.secondary { background: var(--panel-2); border: 1px solid var(--border); color: var(--text); }
button.danger { background: transparent; border: 1px solid var(--danger); color: var(--danger); }

button:disabled { opacity: 0.5; cursor: not-allowed; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
th { color: var(--text-dim); font-weight: 600; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.03em; }

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
}
.badge.active { background: rgba(46, 207, 122, 0.15); color: var(--accent-2); }
.badge.unclaimed { background: rgba(239, 83, 80, 0.15); color: var(--danger); }
.badge.in-stock { background: rgba(46, 207, 122, 0.15); color: var(--accent-2); }
.badge.sold-out { background: rgba(239, 83, 80, 0.15); color: var(--danger); }
/* A third, deliberately quiet tone. Green means "this sells"; red means "this
   is a problem"; a lot of true states are neither, and were being forced into
   one of the two. */
.badge.neutral { background: var(--panel); border: 1px solid var(--border); color: var(--text-dim); }

/* The chooser for the six always-on panels (Media tab). One of them is open at
   a time, so the chip for the open one has to read as pressed — a bare
   `button.active` is styled nowhere else in this stylesheet. */
.section-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.section-chips button.active,
html[data-ui="light"] .section-chips button.active {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
}

.overview-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; }
.overview-stat { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; min-width: 0; }
.overview-stat .value { font-size: 2rem; font-weight: 800; line-height: 1.15; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.overview-stat .label { color: var(--text-dim); font-size: 0.82rem; margin-top: 6px; }
.overview-stat.warn .value { color: var(--danger); }
.overview-stat.good .value { color: var(--accent-2); }

.status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.status-dot.online { background: var(--accent-2); box-shadow: 0 0 6px rgba(46, 207, 122, 0.6); }
.status-dot.offline { background: var(--danger); box-shadow: 0 0 6px rgba(239, 83, 80, 0.6); }

.trend-chart { display: flex; align-items: flex-end; gap: 3px; height: 90px; }
.trend-chart .bar-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.trend-chart .bar { width: 100%; border-radius: 3px 3px 0 0; min-height: 2px; }
.trend-chart .bar.offline-bar { background: var(--danger); }
.trend-chart .bar.scan-bar { background: var(--accent); }
.trend-chart .bar-label { font-size: 0.62rem; color: var(--text-dim); margin-top: 4px; white-space: nowrap; }
.trend-chart .bar-value { font-size: 0.65rem; color: var(--text-dim); margin-bottom: 2px; }
.holiday-dot { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--accent-2); margin-left: 3px; vertical-align: middle; }
.trend-empty { color: var(--text-dim); font-size: 0.85rem; }

.check-list { display: flex; flex-direction: column; gap: 6px; max-height: 160px; overflow-y: auto; padding: 8px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius); }
.check-list label { display: flex; align-items: center; gap: 8px; font-weight: 400; font-size: 0.85rem; cursor: pointer; }
.check-list input[type="checkbox"] { width: auto; accent-color: var(--accent); }
.check-list .empty { color: var(--text-dim); font-size: 0.82rem; }
/* A check-list holding nothing but its own "none yet" note keeps the field
   border and the panel fill it needs when it holds checkboxes — so on an empty
   account "No locations yet." sat inside what looks exactly like a text input
   somebody had already typed into. With nothing to tick, it is a note under a
   label, not a control. */
.check-list:has(> .empty:only-child) {
  background: none; border: 0; padding: 2px 0 0; overflow: visible;
}

.msg { padding: 10px 14px; border-radius: 8px; margin: 12px 0; font-size: 0.9rem; }
.msg.error { background: rgba(239, 83, 80, 0.12); color: #ff8a85; border: 1px solid rgba(239,83,80,0.3); }
.msg.ok { background: rgba(46, 207, 122, 0.12); color: #7fe3ac; border: 1px solid rgba(46,207,122,0.3); }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 720px) { .grid-2 { grid-template-columns: 1fr; } }

.muted { color: var(--text-dim); font-size: 0.85rem; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
/* Wraps rather than scrolls. It used to scroll horizontally with the scrollbar
   deliberately hidden, which on a 1440px laptop put Departments, Users and
   Account Admin past the right edge with nothing on screen suggesting they
   existed — a nav item nobody can see is a feature nobody has. Twelve tabs do
   not fit on one line at laptop widths and there is no reason they must. */
.tabs {
  display: flex; flex-wrap: wrap; gap: 4px 2px;
  border-bottom: 1px solid var(--border); margin-bottom: 20px;
}
.tabs button { background: none; color: var(--text-dim); border-radius: 0; padding: 10px 16px; border-bottom: 2px solid transparent; flex: 0 0 auto; white-space: nowrap; }
.tabs button.active { color: var(--text); border-bottom-color: var(--accent); }
code { background: var(--panel-2); padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }

.library-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; }
/* Six of these grids render their "nothing here yet" line as a bare <p>, which
   the grid then placed in the first 180px track — "No elements yet — add one
   above, then assign it to a profile below." wrapped onto four lines in a
   narrow left column with the rest of the row empty beside it. */
.library-grid > p { grid-column: 1 / -1; margin: 2px 0 0; }
.library-item { background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; transition: border-color 0.15s; }
.library-item:hover { border-color: var(--accent); }
.library-item .thumb { width: 100%; height: 100px; object-fit: cover; background: #000; display: block; }
.library-item .thumb-placeholder { width: 100%; height: 100px; display: flex; align-items: center; justify-content: center; color: var(--text-dim); font-size: 0.8rem; background: #000; }
.library-item .caption { padding: 8px 10px; font-size: 0.85rem; }
.library-item .caption .type { display: block; color: var(--text-dim); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; margin-top: 2px; }
.library-item .thumb-wrap { position: relative; cursor: pointer; }
.library-item .thumb-play-badge {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.5); color: #fff; font-size: 0.78rem; font-weight: 600;
  opacity: 0; transition: opacity 0.15s;
}
.library-item .thumb-wrap:hover .thumb-play-badge { opacity: 1; }
.library-item .row { padding: 0 10px 10px; }

.sap-fields { background: var(--panel-2); border: 1px dashed var(--border); border-radius: 8px; padding: 12px 16px; margin-bottom: 16px; }
.sap-fields .kicker-sm { color: var(--text-dim); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; }
.sap-fields dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0; font-size: 0.85rem; }
.sap-fields dt { color: var(--text-dim); }
.sap-fields dd { margin: 0; }

/* Screen profiles — one nested block per profile (name, four holder
   pickers, deploy row), stacked inside the "Screen profiles" card. A
   dashed border (same treatment as .sap-fields) marks it as a distinct
   sub-unit within the card rather than a whole separate card each. */
.profile-block { background: var(--panel-2); border: 1px dashed var(--border); border-radius: 10px; padding: 16px 18px; margin-bottom: 16px; }
.profile-block h4 { margin: 0 0 2px; font-size: 1rem; }
.profile-block .holder-groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; margin-top: 14px; }
.holder-group-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); margin-bottom: 6px; }
.profile-deploy-row { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.profile-deploy-row .check-list { max-height: 130px; }

/* Voice layout builder — a scaled-down stand-in for the real display's
   own top/bottom holder rows (see public/display.js's renderHolderLayout
   for the real thing), just laid out with plain flexbox blocks instead
   of actual rotating content, since the point here is picking the
   COUNT/POSITION/direction and then what plays in each one, not a
   pixel-exact preview of the real screen. */
.voice-builder-panes { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 18px; }
@media (max-width: 820px) { .voice-builder-panes { grid-template-columns: 1fr; } }
.voice-preview-canvas {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: linear-gradient(160deg, #1a2030, #090b11);
  border: 1px solid var(--border); border-radius: 10px;
  box-sizing: border-box;
  overflow: hidden;
}
/* Every block is positioned in absolute %-of-canvas terms (left/top/
   width/height set inline per-holder — see renderVoicePreviewCanvas),
   real drag-and-resize rather than a fixed top/bottom flex row. Sizing
   and position both start from a sensible zone-based default the first
   time a block appears, then whatever the user drags/resizes it to
   from there is exactly what gets saved (see saveVoiceProfile). */
.voice-preview-block {
  position: absolute; box-sizing: border-box;
  background: rgba(79,140,255,0.14);
  border: 1.5px dashed var(--accent);
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.62rem; line-height: 1.25; color: var(--text-dim); text-align: center;
  cursor: grab; padding: 4px; overflow: hidden;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
}
.voice-preview-block.dragging, .voice-preview-block.resizing { cursor: grabbing; z-index: 5; transition: none; }
.voice-preview-block.selected { border-color: #fff; border-style: solid; background: rgba(79,140,255,0.32); }
.voice-preview-block .voice-block-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.85; pointer-events: none; }
.voice-preview-block .voice-block-label { position: relative; z-index: 1; background: rgba(0,0,0,0.55); border-radius: 4px; padding: 2px 4px; pointer-events: none; }
/* Bottom-right resize handle — its own mousedown target, separate from
   the block body's own drag handling (see the canvas's mousedown
   delegation in dashboard.js: a mousedown starting here is always a
   resize, never a drag/select). */
.voice-block-resize-handle {
  position: absolute; right: -1px; bottom: -1px; width: 14px; height: 14px;
  cursor: nwse-resize; z-index: 2;
  background: linear-gradient(135deg, transparent 50%, var(--accent) 50%);
  border-radius: 0 0 5px 0;
}
.voice-preview-block.selected .voice-block-resize-handle { background: linear-gradient(135deg, transparent 50%, #fff 50%); }
/* Top-left X — its own mousedown/click target, same "never starts a
   drag" treatment as the resize handle above (see dashboard.js's
   canvas mousedown delegation, which excludes both this and the title
   input before deciding drag vs. resize). */
.voice-block-remove {
  position: absolute; left: 3px; top: 3px; z-index: 3;
  width: 18px; height: 18px; line-height: 16px; padding: 0;
  border-radius: 50%; border: 1px solid var(--border);
  background: rgba(0,0,0,0.55); color: var(--text-dim);
  font-size: 0.85rem; cursor: pointer;
}
.voice-block-remove:hover { background: #c0392b; color: #fff; border-color: #c0392b; }
/* Editable block title — sits over the label placeholder, real text
   input rather than contenteditable so it behaves the same as every
   other form field in the dashboard (focus ring, IME, paste). Blank
   just shows the auto-generated placeholder (what's assigned, or a
   prompt to assign something) rather than forcing every block to be
   named. */
.voice-block-title-input {
  position: relative; z-index: 1; width: 88%;
  background: rgba(0,0,0,0.55); border: 1px solid transparent;
  border-radius: 4px; padding: 2px 4px; margin: 0;
  color: #fff; font-size: 0.62rem; text-align: center;
  cursor: text;
}
.voice-block-title-input:focus { border-color: var(--accent); outline: none; }
.voice-block-title-input::placeholder { color: var(--text-dim); }
.library-item.assignable { cursor: pointer; }
/* Brief highlight when "Deploy to screens" on the Screen preview canvas
   scrolls down to the Default profile's own block in the Screen
   profiles list below — same deploy checkboxes every profile already
   has, this just draws the eye to which one just got scrolled to. */
.profile-block.flash-highlight { animation: flash-highlight-pulse 1.8s ease; }
@keyframes flash-highlight-pulse {
  0%, 100% { box-shadow: none; }
  15% { box-shadow: 0 0 0 3px var(--accent); }
}
.library-item.assigned { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(79,140,255,0.35); }

/* Content-source tree — Owner Content / Corporate Partners / API
   Connections / Local Community (see renderLibrary in dashboard.js).
   Owner Content & Corporate Partners hold real draggable tiles (reuses
   .library-item above, just now inside a collapsible category rather
   than one flat grid); API Connections & Local Community are
   visibility-only for now (.source-info-tile) — see dashboard.js's
   comment on why their tiles aren't draggable yet. */
.source-tree { display: flex; flex-direction: column; gap: 10px; }
.source-category { background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; }
.source-category summary { cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 10px; font-weight: 600; font-size: 0.88rem; list-style: none; }
.source-category summary::-webkit-details-marker { display: none; }
.source-category summary::before { content: '▸'; margin-right: 6px; color: var(--text-dim); font-size: 0.7rem; display: inline-block; transition: transform 0.15s; }
.source-category[open] summary::before { transform: rotate(90deg); }
.source-category-count { color: var(--text-dim); font-size: 0.78rem; font-weight: 400; }
.source-category-hint { margin: 4px 0 10px; font-size: 0.75rem; }
.source-category-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; margin-top: 8px; }
.source-category-items .library-item[draggable="true"] { cursor: grab; }
.source-category-items .library-item[draggable="true"]:active { cursor: grabbing; }
.source-tree-empty, .source-tree-more, .source-tree-note, .source-tree-link { grid-column: 1 / -1; margin: 0; }
.source-tree-link { margin-top: 6px; }
.source-info-tile { background: var(--panel); border: 1px dashed var(--border); border-radius: 8px; padding: 8px 10px; opacity: 0.8; cursor: default; }
.source-info-tile .caption { font-size: 0.8rem; }

/* The holder box currently being dragged over — mirrors the .selected
   look (see .voice-preview-block.selected) so a drop target reads as
   "about to receive this" the same visual language as "already picked". */
.voice-preview-block.drop-target { border-color: #fff; border-style: solid; background: rgba(79,140,255,0.4); box-shadow: 0 0 0 3px rgba(79,140,255,0.35); }

/* Shared "onboarding" visual language — used by the login page and the
   device-claim wizard, so the whole path from public marketing site to
   a claimed screen feels like one continuous, deliberately minimal
   product rather than a polished front door opening onto a plain admin
   form. One hero action per screen; everything else stays quieter. */
body.onboard-bg { background: radial-gradient(ellipse 900px 500px at 50% -10%, rgba(79,140,255,0.10), transparent 60%), var(--bg); }
/* A faint sketch-art wallpaper (line-art screens/QR/price-tags, see
   activate-bg.svg) for the front door of the whole activation flow —
   opt-in via this second class rather than folded into .onboard-bg
   itself, so claim.html/login.html aren't affected until they want it
   too. */
body.onboard-wallpaper {
  background:
    radial-gradient(ellipse 900px 500px at 50% -10%, rgba(79,140,255,0.14), transparent 60%),
    url('/activate-bg.svg') repeat,
    var(--bg);
}
.onboard-wrap { max-width: 420px; margin: 0 auto; padding: 48px 20px 80px; }
.onboard-progress { display: flex; gap: 6px; margin-bottom: 28px; }
.onboard-progress .dot { flex: 1; height: 3px; border-radius: 2px; background: var(--border); transition: background 0.25s; }
.onboard-progress .dot.done, .onboard-progress .dot.active { background: var(--accent); }

.onboard-card { text-align: center; }
.onboard-kicker {
  color: var(--accent); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px;
}
.onboard-icon {
  width: 56px; height: 56px; margin: 0 auto 20px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 16px; background: rgba(79,140,255,0.12);
  color: var(--accent);
}
.onboard-card h1 { font-size: 1.5rem; margin: 0 0 8px; letter-spacing: -0.01em; }
.onboard-sub { color: var(--text-dim); font-size: 0.92rem; margin: 0 0 28px; line-height: 1.5; }

.btn-hero {
  display: block; width: 100%; padding: 16px; border: none; border-radius: 14px;
  background: var(--accent); color: #fff; font-size: 1.05rem; font-weight: 700;
  cursor: pointer; box-shadow: 0 8px 24px rgba(79,140,255,0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  text-align: center; text-decoration: none;
}
.btn-hero:active { transform: scale(0.98); }
.btn-hero:disabled { opacity: 0.5; box-shadow: none; cursor: not-allowed; }
.btn-hero.subtle { background: var(--panel-2); color: var(--text); box-shadow: none; border: 1px solid var(--border); }

.onboard-disclosure { text-align: left; margin-top: 18px; }
.onboard-disclosure summary {
  cursor: pointer; color: var(--text-dim); font-size: 0.85rem; list-style: none;
  display: flex; align-items: center; gap: 6px; padding: 4px 0;
}
.onboard-disclosure summary::-webkit-details-marker { display: none; }
.onboard-disclosure summary::before { content: '›'; display: inline-block; transition: transform 0.15s; font-weight: 700; }
.onboard-disclosure[open] summary::before { transform: rotate(90deg); }
.onboard-disclosure .disclosure-body { padding: 10px 0 2px; }

.onboard-store label, .onboard-store input, .onboard-store select { text-align: left; }

/* Pill tabs — a lighter-weight alternative to the underline .tabs style,
   used where several equally-weighted entry points (log in / sign up /
   demo) sit above a single onboard-card rather than inside a dashboard. */
.onboard-tabs { display: flex; gap: 6px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 4px; margin-bottom: 20px; }
.onboard-tabs button { flex: 1; background: none; border: none; border-radius: 9px; padding: 9px 0; color: var(--text-dim); font-size: 0.9rem; font-weight: 600; cursor: pointer; }
.onboard-tabs button.active { background: var(--accent); color: #fff; }

.done-check {
  width: 64px; height: 64px; margin: 0 auto 20px; border-radius: 50%;
  background: rgba(46,207,122,0.14); display: flex; align-items: center; justify-content: center;
  animation: done-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes done-pop { 0% { transform: scale(0.4); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .done-check { animation: none; }
}

.dropzone {
  border: 2px dashed var(--border);
  border-radius: 8px;
  padding: 24px;
  text-align: center;
  color: var(--text-dim);
  cursor: pointer;
  margin-top: 4px;
  transition: border-color 0.15s, background 0.15s;
}
.dropzone.dragover { border-color: var(--accent); background: rgba(79, 140, 255, 0.08); }
.dropzone img { max-width: 100%; max-height: 160px; border-radius: 6px; margin-bottom: 10px; display: block; margin-left: auto; margin-right: auto; }

/* Screen preview — a rough, clickable map of the real display layout
   (see public/display.html), so managing content means "click the
   zone you want to change" instead of guessing which library feeds
   what. Deliberately not pixel-exact to the real display — just
   recognizable enough to map zone → section below. */
.screen-preview {
  background: #0c1420; border: 1px solid var(--border); border-radius: 14px;
  overflow: hidden; aspect-ratio: 16 / 8; max-width: 640px;
}
.sp-tape {
  background: #e53935; color: #fff; font-weight: 800; font-size: 0.7rem;
  letter-spacing: 0.08em; text-transform: uppercase; text-align: center;
  padding: 6px 0; cursor: pointer;
}
.sp-body {
  height: calc(100% - 27px); position: relative; cursor: pointer;
  background: radial-gradient(ellipse at 50% 20%, rgba(79,140,255,0.16), transparent 60%);
  display: flex; align-items: flex-end; padding: 10px;
}
.sp-wallpaper-label {
  position: absolute; top: 10px; left: 12px;
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: rgba(255,255,255,0.4);
}
.sp-zone {
  flex: 1; margin: 0 4px; border-radius: 8px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 4px; padding: 10px 6px; height: 62%;
  border: 1px solid rgba(255,255,255,0.14); transition: transform 0.15s, border-color 0.15s;
}
.sp-zone:hover { transform: translateY(-3px); border-color: var(--accent); }
.sp-zone span { font-size: 0.78rem; font-weight: 700; color: #fff; }
.sp-zone small { font-size: 0.65rem; color: var(--text-dim); }
.sp-zone-price { background: rgba(255,255,255,0.06); position: relative; }
.sp-zone-instore { background: rgba(79,140,255,0.14); flex: 1.3; }
.sp-zone-ad { background: rgba(79,140,255,0.06); }
/* The "4th element" — Insure This — sits over the price card just like
   it does on the real display, rather than being a hidden setting with
   no place in the preview at all. */
.sp-insure-badge {
  position: absolute; top: -9px; left: 50%; transform: translateX(-50%);
  background: linear-gradient(135deg, #00d4ff, #0080ff); color: #04121f;
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.sp-zone.active, .sp-tape.active { outline: 2px solid var(--accent); outline-offset: 2px; }
.sp-insure-badge.active { outline: 2px solid #00d4ff; outline-offset: 2px; }

/* Shared modal — replaces every native confirm() and is the one place a
   piece of content actually plays before it's assigned to a screen. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(5, 7, 10, 0.72);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.modal-card {
  background: var(--panel); border: 1px solid var(--border); border-radius: 16px;
  padding: 28px; max-width: 420px; width: 100%;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
  animation: modal-pop 0.18s ease-out;
}
.modal-card-wide { max-width: 720px; }
@keyframes modal-pop { 0% { transform: scale(0.96); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
.modal-title { margin: 0 0 14px; font-size: 1.1rem; }
.modal-message { margin: 0 0 22px; font-size: 0.95rem; line-height: 1.5; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.modal-actions .btn-hero { width: auto; padding: 10px 20px; font-size: 0.9rem; box-shadow: none; }
.btn-hero.danger { background: var(--danger); color: #fff; }
.modal-video-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: #000; margin-bottom: 18px; }
.modal-video-frame iframe, .modal-video-frame video { width: 100%; height: 100%; border: none; }
.modal-image { display: block; width: 100%; max-height: 70vh; object-fit: contain; border-radius: 10px; margin-bottom: 18px; }

@media (prefers-reduced-motion: reduce) {
  .modal-card { animation: none; }
}

/* Shared by login.html and claim.html's inline sign-in panel — both
   present the same password + "Continue with Google" choice, this way
   the styling only exists once. */
.auth-divider { display:flex; align-items:center; gap:10px; margin:16px 0; color:var(--text-dim); font-size:0.8rem; }
.auth-divider::before, .auth-divider::after { content:''; flex:1; height:1px; background:var(--border); }
.google-btn {
  display:flex; align-items:center; justify-content:center; gap:10px;
  width:100%; padding:11px; border-radius:var(--radius);
  border:1px solid var(--border); background:var(--panel-2); color:var(--text);
  font-size:0.95rem; font-weight:600; text-decoration:none; box-sizing:border-box;
  transition:border-color 0.15s;
}
.google-btn:hover { border-color:var(--text-dim); }
#auth-error { color:var(--danger); font-size:0.85rem; text-align:center; margin:0 0 14px; }

/* Printable guest codes (Departments tab). The screen layout only — the print
   sheet writes its own stylesheet into the popup, because print rules living in
   this file get silently broken by every later change to the dashboard. */
.qr-sheet {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 18px;
}
.qr-card {
  margin: 0;
  width: 176px;
  padding: 14px;
  border: 1px solid var(--line, #2a3444);
  border-radius: 12px;
  text-align: center;
}
.qr-card img {
  width: 148px;
  height: 148px;
  /* The PNG is black-on-transparent; a scanner needs the light quiet zone, and
     on the dark dashboard theme it would otherwise be black on black. */
  background: #fff;
  border-radius: 6px;
}
.qr-card figcaption strong {
  display: block;
  margin-top: 10px;
  font-size: 14px;
  overflow-wrap: anywhere;
}
.qr-card figcaption span {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  opacity: .7;
}

/* ---------------------------------------------------------------------------
   Empty states.

   Two treatments, deliberately different in weight.

   1. `td[colspan].muted` — the one-line row that seventeen tables in
      dashboard.js already render when they have nothing. It was inheriting a
      normal data cell: left-aligned, 10px of padding, its own bottom rule, so
      "No campaigns yet." read as a row of data rather than as the absence of
      any. Styling the selector rather than the seventeen strings means every
      table gets the same answer, including the ones written after today.

   2. `.empty-state` — for the ONE place on a tab where the absence is the whole
      story and there is a specific next action to offer. Not applied to every
      empty table: six identical hero cards on one tab is noise, not guidance.
   -------------------------------------------------------------------------- */
td[colspan].muted {
  text-align: center;
  padding: 24px 12px;
  border-bottom: 0;
}

.empty-state {
  text-align: center;
  padding: 32px 22px 28px;
  border: 1px dashed var(--border);
  border-radius: 12px;
  background: var(--panel-2);
}
.empty-state .es-icon {
  width: 46px; height: 46px; margin: 0 auto 14px;
  display: grid; place-items: center;
  border-radius: 13px;
  background: var(--panel);
  border: 1px solid var(--border);
  /* --accent-text exists only on the light admin surfaces; the fallback keeps
     this legible anywhere else the stylesheet is loaded. */
  color: var(--accent-text, var(--accent));
}
.empty-state b { display: block; font-size: 1rem; letter-spacing: -0.01em; margin-bottom: 5px; }
.empty-state p {
  margin: 0 auto; max-width: 52ch;
  font-size: 0.87rem; line-height: 1.55; color: var(--text-dim);
}
.empty-state .es-actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; justify-content: center;
  margin-top: 18px;
}
.empty-state .es-actions code { font-size: 0.82rem; padding: 8px 10px; }

/* An empty chart is 90px of nothing with a sentence stuck to the floor of it,
   because .trend-chart aligns its bars to the bottom. The message belongs in
   the middle of the space it is explaining. */
.trend-chart:has(.trend-empty) { align-items: center; justify-content: center; }

/* A native file input is the single most "unstyled admin form" thing on the
   page — three of them ship in this dashboard. The control itself cannot be
   restyled, but its button can, and the field it sits in can stop looking like
   a 1997 upload form. */
input[type="file"] {
  padding: 9px 12px;
  cursor: pointer;
  color: var(--text-dim);
  font-size: 0.88rem;
}
input[type="file"]::file-selector-button {
  margin-right: 12px;
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { border-color: var(--text-dim); }

/* The first-run path (Overview). Deliberately not a .card: it is the one thing
   on screen that is asking to be acted on, and it stops existing once it is
   finished, so it should not look like the furniture around it. */
.setup {
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  padding: 20px 22px;
  margin-bottom: 20px;
}
.setup-head {
  display: flex; gap: 16px; align-items: flex-start;
  justify-content: space-between; flex-wrap: wrap;
}
.setup-head h3 { margin: 0 0 4px; }
.setup-head p { margin: 0; font-size: 0.88rem; }
.setup-count { text-align: right; white-space: nowrap; }
.setup-count b { display: block; font-size: 1.6rem; line-height: 1; }
.setup-count span { font-size: 0.78rem; opacity: 0.7; }

.setup-bar {
  height: 4px; border-radius: 2px; background: var(--border);
  margin: 16px 0 4px; overflow: hidden;
}
.setup-bar span {
  display: block; height: 100%; background: var(--accent);
  border-radius: 2px; transition: width 0.4s ease;
}

.setup-steps { list-style: none; margin: 0; padding: 0; }
.setup-steps li {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 0; border-bottom: 1px solid var(--border);
}
.setup-steps li:last-child { border-bottom: 0; padding-bottom: 0; }

.setup-tick {
  flex: 0 0 auto; width: 21px; height: 21px; border-radius: 50%;
  border: 2px solid var(--border);
  display: grid; place-items: center; color: #fff;
}
.setup-tick svg { width: 12px; height: 12px; }
.setup-steps li.done .setup-tick { background: var(--accent); border-color: var(--accent); }
.setup-steps li.next .setup-tick { border-color: var(--accent); }

.setup-body { flex: 1 1 auto; min-width: 0; }
.setup-body b { display: block; font-size: 0.95rem; }
.setup-body em {
  display: block; font-style: normal; font-size: 0.83rem;
  opacity: 0.72; margin-top: 2px; overflow-wrap: anywhere;
}
/* A finished step steps back rather than disappearing — the ticks are the
   point, and a list that only shows what is left never feels like progress. */
.setup-steps li.done .setup-body b { opacity: 0.6; font-weight: 500; }
.setup-steps li.done .setup-body em { opacity: 0.5; }

/* An unfinished step goes somewhere when it is clicked, so the instruction in
   it is not also a dead end. No new button: the next step keeps the only one. */
.setup-steps li.goes { cursor: pointer; }
.setup-steps li.goes:hover { background: rgba(0, 0, 0, 0.028); }
.setup-steps li.goes:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.setup-go { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 560px) {
  .setup-steps li { flex-wrap: wrap; }
  .setup-go { margin-left: 33px; }
}

/* ---------------------------------------------------------------------------
   Phone and tablet — the venue-facing surfaces (dashboard, login, claim).

   Everything below is additive and lives behind a max-width query: the desktop
   rules above are untouched, because on a laptop this all already worked. The
   phone case had simply never been looked at, and a venue owner at the demo
   holds a phone, not a laptop.

   Three classes of defect are fixed here, in this order of severity:

     1. Horizontal page scroll. Six of the twelve dashboard tabs pushed the
        BODY sideways — Departments to 595px inside a 390px viewport — because
        an eight-column <table> has a min-content width and nothing was
        containing it. body{overflow-x:hidden} hid the scrollbar but not the
        problem: the right-hand column of every table (the Edit/Delete buttons,
        the invite field) was simply unreachable.
     2. Overlapping controls. .topbar is a two-child flex row with
        justify-content:space-between and no wrap, so at 390px the tenant name
        and the Log out button printed straight over the wordmark.
     3. Touch targets. Buttons came out at 37-39px and native checkboxes at
        13px against the 44px minimum that both Apple's and Google's guidance
        ask for. A 13px checkbox is a real miss on a phone, not a nicety.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {

  /* --- 2. the top bar stops printing over itself --------------------------- */
  .topbar { flex-wrap: wrap; gap: 8px 14px; padding: 12px 16px; }
  .topbar .row { margin-left: auto; }
  .topbar .brand a { padding: 4px 0; }
  #tenant-name { overflow-wrap: anywhere; }

  .wrap { padding: 22px 16px 72px; }

  /* --- 1. wide content scrolls inside its own box ------------------------- */
  /* display:block turns the <table> itself into the scroll container, so no
     wrapper element has to be introduced around eighteen tables that
     dashboard.js addresses by id. The rows keep their table display types, so
     an anonymous table box still lays the columns out inside — it just now has
     somewhere to overflow to that is not the page. */
  table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* A cell may wrap its own text, but a cell holding controls must not break
     the control row up, and a cell holding a whole email must not force a
     500px column when it can hyphenate instead. */
  td .row, .setup-go, .badge { white-space: nowrap; }
  /* break-word, NOT anywhere: `anywhere` also shrinks the cell's min-content
     size, which is how the table came to fit inside 390px by breaking
     "Reception TV" into "Recept / ion TV" and the column headers into
     "SCREE / N". A cell breaks a word only when the word alone cannot fit, and
     the table scrolls instead. Legible beats narrow. */
  td, th { overflow-wrap: break-word; }

  /* The 14-day trend bars carry nowrap date labels, so the chart has a hard
     min-content width of ~509px. It scrolls rather than dragging the page.
     overflow-x alone is not enough: a one-column grid track is sized
     minmax(auto, 1fr), and that auto minimum is the item's min-content — which
     is the 509px chart. The track grew to 509px and took the page with it, so
     the grid item has to be told it may be narrower than its contents. */
  .grid-2 > * { min-width: 0; }
  /* No min-width on the bars: a bar-wrap's automatic minimum is already the
     min-content width of its own nowrap "Aug 26" label, which is the right
     answer. Rounding it down to a flat 30px only made the label spill 2px over
     its neighbour. */
  .trend-chart { overflow-x: auto; padding-bottom: 4px; }

  /* Same treatment for the other intrinsically-wide blocks. */
  .qr-sheet { justify-content: center; }
  pre, .sap-fields dl { overflow-x: auto; }

  /* --- 3. touch targets --------------------------------------------------- */
  button, .btn, .btn-hero, .link-btn,
  input, select, textarea { min-height: 44px; }
  button, .btn { padding: 11px 16px; }
  .tabs button { min-height: 44px; padding: 11px 14px; }
  .onboard-tabs button { min-height: 44px; }
  .link-btn { display: inline-block; padding: 11px 2px; }
  .onboard-disclosure summary, .source-category summary { min-height: 44px; }

  /* A native checkbox is 13px. Enlarging the box AND giving the whole label
     row the 44px makes the label itself the target, which is what a thumb
     actually hits. */
  input[type="checkbox"], input[type="radio"],
  .check-list input[type="checkbox"], .check-list input[type="radio"] {
    width: 20px; height: 20px; min-width: 20px; min-height: 20px; flex: 0 0 auto;
  }
  .check-list label, label:has(> input[type="checkbox"]) {
    min-height: 44px; display: flex; align-items: center; gap: 10px;
  }
  .check-list { max-height: 220px; }

  /* The wordmark is the way back to the front of the product, and a real
     download link is a real action — both were under 32px. */
  .topbar .brand a { min-height: 44px; }
  .card a[download] { display: inline-block; min-height: 44px; padding: 12px 0; }

  /* Deliberate exceptions: direct-manipulation handles drawn ON the scaled
     screen preview. Forcing 44px there would cover the layout they sit on. */
  .voice-preview-canvas button,
  .voice-preview-canvas input { min-height: 0; padding: 0; }
  .voice-block-remove { width: 22px; height: 22px; line-height: 20px; }
  .voice-block-title-input { padding: 2px 4px; }
  .sp-tape, .sp-zone { min-height: 0; }
}

/* Phone only. The two-up stat grid and the modal need a little more room back
   than the tablet does. */
@media (max-width: 560px) {
  .overview-stats { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
  .overview-stat { padding: 14px; }
  /* Two tiles across a 390px phone leaves ~140px of content width, and the
     desktop 2rem value plus overflow-wrap:anywhere broke "Reception TV" mid
     word. Smaller type fits the whole word in the column. */
  .overview-stat .value { font-size: 1.35rem; }
  .modal-overlay { padding: 12px; }
  .modal-card { padding: 20px; }
  .modal-actions { flex-wrap: wrap; }
  .modal-actions .btn-hero { flex: 1 1 auto; }
  /* The tick and the step text belong on one line; only the button below it
     needs its own row. flex-basis:auto made the text block claim its whole
     max-content width and pushed itself onto a second line under the tick. */
  .setup-steps li .setup-body { flex: 1 1 160px; }
  /* Once .setup-head wraps, the count is no longer opposite the heading — it is
     a shrink-to-fit block sitting at the left margin, and text-align:right just
     shunted the "3" to the right-hand edge of its own narrow box, leaving it
     floating in from the left of everything else. Align it with the title. */
  .setup-count { text-align: left; }
}

/* Google sign-in, when the server has no credentials for it.
   The flag is set on <html> by the server (see the HTML middleware in
   server/index.js). Hiding the divider as well as the button matters: the
   divider exists only to separate the form from that button, so leaving it
   behind is a stray "or" under a form with nothing on the other side of it.
   Every .auth-divider in the product sits immediately before a .google-btn. */
html[data-no-google-auth] .google-btn,
html[data-no-google-auth] .auth-divider,
html[data-no-google-auth] #signup-google-promo-note { display: none !important; }

/* ---- Earned this week ---------------------------------------------------
   The first thing on the overview, because it is what the person paying for
   this opens the page to see. The fleet numbers underneath it are for whoever
   installs the screens.

   Hidden by [hidden] until real money has settled — see paintEarnings. */

.earn {
  background: linear-gradient(135deg, var(--panel, #141c20) 0%, rgba(79, 140, 255, 0.10) 100%);
  border: 1px solid var(--border, #263237);
  border-radius: 14px;
  padding: 22px 24px;
  margin-bottom: 18px;
}

.earn-top { display: flex; align-items: center; justify-content: space-between; gap: 24px; }

.earn-label {
  margin: 0 0 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-dim, #9db0b7);
}

/* Tabular figures so the number does not jitter as it changes. */
.earn-figure {
  margin: 0;
  font-size: clamp(2rem, 5vw, 2.9rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--text, #e8edf4);
}

.earn-sub { margin: 8px 0 0; font-size: 0.9rem; color: var(--text-dim, #9db0b7); }
.earn-sub.up { color: #4ac48a; }
.earn-sub.down { color: #e0a44c; }

.earn-foot {
  margin: 16px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border, #263237);
  font-size: 0.85rem;
  color: var(--text-dim, #9db0b7);
  font-variant-numeric: tabular-nums;
}

.earn-spark { width: 240px; height: 56px; flex: 0 0 auto; }
.earn-spark-fill { fill: rgba(79, 140, 255, 0.16); stroke: none; }
.earn-spark-line { fill: none; stroke: #4f8cff; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.earn-spark-dot { fill: #4f8cff; }

/* The sparkline is the first thing to go: the figure is the message, the
   shape is the supporting detail, and on a phone there is room for one. */
@media (max-width: 620px) {
  .earn-spark { display: none; }
  .earn-top { gap: 0; }
}

/* ===========================================================================
   Navigation — four destinations, not twelve tabs
   ===========================================================================
   This was a flat row of twelve underlined tabs that wrapped onto two lines.
   It read as a filing cabinet: every part of the system exposed at the same
   weight, and a hotel manager made to read all of it to find one thing.

   Primary is now a segmented control of four jobs. Secondary appears only
   inside a group that has more than one thing in it. Settings is a gear at the
   far end, because nobody's job is "settings". */


@media (max-width: 640px) {
}

@media (prefers-reduced-motion: reduce) {
  .side button { transition: none; }
}

/* A guest's photograph on a request card. Big enough to judge from at a glance,
   and it opens full size because on a maintenance ticket the detail is the
   whole point. */
.req-photo { display: inline-block; margin: 2px 0 10px; line-height: 0; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
.req-photo img { width: 132px; height: 132px; object-fit: cover; display: block; transition: transform 0.2s ease; }
.req-photo:hover img { transform: scale(1.04); }
.req-photo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .req-photo img { transition: none; } .req-photo:hover img { transform: none; } }

/* What a guest booked, on the request card. Set apart from the message because
   it is the actionable line: staff read this to know what to prepare. */
.req-booking {
  margin: 2px 0 10px;
  padding: 9px 12px;
  border-left: 3px solid var(--accent);
  background: var(--panel-2);
  border-radius: 0 8px 8px 0;
  font-size: 0.95rem;
}

/* ===========================================================================
   The shell — a sidebar, because navigation should never wrap
   ===========================================================================
   A horizontal row runs out of room and then folds onto a second line, which is
   what made twelve tabs look like a filing cabinet. Vertical has as much room
   as it needs and stays in the same place while the page underneath changes. */

@media (min-width: 900px) {
  .wrap {
    display: grid;
    grid-template-columns: 216px minmax(0, 1fr);
    column-gap: 34px;
    align-items: start;
    max-width: 1240px;
  }
  /* The sidebar spans every row rather than sitting in the first one.
     With `grid-area: nav` it occupied row 1, and row 1 is as tall as its
     tallest cell — so the first thing in the main column was pushed down by
     the whole height of the sidebar, leaving about two hundred pixels of
     nothing under the sub-navigation on every single tab. */
  .wrap > .side { grid-column: 1; grid-row: 1 / span 200; position: sticky; top: 22px; align-self: start; }
  /* Everything that is not the nav flows into the main column. */
  .wrap > *:not(.side) { grid-column: 2; }

}


/* ---- The home screen ---------------------------------------------------- */

.hero { padding: 26px 0 30px; text-align: center; }

.hero-greet {
  margin: 0;
  font-size: clamp(1.7rem, 4vw, 2.4rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-wrap: balance;
}
.hero-sub { margin: 8px 0 0; color: var(--text-dim); font-size: 1.02rem; }

.askbar {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 620px;
  margin: 26px auto 0;
  padding: 7px 7px 7px 20px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.askbar:focus-within { border-color: var(--accent); box-shadow: 0 0 0 4px rgba(79, 140, 255, 0.12); }

.askbar input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 1.02rem;
  padding: 10px 0;
  outline: none;
}
.askbar input::placeholder { color: var(--text-dim); }

.askbar button {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink, #fff);
  cursor: pointer;
  transition: opacity 0.16s ease;
}
.askbar button svg { width: 18px; height: 18px; }
.askbar button:disabled { opacity: 0.45; cursor: default; }

/* What needs a person. Count first — the number is the reason to press it. */
.chips { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 20px 0 0; }
.chips:empty { display: none; }

.chip {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 16px 9px 13px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit; font-size: 0.93rem; font-weight: 600;
  cursor: pointer;
  transition: border-color 0.16s ease, transform 0.16s ease;
}
.chip:hover { border-color: var(--accent); }
.chip:active { transform: scale(0.98); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip b {
  min-width: 24px; padding: 2px 7px;
  border-radius: 999px;
  background: var(--accent); color: var(--accent-ink, #fff);
  font-variant-numeric: tabular-nums; font-size: 0.85rem;
}
.chip.calm b { background: var(--panel); color: var(--text-dim); }

@media (prefers-reduced-motion: reduce) {
  .askbar, .chip { transition: none; }
  .chip:active { transform: none; }
}

/* Present for a screen reader, absent for everyone else.
   dashboard.html marks up labels with this class and the stylesheet had no such
   rule, so every "hidden" label rendered as visible text — the ask box shipped
   with its own label sitting inside it, shoving the input sideways.
   The clip-path/1px pattern rather than display:none, which removes an element
   from the accessibility tree as well as the page. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- First-run notes ----------------------------------------------------
   A small card pointing at the thing it describes. Shown once per browser.
   Deliberately not a modal: nothing behind it is blocked, Escape closes it, and
   the page stays usable while it is open. A tour that traps you is a tour
   people learn to dismiss without reading. */

.coach {
  position: absolute;
  z-index: 300;
  width: min(320px, calc(100vw - 20px));
  padding: 16px 18px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.18);
  animation: coach-in 0.22s ease-out;
}
@keyframes coach-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.coach b { display: block; font-size: 1rem; margin-bottom: 6px; }
.coach p { margin: 0; font-size: 0.92rem; line-height: 1.5; color: var(--text-dim); }

.coach-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.coach-step { font-size: 0.78rem; color: var(--text-dim); font-variant-numeric: tabular-nums; margin-right: auto; }

.coach-skip {
  border: 0; background: none; color: var(--text-dim);
  font: inherit; font-size: 0.88rem; cursor: pointer; padding: 6px 4px;
  text-decoration: underline; text-underline-offset: 3px;
}
.coach-skip:hover { color: var(--text); }

.coach-next {
  border: 0; border-radius: 8px;
  background: var(--accent); color: var(--accent-ink, #fff);
  font: inherit; font-size: 0.88rem; font-weight: 650;
  padding: 8px 16px; cursor: pointer;
}
.coach-next:focus-visible, .coach-skip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The thing being described, ringed for as long as its note is open. Outline
   rather than a full-page dimming overlay: it says "this one" without taking
   the page away.

   The ring is a plain outline in the theme accent, not a hardcoded blue — this
   product ships more than one palette and a ring in the wrong colour reads as a
   rendering fault. The pulse on top of it is decoration; the outline is the
   part that has to survive, which is why removal is done in JS on moving to the
   next note rather than by listening for the end of an animation that
   prefers-reduced-motion never starts. */
.coach-lit {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 12px;
  animation: coach-lit 1.4s ease-out;
}
@keyframes coach-lit {
  0%   { box-shadow: 0 0 0 0 var(--accent); }
  100% { box-shadow: 0 0 0 12px transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .coach { animation: none; }
  .coach-lit { animation: none; }
}

/* ---- Search -------------------------------------------------------------
   One box that reaches everything: a place to go, a screen, something you
   sell, a location, a guest who is waiting. Opened with the button in the
   header or with Command-K / Ctrl-K.

   The header button exists because a keyboard shortcut alone is not a feature
   for the people who run a front desk. The shortcut is shown on it so the ones
   who do use shortcuts learn it without being taught. */

.topsearch {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 10px 7px 11px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--panel-2); color: var(--text-dim);
  font: inherit; font-size: 0.88rem; cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease;
}
.topsearch:hover { border-color: var(--accent); color: var(--text); }
.topsearch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.topsearch svg { width: 15px; height: 15px; }
.topsearch kbd {
  font: inherit; font-size: 0.74rem;
  padding: 1px 5px; border-radius: 5px;
  border: 1px solid var(--border); background: var(--panel);
  color: var(--text-dim);
}
@media (max-width: 720px) { .topsearch span, .topsearch kbd { display: none; } }

.palette-back {
  position: fixed; inset: 0; z-index: 400;
  background: rgba(0, 0, 0, 0.34);
  display: flex; justify-content: center;
  padding: max(9vh, 40px) 16px 16px;
}
.palette {
  width: min(620px, 100%);
  max-height: min(60vh, 520px);
  display: flex; flex-direction: column;
  background: var(--panel-2);
  border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.32);
  overflow: hidden;
  animation: palette-in 0.14s ease-out;
}
@keyframes palette-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}

.palette-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-bottom: 1px solid var(--border);
  color: var(--text-dim);
}
.palette-head svg { width: 17px; height: 17px; flex: none; }
#palette-input {
  flex: 1 1 auto; min-width: 0;
  border: 0; background: none; color: var(--text);
  font: inherit; font-size: 1.02rem; padding: 2px 0;
}
#palette-input:focus { outline: none; }
.palette-close {
  flex: none; border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--text-dim);
  font: inherit; font-size: 0.74rem; padding: 3px 7px; cursor: pointer;
}

#palette-list { overflow-y: auto; padding: 6px; }
.palette-group {
  padding: 10px 12px 4px; font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim);
}
.palette-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border: 0; border-radius: 10px;
  background: none; color: var(--text);
  font: inherit; font-size: 0.95rem; text-align: left; cursor: pointer;
}
/* Highlighted by class rather than :hover so the keyboard and the mouse cannot
   disagree about which row Enter would open. */
.palette-row.on { background: var(--accent); color: var(--accent-ink, #fff); }
.palette-row .p-sub { color: var(--text-dim); font-size: 0.85rem; margin-left: auto; padding-left: 12px; }
.palette-row.on .p-sub { color: inherit; opacity: 0.85; }
.palette-row .p-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-dim); opacity: 0.5;
}
.palette-row.on .p-dot { background: currentColor; opacity: 0.9; }
.palette-row .p-dot.live { background: #2fa84f; opacity: 1; }
.palette-row .p-dot.dark { background: #d0342c; opacity: 1; }

.palette-empty { padding: 22px 14px; color: var(--text-dim); font-size: 0.92rem; text-align: center; }

.palette-foot {
  display: flex; gap: 16px; padding: 9px 16px;
  border-top: 1px solid var(--border);
  font-size: 0.76rem; color: var(--text-dim);
}
.palette-foot kbd {
  font: inherit; font-size: 0.72rem; padding: 0 4px; margin-right: 3px;
  border: 1px solid var(--border); border-radius: 4px; background: var(--panel);
}

@media (prefers-reduced-motion: reduce) { .palette { animation: none; } }

/* ---- The library, and the card that adds to it --------------------------
   One button on the page, and one question at a time behind it. The form this
   replaces was nine screens long and sat open above the grid. */

.lib-head {
  display: flex; align-items: flex-start; gap: 16px;
  margin-bottom: 18px;
}
.lib-head h3 { margin: 0; }
.lib-head p { margin: 4px 0 0; }
.big-add {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 20px; border: 0; border-radius: 10px;
  background: var(--accent); color: var(--accent-ink, #fff);
  font: inherit; font-size: 0.98rem; font-weight: 650;
  cursor: pointer; white-space: nowrap;
}
.big-add span { font-size: 1.15rem; line-height: 1; }
.big-add:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (max-width: 620px) {
  .lib-head { flex-direction: column; }
  .big-add { margin-left: 0; width: 100%; justify-content: center; }
}

.wiz-back {
  position: fixed; inset: 0; z-index: 500;
  background: rgba(0, 0, 0, 0.42);
  display: flex; align-items: flex-start; justify-content: center;
  padding: max(6vh, 24px) 16px 24px;
  overflow-y: auto;
}
/* The page behind must not scroll while a card is open over it, or a phone
   scrolls the page instead of the card and the buttons walk off the screen. */
body.modal-open { overflow: hidden; }

.wiz {
  width: min(560px, 100%);
  display: flex; flex-direction: column;
  background: var(--panel-2);
  border: 1px solid var(--border); border-radius: 18px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.34);
  overflow: hidden;
  animation: wiz-in 0.16s ease-out;
}
@keyframes wiz-in {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

.wiz-head {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 18px 14px;
}
.wiz-head b { font-size: 1.02rem; }
.wiz-x {
  margin-left: auto; border: 0; background: none; color: var(--text-dim);
  font: inherit; font-size: 1.5rem; line-height: 1; cursor: pointer;
  padding: 0 4px; border-radius: 6px;
}
.wiz-x:hover { color: var(--text); }
.wiz-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.wiz-bar { height: 3px; background: var(--border); }
.wiz-bar span {
  display: block; height: 100%; width: 25%;
  background: var(--accent);
  transition: width 0.2s ease;
}

.wiz-body { padding: 22px 22px 6px; }
.wiz-step h4 { margin: 0 0 4px; font-size: 1.24rem; letter-spacing: -0.01em; }
.wiz-step > p.muted { margin: 0 0 18px; font-size: 0.93rem; }
.wiz-step label { margin-top: 14px; }
.wiz-step > label:first-of-type { margin-top: 0; }

.wiz-more { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 12px; }
.wiz-more summary {
  cursor: pointer; color: var(--text-dim); font-size: 0.88rem;
  list-style: none; padding: 2px 0;
}
.wiz-more summary::-webkit-details-marker { display: none; }
.wiz-more summary::before { content: '+ '; font-weight: 700; }
.wiz-more[open] summary::before { content: '\2212 '; }
.wiz-more summary:hover { color: var(--text); }
.wiz-more > *:not(summary) { margin-top: 10px; }

.wiz-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 22px 20px;
}
.wiz-count { color: var(--text-dim); font-size: 0.82rem; margin-right: auto; }
/* Going back and giving up both sit on the left, before the counter, so the
   counter's auto margin pushes the one action that commits anything to the
   right edge where the eye already is. Cancel used to sit beside Save. */
#wiz-back-btn { order: -2; }
#element-cancel-edit-btn { order: -1; }
.wiz-foot button[type="submit"], #wiz-next-btn { min-width: 108px; }

#wiz .dropzone { min-height: 190px; }

@media (prefers-reduced-motion: reduce) {
  .wiz { animation: none; }
  .wiz-bar span { transition: none; }
}

/* The hidden attribute must win.
   Several rules in this file give a button, a section or a div an explicit
   `display`, and any of those beats the user-agent's `[hidden]{display:none}`
   on specificity — so `el.hidden = true` left the element on screen. It showed
   up as Back and "Add it to my library" both sitting on step one of a
   four-step card. Declared once here rather than remembered at every call
   site. */
[hidden] { display: none !important; }

/* A tab's settings, folded away.
   The Media tab opened on a logo uploader, a font picker and a nine-screen
   form, with the library the page is actually about below the fold. */
.adv {
  margin-top: 26px;
  border-top: 1px solid var(--border);
  padding-top: 6px;
}
.adv > summary {
  cursor: pointer; list-style: none;
  padding: 12px 2px; font-weight: 650; color: var(--text-dim);
  border-radius: 8px;
}
.adv > summary::-webkit-details-marker { display: none; }
.adv > summary::before { content: '\25B8 '; display: inline-block; transition: transform 0.15s ease; }
.adv[open] > summary::before { transform: rotate(90deg); }
.adv > summary:hover { color: var(--text); }
.adv > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .adv > summary::before { transition: none; } }

/* ---- The rail -----------------------------------------------------------
   One permanent sidebar listing every page, grouped under small headings.
   It replaced four grouped buttons above a sub-navigation bar that repainted
   underneath them — a shape in which the only way to learn what the product
   contained was to click each group and read what appeared.

   Dark on purpose. It separates "where am I" from "what am I doing" without a
   border doing the work, and it is what both of the admins this was measured
   against (Shopify, Paystack) settle on. */

.side {
  display: flex; flex-direction: column; gap: 2px;
  padding: 14px 10px 12px;
  background: #16181d;
  border-radius: 16px;
  color: #a8aeba;
}
.side-who {
  padding: 6px 12px 16px;
  margin-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
}
.side-who b { display: block; color: #fff; font-size: 0.98rem; letter-spacing: -0.01em; }
.side-who span { display: block; margin-top: 2px; font-size: 0.8rem; color: #767d8c; }

.side-group { display: flex; flex-direction: column; gap: 1px; }
.side-group + .side-group { margin-top: 16px; }
/* Account sits at the bottom, away from the daily work, the way settings does
   in every admin worth copying. */
.side-foot { margin-top: auto; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.09); }

.side-heading {
  padding: 0 12px 7px;
  font-size: 0.69rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: #5f6675;
}

/* Buttons AND links. Two entries in this rail are pages of their own rather
   than panels — the Studio and Approvals — and a page has to be a real anchor
   so it middle-clicks and opens in a new tab. Styled here only as `button`,
   those two rendered as raw underlined links with an unconstrained 18px icon
   ballooning to fill the rail. Every selector below therefore names both. */
.side button, .side a {
  display: flex; align-items: center; gap: 11px;
  width: 100%; padding: 9px 12px;
  border: 0; border-radius: 9px;
  background: none; color: #a8aeba;
  font: inherit; font-size: 0.92rem; font-weight: 500;
  text-align: left; cursor: pointer;
  text-decoration: none;
  box-sizing: border-box;
  transition: background 0.13s ease, color 0.13s ease;
}
.side button svg, .side a svg { width: 18px; height: 18px; flex: none; opacity: 0.72; }
.side button:hover, .side a:hover { background: rgba(255, 255, 255, 0.06); color: #fff; }
.side button:hover svg, .side a:hover svg { opacity: 1; }
.side button.active, .side a.active {
  background: var(--accent); color: var(--accent-ink, #1b1200);
  font-weight: 650;
}
.side button.active svg, .side a.active svg { opacity: 1; }
.side button:focus-visible, .side a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Under 900px the wrap is not a grid, so the rail becomes a scrolling row of
   the same buttons rather than a column that eats the whole first screen. */
@media (max-width: 899px) {
  .side {
    flex-direction: row; align-items: center; gap: 4px;
    overflow-x: auto; scrollbar-width: none;
    padding: 8px; margin-bottom: 18px;
  }
  .side::-webkit-scrollbar { display: none; }
  .side-who { display: none; }
  .side-group { flex-direction: row; gap: 4px; }
  .side-group + .side-group { margin-top: 0; margin-left: 10px; padding-left: 10px; border-left: 1px solid rgba(255, 255, 255, 0.12); }
  .side-foot { margin-top: 0; padding-top: 0; border-top: 0; }
  .side-heading { display: none; }
  .side button { width: auto; white-space: nowrap; padding: 9px 13px; }
}

@media (prefers-reduced-motion: reduce) { .side button { transition: none; } }

/* The venue's name lives in the rail. The header copy is for narrow screens,
   where the rail is a row of icons with no room for it. */
@media (min-width: 900px) { #tenant-name { display: none; } }

/* The rail's own colours, restated at a specificity that survives the global
   `html[data-ui="light"] button { color: var(--accent-ink) }` rule — which is
   (0,1,2), exactly the same as `.side button` plus its ancestor, and was
   winning on source order. It painted every unselected item in the rail
   accent-brown on near-black. */
html .side button { color: #a8aeba; }
html .side button:hover { color: #fff; }
html .side button.active { color: var(--accent-ink, #1b1200); }
html .side-who b { color: #fff; }
html .side-who span { color: #767d8c; }
html .side-heading { color: #5f6675; }


/* Working inside another account. Deliberately unmissable — a subtle badge is
   how somebody edits a demo library believing it is their own. */
.acting-bar{position:fixed;left:0;right:0;top:0;z-index:200;display:flex;
  align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
  padding:9px 16px;background:#b3261e;color:#fff;font-size:14px}
.acting-bar b{font-weight:600}
.acting-bar button{font:inherit;font-size:13.5px;font-weight:500;padding:5px 12px;
  border:1px solid rgba(255,255,255,.5);border-radius:8px;background:transparent;
  color:#fff;cursor:pointer}
.acting-bar button:hover{background:rgba(255,255,255,.14)}
body.is-acting{padding-top:40px}
