/* ============================================
   Rogue AI Portal — Design System
   Ported from rogueai.tech for visual consistency
   ============================================ */

/* --- CSS Custom Properties (Design Tokens) --- */
:root {
  /* Brand Colors */
  --brand-blue: #33A1FD;
  --brand-blue-dark: #2B8AD9;
  --brand-blue-btn: #1478BD;
  --brand-blue-btn-hover: #0D6EAE;

  /* Backgrounds */
  --bg-primary: #0A0E17;
  --bg-secondary: #111827;
  --bg-tertiary: #1A2332;
  --bg-elevated: #1E293B;

  /* Text */
  --text-primary: #FFFFFF;
  --text-secondary: #94A3B8;
  --text-muted: #64748B;
  --text: var(--text-primary);

  /* Accents */
  --success: #10B981;
  --error: #EF4444;
  --warning: #F59E0B;
  --info: #33A1FD;

  /* Effects */
  --glow-subtle: rgba(51, 161, 253, 0.15);
  --glow-medium: rgba(51, 161, 253, 0.25);
  --glow-strong: rgba(51, 161, 253, 0.3);
  --border-default: rgba(255, 255, 255, 0.08);
  --border-hover: rgba(255, 255, 255, 0.15);
  --border-active: rgba(51, 161, 253, 0.4);

  /* Spacing */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* Border Radius */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 400ms ease;
  --tooltip-fade: 100ms ease;
  --tooltip-max-width: 280px;
  --z-tooltip: 10001;

  /* Typography */
  --font-family: 'Inter', system-ui, -apple-system, sans-serif;
  --text-body: 16px;
  --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  --text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
  --text-base: clamp(1rem, 0.925rem + 0.375vw, 1.125rem);
  --text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.375rem);
  --text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.625rem);
  --text-2xl: clamp(1.5rem, 1.25rem + 1.25vw, 2.25rem);
  --text-3xl: clamp(1.875rem, 1.5rem + 1.875vw, 3rem);

  /* Layout */
  --nav-height: 56px;
  --content-max-width: 1200px;
}

/* --- Reset --- */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* --- Base --- */
html, body {
  height: 100%;
  font-family: var(--font-family);
  font-size: var(--text-body);
  line-height: 1.6;
  color: var(--text-secondary);
  background: var(--bg-primary);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: var(--brand-blue);
  text-decoration: none;
  transition: color var(--transition-fast);
}
a:hover {
  color: var(--brand-blue-dark);
}

h1, h2, h3, h4, h5, h6 {
  color: var(--text-primary);
  line-height: 1.2;
  font-weight: 600;
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }

/* --- Layout: App Shell --- */
.app-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

/* --- Top Nav Bar --- */
.top-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--nav-height);
  min-height: var(--nav-height);
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border-default);
  padding: 0 var(--space-5);
  position: sticky;
  top: 0;
  z-index: 100;
}

.nav-left {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.nav-logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.nav-logo-img {
  height: 28px;
  width: auto;
}

/* --- Nav item band ---
   FIXED 2026-08-06 (LANE K). This was `position:absolute; left:50%; translateX(-50%)` with no width
   constraint, so it was removed from the flex flow entirely and grew UNDER .nav-left and
   .nav-right instead of pushing against them. Measured at 1440px as an admin (12 items): the logo
   occupied x=20–220 while the first item started at x=35 (185px overlap) and the last item ended at
   x=1405 against a .nav-right starting at x=1240 (165px overlap) — "Dashboard" and "Training" were
   illegible under the logo and "Settings" sat under the user chip. Pre-existing, not caused by the
   Settings item: hiding it still left 134px/99px of overlap.

   The fix is to put the band back IN the flow as the flexible middle child, so the two fixed-width
   siblings reserve their space first and the band can only ever use what is left. It stays visually
   centred within that space. When the items genuinely do not fit, .nav-items scrolls horizontally
   rather than sliding underneath its neighbours — a strip that scrolls is legible; one that overlaps
   is not. The ≤768px block below replaces `position` outright with the fixed drop-down panel, so
   the mobile nav is unaffected by any of this. */
.nav-center {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 var(--space-3);
}

.nav-items {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: nowrap;
  max-width: 100%;
  overflow-x: auto;
  /* The horizontal scrollbar would otherwise eat visible height out of a 56px bar and render as a
     grey stripe across the chrome. Keyboard users still reach every item — .nav-item:focus-visible
     scrolls it into view — and pointer users can wheel/swipe the strip. */
  scrollbar-width: none;
  scrollbar-color: var(--text-muted) var(--bg-secondary);
  -ms-overflow-style: none;
}

.nav-items::-webkit-scrollbar { display: none; }

/* The active-tab underline is `bottom:-13px`, i.e. it sits OUTSIDE the item's own box. Once
   .nav-items can scroll, that overflow would be clipped and the underline would disappear on the
   active tab. Padding the strip and pulling it back keeps the underline inside the scroll box. */
.nav-items { padding-bottom: 14px; margin-bottom: -14px; }

/* The desktop tightening tier that buys width back before this strip has to scroll lives BELOW,
   just above the mobile block — it has to come AFTER the base `.nav-item` / `.nav-user-info`
   declarations to win the cascade. Putting it here (where it was first written) made it dead. */

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: 6px;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition-fast);
  user-select: none;
  position: relative;
  white-space: nowrap;
}

.nav-item:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}

.nav-item.active {
  color: var(--brand-blue);
  background: transparent;
}

.nav-item.active::after {
  content: '';
  position: absolute;
  bottom: -13px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--brand-blue);
  border-radius: 2px 2px 0 0;
}

.nav-item-icon {
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.nav-item-icon svg {
  width: 18px;
  height: 18px;
}

.nav-right {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* User button & dropdown */
.nav-user {
  position: relative;
}

.nav-user-btn {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: transparent;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: 6px;
  transition: background var(--transition-fast);
  font-family: var(--font-family);
}

.nav-user-btn:hover {
  background: var(--bg-tertiary);
}

.nav-user-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--glow-medium);
  color: var(--brand-blue);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  font-weight: 600;
  flex-shrink: 0;
}

.nav-user-info {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
}

.nav-user-name {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.2;
}

.nav-user-loc {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.2;
}

.nav-user-chevron {
  display: flex;
  align-items: center;
  color: var(--text-muted);
}

.nav-user-chevron svg {
  width: 16px;
  height: 16px;
}

.nav-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 200px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  z-index: 200;
}

.nav-dropdown-header {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-default);
}

.nav-dropdown-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.nav-dropdown-loc {
  font-size: var(--text-xs);
  /* V3 (design QA, layer BE): --text-muted #64748B on the dropdown's --bg-elevated #1E293B is 3.07:1 at
     14px -- an axe color-contrast fail (serious) on an element THIS build added. Measured against
     .nav-dropdown's own composited background: --text-muted 3.07 (FAIL) < --text-secondary 5.71 (pass)
     < --text-primary 14.63 (pass). --text-secondary is the DARKEST existing token that clears 4.5:1, and
     it is what the sibling secondary lines already use (.nav-dropdown-item, .ls-hint, .ls-who after
     P3-32) -- so no new token, and the hierarchy against .nav-dropdown-name (--text-primary) is kept. */
  color: var(--text-secondary);
}

.nav-dropdown-divider {
  height: 1px;
  background: var(--border-default);
}

.nav-dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: transparent;
  border: none;
  color: var(--text-secondary);
  font-family: var(--font-family);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.nav-dropdown-item:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}

/* Hamburger (mobile only) */
.nav-hamburger {
  font-family: var(--font-family);
  display: none;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: var(--space-2);
  border-radius: 6px;
  margin-right: var(--space-2);
}

.nav-hamburger:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}

/* Main content area */
.main-content {
  scrollbar-width: thin;
  scrollbar-color: var(--text-muted) var(--bg-secondary);
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  scroll-behavior: smooth;
}

.content-body {
  padding: var(--space-8);
  flex: 1;
  max-width: var(--content-max-width);
  width: 100%;
}

/* --- Narrow-desktop tightening (LANE K, 2026-08-06) ---
   Buys width back before .nav-items has to scroll. Measured at 1440px as an admin (11 items): the
   strip needs 1288px against a 988px band between the logo and the user chip; dropping the item
   padding to 8px and the user chip's name/location text raises the band and shrinks the strip.
   Enough for the 7-item GM nav at every width; an 11-item admin nav at 1440px still scrolls, which
   is a content-density decision, not a CSS one — see the residual note below.

   ⚠️ POSITION IS LOAD-BEARING. This block must stay AFTER the base `.nav-item` and `.nav-user-info`
   declarations (~line 171 / ~line 265) and BEFORE the ≤768px block. It was first written up beside
   `.nav-center`, i.e. ABOVE both — same specificity, earlier in document order, so the base rules
   won and the whole tier was DEAD while looking perfectly correct in the file and in the CSSOM
   (matchMedia said it matched; the computed padding never changed). Verified in a real browser by
   reading getComputedStyle, not by reading the file. */
@media (min-width: 769px) and (max-width: 1500px) {
  .nav-item { padding: var(--space-2) var(--space-2); }
  .nav-user-info { display: none; }
  /* The icons are what actually decide it. Measured at 1440px, 11 admin items: padding + the user
     chip's text alone leave the strip 111px short and clip "Analytics" and "Settings"; dropping the
     18px icon and its 8px gap takes the strip from 1200px to 914px inside a 1089px band — every
     item visible with ~175px spare, i.e. room for two more before it scrolls again. A text-only nav
     below 1500px is a density trade made deliberately: an invisible nav item at the commonest
     laptop width is a usability defect, a missing decorative glyph is not. */
  .nav-item-icon { display: none; }
}

/* ⚠️ RESIDUAL, MEASURED NOT ASSUMED (LANE K, 2026-08-06): at 1440px an ADMIN has 11 nav items that
   need more strip than the band between logo and user chip provides, so the last items sit
   off-screen inside the scroll container rather than under the user chip. That is strictly better
   than the overlap it replaces — nothing is illegible and every item is still reachable by
   scrolling the strip — but it is degraded gracefully, not solved. Closing it properly means fewer
   top-level items or an overflow menu (a product decision), or scrolling the ACTIVE item into view
   on render, which needs public/js/app.js — a file this lane does not own. Both are in the report. */

/* --- Mobile Responsive (Top Nav) --- */
@media (max-width: 768px) {
  .nav-hamburger {
    display: flex;
    align-items: center;
  }

  .nav-center {
    position: fixed;
    top: var(--nav-height);
    left: 0;
    right: 0;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border-default);
    flex-direction: column;
    align-items: stretch;
    padding: var(--space-3) var(--space-4);
    gap: var(--space-1);
    display: none;
    transform: none;
    z-index: 99;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  }

  .nav-center-open {
    display: flex;
  }

  .nav-items {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-1);
    /* Undo the desktop scroll-strip. This is a vertical drop-down panel, the active underline is
       hidden below, and the negative bottom margin would clip its last item. */
    overflow-x: visible;
    padding-bottom: 0;
    margin-bottom: 0;
  }

  .nav-item {
    padding: var(--space-3) var(--space-4);
  }

  .nav-item.active::after {
    display: none;
  }

  .nav-item.active {
    background: var(--glow-medium);
  }

  .nav-user-info {
    display: none;
  }

  .nav-user-chevron {
    display: none;
  }

  .content-body {
    padding: var(--space-4);
  }
}

/* --- Glass Card --- */
.card {
  background: rgba(17, 24, 39, 0.8);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  transition: border-color var(--transition-base);
}

.card:hover {
  border-color: var(--border-hover);
}

.card-compact {
  padding: var(--space-4);
  border-radius: var(--radius-md);
}

/* --- Buttons --- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-sm);
  font-family: var(--font-family);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  border: none;
  transition: all var(--transition-base);
  min-height: 40px;
  line-height: 1;
}

.btn-primary {
  background: var(--brand-blue-btn);
  color: white;
  box-shadow: 0 0 20px rgba(51, 161, 253, 0.15);
}
.btn-primary:hover {
  background: var(--brand-blue-btn-hover);
  box-shadow: 0 0 30px rgba(51, 161, 253, 0.25);
  transform: translateY(-1px);
}

.btn-outline {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-hover);
}
.btn-outline:hover {
  border-color: var(--brand-blue);
  color: var(--brand-blue);
  background: var(--glow-subtle);
}

.btn-secondary {
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  border: 1px solid var(--border-default);
}
.btn-secondary:hover {
  background: var(--border-default);
  color: var(--text-primary);
}

.btn-sm {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
  min-height: 32px;
}

.btn-danger {
  background: var(--error);
  color: white;
}
.btn-danger:hover {
  background: #DC2626;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

/* Focus visible for keyboard navigation */
.btn:focus-visible,
.form-input:focus-visible,
.form-textarea:focus-visible,
.nav-item:focus-visible,
a[href]:not([aria-disabled="true"]):focus-visible,
.bf-tab:not(:disabled):focus-visible,
.nav-user-btn:not(:disabled):focus-visible,
.nav-hamburger:not(:disabled):focus-visible {
  outline: 2px solid var(--brand-blue);
  outline-offset: 2px;
}

/* --- Forms --- */
.form-group {
  margin-bottom: var(--space-5);
}

.form-label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.form-input,
.form-textarea,
.form-select {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: var(--text-sm);
  transition: border-color var(--transition-fast);
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 3px var(--glow-subtle);
}

.form-textarea {
  min-height: 120px;
  resize: vertical;
}

/* g3c-03 (7.1b walk W-9): a multi-select list box takes the same tokens as .form-select — a native light list on the dark
   card was the Kennels pool editor at SBR, 2026-09-07. Options are styled too: Chromium paints them from UA defaults otherwise. */
select.form-input[multiple] {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  font-family: var(--font-family);
  font-size: var(--text-sm);
  min-height: 7.5em;
}
select.form-input[multiple] option {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}
select.form-input[multiple] option:checked {
  background: var(--brand-blue);
  color: var(--text-primary);
}
select.form-input[multiple]:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 3px var(--glow-subtle);
}


.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--text-muted);
}

/* --- Status Badges --- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1;
}

.badge-new { background: rgba(33, 150, 243, 0.15); color: #2196F3; }
.badge-open { background: rgba(255, 152, 0, 0.15); color: #FF9800; }
.badge-in-progress { background: rgba(156, 39, 176, 0.15); color: #9C27B0; }
.badge-pending { background: rgba(96, 125, 139, 0.15); color: #607D8B; }
.badge-resolved { background: rgba(76, 175, 80, 0.15); color: #4CAF50; }
.badge-closed { background: rgba(158, 158, 158, 0.15); color: #9E9E9E; }

/* --- Tables --- */
.table-wrap {
  overflow-x: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
}

th {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-bottom: 1px solid var(--border-default);
}

td {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  border-bottom: 1px solid var(--border-default);
}

tr:hover td {
  background: var(--glow-subtle);
}

/* --- Grid Layouts --- */
.grid {
  display: grid;
  gap: var(--space-6);
}

.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Settings hub (ROADMAP P2-17) — the top-right gear + menu, the hub chrome, and the .ls-* rules
   MIGRATED VERBATIM from the Lane F page's injected <style> (D4; portal-styling.md rule 2).
   Tokens only. Placed after the base .form-* rules and before the 768px tier below.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
.nav-settings-btn { padding: var(--space-1) var(--space-2); min-width: 40px; min-height: 40px; justify-content: center; } /* desktop: the same 40px box as .nav-user-btn (UX-1 parity); the 44px touch floor applies at the mobile tier below */
.nav-settings-btn svg { width: 20px; height: 20px; }
.nav-settings-btn[aria-expanded="true"] { background: var(--bg-tertiary); color: var(--text-primary); }
.nav-settings-btn:focus-visible, .nav-dropdown-item:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.nav-settings-dropdown { min-width: 220px; }
.nav-settings-dropdown .nav-dropdown-item { text-decoration: none; min-height: 44px; }
.nav-settings-dropdown .nav-dropdown-item.active,
.nav-settings-dropdown .nav-dropdown-item[aria-current="page"] { color: var(--text-primary); background: var(--bg-tertiary); box-shadow: inset 3px 0 0 var(--brand-blue); }

.settings-view { max-width: 980px; margin: 0 auto; }
/* Pilot table links use the same fixed body text size at every viewport width. */
[data-settings-v3-shell="1"] .ls-table .ls-link,
[data-settings-v3-ui="1"] .ls-table .ls-link { font-size: var(--text-body); }
.settings-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-6); }
.settings-head .page-title { margin-bottom: var(--space-1); }
.settings-picker { max-width: 360px; margin-bottom: 0; flex: 1 1 260px; }
.settings-tabs { margin-bottom: var(--space-6); }
.settings-tabs .bf-tab:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--space-4) var(--space-6); }
.settings-grid .form-group { margin-bottom: 0; min-width: 0; }
.settings-view .card, .settings-view .ls-disclosure-btn, .settings-view .form-group { scroll-margin-bottom: calc(var(--space-16) + var(--space-6)); }
.settings-savebar { position: sticky; bottom: 0; display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; padding: var(--space-4) 0; margin-top: var(--space-6); background: var(--bg-primary); border-top: 1px solid var(--border-default); z-index: 5; }
.settings-dirty { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--warning); font-size: var(--text-sm); }
.settings-dirty::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--warning); }
/* 🔴 An author `display:` BEATS the UA stylesheet's `[hidden] { display: none }`, so the `hidden`
   attribute is INERT on any element this file gives a display to. The save bar toggles visibility
   entirely through `hidden` (settings.js updateSavebar / render), so without these rules the
   "Discard changes" button and the unsaved-changes badge stay on screen on a CLEAN page and the
   whole bar stays up on a read-only one. Measured by Gate B P3-03 2026-09-03: the register gates
   `guard.unsaved-changes-*` on "the page has unsaved changes", and it FAILed control.absent-at-rest
   on settings-{grooming-capacity,notifications,store-profile,lifecycle} at BOTH viewports with
   `nodes=1 any_visible=true` while the page was clean. The same trap is already patched three times
   in this file (.chat-asset-preview, .chat-selection-chip, .bf-accordion-content) — this is the
   general form, so a control marked hidden is hidden. */
.settings-savebar[hidden], .settings-dirty[hidden], .btn[hidden] { display: none; }
.settings-modal-copy { color: var(--text-secondary); margin: 0; }

.ls-view { max-width: 980px; margin: 0 auto; }
.ls-head { margin-bottom: var(--space-6); }
.ls-picker { max-width: 360px; }
.ls-tz-note { color: var(--text-secondary); font-size: var(--text-sm); margin: 0 0 var(--space-6); }
.ls-card { margin-bottom: var(--space-6); }
.ls-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.ls-h2 { font-size: var(--text-lg); margin: 0 0 var(--space-2); }
.ls-card-note { margin: 0 0 var(--space-5); }
.ls-muted { color: var(--text-secondary); font-size: var(--text-sm); }
.ls-hint { color: var(--text-secondary); font-size: var(--text-xs); margin: var(--space-2) 0 0; }
.ls-who { color: var(--text-secondary); font-size: var(--text-xs); margin: var(--space-1) 0 0; } /* P3-32: --text-muted #64748B is 3.80:1 on the composited .card background (#101624) at --text-xs (12.2-14px, normal weight => the 4.5:1 tier), an axe color-contrast fail. --text-secondary #94A3B8 is 7.05:1 and is the darkest EXISTING token in the hierarchy that passes; it is already what the sibling .ls-hint/.ls-lock lines use, so no new token. */
.ls-lock { color: var(--text-secondary); font-size: var(--text-xs); margin: var(--space-1) 0 0; }
.ls-derived { color: var(--text-secondary); font-size: var(--text-sm); margin: var(--space-2) 0 0; }
.ls-sub { margin-left: var(--space-6); padding-left: var(--space-4); border-left: 1px solid var(--border-default); }
.ls-textarea { width: 100%; min-height: 96px; resize: vertical; font-family: var(--font-family); line-height: 1.5; margin-top: var(--space-2); }
.ls-sub .form-input:disabled, .ls-view .form-input:disabled, .settings-view .form-input:disabled { opacity: .55; cursor: not-allowed; }
.ls-link { color: var(--brand-blue); text-decoration: none; }
/* g3c-01 (W-4): a raw URL in a hint or link (the branding emblem, a MyTime website) must wrap inside its card at 390 px —
   measured 2026-09-07: `a.ls-link` reached x=397 in a 390 viewport and drew a horizontal scrollbar on .main-content. */
