/* ============================================================
   MyIceland Admin — light, warm, minimal design system.
   Sibling to the dark customer portal. Loaded only on /admin
   pages, so class names are unprefixed; no risk of collision
   with the customer portal's stylesheet.
   ============================================================ */

/* Schibsted Grotesk — primary admin typeface. JetBrains Mono
   for monospace details (refs, counters). Imported once in the
   <head> via <link>, no @import here. */

:root {
  --bg:           #ffffff;
  --bg-2:         #fbfaf7;
  --surface:      #ffffff;
  --surface-hi:   #f6f4ef;
  --line:         #eeece6;
  --line-strong:  #e1ddd2;
  --text:         #131211;
  --text-2:       #5e5a53;
  --text-3:       #9a958c;
  --accent:       #d68a1a;
  --accent-2:     #e8a33b;
  --accent-soft:  rgba(214, 138, 26, 0.07);
  --accent-line:  rgba(214, 138, 26, 0.25);
  --pending:      #c89020;
  --pending-soft: rgba(200, 144, 32, 0.10);
  --danger:       #a93b1f;
  --danger-soft:  rgba(169, 59, 31, 0.05);
  --danger-line:  rgba(169, 59, 31, 0.18);
  --ok:           #4a7e54;

  /* Customer-portal preview palette — stays dark regardless of admin theme */
  --portal-bg:        #0d0d0e;
  --portal-card:      #181818;
  --portal-card-line: #262626;
  --portal-text:      #ffffff;
  --portal-text-2:    rgba(255,255,255,0.5);
  --portal-text-3:    rgba(255,255,255,0.35);
  --portal-input:     #0d0d0e;
  --portal-accent:    #e8a33b;

  --shadow-soft: 0 1px 2px rgba(20,16,8,0.04);
  --shadow-card: 0 1px 1px rgba(20,16,8,0.02), 0 12px 36px -20px rgba(60,40,10,0.10);
  --shadow-pop:  0 24px 64px -28px rgba(60,40,10,0.18);
}

/* ============================================================
   RESET & BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: 'Schibsted Grotesk', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button { font-family: inherit; cursor: pointer; }
input, button, select, textarea { font-family: inherit; }
a { color: inherit; }
.mono { font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace; font-feature-settings: 'ss02'; }

.page { min-height: 100vh; padding-bottom: 120px; }

/* ============================================================
   TOP CHROME — almost invisible
   ============================================================ */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: 60px;
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 28px;
  background: color-mix(in oklab, var(--bg), transparent 25%);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
/* Brand: the MyIceland Split mark + the operator's company name in the
 * logo's lettering, laid out like the logo lockup (mark 29 : capitals 14.5,
 * gap 12). The logo's word is Montserrat SemiBold caps tracked 0.058em
 * (matched against the outlined SVG), cap height 0.703em, so 17px type gives
 * 12px capitals next to the 24px mark, 10px apart. */
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
/* The Split SVGs carry 2 units of room round the artwork so the colour
 * isn't cut off (logo-split README): 29 tall artwork in a 33 tall file.
 * Scaled to a 24px mark (x 24/29) that is 27.31px with a -1.655px margin,
 * so the mark occupies exactly the plain mark's 40x24 box. */
.brand-mark {
  width: auto;
  height: 27.31px;
  margin: -1.655px;
  display: block;
  flex-shrink: 0;
}
.brand-name {
  font-family: 'Montserrat', 'Schibsted Grotesk', system-ui, sans-serif;
  font-weight: 600;
  font-size: 17px;
  line-height: 1;
  letter-spacing: 0.058em;
  text-transform: uppercase;
  color: #161513;   /* logo ink */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.topbar-spacer { flex: 1; }

/* .kbd-hint removed in feat/picker-revert-portal-polish Fix 5 — the
 * ⌘K Search pill it styled was deleted from the topbar template. */

/* Topbar menu — a plain hamburger (right) opening a dropdown with the
   MyIceland logo above Terms / DPA / Log out. Replaces the old avatar
   button (and before it, an avatar that logged out on a single click). */
.user-menu { position: relative; display: inline-flex; }
.menu-btn {
  width: 36px; height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: #161513;
  cursor: pointer;
}
.menu-btn:hover,
.menu-btn[aria-expanded="true"] { background: var(--surface-hi); }
.menu-btn svg { display: block; }
.user-menu-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 232px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: 0 14px 38px rgba(0, 0, 0, 0.14);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 40;
}
.user-menu-dropdown[hidden] { display: none; }
/* The dropdown lives in the topbar's stacking context (z 30), under the
   testing account's sticky banner (z 40), which hid the menu's top row.
   Lift the topbar above it only while the menu is open. */
.topbar:has(.user-menu-dropdown:not([hidden])) { z-index: 45; }
/* The MyIceland Split logo (mark + word) heading the menu, separated from
   the options by a hairline. Artwork 22px tall: the file's 2 units of room
   round the art scale to 25.03px with a -1.517px margin (x 22/29). */
.user-menu-brand {
  padding: 12px 12px 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}
.user-menu-brand img {
  display: block;
  height: 25.03px;
  width: auto;
  margin: -1.517px;
}
.user-menu-logout-form { margin: 0; display: block; }
.user-menu-item {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  padding: 9px 12px;
  border: 0;
  background: transparent;
  border-radius: 8px;
  color: var(--text-2);
  font: inherit;
  font-size: 13.5px;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}
.user-menu-item:hover { background: var(--surface-hi); color: var(--text); }
.user-menu-divider { border: 0; border-top: 1px solid var(--line); margin: 4px 2px; }
.user-menu-item--logout { color: var(--danger); }
.user-menu-item--logout:hover { background: var(--danger-soft); color: var(--danger); }

/* ============================================================
   TAB STRIP
   ============================================================
   The dashboard greeting/date hero was removed (feature/admin-visual-
   polish FIX 2) so the tab strip is the top element under the topbar.
   Spacing is weighted upward (FIX 3): a calm gap above the strip + more
   padding above each tab label than below, so the labels sit closer to
   the separator line than to the topbar. */
.tabs {
  max-width: 1200px;
  /* Tightened above the tab row (was 32px) so it sits closer to the
     topbar. The tab buttons carry their own 16px internal top padding
     (weighted upward, see .tab), so 16px here keeps the label from
     crowding the topbar border while reading clearly tighter. */
  margin: 16px auto 0;
  padding: 0 28px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: stretch;
  gap: 4px;
  overflow-x: auto;
  /* Without an explicit overflow-y, the CSS spec forces it to `auto` when
     overflow-x is `auto` — which made the tab strip vertically scrollable,
     so it could be dragged/pulled down (rubber-band). Pin it to hidden and
     contain overscroll; horizontal scroll for the tab row is preserved. */
  overflow-y: hidden;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Weighted upward (FIX 3): more above the label than below, so the
     label reads as closer to the separator line than to the topbar. */
  padding: 16px 18px 11px;
  background: transparent;
  border: 0;
  color: var(--text-3);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  transition: color 160ms ease;
  border-radius: 8px 8px 0 0;
}
.tab:hover { color: var(--text-2); }
.tab[aria-selected="true"] { color: var(--text); }
.tab[aria-selected="true"]::after {
  content: '';
  position: absolute;
  left: 18px; right: 18px;
  bottom: -1px;
  height: 1.5px;
  background: var(--text);
  border-radius: 2px;
}
.tab-icon { width: 17px; height: 17px; }

/* "Send login link" — an action that lives WITH the config tabs (left), just
   spaced a little further via a small dot so it reads as set-apart-but-part-of
   the cluster (feat/login-links-tool). Styled like a .tab (text + icon, same
   padding/colour) and behaves like one: its chain EclipseIcon is grey at rest
   and lights up to the colour version on hover. It's an <a> with no data-tab,
   so the client tab-switcher never binds it. */
.tab-tool-sep {
  align-self: center;
  flex: 0 0 auto;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--text-3);
  margin: 0 9px;          /* a touch more than the 4px inter-tab gap, so it reads as separated */
}
.tab-tool {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 16px 18px 11px;   /* match .tab so it sits on the same baseline */
  background: transparent;
  color: var(--text-3);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
  transition: color 160ms ease;
}
.tab-tool:hover { color: var(--text-2); }
/* chain glyph: grey at rest, full eclipse colour on hover (like the tabs go
   colour on select). Default greys the core + hides the glow; hover restores
   LINK_CORE + the glow. */
.tab-tool .ec-glow { opacity: 0; transition: opacity .16s ease; }
.tab-tool .ec-core { background-image: none; background-color: #a8a8a0; transition: background-color .16s ease; }
.tab-tool:hover .ec-glow { opacity: .85; }
.tab-tool:hover .ec-core { background-color: transparent; background-image: linear-gradient(45deg, #22e39a 0%, #16b677 32%, #1b1d25 52%, #7b40d4 72%, #a64ff5 100%); }

.tab-panel[hidden] { display: none; }

/* ============================================================
   MAIN CONTENT — split layout
   ============================================================ */
.main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 40px 28px 0;
}
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 400px;
  gap: 56px;
  align-items: start;
}
@media (max-width: 1080px) {
  .split { grid-template-columns: 1fr; gap: 40px; }
  .preview-col { position: static !important; }
}
.settings-col { display: flex; flex-direction: column; gap: 0; }

.section {
  padding: 36px 0;
  border-bottom: 1px solid var(--line);
}
.section:first-child { padding-top: 8px; }
.section:last-of-type { border-bottom: 0; }

.section-head { margin-bottom: 18px; }
.section-title {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 6px;
}
.section-desc {
  font-size: 13.5px;
  color: var(--text-2);
  margin: 0;
  max-width: 56ch;
  line-height: 1.55;
}

/* ============================================================
   IMAGE UPLOAD ROW (shared layout — used by Logo upload, will be reused
   by per-tour photos in Session B). Two-column grid: thumbnail left,
   description + action buttons right. Stacks on narrow viewports.
   ============================================================ */
.cover-row { display: grid; grid-template-columns: 240px 1fr; gap: 24px; align-items: center; }
@media (max-width: 600px) { .cover-row { grid-template-columns: 1fr; } }
.cover-thumb-label {
  position: absolute;
  left: 14px; bottom: 12px;
  color: rgba(255,255,255,0.85);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  z-index: 2;
}
.cover-actions { display: flex; flex-direction: column; gap: 6px; }
.cover-actions p { margin: 0 0 12px; color: var(--text-2); font-size: 13.5px; max-width: 40ch; line-height: 1.55; }
.cover-buttons { display: flex; gap: 8px; flex-wrap: wrap; }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 10px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  transition: all 160ms ease;
  text-decoration: none;
}
.btn:hover { background: var(--surface-hi); }
.btn-block { display: flex; width: 100%; justify-content: center; }
.btn-ghost { border-color: transparent; background: transparent; color: var(--text-2); padding-left: 10px; padding-right: 10px; }
.btn-ghost:hover { color: var(--text); background: var(--surface-hi); }
.btn-primary {
  /* Recipe B bloom (feat/admin-aurora-symbols): domed sticker, two green + one
     purple on dark navy, with a two-tone coloured drop shadow. No theme tint.
     Shadow kept tight (low offset + negative spread) so it never bleeds past a
     snug container like the save-bar pill, even on hover. */
  background-color: #0a1024;
  background-image: radial-gradient(48% 150% at 100% 112%, rgba(131,70,191,.92) 0%, rgba(114,67,171,.5) 40%, transparent 72%), radial-gradient(60% 150% at 18% 116%, rgba(50,186,154,.95) 0%, rgba(24,116,92,.5) 38%, transparent 66%), radial-gradient(46% 80% at 60% -16%, rgba(104,166,182,.55) 0%, transparent 56%), linear-gradient(135deg, #0d1730 0%, #0a1024 100%);
  background-size: cover;
  background-position: center;
  border: 0;
  border-radius: 14px;
  color: #fff;
  box-shadow: 2px 3px 7px -5px rgba(131,70,191,.6), -2px 3px 7px -6px rgba(43,169,140,.55), 0 1px 3px -2px rgba(9,13,30,.4);
}
.btn-primary:hover {
  filter: brightness(1.06);
  background-color: #0a1024;
  background-image: radial-gradient(48% 150% at 100% 112%, rgba(131,70,191,.92) 0%, rgba(114,67,171,.5) 40%, transparent 72%), radial-gradient(60% 150% at 18% 116%, rgba(50,186,154,.95) 0%, rgba(24,116,92,.5) 38%, transparent 66%), radial-gradient(46% 80% at 60% -16%, rgba(104,166,182,.55) 0%, transparent 56%), linear-gradient(135deg, #0d1730 0%, #0a1024 100%);
  box-shadow: 2px 4px 9px -5px rgba(131,70,191,.66), -2px 4px 9px -6px rgba(43,169,140,.6), 0 2px 5px -3px rgba(6,9,20,.5);
}
/* In the save-bar pill, round the button fully to match the pill; the tightened
   shadow above already stays within the bar's padding, so nothing bleeds out. */
.btn-primary:active { transform: translateY(1px) scale(.99); }
@media (prefers-reduced-motion: reduce) { .btn-primary:active { transform: none; } }
.btn-danger {
  color: var(--danger);
  border-color: var(--danger-line);
  background: transparent;
}
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }

/* ============================================================
   COLOR THEME — pairs of two shades
   ============================================================ */
.pairs {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 14px;
  max-width: 560px;
}
@media (max-width: 600px) { .pairs { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.pair {
  border: 0;
  background: transparent;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: pointer;
  text-align: left;
}
.pair-block {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 14px;
  overflow: hidden;
  display: grid;
  grid-template-rows: 1fr 1fr;
  border: 1.5px solid transparent;
  transition: transform 180ms ease, border-color 180ms ease;
  position: relative;
}
.pair-block .top { background: var(--c1); }
.pair-block .bot { background: var(--c2); }
.pair-block::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06);
  pointer-events: none;
}
.pair:hover .pair-block { transform: translateY(-2px); }
.pair[aria-selected="true"] .pair-block {
  /* Selection ring uses operator's chosen accent (Fix 3B). Falls back
   * to admin --text on non-Branding pages where --operator-accent
   * isn't set. */
  border-color: var(--operator-accent, var(--text));
  box-shadow: 0 0 0 4px var(--bg) inset, 0 0 0 1.5px var(--operator-accent, var(--text));
}
.pair[aria-selected="true"] .pair-name { color: var(--text); font-weight: 600; }
.pair-name {
  font-size: 12.5px;
  color: var(--text-3);
  text-align: center;
  transition: color 160ms ease;
}

/* ============================================================
   SEGMENTED CONTROL
   ============================================================ */
.segmented {
  display: inline-flex;
  padding: 4px;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 12px;
  gap: 2px;
}
.segmented button {
  border: 0;
  background: transparent;
  color: var(--text-2);
  padding: 7px 16px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 9px;
  transition: all 160ms ease;
}
.segmented button:hover { color: var(--text); }
.segmented button[aria-selected="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}

/* ============================================================
   INPUTS
   ============================================================ */
.input {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
  font-size: 14px;
  transition: border-color 160ms ease, box-shadow 160ms ease;
  font-family: inherit;
}
.input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.input::placeholder { color: var(--text-3); }
.field-group { display: flex; flex-direction: column; gap: 8px; max-width: 480px; }
.field-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  display: flex; justify-content: space-between; align-items: baseline;
}
.field-counter { font-size: 11px; color: var(--text-3); font-family: 'JetBrains Mono', monospace; }
/* Polish Fix 6: counter sits as a small right-aligned hint below the
 * input (the redundant "Shown on portal homepage" label row was
 * removed since the section description already conveys placement). */
.field-counter--inline { align-self: flex-end; }

/* ============================================================
   TOGGLE LIST
   ============================================================ */
.pref-list { display: flex; flex-direction: column; }
.pref-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.pref-row:last-child { border-bottom: 0; }
.pref-row:first-child { padding-top: 6px; }
.pref-label { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pref-label strong { font-size: 14px; font-weight: 500; color: var(--text); }
.pref-label span { font-size: 13px; color: var(--text-3); }

/* Aurora on/off toggle (feat/admin-aurora-toggle): a sunken light track that,
   when ON, reveals the locked "Spruce" aurora field through frosted glass — the
   same recipe as the handover toggle, but CSS-only (pseudo-elements + a static
   frosted field PNG) so it works on every .toggle including the re-rendered
   tour-card toggles, with no markup change and no extra JS. Driven by the
   existing aria-checked state. */
.toggle {
  position: relative;
  width: 38px;
  height: 22px;
  border-radius: 999px;
  border: 0;
  padding: 0;
  flex-shrink: 0;
  cursor: pointer;
  isolation: isolate;
  -webkit-tap-highlight-color: transparent;
  background: #d6d7d3;
  box-shadow: inset 0 2px 4px rgba(0,0,0,0.22), inset 0 -1px 1px rgba(255,255,255,0.30);
  transition: box-shadow 250ms ease, transform 130ms cubic-bezier(.3,.7,.3,1);
}
.toggle:focus-visible { outline: 2px solid #7b81be; outline-offset: 3px; }
.toggle:active { transform: scale(0.96); }
/* ON field — its own pill radius clips the aurora (no overflow:hidden, which
   would clip the knob's shadow). Sheen + white film layered over the field. */
.toggle::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 999px;
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.30) 0%, rgba(255,255,255,.03) 9%, transparent 26%),
    linear-gradient(rgba(255,255,255,.05), rgba(255,255,255,.05)),
    url("/img/aurora-toggle-field.png");
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.42), inset 0 0 0 1px rgba(255,255,255,.16), inset 0 -7px 11px -9px rgba(4,8,22,.5);
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 240ms ease-out, transform 300ms cubic-bezier(.34,1.4,.5,1);
  pointer-events: none;
}
.toggle::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  z-index: 2;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: linear-gradient(180deg, #ffffff, #f4f5f7);
  box-shadow: 0 1px 3px rgba(6,10,26,0.4), 0 0 0 0.5px rgba(6,10,26,0.07), inset 0 1px 0 rgba(255,255,255,0.9);
  transition: transform 280ms cubic-bezier(.34,1.5,.5,1);
}
.toggle[aria-checked="true"] { box-shadow: inset 0 2px 5px rgba(4,8,22,0.40), inset 0 0 0 1px rgba(255,255,255,0.06); }
.toggle[aria-checked="true"]::before { opacity: 1; transform: scale(1); }
.toggle[aria-checked="true"]::after { transform: translateX(16px); }
@media (prefers-reduced-motion: reduce) {
  .toggle, .toggle::before, .toggle::after { transition-duration: 0.001ms !important; }
  .toggle::before { transform: none; }
}

/* ============================================================
   PREVIEW PANE — "Pending changes"
   ============================================================ */
.preview-col { position: sticky; top: 84px; }
.preview-card {
  border: 1px solid var(--line);
  border-radius: 20px;
  background: var(--surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
/* Single-row preview header: status dot + label on the left, viewport
 * toggle on the right. Replaces the previous two-row treatment. */
.preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
}
.preview-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
}
.preview-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #b8d8c2;            /* light green — "all good" */
  transition: background-color 200ms ease;
}
.preview-status:not(.is-clean) .preview-status-dot {
  background: #f4d491;            /* light amber — "remember to save" */
}
.preview-device {
  padding: 28px 22px;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 80% at 50% 0%, var(--accent-soft) 0%, transparent 60%),
    var(--bg-2);
}

/* ============================================================
   PHONE PREVIEW
   ============================================================ */