.ls-hint, .ls-muted, a.ls-link { overflow-wrap: anywhere; }
.ls-link:hover { text-decoration: underline; }
.ls-error { color: var(--error); }
.ls-hours { display: flex; flex-direction: column; gap: var(--space-3); }
.ls-hours-row { padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-sm); background: var(--bg-secondary); }
.ls-hours-name { font-weight: 600; margin-bottom: var(--space-3); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.ls-hours-fields { display: flex; gap: var(--space-4); flex-wrap: wrap; align-items: flex-end; }
.ls-field { flex: 1 1 160px; margin-bottom: 0; min-width: 0; }
.ls-field--action { flex: 0 0 auto; }
/* Native time/date inputs inherit .form-input's box; color-scheme makes the browser's own picker render dark. */
.ls-time, .ls-date { color-scheme: dark; }
.ls-actions { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-5); flex-wrap: wrap; }
.ls-status { font-size: var(--text-sm); min-height: 1em; }
.ls-status--ok { color: var(--success); }
.ls-status--error { color: var(--error); }
.ls-status--warn { color: var(--warning); }
.ls-banner { padding: var(--space-4); border-radius: var(--radius-sm); font-size: var(--text-sm); margin-bottom: var(--space-5); }
.ls-banner--warn { background: var(--bg-tertiary); border: 1px solid var(--warning); color: var(--warning); }
.ls-banner--error { background: var(--bg-tertiary); border: 1px solid var(--error); color: var(--error); }
.ls-banner--info { background: var(--bg-tertiary); border: 1px solid var(--border-active); color: var(--text-secondary); }
.ls-banner code { font-size: var(--text-xs); word-break: break-all; }
/* R59 — the auto-book readiness block inside an .ls-banner (BI2 L-CONTROL, 2026-09-05).
   No new colours: the banner already carries the state colour, so these only set rhythm, and every
   value is a token (~/.claude/rules/ui-consistency.md rule 2). The head line reuses the .ls-badge
   chip family so READY / NEEDS CONFIGURATION reads as the same pill family as every other chip on
   the page. */
.ls-readiness { margin-bottom: var(--space-5); }
.ls-readiness .ls-banner { margin-bottom: 0; }
.ls-readiness-head { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-2); }
.ls-readiness-list { margin: var(--space-2) 0 0; padding-left: var(--space-5); }
.ls-readiness-list li { margin-top: var(--space-1); }
/* R59 — the kennel setup grid (BI2 L-CONTROL, 2026-09-06). It is an .ls-matrix table, so borders,
   padding and header weight all come from that family; these rules only lay out the tick clusters
   inside a cell and keep a long pool name from squashing them. Every value is a token
   (~/.claude/rules/ui-consistency.md rule 2). */
.ls-kennel-setup th[scope="row"] { white-space: nowrap; vertical-align: top; }
.ls-kennel-setup th[scope="row"] .ls-hint { display: block; margin: var(--space-1) 0 0; font-weight: 400; }
.ls-kennel-setup td { vertical-align: top; }
.ls-tick { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0 var(--space-3) var(--space-2) 0; white-space: nowrap; font-size: var(--text-sm); }
.ls-tick:last-child { margin-right: 0; }
.ls-banner .btn { margin-left: var(--space-3); }
.ls-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.ls-chip { display: inline-flex; align-items: center; gap: var(--space-2); background: var(--bg-tertiary); color: var(--text-primary); padding: var(--space-1) var(--space-3); border-radius: 999px; font-size: var(--text-xs); }
.ls-chip-x { font-family: var(--font-family); background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: var(--text-sm); line-height: 1; padding: 2px; min-width: 24px; min-height: 24px; display: inline-flex; align-items: center; justify-content: center; }
.ls-chip-x:hover { color: var(--error); }
.ls-chip-x:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.ls-badge { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-3); border-radius: 999px; font-size: var(--text-xs); font-weight: 500; line-height: 1; }
.ls-badge--muted { background: var(--bg-tertiary); color: var(--text-secondary); }
.ls-badge--set { background: var(--glow-subtle); color: var(--brand-blue); }
.ls-badge--warn { background: var(--bg-tertiary); color: var(--warning); }
.ls-badge--ok { background: var(--bg-tertiary); color: var(--success); }
.ls-add { display: flex; gap: var(--space-2); align-items: center; max-width: 420px; }
.ls-add .form-input { flex: 1; min-width: 0; }
.ls-check-label { display: flex; align-items: flex-start; gap: var(--space-3); cursor: pointer; font-weight: 400; }
.ls-check { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; accent-color: var(--brand-blue); cursor: pointer; }
.ls-check:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.ls-check:disabled { cursor: not-allowed; opacity: .55; }
/* -- D4 (design QA, layer BE) . PAINT the control at EVERY width, not only <=560px ---------------
   P3-41(b) handed the paint to us inside the `@media (max-width: 560px)` tier only, so at >=1024 an
   unchecked box was still `appearance: auto` and Chromium painted it from the UA colour scheme: a solid
   WHITE square on the portal's dark chrome (measured before/after 2026-09-03 -- `appearance: auto`,
   `background-color: rgba(0,0,0,0)`, i.e. the computed value cannot even SEE the paint, which is why a
   style read missed it and a screenshot did not). Exactly the "white control on dark chrome" defect
   ~/.claude/rules/ui-consistency.md rule 4 names.
   The BOX does not move: `.ls-check` is 18x18 at desktop (measured), and 18x18 is restated here, so every
   rect on all seven pages is byte-identical before/after -- the change is paint only. The selector list is
   the SAME triple the 44px tier uses, so nothing can get one treatment without the other; the <=560 tier
   sits BELOW this block and overrides it with the 44px box + 12px transparent border (document order is
   what decides at equal specificity in this flat stylesheet).
   The tick SVG is the SAME data-URI P3-41(b) introduced and carries the one recorded literal on this
   surface (`%23fff` = --text-primary = #FFFFFF): a data-URI cannot read a custom property. */
.settings-view input[type="checkbox"],
.settings-view input[type="radio"],
.settings-view .ls-check {
  font-family: var(--font-family);
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box; width: 18px; height: 18px;
  background-color: var(--bg-tertiary);
  background-repeat: no-repeat; background-position: center; background-size: 100% 100%;
  box-shadow: inset 0 0 0 1px var(--border-hover);
  transition: background-color var(--transition-fast), box-shadow var(--transition-fast);
}
.settings-view input[type="checkbox"] { border-radius: 4px; }
.settings-view input[type="radio"] { border-radius: 50%; }
.settings-view input[type="checkbox"]:checked,
.settings-view input[type="radio"]:checked,
.settings-view .ls-check:checked { background-color: var(--brand-blue); box-shadow: inset 0 0 0 1px var(--brand-blue); }
.settings-view input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M4.75 10.25 8.5 14l6.75-7'/%3E%3C/svg%3E");
}
.settings-view input[type="radio"]:checked { background-image: radial-gradient(circle at center, var(--text-primary) 0 3.5px, transparent 3.6px); }
.settings-view input[type="checkbox"]:disabled,
.settings-view input[type="radio"]:disabled,
.settings-view .ls-check:disabled { cursor: not-allowed; opacity: .55; }
.ls-radios { display: flex; flex-direction: column; gap: var(--space-3); }
.ls-radio-row { padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-sm); background: var(--bg-secondary); transition: border-color var(--transition-fast), background var(--transition-fast); }
.ls-radio-row:hover { border-color: var(--border-hover); }
.ls-radio-row--on, .ls-radio-row--active { border-color: var(--border-active); background: var(--bg-tertiary); }
.ls-radio-label { display: block; font-weight: 600; color: var(--text-primary); }
.ls-radio-hint { display: block; margin-top: var(--space-1); }
.ls-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.ls-inline { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.ls-num { max-width: 160px; }
.ls-kv { display: grid; grid-template-columns: minmax(160px, 1fr) 2fr; gap: var(--space-2) var(--space-4); font-size: var(--text-sm); margin: 0; }
.ls-kv dt { color: var(--text-secondary); }
.ls-kv dd { margin: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.ls-matrix { width: 100%; border-collapse: collapse; }
.ls-matrix th, .ls-matrix td { padding: var(--space-2) var(--space-3); text-align: left; border-bottom: 1px solid var(--border-default); vertical-align: top; font-size: var(--text-sm); }
.ls-matrix th { color: var(--text-secondary); font-weight: 500; }
/* -- D2 (design QA, layer BE) . a matrix ROW HEADER is a label PLUS a sentence, never one uppercase run
   notifications.js printed `<th scope="row">Label<span class="ls-hint">help</span></th>`, and this file's
   global `th { text-transform: uppercase; letter-spacing: .05em; font-size: var(--text-xs) }` (:687) then
   uppercased the WHOLE cell with no separator, so six rows rendered on production as
   `NEW LEADA LEAD FORM OR A TRAINING ENQUIRY LANDS.` (measured 2026-09-03, both runs at the same colour).
   `.ls-hint`'s `margin: var(--space-2) 0 0` was INERT because a margin-top does nothing on an inline box.
   The th now carries two BLOCK children and the uppercase/tracking is switched off for the ROW header
   only -- the column headers keep the shared `th` chrome, which is the styling family they belong to.
   The label takes the surface's own label recipe (`.form-label`: block, --text-sm, 500, --text-primary =
   the same 500 weight `.ls-matrix th` already renders), so the name is separable from its description by
   colour AND size, not by position alone. No new token, no literal.
   The un-transform is set on the two CHILDREN, not on `th[scope="row"]`: `text-transform` and
   `letter-spacing` INHERIT, so overriding them on the label and the help covers every text node in the
   cell (the th now has no text node of its own -- asserted in the jsdom test), while a th-level rule
   would also have caught the grooming coat matrix's single-word band headers, which are NOT this defect
   and whose rects must not move (measured: a th-level rule shifted 24 rects on grooming-capacity). */
.ls-matrix-label { display: block; text-transform: none; letter-spacing: normal; font-weight: 500; color: var(--text-primary); }
.ls-matrix-help { display: block; text-transform: none; letter-spacing: normal; margin-top: var(--space-1); }
/* ── P3-46(a) · the notifications matrix owns its horizontal scroll, from the keyboard too ─────────
   Measured at 320×640 (400 % zoom): the `.ls-matrix` table (21 td, 15 badges, 15 buttons) had 70
   elements with right edges up to 621 px inside a 320 px viewport, body scrollWidth 320 (the page did
   not scroll sideways) and the content was simply CLIPPED — WCAG 1.4.10. `.table-wrap` declares
   overflow-x:auto for a POINTER; a scroll container is operable from the keyboard only when it can take
   focus, so notifications.js marks the wrapper tabindex=0 + role=region + a name, and this rule gives
   that focus the surface's standard ring. `min-width: 0` + `max-width: 100%` let the flex/grid ancestors
   shrink so the WRAPPER, never the page, is the element that overflows. Tokens only, no literal. */
.ls-scroll-x { overflow-x: auto; max-width: 100%; min-width: 0; -webkit-overflow-scrolling: touch; }
.ls-scroll-x:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.ls-disclosure-btn { display: flex; width: 100%; justify-content: space-between; align-items: center; gap: var(--space-3); background: transparent; border: none; color: var(--text-primary); font: inherit; font-weight: 600; cursor: pointer; padding: 0; text-align: left; min-height: 44px; }
.ls-disclosure-btn svg { width: 16px; height: 16px; transition: transform var(--transition-fast); }
.ls-disclosure-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.ls-list { margin: 0; padding-left: var(--space-5); color: var(--text-secondary); font-size: var(--text-sm); }
.form-input[aria-invalid="true"] { border-color: var(--error); }
.ls-field-error { color: var(--error); font-size: var(--text-xs); margin: var(--space-1) 0 0; }
/* ── P3-41(a) · a long unbreakable MyTime value must WRAP INSIDE its grid cell ────────────────
   store-profile.js:60 prints the MyTime value as `<p class="ls-muted" id="ls-ov-<col>-mytime">`,
   and for website/facebook_url/google_url that value is a URL — ONE unbreakable token. `min-width:0`
   (already on `.settings-grid .form-group` and `.ls-field`) stops the token widening the TRACK, but
   it does not stop it PAINTING outside the cell, so on production it ran over the neighbouring cell
   and pushed `.main-content` to scrollWidth 1121/1024 and 434/390 (a real horizontal scrollbar
   inside the embed). Reproduced on the preview with the production strings: 1122/1024, 437/390, and
   the Website value painting 62px into the Facebook cell at 1440. `overflow-wrap: anywhere` is the
   operative rule — unlike `break-word` it also reduces the MIN-CONTENT width, so the token can never
   size the cell either; `word-break: break-word` is the fallback for engines that lack `anywhere`.
   `min-width: 0` is restated on every grid child (not just `.form-group`) so a cell that is not a
   form-group cannot reintroduce the same overflow. */
.settings-grid > * { min-width: 0; }
.settings-view .ls-field p.ls-muted,
.ls-view .ls-field p.ls-muted { min-width: 0; word-break: break-word; overflow-wrap: anywhere; }
/* -- D5 (design QA, layer BE) . the page-level warn banner spans the page column, like every card ---
   `.editor-warn-banner` (:2160) is the Pluto editor's standalone banner and carries `max-width: 720px;
   margin: 24px auto` -- correct where it is the ONLY thing on screen, wrong as the first block of a
   settings page: on grooming-capacity it measured x 360->1080 (720 wide, centred) while every `.card`
   on the same page measured 230->1210 (980 wide), i.e. 130px of inset on each side belonging to no
   token step (measured 2026-09-03; 120px each side at 1024). Scoped to the settings surface so the
   editor's own usage is untouched; the vertical rhythm keeps the token step the cards use. */
.settings-view .editor-warn-banner { max-width: none; margin-left: 0; margin-right: 0; margin-bottom: var(--space-6); }
@media (max-width: 560px) {
  .nav-logo { min-height: 44px; }
  .ls-hours-fields { flex-direction: column; align-items: stretch; }
  .ls-field--action { width: 100%; }
  .ls-field--action .btn { width: 100%; }
  .ls-add { max-width: none; }
  .settings-head { flex-direction: column; align-items: stretch; }
  .ls-kv { grid-template-columns: 1fr; }
  .nav-settings-btn { min-width: 44px; min-height: 44px; }   /* U-TOU touch floor (register min_target_px 44) at the touch tier */
  /* ── TL-FIX-17 D-5 · the SHELL's own nav targets, once, for every page and both seats ───────
     VISUAL-AUDIT-4 measured `.nav-hamburger` 40x40 and `.nav-user-btn` 48x40 at 390 on all 13
     pages while `.nav-settings-btn` beside them was already 44x44 — the shell was inconsistent
     with itself as well as with the >=44px criterion. The box itself has to move: a padded
     wrapper or an outline does not change getBoundingClientRect(). Text size is unchanged. */
  .nav-hamburger,
  .nav-user-btn { min-width: 44px; min-height: 44px; justify-content: center; }
  /* ── P3-22 · coarse-pointer touch floor (44px) on the SETTINGS SURFACE ─────────────────────
     The register declares affordance.touch.min_target_px 44 on every settings control and the
     Gate B oracle asserts the RENDERED getBoundingClientRect(), never a declared style — so each
     rule below has to grow the element's OWN box. A padded wrapper, a ::before hit-area or an
     outline does NOT move the rect and would pass a floor the product does not implement.
     SCOPED to the settings surface on purpose: the portal-wide .btn-sm / .form-input / nav-shell
     boxes are a separate design ruling and are deliberately NOT changed here. No text size moves. */
  .settings-view .form-input,
  .settings-view .ls-textarea,
  .settings-view select,
  .settings-view textarea { min-height: 44px; }
  /* ── TL-FIX-17 D-5 · the floor has to OUTRANK a page's own injected style ───────────────────
     `.kn-v3 .form-input{min-height:var(--space-10)}` (kennels) and `.dr-field .form-input{…}`
     (dogs & records) are the same (0,2,0) specificity as the rule above and sit LATER in the
     cascade, so the 44px floor silently lost and those controls rendered 43px at 390. Those two
     declarations are removed at source in the same change; this doubled-class selector (0,3,0)
     is the structural guard, so no future page-level style can drop a settings control back
     below the touch floor without also beating this. */
  .settings-view.settings-view .form-input,
  .settings-view.settings-view .ls-textarea,
  .settings-view.settings-view select,
  .settings-view.settings-view textarea { min-height: 44px; }
  .settings-view .btn,
  .settings-view .btn-sm,
  .settings-view .bf-tab,
  .settings-view #settings-save,
  .settings-view #settings-discard,
  .settings-view #settings-restore-draft,
  [data-modal="discard-confirm"] .btn { min-height: 44px; }
  /* Native checkbox/radio: Chromium IGNORES padding AND border on an appearance:auto control —
     measured 2026-09-03 (width:18/height:18 with padding:13px, and with a 13px transparent border,
     both still rendered an 18x18 rect). The box itself is the only thing that moves the rect, so
     the floor is set on the input; the wrapping .ls-check-label grows with it. */
  .settings-view input[type="checkbox"],
  .settings-view input[type="radio"],
  .settings-view .ls-check { min-width: 44px; min-height: 44px; color-scheme: dark; }
  /* `color-scheme: dark` is REQUIRED here, not cosmetic: Chromium paints an appearance:auto
     checkbox from the UA colour scheme, so a 44px unchecked box renders as a WHITE SLAB on the
     portal's dark chrome (measured — the exact "white control on dark chrome" defect
     ~/.claude/rules/ui-consistency.md rule 4 names). Dark scheme paints it --bg-tertiary-grey
     unchecked and brand-blue-with-a-tick checked, and does not change the rect. The 18px control
     ABOVE this tier has the same UA-white unchecked box; that is pre-existing and out of scope
     here (the portal-wide control palette is a separate design ruling). Same mechanism as
     `.ls-time, .ls-date { color-scheme: dark }` above. */
  /* ── P3-41(b) · PAINT the control, do not just grow it ───────────────────────────────────
     The 44px floor above leaves the native `appearance: auto` control AS the hit area, so at
     <=560px Chromium renders a 44x44 checkbox — a large blank square (measured 2026-09-03:
     44x44, appearance auto, background none). `appearance: none` hands the paint to us: the
     BOX stays 44x44 so every touch-target row keeps passing, and a 12px TRANSPARENT BORDER
     with `background-clip: content-box` confines the paint to a 20px control centred in it
     (the desktop control is 18px). Chromium ignores padding/border on an appearance:AUTO
     control but honours both once appearance is none — that is the whole difference from the
     P3-22 note above. The rounded corners come out of the border-radius shrink rule
     (padding-box radius = border-box radius - border width): 16px - 12px = 4px for the
     checkbox, and 50% stays a circle for the radio. `box-shadow: inset` paints inside the
     padding box, i.e. exactly around the 20px control, which is how the 1px edge is drawn
     without a visible border on the 44px box. --border-hover (0.15 alpha), not --border-default
     (0.08), because a 1px edge at 0.08 on --bg-secondary is not visible.
     The tick is the ONLY literal colour in this change: a data-URI SVG cannot read a CSS custom
     property and `currentColor` does not resolve inside a background image, so `%23fff` (URL-
     encoded #fff) stands for --text-primary, which IS #FFFFFF. The radio's dot needs no SVG —
     a radial-gradient takes the token directly. Selector list matches the 44px rule above
     EXACTLY, so nothing can get the 44px box without also getting the new paint. */
  .settings-view input[type="checkbox"],
  .settings-view input[type="radio"],
  .settings-view .ls-check {
    -webkit-appearance: none; appearance: none;
    box-sizing: border-box; width: 44px; height: 44px;
    border: 12px solid transparent;
    background-color: var(--bg-tertiary);
    background-clip: content-box;
    background-repeat: no-repeat; background-position: center; background-size: 20px 20px;
    box-shadow: inset 0 0 0 1px var(--border-hover);
    transition: background-color var(--transition-fast), box-shadow var(--transition-fast);
  }
  .settings-view input[type="checkbox"] { border-radius: 16px; }
  .settings-view input[type="radio"] { border-radius: 50%; }
  .settings-view input[type="checkbox"]:checked,
  .settings-view input[type="radio"]:checked,
  .settings-view .ls-check:checked { background-color: var(--brand-blue); box-shadow: inset 0 0 0 1px var(--brand-blue); }
  .settings-view input[type="checkbox"]:checked {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M4.75 10.25 8.5 14l6.75-7'/%3E%3C/svg%3E");
  }
  .settings-view input[type="radio"]:checked { background-image: radial-gradient(circle at center, var(--text-primary) 0 3.5px, transparent 3.6px); }
  /* Match the shared focus ring around the full keyboard target. */
  .settings-view input[type="checkbox"]:focus-visible,
  .settings-view input[type="radio"]:focus-visible,
  .settings-view .ls-check:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
  .settings-view .ls-chip-x { min-width: 44px; min-height: 44px; }
  [data-settings-v3-shell="1"] .ls-dropdown-option,
  [data-settings-v3-ui="1"] .ls-dropdown-option { min-height: 44px; }
  .settings-view a.ls-link,
  .settings-view #settings-back { display: inline-flex; align-items: center; min-height: 44px; }
  [data-modal="discard-confirm"] .editor-modal-dismiss { min-width: 44px; min-height: 44px; }
  /* ── TL-FIX-21 E-1 · the Staff-notifications table reflows instead of being clipped ────────
     VISUAL-AUDIT-5: at 390 the three-column table was cut off at its container edge — the seat
     read `PER SERVI`, `one list for t`, `Edit for this type` wrapped to four lines in a ~75 px
     box, and the twelve per-service recipient inputs rendered 45x44 px beside an Add button
     reading `A`. Every prior gate passed it because the page-level `pageScrollsHorizontally`
     predicate reads FALSE correctly: the table scrolls INSIDE `div.ls-scroll-x`, which no
     instrument in the series measured.
     The reflow is the one `dogs-records` reads as at this width — one event per stacked block,
     the event name as the block's own heading, full-width controls, the Add button on its own
     line. SCOPED to `#ls-matrix-scroll` on purpose: `.ls-matrix` is also the kennel setup grid
     (R59), which is not this defect and is deliberately NOT changed. The `<thead>` is
     visually-hidden rather than removed, so the column names still reach a screen reader. */
  #ls-matrix-scroll { overflow-x: visible; }
  #ls-matrix-scroll .ls-matrix thead {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  #ls-matrix-scroll .ls-matrix,
  #ls-matrix-scroll .ls-matrix tbody,
  #ls-matrix-scroll .ls-matrix tr,
  #ls-matrix-scroll .ls-matrix th,
  #ls-matrix-scroll .ls-matrix td { display: block; width: 100%; min-width: 0; box-sizing: border-box; }
  #ls-matrix-scroll .ls-matrix tr {
    border-bottom: 1px solid var(--border-default);
    padding: var(--space-3) 0;
  }
  #ls-matrix-scroll .ls-matrix th,
  #ls-matrix-scroll .ls-matrix td { border-bottom: 0; padding: var(--space-2) 0; }
  #ls-matrix-scroll .ls-matrix th[scope="row"] { color: var(--text-primary); }
  /* The twelve per-service recipient editors: full width, never a 45 px box. */
  #ls-matrix-scroll .ls-matrix .form-input,
  #ls-matrix-scroll .ls-matrix .ls-chip-input { width: 100%; min-width: 0; box-sizing: border-box; }
  #ls-matrix-scroll .ls-matrix .ls-add { flex-wrap: wrap; max-width: none; }
  #ls-matrix-scroll .ls-matrix .ls-add .form-input { flex: 1 1 100%; }
  #ls-matrix-scroll .ls-matrix .ls-add .btn { flex: 1 1 100%; width: 100%; }
  #ls-matrix-scroll .ls-matrix .btn { white-space: normal; }
  #ls-matrix-scroll .ls-matrix .ls-matrix-editor { margin-top: var(--space-2); }
  /* ── TL-FIX-23 · the Dogs & records size ranges reflow instead of rendering 86 px boxes ────
     HUB-CAPTURE-8 §6 item 2: at 390, expanded, the size-range rows rendered `Range name` 107 px
     and `From (lb)` / `Through (lb)` 86 px each, and the Breeds field 72 px — 26 inputs under the
     clipping predicate's 120 px floor, the same class as E-1 on a page nobody had looked at.
     The measured cause is four grid tracks across 358 px: the two-column `.dr-table`
     (`Size range` | `Weight cutoffs`) with a nested two-column `.dr-pair` inside the second cell.
     The reflow is E-1's — one RANGE per block, each control keeping its own visible label, every
     input full width, the buttons on their own line. SCOPED to `.dr-fieldset`, the Dogs & records
     editor, on purpose: `.ls-scroll-x` is shared with every other settings table and
     `.kn-v3 .kn-pair` is the kennel setup grid (R59) — neither is this defect and neither is
     changed here. The <thead> is visually hidden rather than removed, so the two column names
     still reach a screen reader. */
  .dr-fieldset .ls-scroll-x { overflow-x: visible; }
  .dr-fieldset .dr-table thead {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .dr-fieldset .dr-table,
  .dr-fieldset .dr-table tbody,
  .dr-fieldset .dr-table tr,
  .dr-fieldset .dr-table th,
  .dr-fieldset .dr-table td { display: block; width: 100%; min-width: 0; box-sizing: border-box; }
  .dr-fieldset .dr-table tr {
    border-bottom: 1px solid var(--border-default);
    padding: var(--space-3) 0;
  }
  .dr-fieldset .dr-table th,
  .dr-fieldset .dr-table td { border-bottom: 0; padding: var(--space-2) 0; }
  /* One range per block: the two weight fields stop sharing a row with each other. */
  .dr-fieldset .dr-pair { grid-template-columns: 1fr; }
  /* Every control full width — never an 86 px box beside another 86 px box. */
  .dr-fieldset .dr-field .form-input,
  .dr-fieldset .dr-table input,
  .dr-fieldset .ls-dropdown-bar { width: 100%; min-width: 0; box-sizing: border-box; }
  /* Breeds: the Add button takes its own line instead of squeezing the field to 72 px. */
  .dr-fieldset .ls-add { flex-wrap: wrap; max-width: none; }
  .dr-fieldset .ls-add .form-input { flex: 1 1 100%; }
  .dr-fieldset .ls-add .btn { flex: 1 1 100%; width: 100%; }
  /* Add a range / Use the standard sizes: their own line each, wrapping rather than truncating. */
  .dr-fieldset .ls-actions { flex-wrap: wrap; }
  .dr-fieldset .ls-actions .btn { flex: 1 1 100%; width: 100%; white-space: normal; }
  /* ── TL-FIX-23 · the tab strip FITS at 390 instead of scrolling 3 px ────────────────────
     HUB-CAPTURE-8 §6 item 1: at 390 the four-tab strip measured scrollWidth 361 against
     clientWidth 358 on every tabbed page — 39 instances, the value 3 in all of them.
     MEASURED CAUSE (this lane, real Chromium, TL-FIX-23-probe.cjs): `.bf-tabs` has NO padding and
     no left/right border — padding-left, padding-right, border-left-width and border-right-width
     all compute to 0px, so there is no shared padding to remove. The 3 px is the TABS' own
     content: four labels (225.16 px of text here) + 4 × 32 px of `.bf-tab` side padding
     + 3 × 2 px gap = 359.16 px against 358 px of available width. The constant 3 across all 39
     readings is the same four labels measured 39 times, not one shared box.
     So the strip is given the room by trimming the side padding at this tier only: 16 px → 12 px
     buys back 32 px. The 44 px touch floor is carried by `min-height` (`.settings-view .bf-tab`
     above), never by padding, so it is untouched — asserted at every width in the probe. */
  .bf-tabs .bf-tab { padding-left: 12px; padding-right: 12px; }
}

@media (max-width: 768px) {
  .grid-2 { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: 1fr; }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  .grid-4 { grid-template-columns: 1fr; }
}

/* --- Content Cards (Training Library) --- */
.content-card {
  background: rgba(17, 24, 39, 0.8);
  backdrop-filter: blur(12px);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: all var(--transition-base);
  cursor: pointer;
}

.content-card:hover {
  border-color: var(--border-hover);
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

.content-card-thumbnail {
  aspect-ratio: 16 / 9;
  background: var(--bg-tertiary);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}

.content-card-thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.content-card-type-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--glow-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand-blue);
}

.content-card-body {
  padding: var(--space-4);
}

.content-card-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-1);
}