.phone {
  width: 100%;
  max-width: 280px;
  aspect-ratio: 9 / 17.5;
  background: var(--portal-bg);
  border-radius: 36px;
  border: 1px solid #1c1f24;
  box-shadow:
    0 0 0 8px #0a0b0e,
    0 0 0 9px #1c1f24,
    var(--shadow-pop);
  overflow: hidden;
  position: relative;
  color: var(--portal-text);
  font-family: 'Schibsted Grotesk', sans-serif;
}
.phone-notch {
  position: absolute;
  top: 9px; left: 50%; transform: translateX(-50%);
  width: 80px; height: 18px;
  background: #000;
  border-radius: 12px;
  z-index: 5;
}
.phone-content {
  position: absolute;
  inset: 0;
  padding: 64px 22px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 3;
}
.phone-logo { width: 50px; height: 30px; margin-top: 28px; }
.phone-wordmark {
  margin-top: 8px;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--portal-text);
}
.phone-subtitle {
  font-size: 11.5px;
  color: var(--portal-text-2);
  margin-top: 22px;
  text-align: center;
}
.phone-card {
  width: 100%;
  margin-top: 14px;
  background: var(--portal-card);
  border: 1px solid var(--portal-card-line);
  border-radius: 16px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.phone-field-label {
  font-size: 9.5px;
  color: var(--portal-text-2);
  margin-bottom: -2px;
}
.phone-field-input {
  background: var(--portal-input);
  border: 1px solid var(--portal-card-line);
  border-radius: 10px;
  padding: 8px 11px;
  color: var(--portal-text);
  font-size: 11px;
  font-family: 'Schibsted Grotesk', sans-serif;
}
.phone-cta {
  margin-top: 4px;
  padding: 11px;
  background: var(--portal-accent);
  border-radius: 12px;
  color: #1a1208;
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  letter-spacing: -0.005em;
  transition: background 200ms ease;
}
.phone-foot {
  text-align: center;
  font-size: 9.5px;
  color: var(--portal-text-3);
  margin-top: 16px;
}

.preview-foot {
  padding: 12px 18px;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: var(--text-3);
  background: var(--bg-2);
}
.preview-foot a { color: var(--text-2); text-decoration: none; font-weight: 500; }
.preview-foot a:hover { color: var(--text); }

/* ============================================================
   PREVIEW VIEWPORT TOGGLE + IFRAME FRAME
   Replaces the simulated-phone bezel with a clean viewport-shaped
   container. Mobile = portrait phone aspect, desktop = landscape
   browser aspect. The iframe inside loads /admin/preview/portal so
   what the operator sees is exactly what their customers see.
   ============================================================ */

/* Inline [Mobile] [Desktop] toggle inside .preview-header. Pills render
 * as a unified segmented control, with the active option highlighted by
 * a soft surface tint rather than the previous underline (which read as
 * tab nav and was confusing alongside the page-level admin tabs). */
.preview-viewport-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.preview-viewport-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  background: transparent;
  border: 0;
  color: var(--text-3);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  border-radius: 6px;
  transition: color 160ms ease, background 160ms ease;
}
.preview-viewport-btn:hover { color: var(--text-2); }
.preview-viewport-btn[aria-selected="true"] {
  color: var(--text);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(19, 18, 17, 0.06);
}
.preview-viewport-btn svg { opacity: 0.75; }

/* The frame itself. Outer wrapper handles centring + the gradient
 * background; inner iframe is the actual viewport-aspect rectangle.
 * data-viewport="mobile" → portrait phone aspect; "desktop" → landscape. */
.preview-frame {
  padding: 24px 18px;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 80% at 50% 0%, var(--accent-soft) 0%, transparent 60%),
    var(--bg-2);
}
/* Inner wrapper sized by JS in desktop mode so the iframe's visual
 * footprint matches its scaled box (the iframe itself stays 1280×800
 * unscaled, then transform: scale(N) shrinks it). For mobile, this
 * wrapper is just `display: contents` so the iframe sizes itself. */
.preview-frame-inner {
  position: relative;
}
.preview-frame iframe {
  display: block;
  background: #fff;
  /* Stacked shadow (feat/favicon-and-preview-and-drag Fix 2). The
   * previous single var(--shadow-pop) put almost all the shadow mass
   * below the iframe with -28 px spread, so the top and side edges had
   * no contrast contribution. In light-mode portal previews (--bg-page
   * #f7f0e8 inside the iframe vs --bg-2 #fbfaf7 outside, ΔL* ≈ 4) the
   * background-only difference is too small to define the boundary and
   * the iframe blends into the chrome. Three-layer stack:
   *
   *   1. Hairline edge ring — 0 0 0 1 px rgba(20,18,14,0.12). The admin
   *      neutral-dark at 12 % alpha. Defines the iframe outline as a
   *      light/dark edge independent of what theme is inside.
   *   2. Drop shadow — 0 8 px 28 px -4 px rgba(20,18,14,0.18). Smaller
   *      blur + less-negative spread than the bloom below, so the
   *      darkening reaches the sides + top, not just the bottom.
   *   3. Warm bloom — original var(--shadow-pop) kept as the bottom
   *      character. Sits beneath ring + drop in cascade order; the new
   *      contrast wins where they overlap.
   *
   * Dark-mode portal previews still trivially differ from the light
   * chrome (ΔL* ≈ 94), so the new ring is decorative there rather than
   * load-bearing — no regression. See docs/LIGHT_MODE_CHROME_CONTRAST.md
   * for ΔE / decision trail.
   *
   * border-radius is applied per-viewport below so desktop-mode
   * (different geometry) can opt out if needed. */
  box-shadow:
    0 0 0 1px rgba(20, 18, 14, 0.10),
    0 8px 28px -4px rgba(20, 18, 14, 0.14),
    var(--shadow-pop);
  border: 0;
}
/* Mobile viewport — iPhone 17 logical resolution (402 × 874),
 * visually scaled to fit whatever vertical space is actually
 * available in the admin viewport (fix/preview-fit-viewport).
 *
 * The iframe's layout box stays 402 × 874 so the customer-portal
 * page inside computes its layout against the iPhone 17 viewport
 * — operators see exactly what their customers see. The
 * .preview-frame-inner wrapper computes a single source-of-truth
 * --visual-h via clamp() and derives --scale + --visual-w from
 * it. The iframe inside is transform: scale(--scale).
 *
 * Vertical chrome budget (y-axis space already consumed before
 * the iframe is drawn, when .preview-col is sticky-pinned):
 *   84  px  preview-col sticky `top: 84px`  (.topbar 60 + 24 gap)
 *    1  px  .preview-card border-top
 *   44  px  .preview-header (12 + 12 padding + ~20 content row)
 *    1  px  .preview-header border-bottom
 *   24  px  .preview-frame padding-top
 *   ----
 *  154  px  ABOVE the iframe
 *
 *   24  px  .preview-frame padding-bottom
 *    1  px  .preview-card border-bottom
 *   24  px  bottom breathing room
 *   ----
 *   49  px  BELOW the iframe
 *
 * Total chrome ≈ 203 px. Round to 210 for a touch of safety
 * margin. Iframe gets `min(739, 100dvh - 210)` so it caps at the
 * old fixed scale (0.845) on tall screens and shrinks gracefully
 * on shorter ones. Floor at 500 so on a really short window the
 * preview stays usable rather than collapsing to nothing — at
 * that floor it does overflow the viewport, but the operator can
 * resize.
 *
 * Why CSS clamp + custom properties instead of a JS resize
 * observer: the calc inputs (100dvh + a constant) are always
 * known to the browser; modern viewport units handle iOS
 * dynamic-toolbar resizes natively; and a single CSS rule beats
 * an extra JS write loop on every window resize. The pre-existing
 * desktop-mode JS scale logic stays — desktop has different
 * geometry (1280×800 base) and is handled separately.
 *
 * Click hit areas are computed against the visual (transformed)
 * box per the CSS Transforms spec — clicks land where the
 * operator sees them at every scale. */
.preview-frame[data-viewport="mobile"] .preview-frame-inner {
  /* Visual height comes from CSS clamp — that part works native:
   *   floor 500 / desired = 100dvh − 210 chrome / cap 739 (= old 0.845 scale).
   * --scale and --visual-w are JS-driven (recomputeMobileScale in
   * admin.js), mirroring the existing recomputeDesktopScale pattern.
   *
   * Why JS rather than pure CSS: CSS calc(<length> / <number>) returns
   * <length>, not <number>. So calc(--visual-h / 874) gives 0.789px,
   * which (a) can't multiply with another length (so --visual-w
   * collapsed to auto in the previous attempt) and (b) is invalid as
   * an argument to scale() (so transform fell back to none and the
   * iframe painted at full 402×874, spilling off the right). The
   * unit-strip trick tan(atan2(L, 1px)) works in modern browsers but
   * we already have the desktop JS scale pattern — using the same
   * mechanism for mobile keeps the codebase consistent.
   *
   * Fallbacks below are the steady-state values for a typical 1080p
   * screen, used briefly before the JS resize listener first fires
   * (and as a hard floor if scripts are blocked). They match the old
   * fixed 0.845 scale exactly so the worst-case appearance is the
   * pre-clamp() shipping size, not a broken layout. */
  --visual-h: clamp(500px, calc(100dvh - 210px), 739px);
  --scale:    0.845;
  --visual-w: 340px;

  position: relative;
  width:  var(--visual-w);
  height: var(--visual-h);
}
.preview-frame[data-viewport="mobile"] iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 402px;
  height: 874px;
  transform: scale(var(--scale));
  transform-origin: top left;
  /* Rounded screen corners — 44px at the iframe's native 402-wide
   * resolves to ~37px at the max 0.845 scale and ~25px at the
   * minimum 0.572, both of which read as a believable phone screen.
   * border-radius scales with transform so we don't need to keep
   * this in sync with --scale. overflow: hidden clips the customer-
   * portal content to the rounded shape (modern browsers honour
   * border-radius on iframes natively, but pairing it with overflow
   * is the bullet-proof combination). The box-shadow inherited from
   * .preview-frame iframe naturally traces the rounded outline. */
  border-radius: 44px;
  overflow: hidden;
}
/* Desktop mode: render the iframe at a true desktop viewport width so
 * the customer page lays out the way it would for a real desktop user,
 * then CSS-scale the iframe down to fit the preview frame. JS sets
 * --preview-scale and the wrapper's explicit width/height each time
 * the viewport changes (or the window resizes). */
.preview-frame[data-viewport="desktop"] .preview-frame-inner {
  width: 100%;
  height: var(--preview-desktop-scaled-height, 0);
  overflow: hidden;
}
.preview-frame[data-viewport="desktop"] iframe {
  width: 1280px;
  height: 800px;
  transform-origin: top left;
  transform: scale(var(--preview-desktop-scale, 1));
}

/* ============================================================
   BELOW-FOLD SETTINGS
   ============================================================ */
.below {
  max-width: 1200px;
  margin: 64px auto 0;
  padding: 0 28px;
}
.below-section {
  padding: 36px 0;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 56px;
}
@media (max-width: 880px) {
  .below-section { grid-template-columns: 1fr; gap: 16px; }
}
.below-head h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 6px;
  color: var(--text);
}
.below-head p {
  font-size: 13px;
  color: var(--text-3);
  margin: 0;
  max-width: 32ch;
  line-height: 1.5;
}

.url-row {
  display: flex;
  align-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
  max-width: 600px;
}
.url-row .prefix {
  padding: 11px 14px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  color: var(--text-3);
  background: var(--surface-hi);
  border-right: 1px solid var(--line);
  white-space: nowrap;
}
.url-row input {
  flex: 1;
  border: 0;
  padding: 11px 14px;
  background: transparent;
  color: var(--text);
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
}
.url-row input:focus { outline: none; }
.url-row .copy {
  border: 0;
  border-left: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-2);
  padding: 11px 16px;
  font-size: 12.5px;
  font-weight: 500;
}
.url-row .copy:hover { color: var(--text); background: var(--surface-hi); }
/* Polish Fix 1: small icon-only Copy button + larger "Open in new tab"
 * link. Open is the primary action (more common workflow); copy is a
 * quiet secondary affordance. */
.url-row .url-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-left: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-2);
  padding: 0 12px;
  cursor: pointer;
  transition: color 160ms ease, background 160ms ease;
}
.url-row .url-icon-btn:hover { color: var(--text); background: var(--surface-hi); }
.url-row .url-open {
  display: inline-flex;
  align-items: center;
  border-left: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  padding: 11px 16px;
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background 160ms ease;
}
.url-row .url-open:hover { background: var(--surface-hi); }

.danger-card {
  border: 1px solid var(--danger-line);
  background: var(--danger-soft);
  border-radius: 14px;
  padding: 18px 22px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
@media (max-width: 600px) {
  .danger-card { flex-direction: column; align-items: stretch; gap: 12px; }
}
.danger-info strong {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 2px;
}
.danger-info span {
  font-size: 13px;
  color: var(--text-2);
}

/* Portal-state status indicator (fix/disable-portal-actually-works-
 * and-status-dot FIX 2). Stripe/Linear/Mercury status-dot pattern:
 * a small 8px colored dot carries the state signal; the adjacent
 * text stays neutral admin-text-primary so the row reads as
 * "informational status" rather than "promotional success/alert."
 * The dot's color (live = muted herb green #4ea672; offline =
 * var(--danger) red) is the only chromatic cue. The asymmetric
 * casing in the text ("live" lowercase vs "OFFLINE" uppercase) is
 * preserved so the operator immediately notices the down state. */
.danger-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 4px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}
.danger-status__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.danger-status__dot--live    { background: #4ea672; }
.danger-status__dot--offline { background: var(--danger); }
.danger-description {
  display: block;
  margin: 0;
  font-size: 13px;
  color: var(--text-2);
  line-height: 1.4;
}

/* ============================================================
   PLACEHOLDER CARD (non-Branding tabs — Settings, Tours, Upsell)
   ============================================================
   Single card-shaped block centered in the tab area. The "Coming
   soon" eyebrow is the load-bearing signal for "intentionally
   empty"; the title and body explain what will actually live here
   in operator-friendly language. */
.placeholder-card {
  max-width: 560px;
  margin: 80px auto;
  padding: 48px 40px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow-card);
}
.placeholder-eyebrow {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-3);
  margin: 0 0 18px;
}
.placeholder-title {
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 0 0 12px;
  color: var(--text);
}
.placeholder-body {
  font-size: 14.5px;
  color: var(--text-2);
  max-width: 44ch;
  margin: 0 auto;
  line-height: 1.6;
}

/* ============================================================
   LOGO THUMBNAIL (Branding tab — Logo section)
   ============================================================ */
.logo-thumb {
  aspect-ratio: 16 / 10;
  border-radius: 16px;
  overflow: hidden;
  position: relative;
  background-color: var(--bg-2);
  border: 1px dashed var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
}
.logo-thumb .cover-thumb-label {
  /* Reset the absolute-positioned offsets from .cover-thumb-label
   * (left:14px; bottom:12px) — those are correct for cover/banner
   * thumbnails where the label floats over an image, but here the
   * .logo-thumb uses flex centering and the offsets push the text
   * off-centre. */
  position: static;
  left: auto;
  right: auto;
  top: auto;
  bottom: auto;
  font-size: 12px;
  color: var(--text-3);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
}
/* When backgroundImage is set inline, hide the placeholder label */
.logo-thumb[style*="background-image"] .cover-thumb-label { display: none; }

/* ============================================================
   THEME-MODE PILLS (Branding tab — Theme mode section)
   Landscape mini-previews showing the customer portal login screen
   in light vs dark. Each pill is a self-contained abstract scene:
   page surface + raised card + two input bars + accent button.
   The button uses var(--operator-accent) so swapping color theme
   updates both pills in real time (already wired by the color-
   pair click handler in admin.js — no extra JS needed).
   ============================================================ */
.theme-mode {
  display: inline-flex;
  /* Three pills since 2026-10-12 (508 px in a row): on a window too narrow
   * for them the third goes to a second line instead of out of the page. */
  flex-wrap: wrap;
  gap: 14px;
}
.theme-mode-swatch {
  /* 16:10 landscape, sized for the Branding tab's left column. */
  width: 160px;
  height: 100px;
  border-radius: 12px;
  cursor: pointer;
  padding: 0;
  /* Hidden so the inner card's shadow can't bleed past the pill's
   * rounded edge. */
  overflow: hidden;
  position: relative;
  /* Default: 1px subtle border in admin neutral; on selection the
   * border thickens to 2px in operator-accent and a soft halo
   * appears (box-shadow ring). */
  border: 1px solid var(--line-strong);
  transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
}
.theme-mode-swatch:hover { transform: translateY(-1px); }
.theme-mode-swatch--light {
  /* Matches customer portal --t-page-bg in light mode. */
  background: #f7f0e8;
}
.theme-mode-swatch--dark {
  /* Matches customer portal --t-page-bg in dark mode. */
  background: #0d0c0a;
}
.theme-mode-swatch[aria-checked="true"] {
  /* Selection ring uses operator's chosen accent. Falls back to admin
   * orange on non-Branding contexts. */
  border-color: var(--operator-accent, var(--accent));
  border-width: 2px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--operator-accent, var(--accent)) 22%, transparent);
}

/* The "card" that floats inside each pill — represents the login
 * form-card on the real portal. Centered with absolute positioning
 * so we can use top/left/right/bottom insets to size it relative
 * to the pill rather than juggling flex+padding. */
.theme-mode-card {
  position: absolute;
  inset: 18px 26px;        /* ~56% width × ~64% height of the pill */
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  padding: 10px 9px;
}
.theme-mode-swatch--light .theme-mode-card,
.theme-mode-half--light .theme-mode-card {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(20, 18, 14, 0.08);
}
.theme-mode-swatch--dark .theme-mode-card,
.theme-mode-half--dark .theme-mode-card {
  background: #1a1a1e;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Input bars — two stacked, identical. Subtle gray-on-surface so they
 * suggest "field placeholders" without competing with the accent button. */
.theme-mode-input {
  height: 5px;
  border-radius: 2px;
  width: 100%;
}
.theme-mode-swatch--light .theme-mode-input,
.theme-mode-half--light .theme-mode-input {
  background: rgba(20, 18, 14, 0.06);
}
.theme-mode-swatch--dark .theme-mode-input,
.theme-mode-half--dark .theme-mode-input {
  background: rgba(255, 255, 255, 0.08);
}

/* The accent button — bottom of the card, same width as inputs (so it
 * reads as "the action below the form"). Uses --operator-accent so it
 * tracks the operator's chosen color theme in real time. */
.theme-mode-button {
  height: 7px;
  border-radius: 3px;
  width: 100%;
  margin-top: 3px;
  background: var(--operator-accent, var(--accent));
}

/* ------------------------------------------------------------
   THE THIRD PILL: "User's device settings" (theme_mode 'device';
   the customer's own device decides, light or dark). 2026-10-12.

   A sibling of the two above: the same pill, the same ring when
   chosen, the same little login scene. Here the scene is there
   twice, the light one whole and the dark one cut to the triangle
   below the corner-to-corner diagonal, with a hairline on the cut.
   In the middle a person in white frosted glass (three layers that
   share one path, see THEME_MODE_PERSON in server/routes/admin.js).

   The BUTTON does not clip here (overflow: visible), the inner
   .theme-mode-scene does: the hover text must be able to stand
   above the pill.
   ------------------------------------------------------------ */
.theme-mode-swatch--device {
  overflow: visible;
  /* The pill's own ground, under the scene: the same two colours on the
   * same diagonal, so the rounded corners never show a third one. */
  background: linear-gradient(to bottom right, #f7f0e8 50%, #0d0c0a 50%);
}
.theme-mode-scene {
  position: absolute;
  inset: 0;
  /* the pill's 12 px corner, less its 1 px border (2 px when chosen) */
  border-radius: 11px;
  overflow: hidden;
}
.theme-mode-swatch--device[aria-checked="true"] .theme-mode-scene { border-radius: 10px; }
/* THE LITTLE LOGIN CARD STANDS IN BOTH HALVES (2026-10-14), light above the
 * diagonal and dark below it, cut by the same line: it takes its place and
 * its colours from the two sibling pills' own rules, above. Until then one
 * line here hid it (display: none on .theme-mode-card) and the two grounds
 * were bare. Noki: "the users device setting is missing the content into the
 * window like the othe two. Two different versinos cut dioganally". Do not
 * hide it again. The person stays in the middle, over the card (his words of
 * 2026-10-12: "a diagonal line and dark on other side and light on the other
 * side and ... user icon in the middle"). */
.theme-mode-half {
  position: absolute;
  inset: 0;
}
.theme-mode-half--light { background: #f7f0e8; }
.theme-mode-half--dark {
  background: #0d0c0a;
  /* below the diagonal that runs from the top right corner to the bottom left */
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
.theme-mode-split {
  position: absolute;
  inset: 0;
  /* "to bottom right" puts the 50 % line exactly on that same diagonal. */
  background: linear-gradient(to bottom right,
    transparent calc(50% - 0.5px),
    rgba(134, 128, 118, 0.85) calc(50% - 0.5px),
    rgba(134, 128, 118, 0.85) calc(50% + 0.5px),
    transparent calc(50% + 0.5px));
}
.theme-mode-person {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 48px;
  height: 48px;
  margin: -24px 0 0 -24px;
  /* The path is drawn in a 48 px box; shown at 55 px, about the weight the
   * login card has in the sibling pills. Scaled about the pill's centre, which
   * lies on the diagonal, so the blurred copy inside stays on the real one. */
  transform: scale(1.15);
}
.theme-mode-person > * {
  position: absolute;
  inset: 0;
  display: block;
}
.theme-mode-person-shadow {
  fill: rgba(20, 18, 14, 0.4);
  filter: blur(2.5px);
  transform: translateY(1.5px);
}
.theme-mode-person-glass {
  /* cut to the person by the inline clip-path the page writes (one path for
   * all three layers) */
  overflow: hidden;
}
/* White frosted glass, by hand: a second copy of the pill's own ground, laid
 * exactly over the first (the pill's inner box, centred on the same point)
 * and blurred, and over it a white tint that is lighter where the light
 * falls (top left). Not backdrop-filter: Chrome does not cut that by a
 * clip-path, and this blurs the same everywhere. */
.theme-mode-person-behind {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 158px;
  height: 98px;
  margin: -49px 0 0 -79px;
  filter: blur(4px) saturate(150%);
}
.theme-mode-person-glass::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(155deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.66));
}
.theme-mode-person-edge {
  fill: none;
  stroke: rgba(255, 255, 255, 0.92);
  stroke-width: 1;
}
/* The hover text. The look is the admin's own tooltip (.tour-card-tooltip,
 * further down); shown at once on hover and on keyboard focus, and not kept
 * up by a click's focus, so it leaves with the pointer. */
.theme-mode-tip { white-space: nowrap; }
.theme-mode-swatch:hover .theme-mode-tip,
.theme-mode-swatch:focus-visible .theme-mode-tip { opacity: 1; }

/* ============================================================
   PREVIEW PANE — view switcher (Login / Tour overview / Manage)
   ============================================================ */
.preview-tabs {
  display: flex;
  gap: 6px;
  padding: 6px 14px 0;
  margin-top: -2px;
}
.preview-tab-btn {
  background: transparent;
  border: none;
  padding: 8px 10px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color 160ms ease, border-color 160ms ease;
}
.preview-tab-btn:hover { color: var(--text-2); }
.preview-tab-btn[aria-selected="true"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

/* ============================================================
   PREVIEW PHONE — Tour overview + Manage mockups
   ============================================================ */
/* Uploaded operator logo replaces the legacy mountain SVG inside the
 * preview phone. object-fit:contain keeps any aspect ratio looking right. */
.phone-logo--uploaded {
  width: auto !important;
  max-width: 92px;
  max-height: 38px;
  margin-top: 28px !important;
  margin-bottom: 4px;
  height: auto !important;
  object-fit: contain;
}
/* "Your Logo" placeholder for env-var fallback */
.phone-wordmark--placeholder {
  color: rgba(255,255,255,0.45);
  font-weight: 500;
  letter-spacing: 0.06em;
}

/* Tour overview — header + scrolling card list */
.phone-overview-head {
  margin: 24px 16px 8px;
  text-align: left;
}
.phone-overview-eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255,255,255,0.45);
  margin-bottom: 4px;
}
.phone-overview-title {
  font-size: 20px;
  font-weight: 600;
  color: #fff;
  letter-spacing: -0.01em;
}
.phone-overview-list {
  margin: 12px 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.phone-tour-card {
  display: flex;
  gap: 10px;
  padding: 8px;
  background: rgba(255,255,255,0.04);
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,0.06);
}
.phone-tour-img {
  width: 74px;
  height: 74px;
  border-radius: 8px;
  background-size: cover;
  background-position: center;
  background-color: rgba(255,255,255,0.05);
  flex-shrink: 0;
}
.phone-tour-img--lg {
  width: 100%;
  height: 130px;
  border-radius: 12px;
}
.phone-tour-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.phone-tour-name {
  font-size: 12.5px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.phone-tour-meta {
  font-size: 11px;
  color: rgba(255,255,255,0.7);
}
.phone-tour-where {
  font-size: 10.5px;
  color: rgba(255,255,255,0.45);
}

/* Manage view — single tour deep-dive */
.phone-manage-card {
  margin: 22px 14px 18px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  padding: 10px;
  text-align: left;
}
.phone-manage-body { padding: 10px 6px 4px; display: flex; flex-direction: column; gap: 6px; }
.phone-manage-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  border-top: 1px solid rgba(255,255,255,0.05);
  font-size: 11px;
}
.phone-manage-row:first-of-type { border-top: none; padding-top: 8px; }
.phone-manage-row span { color: rgba(255,255,255,0.55); }
.phone-manage-row strong { color: #fff; font-weight: 500; }
.phone-manage-actions {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.phone-manage-btn {
  text-align: center;
  font-size: 11px;
  font-weight: 500;
  padding: 8px;
  background: rgba(255,255,255,0.05);
  color: #fff;
  border-radius: 6px;
}
.phone-manage-btn--danger {
  background: rgba(239,75,75,0.12);
  color: #ff8a8a;
}

/* ============================================================
   ADMIN TOAST — small banner at bottom
   ============================================================ */
.admin-toast {
  position: fixed;
  left: 50%;
  bottom: 96px;            /* clear the save bar's hovering position */
  transform: translateX(-50%) translateY(8px);
  padding: 10px 16px;
  background: rgba(20,20,24,0.92);
  color: #fff;
  border-radius: 8px;
  font-size: 13px;
  letter-spacing: 0.005em;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease, transform 200ms ease;
  z-index: 100;
  box-shadow: 0 6px 24px rgba(0,0,0,0.18);
}
.admin-toast.is-active {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ============================================================
   STICKY SAVE BAR
   ============================================================ */
.save-bar {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translate(-50%, 16px);
  opacity: 0;
  pointer-events: none;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 10px 10px 18px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 14px;   /* soft rectangle — a control surface, not a pill (25.08, Noki) */
  box-shadow: var(--shadow-pop);
  transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 200ms ease;
}
.save-bar.is-active {
  transform: translate(-50%, 0);
  opacity: 1;
  pointer-events: auto;
}
.save-bar .msg { font-size: 13.5px; color: var(--text-2); }
.save-bar .msg strong { color: var(--text); font-weight: 600; }

/* Hide the save-bar list on narrow screens, keep just the count */
@media (max-width: 720px) {
  .save-bar .msg .changed-list { display: none; }
}

/* ----- Saved confirmation (transient) ---------------------------
 * The pattern the big admin products use (Shopify's contextual save
 * bar, Linear's toasts): a small, calm surface that rises a few px,
 * holds, and fades — no glow, no radiant, nothing that asks for
 * attention twice. Same JS lifecycle as before (is-active ~1.75s). */
.saved-pill {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translate(-50%, 8px);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px 9px 12px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: var(--shadow-pop);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  opacity: 0;
  pointer-events: none;
  z-index: 60;
}
.saved-pill[hidden] { display: none; }
.saved-pill-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; border-radius: 50%;
  background: #52a874; color: #fff;
  font-size: 10.5px; font-weight: 700; line-height: 1;
}
.saved-pill-text { padding-right: 2px; }
.saved-pill.is-active { animation: saved-toast 1750ms ease both; }
@keyframes saved-toast {
  0%   { opacity: 0; transform: translate(-50%, 8px); }
  10%  { opacity: 1; transform: translate(-50%, 0); }
  82%  { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .saved-pill.is-active { animation-duration: 0.001ms; opacity: 1; }
}
  }
}
.save-bar-check {
  display: inline-block;
  width: 16px;
  height: 16px;
  line-height: 16px;
  text-align: center;
  border-radius: 50%;
  background: #52a874;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  margin-right: 6px;
  vertical-align: -2px;
}

/* ----- Error state on the save-bar ------------------------------
 * If the Publish POST fails, the bar keeps .is-active (operator can
 * fix and retry), tints its border red, and the message turns red.
 * Cleared automatically on the next Publish click.
 */
.save-bar.is-error {
  border-color: rgba(212, 80, 80, 0.55);
}
.save-bar-msg--error {
  color: #c84b4b;
  font-weight: 500;
}

/* ============================================================
   AUTH PAGES (login, sent, verify-error)
   ============================================================ */
.auth-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Tight gap so wordmark + card read as one composition centred on
   * the viewport rather than two floating elements. */
  gap: 20px;
  padding: 24px;
  background: var(--bg-2);
}
/* Standalone full logo (mark above MYICELAND, white) above the auth card.
 * Auth pages have no operator context yet, so they show the full
 * MyIceland brand instead of the mark-only lockup used in the
 * signed-in topbar. */
.auth-brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.auth-brand-wordmark {
  height: 70px;
  width: auto;
  display: block;
}
.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 36px 32px;
  box-shadow: var(--shadow-card);
}
.auth-card h1 {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}
/* The name alone in the logo's lettering (as .brand-name); the rest of the
 * heading stays in the UI font so the name reads as the brand. Montserrat's
 * cap height (0.70em) equals Schibsted Grotesk's, so at the heading's size
 * the capitals line up with "Sign in to". */
.auth-card .auth-brand-name {
  font-family: 'Montserrat', 'Schibsted Grotesk', system-ui, sans-serif;
  letter-spacing: 0.058em;
  text-transform: uppercase;
}
/* That lettering is wider: the heading needs ~248px, so on 360px-wide phones
 * the card gives up a little side padding to keep it on one line. */
@media (max-width: 380px) {
  .auth-card { padding-left: 24px; padding-right: 24px; }
}
.auth-card p {
  font-size: 13.5px;
  color: var(--text-2);
  line-height: 1.55;
  margin: 0 0 20px;
}
.auth-card .auth-form { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
/* Keeps a hyphenated word whole on phones ("sign-" / "in link."). */
.auth-card .nobr { white-space: nowrap; }

/* ============================================================
   AUTH PAGES — Northern Lights + frosted glass (FIX 1)
   ============================================================
   The aurora WebGL canvas plays full-screen behind a frosted-glass card.
   Text is white throughout: the background is always the dark aurora,
   regardless of the OS light/dark setting. Performance: the backdrop-filter
   blur is scoped to the card footprint (not full screen) to keep the
   per-frame blur recompute cheap over the live canvas. */
.auth-body .aurora-loader--bg {
  top: 0;            /* full viewport (syncTopAnchor also sets this inline) */
  z-index: 0;        /* behind the auth shell — beats base .aurora-loader z-index:25 */
}
.auth-body .auth-shell {
  background: transparent;   /* let the aurora show through */
  position: relative;
  z-index: 1;                /* paint above the aurora (z-index:0) */
}
.auth-body .auth-card {
  /* Frosted glass. The -webkit- prefix is MANDATORY for iOS Safari. */
  background: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(12px) saturate(125%);
  backdrop-filter: blur(12px) saturate(125%);
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.28);
}
.auth-body .auth-card h1 { color: #fff; }
.auth-body .auth-card p  { color: rgba(255, 255, 255, 0.82); }
.auth-body .auth-card a:not(.btn) { color: #fff; }
/* Inputs read as part of the glass — translucent, white text. */
.auth-body .input {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.28);
  color: #fff;
}
.auth-body .input::placeholder { color: rgba(255, 255, 255, 0.55); }
.auth-body .input:focus {
  border-color: rgba(255, 255, 255, 0.65);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.12);
}
/* Secondary/ghost actions (e.g. "Use a different email") read as glass. */
.auth-body .btn {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.28);
  color: #fff;
}
.auth-body .btn:hover { background: rgba(255, 255, 255, 0.18); color: #fff; }
/* Primary action: solid white so the CTA clearly leads over the aurora.
   Uses .btn.btn-primary (3 classes) to win over .auth-body .btn regardless
   of source order. */
.auth-body .btn.btn-primary {
  background: #fff;
  border-color: #fff;
  color: #14110c;
}
.auth-body .btn.btn-primary:hover { background: rgba(255, 255, 255, 0.92); filter: none; }
/* Inline submit error (e.g. rate-limited) — soft red legible on the dark glass.
   Three classes so it beats `.auth-body .auth-card p` (white) on specificity. */
.auth-body .auth-card .auth-error {
  margin: 2px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: #ffb4a2;
}

/* Autofill — keep the field frosted/translucent with white text instead of the
   browser's opaque yellow/white fill. -webkit-autofill can't take a real
   background, so we paint it via an inset box-shadow the size of the field and
   force the text colour. Long transition delay defeats the brief flash where
   Chrome repaints its default before our shadow applies. */
.auth-body .input:-webkit-autofill,
.auth-body .input:-webkit-autofill:hover,
.auth-body .input:-webkit-autofill:focus,
.auth-body .input:-webkit-autofill:active {
  -webkit-text-fill-color: #fff;
  caret-color: #fff;
  -webkit-box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.10) inset;
  box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.10) inset;
  transition: background-color 600000s 0s, color 600000s 0s;
}

/* ============================================================
   GMAIL BUTTON — appears under "Use a different email" on the
   "check your email" state. Theme: calm satisfaction.
   ============================================================
   The animation runs ONLY when the button gains .is-appearing (added by
   auth-login.js the moment the card swaps to the sent state). Two parts:
   (1) the button grows from near-zero width to full width; (2) the Gmail
   logo pops in tiny → normal with a quick settle-bounce, then locks. */
.auth-gmail-wrap {
  margin: 10px 0 0;
  display: flex;
  justify-content: center;
}
.auth-gmail-btn {
  /* Same glass as the other secondary buttons, but the logo is the hero. */
  padding: 10px 14px;
  overflow: hidden;
  white-space: nowrap;
}
.auth-gmail-logo {
  display: block;
  height: 19px;
  width: auto;
  opacity: 0.9;            /* per spec: the logo itself at 90% */
}
/* Pre-animation resting state when the button is set to appear: collapsed
   width + hidden logo, so the first frame of .is-appearing has somewhere to
   grow FROM (avoids a flash of the full-size button). */
.auth-gmail-btn.is-appearing {
  animation: gmail-btn-spread 360ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.auth-gmail-btn.is-appearing .auth-gmail-logo {
  /* 90% opacity held throughout; only scale animates (bounce settle). */
  animation: gmail-logo-pop 520ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: 120ms;  /* logo starts popping once the width is opening */
}
@keyframes gmail-btn-spread {
  0%   { max-width: 12%;  opacity: 0.2; transform: scaleX(0.06); }
  60%  { opacity: 1; }
  100% { max-width: 100%; opacity: 1; transform: scaleX(1); }
}
/* Tiny → normal with a quick over/under settle-bounce, then SET.
   Opacity rises fast and holds at 0.9 (the logo's resting opacity is 0.9). */
@keyframes gmail-logo-pop {
  0%   { transform: scale(0.0); opacity: 0; }
  25%  { transform: scale(1.18); opacity: 0.9; }   /* bit too large */
  45%  { transform: scale(0.92); opacity: 0.9; }   /* bit too small */
  62%  { transform: scale(1.08); opacity: 0.9; }   /* bit too large */
  78%  { transform: scale(0.97); opacity: 0.9; }   /* bit too small */
  90%  { transform: scale(1.03); opacity: 0.9; }   /* bit too large */
  100% { transform: scale(1);    opacity: 0.9; }   /* settled, SET */
}
@media (prefers-reduced-motion: reduce) {
  .auth-gmail-btn.is-appearing,
  .auth-gmail-btn.is-appearing .auth-gmail-logo { animation-duration: 1ms; animation-delay: 0ms; }
}

/* ============================================================
   ICONS
   ============================================================ */
.icon { width: 14px; height: 14px; flex-shrink: 0; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.icon-sm { width: 12px; height: 12px; }

/* ============================================================
   COMPANY SETTINGS TABS — Settings / Protection / Legal / Upsell
   ============================================================
   Reuses .below + .below-section for the 2-column grid rhythm.
   The non-Branding tabs don't need a top margin like Branding's
   below-fold sections do (which sit beneath a hero+split layout);
   the .tab-content modifier removes that margin so the tab opens
   flush to the tab strip. */
.below.tab-content {
  margin-top: 24px;
}
.below.tab-content .below-section:first-child {
  border-top: 0;
  padding-top: 8px;
}

/* Field helpers + inline validation. Helper text is the calmer,
 * always-on context line; the error span is hidden by default and
 * appears below the helper when validation fails. */
.field-helper {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--text-3);
  line-height: 1.5;
  max-width: 56ch;
}
.field-helper code {
  font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
  font-size: 11.5px;
  background: var(--surface);
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--text-2);
}
.field-error {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--danger);
  line-height: 1.5;
}
.input.is-invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 12%, transparent);
}

/* The .field-group inside a Settings/Legal section gets a wider max-width
 * than Branding's tagline field — these are operator-typed addresses,
 * not 60-char tags. */
.below.tab-content .field-group { max-width: 520px; margin-bottom: 18px; }
.below.tab-content .field-group:last-child { margin-bottom: 0; }
.below.tab-content .field-label label { font-weight: 500; color: var(--text); }

/* Suppressor list — chip-style entries with a small × button.
 * Empty state is a calm muted line, not a sad fallback. */