.content-card-desc {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
}

/* --- Video Player --- */
.video-player-wrap {
  background: #000;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.video-player-wrap video {
  width: 100%;
  height: 100%;
}

/* --- Ticket List --- */
.ticket-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border-default);
  cursor: pointer;
  transition: background var(--transition-fast);
}

.ticket-row:hover {
  background: var(--glow-subtle);
}

.ticket-subject {
  flex: 1;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.ticket-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* --- Timeline (Ticket Comments) --- */
.timeline {
  position: relative;
  padding-left: var(--space-8);
}

.timeline::before {
  content: '';
  position: absolute;
  left: 12px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--border-default);
}

.timeline-item {
  position: relative;
  padding-bottom: var(--space-6);
}

.timeline-dot {
  position: absolute;
  left: calc(-1 * var(--space-8) + 8px);
  top: 4px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--brand-blue);
  border: 2px solid var(--bg-primary);
}

.timeline-content {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.timeline-author {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-1);
}

.timeline-time {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.timeline-body {
  font-size: var(--text-sm);
  margin-top: var(--space-2);
}

/* --- AI Analysis Card --- */
.ai-analysis-card {
  border-color: rgba(51, 161, 253, 0.2);
  background: linear-gradient(135deg, rgba(51, 161, 253, 0.06), rgba(17, 24, 39, 0.8));
}

.ai-analysis-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.ai-analysis-icon {
  display: flex;
  align-items: center;
  color: var(--brand-blue);
}

.ai-analysis-icon svg {
  width: 20px;
  height: 20px;
}

.ai-analysis-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--brand-blue);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.ai-analysis-body {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.ai-analysis-content {
  color: var(--text-primary);
}

.ai-analysis-loading {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-muted);
}

.ai-pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--brand-blue);
  animation: ai-pulse 1.5s ease-in-out infinite;
}

@keyframes ai-pulse {
  0%, 100% { opacity: 0.3; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1.2); }
}

.ai-analysis-error {
  color: var(--text-muted);
  font-style: italic;
}

/* --- AI Analysis Markdown Rendering --- */
.ai-analysis-content h1,
.ai-analysis-content h2,
.ai-analysis-content h3,
.ai-analysis-content h4 {
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
}

.ai-analysis-content h1 { font-size: var(--text-xl); }
.ai-analysis-content h2 { font-size: var(--text-lg); }
.ai-analysis-content h3 { font-size: var(--text-base); font-weight: 600; }
.ai-analysis-content h4 { font-size: var(--text-sm); font-weight: 600; }

.ai-analysis-content p {
  margin-bottom: var(--space-3);
}

.ai-analysis-content ul,
.ai-analysis-content ol {
  padding-left: var(--space-6);
  margin-bottom: var(--space-3);
}

.ai-analysis-content li {
  margin-bottom: var(--space-1);
}

.ai-analysis-content strong {
  color: var(--text-primary);
  font-weight: 600;
}

.ai-analysis-content hr {
  border: none;
  border-top: 1px solid var(--border-default);
  margin: var(--space-4) 0;
}

.md-code-block {
  background: var(--bg-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  overflow-x: auto;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: var(--text-xs);
  margin: var(--space-3) 0;
}

.md-inline-code {
  background: var(--bg-tertiary);
  padding: 2px var(--space-2);
  border-radius: 4px;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 0.9em;
}

.md-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-3) 0;
  font-size: var(--text-xs);
}

.md-table th,
.md-table td {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-default);
}

.md-table th {
  background: var(--bg-tertiary);
  font-weight: 600;
  color: var(--text-primary);
}

.md-table td {
  color: var(--text-secondary);
}

/* --- Cbot Embed (AI Chat Sub-window) --- */
.cbot-embed-container {
  margin-top: 0;
}

.cbot-embed-wrap {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-top: var(--space-3);
}

.cbot-embed-iframe {
  width: 100%;
  height: 500px;
  border: none;
  background: var(--bg-primary);
}

/* --- Announcements --- */
.announcement-banner {
  background: linear-gradient(135deg, rgba(51, 161, 253, 0.1), rgba(51, 161, 253, 0.05));
  border: 1px solid rgba(51, 161, 253, 0.2);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}

.announcement-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--brand-blue);
  margin-bottom: var(--space-1);
}

.announcement-body {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* --- Empty States --- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-16) var(--space-8);
  text-align: center;
}

.empty-state-icon {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-lg);
  background: var(--glow-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand-blue);
  margin-bottom: var(--space-5);
}

.empty-state-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-2);
}

.empty-state-desc {
  font-size: var(--text-sm);
  color: var(--text-muted);
  max-width: 400px;
}

/* --- Loading --- */
.skeleton {
  background: linear-gradient(90deg, var(--bg-tertiary) 25%, var(--bg-elevated) 50%, var(--bg-tertiary) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius-sm);
}

@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* --- Article Content (Marketing Guide) --- */
.article-content {
  font-size: var(--text-base);
  line-height: 1.7;
  color: var(--text-secondary);
}

.article-content h1,
.article-content h2,
.article-content h3 {
  margin-top: var(--space-8);
  margin-bottom: var(--space-4);
}

.article-content p {
  margin-bottom: var(--space-4);
}

.article-content ul,
.article-content ol {
  margin-bottom: var(--space-4);
  padding-left: var(--space-6);
}

.article-content li {
  margin-bottom: var(--space-2);
}

.article-content code {
  background: var(--bg-tertiary);
  padding: var(--space-1) var(--space-2);
  border-radius: 4px;
  font-size: var(--text-sm);
}

.article-content blockquote {
  border-left: 3px solid var(--brand-blue);
  padding-left: var(--space-4);
  margin: var(--space-4) 0;
  color: var(--text-muted);
  font-style: italic;
}

/* --- Search --- */
.search-box {
  position: relative;
}

.search-box .form-input {
  padding-left: var(--space-10);
}

.search-box-icon {
  position: absolute;
  left: var(--space-4);
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
}

/* --- Category Tree (Sidebar Navigation) --- */
.category-tree {
  list-style: none;
}

.category-item {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: all var(--transition-fast);
}

.category-item:hover {
  background: var(--glow-subtle);
  color: var(--text-primary);
}

.category-item.active {
  color: var(--brand-blue);
  background: var(--glow-medium);
}

.category-children {
  padding-left: var(--space-4);
}

/* --- Tags --- */
.tag {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  gap: var(--space-1);
}

.tag-input {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  min-height: 40px;
  align-items: center;
}

.tag-input input {
  flex: 1;
  min-width: 120px;
  background: none;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: var(--text-sm);
}

/* --- File Upload --- */
.file-drop-zone {
  border: 2px dashed var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
  text-align: center;
  cursor: pointer;
  transition: all var(--transition-base);
}

.file-drop-zone:hover,
.file-drop-zone.dragover {
  border-color: var(--brand-blue);
  background: var(--glow-subtle);
}

/* --- Utility --- */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: var(--space-2); }
.gap-4 { gap: var(--space-4); }
.gap-6 { gap: var(--space-6); }
.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }
.text-muted { color: var(--text-muted); }
.text-sm { font-size: var(--text-sm); }
.text-xs { font-size: var(--text-xs); }
.font-semibold { font-weight: 600; }
.hidden { display: none; }
.w-full { width: 100%; }

/* --- Sidebar Layouts (Mobile) --- */
@media (max-width: 768px) {
  .training-layout,
  .marketing-layout {
    flex-direction: column;
  }

  .training-sidebar,
  .marketing-sidebar {
    width: 100% !important;
    border-right: none !important;
    padding-right: 0 !important;
    border-bottom: 1px solid var(--border-default);
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-4);
    position: static !important;
  }

  .ticket-row {
    flex-wrap: wrap;
    gap: var(--space-2);
  }
}

/* --- Quick Action Cards (Dashboard) --- */
.quick-action-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-6) var(--space-4);
  transition: all var(--transition-base);
}

.quick-action-card:hover {
  border-color: var(--brand-blue);
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(51, 161, 253, 0.1);
}

.quick-action-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--glow-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand-blue);
  margin-bottom: var(--space-3);
  transition: background var(--transition-base);
}

.quick-action-card:hover .quick-action-icon {
  background: var(--glow-medium);
}

.quick-action-icon svg {
  width: 24px;
  height: 24px;
}

.quick-action-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-1);
}

.quick-action-desc {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
}

/* --- Value Summary Cards (Dashboard) --- */
.value-summary-header {
  margin-bottom: var(--space-4);
}

.value-summary-header h4 {
  font-size: var(--text-lg);
  color: var(--text-primary);
  font-weight: 600;
}

.value-summary-header p {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

.value-summary-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-5);
  position: relative;
  overflow: hidden;
  transition: all var(--transition-base);
}

.value-summary-card:hover {
  border-color: var(--border-hover);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.value-summary-accent {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.value-summary-icon {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-3);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
}

.value-summary-icon svg {
  width: 20px;
  height: 20px;
}

.value-summary-number {
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1;
  margin-bottom: var(--space-1);
}

.value-summary-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-1);
}

.value-summary-desc {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
  margin-bottom: var(--space-3);
}

.value-summary-sub {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-default);
}

/* --- Dashboard Header --- */
.dashboard-header h3 {
  font-size: var(--text-2xl);
}

/* --- Back Button Polish --- */
.btn-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 500;
  border-radius: var(--radius-sm);
  transition: all var(--transition-fast);
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: var(--font-family);
  text-decoration: none;
}

.btn-back:hover {
  color: var(--text-primary);
  background: var(--bg-tertiary);
}

.btn-back svg {
  width: 16px;
  height: 16px;
}

/* --- Ticket Row last-child border --- */
.ticket-row:last-child {
  border-bottom: none;
}

/* ============================================
   Template Editor (Pluto)
   ============================================ */

/* Remove padding/max-width constraints when editor is active */
.content-body:has(.template-editor) {
  padding: 0;
  max-width: none;
  width: 100%;
}

.main-content:has(.template-editor) {
  overflow: hidden;
}

.template-editor {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 56px);
  overflow: hidden;
}

.editor-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-default);
  background: var(--bg-secondary);
  flex-shrink: 0;
}

.editor-toolbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 1;
}

.editor-toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.editor-title {
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;
}

.editor-title span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.editor-main {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.editor-preview {
  flex: 0 0 55%;
  overflow: auto;
  border-right: 1px solid var(--border-default);
  background: #f4f4f4;
}

.editor-chat {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}

.editor-chat-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-default);
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--text-primary);
  flex-shrink: 0;
}

.editor-chat-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

/* ============================================
   Editor Modals + Banners + Settings Panel
   --------------------------------------------
   Shared chrome for Pluto editor modals (rename,
   upload photo, sync warning) + the not-configured
   banner + the Template Settings panel. ALL colors
   resolve via design tokens from :root — DO NOT
   hardcode hex/rgb here. See .claude/rules/
   portal-styling.md for the full style policy.
   ============================================ */

/* Backdrop overlay shared across every editor modal. Pluto preview iframes
   are sandboxed so the email's white background is OK to render against
   this dark backdrop — only the chrome around it adopts the dark theme. */
.editor-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
}

.editor-modal-dialog {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
  width: 92vw;
  font-family: inherit;
  padding: 20px;
}
.editor-modal-dialog--sm  { max-width: 440px; }
.editor-modal-dialog--md  { max-width: 520px; }
.editor-modal-dialog--lg  { max-width: 640px; }

.editor-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
}

.editor-modal-title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text-primary);
}
.editor-modal-title--warn { color: var(--warning); }
.editor-modal-title--error { color: var(--error); }