.suppressor-list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.suppressor-entry {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 8px 9px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.suppressor-entry-text {
  font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
  font-size: 13px;
  color: var(--text);
}
.suppressor-remove {
  width: 26px; height: 26px;
  border: none;
  background: transparent;
  color: var(--text-3);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
  transition: background 140ms ease, color 140ms ease;
}
.suppressor-remove:hover {
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  color: var(--danger);
}
.suppressor-empty {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--text-3);
  font-style: italic;
}
.suppressor-add {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.suppressor-add .input {
  flex: 1;
  font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
  font-size: 13.5px;
}
.suppressor-add-btn {
  flex-shrink: 0;
  padding: 0 16px;
  font-size: 14px;
}

/* "Coming soon" pill on the Upsell tab's disabled toggle. Sits to the
 * left of the toggle so the eye reads "label — pill — toggle" left to
 * right; the toggle itself is rendered visibly muted. */
.coming-soon-pill {
  display: inline-block;
  padding: 3px 10px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  margin-right: 12px;
  vertical-align: middle;
}
.pref-row.is-disabled { opacity: 0.65; }
.pref-row .pref-control {
  display: inline-flex;
  align-items: center;
}
.toggle[disabled] {
  cursor: not-allowed;
  opacity: 0.55;
  pointer-events: none;
}

/* ====================================================================
 * TOURS TAB — cards-list rhythm matching the Settings/Branding tabs.
 *
 * Layout: vertical stack of full-width cards. Each card has a 3-column
 * row (image · body · inline-edit cluster). Expanded view drops a
 * detail grid below the row, in-card.
 * ==================================================================== */

.tours-tab {
  /* Inherit the page-level rhythm of .below (1200px max, 28px gutters)
   * but lay out as a single-column stream — Tours doesn't use the 2-
   * column "explanation + controls" grid the other tabs do.
   *
   * Top margin matched to .below.tab-content (24px) in operator-side-
   * polish-3 Fix 5 — Tours had 64px and visibly sat lower than Upsell,
   * which made the two tabs feel misaligned when switching between them. */
  display: block;
  max-width: 1200px;
  margin: 24px auto 0;
  padding: 0 28px;
}
.tours-tab .below-section { display: block; }

/* Status banner above the toolbar — calm dot + message. Fades in/out via
 * the .is-active class on a 220ms ease. Hidden via [hidden] when not in
 * use; the class controls opacity for the fade. */
.tours-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 16px;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  color: var(--text-2);
  font-size: 13.5px;
  opacity: 0;
  transition: opacity 220ms ease;
}
/* Browsers default [hidden] to display:none, but the .tours-banner rule
 * above sets display:flex which would override that. Make [hidden] win. */
.tours-banner[hidden] { display: none; }
.tours-banner.is-active { opacity: 1; }
.tours-banner-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: tours-banner-pulse 1.6s ease-in-out infinite;
}
@keyframes tours-banner-pulse {
  0%, 100% { opacity: 0.45; transform: scale(0.92); }
  50%      { opacity: 1;    transform: scale(1.08); }
}

/* Not sticky (2026-08-09, Noki): the stuck row slid UNDER the frosted app
   header while scrolling, which read as a glitch rather than an affordance.
   Scrolls away with the page like everything else. */
.tours-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
}
.tours-search {
  position: relative;
  flex: 1;
  max-width: 420px;
  display: flex;
  align-items: center;
}
.tours-search .icon {
  position: absolute;
  left: 12px;
  pointer-events: none;
  color: var(--text-3);
}
.tours-search .input {
  width: 100%;
  padding-left: 36px;
  font-size: 14px;
}

/* The list itself — vertical stack with consistent gaps. */
.tours-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ----- Card ----- */
.tour-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 14px 16px;
  transition: border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease;
}
.tour-card:hover {
  border-color: var(--line-strong);
  box-shadow: 0 1px 3px rgba(19, 18, 17, 0.04);
}
.tour-card--archived {
  opacity: 0.55;
}
.tour-card--archived:hover { opacity: 0.7; }

.tour-card-row {
  display: grid;
  grid-template-columns: 120px 1fr auto;
  gap: 18px;
  align-items: center;
}
.tour-card-img {
  width: 120px;
  height: 90px;
  border-radius: 10px;
  background: var(--surface-hi) center/cover no-repeat;
  object-fit: cover;
  display: block;
}
.tour-card-img--blank {
  background: linear-gradient(135deg, var(--surface-hi), var(--line));
}

/* Tour-card photo button — entire 120×90 photo area is the click
 * target for the replace modal (feat/tour-photo-override-operator-side
 * Fix 2). The <button> wraps the existing <img class="tour-card-img">
 * (or blank fallback) plus a scrim + 48px liquid-glass icon disc. The
 * inner image keeps its own dimensions; the wrapper takes the same
 * layout slot the bare <img> previously occupied.
 *
 * Reset button chrome (padding / border / background / inherited font)
 * so it sits flush with the surrounding card layout. overflow:hidden
 * confines the scrim to the rounded photo bounds.
 */
.tour-card-photo-btn {
  position: relative;
  display: block;
  width: 120px;
  height: 90px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  flex-shrink: 0;
  font: inherit;
  color: inherit;
}
.tour-card-photo-btn > .tour-card-img {
  display: block;
  width: 100%;
  height: 100%;
  /* The wrapper carries the rounding via overflow:hidden; keep the
   * inner img unrounded so its bottom edge can't double-clip. */
  border-radius: 0;
}
/* Dark scrim layered on the photo. Hidden by default; fades in on
 * hover (desktop) so the white liquid-glass icon stays legible
 * against any photo content. */
.tour-card-photo-scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.15);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
/* 48px liquid-glass disc holding the replace icon. Hidden by default
 * on hover-capable devices; surfaces at 0.4 opacity on touch where
 * hover isn't possible. translate(-50%, -50%) keeps it centred even
 * if the wrapper's dimensions change. */
.tour-card-photo-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  /* Frosted fill — more see-through than the original 0.18 alpha so
   * the photo reads through the disc more clearly. The backdrop-filter
   * blur + saturate still carry the frost effect; the lower alpha just
   * lets more of the photo bleed in. (fix/photo-icon-opacity-and-frost.) */
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
  color: #fff;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
@media (hover: hover) {
  .tour-card-photo-btn:hover .tour-card-photo-scrim,
  .tour-card-photo-btn:focus-visible .tour-card-photo-scrim {
    opacity: 1;
  }
  /* Icon opacity back to full on hover (fix/photo-icon-opacity-and-frost):
   * the prior 0.65 felt too quiet — the affordance gets quieted instead
   * by lowering the disc fill from 0.18 to 0.10 alpha (above), which
   * keeps the frost effect while letting more photo bleed through. */
  .tour-card-photo-btn:hover .tour-card-photo-icon,
  .tour-card-photo-btn:focus-visible .tour-card-photo-icon {
    opacity: 1;
  }
}
@media (hover: none) {
  /* Touch — keep the icon visible at low opacity so operators on iPad /
   * phone don't miss the affordance. Scrim stays off (it would compete
   * with photo clarity and the always-visible icon already signals
   * interactivity). */
  .tour-card-photo-icon { opacity: 0.4; }
}

/* Upsell-row photo button — mirrors the Tour-tab .tour-card-photo-btn
 * pattern (feat/admin-photo-override-upsell-parity §1). Same hover
 * affordance (scrim + 48px liquid-glass disc) at the same calibrated
 * 0.65 opacity. The discount badge sits inside the button as a child
 * overlay; pointer-events:none on the badge so the operator can click
 * anywhere on the photo (including the badge zone) to replace.
 *
 * .me-row-photo styles itself in the existing block above; this rule
 * set adds the button-specific chrome (cursor, border-reset, scrim,
 * icon disc) on top. */
.me-row-photo-btn {
  position: relative;
  cursor: pointer;
  padding: 0;
  border: 0;
  font: inherit;
  color: inherit;
}
.me-row-photo-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.me-row-photo-scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.15);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.me-row-photo-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  /* Matches the Tour-tab disc — 0.10 alpha (see-through frost) +
   * full hover opacity. (fix/photo-icon-opacity-and-frost.) */
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
  color: #fff;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
@media (hover: hover) {
  .me-row-photo-btn:hover .me-row-photo-scrim,
  .me-row-photo-btn:focus-visible .me-row-photo-scrim {
    opacity: 1;
  }
  .me-row-photo-btn:hover .me-row-photo-icon,
  .me-row-photo-btn:focus-visible .me-row-photo-icon {
    opacity: 1;
  }
}
@media (hover: none) {
  .me-row-photo-icon { opacity: 0.4; }
}
/* Discount badge sits inside the photo button now; ensure it doesn't
 * intercept clicks meant for the replace affordance. The badge stays
 * visible and pixel-identical — only its hit-test behavior changes.
 * (feat/admin-photo-override-upsell-parity §1.) */
.me-row-discount-badge {
  pointer-events: none;
}

.tour-card-body { min-width: 0; }
.tour-card-titlerow {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.tour-card-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
  /* Truncate gracefully when an operator picks a very long display name. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
/* Pencil indicator on a customised tour name
 * (feat/tours-tab-customized-name-indicator). Shown by tours.js only
 * when row.settings.display_name (trimmed) differs from the Bókun
 * original (trimmed). Purely informational — no cursor, no hover, no
 * click handler; visual only. inline-flex + vertical-align: middle
 * sits the icon visually centred with the title text; flex-shrink:0
 * is harmless inside the non-flex h3 and protects the icon if this
 * is ever re-parented into a flex container. Tours-tab ONLY — Upsell
 * catalog and customer-facing surfaces intentionally do not render
 * this. Uses --accent directly; no new tokens introduced. */
.tour-card-title-pencil {
  display: inline-flex;
  vertical-align: middle;
  margin-right: 6px;
  color: var(--accent);
  flex-shrink: 0;
}
.tour-card-pill {
  display: inline-block;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.tour-card-meta {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--text-3);
  /* Same truncation as title — meta is decorative, not load-bearing. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.tour-card-inline {
  display: flex;
  align-items: center;
  gap: 14px;
}
.tour-inline-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: var(--text-3);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.tour-inline-row .toggle { margin: 0; }
.tour-inline-row--select { gap: 4px; }
.tour-inline-row--select .select { font-size: 13px; }

.tour-more-btn {
  font-size: 13px;
  padding: 6px 12px;
}

/* Selects use the same surface-hi background as inputs, with a
 * dropdown caret as a CSS background image. Match input height. */
.select {
  appearance: none;
  -webkit-appearance: none;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 26px 6px 10px;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-2) 50%),
    linear-gradient(135deg, var(--text-2) 50%, transparent 50%);
  background-position:
    calc(100% - 14px) calc(50% - 1px),
    calc(100% - 9px) calc(50% - 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: border-color 150ms ease;
}
.select:focus { outline: none; border-color: var(--text-3); }

/* ----- Expanded card body ----- */
.tour-card-expanded {
  border-top: 1px solid var(--line);
  margin-top: 14px;
  padding-top: 14px;
  animation: tour-expand 180ms ease;
}
@keyframes tour-expand {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.tour-detail-grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.tour-detail-row {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 18px;
  align-items: start;
}
/* Conditionally-shown rows (meeting-point name, custom policy, custom
 * window) need [hidden] to win over the row's `display: grid`. */
.tour-detail-row[hidden] { display: none; }
/* Customer-action toggle rows (feature/customer-action-permissions): the
 * toggle sits at the top of the control column, vertically aligned with the
 * label's first line. align-items:center reads better for a single control. */
.tour-detail-row--toggle { align-items: center; }
.tour-detail-row--toggle .toggle { margin: 0; }
.tour-detail-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.tour-detail-label > span {
  font-weight: 500;
  font-size: 14px;
  color: var(--text);
}
.tour-detail-label > small {
  font-size: 12.5px;
  color: var(--text-3);
  font-weight: 400;
  line-height: 1.45;
}
.tour-detail-row .input,
.tour-detail-row .tour-policy-input {
  width: 100%;
  max-width: 480px;
}
/* Tours-tab description textarea removed in operator-side-polish-3 Fix 1.
 * The field moved to the Upsell-tab catalog row (.me-row-description).
 * The tour-description-input rule on .tour-detail-row .input above is
 * dead but harmless and removed below as well. */
.tour-detail-footer {
  margin-top: 14px;
  display: flex;
  justify-content: flex-end;
}

/* Segmented control — pill row, current option highlighted. Same shape
 * as the theme-mode pills on Branding for visual consistency. */
.tour-segmented {
  display: inline-flex;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px;
}
.tour-segmented-btn {
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 500;
  background: transparent;
  border: none;
  border-radius: 999px;
  color: var(--text-2);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.tour-segmented-btn[aria-checked="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(19, 18, 17, 0.06);
}
.tour-segmented-btn:hover { color: var(--text); }

/* ----- Empty / loading / error states ----- */
.tours-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 48px 24px;
  margin-top: 8px;
  border: 1px dashed var(--line);
  border-radius: 14px;
}
/* Author rule with display:flex above ties on specificity with the UA
 * `[hidden] { display: none }` and wins by source order, so the empty
 * state would render even when JS set the [hidden] attribute. Mirror
 * the same pattern used by .tours-banner[hidden] further up. */
.tours-empty[hidden] { display: none; }
.tours-empty-title {
  margin: 0 0 6px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}
.tours-empty-sub {
  margin: 0;
  font-size: 13.5px;
  color: var(--text-3);
}
.tours-error {
  padding: 16px;
  font-size: 14px;
  color: var(--danger);
  background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  border-radius: 12px;
}

/* ----- Skeleton cards (initial load) ----- */
.tour-card--skeleton {
  pointer-events: none;
}
.tour-card-skel-img,
.tour-card-skel-line {
  background: linear-gradient(90deg, var(--surface-hi) 0%, var(--line) 50%, var(--surface-hi) 100%);
  background-size: 200% 100%;
  animation: tour-skel-shimmer 1.4s linear infinite;
  border-radius: 8px;
}
@keyframes tour-skel-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.tour-card--skeleton .tour-card-row {
  /* Only the row-level grid; the skeleton has no inline-edit cluster. */
  grid-template-columns: 120px 1fr;
}
.tour-card-skel-img { height: 90px; width: 120px; }
.tour-card-skel-body { display: flex; flex-direction: column; gap: 10px; }
.tour-card-skel-line {
  height: 14px;
  border-radius: 6px;
}
.tour-card-skel-line--title { width: 60%; height: 18px; }
.tour-card-skel-line--meta  { width: 40%; }

/* ----- Mobile ----- */
@media (max-width: 720px) {
  .tour-card-row {
    grid-template-columns: 80px 1fr;
    gap: 12px;
  }
  .tour-card-img { width: 80px; height: 60px; }
  .tour-card-photo-btn { width: 80px; height: 60px; }
  .tour-card-skel-img { width: 80px; height: 60px; }
  .tour-card-inline {
    grid-column: 1 / -1;
    margin-top: 10px;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: space-between;
  }
  .tour-detail-row {
    grid-template-columns: 1fr;
    gap: 8px;
  }
  .tour-detail-row .input,
  .tour-detail-row .tour-policy-input { max-width: 100%; }
}

/* ============================================================
   TOURS TAB — architecture upgrade (booking-history model)
   Migration 005. New affordances: + Add tour button, Updating
   pill, welcoming empty state, supplier-inactive badge,
   dismiss action, Add tour modal, dismiss confirm modal.
   ============================================================ */

/* ----- Toolbar additions: right-side group with pill + Add btn ----- */
.tours-toolbar { justify-content: space-between; }
.tours-toolbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.tours-add-btn {
  font-size: 13.5px;
  padding: 9px 18px;
  white-space: nowrap;
}
.tours-add-btn span { margin-right: 4px; font-weight: 700; }

.tours-updating-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  font-size: 12px;
  color: var(--text-3);
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.tours-updating-pill[hidden] { display: none; }
.tours-updating-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand, #d68a1a);
  animation: tours-updating-pulse 1.6s ease-in-out infinite;
}
@keyframes tours-updating-pulse {
  0%, 100% { opacity: 0.4; transform: scale(0.92); }
  50%      { opacity: 1;   transform: scale(1.05); }
}

/* ----- Welcoming empty state (zero-bookings operator) ----- */
.tours-empty--welcoming {
  text-align: center;
  padding: 60px 24px;
  background: var(--surface-hi);
  border: 1px dashed var(--line);
  border-radius: 14px;
  margin: 24px 0;
}
.tours-empty--welcoming .tours-empty-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 8px;
}
.tours-empty--welcoming .tours-empty-sub {
  font-size: 14px;
  color: var(--text-2);
  max-width: 440px;
  margin: 0 auto 24px;
  line-height: 1.5;
}
.tours-empty-add-btn {
  font-size: 14px;
  padding: 10px 22px;
}
.tours-empty-add-btn span { margin-right: 4px; font-weight: 700; }

/* ----- New per-card additions: vendor name, last-sold, supplier badge ----- */
.tour-card-vendor {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--text-3);
  font-weight: 500;
}
/* .tour-card-last-sold rule removed in operator-side-polish-3 Fix 3
 * along with the "Last sold X ago" display in tours.js. */
.tour-card-pill--neutral {
  color: var(--text-2);
  background: var(--surface-hi);
  text-transform: none;
  letter-spacing: normal;
  font-weight: 500;
  font-size: 11px;
  cursor: help;
  position: relative;        /* anchor for the tooltip child */
}
.tour-card--supplier-inactive {
  border-style: dashed;
}

/* ----- Tooltip on the "Experience unavailable" badge -----
 * Custom hover/focus tooltip — title="…" was set previously but browsers
 * delay native tooltips ~1s and style them inconsistently. This child
 * <span class="tour-card-tooltip"> renders immediately on hover/focus. */
.tour-card-tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 280px;
  padding: 8px 10px;
  background: var(--text);
  color: var(--surface);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: normal;
  text-transform: none;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
  z-index: 60;
  white-space: normal;
  text-align: left;
}
.tour-card-tooltip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--text);
}
.has-tooltip:hover .tour-card-tooltip,
.has-tooltip:focus-visible .tour-card-tooltip,
.has-tooltip:focus .tour-card-tooltip {
  opacity: 1;
}
.tour-card.is-dismissing {
  opacity: 0;
  transform: scale(0.96) translateY(-6px);
  transition: opacity 200ms ease, transform 200ms ease;
}

/* ----- Footer in expanded More panel ----- */
.tour-detail-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

/* Trash-icon dismiss button (feat/tours-picker-upgrade Fix 2). Replaces
 * the old text "Remove from list" with the same ghost-icon affordance
 * the Upsell catalog uses (.me-row-remove). Lives in the expanded
 * footer (left side, opposite the "Less" navigation button) so removal
 * remains a deliberate post-expansion action while reading visually
 * the same across both tabs. */