.editor-modal-dismiss {
  border: none;
  background: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: var(--text-secondary);
  line-height: 1;
  padding: 0 4px;
}
.editor-modal-dismiss:hover { color: var(--text-primary); }

.editor-modal-body {
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text-secondary);
}
.editor-modal-body strong { color: var(--text-primary); }
.editor-modal-body p { margin: 0 0 12px; }
.editor-modal-body code {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 0.85em;
}

.editor-modal-info-block {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin: 8px 0 16px;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* Embed modal: the paste-ready snippet + the list of websites approved to frame the form. */
.embed-snippet {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 0.78rem;
  line-height: 1.6;
  padding: 12px;
  margin: 0 0 8px;
  /* The snippet is one long URL — it must scroll inside its own box, never widen the modal. */
  overflow-x: auto;
  white-space: pre;
  user-select: all;
}

.embed-origin-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.embed-origin-list li { color: var(--text-secondary); }
.editor-modal-info-block strong { color: var(--text-primary); }
.editor-modal-info-block .editor-modal-info-warn {
  margin-top: 8px;
  color: var(--warning);
}

/* Sync-warning modal: the first differing line, shown side by side. Added 2026-08-31 (L7) so a
   one-parameter difference is visibly a one-parameter difference and not "the template changed". */
.editor-sync-diff {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin: 0 0 16px;
}
.editor-sync-diff-label {
  color: var(--text-muted);
  font-size: 0.78rem;
  margin-bottom: 8px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.editor-sync-diff-row {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-bottom: 6px;
}
.editor-sync-diff-row:last-child { margin-bottom: 0; }
.editor-sync-diff-side {
  flex: 0 0 88px;
  color: var(--text-secondary);
  font-size: 0.78rem;
  padding-top: 2px;
}
/* One long HTML line must scroll inside its own box, never widen the dialog. */
.editor-sync-diff-row code {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  background: var(--bg-secondary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 0.74rem;
  line-height: 1.5;
  padding: 6px 8px;
  overflow-x: auto;
  white-space: pre;
}
.editor-sync-fact { color: var(--text-secondary); }
.editor-sync-fact strong { color: var(--text-primary); }

.editor-modal-footer {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
}

.editor-modal-input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border-default);
  border-radius: 4px;
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-size: 0.9rem;
  font-family: inherit;
  box-sizing: border-box;
}
.editor-modal-input:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 2px var(--glow-subtle);
}
.editor-modal-input:disabled,
.editor-modal-input[readonly] {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-secondary);
  cursor: default;
}
.editor-modal-input--invalid {
  border-color: var(--error);
}

.editor-modal-status {
  margin-top: 8px;
  min-height: 1.2em;
  font-size: 0.82rem;
  color: var(--text-secondary);
}
.editor-modal-status--ok    { color: var(--success); }
.editor-modal-status--warn  { color: var(--warning); }
.editor-modal-status--error { color: var(--error); }

.editor-modal-helper {
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.4;
  margin-top: 4px;
}

.editor-modal-link {
  color: var(--brand-blue);
  text-decoration: underline;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  padding: 0;
}
.editor-modal-link:hover { color: var(--brand-blue-dark); }

/* Dropzone for file uploads */
.editor-modal-dropzone {
  border: 2px dashed var(--border-hover);
  border-radius: var(--radius-sm);
  padding: 24px;
  text-align: center;
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
  background: var(--bg-tertiary);
}
.editor-modal-dropzone:hover,
.editor-modal-dropzone--active {
  border-color: var(--brand-blue);
  background: var(--glow-subtle);
  color: var(--text-primary);
}
.editor-modal-dropzone--locked {
  opacity: 0.5;
  pointer-events: none;
}

/* Inline preview for selected upload */
.editor-modal-preview-img {
  max-width: 100%;
  max-height: 240px;
  display: block;
  margin: 12px auto 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary);
}
.editor-modal-preview-info {
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--text-secondary);
  text-align: center;
}

.editor-modal-field-label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 4px;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}

/* Tag for "likely match" hint inside the not-configured banner */
.editor-modal-pill {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 3px;
  vertical-align: middle;
  margin-left: 4px;
  background: var(--success);
  color: var(--bg-primary);
}

/* Inline-list row used by the not-configured banner. */
.editor-modal-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px;
  border: 1px solid var(--border-default);
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
}
.editor-modal-list-row:hover { border-color: var(--border-hover); }
.editor-modal-list-row .editor-modal-list-name {
  font-weight: 600;
  color: var(--text-primary);
  word-break: break-word;
}
.editor-modal-list-row .editor-modal-list-meta {
  font-size: 0.75rem;
  color: var(--text-secondary);
}

/* Standalone banner (rendered inline, not as a modal) used when a session
   can't be created because the local template isn't linked to a GHL ID. */
.editor-warn-banner {
  padding: 24px;
  margin: 24px auto;
  max-width: 720px;
  background: var(--bg-elevated);
  border: 1px solid var(--warning);
  border-left: 4px solid var(--warning);
  border-radius: var(--radius-md);
  color: var(--text-primary);
}
.editor-warn-banner h2 {
  margin: 0 0 8px;
  font-size: 1.2rem;
  color: var(--warning);
}
.editor-warn-banner p {
  margin: 0 0 16px;
  color: var(--text-secondary);
  font-size: 0.95rem;
  line-height: 1.5;
}
.editor-warn-banner code {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 0.85em;
}
.editor-warn-banner .editor-warn-banner-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.editor-warn-banner .editor-warn-banner-loading {
  font-size: 0.9rem;
  color: var(--text-secondary);
}
.editor-warn-banner-footer {
  margin: 16px 0 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* ── Template Settings Panel (collapsible row above the editor) ── */
.editor-settings-panel {
  display: none;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border-default);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-size: 0.9rem;
}
.editor-settings-panel--open { display: block; }
.editor-settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 20px;
  align-items: start;
}
.editor-settings-panel .esp-label {
  font-weight: 600;
  color: var(--text-primary);
  font-size: 0.78rem;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.editor-settings-panel .esp-label-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.editor-settings-panel .esp-hint-icon {
  cursor: help;
  color: var(--text-secondary);
  font-weight: 400;
  margin-left: 4px;
}
.editor-settings-panel input {
  padding: 8px 10px;
  border: 1px solid var(--border-default);
  border-radius: 4px;
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-size: 0.9rem;
  font-family: inherit;
}
.editor-settings-panel input:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 2px var(--glow-subtle);
}
.editor-settings-panel input[readonly],
.editor-settings-panel input:disabled {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-secondary);
  cursor: default;
}
.editor-settings-panel .esp-foot {
  font-size: 0.72rem;
  color: var(--text-secondary);
  min-height: 1em;
}
.editor-settings-panel .esp-foot--ok    { color: var(--success); }
.editor-settings-panel .esp-foot--warn  { color: var(--warning); }
.editor-settings-panel .esp-foot--error { color: var(--error); }
.editor-settings-panel .esp-from-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
}
/* "Auto-generate" button — explicit hover overrides the global
   .btn-secondary:hover (which uses a translucent white overlay that becomes
   invisible against any white-ish parent). */
.editor-settings-panel .esp-gen-btn {
  font-size: 0.72rem;
  padding: 3px 10px;
  min-height: 24px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.editor-settings-panel .esp-gen-btn:hover:not(:disabled) {
  background: var(--brand-blue);
  color: #fff;
  border-color: var(--brand-blue);
}
.editor-settings-panel .esp-gen-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Chat Messages */
.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.chat-bubble {
  padding: 10px 14px;
  border-radius: 12px;
  max-width: 88%;
  font-size: 0.8125rem;
  line-height: 1.6;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble code {
  background: rgba(255, 255, 255, 0.1);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 0.8em;
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
}

.chat-bubble-user {
  align-self: flex-end;
  background: var(--brand-blue);
  color: #fff;
  border-bottom-right-radius: 4px;
}

.chat-bubble-assistant {
  align-self: flex-start;
  background: var(--bg-tertiary);
  color: var(--text-primary);
  border-bottom-left-radius: 4px;
}

.chat-bubble-assistant strong {
  color: var(--text-primary);
}

.chat-system-message {
  text-align: center;
  padding: 6px 12px;
  font-size: 0.75rem;
  color: var(--text-muted);
  font-style: italic;
}

.chat-change-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 0.75rem;
  color: var(--success);
  font-weight: 500;
}

.chat-change-indicator svg {
  width: 14px;
  height: 14px;
}

.chat-error-text {
  color: var(--error);
}

/* Thinking animation */
.chat-thinking {
  min-height: 36px;
  display: flex;
  align-items: center;
}

/* Immediate ack line shown next to the thinking dots (server `ack` SSE event) */
.chat-ack-text {
  color: var(--text-muted);
  font-style: italic;
  margin-right: 8px;
}

.thinking-dots {
  display: flex;
  gap: 4px;
  padding: 4px 0;
}

.thinking-dots span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-muted);
  animation: thinking-bounce 1.2s ease-in-out infinite;
}

.thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
.thinking-dots span:nth-child(3) { animation-delay: 0.3s; }

@keyframes thinking-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30% { transform: translateY(-6px); opacity: 1; }
}

/* Brand-asset generation card — generate → review → approve, inline in the chat */
.chat-asset-card {
  align-self: flex-start;
  max-width: 88%;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  margin-top: var(--space-1);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-active);
  border-radius: var(--radius-md);
}

.chat-asset-head {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.chat-asset-summary {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.chat-asset-status {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.chat-asset-status--warn { color: var(--warning); }
.chat-asset-status--error { color: var(--error); }

.chat-asset-preview {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-default);
  display: block;
}

.chat-asset-preview[hidden] { display: none; }

.chat-asset-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Selectable option cards streamed into the chat by Pluto */
.chat-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-self: flex-start;
  max-width: 88%;
  margin-top: var(--space-1);
}

.chat-options-prompt {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  padding-left: var(--space-1);
}

.chat-option-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.chat-option-card:hover:not(:disabled) {
  background: var(--bg-elevated);
  border-color: var(--border-active);
}

.chat-option-card:disabled {
  opacity: 0.5;
  cursor: default;
}

.chat-option-card--chosen {
  border-color: var(--brand-blue);
  opacity: 1;
}

.chat-option-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.chat-option-detail {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.chat-option-preview {
  width: 100%;
  height: 120px;
  margin-top: var(--space-2);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--text-primary);
  pointer-events: none;
}

/* Element-picker selection chip — sits above the composer while an element is selected */
.chat-selection-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-active);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.chat-selection-chip[hidden] { display: none; }

.chat-selection-icon {
  color: var(--brand-blue);
  flex-shrink: 0;
  line-height: 1;
}

.chat-selection-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-primary);
}

.chat-selection-clear {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: var(--text-base);
  line-height: 1;
  cursor: pointer;
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  transition: color var(--transition-fast), background var(--transition-fast);
}

.chat-selection-clear:hover {
  color: var(--text-primary);
  background: var(--bg-elevated);
}

/* Persistent record, in the transcript, of what a message was scoped to */
.chat-selection-sent {
  align-self: flex-end;
  max-width: 88%;
  margin-bottom: calc(var(--space-1) * -1);
  padding: 0 var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Chat Input */
.chat-input-area {
  padding: 12px 16px;
  border-top: 1px solid var(--border-default);
  flex-shrink: 0;
}

.chat-input-wrap {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: 12px;
  padding: 4px 4px 4px 12px;
  transition: border-color 0.15s;
}

.chat-input-wrap:focus-within {
  border-color: var(--brand-blue);
}

.chat-input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: 0.8125rem;
  font-family: inherit;
  resize: none;
  padding: 6px 0;
  line-height: 1.5;
  max-height: 120px;
}

.chat-input::placeholder {
  color: var(--text-muted);
}

.chat-send-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: none;
  background: var(--brand-blue);
  color: #fff;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s;
}

.chat-send-btn:hover {
  background: var(--brand-blue-dark);
}

.chat-send-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.chat-send-btn svg {
  width: 16px;
  height: 16px;
}

.chat-send-btn.sending {
  opacity: 0.5;
  cursor: wait;
}

/* Mobile responsive */
@media (max-width: 768px) {
  .template-editor {
    height: calc(100vh - 56px);
  }

  .editor-main {
    flex-direction: column;
  }

  .editor-preview {
    flex: 0 0 45%;
    border-right: none;
    border-bottom: 1px solid var(--border-default);
  }

  .editor-chat {
    flex: 1;
  }

  .editor-toolbar-left {
    flex-wrap: nowrap;
    overflow: hidden;
  }

  .editor-title {
    font-size: 0.8125rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 50vw;
  }

  .editor-journey-badge {
    display: none;
  }
}

@media (max-width: 480px) {
  .editor-preview {
    flex: 0 0 35%;
  }
}

/* --- Reduced Motion --- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── Event Forms view + editor shared classes ───────────────────────────────
   Replaces all hardcoded hex/rgb literals that were inline in JS.           */

/* Tertiary button — transparent with a subtle border */
.btn-tertiary {
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--text-muted);
}
.btn-tertiary:hover {
  background: var(--bg-tertiary);
  border-color: var(--border-hover);
  color: var(--text-primary);
}

/* Tab navigation bar buttons */
.event-form-tab {
  padding: 12px 20px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.event-form-tab--active {
  color: var(--brand-blue);
  border-bottom-color: var(--brand-blue);
}

/* Label style for editor form fields (replaces injected <style> tag) */
.event-form-editor-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  margin-bottom: 4px;
  color: var(--text-muted);
}

/* Pills used on form cards and header */
.ef-deleted-pill {
  background: var(--bg-tertiary);
  color: var(--text-muted);
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 500;
}
.ef-expires-pill {
  background: rgba(245, 158, 11, 0.15);
  color: var(--warning);
  border: 1px solid rgba(245, 158, 11, 0.3);
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.7rem;
}

/* ── Booking Forms (design tokens only) ─────────────────────────────────────── */
.bf-scope-pill {
  background: var(--brand-blue-btn, var(--brand-blue));
  color: var(--text-primary);
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
/* ── TL-FIX-22 E-3 · the tab strip is ONE row at every width ───────────────────────────────────
   VISUAL-AUDIT-5 E-3: at 390 the four service tabs wrapped, `Grooming` landed alone on row 2, and
   the accent underline marking the selection stayed under `Overall` on row 1 — the selected-state
   indicator and the selected tab read as two different controls (ui-consistency rule 9, one grid).
   A scroller keeps the strip one row at every width; where the tabs already fit, nothing about the
   rendering moves. `scrollbar-width` and `scrollbar-color` travel TOGETHER (rule 10): width alone
   opts the element into the standard scrollbar, which ignores ::-webkit-scrollbar and paints a
   white bar on this dark chrome. Shared component — one rule for every page using .bf-tabs. */
.bf-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border-default);
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--text-muted) var(--bg-secondary);
}
.bf-tab {
  font-family: var(--font-family);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  padding: 10px 16px;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
  /* In a nowrap flex container a tab would otherwise shrink below its label rather than scroll. */
  flex: 0 0 auto;
  white-space: nowrap;
}
.bf-tab:hover { color: var(--text-primary); }
.bf-tab.active { color: var(--text-primary); border-bottom-color: var(--brand-blue); }
/* The strip is now a scroll container, and a scroll container clips at its PADDING box — so a focus
   ring drawn outside a tab's border box (outline-offset: 2px) would be cut off top and bottom. The
   ring is drawn just inside instead: same 2px brand-blue, fully visible, never clipped. */
.bf-tabs .bf-tab:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: -2px; }
/* ── TL-FIX-23 · a strip that GENUINELY scrolls says so ─────────────────────────────
   HUB-CAPTURE-8 §6 item 1: E-3 turned the strip into a scroller, and a scroller with no visible
   affordance is a control whose remaining content nobody can know about — the last tab sat flush
   against the container edge. The right edge now fades into the page background while there is
   content to the right of it, and the fade is GONE once the strip is scrolled to its end.
   It is driven by the strip's OWN inline scroll progress, so a strip that fits renders exactly as
   it does today: a scroller with no scroll range has an INACTIVE timeline and the animation does
   not apply (measured in this lane's probe across three states — fits, scrollable-at-start,
   scrolled-to-end). `mask-image` fades the tab LABELS, which a background gradient behind
   transparent buttons cannot do. Behind @supports so a browser without scroll timelines keeps
   today's rendering and the thin scrollbar, rather than a static fade it could never retract. */
@supports (animation-timeline: scroll(self inline)) {
  .bf-tabs {
    animation: bf-tabs-edge-fade linear both;
    animation-timeline: scroll(self inline);
  }
  @keyframes bf-tabs-edge-fade {
    from { mask-image: linear-gradient(to right, #000 calc(100% - 32px), rgba(0, 0, 0, 0) 100%); }
    to { mask-image: linear-gradient(to right, #000 100%, rgba(0, 0, 0, 0) 100%); }
  }
}
/* Viewport toggle (Desktop/Mobile) inside the preview pill — active reads in brand blue (parity with
   the event-form builder), since these buttons suppress the underline that .bf-tab.active normally uses. */
.bf-viewport-tab.active { color: var(--brand-blue); }
.bf-card {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  margin-bottom: var(--space-4);
}
/* Two-pane editor (controls left, live preview right) — parity with the event-form builder. */
.bf-editor-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  align-items: start;
}
@media (max-width: 900px) {
  .bf-editor-split { grid-template-columns: 1fr; }
}
/* Collapsible category accordions (Services & Pricing) — all collapsed by default. */
.bf-accordion {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  overflow: hidden;
}
.bf-accordion-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: transparent;
  border: none;
  padding: var(--space-4) var(--space-5);
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
  transition: background var(--transition-fast);
}
.bf-accordion-header:hover { background: var(--bg-secondary); }
.bf-accordion-count { color: var(--text-muted); font-weight: 500; font-size: 0.8125rem; }
.bf-accordion-chevron {
  color: var(--text-muted);
  transition: transform var(--transition-fast);
  font-size: 0.75rem;
}
.bf-accordion.open .bf-accordion-chevron { transform: rotate(180deg); }
.bf-accordion-content {
  padding: 0 var(--space-5) var(--space-4);
  border-top: 1px solid var(--border-default);
}
.bf-accordion-content[hidden] { display: none; }
.bf-section-title {
  margin: 0 0 var(--space-4) 0;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-primary);
}
.bf-field { margin-bottom: var(--space-4); }
.bf-field-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 8px 0;
  border-bottom: 1px solid var(--border-default);
  flex-wrap: wrap;
}
.bf-field-row:last-child { border-bottom: none; }
.bf-check { display: inline-flex; align-items: center; gap: 6px; font-size: 0.875rem; color: var(--text-primary); cursor: pointer; min-width: 200px; }
.bf-check.bf-req { color: var(--text-muted); min-width: auto; }
.bf-lock { font-size: 0.7rem; color: var(--text-muted); font-weight: 400; }
/* A field whose value is owned by another page (Location Settings). Deliberately NOT a disabled
   control: a greyed checkbox reads as "nobody may change this", which is false and was actively
   misleading once the Settings card shipped (LANE K, 2026-08-06). */
.bf-managed { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; font-size: 0.8125rem; color: var(--text-primary); }
.bf-managed .editor-modal-helper { margin: 0; }
.bf-label-input { flex: 1; min-width: 140px; max-width: 300px; }
.bf-pricing-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
.bf-pricing-table th {
  text-align: left;
  color: var(--text-muted);
  font-weight: 600;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border-default);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.bf-pricing-table td { padding: 8px 10px; border-bottom: 1px solid var(--border-default); color: var(--text-primary); vertical-align: middle; }
.bf-sm-input { max-width: 160px; padding: 6px 10px !important; font-size: 0.8125rem !important; }
.bf-saved { border-color: var(--success) !important; }
/* Read-only per-service booking window in the Advanced tab. Booking hours are owned by the
   Location Settings page (LANE K, 2026-08-06); this displays the effective window and links there,
   so there is exactly one writable home for the value. Same box metrics as .bf-sm-input so the
   read-only value sits on the same baseline as the editable controls beside it. */
.bf-ro-hours { display: flex; align-items: end; gap: var(--space-2); flex-wrap: wrap; margin-bottom: 4px; }
.bf-ro-value { padding: 6px 10px; font-size: 0.8125rem; color: var(--text-primary); background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm); min-width: 90px; }
.bf-status { padding: 2px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; text-transform: capitalize; }
/* Advanced-builder drag-drop role assignor (design tokens). */
.bf-dnd-panel { margin-bottom: var(--space-4); }
.bf-dnd-svc { font-size: 0.875rem; font-weight: 700; color: var(--text-primary); margin-bottom: var(--space-2); }
.bf-dnd-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-3); }
.bf-dnd-col { background: var(--bg-secondary); border: 1.5px dashed var(--border-default); border-radius: var(--radius-md); padding: var(--space-3); min-height: 70px; transition: border-color var(--transition-fast), background var(--transition-fast); }
.bf-dnd-col-over { border-color: var(--brand-blue); background: var(--bg-elevated); }
.bf-dnd-col-title { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: var(--space-2); }
.bf-chip { display: flex; align-items: center; justify-content: space-between; gap: 6px; background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm); padding: 6px 10px; margin-bottom: 6px; font-size: 0.8125rem; color: var(--text-primary); cursor: grab; }
.bf-chip:hover { border-color: var(--border-hover); }
.bf-chip-dragging { opacity: 0.5; }
.bf-chip-price { color: var(--text-muted); font-size: 0.75rem; margin-left: 4px; }
.bf-chip-reset { background: transparent; border: none; color: var(--brand-blue); cursor: pointer; font-size: 0.9rem; padding: 0 2px; }

/* Status-pill backgrounds are token-equivalent rgba (15% tints of the matching token), the
   sanctioned pattern from .ef-expires-pill: --brand-blue-btn #1478BD, --success #10B981,
   --warning #F59E0B, --error #EF4444. */
.bf-status--lead { background: rgba(20,120,189,0.15); color: var(--brand-blue); }
.bf-status--booked { background: rgba(16,185,129,0.15); color: var(--success); }
.bf-status--pending { background: var(--bg-secondary); color: var(--text-muted); }
.bf-status--conflict { background: rgba(245,158,11,0.15); color: var(--warning); }
.bf-status--failed { background: rgba(239,68,68,0.15); color: var(--error); }

/* Monospace URL / slug code block */
.ef-code-block {
  font-family: monospace;
  font-size: 0.8125rem;
  color: var(--text-primary);
  text-decoration: none;
  background: var(--bg-tertiary);
  padding: 4px 10px;
  border-radius: 4px;
}

/* Field row inside the editor controls panel */
.ef-field-row {
  border: 1px solid var(--border-default);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 8px;
  background: var(--bg-secondary);
}

/* Submission status color modifiers */
.ef-status-ok    { color: var(--success); font-weight: 500; }
.ef-status-warn  { color: var(--warning); font-weight: 500; }
.ef-status-error { color: var(--error);   font-weight: 500; }

/* Version history source-label color modifiers */
.ef-source-create      { color: var(--success);    font-weight: 500; }
.ef-source-manual-edit { color: var(--brand-blue);  font-weight: 500; }
.ef-source-ai-edit     { color: #7e57c2;            font-weight: 500; } /* purple — no token exists */
.ef-source-rollback    { color: var(--warning);     font-weight: 500; }
.ef-source-default     { color: var(--text-muted);  font-weight: 500; }

/* AI result banners (inline, not modal) */
.ef-banner-success {
  background: rgba(16, 185, 129, 0.12);
  border: 1px solid rgba(16, 185, 129, 0.3);
  color: var(--success);
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  margin-top: 12px;
}
.ef-banner-error {
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: var(--error);
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
}


/* ============================================================ */
/* Offers View — per-location Rogue CRM offer custom values      */
/* ============================================================ */

.offers-view {
  padding: var(--space-6) var(--space-8);
  max-width: 1400px;
  margin: 0 auto;
}

.offers-header {
  margin-bottom: var(--space-6);
}

.offers-header .page-title {
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 var(--space-2) 0;
}

.offers-header .page-subtitle {
  color: var(--text-secondary);
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
  max-width: 720px;
}

.offers-loading {
  color: var(--text-muted);
  font-size: 0.95rem;
  padding: var(--space-8) 0;
  text-align: center;
}

.offers-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
  gap: var(--space-5);
}

.offers-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  transition: border-color var(--transition-fast);
}

.offers-card:hover {
  border-color: var(--border-hover);
}

.offers-card-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  border-bottom: 1px solid var(--border-default);
  padding-bottom: var(--space-3);
}

.offers-card-title {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.offers-card-sub {
  font-size: 0.78rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.offers-subblock {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.offers-subblock-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding-bottom: var(--space-1);
}

.offers-field-row {
  display: grid;
  grid-template-columns: 64px 1fr auto auto;
  gap: var(--space-3);
  align-items: center;
}

.offers-field-label {
  font-size: 0.85rem;
  color: var(--text-secondary);
  font-weight: 500;
  cursor: help;
}

.offers-field-input {
  width: 100%;
}

.offers-field-status {
  font-size: 0.78rem;
  min-width: 50px;
  text-align: right;
}
.offers-field-status--ok { color: var(--success); }
.offers-field-status--error { color: var(--error); }

.offers-save-btn {
  min-width: 60px;
}

/* RELEASE-DSF-OFFERDL: "How long is this offer good for? [14] days after the email" — the same field family as the offer rows,
   one extra column for the unit. Below 480px the question takes its own line so nothing scrolls sideways. */
.offers-deadline-row {
  grid-template-columns: minmax(0, 1fr) 72px auto auto auto;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-default);
}
.offers-deadline-row .offers-field-label {
  cursor: default;
}
.offers-deadline-unit {
  font-size: 0.85rem;
  color: var(--text-secondary);
  white-space: nowrap;
}
@media (max-width: 480px) {
  .offers-deadline-row {
    grid-template-columns: 72px minmax(0, 1fr) auto;
  }
  .offers-deadline-row .offers-field-label {
    grid-column: 1 / -1;
  }
  .offers-deadline-row .offers-save-btn {
    grid-column: 1 / -1;
    justify-self: start;
  }
}

.offers-preview {
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.offers-preview-label {
  font-size: 0.7rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
}

.offers-preview-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.offers-preview-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.9rem;
  color: var(--text-primary);
}

.offers-preview-svc {
  font-size: 0.75rem;
  color: var(--text-secondary);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  background: var(--bg-secondary);
  border-radius: var(--radius-sm);
}

.offers-preview-value {
  color: var(--brand-blue);
  font-weight: 600;
}

.offers-preview-label-text {
  color: var(--text-secondary);
}

.offers-preview-code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.78rem;
  padding: 2px 8px;
  background: var(--bg-secondary);
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  margin-left: auto;
}


/* ── Email chip/tag input (multi-recipient tickets) ─────────────────────────── */
/* Mirrors .form-input so it sits seamlessly among the other form fields. */
.email-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 46px;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-family);
  font-size: var(--text-sm);
  cursor: text;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.email-chips:focus-within {
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 3px var(--glow-subtle);
}
.email-chips--invalid {
  border-color: var(--error);
}
.email-chips--invalid:focus-within {
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
}
.email-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px var(--space-2);
  background: var(--glow-medium);
  border: 1px solid var(--border-active);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: var(--text-xs);
  line-height: 1.4;
  max-width: 100%;
}
.email-chip-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.email-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.email-chip-remove:hover {
  color: var(--text-primary);
  background: var(--brand-blue-btn);
}
.email-chips-input {
  flex: 1 1 140px;
  min-width: 120px;
  border: none;
  outline: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  padding: var(--space-1) 0;
}
.email-chips-input::placeholder {
  color: var(--text-muted);
}
.email-chips-error {
  color: var(--error);
  font-size: var(--text-xs);
  margin-top: var(--space-1);
}

/* ── Onboarding admin tab (onb-*) ─────────────────────────────────────────── */
.onb-head { margin-bottom: var(--space-6); }
.onb-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: var(--space-4);
}
.onb-card { display: flex; flex-direction: column; gap: var(--space-3); }
.onb-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.onb-loc { font-size: var(--text-lg, 1.125rem); font-weight: 600; color: var(--text-primary); margin: 0; }
.onb-muted { color: var(--text-muted); font-size: var(--text-sm, 0.875rem); }
.onb-times { font-size: var(--text-xs, 0.75rem); }
.onb-error { color: var(--error); }
.onb-status-line { color: var(--text-secondary); font-size: var(--text-sm, 0.875rem); }
.onb-status-line--error { color: var(--error); }
.onb-progress-wrap { display: flex; flex-direction: column; gap: var(--space-1); }
.onb-progress-meta {
  display: flex;
  justify-content: space-between;
  color: var(--text-secondary);
  font-size: var(--text-xs, 0.75rem);
}
.onb-progress {
  height: 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  overflow: hidden;
}
.onb-progress-fill {
  height: 100%;
  border-radius: var(--radius-sm);
  background: var(--brand-blue);
  transition: width var(--transition-slow, 0.4s ease);
}
.onb-progress-fill--waiting { background: var(--warning); }
.onb-progress-fill--error { background: var(--error); }
.onb-steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  max-height: 260px;
  overflow-y: auto;
}
.onb-step-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm, 0.875rem);
  color: var(--text-secondary);
}
.onb-step-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--border-hover); }
.onb-step-dot--done { background: var(--success); }
.onb-step-dot--running { background: var(--brand-blue); box-shadow: var(--glow-subtle); }
.onb-step-dot--waiting { background: var(--warning); }
.onb-step-dot--error { background: var(--error); }
.onb-step-dot--skipped { background: var(--text-muted); }
.onb-step-label { flex: 1 1 auto; }
.onb-step-count { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.onb-step-note { color: var(--text-muted); font-size: var(--text-xs, 0.75rem); }
.onb-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: auto; }
.onb-armed { background: var(--warning); }
.badge-error { background: rgba(239, 68, 68, 0.15); color: var(--error); }

/* Onboarding pre-flight checklist modal (onb-cl-*) */
.onb-cl-progress { margin-bottom: var(--space-3); }
.onb-cl-group { margin: var(--space-4) 0 var(--space-2); }
.onb-cl-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary);
  margin-bottom: var(--space-2);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}
.onb-cl-row:hover { border-color: var(--border-hover); }
.onb-cl-row--done { opacity: 0.65; }
.onb-cl-row--done .onb-cl-label { text-decoration: line-through; }
.onb-cl-check { margin-top: 3px; accent-color: var(--brand-blue); flex: 0 0 auto; }
.onb-cl-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.onb-cl-label { color: var(--text-primary); font-size: var(--text-sm, 0.875rem); font-weight: 500; }
.onb-cl-hint { color: var(--text-muted); font-size: var(--text-xs, 0.75rem); }
.onb-cl-auto {
  display: inline-block;
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  background: rgba(51, 161, 253, 0.15);
  color: var(--brand-blue);
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  vertical-align: middle;
}
.onb-cl-cred {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  margin: var(--space-2) 0 var(--space-3);
  border: 1px solid var(--border-active);
  border-radius: var(--radius-sm);
  background: var(--bg-secondary);
}
.onb-cl-cred-row { display: flex; gap: var(--space-2); align-items: center; }
.onb-cl-cred-row .form-input { flex: 1 1 0; }
.onb-cl-cred-status { font-size: var(--text-xs, 0.75rem); }
.onb-cl-cred-status--ok { color: var(--success); }

/* ── Pending Booking Requests panel (public/js/views/pending-bookings.js) ─────
   Live list of online booking requests still awaiting a hand-keyed MyTime booking, shown above the
   booking-form editor. Reuses .editor-settings-panel chrome + .editor-modal-list-row rows; only the
   layout scaffolding below is new. Tokens only — see .claude/rules/portal-styling.md. */
/* Match the booking-form editor's centered 1100px column so the two read as one page. */
.pending-reqs { display: block; max-width: 1100px; margin: 0 auto; }
.pending-reqs-panel {
  margin-bottom: var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
.pending-reqs-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: 4px;
}
.pending-reqs-head .editor-modal-field-label { margin-bottom: 0; }
.pending-reqs-count {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}
.pending-reqs-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.pending-reqs-row { align-items: flex-start; }
.pending-reqs-main { min-width: 0; flex: 1 1 auto; }
.pending-reqs-main .editor-modal-list-meta { margin-top: 2px; }
.pending-reqs-actions {
  display: flex;
  gap: var(--space-2);
  flex: 0 0 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.pending-reqs-status { margin-top: var(--space-3); }
.pending-reqs-resolved { margin-top: var(--space-4); }
.pending-reqs-resolved > summary {
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
.pending-reqs-resolved > summary:hover { color: var(--text-primary); }
.pending-reqs-resolved .pending-reqs-row { margin-top: var(--space-2); }
@media (max-width: 700px) {
  .pending-reqs-row { flex-direction: column; align-items: stretch; }
  .pending-reqs-actions { justify-content: flex-start; }
}

/* ── Email-template PDF export modal ────────────────────────────────────── */
.export-modal input[type="checkbox"],
.export-modal input[type="radio"] {
  accent-color: var(--brand-blue);
  width: 16px;
  height: 16px;
  cursor: pointer;
  flex-shrink: 0;
  margin: 0;
}
.export-modal-list {
  max-height: 40vh;
  overflow-y: auto;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
}
.export-modal-group { margin-bottom: var(--space-2); }
.export-modal-group:last-child { margin-bottom: 0; }
.export-modal-group-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border-bottom: 1px solid var(--border-default);
  cursor: pointer;
}
.export-modal-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.export-modal-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--text-primary);
  transition: background var(--transition-fast);
}
.export-modal-row:hover { background: var(--bg-tertiary); }
.export-modal-row--all {
  margin-bottom: var(--space-2);
  border: 1px solid var(--border-default);
}
.export-modal-stage {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
}
.export-modal-count {
  margin-left: auto;
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.export-modal-options {
  border-top: 1px solid var(--border-default);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.export-modal-option-label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--text-primary);
}
.export-modal-option-label input { margin-top: 2px; }
.export-modal-hint {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 2px;
  line-height: 1.4;
}

/* S-SHELL-2 opt-in v3 UI. Marker is added only by mountV3SettingsUi(ctx.ia === 'v3'). */
[data-settings-v3-ui="1"] .ls-v3-disclosure.card { padding: 0; }
[data-settings-v3-ui="1"] .ls-v3-disclosure > summary {
  padding: var(--space-3) var(--space-4);
  list-style: none;
  font-size: var(--text-base);
}
[data-settings-v3-ui="1"] .ls-v3-disclosure > summary::-webkit-details-marker { display: none; }
[data-settings-v3-ui="1"] .ls-v3-disclosure-copy {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0 var(--space-3); min-width: 0; overflow-wrap: anywhere;
}
[data-settings-v3-ui="1"] .ls-v3-disclosure-copy .ls-muted { font-size: var(--text-sm); font-weight: normal; }
[data-settings-v3-ui="1"] .ls-v3-chevron { flex-shrink: 0; transition: transform var(--transition-fast); }
[data-settings-v3-ui="1"] .ls-v3-disclosure[open] > summary .ls-v3-chevron { transform: rotate(180deg); }
[data-settings-v3-ui="1"] .ls-v3-disclosure-body { padding: 0 var(--space-4) var(--space-4); }
[data-settings-v3-ui="1"] .ls-v3-tooltip {
  position: fixed; z-index: var(--z-tooltip); pointer-events: none;
  width: max-content; max-width: min(var(--tooltip-max-width), calc(100vw - (var(--space-4) * 2)));
  padding: var(--space-2) var(--space-3);
  color: var(--text); background: var(--bg-tertiary);
  border: thin solid var(--border-active); border-radius: var(--radius-sm);
  font-size: var(--text-xs); font-weight: normal; line-height: 1.4;
  white-space: normal; overflow-wrap: anywhere;
  opacity: 0; visibility: hidden; transition: opacity var(--tooltip-fade), visibility var(--tooltip-fade);
}
[data-settings-v3-ui="1"] .ls-v3-tooltip.is-visible { opacity: 1; visibility: visible; }
[data-settings-v3-ui="1"] :is([role="tabpanel"], input[type="checkbox"], summary):focus-visible {
  outline: calc(var(--space-1) / 2) solid var(--brand-blue);
  outline-offset: calc(var(--space-1) / 2);
}
@media (prefers-reduced-motion: reduce) {
  [data-settings-v3-ui="1"] .ls-v3-chevron,
  [data-settings-v3-ui="1"] .ls-v3-tooltip { transition: none; }
}

/* W1-01 / W1-16. M5-042: scoped to v3; OFF appearance is preserved. */
.settings-view[data-settings-v3-shell="1"],
.settings-view[data-settings-v3-shell="1"] *,
[data-settings-v3-ui="1"],
[data-settings-v3-ui="1"] * {
  scrollbar-width: thin;
  scrollbar-color: var(--text-muted) var(--bg-secondary);
}
.settings-view[data-settings-v3-shell="1"] input[type="number"],
[data-settings-v3-ui="1"] input[type="number"] {
  appearance: textfield;
  -moz-appearance: textfield;
}
.settings-view[data-settings-v3-shell="1"] input[type="number"]::-webkit-inner-spin-button,
.settings-view[data-settings-v3-shell="1"] input[type="number"]::-webkit-outer-spin-button,
[data-settings-v3-ui="1"] input[type="number"]::-webkit-inner-spin-button,
[data-settings-v3-ui="1"] input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
[data-settings-v3-shell="1"] .ls-dropdown,
[data-settings-v3-ui="1"] .ls-dropdown { width: 100%; min-width: 0; }
[data-settings-v3-shell="1"] .ls-dropdown-bar,
[data-settings-v3-ui="1"] .ls-dropdown-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); text-align: left; cursor: pointer;
}
[data-settings-v3-shell="1"] .ls-dropdown-caption,
[data-settings-v3-ui="1"] .ls-dropdown-caption { min-width: 0; overflow-wrap: anywhere; }
/* TL-FIX-29 C-01: a bar showing its placeholder is a prompt, not the seat's answer. */
[data-settings-v3-shell="1"] .ls-dropdown-caption.is-placeholder,
[data-settings-v3-ui="1"] .ls-dropdown-caption.is-placeholder { color: var(--text-secondary); }
[data-settings-v3-shell="1"] .ls-dropdown-bar[aria-expanded="true"],
[data-settings-v3-ui="1"] .ls-dropdown-bar[aria-expanded="true"] {
  border-color: var(--border-active); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
[data-settings-v3-shell="1"] .ls-dropdown-panel,
[data-settings-v3-ui="1"] .ls-dropdown-panel {
  position: static; width: 100%; max-height: calc(var(--space-16) * 4); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--text-muted) var(--bg-secondary);
  background: var(--bg-tertiary); border: thin solid var(--border-active); border-top: 0;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm); padding: var(--space-2);
  transform-origin: top; animation: ls-dropdown-open var(--transition-fast);
}
[data-settings-v3-shell="1"] .ls-dropdown-panel[hidden],
[data-settings-v3-ui="1"] .ls-dropdown-panel[hidden] { display: none; }
[data-settings-v3-shell="1"] .ls-dropdown-option,
[data-settings-v3-ui="1"] .ls-dropdown-option {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  width: 100%; padding: var(--space-2) var(--space-3); border: 0;
  border-radius: var(--radius-sm); background: transparent; color: var(--text-primary);
  font: inherit; text-align: left; cursor: pointer;
}
[data-settings-v3-shell="1"] .ls-dropdown-option:hover,
[data-settings-v3-ui="1"] .ls-dropdown-option:hover { background: var(--bg-elevated); }
[data-settings-v3-shell="1"] .ls-dropdown-option[aria-selected="true"],
[data-settings-v3-ui="1"] .ls-dropdown-option[aria-selected="true"] {
  background: var(--glow-subtle); color: var(--brand-blue);
}
[data-settings-v3-shell="1"] .ls-dropdown-check,
[data-settings-v3-ui="1"] .ls-dropdown-check { opacity: 0; }
[data-settings-v3-shell="1"] [aria-selected="true"] > .ls-dropdown-check,
[data-settings-v3-ui="1"] [aria-selected="true"] > .ls-dropdown-check { opacity: 1; }
[data-settings-v3-shell="1"] .ls-dropdown-option:focus-visible,
[data-settings-v3-ui="1"] .ls-dropdown-option:focus-visible {
  outline: calc(var(--space-1) / 2) solid var(--brand-blue); outline-offset: calc(var(--space-1) / -2);
}
[data-settings-v3-shell="1"] .ls-dropdown-option:disabled,
[data-settings-v3-ui="1"] .ls-dropdown-option:disabled { color: var(--text-muted); cursor: not-allowed; }
[data-settings-v3-shell="1"] .ls-dropdown-chevron,
[data-settings-v3-ui="1"] .ls-dropdown-chevron { transition: transform var(--transition-fast); }
[data-settings-v3-shell="1"] [aria-expanded="true"] .ls-dropdown-chevron,
[data-settings-v3-ui="1"] [aria-expanded="true"] .ls-dropdown-chevron { transform: rotate(180deg); }
@keyframes ls-dropdown-open {
  from { opacity: 0; transform: translateY(calc(var(--space-1) * -1)) scaleY(.98); }
  to { opacity: 1; transform: translateY(0) scaleY(1); }
}
@media (prefers-reduced-motion: reduce) {
  [data-settings-v3-shell="1"] .ls-dropdown-panel,
  [data-settings-v3-ui="1"] .ls-dropdown-panel { animation: none; }
  [data-settings-v3-shell="1"] .ls-dropdown-chevron,
  [data-settings-v3-ui="1"] .ls-dropdown-chevron { transition: none; }
}