.tour-dismiss-btn--icon {
  background: transparent; border: 0; padding: 6px;
  color: var(--text-3); cursor: pointer; border-radius: 6px;
  display: grid; place-items: center;
  transition: color 160ms ease, background 160ms ease;
}
.tour-dismiss-btn--icon:hover {
  color: var(--danger);
  background: var(--danger-soft, rgba(214, 80, 80, 0.06));
}
.tour-dismiss-btn--icon:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.btn-danger-ghost {
  background: transparent;
  border: 1px solid transparent;
  color: var(--danger, #c84b4b);
  font-size: 13px;
  padding: 6px 12px;
  border-radius: 8px;
  transition: background-color 120ms ease;
}
.btn-danger-ghost:hover {
  background: rgba(200, 75, 75, 0.08);
}
.btn-danger {
  background: var(--danger, #c84b4b);
  border: 1px solid var(--danger, #c84b4b);
  color: #fff;
  font-weight: 500;
}
/* Override the earlier `.btn-danger:hover { background: var(--danger-soft); }`
 * rule (line ~329) which made a near-white hover bg, leaving the white
 * label invisible. Keep the solid red and just darken slightly. */
.btn-danger:hover {
  background: var(--danger, #c84b4b);
  border-color: var(--danger, #c84b4b);
  color: #fff;
  filter: brightness(0.92);
}

/* ============================================================
   ADD TOUR MODAL
   ============================================================ */
.add-tour-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 10, 16, 0.55);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 80px 16px 16px;
  z-index: 200;
  overflow-y: auto;
}
.add-tour-modal-overlay[hidden] { display: none; }

.add-tour-modal {
  width: 100%;
  max-width: 640px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(8, 10, 16, 0.32);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 96px);
}

.add-tour-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px 12px;
  border-bottom: 1px solid var(--line);
}
.add-tour-modal-header h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  color: var(--text);
}
.add-tour-modal-close {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.add-tour-modal-close:hover { background: var(--surface-hi); color: var(--text); }
.add-tour-modal-close svg { width: 18px; height: 18px; }

.add-tour-modal-body {
  padding: 16px 24px 24px;
  overflow-y: auto;
}

/* Footer for the multi-select picker (operator-side-bugfix-1 Bug 3).
 * Sticks to the bottom of the modal with the selection count on the
 * left and Cancel + "Add N tours" on the right. */
.add-tour-modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 24px;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.add-tour-modal-count {
  font-size: 12.5px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.add-tour-modal-actions {
  display: flex;
  gap: 8px;
}

.add-tour-search {
  position: relative;
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}
.add-tour-search .icon {
  position: absolute;
  left: 12px;
  pointer-events: none;
  color: var(--text-3);
}
.add-tour-search .input {
  width: 100%;
  padding-left: 36px;
  font-size: 14px;
}

/* .add-tour-section-title removed in feat/tours-picker-upgrade Fix 1 —
 * the "Recommended" / "Search results" split is gone, so the section
 * headers it styled no longer render. */

.add-tour-results {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Row turned into a button + selectable card (operator-side-bugfix-1
 * Bug 3) so the multi-select pattern works without a per-row "+ Add".
 * Checkmark replaces the button; click anywhere on the row toggles. */
.add-tour-result-row {
  display: grid;
  grid-template-columns: 56px 1fr 22px;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  background: var(--surface-hi);
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 120ms ease, background 120ms ease;
  width: 100%;
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.add-tour-result-row:hover { border-color: var(--line-strong); }
.add-tour-result-row.is-selected {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-hi));
}
.add-tour-result-check {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border: 1.5px solid var(--line-strong);
  border-radius: 50%;
  font-size: 11px; color: transparent;
  flex-shrink: 0;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.add-tour-result-row.is-selected .add-tour-result-check {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--surface);
}

.add-tour-result-img {
  width: 56px;
  height: 42px;
  border-radius: 6px;
  object-fit: cover;
  display: block;
  background: var(--surface);
}
.add-tour-result-img--blank {
  background: linear-gradient(135deg, var(--surface), var(--line));
}

.add-tour-result-body { min-width: 0; }
.add-tour-result-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.add-tour-result-vendor {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* .add-tour-result-err and .add-tour-result-btn rules removed in
 * operator-side-bugfix-1 Bug 3 — the per-row "+ Add" button and its
 * inline error message went away with the switch to multi-select. */

.add-tour-hint, .add-tour-empty, .add-tour-loading {
  margin: 16px 0 0;
  font-size: 13px;
  color: var(--text-3);
  text-align: center;
  padding: 16px;
}
.add-tour-empty { color: var(--text-2); }
.add-tour-empty-title { margin: 0; }
.add-tour-hint span { color: var(--text); font-weight: 500; }

/* ============================================================
   DISMISS CONFIRM MODAL
   ============================================================ */
.dismiss-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 10, 16, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 250;
}
.dismiss-modal-overlay[hidden] { display: none; }

.dismiss-modal {
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  padding: 22px 24px 20px;
  box-shadow: 0 18px 44px rgba(8, 10, 16, 0.32);
}
.dismiss-modal-title {
  margin: 0 0 8px;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}
.dismiss-modal-body {
  margin: 0 0 20px;
  font-size: 13.5px;
  color: var(--text-2);
  line-height: 1.5;
}
/* "Also remove from upsell" opt-in inside the Tours remove modal. */
.dismiss-upsell {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 0 0 20px; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg-2);
  font-size: 13px; line-height: 1.45; color: var(--text-2); cursor: pointer;
}
.dismiss-upsell input { margin-top: 1px; flex: 0 0 auto; accent-color: var(--operator-accent, #d97d27); }
.dismiss-upsell strong { color: var(--text); font-weight: 600; }
.dismiss-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* ============================================================
   TOUR PHOTO REPLACE MODAL
   (feat/tour-photo-override-operator-side Fix 3)
   Mirrors .dismiss-modal-overlay layout posture (fixed, dimmed
   backdrop, centred card) so it sits in the same z-index band and
   visual tradition as the other per-tour confirms. Single modal,
   four sub-states swapped via [hidden] in tours.js.
   ============================================================ */
.tour-photo-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(11, 11, 13, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.tour-photo-modal-overlay[hidden] { display: none; }
.tour-photo-modal {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  width: 100%;
  max-width: 460px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
}
.tour-photo-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 20px 12px;
  border-bottom: 1px solid var(--line);
}
.tour-photo-modal-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}
.tour-photo-modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-2);
  border-radius: 6px;
  cursor: pointer;
}
.tour-photo-modal-close:hover { background: var(--surface-hi); color: var(--text); }
.tour-photo-modal-close svg { width: 18px; height: 18px; }
.tour-photo-modal-body { padding: 18px 20px 20px; }

/* Each top-level state is a flex column with gap. tours.js toggles
 * [hidden] so exactly one is visible at a time. */
.tour-photo-state {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.tour-photo-state[hidden] { display: none; }

/* Drop zone — dashed border, soft hover, prominent drag-over state.
 * The zone is a <label> wrapping a hidden <input type="file">, so
 * click + drop both route through the same file-acceptance path. */
.tour-photo-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 28px 20px;
  border: 2px dashed var(--line-strong);
  border-radius: 10px;
  background: var(--surface-hi);
  color: var(--text-2);
  cursor: pointer;
  text-align: center;
  transition: border-color 150ms ease, background-color 150ms ease;
}
.tour-photo-drop:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 4%, var(--surface-hi));
}
.tour-photo-drop.is-dragover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-hi));
}
.tour-photo-drop-icon { color: var(--text-2); }
.tour-photo-drop-text { margin: 0; font-size: 14px; font-weight: 500; color: var(--text); }
.tour-photo-drop-hint { margin: 0; font-size: 12px; color: var(--text-3); }

/* Bókun-thumbnail block (State A — shown below the drop zone when no
 * override exists yet; hidden in the Replace flow). */
.tour-photo-current-bokun {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.tour-photo-current-bokun[hidden] { display: none; }
.tour-photo-current-bokun-img {
  width: 120px;
  height: auto;
  max-height: 80px;
  border-radius: 8px;
  object-fit: cover;
}
.tour-photo-current-caption {
  margin: 0;
  font-size: 12px;
  color: var(--text-3);
  text-align: center;
}

/* Custom photo preview (State B). */
.tour-photo-custom-img {
  display: block;
  width: 100%;
  max-width: 280px;
  margin: 0 auto;
  border-radius: 10px;
  object-fit: cover;
}

.tour-photo-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
}
.tour-photo-actions[hidden] { display: none; }

/* Inline confirm sub-state — swaps in for the action row when the
 * operator clicks Remove. Visually grouped via a soft surface tint so
 * the destructive primary stands out without a separate modal. */
.tour-photo-confirm {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  background: var(--surface-hi);
  border-radius: 8px;
}
.tour-photo-confirm[hidden] { display: none; }
.tour-photo-confirm-msg {
  margin: 0;
  font-size: 13px;
  color: var(--text);
  text-align: center;
}
.tour-photo-confirm-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
}

/* Uploading state — covers both upload and delete round-trips. The
 * spinner is a single rotating ring keyed off --accent so it threads
 * the brand colour through the wait. */
.tour-photo-state--uploading {
  align-items: center;
  padding: 28px 0;
}
.tour-photo-spinner {
  width: 28px;
  height: 28px;
  border: 2.5px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: tour-photo-spin 800ms linear infinite;
}
@keyframes tour-photo-spin {
  to { transform: rotate(360deg); }
}
.tour-photo-uploading-text {
  margin: 0;
  font-size: 13px;
  color: var(--text-2);
}

/* ============================================================
   FOCAL SELECTOR — booked-tour card crop picker
   (feature/tour-card-focal-selector). Desktop pointer interaction.
   ============================================================ */
.tour-focal-hint {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--text-2);
  text-align: center;
}
/* Stage: holds the green glow (behind), the clipped image+band, and the
   floating card mock (over, unclipped). The whole stage scales for the
   click press-rebound. */
.tour-focal-stage {
  position: relative;
  width: 100%;
  cursor: pointer;
  touch-action: none;
  transition: transform 170ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.tour-focal-stage.is-pressing { transform: scale(0.965); }
/* Green success glow — fades in then out behind the image on lock. Green
   is the success-meaning colour, deliberately NOT the operator theme. */
.tour-focal-glow {
  position: absolute;
  inset: -12px;
  border-radius: 18px;
  background: radial-gradient(ellipse at 50% 50%, rgba(52, 199, 89, 0.6), rgba(52, 199, 89, 0) 70%);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
}
.tour-focal-glow.is-flash { animation: tour-focal-glow-flash 900ms ease-in-out; }
@keyframes tour-focal-glow-flash {
  0%   { opacity: 0; }
  35%  { opacity: 1; }
  100% { opacity: 0; }
}
/* Image clip: overflow:hidden so the band's huge box-shadow darkens only
   within the image bounds (not the whole modal). */
.tour-focal-imgclip {
  position: relative;
  z-index: 1;
  overflow: hidden;
  border-radius: 10px;
  line-height: 0;
  /* JS sizes the clip to the source's natural ratio (height-capped); centre
     it so a capped portrait (narrower than the stage) still has the band
     spanning exactly the image. */
  margin: 0 auto;
}
.tour-focal-img {
  display: block;
  width: 100%;
  height: 100%;          /* JS sizes the clip; the image fills it (cover) */
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}
/* The band: a transparent full-width window at the card aspect ratio.
   Its box-shadow darkens everything outside it; rounded TOP, square
   bottom. JS sets top + height; hidden until the pointer is over. */
.tour-focal-band {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0;
  border-radius: 7px 7px 0 0;
  border-top: 2px solid rgba(255, 255, 255, 0.92);
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 130ms ease;
}
/* Band painted while hovering OR mid-lock-animation (so leaving during the
   success animation keeps the darkened band until the animation ends). */
.tour-focal-stage.is-hovering .tour-focal-band,
.tour-focal-stage.is-locking .tour-focal-band { opacity: 1; }
/* Floating live card mock — a faithful booked-tour card at 80% opacity.
   pointer-events:none so the pointer is ALWAYS measured against the image
   (this is what makes the "leave out the bottom" edge case work). */
.tour-focal-cardmock {
  position: absolute;
  left: 50%;
  top: 0;
  width: min(280px, 82%);
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
  z-index: 3;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.24);
  transition: opacity 110ms ease, transform 170ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.tour-focal-stage.is-hovering .tour-focal-cardmock { opacity: 1; }
.tour-focal-cardmock.is-pressing { transform: translateX(-50%) scale(0.955); }
.tfm-img-wrap {
  width: 100%;
  /* Card image ratio — matches the booked card (~440x80 desktop = 5.5:1,
     derived from the Tour-overview .bc-img-wrap layout). */
  aspect-ratio: 5.5 / 1;
  overflow: hidden;
  background: var(--surface-hi);
}
.tfm-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* object-position set by JS to 50% {focalY}% — identical to the real card. */
}
.tfm-body { padding: 10px 12px 12px; }
.tfm-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tfm-meta { font-size: 12px; color: var(--text-2); margin: 3px 0 10px; }
.tfm-actions { display: flex; gap: 8px; }
.tfm-btn {
  flex: 1;
  text-align: center;
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 600;
}
/* Ticket = the operator's selected theme colour (injected on the admin
   body as --operator-accent); Manage = neutral. */
.tfm-btn--ticket { background: var(--operator-accent, #d97d27); color: #fff; }
.tfm-btn--manage { background: var(--surface-hi); color: var(--text-2); border: 1px solid var(--line); }
.tour-focal-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}
/* Remove (override only) sits left; Replace + Done sit right. */
#tourFocalRemoveBtn { margin-right: auto; }
#tourFocalRemoveBtn[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .tour-focal-stage, .tour-focal-cardmock { transition: none; }
  .tour-focal-glow.is-flash { animation-duration: 1ms; }
}

/* Error banner above the drop zone — appears on validation reject AND
 * server-side upload failure. The drop zone stays interactive so the
 * operator can retry without dismissing the modal. */
.tour-photo-error {
  margin: 0;
  padding: 8px 12px;
  background: color-mix(in srgb, var(--danger) 10%, var(--surface-hi));
  border: 1px solid var(--danger-line);
  border-radius: 8px;
  color: var(--danger);
  font-size: 13px;
}
.tour-photo-error[hidden] { display: none; }

/* ============================================================
   AURORA LOADING ANIMATION — body-level viewport overlay
   The element is a sibling of .page (rendered just before </body>)
   so it isn't constrained by any content wrapper's max-width or
   content-driven height. It sits fixed below the topbar and is
   stacked under both the topbar (z:30) and the .tabs nav row
   (raised to z:26 below) so navigation stays clickable while the
   first-ever Tours tab sync runs.
   ============================================================ */

/* The .tabs nav must paint above the aurora overlay so operators can
 * still switch to other tabs during a long sync. .tabs is non-positioned
 * by default — promote it to relative + z-index. */
.tabs {
  position: relative;
  z-index: 26;
}

.aurora-loader {
  position: fixed;
  /* Top edge sits just below the .topbar (height: 60px, sticky). */
  top: 60px;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 25;
  background: #02040a;
  opacity: 0;
  visibility: hidden;
  /* Base rule = the natural-completion fade-out path (stopFor): when
     .is-active is removed normally, opacity dissolves over 0.9s as the
     content underneath reveals. Tab-switch (forceStop) bypasses this
     entirely via inline `transition: none` — see aurora-loader.js's
     stopImmediate(). */
  transition: opacity 0.9s ease, visibility 0s linear 0.9s;
  pointer-events: none;
  overflow: hidden;
}
.aurora-loader.is-active {
  opacity: 1;
  visibility: visible;
  /* INSTANT entry — the tab is already loading when the operator gets
     here, so the loader is just the visual confirmation. No fade-in. */
  transition: none;
  pointer-events: auto;
}

.aurora-loader canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.aurora-message {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 32px;
  text-align: center;
  pointer-events: none;
}

.aurora-logo {
  /* The logo_Final_2026 stacked logo has a tight viewBox (the old inline art
   * sat in a wide padded box), so it needs less width for the same size. */
  width: clamp(180px, 20vw, 230px);
  height: auto;
  color: rgba(255, 255, 255, 0.92);
  filter: drop-shadow(0 2px 24px rgba(0, 30, 60, 0.55));
  opacity: 0;
  transform: translateY(8px);
}
/* Instant appearance — no fade-in. Matches the loader's overall
   "just be loading" model; the previous 0.5s-delay/1.3s-duration
   logo-fade-in keyframe was removed. */
.aurora-loader.is-active .aurora-logo {
  opacity: 0.92;
  transform: translateY(0);
}

.aurora-message p {
  font-family: 'Schibsted Grotesk', -apple-system, sans-serif;
  font-weight: 400;
  font-size: clamp(15px, 1.6vw, 17px);
  line-height: 1.5;
  letter-spacing: 0.005em;
  max-width: 620px;
  margin: 0;
  opacity: 0;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.45) 0%,
    rgba(255, 255, 255, 0.45) 35%,
    rgba(255, 255, 255, 1)    50%,
    rgba(255, 255, 255, 0.45) 65%,
    rgba(255, 255, 255, 0.45) 100%
  );
  background-size: 200% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
}
/* Instant appearance + shimmer from frame 0. The previous 0.85s-delay
   message-fade-in keyframe was removed. */
.aurora-loader.is-active .aurora-message p {
  opacity: 1;
  transform: translateY(0);
  animation: shimmer 2.4s linear infinite;
}
@keyframes shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .aurora-loader.is-active .aurora-message p {
    animation: none; opacity: 1;
  }
}

/* ============================================================
   MORE EXPERIENCES (Upsell tab) — operator-facing catalog manager.
   Companion to public/admin/upsell.js + server/services/more-
   experiences.js. Layout reuses .below / .below-section rhythm
   from the Branding tab where it makes sense; everything else is
   me-* prefixed to avoid clashing with future tab-specific work.
   ============================================================ */
.me-tab { padding-bottom: 96px; }

/* Shared tab-header rhythm. The Tours tab (operator-side-polish-2 Fix 2)
   piggy-backs on the Upsell header's typography + spacing via a combined
   selector — a future refactor to a generic .tab-header is one find-and-
   replace away if a third tab ever needs the same treatment. */
.me-header,
.tours-header { margin: 0 0 28px; }
.me-title,
.tours-title {
  font-size: 22px; font-weight: 600; letter-spacing: -0.005em;
  color: var(--text); margin: 0 0 8px;
}
.me-subtitle,
.tours-subtitle {
  font-size: 13.5px; color: var(--text-2); line-height: 1.55;
  margin: 0; max-width: 640px;
}

/* Upsell-tab orientation line (operator-side-polish-3 Fix 6). Replaces
 * the prior heading+description block — kept small and gray so it reads
 * as guidance, not as a section title. Same typography as .me-subtitle
 * minus the heading above it; aligned bottom margin with .me-header. */
.me-orientation {
  font-size: 13px; color: var(--text-2); line-height: 1.55;
  margin: 0 0 24px; max-width: 640px;
}

/* Persistent orientation note under a tab header (Tours). Plain guidance text
 * with a small info glyph to its left — no callout box, no tint. */
.tab-intro-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px; color: var(--text-2); line-height: 1.55;
  margin: 14px 0 0; max-width: 620px;
}
.tab-intro-note__icon {
  flex: 0 0 auto;
  width: 15px; height: 15px;
  margin-top: 2px;              /* optical-align the glyph with the first line */
  color: var(--text-3);
}
.tab-intro-note strong { color: var(--text); font-weight: 600; }

/* ───────────────────────────────────────────────────────────────────────────
 * First-login coach-marks — spotlight walkthrough of the nav.
 * (public/admin/coachmarks.js.) One card per item, the target spotlit while the
 * rest dims via a single giant box-shadow on a transparent "hole." Styled in
 * the My-Iceland app brand — green + dark + purple, the same gradient the
 * selected tab icons use — NOT the operator's tenant accent.
 * ─────────────────────────────────────────────────────────────────────────── */