/* Intake records reuse Booking Form submissions' card/table, existing buttons and v3 dropdown.
   Only the eight-column responsive fit is scoped here; sibling geometry is untouched. */
.intake-submissions { min-width: 0; }
.intake-submissions-heading, .intake-submissions-pages { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-6); }
.intake-submissions-heading h1 { margin: 0; font-size: var(--text-xl); }
.intake-submissions input[type="date"] { color-scheme: dark; }
.intake-submissions-filters { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); align-items: start; margin-bottom: var(--space-4); }
.intake-submissions-filters .form-group { margin: 0; min-width: 0; }
.intake-submissions .bf-pricing-table { table-layout: fixed; }
.intake-submissions .bf-pricing-table th, .intake-submissions .bf-pricing-table td { overflow-wrap: anywhere; }
/* Archive attribution keeps words intact; reserve room in the desktop table. */
.intake-submissions .bf-pricing-table:has(td[data-label="Actions"]) th:last-child {
  width: calc(var(--space-16) * 3); min-width: calc(var(--space-16) * 3);
}
.intake-submissions .bf-pricing-table td[data-label="Actions"] {
  word-break: normal; overflow-wrap: normal;
}
.intake-submissions-caption { text-align: left; color: var(--text-secondary); padding-bottom: var(--space-3); }
.intake-submissions-pages[hidden], #training-intake-submissions-link[hidden] { display: none; }
@media (max-width: 900px) {
  .intake-submissions-filters { grid-template-columns: minmax(0, 1fr); }
  .intake-submissions .bf-pricing-table, .intake-submissions .bf-pricing-table tbody { display: block; }
  .intake-submissions-caption { display: block; width: 100%; }
  .intake-submissions .bf-pricing-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .intake-submissions .bf-pricing-table tr { display: block; padding-block: var(--space-3); border-bottom: thin solid var(--border-default); }
  .intake-submissions .bf-pricing-table td { display: block; width: 100%; border-bottom: 0; }
  .intake-submissions .bf-pricing-table td::before { content: attr(data-label); display: block; color: var(--text-secondary); margin-bottom: var(--space-1); }
}

/* Training intake: shared tooltip exposes full contact text on focus and hover. */
.intake-submissions .intake-contact-value {
  display: block; max-width: calc(var(--space-16) * 3); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; overflow-wrap: normal; word-break: normal;
}

/* Shared in-page date picker, TL-FIX-8. */
[data-settings-v3-ui="1"] .ls-date-panel { padding: var(--space-3); max-height: none; }
[data-settings-v3-ui="1"] .ls-date-navigation { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); margin-block: var(--space-3); }
[data-settings-v3-ui="1"] .ls-date-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-1); text-align: center; }
[data-settings-v3-ui="1"] .ls-date-grid .ls-dropdown-option { justify-content: center; padding: var(--space-2) 0; }
[data-settings-v3-ui="1"] .ls-date-grid [aria-pressed="true"] { background: var(--brand-blue-btn); color: var(--text-primary); }

/* Shared in-page TIME picker, TL-FIX-24 (VISUAL-AUDIT-6 TL-8 / ui-consistency 1, 4, 5, 11, 13).
   The native control stays in the page so every binder, the dirty check and the leave guard read
   the same element; it is given no size and no OS chrome, and the themed bar above it is what a
   store seat sees. Tokens only, and the panel expands DOWN in flow like every other v3 chooser. */
[data-settings-v3-shell="1"] .ls-time-picker,
[data-settings-v3-ui="1"] .ls-time-picker { position: relative; }
[data-settings-v3-shell="1"] .ls-time-native,
[data-settings-v3-ui="1"] .ls-time-native {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  width: 0; height: 0; min-width: 0; min-height: 0; padding: 0; margin: 0; border: 0;
  opacity: 0; overflow: hidden; pointer-events: none;
  appearance: none; -webkit-appearance: none;
}
[data-settings-v3-shell="1"] .ls-time-native::-webkit-calendar-picker-indicator,
[data-settings-v3-ui="1"] .ls-time-native::-webkit-calendar-picker-indicator { display: none; }
[data-settings-v3-shell="1"] .ls-time-panel,
[data-settings-v3-ui="1"] .ls-time-panel { max-height: none; padding: var(--space-3); }
[data-settings-v3-shell="1"] .ls-time-columns,
[data-settings-v3-ui="1"] .ls-time-columns {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); align-items: start;
}
[data-settings-v3-shell="1"] .ls-time-column-label,
[data-settings-v3-ui="1"] .ls-time-column-label {
  margin: 0 0 var(--space-2); color: var(--text-secondary); font-size: var(--text-sm);
}
[data-settings-v3-shell="1"] .ls-time-column,
[data-settings-v3-ui="1"] .ls-time-column {
  display: flex; flex-direction: column; gap: var(--space-1);
  /* TL-FIX-29 TL-22 (VISUAL-AUDIT-8): 3 x 64 = 192 is EXACTLY four 48 px rows at the coarse tier
     (44 px option + 4 px gap), so at rest nothing reached the fade and the scroll affordance was
     invisible on an unset field. 212 is a non-multiple of the pitch at every tier, so a partial
     row is always in view and the fade always has something to fade. */
  max-height: calc(var(--space-16) * 3 + var(--space-5)); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--text-muted) var(--bg-secondary);
}
[data-settings-v3-shell="1"] .ls-time-column .ls-dropdown-option,
[data-settings-v3-ui="1"] .ls-time-column .ls-dropdown-option { justify-content: center; }
[data-settings-v3-shell="1"] .ls-time-clear,
[data-settings-v3-ui="1"] .ls-time-clear { margin-top: var(--space-3); }
[data-settings-v3-shell="1"] .ls-time-clear[hidden],
[data-settings-v3-ui="1"] .ls-time-clear[hidden] { display: none; }

/* -- TL-FIX-27 - the opened panel breaks out to its form section at the coarse-pointer tier ------
   HUB-CAPTURE-9 §6 item 1: on `hours-availability` a time bar shares its row with its pair, so at
   390 the bar is 172 px and the panel with it. Three columns do not fit: `AM or PM` wrapped to two
   lines and all 26 option boxes measured 43.3 px wide, under the 44 px floor, against 0 of 26 in
   the 356 px panel on `booking-rules` at the same width. The bars stay paired - stacking them
   doubles the height of every one of the seven weekday rows - and the OPENED panel alone spans the
   section. It is still IN FLOW: `position` stays static (never set here), the left edge is the
   section's, and the panel's height still pushes what follows down. The picker supplies the two
   measured numbers; every declaration is here. */
@media (max-width: 560px) {
  [data-settings-v3-shell="1"] .ls-time-picker[data-time-breakout="1"] > .ls-time-panel,
  [data-settings-v3-ui="1"] .ls-time-picker[data-time-breakout="1"] > .ls-time-panel {
    width: var(--ls-time-breakout-width);
    max-width: none;
    margin-inline-start: var(--ls-time-breakout-offset);
  }
}

/* -- TL-FIX-27 / TL-FIX-28 - the pair keeps its place while one of its panels is open ----------
   The pair is bottom-aligned so two bars keep one baseline when their labels wrap differently. An
   open panel makes its own cell ~304 px taller, and bottom alignment then drops the OTHER bar of
   the pair to the foot of the panel. While one panel is open the row's cells align to the top
   instead, which is where both bars already sit. `align-self` on the CELLS, not `align-items` on
   the container, because the container's alignment is an inline style the page module writes and
   nothing but `!important` outranks an inline style.
   TL-FIX-28 moves this rule OUT of the 560 px tier it shipped in. The drop is not a phone
   behaviour: TL-FIX-27 measured the sibling falling +294.1 px at 768 and +294.6 px at 1440, where
   the rule was never in scope, because `align-items: end` is written inline at every width. The
   panel BREAKOUT above stays in the coarse tier - that one really is a phone fix - and the picker
   now marks its host section on every open rather than only when it breaks out. The rule is inert
   unless something carries the attribute, which only an OPEN panel does, and it is inert again the
   moment the panel closes. */
[data-settings-v3-shell="1"] [data-ls-time-breakout-host="1"] > *,
[data-settings-v3-ui="1"] [data-ls-time-breakout-host="1"] > * { align-self: start; }

/* -- TL-FIX-27 - a column that GENUINELY scrolls says so ---------------------------------------
   HUB-CAPTURE-9 §6 item 2: Hour and Minutes each hide 380 px of scroll at EVERY width, and this
   Chromium reserves no scrollbar gutter for them at all (measured: offsetWidth - clientWidth = 0 on
   33 of 33 readings), so the `scrollbar-width: thin` above buys no affordance a seat can see. Same
   class TL-FIX-23 fixed for `.bf-tabs` one component over, and the same mechanism: the column's OWN
   block scroll progress drives the fade, so a column with nothing to scroll to has an INACTIVE
   timeline, its two lengths stay 0 and it renders exactly as it does today.
   What is NOT the same as `.bf-tabs`: the two edges are animated as REGISTERED <length> custom
   properties rather than by interpolating `mask-image` itself. Measured in this lane: `mask-image`
   interpolates DISCRETELY, so a column scrolled to 56% snapped to the end keyframe and lost its
   bottom fade while 166 px of options were still below it - an affordance that says the opposite of
   the truth in the middle of its own range. A registered <length> interpolates continuously, so the
   fade is at the bottom at the start, at the top at the end, and at BOTH in between - which is
   where a chosen 6:30 puts the column on open. `scrollbar-width` and `scrollbar-color` still travel
   together above (ui-consistency rule 10); this adds to them, it does not replace them. Behind
   @supports, so a browser without scroll timelines keeps today's rendering rather than a fade it
   could never retract. */
@supports (animation-timeline: scroll(self block)) {
  @property --ls-time-fade-top { syntax: "<length>"; inherits: false; initial-value: 0px; }
  @property --ls-time-fade-bottom { syntax: "<length>"; inherits: false; initial-value: 0px; }
  [data-settings-v3-shell="1"] .ls-time-column,
  [data-settings-v3-ui="1"] .ls-time-column {
    mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0px, #000 var(--ls-time-fade-top),
      #000 calc(100% - var(--ls-time-fade-bottom)), rgba(0, 0, 0, 0) 100%);
    animation: ls-time-column-edge-fade linear both;
    animation-timeline: scroll(self block);
  }
  @keyframes ls-time-column-edge-fade {
    from { --ls-time-fade-top: 0px; --ls-time-fade-bottom: 28px; }
    to { --ls-time-fade-top: 28px; --ls-time-fade-bottom: 0px; }
  }
}

/* Shared fixed-catalogue multi-choice chips, including training email weekdays. */
.ls-chip-choices { width: 100%; }
.ls-chip-choices .ls-chip[aria-pressed="true"] {
  color: var(--text-primary);
  background: var(--brand-blue-btn);
  border-color: var(--border-active);
}
.ls-chip-choices .ls-chip:focus-visible {
  outline: var(--space-1) solid var(--border-active);
  outline-offset: var(--space-1);
}

/* ============================================
   Settings pages — Kennels (.kn-v3) and Dogs & records (.dr-page)
   D-15: these rules were injected as a <style> block inside each page's rendered
   HTML. Page-level CSS competing with this stylesheet is how the 44px touch floor
   was lost once already (D-5), and an injected block is invisible to any sweep that
   reads the stylesheet. They live here now; the pages render markup only.
   ============================================ */
.kn-v3 [hidden]{display:none!important}
.kn-v3 .kn-pair,.kn-v3 .kn-heading,.kn-v3 .ls-chips{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:start}
.kn-v3 .kn-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch}
.kn-v3 .kn-pair> *{min-width:0;display:grid;grid-template-rows:subgrid;grid-row:span 2}
.kn-v3 .kn-pair .form-label{display:block}
.kn-v3 .form-input{width:100%}
.kn-v3 .kn-pool,.kn-v3 .kn-rule{border:1px solid var(--border-default);border-radius:var(--radius-md);padding:var(--space-4);margin-block:var(--space-3)}
.kn-v3 .kn-heading{justify-content:space-between}
.kn-v3 fieldset{border:0;padding:0;min-width:0}
.kn-v3 .kn-editor{margin-block:var(--space-4)}
/* TL-FIX-29 C-04: openPool scrolls the editor to the top of the scrollport, where the sticky
   .top-nav covered Cancel -- the editor's only exit (ui-consistency rule 8). Reserve the header.
   Its OWN rule: tl-fix-17-3's D-15 registry holds the rule above byte-identically. */
.kn-v3 .kn-editor{scroll-margin-top:calc(var(--nav-height) + var(--space-4))}
.kn-v3 dialog{max-width:100%;background:var(--bg-secondary);color:var(--text-primary)}

/* -- TL-FIX-31 - the Kennels pairs stack at the coarse-pointer tier ----------------------------
   HUB-CAPTURE-12 §7 observations 2 and 4: `.kn-v3 .kn-pair` is a fixed TWO-column grid at every
   width, so at 390 each bar inside one measured 156 px on a 324 px section while every sibling
   control on the page measured 308 px. The grid keeps both tracks even when only ONE child is in
   it, which is why `Filter pools by weight range` - the only child of the filter pair on a store
   with no suite-stay filter - was 156 px as well. At that width the prompts wrap inside their own
   bars (`Add a boarding service` to three lines, h 77; `All weight ranges` to two, h 60), the two
   bars of one row render visibly unequal, and the opened panel inherits the 156 px so every option
   wraps with it. One column at <=560 px gives each bar the section's full width - which is where
   the four `Kennel priority per dog size` bars beside them already are.
   SCOPED to the Kennels pair on purpose: `.dr-pair` (Dogs & records, TL-FIX-23), `.bf-tabs`,
   `.ls-time-*` (TL-FIX-27) and `.ti-qr-*` are four other multi-column shapes at this tier and none
   of them is this defect. Only the TRACK COUNT moves: the children keep their `grid-row: span 2`
   subgrid, so each bar still sits under its own label exactly as it does above 560 px.
   The DOUBLED class is the TL-FIX-17 D-5 structural guard, for the same reason it was used there:
   this page's layout rules were page-level injected `<style>` until D-15 moved them here, the
   injected form of this exact rule is `.kn-v3 .kn-pair{…repeat(2,…)}` at (0,2,0), and a tier that
   only matched it could be put back to two columns by any page-level style without having to beat
   anything. At (0,3,0) it cannot. It is also what keeps TL-FIX-17-3's D-15 relocation fingerprint
   honest: that test re-injects the old block and compares the two renderings, so a deliberate
   later change has to be present on BOTH sides or the test freezes this page forever —
   TL-FIX-23's `.dr-fieldset .dr-pair` reflow sits one class above `.dr-pair` for the same reason. */
@media (max-width: 560px) {
  .kn-v3.kn-v3 .kn-pair { grid-template-columns: minmax(0, 1fr); }
}
.dr-page [hidden]{display:none!important}
.dr-tabs{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.dr-section{margin-top:var(--space-4)}
.dr-disclosure{font:inherit;color:inherit;background:transparent;border:0;cursor:pointer;display:flex;justify-content:space-between;width:100%;text-align:left;padding:0}
.dr-fieldset{border:0;padding:0;margin:0;min-width:0}
.dr-fieldset:disabled{opacity:.6}
.dr-table{width:100%;border-collapse:collapse}
.dr-table th{text-align:left}
.dr-table td{padding:var(--space-2);vertical-align:bottom}
.dr-table input{width:100%;min-width:0;box-sizing:border-box}
.dr-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:end;gap:var(--space-3)}
.dr-field{display:flex;flex-direction:column;min-width:0;gap:var(--space-2)}
.dr-field .form-input{box-sizing:border-box;min-width:0;width:100%}
.dr-rule{margin-block:var(--space-3)}
.dr-rule .ls-chips{flex-wrap:wrap}
.dr-tabs [aria-selected=true]{color:var(--brand-blue)}

/* ============================================
   Forms — Training intake QR card (.ti-qr-*)
   TL-FIX-22 E-6: these rules lived in public/css/training-intake-editor.css, which is appended to
   the document ONLY by the draft-layout probe in public/js/views/training-intake-layout.js. On the
   card at rest they therefore never loaded: the four preset swatches collapsed to 4x4 px and both
   colour inputs rendered as white OS chrome at 50x27 px on the dark card (VISUAL-AUDIT-5 E-6,
   measured on the Deerfield control). Same move D-15 made for the injected <style> blocks — a rule
   the shared stylesheet does not carry is a rule no stylesheet sweep can see.
   ============================================ */
.ti-qr-preview { display: block; width: 200px; height: 200px; background: #ffffff; border: 1px solid var(--border-default); border-radius: var(--radius-sm); margin: var(--space-2) 0 var(--space-3); }
.ti-qr-colors { display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-6); }
.ti-qr-colors .form-group { flex: 1 1 220px; margin: 0; }
.ti-qr-pick { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-2); flex-wrap: wrap; }
/* The control IS the swatch: 44px square (touch floor), token border/radius/background. Each engine
   paints the native swatch through its own pseudo-element, so `appearance: none` alone does not
   remove the OS chrome — both are made borderless so the chosen colour fills the control. */
.ti-qr-pick input[type="color"] { -webkit-appearance: none; appearance: none; width: 44px; height: 44px; min-height: 44px; padding: 3px; border: 1px solid var(--border-default); border-radius: var(--radius-sm); background: var(--bg-elevated); cursor: pointer; }
.ti-qr-pick input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.ti-qr-pick input[type="color"]::-webkit-color-swatch { border: none; border-radius: calc(var(--radius-sm) - 3px); }
.ti-qr-pick input[type="color"]::-moz-color-swatch { border: none; border-radius: calc(var(--radius-sm) - 3px); }
.ti-qr-pick .form-input { width: 120px; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
.ti-qr-swatches { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.ti-qr-swatch { width: 28px; height: 28px; padding: 0; border: 1px solid var(--border-default); border-radius: var(--radius-sm); cursor: pointer; }
/* Rule 7: the current choice is visibly selected, in the portal's accent. The rule this replaces
   read `var(--accent, var(--primary, #e31837))` — neither token is defined in this stylesheet, so
   it resolved to a hardcoded hex (rule 2). --brand-blue is what .bf-tab.active already uses. */
.ti-qr-swatch.is-current { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.ti-qr-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
/* ── TL-FIX-23 · TL-12 (HUB-VISUAL-AUDIT-6) ──────────────────────────────────────
   A preset swatch IS a control — it sets the colour — and the hub's floor for every non-chip
   control at the coarse-pointer tier is 44 px. E-6 brought these rules into the shared stylesheet
   at their authored 28 px, which is what the card read at 390. The colour input beside them is
   already 44 px, so a seat on a phone met two controls of the same kind at two different sizes.
   28 px stays wherever a mouse is the pointer, so the desktop card is unchanged. The media list is
   width OR pointer, because a narrow desktop window is not a touch target and a wide tablet is. */
@media (max-width: 560px), (pointer: coarse) {
  /* The selector is the CHILD form on purpose. TL-FIX-22's E-6 red leg deletes the top-level
     `.ti-qr-swatch { ... }` rule from a copy of this file and requires the selector to disappear;
     a second rule whose selector is the bare `.ti-qr-swatch` would survive that cut and the
     neighbour's red leg would stop going red — a guard silently disarmed by an unrelated fix
     (PB-00040). `.ti-qr-swatches .ti-qr-swatch` reaches the same buttons, wins on specificity,
     and is a different selector string, so that leg still fires. Measured: it did break, in the
     whole-suite run, and this is the repair. */
  .ti-qr-swatches .ti-qr-swatch { width: 44px; height: 44px; }
}

/* ── Settings sizing + link-button reset (owner D-12 style sweep, 2026-09-25; PLAN-B, PLANB-R1) ──────────────
   Began as a stylesheet hotfix; the hides it carried were removed at source in PLANB-R1 (v3/booking-rules.js no longer
   renders the Overall first-time summary, the pet-details sentence, the Notice link or the Rules sections — owner D-12,
   D-13). What stays is permanent styling. Record: dogstop docs/builds/settings-loom-walkthrough/DECISIONS.md D-12/D-13. */
/* A <button> carrying .ls-link must read as a link, never as a white browser-default button. */
button.ls-link { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: var(--brand-blue); cursor: pointer; }
button.ls-link:hover { text-decoration: underline; }
/* "Pricing checked by Rogue AI" per-service names are sub-labels (the only <h3> in v3/booking-rules.js): form-label scale. */
[data-br-panel] .ls-field > h3 { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); margin: var(--space-3) 0 var(--space-1); }
/* Sub-group headings inside settings cards (Booking rules add-on question groups = the only <h4> in v3/booking-rules.js;
   Kennels "Kennels not in any pool" = the <h3> inside .kn-editor): form-label scale. */
[data-br-panel] h4, .kn-editor h3 { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); margin: var(--space-3) 0 var(--space-1); }
/* Kennels: the "Edit pool" title takes the card-title scale; a pool name inside "Your pools" takes body size, semibold. */
.kn-v3 .kn-editor > h2 { font-size: var(--text-lg); }
.kn-v3 .kn-pool .kn-heading h3 { font-size: var(--text-body); font-weight: 600; }

/* ── Training library (PLANB-R1, 2026-09-25, owner D-16) ─────────────────────────────────────────────────────
   Library: categories (only those with something to open) + search + cards with a signed thumbnail and a length badge.
   Item page: Back · the series as tabs · the video (poster, captions, nothing autoplays) · transcript · title · the
   written guide (one .card per Settings card, top to bottom) · Previous / Next. Tokens only; shared classes reused.
   Research + decisions: dogstop docs/builds/settings-loom-walkthrough/research/TRAINING-LIBRARY-RESEARCH.md */
.training-layout { min-height: 400px; }
.training-sidebar { width: 200px; flex-shrink: 0; }
.training-sidebar-title { font-size: var(--text-sm); font-weight: 600; color: var(--text-secondary); margin: 0 0 var(--space-4); }
.training-content { flex: 1; min-width: 0; }
/* The category list items are real buttons now (keyboard + screen reader); they keep the .category-item look. */
button.category-item { display: block; width: 100%; text-align: left; background: none; border: 0; font-family: var(--font-family); line-height: 1.4; }
button.category-item:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: -2px; }
.category-tree li + li { margin-top: 2px; }
.training-skeleton { height: 200px; grid-column: 1 / -1; }
/* Cards are links now (open in a new tab, keyboard focus); they keep the .content-card look. */
a.content-card { display: block; color: inherit; text-decoration: none; }
a.content-card:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
.content-card-desc { color: var(--text-secondary); } /* --text-muted fails 4.5:1 at --text-xs on the card (see .ls-who) */
.content-card-duration {
  position: absolute; right: var(--space-2); bottom: var(--space-2);
  padding: 2px var(--space-2); border-radius: var(--radius-sm);
  background: var(--bg-primary); color: var(--text-primary);
  font-size: var(--text-xs); font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.4;
}

.training-item { max-width: 960px; display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.training-item > .btn-back, .training-item-body > .training-open-tab { align-self: flex-start; }
/* PLANB-R6 (owner D-25): everything below the header (Back + series tabs) is ONE body. Moving to another video in the
   series keeps the header and dims this body out, then fades the new one up — never a page-wide loading block. */
.training-item-body { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0;
  transition: opacity var(--transition-base), transform var(--transition-base); }
.training-item-body.is-leaving { opacity: 0.35; pointer-events: none; }
.training-item-body.is-entering { opacity: 0; transform: translateY(6px); }
@media (prefers-reduced-motion: reduce) {
  .training-item-body { transition: none; }
  .training-item-body.is-entering { transform: none; }
}
.training-item .btn-sm { min-height: 44px; }
/* Series tabs WRAP instead of scrolling (owner 2026-09-20: nothing scrolls sideways on a phone). Each tab carries its
   own underline, so a tab on a second row still shows its own selected state. */
.bf-tabs.training-series-tabs { flex-wrap: wrap; overflow: visible; row-gap: var(--space-1); animation: none; mask-image: none; }
.bf-tabs.training-series-tabs .bf-tab { text-decoration: none; }
.bf-tabs.training-series-tabs .bf-tab:focus-visible { outline-offset: 2px; }
.video-player-wrap { position: relative; }
.video-player-wrap video { display: block; }
.video-player-error { display: none; }
.video-player-wrap--error .video-player-error {
  display: flex; align-items: center; justify-content: center; position: absolute; inset: 0; margin: 0;
  padding: var(--space-6); text-align: center; background: var(--bg-secondary); color: var(--text-primary); font-size: var(--text-sm);
}
.training-title { font-size: var(--text-2xl); margin: var(--space-2) 0 0; }
.training-meta { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; }
.training-description { font-size: var(--text-base); line-height: 1.6; color: var(--text-secondary); margin: 0; max-width: 72ch; }
.training-document { display: flex; flex-direction: column; align-items: center; gap: var(--space-4); text-align: center; }
.training-guide { display: flex; flex-direction: column; gap: var(--space-4); border-top: 1px solid var(--border-default); padding-top: var(--space-6); }
.training-guide-title { font-size: var(--text-xl); margin: 0; }
.training-guide-intro { font-size: var(--text-base); line-height: 1.7; color: var(--text-secondary); margin: 0; max-width: 72ch; }
.training-guide-intro strong, .training-guide-section strong { color: var(--text-primary); font-weight: 600; }
.training-guide-section h3 { font-size: var(--text-lg); margin: 0 0 var(--space-3); }
.training-guide-section p, .training-guide-section li { font-size: var(--text-base); line-height: 1.7; color: var(--text-secondary); max-width: 72ch; }
.training-guide-section p { margin: 0 0 var(--space-3); }
.training-guide-section ul { margin: 0 0 var(--space-3); padding-left: var(--space-6); }
.training-guide-section li { margin-bottom: var(--space-2); }
.training-guide-section > :last-child { margin-bottom: 0; }
.training-guide-note { border-left: 3px solid var(--brand-blue); padding-left: var(--space-4); }
.training-guide-standard { background: var(--bg-tertiary); border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4); }
.training-guide-standard-label { color: var(--text-primary); font-weight: 600; }
.training-watch { margin: 0 0 var(--space-3); }
.training-guide-figure { margin: var(--space-4) 0 0; }
.training-guide-figure img { display: block; width: 100%; height: auto; border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--bg-tertiary); }
.training-guide-figure figcaption { font-size: var(--text-xs); color: var(--text-secondary); margin-top: var(--space-2); }
.training-pager { display: flex; flex-wrap: wrap; gap: var(--space-3); border-top: 1px solid var(--border-default); padding-top: var(--space-6); }
.training-pager .btn { max-width: 100%; white-space: normal; text-align: left; line-height: 1.3; min-height: 44px; }
.training-pager-next { margin-left: auto; }
@media (max-width: 480px) {
  .training-guide-section.card { padding: var(--space-4); }
  .training-pager .btn { width: 100%; justify-content: center; text-align: center; }
  .training-pager-next { margin-left: 0; }
}

/* ── Training video player (PLANB-R3, 2026-09-25, owner D-19: "a similar layout … as YouTube video player bottoms") ──
   Progress bar across the top of a bottom bar; play · volume · time on the left, CC · speed · full screen on the
   right; the bar fades while playing and the pointer is still. Captions are drawn above the bar. */
.vp { position: relative; outline: none; --vp-pct: 0%; --vp-buf: 0%; --vp-vol: 100%;
  --vp-scrim: color-mix(in srgb, var(--bg-primary) 88%, transparent); --vp-rail: color-mix(in srgb, var(--text-primary) 22%, transparent);
  --vp-rail-buf: color-mix(in srgb, var(--text-primary) 45%, transparent); }
.vp:focus-visible { box-shadow: 0 0 0 2px var(--brand-blue); }
.vp .vp-video { width: 100%; height: 100%; display: block; cursor: pointer; background: var(--bg-primary); }
.vp.vp--idle, .vp.vp--idle .vp-video { cursor: none; }
.vp-big-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 72px; height: 50px;
  display: flex; align-items: center; justify-content: center; border: 0; border-radius: var(--radius-md); cursor: pointer;
  background: color-mix(in srgb, var(--bg-primary) 78%, transparent); color: var(--text-primary);
  transition: background var(--transition-fast), transform var(--transition-fast); }
.vp-big-play:hover, .vp-big-play:focus-visible { background: var(--brand-blue-btn); }
.vp-big-play:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 2px; }
.vp-big-play svg { width: 32px; height: 32px; fill: currentColor; }
.vp.vp--started .vp-big-play { display: none; }
.vp-spinner { position: absolute; left: 50%; top: 50%; width: 48px; height: 48px; margin: -24px 0 0 -24px; border-radius: 50%;
  border: 4px solid var(--vp-rail); border-top-color: var(--brand-blue); display: none; animation: vp-spin 0.9s linear infinite; pointer-events: none; }
.vp.vp--waiting.vp--started .vp-spinner { display: block; }
@keyframes vp-spin { to { transform: rotate(360deg); } }
.vp-captions { position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%); width: max-content; max-width: 88%;
  display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; pointer-events: none;
  transition: bottom var(--transition-base); }
.vp.vp--idle .vp-captions { bottom: var(--space-4); }
.vp-caption-line { background: color-mix(in srgb, var(--bg-primary) 82%, transparent); color: var(--text-primary);
  font-size: var(--text-base); line-height: 1.35; padding: 1px var(--space-2); border-radius: var(--radius-sm); }
.vp.vp--fs .vp-caption-line { font-size: var(--text-2xl); }
.vp-bar { position: absolute; left: 0; right: 0; bottom: 0; padding: var(--space-8) var(--space-3) 0;
  background: linear-gradient(to top, var(--vp-scrim), transparent); transition: opacity var(--transition-base); }
.vp.vp--idle .vp-bar { opacity: 0; pointer-events: none; }
.vp-progress { display: flex; align-items: center; height: 14px; }
.vp-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); height: 48px; }
.vp-left, .vp-right { display: flex; align-items: center; gap: 2px; min-width: 0; }
.vp-btn { width: 40px; height: 40px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; padding: 0;
  border: 0; border-radius: var(--radius-sm); background: none; color: var(--text-primary); cursor: pointer; opacity: 0.9;
  transition: opacity var(--transition-fast); }
.vp-btn:hover { opacity: 1; }
.vp-btn:focus-visible { opacity: 1; outline: 2px solid var(--brand-blue); outline-offset: -2px; }
.vp-btn svg { width: 24px; height: 24px; fill: currentColor; }
.vp-btn[hidden] { display: none; }
.vp-cc { position: relative; }
.vp-cc[aria-pressed="true"]::after { content: ''; position: absolute; left: 10px; right: 10px; bottom: 6px; height: 3px;
  border-radius: 2px; background: var(--brand-blue); }
.vp-time { padding: 0 var(--space-2); font-size: var(--text-sm); color: var(--text-primary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.vp-sep { color: var(--text-secondary); }
.vp-volume { display: flex; align-items: center; }
.vp-vol { width: 0; opacity: 0; transition: width var(--transition-base), opacity var(--transition-base); }
.vp-volume:hover .vp-vol, .vp-volume:focus-within .vp-vol { width: 64px; opacity: 1; margin-right: var(--space-1); }
/* Range inputs: one rail for seek and volume; the played part is brand blue. Standard + WebKit + Firefox parts. */
.vp input[type="range"] { -webkit-appearance: none; appearance: none; margin: 0; background: transparent; cursor: pointer; height: 14px; }
.vp-seek { width: 100%; }
.vp input[type="range"]:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; border-radius: 2px; }
.vp-seek::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; transition: height var(--transition-fast);
  background: linear-gradient(to right, var(--brand-blue) var(--vp-pct), var(--vp-rail-buf) var(--vp-pct) var(--vp-buf), var(--vp-rail) var(--vp-buf)); }
.vp-seek::-moz-range-track { height: 4px; border-radius: 2px; border: 0;
  background: linear-gradient(to right, var(--brand-blue) var(--vp-pct), var(--vp-rail-buf) var(--vp-pct) var(--vp-buf), var(--vp-rail) var(--vp-buf)); }
.vp-progress:hover .vp-seek::-webkit-slider-runnable-track { height: 6px; }
.vp-vol::-webkit-slider-runnable-track { height: 4px; border-radius: 2px;
  background: linear-gradient(to right, var(--text-primary) var(--vp-vol), var(--vp-rail) var(--vp-vol)); }
.vp-vol::-moz-range-track { height: 4px; border-radius: 2px; border: 0;
  background: linear-gradient(to right, var(--text-primary) var(--vp-vol), var(--vp-rail) var(--vp-vol)); }
.vp input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; margin-top: -4.5px;
  border: 0; border-radius: 50%; background: var(--brand-blue); transform: scale(0); transition: transform var(--transition-fast); }
.vp input[type="range"]::-moz-range-thumb { width: 13px; height: 13px; border: 0; border-radius: 50%; background: var(--brand-blue);
  transform: scale(0); transition: transform var(--transition-fast); }
.vp-vol::-webkit-slider-thumb { background: var(--text-primary); }
.vp-vol::-moz-range-thumb { background: var(--text-primary); }
.vp-progress:hover .vp-seek::-webkit-slider-thumb, .vp-seek:focus-visible::-webkit-slider-thumb, .vp-vol::-webkit-slider-thumb { transform: scale(1); }
.vp-progress:hover .vp-seek::-moz-range-thumb, .vp-seek:focus-visible::-moz-range-thumb, .vp-vol::-moz-range-thumb { transform: scale(1); }
.vp-progress:hover .vp-seek::-webkit-slider-thumb { margin-top: -3.5px; }
.vp-settings { position: relative; }
.vp-menu { position: absolute; right: 0; bottom: calc(100% + var(--space-2)); min-width: 176px; padding: var(--space-2) 0;
  background: var(--bg-elevated); border: 1px solid var(--border-default); border-radius: var(--radius-md); box-shadow: 0 8px 24px var(--vp-scrim);
  z-index: 2; }
.vp-menu[hidden] { display: none; }
.vp-menu-title { padding: var(--space-1) var(--space-4) var(--space-2); font-size: var(--text-xs); font-weight: 600; color: var(--text-secondary); }
.vp-menu-item { display: flex; align-items: center; width: 100%; min-height: 36px; padding: 0 var(--space-4) 0 var(--space-8); border: 0;
  background: none; color: var(--text-primary); font-family: var(--font-family); font-size: var(--text-sm); text-align: left; cursor: pointer; position: relative; }
.vp-menu-item:hover, .vp-menu-item:focus-visible { background: var(--glow-subtle); outline: none; }
.vp-menu-item[aria-checked="true"]::before { content: '\2713'; position: absolute; left: var(--space-3); color: var(--brand-blue); }
.vp:fullscreen { width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; background: var(--bg-primary); }
.vp:fullscreen .vp-video { object-fit: contain; }
@media (max-width: 480px) {
  .vp-bar { padding: var(--space-6) var(--space-1) 0; }
  .vp-row { height: 42px; gap: 0; }
  .vp-btn { width: 36px; height: 36px; }
  .vp-btn svg { width: 22px; height: 22px; }
  .vp-volume { display: none; }
  .vp-time { font-size: var(--text-xs); padding: 0 var(--space-1); }
  .vp-captions { bottom: 54px; }
  .vp-caption-line { font-size: var(--text-sm); }
}
@media (prefers-reduced-motion: reduce) {
  .vp-bar, .vp-captions, .vp-vol, .vp-big-play, .vp input[type="range"]::-webkit-slider-thumb { transition: none; }
  .vp-spinner { animation-duration: 2.4s; }
}

/* ── PLANB-R4 · Settings tab strip fits a phone (2026-09-25, coordinator D-20; UI rule 14) ───────────────────
   MEASURED (store seats at SBR + Kenner, every tabbed Settings page): the four tabs need 329 px (225 px of labels +
   4 × 24 px padding + 3 × 2 px gap); at 320 the strip has 288 px and at 360 it has 328, so it scrolled sideways and
   Grooming read "Groo". Below 375 px the tabs take 6 px side padding and no gap (≈ 273 px, one row at 320). If the
   labels ever need more room (a fifth tab, a wider fallback font) the strip WRAPS onto a second row instead of
   scrolling; each tab keeps its own blue underline (UI rule 7), and the scroll fade is off because nothing scrolls.
   The 44 px touch height stays on `min-height`. */
@media (max-width: 414px) {
  /* The whole phone range of UI rule 14: never a sideways scroller — an overflowing strip wraps (PLANB-R5, D-21; measured with a
     fifth tab: 409 px in 358 at 390 used to scroll). A strip that fits renders exactly as before. */
  .settings-view .bf-tabs { flex-wrap: wrap; overflow: visible; animation: none; mask-image: none; }
}
@media (max-width: 374px) {
  .settings-view .bf-tabs { gap: 0; }
  .settings-view .bf-tabs .bf-tab { padding-left: 6px; padding-right: 6px; }
}

/* ── Pluto v2 · the email editor's chat bar, Formats library and tools (pilot `pluto_v2_pilot`, 2026-09-30) ──
   Every rule here is scoped to `.template-editor--v2`, which public/js/views/pluto-v2.js adds ONLY where the
   store's pilot is ON, so an OFF store renders exactly as before. Tokens only (portal-styling.md). The one light
   surface is the Formats preview frame: it shows EMAIL content, which rule 7 allows to be light. */
.template-editor--v2 .chat-input-wrap { align-items: flex-end; gap: var(--space-1); padding-left: var(--space-1); }
.template-editor--v2 .chat-input { min-width: 0; }

.pluto-composer-tools { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.pluto-v2-tool {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; flex-shrink: 0;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-secondary); cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}
.pluto-v2-tool svg { width: 18px; height: 18px; }
.pluto-v2-tool:hover { background: var(--bg-elevated); color: var(--text-primary); }
.pluto-v2-tool:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-v2-tool--active { color: var(--brand-blue); border-color: var(--border-active); background: var(--bg-elevated); }
.pluto-v2-tool--recording { color: var(--error); border-color: var(--error); animation: pluto-rec-pulse 1.4s ease-in-out infinite; }
.pluto-v2-tool--busy { opacity: 0.6; cursor: wait; }
.pluto-v2-tool--unavailable { color: var(--text-muted); }
.pluto-v2-tool--unavailable:hover { color: var(--text-secondary); }
@keyframes pluto-rec-pulse { 0%, 100% { background: transparent; } 50% { background: var(--bg-elevated); } }

/* Status line under the chat bar (dictation, picks). Links inside use .editor-modal-link. */
.pluto-composer-status { margin-top: var(--space-2); font-size: var(--text-xs); line-height: 1.5; color: var(--text-secondary); overflow-wrap: anywhere; }
.pluto-composer-status[hidden] { display: none; }
.pluto-composer-status--ok { color: var(--success); }
.pluto-composer-status--warn { color: var(--warning); }
.pluto-composer-status--error { color: var(--error); }