.coach-root {
  position: fixed; inset: 0; z-index: 10050; opacity: 0;
  --coach-grad: linear-gradient(135deg,#22e39a 0%,#149a68 26%,#1b1d25 46%,#191522 54%,#6a36c4 72%,#a64ff5 100%);
  --coach-green: #149a68;
  --coach-purple: #6a36c4;
}
.coach-root.is-in { opacity: 1; transition: opacity .2s ease; }
.coach-root.is-leaving { opacity: 0; transition: opacity .2s ease; }

/* Full-screen click catcher — blocks interaction with the app beneath so the
 * walkthrough is modal; only the card's controls respond. */
.coach-catch { position: fixed; inset: 0; z-index: 1; cursor: default; }

/* The spotlight hole: transparent, framed in brand green with a soft purple
 * halo, and one huge box-shadow in the brand's near-black that dims the whole
 * viewport around it. */
.coach-spot {
  position: fixed; z-index: 2; border-radius: 12px;
  background: transparent; pointer-events: none;
  border: 2px solid var(--coach-green);
  box-shadow:
    0 0 0 5px color-mix(in oklab, var(--coach-purple) 24%, transparent),
    0 0 0 9999px rgba(21, 19, 32, 0.66);
}
/* Glide between items only once the tour is live (keeps first placement jump-free). */
.coach-root.is-in .coach-spot {
  transition: top .34s cubic-bezier(.4,.14,.2,1), left .34s cubic-bezier(.4,.14,.2,1),
              width .34s cubic-bezier(.4,.14,.2,1), height .34s cubic-bezier(.4,.14,.2,1);
}

/* The teaching card. */
.coach-card {
  position: fixed; z-index: 3; width: 340px; max-width: calc(100vw - 32px);
  background: var(--surface, #fff); color: var(--text);
  border: 1px solid var(--line); border-radius: 16px;
  box-shadow: var(--shadow-pop); padding: 18px 20px 16px;
}
.coach-root.is-in .coach-card {
  transition: top .34s cubic-bezier(.4,.14,.2,1), left .34s cubic-bezier(.4,.14,.2,1);
}
.coach-card-arrow {
  position: absolute; top: -7px; width: 14px; height: 14px;
  background: var(--surface, #fff);
  border-left: 1px solid var(--line); border-top: 1px solid var(--line);
  transform: rotate(45deg); border-radius: 3px 0 0 0;
}
.coach-eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--coach-green); margin-bottom: 7px;
}
.coach-title { font-size: 17px; font-weight: 650; line-height: 1.28; margin: 0 0 8px; color: var(--text); }
.coach-body  { font-size: 13.5px; line-height: 1.55; color: var(--text-2); margin: 0; }
.coach-simple {
  font-size: 13px; line-height: 1.5; color: var(--text); margin: 12px 0 0;
  padding: 9px 12px; border-radius: 10px;
  background: color-mix(in oklab, var(--coach-green) 10%, transparent);
}
.coach-simple strong { color: var(--coach-purple); font-weight: 700; }

.coach-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 16px; }
.coach-skip { background: none; border: 0; padding: 4px 2px; cursor: pointer; font: inherit; font-size: 12.5px; color: var(--text-2); }
.coach-skip:hover { color: var(--text); text-decoration: underline; }
.coach-dots { display: flex; gap: 6px; }
.coach-dot { width: 6px; height: 6px; border-radius: 999px; background: var(--line); transition: width .2s ease, background .2s ease; }
.coach-dot.is-done { background: color-mix(in oklab, var(--coach-green) 45%, var(--line)); }
.coach-dot.is-on { width: 18px; background: var(--coach-green); }
.coach-nav { display: flex; gap: 8px; }
/* Back + Next reuse the admin button styles (.btn / .btn-primary) so the tour's
 * buttons match the rest of the console; only add the disabled affordance for
 * Back, which the base .btn does not carry. */
.coach-back:disabled { opacity: .4; cursor: default; }
.coach-back:disabled:hover { background: var(--surface); }

/* Spotlit nav item shows its selected/open state: the icon lights into the
 * brand gradient (matching .tab[aria-selected]) so the item reads as "open"
 * while the coach-mark points at it. */
.tab.is-coach-active { color: var(--text); }
.tab.is-coach-active .ec-glow { opacity: .85; }
.tab.is-coach-active .ec-core { background-color: transparent; background-image: linear-gradient(135deg,#22e39a 0%,#149a68 26%,#1b1d25 46%,#191522 54%,#6a36c4 72%,#a64ff5 100%); }
.tab-tool.is-coach-active { color: var(--text-2); }
.tab-tool.is-coach-active .ec-glow { opacity: .85; }
.tab-tool.is-coach-active .ec-core { background-color: transparent; background-image: linear-gradient(45deg,#22e39a 0%,#16b677 32%,#1b1d25 52%,#7b40d4 72%,#a64ff5 100%); }

@media (prefers-reduced-motion: reduce) {
  .coach-root, .coach-root.is-in, .coach-root .coach-spot, .coach-root .coach-card { transition: none; }
}

/* Catalog toolbar (operator-side-polish-4 Fix 6) — search input on
 * the left, Add tours button + tag-mechanic ⓘ on the right. Mirrors
 * .tours-toolbar's rhythm so the two tabs feel like the same product. */
.me-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
}
.me-search {
  position: relative;
  flex: 1;
  max-width: 420px;
  display: flex;
  align-items: center;
}
.me-search .icon {
  position: absolute;
  left: 12px;
  pointer-events: none;
  color: var(--text-3);
}
.me-search .input {
  width: 100%;
  padding-left: 36px;
  font-size: 14px;
}
.me-toolbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

/* Card shell for the discount + catalog sections.
 *
 * NOTE on overflow (operator-side-bugfix-1 Bug 2): previously this rule
 * had `overflow: hidden` to clip child backgrounds to the 14px rounded
 * corners. That also clipped the .me-tag-info-popover, which is
 * positioned `bottom: calc(100% + 8px)` relative to its anchor inside
 * the toolbar — the popover got cut off behind the card's top edge.
 *
 * The clip is no longer needed because the only child that used to
 * have a background extending to the corners (.me-card-head) was
 * removed in operator-side-polish-2 Fix 4. The remaining children
 * (.me-card-body, .pref-row, the catalog list, etc.) don't set
 * background-color, so removing overflow:hidden has no visible effect
 * on the card chrome but lets the tooltip escape the card. The Tours
 * tab's tooltip works for the same reason — its ancestor chain has
 * no overflow:hidden anywhere. */
.me-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  margin-bottom: 24px;
}
.me-card-head {
  padding: 18px 22px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
}
.me-card-title {
  font-size: 15px; font-weight: 600; margin: 0 0 4px;
  color: var(--text);
}
.me-card-desc {
  font-size: 12.5px; color: var(--text-2); margin: 0; line-height: 1.5;
}
.me-card-body { padding: 20px 22px; }

/* Promo section — input visible only when toggle ON. The promo-desc
 * (operator-side-polish-2 Fix 4) replaces the previous dynamic on/off
 * helper text under the toggle label: a single static line that doesn't
 * change with toggle state. Same typography as .me-card-desc but lives
 * inside .me-card-body (no .me-card-head wrapper anymore), so it gets
 * its own top margin instead of inheriting from the head's bottom border. */
.me-promo-desc {
  font-size: 12.5px; color: var(--text-2); line-height: 1.5;
  margin: 12px 0 0; max-width: 640px;
}
.me-promo-input-wrap { margin-top: 16px; }
.me-promo-input-wrap[hidden] { display: none; }

/* Tag-info ⓘ tooltip — replaces the prior dismissable yellow banner
 * (Fix 4). Mirrors the .has-tooltip / .tour-card-tooltip pattern from
 * the Tours tab (admin.css §"Tooltip on the Experience-unavailable
 * badge"): an absolutely-positioned popover anchored to the icon,
 * shown on :hover or :focus-visible. The icon sits inline next to the
 * "Add tours" button so the explanatory copy is one click of attention
 * away without taking over the section header. */
.me-tag-info {
  position: relative;
  display: inline-grid; place-items: center;
  width: 22px; height: 22px;
  margin-left: 8px;
  border-radius: 50%;
  cursor: help;
  color: var(--text-2);
  transition: color 140ms ease, background 140ms ease;
}
.me-tag-info:hover, .me-tag-info:focus-visible {
  color: var(--text);
  background: var(--surface-hi);
  outline: none;
}
.me-tag-info svg { width: 14px; height: 14px; }

.me-tag-info-popover {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 300px;
  padding: 10px 12px;
  background: var(--text);
  color: var(--surface);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: normal;
  text-transform: none;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
  z-index: 60;
  white-space: normal;
  text-align: left;
}
.me-tag-info-popover::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--text);
}
.me-tag-info:hover .me-tag-info-popover,
.me-tag-info:focus-visible .me-tag-info-popover,
.me-tag-info:focus .me-tag-info-popover {
  opacity: 1;
}

/* Catalog list — drag-reorderable rows. */
.me-catalog-state {
  font-size: 12.5px; color: var(--text-2); padding: 12px 0;
}
.me-empty {
  text-align: center; padding: 40px 0 28px;
}
.me-empty-title { font-size: 14px; font-weight: 500; color: var(--text); margin: 0 0 4px; }
.me-empty-sub   { font-size: 12.5px; color: var(--text-2); margin: 0; }

.me-catalog-list {
  list-style: none; padding: 0; margin: 0 0 16px;
  display: flex; flex-direction: column; gap: 10px;
}
/* Catalog rows redesigned as preview cards (feat/upsell-catalog-as-
 * preview Fix 1). Each row now visually mirrors the customer-facing
 * More Experiences card: large left photo, bigger title, description
 * preview, tag chips at bottom. Edit affordances appear on :hover.
 * The drag handle is permanently visible at 30% opacity, jumping to
 * 100% on row hover. */
.me-row {
  display: flex; align-items: stretch; gap: 16px;
  padding: 16px;
  /* Slight warm-cream tint (feat/admin-polish-greeting-promo-favicon Fix 1).
   * --surface is #ffffff; #fffefc is a very faint cream that picks up the
   * cream-themed admin palette without reading as "off-white" against the
   * surrounding --bg-2 chrome. Hover state still flips to --surface-hi
   * (a stronger cream) for the lift contrast. */
  background: #fffefc;
  border: 1px solid var(--line);
  border-radius: 12px;
  /* transform intentionally NOT in the transition list
   * (feat/upsell-drag-clone-positioning-and-feel Fix 4). SortableJS sets
   * and clears inline transform values on the row during FLIP reorder
   * animations; an own-transition watching transform would briefly
   * animate the clearing back to identity, fighting Sortable's timing.
   * Sortable owns transform; we own the other surface-feel properties. */
  transition: box-shadow 160ms ease, border-color 160ms ease,
              background 160ms ease;
  position: relative;
  /* cursor: grab REMOVED from the row in feat/preview-and-drag-
   * investigation B2. SortableJS's `handle` option (set in upsell.js
   * to '.me-row-handle, .me-row-photo') only initiates drag from those
   * two zones; the row-wide `cursor: grab` signaled draggability where
   * it didn't apply. Grab cursor now lives on the handle + photo only. */
}
/* .me-row uses display:flex, which overrides the UA [hidden]{display:none}, so
 * applyCatalogSearch()'s setAttribute('hidden','') was visually inert and the
 * More-experiences catalog search filtered nothing. Make the attribute win.
 * Mirrors the existing .tours-banner[hidden] / .tour-detail-row[hidden] fixes. */
.me-row[hidden] { display: none; }

/* .me-row:active grab-cursor and the interactive-zone cursor resets
 * also removed — without `cursor: grab` on .me-row, the page's default
 * cursor applies everywhere outside the explicit drag zones. */
.me-row:hover {
  border-color: var(--line-strong);
  background: var(--surface-hi);
  box-shadow: 0 1px 3px rgba(20, 18, 14, 0.05);
}
/* Slot indicator — the empty space the card will land in. Reads as
 * "placeholder" via low row opacity + child-hide, distinct from the
 * elevated clone the operator is holding. (feat/upsell-drag-clone-
 * positioning-and-feel Fix 3.) */
.me-row--ghost {
  opacity: 0.35;
  background: var(--surface-hi);
}
.me-row--ghost > * {
  opacity: 0;
}

/* Brief mousedown-before-drag state. Unchanged from prior branches. */
.me-row--chosen { background: var(--surface-hi); }

/* The held card — what follows the cursor during drag. Appended to
 * document.body by SortableJS (fallbackOnBody: true) so this lives in
 * the global stacking context. !important on the visual properties
 * because (a) SortableJS sets inline styles on the clone and (b) the
 * base .me-row cascade would otherwise win. NOT !important on
 * position/top/left/width/height — those stay Sortable-controlled.
 * (feat/upsell-drag-clone-positioning-and-feel Fix 2.) */
.me-row--drag {
  /* Opaque — this is the card you are holding, not a placeholder. */
  opacity: 1 !important;
  background: var(--surface) !important;
  /* Elevated — the shadow alone communicates "held card" cleanly
   * (Linear, Notion, Things 3 all rely on shadow without scale). A
   * prior scale(1.02) co-transform fought Sortable's inline top/left
   * position-tracking on every frame, freezing the clone in place;
   * removed in feat/upsell-drag-unfreeze-and-earlier-scroll Fix 1. */
  box-shadow:
    0 12px 32px rgba(20, 18, 14, 0.16),
    0 4px 12px rgba(20, 18, 14, 0.08) !important;
  border-radius: 12px;
  /* Clone sits above everything — it's appended to body so z-index
   * applies in the global stacking context. */
  z-index: 10000 !important;
  /* Belt-and-brace: fallbackOnBody detaches the clone from the list,
   * so it no longer inherits the list's width box-sizing. Sortable
   * sets explicit width inline, but defensive box-sizing keeps padding
   * + borders from changing the perceived width. */
  box-sizing: border-box;
  /* No own transition — Sortable handles drag-clone animation timing
   * (add/remove). An own transition here would fight Sortable's. */
  transition: none !important;
  cursor: grabbing !important;
}

/* Single drag wrapper (feat/favicon-and-preview-and-drag Fix 3) — wraps
 * .me-row-handle + .me-row-photo so dragging works across the entire
 * left section of the row, including the flex gap between dots and
 * photo. SortableJS's `handle` option targets this single selector.
 *
 * cursor: grab + user-select: none live HERE on the wrapper so the
 * whole zone signals draggability uniformly. Both properties are
 * inherited, so the .me-row-handle and .me-row-photo children pick
 * them up without per-child duplication. The :active rule swaps to
 * grabbing across the whole zone (no per-child :active needed).
 *
 * align-self: stretch makes the wrapper span the full row height so
 * the column-style hit zone extends top to bottom even when the row's
 * tallest content (the photo at 100 px+) is shorter than the body
 * (title + description + tags + tag-edit button). */
.me-row-drag-zone {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  align-self: stretch;
  flex-shrink: 0;
  /* Hit area sized to fill the row's left strip without overlapping
   * the photo (fix/upsell-drag-zone-wider). Doubled width vs. the
   * 22px handle (44px → twice as wide as the dots column alone) so
   * the operator can grab anywhere in the side gutter. Vertical
   * negative-margin pull (−8px top/bottom) extends the hit zone
   * into the row's 16px padding on each side — stays well inside
   * the card border (8 of 16px consumed) so it never bleeds out.
   * Visual position of the dots themselves doesn't change; the
   * .me-row-handle is centered inside this wider zone. */
  min-width: 44px;
  margin-top: -8px;
  margin-bottom: -8px;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.me-row-drag-zone:active { cursor: grabbing; }

.me-row-handle {
  display: grid; place-items: center;
  width: 22px; flex-shrink: 0;
  /* align-self: stretch (feat/upsell-layout-cleanup Fix 4) — keeps the
   * narrow grip column full-height inside the drag-zone wrapper. The
   * grip-dots stay visually centered via place-items: center; the
   * surrounding empty space is part of the drag-zone wrapper's
   * inherited cursor/user-select. */
  align-self: stretch;
  color: var(--text-3);
  /* 30% at rest, 100% on row hover (Fix 1). */
  opacity: 0.3; transition: opacity 160ms ease, color 160ms ease;
}
.me-row:hover .me-row-handle { opacity: 1; color: var(--text-2); }
.me-row-handle svg { display: block; }
/* FIX 2: while a catalog search is active (.me-search-active on #meCatalogList)
 * drag-reorder is disabled (Sortable.option('disabled') in applyCatalogSearch),
 * so the six-dot grip is swapped for a padlock and the grab cursor is dropped —
 * reads as "can't reorder while searching", not broken. */
.me-row-handle-lock { display: none; }
.me-search-active .me-row-handle { opacity: 0.5; }
.me-search-active .me-row-handle-dots { display: none; }
.me-search-active .me-row-handle-lock { display: grid; place-items: center; }
.me-search-active .me-row-drag-zone,
.me-search-active .me-row-drag-zone:active { cursor: default; }

/* Photo is the customer-card hero: full row height, ~140 px wide, with
 * the discount badge positioned over it. cursor: grab + user-select:
 * none inherited from .me-row-drag-zone (feat/favicon-and-preview-and-
 * drag Fix 3) — no per-element duplication needed. */
.me-row-photo {
  position: relative;
  width: 140px; min-height: 100px; align-self: stretch;
  border-radius: 8px;
  background-size: cover; background-position: center;
  background-color: var(--surface-hi);
  flex-shrink: 0;
}
.me-row-photo--placeholder {
  background: linear-gradient(135deg, #e1ddd2, #f6f4ef);
}

/* Discount badge — mirrored frosted-bar restyle (feat/discount-chip-
 * restyle). It matched the customer .chip--discount in style.css as that
 * was before the customer card's v2 (2026-10-11); the customer ribbon has
 * since changed (height, font, slanted end, label format) and the admin
 * keeps this one on purpose. The color differs too: operator-accent orange
 * at 0.55α (instead of customer's semantic wine-red at 0.55α). */
.me-row-discount-badge {
  /* Recipe B chip (feat/admin-aurora-symbols): dark capsule, two purple + one
     green bloom (the inverse of the buttons), white label. Replaces the orange
     fade badge. */
  position: absolute;
  top: 8px;
  right: 8px;
  left: auto;
  z-index: 2;
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  background-color: #0a1024;
  background-image: radial-gradient(70% 150% at 8% 58%, rgba(131,70,191,.6) 0%, rgba(114,67,171,.3) 42%, transparent 72%), radial-gradient(60% 150% at 98% -20%, rgba(146,84,214,.5) 0%, rgba(116,67,176,.26) 44%, transparent 72%), radial-gradient(54% 140% at 60% 126%, rgba(50,186,154,.58) 0%, rgba(24,116,92,.3) 40%, transparent 66%), linear-gradient(135deg, #0d1730 0%, #0a1024 100%);
  background-size: cover;
  background-position: center;
  -webkit-mask-image: none;
  mask-image: none;
  color: #fff;
  font-family: 'Inter Tight', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.01em;
  text-transform: none;
  border: none;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08), 0 2px 6px -2px rgba(0,0,0,.55);
  white-space: nowrap;
}

.me-row-body {
  flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0;
}
/* Title — clip to one line with right-edge fade (feat/3b.2.x-admin-
 * promo-review-redesign §2d). The fade mask makes long titles read
 * cleanly inside the limited body width. On row hover, JS adds
 * .is-marquee when scrollWidth > clientWidth so the text slides back
 * and forth via CSS keyframes. Non-truncated titles do nothing
 * (mask still applies but has no visible content to fade). */
.me-row-title {
  font-size: 16px; font-weight: 600; color: var(--text);
  line-height: 1.3; letter-spacing: -0.005em;
  display: flex; align-items: center; gap: 8px;
  flex-wrap: nowrap;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
}
.me-row-title-text {
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
  min-width: 0;
  transform: translateX(0);
}
.me-row-title.is-marquee .me-row-title-text {
  /* Slow back-and-forth slide. --marquee-shift carries the negative
   * pixel offset the JS handler measured (clientWidth - scrollWidth).
   * Alternate direction + linear timing gives a calm scroll that
   * doesn't jolt at the turnaround. 8s suits typical 40-80 char names. */
  animation: meRowTitleMarquee 8s linear infinite alternate;
}
@keyframes meRowTitleMarquee {
  0%   { transform: translateX(0); }
  100% { transform: translateX(var(--marquee-shift, 0)); }
}
@media (prefers-reduced-motion: reduce) {
  .me-row-title.is-marquee .me-row-title-text { animation: none; }
}
/* .me-row-summary rule removed in operator-side-polish-4 Fix 1 — the
 * read-only preview block was dropped in favor of the editable
 * description textarea alone (no duplicate content). */

/* Tour operator (vendor) — surfaced between description and tags
 * so the admin can see who runs each tour at a glance. Dim
 * secondary color (text-3) so it reads as metadata, not body
 * content; same font size as tags chips for visual continuity
 * with the row's secondary information register. */
.me-row-operator {
  font-size: 11.5px;
  color: var(--text-3);
  margin-top: 0;
  margin-bottom: 0;
  line-height: 1.15;
}
/* When the operator line is present, pull the tags closer to it
 * (the tags' own margin-top is 4px by default; this override drops
 * the operator → tags gap to ~1px so the block reads as a tight
 * stack of secondary metadata). */
.me-row-operator + .me-row-tags {
  margin-top: 1px;
}

/* Tags line = 2-column grid (feat/card-affordances). Column 1 holds the
   wrapping tag list, column 2 the NEW toggle. `minmax(0,1fr)` lets column 1
   shrink instead of pushing the toggle out of the row when tags are long.
   align-items:start keeps the toggle on the FIRST line of tags even when they
   wrap to several lines — it belongs to the row, not to the last tag. */
.me-row-tagline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  column-gap: 10px;
  margin-top: 4px;
}
.me-row-tags {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px;
}

/* "Promote as new tour" — label + the STANDARD admin .toggle (aurora
   green/gray). Quiet at rest so it doesn't compete with the tags; the row's
   hover lifts it to full. Hover reveal is gated behind (hover:hover) so
   touch/pen admins, who have no hover, see it at full strength always.
   When ON, full opacity regardless — an active promotion must be obvious
   while scanning the catalog. */
.me-row-new {
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
  opacity: 0.45;
  transition: opacity 140ms ease;
}
.me-row-new-label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.005em;
  color: var(--text-2);
}
@media (hover: hover) {
  .me-row:hover .me-row-new { opacity: 0.9; }
  .me-row-new:hover { opacity: 1; }
}
@media (hover: none) {
  .me-row-new { opacity: 1; }
}
.me-row-new:has(.toggle[aria-checked="true"]) { opacity: 1; }
.me-row-new:focus-within { opacity: 1; }
/* Compact variant of the standard .toggle, scoped to this row control only —
   the per-tour drawer toggles keep their designed 38x22. Same proportions:
   track 26x16, knob 12 (16-2x2 inset), travel 10 (26-12-2x2). The aurora
   field ::before is inset:0/cover, so it scales with the track for free. */
.me-row-new .toggle { width: 26px; height: 16px; }
.me-row-new .toggle::after { width: 12px; height: 12px; }
.me-row-new .toggle[aria-checked="true"]::after { transform: translateX(10px); }

.me-row-tags-empty {
  font-size: 11.5px; color: var(--text-3); font-style: italic;
}
.me-row-edit-tags {
  margin-left: 4px;
  background: transparent; border: 0; padding: 3px 6px;
  font: inherit; font-size: 11.5px; color: var(--text-2);
  cursor: pointer; border-radius: 4px;
  display: inline-flex; align-items: center; gap: 4px;
}
.me-row-edit-tags:hover { background: var(--surface-hi); color: var(--text); }

/* Click-to-edit description (feat/upsell-catalog-as-preview Fix 2).
 * Default state is a <p> with prose; CSS line-clamps to 3 lines with an
 * ellipsis. Hover shows the text cursor as a subtle "this is editable"
 * cue. Clicking the .me-row-description wrapper swaps the prose for the
 * inline textarea + char-counter via JS; the textarea rules below apply
 * in that state. */
.me-row-description {
  position: relative;
}
.me-row-description-prose {
  margin: 0;
  padding: 4px 6px;
  margin-left: -6px;        /* visually align edge despite the inset padding */
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-2);
  /* Multi-line clamp — keeps the card height predictable while showing
   * enough of the description to read. 3 lines feels right at 140 px
   * photo height. Fix 4 verified: clamp is intact and the line-clamp
   * + -webkit-box combo is the standard cross-browser shape (works
   * everywhere modern). */
  display: -webkit-box;
  /* 2-line clamp (feat/3b.2.x-admin-promo-review-redesign §2e). Was
   * 3 — tightened to keep row heights uniform regardless of
   * description length; full text shows on click via the existing
   * click-to-edit textarea swap. */
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: normal;
  /* Click-to-edit affordance (Fix 3): pointer cursor + soft hover tint
   * so hovering over the prose signals "this is clickable" without
   * needing an explicit pencil icon. */
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 120ms ease;
}
.me-row-description-prose:hover {
  background-color: rgba(20, 18, 14, 0.04);
}
.me-row-description-prose--empty {
  color: var(--text-3);
  font-style: italic;
}
.me-row-description-input {
  width: 100%;
  font-size: 13px;
  line-height: 1.55;
  resize: vertical;
  min-height: 80px;
}
.me-row-description-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 4px;
}
.me-row-description-helper {
  font-size: 11.5px;
  color: var(--text-3);
  line-height: 1.45;
}
.me-row-description-count {
  font-size: 11.5px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  transition: color 120ms ease;
  white-space: nowrap;
}
.me-row-description-count[data-state="warn"] { color: var(--accent); }
.me-row-description-count[data-state="over"] { color: var(--danger, #d2434d); }

.me-row-badge {
  display: inline-block; padding: 2px 6px; border-radius: 4px;
  font-size: 10.5px; font-weight: 500; letter-spacing: 0.02em;
  text-transform: uppercase;
}
.me-row-badge--warn {
  background: rgba(196, 98, 45, 0.10); color: #a8501a;
}

/* Trash icon (Fix 1): hidden by default, revealed on row hover. The
 * destructive action still has its confirm modal so accidental clicks
 * are caught — the hide-by-default treatment is just visual restraint,
 * not a guard. focus-visible keeps it keyboard-accessible. */
.me-row-remove {
  background: transparent; border: 0; padding: 6px;
  color: var(--text-3); cursor: pointer; border-radius: 6px;
  align-self: flex-start;
  display: grid; place-items: center;
  opacity: 0;
  transition: color 160ms ease, background 160ms ease, opacity 160ms ease;
}
.me-row:hover .me-row-remove,
.me-row-remove:focus-visible { opacity: 1; }
.me-row-remove:hover { color: var(--danger); background: var(--danger-soft, rgba(214, 80, 80, 0.06)); }

/* Mobile responsive (Fix 1): below 640 px, the photo shrinks and the
 * card content compresses. Below 480 px the card stacks vertically —
 * photo on top, body below — so the title doesn't get cramped against
 * a narrow column. The drag-zone wrapper handles ordering at the row
 * level (feat/favicon-and-preview-and-drag Fix 3 lifted .me-row-handle
 * + .me-row-photo into a single child of .me-row); inside the wrapper,
 * the flex-direction flips to column on mobile so the dots row sits
 * ABOVE the photo, preserving the previous mobile hierarchy (photo as
 * full-width hero, dots as a thin row above it). */
@media (max-width: 640px) {
  .me-row { padding: 12px; gap: 12px; }
  .me-row-drag-zone { gap: 12px; }
  .me-row-photo { width: 96px; min-height: 80px; }
  .me-row-title { font-size: 15px; }
}
@media (max-width: 480px) {
  .me-row { flex-wrap: wrap; }
  /* Wrapper takes the full row width and stacks its children
   * vertically so the photo is the hero and the dots sit above it. */
  .me-row-drag-zone {
    order: 0;
    flex-basis: 100%;
    flex-direction: column;
    gap: 8px;
  }
  .me-row-photo {
    width: 100%;
    min-height: 140px;
  }
  .me-row-body { order: 1; flex-basis: 100%; }
  /* Trash icon always visible on touch — no hover state to reveal it. */
  .me-row-remove { opacity: 1; position: absolute; top: 12px; right: 12px; }
}

/* Tag chips — read-only and editable variants. */
.me-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px; border-radius: 999px;
  background: var(--surface-hi);
  border: 0.5px solid var(--line-strong);
  color: var(--text-2);
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.01em;
}
.me-chip--editable {
  background: rgba(214, 138, 26, 0.08);
  border-color: rgba(214, 138, 26, 0.25);
  color: var(--accent, #6a4220);
}
.me-chip-x {
  background: transparent; border: 0; padding: 0;
  font: inherit; font-size: 14px; line-height: 1;
  cursor: pointer; color: inherit; opacity: 0.7;
}
.me-chip-x:hover { opacity: 1; }
.me-chip-row-edited {
  display: inline-flex; align-items: center;
  padding: 3px 6px; border-radius: 4px;
  font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase;
  background: rgba(20, 18, 14, 0.05); color: var(--text-3);
}

/* Inline tag editor */
.me-tag-editor { display: flex; flex-direction: column; gap: 8px; flex: 1; min-width: 0; }
.me-tag-editor-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.me-tag-editor-input {
  width: 100%; max-width: 320px;
  padding: 6px 10px; border-radius: 6px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  font: inherit; font-size: 12.5px;
  outline: none;
  transition: border-color 140ms ease;
}
.me-tag-editor-input:focus { border-color: var(--accent, #d68a1a); }
.me-tag-editor-error {
  margin: 0; font-size: 11.5px; color: var(--danger, #c4622d);
}
.me-tag-editor-actions { display: flex; gap: 8px; }
.btn-sm { padding: 5px 12px; font-size: 12px; }

/* Catalog action footer */
.me-catalog-actions {
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
}

/* Picker modal */
.me-modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(20, 18, 14, 0.45);
  display: grid; place-items: center;
  padding: 24px;
}
.me-modal-overlay[hidden] { display: none; }

.me-modal {
  background: var(--surface);
  border-radius: 14px;
  width: 100%; max-width: 720px;
  max-height: 80vh; display: flex; flex-direction: column;
  overflow: hidden;
  box-shadow: 0 24px 64px -16px rgba(20, 18, 14, 0.30);
}
.me-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 18px 22px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
}
.me-modal-title { font-size: 16px; font-weight: 600; margin: 0; color: var(--text); }
.me-modal-close {
  background: transparent; border: 0; padding: 6px; cursor: pointer;
  color: var(--text-2); border-radius: 6px;
  display: grid; place-items: center;
}
.me-modal-close:hover { background: var(--surface-hi); color: var(--text); }
.me-modal-close svg { width: 16px; height: 16px; }

.me-modal-body {
  flex: 1; overflow-y: auto;
  padding: 18px 22px;
  display: flex; flex-direction: column; gap: 14px;
}
.me-modal-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 14px 22px;
  border-top: 1px solid var(--line);
  background: var(--bg-2);
}
.me-modal-actions { display: flex; gap: 10px; }

/* Picker search bar */
.me-picker-search {
  position: relative;
}
.me-picker-search .input { padding-left: 36px; }
.me-picker-search .icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-3); }

.me-picker-hint  { font-size: 12px; color: var(--text-3); margin: 0; }
.me-picker-empty { font-size: 12.5px; color: var(--text-2); padding: 16px 0; text-align: center; }
.me-picker-count { font-size: 12.5px; color: var(--text-2); }