.pluto-chips { display: flex; flex-direction: column; }
.pluto-chips[hidden] { display: none; }

/* The in-page panel: expands UP from the chat bar and pushes the conversation up — part of the page (UI rule 11). */
.pluto-panel {
  display: flex; flex-direction: column; max-height: 0; opacity: 0; overflow: hidden;
  margin-bottom: var(--space-2);
  background: var(--bg-secondary); border: 1px solid var(--border-default); border-radius: var(--radius-md);
  transition: max-height var(--transition-base), opacity var(--transition-fast);
}
.pluto-panel[hidden] { display: none; }
.pluto-panel--open { max-height: min(60vh, 460px); opacity: 1; }
/* M7 (live walk F5): the open panel always fits the chat column it lives in. On a phone that column is short (the
   preview sits above it; measured 375 x 568: about 200 px), and the panel used to be bounded only by the screen, so it
   ran past the column's clipped bottom; the first card scrolled into view then scrolled the whole column and carried
   the panel's × under the preview. Now the chat bar area may shrink, the panel inside it shrinks with it, and only the
   panel's own card grid scrolls. Pilot editor only; with the panel closed nothing moves. */
.template-editor--v2 .chat-input-area { display: flex; flex-direction: column; flex-shrink: 1; min-height: 0; }
.template-editor--v2 .chat-input-area > * { flex: none; }
.template-editor--v2 .chat-input-area > .pluto-panel { flex: 0 1 auto; min-height: 0; }
/* M8 (live walk #2): on a phone the open panel (Formats, or the photo picker) was only ~80 px tall at 568 px — the email
   preview kept 35-45% of the screen above it. While a panel is open on a phone, the preview pane shows only its bar
   (Desktop/Phone, Undo, History... stay reachable) and the email itself steps aside until the panel closes. Measured:
   panel 80 → 163 px at 568 tall, 205 → 355 px at 760; desktop (stacked layout ends at 768 px) untouched. */
@media (max-width: 768px) {
  .template-editor--v2:has(.pluto-panel--open) .editor-preview { flex: 0 0 auto; }
  .template-editor--v2:has(.pluto-panel--open) .pluto-preview-stage { display: none; }
}
.pluto-panel-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border-default); flex-shrink: 0;
}
.pluto-panel-title { margin: 0; font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.pluto-panel-body {
  padding: var(--space-3); overflow-y: auto; overscroll-behavior: contain; min-height: 0;
  scrollbar-width: thin; scrollbar-color: var(--border-hover) transparent;
}

/* Tabs wrap onto a second row rather than scroll sideways (UI rule 14); the chosen tab reads in the accent (rule 7). */
.pluto-tabs { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-bottom: var(--space-3); }
.pluto-tab {
  padding: var(--space-1) var(--space-3); border: 1px solid var(--border-default); border-radius: var(--radius-xl);
  background: var(--bg-tertiary); color: var(--text-secondary); font-family: inherit; font-size: var(--text-xs);
  cursor: pointer; transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
}
.pluto-tab:hover { color: var(--text-primary); border-color: var(--border-hover); }
.pluto-tab:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-tab--active { color: var(--text-primary); background: var(--bg-elevated); border-color: var(--brand-blue); }

.pluto-format-group { margin: var(--space-2) 0 var(--space-1); font-size: var(--text-xs); font-weight: 600; color: var(--text-primary); }
.pluto-format-group:first-child { margin-top: 0; }
.pluto-format-group-note { margin: 0 0 var(--space-2); font-size: var(--text-xs); color: var(--text-muted); }
.pluto-format-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); gap: var(--space-2); margin-bottom: var(--space-2); }
.pluto-format-card {
  display: flex; flex-direction: column; align-items: stretch; gap: var(--space-1); min-width: 0;
  padding: var(--space-2); text-align: left; font-family: inherit; cursor: pointer;
  background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.pluto-format-card:hover { background: var(--bg-elevated); border-color: var(--border-active); }
.pluto-format-card:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-format-card--chosen { border-color: var(--brand-blue); }
.pluto-format-name { font-size: var(--text-xs); font-weight: 600; color: var(--text-primary); }
.pluto-format-hint { font-size: var(--text-xs); color: var(--text-secondary); line-height: 1.4; }
/* The preview frame draws the block at its real email width (600 px) and scales it to the card; pluto-v2.js
   measures the card and sets --pluto-preview-scale. 600 / 300 / 64 are the email's own sizes, not layout px. */
.pluto-format-preview {
  --pluto-preview-h: 300px;
  position: relative; overflow: hidden; border-radius: var(--radius-sm); background: var(--text-primary);
  height: calc(var(--pluto-preview-h) * var(--pluto-preview-scale, 0.25));
}
.pluto-format-preview--font { --pluto-preview-h: 64px; }
.pluto-format-preview iframe {
  position: absolute; top: 0; left: 0; width: 600px; height: var(--pluto-preview-h); border: 0;
  transform: scale(var(--pluto-preview-scale, 0.25)); transform-origin: top left; pointer-events: none;
}

/* Preview bar (extras 1, 3, 4, 6, 7): sits above the email preview so the top toolbar keeps its five buttons. */
.template-editor--v2 .editor-preview { display: flex; flex-direction: column; overflow: hidden; }
.pluto-preview-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); background: var(--bg-secondary); border-bottom: 1px solid var(--border-default); flex-shrink: 0;
}
.pluto-segmented { display: inline-flex; border: 1px solid var(--border-default); border-radius: var(--radius-sm); overflow: hidden; }
.pluto-bar-actions { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.pluto-bar-btn {
  display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2);
  border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary);
  font-family: inherit; font-size: var(--text-xs); cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}
.pluto-segmented .pluto-bar-btn { border-radius: 0; }
.pluto-bar-btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.pluto-bar-btn:hover:not(:disabled) { background: var(--bg-elevated); color: var(--text-primary); }
.pluto-bar-btn:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-bar-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.pluto-bar-btn--on { color: var(--text-primary); background: var(--bg-elevated); border-color: var(--brand-blue); }
/* The email itself keeps its light page (rule 7); only the chrome above it is dark. */
.pluto-preview-stage { flex: 1; min-height: 0; overflow: auto; }
.pluto-preview-stage iframe { width: 100%; height: 100%; border: 0; display: block; }
/* Phone view: a phone-sized column (390 px is a common phone's CSS width — the email's reader, not layout). */
.pluto-preview-stage--phone { background: var(--bg-tertiary); padding: var(--space-3) 0; }
.pluto-preview-stage--phone iframe { width: 390px; max-width: 100%; margin: 0 auto; border-radius: var(--radius-md); box-shadow: 0 0 0 1px var(--border-hover); }

/* Attach menu (extra 8) */
.pluto-attach-menu { display: flex; flex-direction: column; gap: var(--space-2); }
.pluto-attach-option {
  display: flex; align-items: flex-start; gap: var(--space-3); width: 100%; padding: var(--space-3); text-align: left;
  background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  color: var(--text-primary); font-family: inherit; font-size: var(--text-sm); cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.pluto-attach-option:hover { background: var(--bg-elevated); border-color: var(--border-active); }
.pluto-attach-option:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-attach-option svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--brand-blue); }
.pluto-attach-option strong { display: block; font-weight: 600; }
.pluto-attach-hint { display: block; margin-top: 2px; font-size: var(--text-xs); color: var(--text-secondary); }
.pluto-photo-card {
  display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-2); text-align: left;
  background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  font-family: inherit; cursor: pointer; transition: border-color var(--transition-fast), background var(--transition-fast);
}
.pluto-photo-card:hover { background: var(--bg-elevated); border-color: var(--border-active); }
.pluto-photo-card:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-photo-more { display: block; width: 100%; margin-top: var(--space-3); }   /* F7: next page of "Your photos" */
.pluto-photo-thumb { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: var(--radius-sm); background: var(--bg-elevated); display: block; }
.pluto-photo-thumb[hidden] { display: none; }

/* Modals opened by the v2 tools (they reuse .editor-modal-*; these are their list parts). */
.pluto-change-list { margin: var(--space-1) 0 var(--space-3); padding-left: var(--space-5); color: var(--text-primary); font-size: var(--text-sm); line-height: 1.5; }
.pluto-change-list li + li { margin-top: 2px; }
.pluto-apply-subject { margin-bottom: var(--space-3); font-size: var(--text-sm); color: var(--text-secondary); overflow-wrap: anywhere; }
.pluto-check-list { display: flex; flex-direction: column; gap: var(--space-1); margin-bottom: var(--space-2); }
.pluto-check { padding-left: var(--space-4); position: relative; font-size: var(--text-sm); line-height: 1.5; color: var(--text-secondary); overflow-wrap: anywhere; }
.pluto-check::before { content: ''; position: absolute; left: 0; top: 0.5em; width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); }
.pluto-check--ok::before { background: var(--success); }
.pluto-check--problem { color: var(--text-primary); }
.pluto-check--problem::before { background: var(--error); }
.pluto-check--unchecked::before { background: var(--warning); }
.pluto-check-population { font-size: var(--text-xs); color: var(--text-muted); }
.pluto-history-list { display: flex; flex-direction: column; gap: var(--space-2); }
.pluto-history-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* M8 (live walk #2): on a phone the text takes the full width and "Restore this version" sits under it, so the words never
   wrap one to a line beside the button at 320 px. */
@media (max-width: 414px) {
  .pluto-history-list .editor-modal-list-row { flex-direction: column; align-items: stretch; }
  .pluto-history-list .editor-modal-list-row > button { align-self: flex-start; }
}
.pluto-choice-list { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
.pluto-choice {
  width: 100%; padding: var(--space-2) var(--space-3); text-align: left; overflow-wrap: anywhere;
  background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  color: var(--text-primary); font-family: inherit; font-size: var(--text-sm); cursor: pointer;
}
.pluto-choice:hover { border-color: var(--border-hover); }
.pluto-choice:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-choice--on { border-color: var(--brand-blue); background: var(--bg-elevated); }
.pluto-batch-text { width: 100%; resize: vertical; }
.pluto-batch-count { margin-top: var(--space-3); }
.pluto-pick-list {
  display: flex; flex-direction: column; gap: 2px; max-height: 40vh; overflow-y: auto; margin-top: var(--space-1);
  scrollbar-width: thin; scrollbar-color: var(--border-hover) transparent;
}
.pluto-pick-row { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2); border-radius: var(--radius-sm); cursor: pointer; font-size: var(--text-sm); }
.pluto-pick-row:hover { background: var(--bg-tertiary); }
.pluto-pick-row input { accent-color: var(--brand-blue); width: 16px; height: 16px; flex-shrink: 0; }
.pluto-pick-name { flex: 1; min-width: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.pluto-pick-meta { color: var(--text-muted); font-size: var(--text-xs); flex-shrink: 0; }
.pluto-batch-list { display: flex; flex-direction: column; gap: var(--space-2); }
.pluto-batch-row { padding: var(--space-3); background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm); }
.pluto-batch-row--ready { border-color: var(--border-active); }
.pluto-batch-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); }
.pluto-batch-state { font-size: var(--text-xs); color: var(--text-secondary); }
.pluto-batch-row--ready .pluto-batch-state { color: var(--success); }
.pluto-batch-row--refused .pluto-batch-state, .pluto-batch-row--failed .pluto-batch-state, .pluto-batch-row--stopped .pluto-batch-state { color: var(--warning); }
.pluto-batch-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.pluto-result-frame { width: 100%; height: 60vh; border: 0; border-radius: var(--radius-sm); background: var(--text-primary); display: block; }

/* Banner maker (M6): a banner from the store's own photo or a brand colour, no AI drawing (pluto-v2-banner.js).
   On a short phone screen the dialog is bounded and ONLY its body scrolls, so the title with its close button and
   the footer (Cancel / Preview / Use) stay in view at every scroll position (UI rule 8: always a visible way out).
   Nothing in it is wider than the dialog at 320 px. */
.pluto-banner-dialog {
  display: flex; flex-direction: column; overflow: hidden;
  max-height: calc(100vh - var(--space-8)); max-height: calc(100dvh - var(--space-8));
}
.pluto-banner-dialog > .editor-modal-header,
.pluto-banner-dialog > .editor-modal-status,
.pluto-banner-dialog > .editor-modal-footer { flex: none; }
.pluto-banner-dialog > .editor-modal-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin; scrollbar-color: var(--border-hover) transparent;
}
.pluto-banner-dialog .editor-modal-footer { flex-wrap: wrap; }
.pluto-banner-section { margin-top: var(--space-4); min-width: 0; }
.pluto-banner-section:first-child { margin-top: 0; }
.pluto-banner-photos {
  max-height: 260px; overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin; scrollbar-color: var(--border-hover) transparent;
}
.pluto-banner-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(120px, 100%), 1fr)); gap: var(--space-2); }
.pluto-banner-swatch {
  display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-2); text-align: left;
  background: var(--bg-tertiary); border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  font-family: inherit; cursor: pointer; transition: border-color var(--transition-fast), background var(--transition-fast);
}
.pluto-banner-swatch:hover { background: var(--bg-elevated); border-color: var(--border-active); }
.pluto-banner-swatch:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 1px; }
.pluto-banner-swatch-colour { display: block; height: 40px; border-radius: var(--radius-sm); }
.pluto-banner-choice[aria-pressed="true"] { border-color: var(--brand-blue); background: var(--bg-elevated); }
.pluto-banner-headline { resize: vertical; min-height: 72px; }
.pluto-banner-preview { display: block; width: 100%; max-width: 100%; height: auto; margin-top: var(--space-3); border-radius: var(--radius-sm); border: 1px solid var(--border-default); }
.pluto-banner-preview[hidden] { display: none; }
@media (max-width: 414px) {
  .pluto-banner-dialog { padding: var(--space-3); --modal-pad: var(--space-3); }
  .pluto-banner-photos { max-height: 200px; }
}

/* Narrow screens: the editor toolbar now wraps for EVERY store — see "Narrow-screen layout floors" at the end. */
@media (max-width: 414px) {
  .template-editor--v2 .chat-input-area { padding-left: var(--space-2); padding-right: var(--space-2); }
  .pluto-bar-label { display: none; }   /* icon-only on phones: each button still names itself (title + aria-label) */
  .pluto-preview-bar { padding-left: var(--space-2); padding-right: var(--space-2); }
  .pluto-v2-tool { width: 30px; height: 30px; }
  .pluto-panel--open { max-height: 55vh; }
}

/* ── Narrow-screen layout floors, every store (UI rule 14; Pluto v2 M4 addendum, 2026-10-01) ──────────────
   Measured live in a real browser at 320-414 px: (a) at 320 px the account menu (.nav-right and everything in
   it) ended past the viewport at EVERY store — the 200 px logo and the account controls could not both fit and
   neither was allowed to shrink, so the page clipped the account button; (b) the editor toolbar ran past the edge
   at 375/414 px on a store whose toolbar still has six buttons. Layout only: no element, text or request changes.
   ⚠️ POSITION IS LOAD-BEARING: this block is the LAST thing in the file, after every base and narrower rule it
   overrides (same specificity — document order decides; see .claude/rules/portal-styling.md). */
@media (max-width: 768px) {
  /* (a) Where the nav links are a drop-down, the LOGO gives way and scales down to the space that is left; the
     account controls never shrink. The image keeps its shape (28 px tall at most, as on desktop). */
  .nav-left { flex-shrink: 1; min-width: 0; }
  .nav-logo { flex-shrink: 1; min-width: 0; }
  .nav-logo-img { height: auto; max-height: 28px; max-width: 100%; min-width: 0; object-fit: contain; }
  .nav-right { flex-shrink: 0; }
  /* (b) The toolbar wraps: its title on one line, the buttons below, wrapping among themselves. */
  .editor-toolbar { flex-wrap: wrap; }
  .editor-toolbar-right { flex-wrap: wrap; flex-shrink: 1; min-width: 0; }
}

/* ── Layout floors, PILOT EDITORS ONLY for now (Pluto v2 M7/M7c, from the live walk 2026-10-02) ───────────────────────
   Built as fleet-wide floors (R16 lets layout/safety floors ship to every store); held behind pluto_v2_pilot until the
   owner rules them fleet-wide (M7c, 2026-10-02). They apply only on a page whose template editor is the v2 (pilot)
   editor — `.template-editor--v2`, which the pilot editor's own mount sets — so an OFF store gets exactly the CSS it
   had before. Dialogs are attached to <body>, outside the editor, so they are scoped with body:has(...). To ship them
   fleet-wide, drop the `body:has(.template-editor--v2) ` / `.template-editor--v2 ` prefixes and nothing else.
   Layout only: no element, text, request or behaviour changes, and nothing moves on a dialog or panel that already
   fits. ⚠️ POSITION IS LOAD-BEARING: after every base rule these override. */

/* (F2) A dialog never grows past the screen. Measured live at 320/375 x 760: the template History dialog was taller
   than the screen, the backdrop centred it, and both its close button (y -200) and its Close button (y 928) were off
   screen with no way to reach them. Now the dialog is bounded and scrolls INSIDE itself, and its header (title + ×)
   and its footer (the buttons) stick to its top and bottom edges, so a way out is always on screen. The dialog keeps
   block layout (no margin moves) and a dialog that fits gets no scrolling, so its geometry is unchanged. The header's
   and footer's own padding takes over the dialog's padding strip behind them (20 px, the dialog's padding, or a
   dialog's own --modal-pad), so they sit flush with the edges. */
body:has(.template-editor--v2) .editor-modal-dialog {
  max-height: calc(100vh - 2 * var(--space-4));
  max-height: calc(100dvh - 2 * var(--space-4));
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--border-hover) transparent;
}
body:has(.template-editor--v2) .editor-modal-dialog > .editor-modal-header {
  position: sticky; top: calc(-1 * var(--modal-pad, 20px)); z-index: 1;
  margin-top: calc(-1 * var(--modal-pad, 20px)); padding-top: var(--modal-pad, 20px);
  background: var(--bg-elevated);
}
body:has(.template-editor--v2) .editor-modal-dialog > .editor-modal-footer {
  position: sticky; bottom: calc(-1 * var(--modal-pad, 20px)); z-index: 1;
  margin-bottom: calc(-1 * var(--modal-pad, 20px)); padding-bottom: var(--modal-pad, 20px);
  background: var(--bg-elevated);
}

/* (Settings) The template Settings panel's two columns ran past the screen at 320-414 px (measured live at every
   store: the right column at x 269-502, clipped by .template-editor). A grid column may shrink below its fields'
   natural width on a narrow screen, and on a phone the fields stack in one column. */
@media (max-width: 768px) {
  .template-editor--v2 .editor-settings-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  .template-editor--v2 .editor-settings-grid { grid-template-columns: minmax(0, 1fr); }
}

/* M9 (CEO D-1306): on a phone the toolbar wrapped to three rows (~143 px). pluto-v2.js mounts a "More" menu at ≤414 px
   only (pilot editors only): Settings, Rename, Export PDF and Revert live in it there, Back and the title keep their own
   row, and More + Apply & Push share the second. The menu expands DOWN inside the page with the shared dropdown look
   (UI rule 11). Above 414 px nothing is mounted, so these rules match nothing and the desktop toolbar is unchanged.
   ⚠️ After the ≤768 toolbar wrap block above — same specificity family, document order decides. */
@media (max-width: 414px) {
  .template-editor--v2 .editor-toolbar-left { flex-basis: 100%; }
  .template-editor--v2 .editor-toolbar-right > [data-pluto-more] { display: none; }
}
.template-editor--v2 .pluto-more-toggle { display: inline-flex; align-items: center; gap: var(--space-1); }
.template-editor--v2 .pluto-more-chevron { transition: transform var(--transition-fast); }
.template-editor--v2 .pluto-more-toggle[aria-expanded="true"] .pluto-more-chevron { transform: rotate(180deg); }
.template-editor--v2 .pluto-more { flex: 0 0 100%; min-width: 0; }
.template-editor--v2 .pluto-more[hidden] { display: none; }
.template-editor--v2 .pluto-more [aria-checked="true"] > .ls-dropdown-check { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .template-editor--v2 .pluto-more-chevron { transition: none; }
}