.me-picker-list {
  display: flex; flex-direction: column; gap: 6px;
}
.me-picker-card {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  font: inherit; text-align: left; width: 100%;
  transition: background 140ms ease, border-color 140ms ease;
  position: relative;
}
.me-picker-card:hover {
  background: var(--surface-hi);
  border-color: var(--line-strong);
}
.me-picker-card.is-selected {
  background: rgba(214, 138, 26, 0.06);
  border-color: rgba(214, 138, 26, 0.45);
}
.me-picker-photo {
  width: 48px; height: 48px;
  border-radius: 6px;
  background-size: cover; background-position: center;
  background-color: var(--surface-hi);
  flex-shrink: 0;
}
.me-picker-card-body {
  display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1;
}
.me-picker-card-title {
  font-size: 13px; font-weight: 500; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.me-picker-card-supplier {
  /* "by [Supplier Name]" line (operator-side-polish-4 Fix 4) — small,
   * gray, sits between the title and the summary. Helps operators
   * distinguish their own tours from supplier sub-catalog tours at a
   * glance. */
  font-size: 11.5px; color: var(--text-3); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.me-picker-card-summary {
  font-size: 11.5px; color: var(--text-2); line-height: 1.4;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
  -webkit-line-clamp: 1; -webkit-box-orient: vertical;
}
.me-picker-card-meta {
  font-size: 11px; color: var(--text-3); font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.me-picker-checkmark {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border: 1.5px solid var(--line-strong);
  border-radius: 50%;
  font-size: 11px; color: transparent;
  flex-shrink: 0;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.me-picker-card.is-selected .me-picker-checkmark {
  background: var(--accent, #d68a1a);
  color: #fff;
  border-color: var(--accent, #d68a1a);
}

/* =========================================================
   Manual rating fields — Tour tab + Upsell tab editor
   (feat/3b.2.x-card-enrichment-phase-1 §3)

   Both editors expose the same four fields: rating_value (number
   0-5, step 0.1), rating_review_count (integer), rating_show_value
   (toggle), rating_show_count (toggle). The .tour-rating-inputs
   container is used in the Tours-tab detail-grid; .me-row-rating
   is the Upsell-row variant. Both lay out the value/count input
   inline with the toggle so the operator can see what they're
   editing at a glance.
   ========================================================= */
/* .tour-rating-inputs + .tour-rating-toggle-label rules removed in
 * feat/3b.2.x-admin-rating-fixes §3 — the Tour-tab rating editors
 * were lifted out and the Upsell tab is now the canonical edit
 * surface. Selectors were Tour-tab-only; no Upsell rule depends
 * on them. */

.me-row-rating {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 0.5px dashed var(--line);
}
.me-row-rating-field {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-2);
}
.me-row-rating-key {
  font-weight: 600;
  color: var(--text);
  min-width: 50px;
}
.me-row-rating-field > .input {
  width: 90px;
  flex-shrink: 0;
}

/* =========================================================
   Upsell row — rating aside + smart rating inputs
   (feat/3b.2.x-admin-promo-review-redesign §2)

   Right-side rating area, sibling of .me-row-body, divided by a thin
   vertical line that respects the row's padding on top/bottom.
   Replaces the in-body .me-row-rating + show-* toggles from phase 1
   (the phase-1 .me-row-rating block above is still defined but no
   longer rendered — kept as a no-op for forward-compat).
   ========================================================= */
.me-row-rating-aside {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  position: relative;
  padding-left: 16px;
  padding-right: 4px;
  gap: 10px;
}
/* Thin vertical divider — top/bottom inset so it doesn't touch the
 * row container edges. (Brief: "with top and bottom padding so it
 * does not touch the card container edges.") */
.me-row-rating-aside::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 1px;
  background: var(--line);
}

.me-row-rating-inputs {
  display: flex;
  align-items: center;
  gap: 10px;
}
.me-row-rating-cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface, #fff);
  transition: border-color 120ms ease, background 120ms ease;
}
.me-row-rating-cell:focus-within {
  border-color: var(--accent, #c4622d);
  background: var(--surface);
}
.me-row-rating-input {
  border: 0 !important;
  background: transparent !important;
  padding: 0 !important;
  /* Square the input so the .input class's inherited border-radius: 12px
   * can't paint a rounded background-tint rectangle inside the cell
   * (the "inner pill" that was clipping the caret at the left edge).
   * The visual pill is now SOLELY the outer .me-row-rating-cell.
   * (feat/3b.2.x-card-pills-and-price-fixes §1 / INV-A.) */
  border-radius: 0 !important;
  width: 52px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--text);
  outline: 0 !important;
  box-shadow: none !important;
}
.me-row-rating-input::placeholder { color: var(--text-3); font-weight: 400; }
/* Strip number-input spinners in case a browser ignores type="text".
 * Defense-in-depth: the inputs ARE type="text" in markup for the
 * smart conversion to work, but some user agents may surface spinners
 * on inputmode="decimal" too. */
.me-row-rating-input::-webkit-outer-spin-button,
.me-row-rating-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.me-row-rating-input[type="number"] { -moz-appearance: textfield; }
/* Out-of-range non-blocking error state. The tint now lives on the
 * outer cell (not the input) so there's no nested rounded surface
 * sitting over the input's left edge to clip the caret. Red border
 * wins under :focus-within too. The input stays fully transparent.
 * (feat/3b.2.x-card-pills-and-price-fixes §1 / INV-A.) */
.me-row-rating-cell:has(.me-row-rating-input.is-invalid),
.me-row-rating-cell:focus-within:has(.me-row-rating-input.is-invalid) {
  border-color: var(--danger, #d2434d);
  background-color: rgba(210, 67, 77, 0.06);
}

/* Star glyphs — large, sits inline with the input for the "★ value"
 * compact pill look. The layered glyph stacks an outline ☆ behind a
 * filled ★ for the "count of stars" semantics. */
.me-row-rating-glyph {
  font-size: 14px;
  line-height: 1;
  color: var(--accent, #c4622d);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Three-people glyph for the count cell. The mark itself is now the eclipse
 * count glyph (.ec-icon.ec-count, sized inline in upsell.js); this wrapper
 * just centres it next to the value cell's eclipse star so the two read as a
 * matched pair (feat/admin-aurora-symbols). */
.me-row-rating-glyph--people {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Info tooltip — circular "i" + hover popover with the help copy.
 * Pure CSS show/hide on hover/focus-within; no JS needed for the
 * tooltip itself. */
.me-row-rating-info {
  position: relative;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  cursor: help;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.me-row-rating-info:hover,
.me-row-rating-info:focus-visible {
  border-color: var(--text-3);
}
.me-row-rating-info-glyph {
  font-size: 11px;
  font-weight: 700;
  font-style: italic;
  color: var(--text-3);
  line-height: 1;
}
.me-row-rating-info-tip {
  position: absolute;
  bottom: calc(100% + 8px);
  right: -4px;
  width: 240px;
  padding: 10px 12px;
  background: var(--text);
  color: var(--surface, #fff);
  font-size: 12px;
  line-height: 1.45;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(20, 18, 14, 0.18);
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease;
  pointer-events: none;
  z-index: 5;
}
.me-row-rating-info:hover .me-row-rating-info-tip,
.me-row-rating-info:focus-visible .me-row-rating-info-tip {
  opacity: 1;
  visibility: visible;
}

/* =========================================================
   Promo code panel — three-column refactor
   (feat/3b.2.x-admin-promo-review-redesign §1)

   Inherits the .below-section grid from the Cancellation protection
   pattern (280px title-block + 1fr body). Custom tweaks: top border
   off (this is the FIRST section on the tab, no rule above), and
   the input wrap sits UNDER the title in the left column.
   ========================================================= */
.me-promo-section {
  border-top: 0;
  padding-top: 0;
  /* Thin separator beneath the promo row — mirrors the Protection-tab
   * pattern where each .below-section's border-top draws a line above
   * it. Padding-bottom tightened from .below-section's default 36px
   * to 16px so the line sits closer to the promo content; the More
   * Experiences header that follows gets its own top margin (rule
   * below) to put the visual breathing room ON the header side of
   * the divider. (fix/upsell-promo-bottom-divider +
   * fix/upsell-promo-divider-spacing) */
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
/* Space the More Experiences header away from the divider above it.
 * Adjacent-sibling selector so this only fires when the header sits
 * directly after the promo section (the only place this layout
 * pairing occurs today). */
.me-promo-section + .me-header { margin-top: 36px; }
.me-promo-section .below-head h3 {
  /* Match the Cancellation protection title scale exactly — already
   * styled by the shared .below-head h3 rule, but force-set here so
   * the .me-tab cascade can't accidentally win. */
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 6px;
}
.me-promo-section .me-promo-input-wrap {
  margin-top: 12px;
  animation: mePromoInputFadeIn 180ms ease both;
}
@keyframes mePromoInputFadeIn {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}



/* ============================================================
   AURORA SYMBOL SYSTEM — Recipe A eclipse glyphs (feat/admin-aurora-symbols)
   GENERATED block — do not hand-edit the masks. Source: _gen_aurora_css.js.
   An SVG path is a CSS mask over a green->purple gradient core, with a soft
   blurred glow behind it. Colour lives in the gradient; the glyph just decides
   what shows through. Used by: tab icons, star, count (people), edit pen.
   (Recipe B — the bloom buttons + discount chip — lives at .btn-primary and
   .me-row-discount-badge, edited in place above.)
   ============================================================ */

/* ---- Recipe A: eclipse glyph ---- */
.ec-icon { position: relative; display: inline-block; flex: none; vertical-align: middle; }
.ec-lyr {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
}
.ec-glow { filter: blur(2px); opacity: .85; transform: scale(1.14); background-image: linear-gradient(135deg, #11cf95 0%, #3a8fde 46%, #9a32ef 100%); }
.ec-core { background-image: linear-gradient(135deg, #22e39a 0%, #149a68 26%, #1b1d25 46%, #191522 54%, #6a36c4 72%, #a64ff5 100%); }

/* per-glyph masks (applied to BOTH layers) + core overrides */
.ec-star .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'black'%20stroke%3D'none'%3E%3Cpath%20d%3D'M12%202.6l2.7%205.85%206.45.55-4.9%204.25%201.5%206.25L12%2016.7%206.25%2019.5l1.5-6.25-4.9-4.25%206.45-.55z'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'black'%20stroke%3D'none'%3E%3Cpath%20d%3D'M12%202.6l2.7%205.85%206.45.55-4.9%204.25%201.5%206.25L12%2016.7%206.25%2019.5l1.5-6.25-4.9-4.25%206.45-.55z'%2F%3E%3C%2Fsvg%3E"); }
.ec-star .ec-core { background-image: linear-gradient(135deg, #2cf0a4 0%, #16b677 28%, #1c1e28 46%, #1a1426 52%, #9152f2 67%, #cd82ff 100%); }
.ec-count .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'black'%20stroke%3D'none'%3E%3Ccircle%20cx%3D'12'%20cy%3D'7'%20r%3D'3'%2F%3E%3Cpath%20d%3D'M12%2011.4c-3.2%200-5.4%202-5.4%204.9%200%20.8.6%201.4%201.4%201.4h8c.8%200%201.4-.6%201.4-1.4%200-2.9-2.2-4.9-5.4-4.9z'%2F%3E%3Ccircle%20cx%3D'4.7'%20cy%3D'9'%20r%3D'2.3'%2F%3E%3Cpath%20d%3D'M4.7%2012.6c-2.2%200-3.7%201.5-3.7%203.6%200%20.7.5%201.1%201.1%201.1h2c.1-1.9%201-3.5%202.4-4.5-.5-.1-1.1-.2-1.8-.2z'%2F%3E%3Ccircle%20cx%3D'19.3'%20cy%3D'9'%20r%3D'2.3'%2F%3E%3Cpath%20d%3D'M19.3%2012.6c2.2%200%203.7%201.5%203.7%203.6%200%20.7-.5%201.1-1.1%201.1h-2c-.1-1.9-1-3.5-2.4-4.5.5-.1%201.1-.2%201.8-.2z'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'black'%20stroke%3D'none'%3E%3Ccircle%20cx%3D'12'%20cy%3D'7'%20r%3D'3'%2F%3E%3Cpath%20d%3D'M12%2011.4c-3.2%200-5.4%202-5.4%204.9%200%20.8.6%201.4%201.4%201.4h8c.8%200%201.4-.6%201.4-1.4%200-2.9-2.2-4.9-5.4-4.9z'%2F%3E%3Ccircle%20cx%3D'4.7'%20cy%3D'9'%20r%3D'2.3'%2F%3E%3Cpath%20d%3D'M4.7%2012.6c-2.2%200-3.7%201.5-3.7%203.6%200%20.7.5%201.1%201.1%201.1h2c.1-1.9%201-3.5%202.4-4.5-.5-.1-1.1-.2-1.8-.2z'%2F%3E%3Ccircle%20cx%3D'19.3'%20cy%3D'9'%20r%3D'2.3'%2F%3E%3Cpath%20d%3D'M19.3%2012.6c2.2%200%203.7%201.5%203.7%203.6%200%20.7-.5%201.1-1.1%201.1h-2c-.1-1.9-1-3.5-2.4-4.5.5-.1%201.1-.2%201.8-.2z'%2F%3E%3C%2Fsvg%3E"); }
.ec-pen .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%2020h9'%2F%3E%3Cpath%20d%3D'M16.5%203.5a2.12%202.12%200%200%201%203%203L7%2019l-4%201%201-4Z'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%2020h9'%2F%3E%3Cpath%20d%3D'M16.5%203.5a2.12%202.12%200%200%201%203%203L7%2019l-4%201%201-4Z'%2F%3E%3C%2Fsvg%3E"); }
.ec-pen .ec-core { background-image: linear-gradient(135deg, #1b1d25 0%, #1d2a24 26%, #1f6248 42%, #21885b 49%, #1c1822 57%, #6a36c4 76%, #a64ff5 100%); }

/* tab glyphs: per-tab mask (stroked). Idle = flat grey, active gets the
   gradient + glow. Driven by the existing .tab[aria-selected] state. */
.ec-tab-branding .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Ccircle%20cx%3D'12'%20cy%3D'12'%20r%3D'9'%2F%3E%3Cpath%20d%3D'M3%2012h18'%2F%3E%3Cpath%20d%3D'M12%203c3%203%203%2015%200%2018M12%203c-3%203-3%2015%200%2018'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Ccircle%20cx%3D'12'%20cy%3D'12'%20r%3D'9'%2F%3E%3Cpath%20d%3D'M3%2012h18'%2F%3E%3Cpath%20d%3D'M12%203c3%203%203%2015%200%2018M12%203c-3%203-3%2015%200%2018'%2F%3E%3C%2Fsvg%3E"); }
.ec-tab-settings .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Ccircle%20cx%3D'12'%20cy%3D'12'%20r%3D'3.6'%2F%3E%3Cpath%20d%3D'M12%202v2.4M12%2019.6V22M2%2012h2.4M19.6%2012H22M4.9%204.9l1.7%201.7M17.4%2017.4l1.7%201.7M19.1%204.9l-1.7%201.7M6.6%2017.4l-1.7%201.7'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Ccircle%20cx%3D'12'%20cy%3D'12'%20r%3D'3.6'%2F%3E%3Cpath%20d%3D'M12%202v2.4M12%2019.6V22M2%2012h2.4M19.6%2012H22M4.9%204.9l1.7%201.7M17.4%2017.4l1.7%201.7M19.1%204.9l-1.7%201.7M6.6%2017.4l-1.7%201.7'%2F%3E%3C%2Fsvg%3E"); }
.ec-tab-protection .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%203l7%203v5c0%204.4-3%207.6-7%209-4-1.4-7-4.6-7-9V6z'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%203l7%203v5c0%204.4-3%207.6-7%209-4-1.4-7-4.6-7-9V6z'%2F%3E%3C%2Fsvg%3E"); }
.ec-tab-tours .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M3%2018%20L9%208%20L13%2013%20L17%207%20L21%2018%20Z'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M3%2018%20L9%208%20L13%2013%20L17%207%20L21%2018%20Z'%2F%3E%3C%2Fsvg%3E"); }
.ec-tab-upsell .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%2019V5'%2F%3E%3Cpath%20d%3D'M6%2011l6-6%206%206'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%2019V5'%2F%3E%3Cpath%20d%3D'M6%2011l6-6%206%206'%2F%3E%3C%2Fsvg%3E"); }
/* Chain glyph MASK for "Send login link" (feat/login-links-tool) — verbatim
   ICONS.chain from Symbol & Component Handover (the stroked two-link chain).
   The colour (grey at rest / LINK_CORE on hover) is driven by .tab-tool above,
   so this only supplies the shape. */
.ec-chain .ec-lyr { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M10%2013a5%205%200%200%200%207.54.54l3-3a5%205%200%200%200-7.07-7.07l-1.72%201.71'%2F%3E%3Cpath%20d%3D'M14%2011a5%205%200%200%200-7.54-.54l-3%203a5%205%200%200%200%207.07%207.07l1.71-1.71'%2F%3E%3C%2Fsvg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M10%2013a5%205%200%200%200%207.54.54l3-3a5%205%200%200%200-7.07-7.07l-1.72%201.71'%2F%3E%3Cpath%20d%3D'M14%2011a5%205%200%200%200-7.54-.54l-3%203a5%205%200%200%200%207.07%207.07l1.71-1.71'%2F%3E%3C%2Fsvg%3E"); }

.tab .ec-glow { opacity: 0; }
.tab .ec-core { background-image: none; background-color: #a8a8a0; }
.tab:hover .ec-core { background-color: #8c8c84; }
.tab[aria-selected="true"] .ec-glow { opacity: .85; }
.tab[aria-selected="true"] .ec-core { background-color: transparent; background-image: linear-gradient(135deg, #22e39a 0%, #149a68 26%, #1b1d25 46%, #191522 54%, #6a36c4 72%, #a64ff5 100%); }

/* ======================================================================
   App icon (square) + Smart Select modal (admin redesign).
   The section shows a SQUARE live canvas; "Smart Select" opens a modal
   that crops the MAIN LOGO. App-icon only; favicon stays transparent.
   ====================================================================== */

/* --- Section: square preview + actions --- */
.app-icon-row { display: flex; gap: 22px; align-items: center; flex-wrap: wrap; }
.app-icon-square {
  position: relative;
  width: 132px;
  height: 132px;
  flex: none;
  border: 1px solid var(--line-strong);
  /* Match the .ss-tile corner SHAPE, not just its raw value: ss-tile is
   * 23px on a 104px tile (≈22%); 23px on THIS 132px tile read noticeably
   * squarer. 29px ≈ 22% of 132 → same iOS-squircle curve as ss-tile. */
  border-radius: 29px;
  background: var(--surface);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Clip the canvas to the SAME radius as its container. overflow:hidden on
 * the container imperfectly masks a canvas's square corners, leaking a thin
 * sliver where the canvas (transparent in its corners) lets the container's
 * --surface background show through — the "white corner sliver". Rounding
 * the canvas element itself removes that leak so no white shows. */
.app-icon-square canvas { display: block; width: 100%; height: 100%; border-radius: 29px; }
.app-icon-square canvas[hidden] { display: none; }
/* The tile is a <button> (FIX 2: click to re-edit the icon). Reset default
 * button chrome; the tile keeps its own border/background/radius (above). */
.app-icon-square { padding: 0; cursor: pointer; font: inherit; color: inherit; }
.app-icon-square:disabled { cursor: default; }
/* Hover edit affordance — mirrors the Tour/Upsell .tour-card-photo-* /
 * .me-row-photo-* pattern (dark scrim + 48px liquid-glass disc with the same
 * swap icon), scoped to the icon tile. Surfaces on hover/focus when the tile
 * is editable; stays hidden while disabled (nothing to edit). */
.app-icon-edit-scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.15);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.app-icon-edit-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
  color: #fff;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
@media (hover: hover) {
  .app-icon-square:not(:disabled):hover .app-icon-edit-scrim,
  .app-icon-square:not(:disabled):focus-visible .app-icon-edit-scrim { opacity: 1; }
  .app-icon-square:not(:disabled):hover .app-icon-edit-icon,
  .app-icon-square:not(:disabled):focus-visible .app-icon-edit-icon { opacity: 1; }
}
@media (hover: none) {
  .app-icon-square:not(:disabled) .app-icon-edit-icon { opacity: 0.4; }
}
.app-icon-empty { font-size: 12px; color: var(--text-3); text-align: center; padding: 0 10px; }
.app-icon-empty[hidden] { display: none; }
.app-icon-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.app-icon-actions .btn { min-width: 150px; justify-content: center; }

/* --- Modal shell (mirrors .dismiss-modal posture, wider) --- */
.ss-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(8, 10, 16, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.ss-modal-overlay[hidden] { display: none; }
.ss-modal {
  width: 100%;
  max-width: 640px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 16px;
  box-shadow: 0 18px 48px rgba(8, 10, 16, 0.38);
  display: flex;
  flex-direction: column;
}
.ss-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px 12px; }
.ss-modal-title { margin: 0; font-size: 16px; font-weight: 600; color: var(--text); }
.ss-modal-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 8px;
  border: none; background: transparent; color: var(--text-2); cursor: pointer;
}
.ss-modal-close:hover { background: var(--surface-2, rgba(127,127,127,0.12)); color: var(--text); }
.ss-modal-body {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(196px, 0.75fr);
  gap: 22px;
  padding: 4px 20px 8px;
}
@media (max-width: 600px) { .ss-modal-body { grid-template-columns: 1fr; } }
.ss-modal-actions { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px 18px; }

/* --- Crop stage: larger than the logo so the square can overflow its edges;
       the stage bg is neutral and the square's scrim darkens outside it. --- */
.ss-stage {
  position: relative;
  width: 100%;
  min-height: 260px;
  aspect-ratio: 1 / 1;
  box-sizing: border-box;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background:
    repeating-conic-gradient(rgba(127,127,127,0.10) 0% 25%, transparent 0% 50%) 0 / 18px 18px,
    var(--surface);
  overflow: hidden;
  touch-action: none;
  user-select: none;
  cursor: grab;
}
.ss-stage:active { cursor: grabbing; }
/* JS positions + sizes the image (zoom + pan); pointer-events:none so drags on
   the image area reach the stage (pan) and the square (move). */
.ss-stage-img { position: absolute; display: block; pointer-events: none; user-select: none; -webkit-user-drag: none; }
/* Zoom control — overlaid bottom-right of the stage. */
.ss-zoom {
  position: absolute;
  right: 8px; bottom: 8px;
  display: flex;
  gap: 4px;
  z-index: 3;
}
.ss-zoom-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.18);
}
.ss-zoom-btn:hover { background: var(--surface-2, rgba(127,127,127,0.12)); }
.ss-square {
  position: absolute;
  box-sizing: border-box;
  border: 2px solid #fff;
  border-radius: 3px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
  cursor: grab;
  outline: none;
}
.ss-square:active { cursor: grabbing; }
.ss-square::before {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(to right, transparent calc(33.33% - 0.5px), rgba(255,255,255,0.3) calc(33.33% - 0.5px) calc(33.33% + 0.5px), transparent calc(33.33% + 0.5px)),
    linear-gradient(to right, transparent calc(66.66% - 0.5px), rgba(255,255,255,0.3) calc(66.66% - 0.5px) calc(66.66% + 0.5px), transparent calc(66.66% + 0.5px)),
    linear-gradient(to bottom, transparent calc(33.33% - 0.5px), rgba(255,255,255,0.3) calc(33.33% - 0.5px) calc(33.33% + 0.5px), transparent calc(33.33% + 0.5px)),
    linear-gradient(to bottom, transparent calc(66.66% - 0.5px), rgba(255,255,255,0.3) calc(66.66% - 0.5px) calc(66.66% + 0.5px), transparent calc(66.66% + 0.5px));
  pointer-events: none; opacity: 0; transition: opacity 140ms ease;
}
.ss-square:hover::before, .ss-square:active::before { opacity: 1; }
/* Four corner handles — all resize symmetrically from the centre. */
.ss-handle {
  position: absolute;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.25);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.ss-handle-nw { left: -7px; top: -7px; cursor: nwse-resize; }
.ss-handle-ne { right: -7px; top: -7px; cursor: nesw-resize; }
.ss-handle-sw { left: -7px; bottom: -7px; cursor: nesw-resize; }
.ss-handle-se { right: -7px; bottom: -7px; cursor: nwse-resize; }

/* --- Columns / control rows --- */
.ss-crop-col { min-width: 0; }
.ss-control-row { display: flex; flex-direction: column; }
.ss-controls-col { display: flex; flex-direction: column; gap: 18px; }
.ss-control-label { display: block; font-size: 13px; font-weight: 600; color: var(--text-2); margin-bottom: 8px; }
.ss-preview-block { display: flex; flex-direction: column; align-items: flex-start; }
.ss-tile {
  width: 104px; height: 104px;
  border-radius: 23px;
  overflow: hidden;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}
.ss-tile canvas { display: block; width: 100%; height: 100%; }
.ss-grad-row { padding: 6px 0; border: 0; }

/* --- Color: clickable swatch + custom HSV popover (no r/g/b fields) --- */
.ss-color { position: relative; }
.ss-swatch {
  width: 100%; height: 38px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  cursor: pointer;
  background: #000;
  box-shadow: inset 0 0 0 2px var(--surface);
}
.ss-cp {
  /* position:fixed (coords set in JS from the swatch) so the modal's
     overflow:auto can't clip it; above the modal overlay (z-index 300). */
  position: fixed;
  width: 212px;
  z-index: 360;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: 0 12px 30px rgba(8, 10, 16, 0.3);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ss-cp[hidden] { display: none; }
.ss-cp-sv {
  position: relative;
  width: 100%; height: 132px;
  border-radius: 8px;
  cursor: crosshair;
  touch-action: none;
}
.ss-cp-sv-thumb {
  position: absolute;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.ss-cp-hue {
  position: relative;
  width: 100%; height: 14px;
  border-radius: 7px;
  cursor: pointer;
  touch-action: none;
  background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.ss-cp-hue-thumb {
  position: absolute; top: 50%;
  width: 6px; height: 20px;
  border-radius: 4px;
  background: #fff;
  border: 1px solid rgba(0,0,0,0.35);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.ss-cp-hex-row {
  display: flex; align-items: center; gap: 4px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  padding: 6px 10px;
}
.ss-cp-hash { color: var(--text-3); font-size: 13px; }
.ss-cp-hex {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: transparent;
  color: var(--text); font-size: 13px;
  text-transform: lowercase; font-family: inherit;
}


/* Bokun-connection section (Settings tab) — status banner for the
   POST-redirect result. Form fields reuse the existing field classes. */
.bokun-banner { font-size: 13px; border-radius: 8px; padding: 10px 12px; margin: 0 0 14px; }
.bokun-banner--ok  { color: var(--ok, #7fc97f); background: rgba(110, 200, 110, 0.08); border: 1px solid rgba(110, 200, 110, 0.25); }
.bokun-banner--err { color: var(--danger, #ff8a8a); background: rgba(255, 90, 90, 0.08); border: 1px solid rgba(255, 90, 90, 0.25); }
.bokun-conn-form .field-label { margin-top: 12px; }

/* Testing-account banner: its rules live in testing-banner.css since
   2026-10-23, linked by admin.js testingBannerHtml() on every page that shows
   it. */
