/* ===== Oasis Component Styles ===== */

/* --- Navigation Tabs --- */
.nav-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ctp-overlay1);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out-expo);
  white-space: nowrap;
  border: 1px solid transparent;
  background: transparent;
}
.nav-tab:hover {
  color: var(--ctp-text);
  background: var(--surface-hover);
}
.nav-tab-active {
  color: var(--ctp-blue) !important;
  background: color-mix(in srgb, var(--ctp-blue) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-blue) 20%, transparent);
}
.nav-tab-active.nav-tab-peach {
  color: var(--ctp-peach) !important;
  background: color-mix(in srgb, var(--ctp-peach) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-peach) 20%, transparent);
}
.nav-tab-active.nav-tab-green {
  color: var(--ctp-green) !important;
  background: color-mix(in srgb, var(--ctp-green) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-green) 20%, transparent);
}
.nav-tab-active.nav-tab-lavender {
  color: var(--ctp-lavender) !important;
  background: color-mix(in srgb, var(--ctp-lavender) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-lavender) 20%, transparent);
}
.nav-tab-active.nav-tab-pink {
  color: var(--ctp-pink) !important;
  background: color-mix(in srgb, var(--ctp-pink) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-pink) 20%, transparent);
}
.nav-tab-active.nav-tab-red {
  color: var(--ctp-red) !important;
  background: color-mix(in srgb, var(--ctp-red) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-red) 20%, transparent);
}
.nav-tab-active.nav-tab-flamingo {
  color: var(--ctp-flamingo) !important;
  background: color-mix(in srgb, var(--ctp-flamingo) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-flamingo) 20%, transparent);
}
.nav-tab-active.nav-tab-sky {
  color: var(--ctp-sky) !important;
  background: color-mix(in srgb, var(--ctp-sky) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-sky) 20%, transparent);
}
.nav-tab-active.nav-tab-teal {
  color: var(--ctp-teal) !important;
  background: color-mix(in srgb, var(--ctp-teal) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-teal) 20%, transparent);
}
.nav-tab-active.nav-tab-yellow {
  color: var(--ctp-yellow) !important;
  background: color-mix(in srgb, var(--ctp-yellow) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-yellow) 20%, transparent);
}
.nav-tab-active.nav-tab-mauve {
  color: var(--ctp-mauve) !important;
  background: color-mix(in srgb, var(--ctp-mauve) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--ctp-mauve) 20%, transparent);
}

/* --- Global sidebar: uniform icons + container-responsive layout ---
   The aside is a size container, so these rules track the USER-DRAGGED width
   (not the viewport): below 170px the labels drop and it becomes an icon
   rail; past 300px text and icons scale up. No JS involved. */
.oasis-sidebar {
  container-type: inline-size;
}
.oasis-sidebar .nav-ico {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}
.oasis-sidebar .nav-tab {
  font-size: var(--text-sm);
  gap: 10px;
  padding: 8px 12px;
}
/* Brand: below 170px the full lockup cross-fades into the bare tile
   (mirrors the collapsed rail). Both stay centered; opacity + scale +
   width animate so the swap reads as one smooth morph while dragging. */
.oasis-sidebar .brand-lockup {
  display: flex;
  justify-content: center;
  width: 100%;
  min-width: 0;
  opacity: 1;
  transform: scale(1);
  overflow: hidden;
  transition: opacity 0.22s ease, transform 0.22s ease, width 0.22s ease;
}
.oasis-sidebar .brand-tile-narrow {
  display: block;
  width: 0;
  opacity: 0;
  transform: scale(0.5);
  overflow: hidden;
  transition: opacity 0.22s ease, transform 0.22s ease, width 0.22s ease;
}
@container (max-width: 169px) {
  .oasis-sidebar .nav-label {
    display: none !important;
  }
  .oasis-sidebar .brand-lockup {
    width: 0;
    opacity: 0;
    transform: scale(0.6);
  }
  .oasis-sidebar .brand-tile-narrow {
    width: 2rem;
    opacity: 1;
    transform: scale(1);
  }
  /* aside.oasis-sidebar (not .oasis-sidebar) so this outranks the later
     ".oasis-sidebar .nav-tab { justify-content: flex-start }" block — same
     specificity would lose on source order and leave icons left-aligned. */
  aside.oasis-sidebar .nav-tab {
    justify-content: center;
    padding-left: 4px;
    padding-right: 4px;
  }
  /* Narrow drag: stack the collapse chevron under the tile (like the
     collapsed rail) so it never crowds the brand. !important beats the
     inline absolute positioning used at full width. */
  .oasis-sidebar .brand-header {
    flex-direction: column;
    gap: 8px;
  }
  .oasis-sidebar .brand-header > button {
    position: static !important;
    transform: none !important;
  }
}
@container (min-width: 300px) {
  .oasis-sidebar .nav-ico {
    width: 24px;
    height: 24px;
  }
  .oasis-sidebar .nav-tab {
    font-size: var(--text-base);
    padding: 10px 14px;
  }
}

/* --- Buttons --- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 500;
  font-family: var(--font-display);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out-expo);
  border: 1px solid transparent;
  outline: none;
}
.btn:active { transform: scale(0.97); }
.btn:focus-visible {
  box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 4px var(--ctp-blue);
}

.btn-primary {
  background: var(--ctp-blue);
  color: var(--ctp-base);
}
.btn-primary:hover {
  filter: brightness(1.1);
  box-shadow: var(--glow-blue);
}

.btn-secondary {
  background: var(--ctp-surface0);
  color: var(--ctp-text);
  border-color: var(--ctp-surface1);
}
.btn-secondary:hover {
  background: var(--ctp-surface1);
  border-color: var(--ctp-surface2);
}

.btn-ghost {
  background: transparent;
  color: var(--ctp-subtext0);
}
.btn-ghost:hover {
  background: var(--surface-hover);
  color: var(--ctp-text);
}

.btn-danger {
  background: var(--ctp-red);
  color: var(--ctp-base);
}
.btn-danger:hover {
  filter: brightness(1.1);
}

.btn-sm {
  padding: 4px 10px;
  font-size: var(--text-xs);
}
.btn-xs {
  padding: 2px 8px;
  font-size: var(--text-xs);
  gap: 4px;
}
.btn-lg {
  padding: 10px 20px;
  font-size: var(--text-base);
}

/* --- Cards --- */
.card {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-lg);
  padding: 20px;
  transition: all var(--duration-normal) var(--ease-out-expo);
}
.card-hover:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--ctp-surface2);
}

/* Stat card with gradient accent */
.stat-card {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-lg);
  padding: 16px 20px;
  position: relative;
  overflow: hidden;
  transition: all var(--duration-normal) var(--ease-out-expo);
}
.stat-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-mauve));
  opacity: 0;
  transition: opacity var(--duration-normal);
}
.stat-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--ctp-surface2);
}
.stat-card:hover::before { opacity: 1; }
.stat-card .stat-value {
  font-size: var(--text-2xl);
  font-weight: 700;
  font-family: var(--font-mono);
  color: var(--ctp-text);
  line-height: 1.2;
}
.stat-card .stat-label {
  font-size: var(--text-xs);
  font-family: var(--font-display);
  color: var(--ctp-overlay1);
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* --- Tables --- */
.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-sm);
}
.data-table thead th {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  padding: 10px 14px;
  text-align: left;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--ctp-overlay1);
  border-bottom: 1px solid var(--ctp-surface1);
  cursor: pointer;
  user-select: none;
  transition: color var(--duration-fast);
}
.data-table thead th:hover { color: var(--ctp-text); }
.data-table thead th.sorted { color: var(--ctp-blue); }

.data-table tbody tr {
  transition: background var(--duration-fast);
  border-left: 2px solid transparent;
}
.data-table tbody tr:hover {
  background: var(--surface-hover);
  border-left-color: var(--ctp-blue);
}
.data-table tbody td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--ctp-surface0);
  font-family: var(--font-mono);
}

/* Alternating rows */
.data-table tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--ctp-surface0) 30%, transparent);
}
.data-table tbody tr:nth-child(even):hover {
  background: var(--surface-hover);
}

/* --- Skeleton Loaders --- */
.skeleton {
  background: var(--ctp-surface1);
  border-radius: var(--radius-sm);
  position: relative;
  overflow: hidden;
}
.skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, var(--ctp-surface2) 50%, transparent 100%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}
.skeleton-text { height: 14px; width: 80%; }
.skeleton-text-sm { height: 11px; width: 60%; }
.skeleton-heading { height: 22px; width: 40%; }
.skeleton-card { height: 80px; }
.skeleton-row { height: 44px; width: 100%; margin-bottom: 2px; }
/* Rows are the app's densest placeholder and 44px is a desktop row. On a phone
   the real tables sit tighter, and a placeholder that over-states the height
   makes the page jump downward when data lands — the exact thing a skeleton is
   there to prevent. Mobile-first house rule applies to the waiting state too. */
@media (max-width: 640px) {
  .skeleton-row { height: 36px; }
  .skeleton-card { height: 68px; }
}
/* A shimmer that never stops is a distraction for anyone who has asked the OS
   to calm animation down. Keep the shape, drop the movement. */
@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; opacity: 0; }
  .skeleton { background: var(--ctp-surface2); }
}

/* --- Badges --- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 9999px;
  font-size: var(--text-xs);
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.4;
}
.badge-blue { background: color-mix(in srgb, var(--ctp-blue) 15%, transparent); color: var(--ctp-blue); }
.badge-green { background: color-mix(in srgb, var(--ctp-green) 15%, transparent); color: var(--ctp-green); }
.badge-yellow { background: color-mix(in srgb, var(--ctp-yellow) 15%, transparent); color: var(--ctp-yellow); }
.badge-red { background: color-mix(in srgb, var(--ctp-red) 15%, transparent); color: var(--ctp-red); }
.badge-mauve { background: color-mix(in srgb, var(--ctp-mauve) 15%, transparent); color: var(--ctp-mauve); }
.badge-surface { background: var(--ctp-surface1); color: var(--ctp-subtext0); }

/* --- Glass Panel --- */
.glass {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
}

/* --- Deep-link focus row (Topics Explorer) --- */
/* A row arrived-at via a "?geo=&topic=" deep link (e.g. from the Site Content
   Plans matched-topic popover). Flashes bright on land, then settles to a
   subtle tint so the row stays identifiable. !important overrides the zebra
   stripe; background (not box-shadow/ring) is reliable on a <tr>. */
@keyframes ostFocusFlash {
  0%   { background-color: rgba(249, 226, 175, 0.45); }
  100% { background-color: rgba(249, 226, 175, 0.12); }
}
.ost-focus-row {
  background-color: rgba(249, 226, 175, 0.12) !important;
  animation: ostFocusFlash 1.8s ease-out 1;
}
/* The ONE keyword a notification / edit-log row is about, inside the topic
   modal's keyword list (?mark=). Same flash language as the focused row so
   "here is the thing that changed" reads the same everywhere. */
.v2-kw-marked {
  background-color: rgba(249, 226, 175, 0.14) !important;
  box-shadow: inset 2px 0 0 var(--ctp-yellow);
  animation: ostFocusFlash 1.8s ease-out 1;
}

/* --- Modal Overlay --- */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.modal-panel {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-xl);
  max-width: 640px;
  width: calc(100% - 32px);
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  padding: 24px;
}
.modal-panel-lg { max-width: 900px; }
.modal-panel-xl { max-width: 1200px; }

/* --- Toast Container --- */
/* ── "Oasis is updating" banner ─────────────────────────────────────────────
   Top-centre, above every panel, so the answer to "is it broken?" is the first
   thing on screen during a deploy. Sits clear of the mobile tab bar by being at
   the top, and never blocks a click: it is one line with one button. */
.upd-bar {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 400; display: flex; align-items: center; gap: 9px;
  /* left:50% makes the available width for shrink-to-fit half the viewport, so
     a phone squeezed this to 195px and stacked the sentence four lines high.
     max-content + max-width sizes it to the text, then clamps. */
  width: max-content; max-width: min(560px, calc(100vw - 20px));
  padding: 9px 14px; border-radius: var(--radius-md);
  background: var(--ctp-surface0); border: 1px solid var(--ctp-peach);
  box-shadow: var(--shadow-lg); font-size: 12.5px; font-weight: 600;
  color: var(--ctp-text);
}
.upd-bar-back { border-color: var(--ctp-green); color: var(--ctp-green); }
.upd-msg { min-width: 0; }
.upd-btn { flex-shrink: 0; padding: 3px 10px; border-radius: 6px; cursor: pointer;
  font-size: 11px; font-weight: 800; color: var(--ctp-base); background: var(--ctp-green); }
.upd-spin { flex-shrink: 0; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ctp-peach) 30%, transparent);
  border-top-color: var(--ctp-peach); animation: upd-spin .8s linear infinite; }
@keyframes upd-spin { to { transform: rotate(360deg); } }
.upd-enter-active, .upd-leave-active { transition: opacity .18s, transform .18s; }
.upd-enter-from, .upd-leave-to { opacity: 0; transform: translate(-50%, -8px); }
@media (max-width: 768px) {
  .upd-bar { top: 8px; font-size: 12px; padding: 8px 12px; }
}

.toast-container {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 200;
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--text-sm);
  font-family: var(--font-display);
  min-width: 300px;
  max-width: 420px;
  position: relative;
  overflow: hidden;
}
.toast::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  background: var(--ctp-blue);
  animation: toastProgress linear forwards;
}
@keyframes toastProgress {
  from { width: 100%; }
  to { width: 0%; }
}
.toast-success { border-left: 3px solid var(--ctp-green); }
.toast-error { border-left: 3px solid var(--ctp-red); }
.toast-info { border-left: 3px solid var(--ctp-blue); }
.toast-warning { border-left: 3px solid var(--ctp-yellow); }

/* --- Input/Select polish --- */
.input {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-md);
  padding: 7px 12px;
  font-size: var(--text-sm);
  color: var(--ctp-text);
  transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
  outline: none;
  width: 100%;
}
.input:focus {
  border-color: var(--ctp-blue);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ctp-blue) 15%, transparent);
}
.input::placeholder { color: var(--ctp-overlay0); }

/* --- Progress Bar --- */
.progress-bar {
  height: 4px;
  background: var(--ctp-surface1);
  border-radius: 2px;
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-teal));
  border-radius: 2px;
  transition: width var(--duration-normal) var(--ease-out-expo);
}

/* --- Sort Arrow --- */
.sort-arrow {
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 4px;
  vertical-align: middle;
  transition: transform var(--duration-fast);
}
.sort-arrow-asc {
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-bottom: 5px solid currentColor;
}
.sort-arrow-desc {
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
}

/* --- Focus Ring (accessibility) --- */
.focus-ring:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 4px var(--ctp-blue);
}

/* --- Scrollbar (thin variant for panels) --- */
.scrollbar-thin::-webkit-scrollbar { width: 6px; height: 6px; }
.scrollbar-thin::-webkit-scrollbar-thumb { border-radius: 3px; }

/* --- Empty State --- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48px 24px;
  text-align: center;
  color: var(--ctp-overlay1);
}
.empty-state svg {
  width: 48px;
  height: 48px;
  margin-bottom: 16px;
  opacity: 0.4;
}
.empty-state p {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  max-width: 320px;
}

/* --- Action Pills (compact action bar buttons) --- */
.action-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 500;
  font-family: var(--font-display);
  letter-spacing: 0.01em;
  color: var(--ctp-overlay1);
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface2);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out-expo);
  white-space: nowrap;
}
.action-pill:hover:not(:disabled) {
  color: var(--ctp-text);
  background: var(--ctp-surface1);
  border-color: var(--ctp-overlay0);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
.action-pill:active:not(:disabled) {
  transform: scale(0.97);
}
.action-pill:disabled, .action-pill-disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}
.action-pill-active {
  font-weight: 600;
}
.action-pill-active.action-pill-yellow {
  color: var(--ctp-yellow);
  background: color-mix(in srgb, var(--ctp-yellow) 12%, var(--ctp-surface0));
  border-color: color-mix(in srgb, var(--ctp-yellow) 35%, transparent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ctp-yellow) 15%, transparent);
}
.action-pill-active.action-pill-sky {
  color: var(--ctp-sky);
  background: color-mix(in srgb, var(--ctp-sky) 12%, var(--ctp-surface0));
  border-color: color-mix(in srgb, var(--ctp-sky) 35%, transparent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ctp-sky) 15%, transparent);
  animation: pulse 2s ease-in-out infinite;
}
.action-pill-active.action-pill-peach {
  color: var(--ctp-peach);
  background: color-mix(in srgb, var(--ctp-peach) 12%, var(--ctp-surface0));
  border-color: color-mix(in srgb, var(--ctp-peach) 35%, transparent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ctp-peach) 15%, transparent);
  animation: pulse 2s ease-in-out infinite;
}
.action-pill-active.action-pill-mauve {
  color: var(--ctp-mauve);
  background: color-mix(in srgb, var(--ctp-mauve) 12%, var(--ctp-surface0));
  border-color: color-mix(in srgb, var(--ctp-mauve) 35%, transparent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ctp-mauve) 15%, transparent);
  animation: pulse 2s ease-in-out infinite;
}
.action-pill-active.action-pill-teal {
  color: var(--ctp-teal);
  background: color-mix(in srgb, var(--ctp-teal) 12%, var(--ctp-surface0));
  border-color: color-mix(in srgb, var(--ctp-teal) 35%, transparent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--ctp-teal) 15%, transparent);
  animation: pulse 2s ease-in-out infinite;
}

/* ===== Mobile Responsive ===== */

/* --- Mobile Menu Toggle Button --- */
.mobile-menu-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--ctp-subtext0);
  border: none;
  cursor: pointer;
  transition: all var(--duration-fast);
  flex-shrink: 0;
}
.mobile-menu-btn:hover {
  background: var(--surface-hover);
  color: var(--ctp-text);
}

/* --- Mobile Sidebar Overlay --- */
.mobile-sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 60;
}
.mobile-sidebar-overlay.active { display: block; }

/* --- Mobile Sidebar Toggle Button --- */
.mobile-sidebar-toggle {
  display: none;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  flex-shrink: 0;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ctp-teal);
  background: color-mix(in srgb, var(--ctp-teal) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-teal) 20%, transparent);
  cursor: pointer;
  transition: all var(--duration-fast);
}
.mobile-sidebar-toggle:hover {
  background: color-mix(in srgb, var(--ctp-teal) 15%, transparent);
}

/* Phone-only chrome: scrim + bottom tab bar exist only under 768px */
.nav-scrim { display: none; }
.mobile-tabbar { display: none; }

/* "New build available" banner — must ALWAYS be fully visible, never clipped
   by the left sidebar (z-50) or the mobile tab bar (z-70). Hand-written (not
   Tailwind utilities) because Tailwind here is precompiled and arbitrary
   position/calc classes wouldn't exist at runtime. */
.update-banner {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 300;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100vw - 32px);
}
@media (max-width: 768px) {
  /* Phone: full-width bar lifted clear of the bottom tab bar. */
  .update-banner {
    left: 16px;
    right: 16px;
    bottom: calc(76px + env(safe-area-inset-bottom));
    justify-content: space-between;
  }
}

@media (max-width: 768px) {
  /* --- Header --- */
  .mobile-menu-btn { display: flex; }

  .header-stats,
  .header-actions-desktop {
    display: none !important;
  }

  .header-actions-mobile {
    display: flex !important;
  }

  /* Mobile dropdown menu */
  .mobile-dropdown-menu {
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 60;
    background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xl);
    padding: 8px;
    min-width: 200px;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .mobile-dropdown-menu .btn {
    width: 100%;
    justify-content: flex-start;
  }

  /* --- Navigation Tabs --- */
  .nav-tab {
    padding: 8px 14px;
    font-size: var(--text-sm);
    min-height: 44px;
  }
  .nav-tab svg { width: 16px; height: 16px; }

  /* --- Main Content --- */
  .main-content {
    padding-left: 12px !important;
    padding-right: 12px !important;
    padding-top: 16px !important;
    padding-bottom: 16px !important;
  }

  /* --- Topics List Sidebar --- */
  .mobile-sidebar-toggle { display: flex; }

  .tl-layout {
    flex-direction: column !important;
    gap: 0 !important;
  }
  .tl-sidebar {
    width: 100% !important;
    flex-shrink: 1 !important;
    position: fixed;
    top: 0;
    left: -100%;
    bottom: 0;
    width: 280px !important;
    z-index: 70;
    background: var(--ctp-base);
    padding: 16px;
    overflow-y: auto;
    transition: left 0.25s var(--ease-out-expo);
  }
  .tl-sidebar.mobile-open {
    left: 0;
  }
  .tl-sidebar .sticky { position: static !important; }

  /* --- Cards --- */
  .card { padding: 14px; }
  .stat-card { padding: 12px 14px; }

  /* --- Tables --- */
  .data-table { font-size: var(--text-xs); }
  .data-table thead th { padding: 8px 10px; }
  .data-table tbody td { padding: 8px 10px; }

  /* --- Modal --- */
  .modal-panel {
    width: calc(100% - 16px);
    max-height: calc(100vh - 32px);
    padding: 16px;
    border-radius: var(--radius-lg);
  }
  .modal-panel-lg { max-width: 100%; }
  .modal-panel-xl { max-width: 100%; }

  /* --- Buttons: touch-friendly --- */
  .btn { min-height: 44px; padding: 10px 16px; }
  .btn-sm { min-height: 36px; padding: 8px 12px; }
  .btn-xs { min-height: 32px; padding: 6px 10px; }

  /* --- Inputs: touch-friendly --- */
  .input { min-height: 44px; padding: 10px 12px; font-size: 16px; }
  select.input { font-size: 16px; }

  /* --- Nav tab pills: touch-friendly --- */
  .action-pill { min-height: 36px; padding: 8px 14px; }

  /* --- Toast on mobile --- */
  .toast-container {
    left: 12px;
    right: 12px;
    bottom: 12px;
  }
  .toast {
    min-width: auto;
    max-width: 100%;
  }

  /* --- Prevent horizontal overflow --- */
  .max-w-7xl { max-width: 100% !important; }

  /* --- Fix fixed-width elements --- */
  img, video, canvas, svg.chart { max-width: 100%; height: auto; }

  /* --- Geo dropdown mobile --- */
  [class*="w-64"],
  [class*="w-72"],
  [class*="w-80"],
  [class*="w-96"] {
    max-width: calc(100vw - 32px);
  }


  /* Body overflow is handled once, further down this same 768px block:
     `html { overflow-x: clip }` + `body { overflow: visible !important }`.
     A rule here setting overflow-x on BODY used to fight it — overflow-x on
     body computes overflow-y to auto, making body a scroll container and
     silently killing every position:sticky descendant. It only ever appeared
     harmless because the later rule overrode it. Removed 2026-07-31; do not
     reintroduce, put body-overflow rules in one place. */

  /* --- Flex containers stack on mobile --- */
  .flex-row-mobile-col { flex-direction: column !important; }
  .gap-5, .gap-6, .gap-8 { gap: 12px !important; }

  /* --- Font size adjustments for readability --- */
  .text-2xl { font-size: 1.25rem !important; }
  .text-3xl { font-size: 1.5rem !important; }
  .text-4xl { font-size: 1.75rem !important; }

  /* --- Ensure all overflow-x containers are scrollable --- */
  .overflow-x-auto { -webkit-overflow-scrolling: touch; }

  /* --- Fixed-width elements should not exceed viewport --- */
  pre, code, .font-mono { max-width: 100%; overflow-x: auto; word-break: break-word; }
  /* .font-mono is also tabular numbers inside tables — the code-block guard
     above must never apply there: overflow-x:auto turns each cell into its own
     scroll container (min-content ≈ 0) and the whole table letter-crushes
     (the "E S T . C L I C K S" bug). Tables already scroll via their wrapper
     or the .main-content table fallback below. */
  table .font-mono, th.font-mono, td.font-mono {
    overflow-x: visible; word-break: normal; max-width: none;
  }

  /* --- Modal improvements --- */
  .modal-panel h2, .modal-panel h3 { font-size: 1rem; }
  .modal-panel .text-sm { font-size: 0.8125rem; }

  /* --- Phone nav = fixed bottom bar (5 shortcuts) + the REAL sidebar as a
     slide-in drawer behind the ☰ cell.
     (Mark's design 2026-07-08: main sections in a bar at the bottom of the
     phone screen. Round 5, 2026-07-26: the drawer is the actual
     `aside.oasis-sidebar`, not a hand-copied menu — the old .mobile-more-sheet
     duplicate silently lost Feedback / Intake / Brand when they were added to
     the sidebar. One menu, one source of truth, nothing can go missing.)
     Drawer open == !navCollapsed on phones (app.js forces collapsed at boot
     and on every navigation), so the existing .nav-scrim + auto-close watcher
     drive it with no extra state. --- */
  .oasis-sidebar {
    display: flex !important;
    position: fixed !important;
    top: 0;
    left: 0;
    /* Stops ABOVE the bottom bar so the ☰ cell (which becomes ✕) stays
       tappable — a drawer covering its own close button is a trap.
       Stated as an explicit HEIGHT, not `bottom`: the aside already carries a
       `height` (100vh, plus Tailwind's h-screen), and top+bottom+height is
       over-constrained — the browser then ignores `bottom` and the drawer ran
       down over the bar. `dvh` so the iOS URL bar collapsing can't shift it. */
    height: calc(100vh - var(--mobile-tabbar-h, 58px)) !important;
    height: calc(100dvh - var(--mobile-tabbar-h, 58px)) !important;
    max-height: none !important;
    width: min(310px, 86vw) !important;
    max-width: 86vw;
    /* !important beats the aside's Tailwind z-50 / .glass background whatever
       order the stylesheets land in. */
    z-index: 80 !important;
    background: var(--ctp-base) !important;
    backdrop-filter: none;
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
    transition: transform 0.25s var(--ease-out-expo);
    box-shadow: 12px 0 40px rgba(0, 0, 0, 0.28);
  }
  /* Closed = parked off-canvas (transform, not `left`: it never reflows the
     page and cannot widen the document). */
  .oasis-sidebar:not(.mobile-open) {
    transform: translateX(-102%);
    box-shadow: none;
    pointer-events: none;
  }
  .oasis-sidebar.mobile-open { transform: none; }
  /* The drag-resize handle is mouse-only chrome — on a phone it just eats
     swipes at the drawer's edge. */
  .oasis-sidebar .cursor-col-resize { display: none; }
  /* Tap-outside scrim (markup already exists, desktop-hidden). Stops above the
     bottom bar too, so the nav stays live while the drawer is open. */
  .nav-scrim {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: calc(100vh - var(--mobile-tabbar-h, 58px));
    height: calc(100dvh - var(--mobile-tabbar-h, 58px));
    z-index: 79;
    background: rgba(0, 0, 0, 0.45);
  }
  .mobile-tabbar {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    gap: 2px;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    background: var(--ctp-mantle);
    border-top: 1px solid var(--ctp-surface1);
  }
  /* Equal cells, no sidescroll (Mark: "the sidescroll is weird") — icon over
     a small label, native tab-bar style. Everything else lives in the drawer. */
  .mobile-tabbar-item {
    flex: 1 1 0;
    min-width: 0;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 5px 2px;
    border-radius: 12px;
    font-size: 10px;
    font-weight: 700;
    font-family: var(--font-display);
    color: var(--ctp-overlay1);
    white-space: nowrap;
    background: transparent;
    border: 1px solid transparent;
    min-height: 46px;
  }
  .mobile-tabbar-item.active {
    color: var(--ctp-blue);
    background: color-mix(in srgb, var(--ctp-blue) 10%, transparent);
    border-color: color-mix(in srgb, var(--ctp-blue) 20%, transparent);
  }
  .mobile-tabbar-item .nav-ico { width: 20px; height: 20px; flex-shrink: 0; }
  /* content must clear the fixed bar */
  .main-content { padding-bottom: 92px !important; }

  /* --- Tables: any bespoke table without a scroll wrapper scrolls itself
     instead of blowing out the page width (body overflow-x is hidden);
     the first column stays pinned so rows keep their identity mid-scroll. --- */
  .main-content table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    /* THE letter-crush fix (2026-07-27). `display: block` makes the table its
       own scrollport, but its internal table layout then gets the block's
       content width — i.e. the viewport — so every column is squeezed toward
       min-content and long labels break one character per line ("E S T .
       C L I C K S", "T O P I C · G B", a "Keyword" header 11px wide and 110px
       tall). min-content: max-content lets the columns keep their natural
       width and the table scroll instead, which is what the wrapper was for.
       Applies to every bespoke table on every tab, so this class of bug can't
       come back one screen at a time. */
    min-width: max-content;
  }
  /* …except the Blacklist, which answers narrowness by FOLDING columns into the
     keyword's second line rather than by scrolling. `display: block` also drops
     table-layout:fixed, so the declared widths stopped applying and a 56px
     un-block column rendered 346px wide while the table kept a 473px
     max-content floor — a sideways scroll on every phone (Mark 2026-07-29:
     "its not responsive?"). Opting out restores real table layout. */
  .main-content table.bl-table {
    display: table; min-width: 0; overflow-x: visible;
  }
  .main-content table thead th:first-child,
  .main-content table tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--ctp-mantle);
  }
  /* Pinning the first column is for tables that scroll; this one doesn't, and
     the hard mantle background would paint over the green on a just-added row. */
  .main-content table.bl-table thead th:first-child,
  .main-content table.bl-table tbody td:first-child {
    position: static; background: inherit;
  }
}

/* Small mobile (< 480px) */
@media (max-width: 480px) {
  .nav-tab span:not(.badge):not([class*="px-"]) {
    /* Hide tab text labels, show icons only on very small screens */
  }
  .stat-card .stat-value { font-size: var(--text-xl); }
  .stat-card .stat-label { font-size: 11.5px; }

  /* Stack 2-col grids on very small screens where appropriate */
  .grid-cols-2:not(.gap-x-2) { grid-template-columns: 1fr !important; }
}

/* --- Article Prose (rendered markdown) --- */
.tl-article-prose {
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--ctp-text);
}
.tl-article-prose h1 {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--ctp-teal);
  margin: 0 0 0.75rem;
  line-height: 1.3;
}
.tl-article-prose h2 {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--ctp-blue);
  margin: 1.5rem 0 0.5rem;
  line-height: 1.3;
}
.tl-article-prose h3 {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ctp-mauve);
  margin: 1.25rem 0 0.4rem;
}
.tl-article-prose h4 {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ctp-peach);
  margin: 1rem 0 0.3rem;
}
.tl-article-prose p {
  margin: 0 0 0.75rem;
}
.tl-article-prose ul, .tl-article-prose ol {
  margin: 0 0 0.75rem;
  padding-left: 1.5rem;
}
.tl-article-prose ul { list-style-type: disc; }
.tl-article-prose ol { list-style-type: decimal; }
.tl-article-prose li {
  margin-bottom: 0.25rem;
}
.tl-article-prose li > ul, .tl-article-prose li > ol {
  margin-top: 0.25rem;
  margin-bottom: 0;
}
.tl-article-prose strong {
  font-weight: 600;
  color: var(--ctp-text);
}
.tl-article-prose em {
  font-style: italic;
}
.tl-article-prose a {
  color: var(--ctp-blue);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.tl-article-prose a:hover {
  color: var(--ctp-teal);
}
.tl-article-prose blockquote {
  border-left: 3px solid var(--ctp-surface2);
  padding: 0.5rem 1rem;
  margin: 0.75rem 0;
  color: var(--ctp-subtext0);
  background: var(--ctp-surface0);
  border-radius: 0 0.375rem 0.375rem 0;
}
.tl-article-prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 0.75rem 0;
  font-size: 0.8125rem;
}
.tl-article-prose th {
  text-align: left;
  font-weight: 600;
  padding: 0.5rem 0.75rem;
  border-bottom: 2px solid var(--ctp-surface2);
  color: var(--ctp-subtext1);
  background: var(--ctp-surface0);
}
.tl-article-prose td {
  padding: 0.4rem 0.75rem;
  border-bottom: 1px solid var(--ctp-surface1);
}
.tl-article-prose tr:hover td {
  background: var(--ctp-surface0);
}
.tl-article-prose code {
  font-size: 0.8125rem;
  background: var(--ctp-surface1);
  padding: 0.15rem 0.35rem;
  border-radius: 0.25rem;
}
.tl-article-prose hr {
  border: none;
  border-top: 1px solid var(--ctp-surface2);
  margin: 1.25rem 0;
}
.tl-article-prose img {
  max-width: 100%;
  border-radius: 0.5rem;
}

/* Parent-topic chips — colored via inline --accent CSS var so dynamic
 * parents tint correctly without depending on Tailwind opacity utilities
 * (which silently no-op when the underlying color is a full var()). */
.parent-chip {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
}
.parent-chip-soft {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* Topics V2 — Parent row V10 polish (volume bar, geo-grid, top-3 peek,
 * accent swim-lane stripe, expand-time lede). Loaded via ported polish
 * Topics V2 design pass (V10).
 *
 * Stripe uses box-shadow inset on the first <td> rather than a ::before
 * pseudo on the <tr> — pseudos on table-row display are unreliable. */
.ost-prow > td:first-child {
  box-shadow: inset 4px 0 0 var(--accent);
}
/* Parent rollup header — laid out as real table cells (geo+parent+topic merged
 * via colspan, then priority/kws/vol/star) so the rollup counts line up exactly
 * above the per-topic KWS / VOL columns below. */
.ost-prow-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.ost-prow-tcount { flex: none; font: 600 11px/1 var(--font-data); color: var(--ctp-overlay1); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Priority pills — one compact pill per non-empty P0–P5 tier, left-packed.
   No stretched bar, no inline legend, nothing to overlap in multi-geo. */
.ost-pills { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; min-width: 0; }
.ost-pill { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px 2px 5px; border-radius: 99px;
  font: 700 10px/1.4 var(--font-data); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ost-pill > i { width: 7px; height: 7px; border-radius: 99px; display: inline-block; flex: none; }
.ost-pill-p0 { background: color-mix(in srgb, var(--ctp-red) 14%, transparent); color: var(--ctp-red); }
.ost-pill-p0 > i { background: var(--ctp-red); }
.ost-pill-p1 { background: color-mix(in srgb, var(--ctp-peach) 14%, transparent); color: var(--ctp-peach); }
.ost-pill-p1 > i { background: var(--ctp-peach); }
.ost-pill-p2 { background: color-mix(in srgb, var(--ctp-yellow) 16%, transparent); color: var(--ctp-yellow); }
.ost-pill-p2 > i { background: var(--ctp-yellow); }
.ost-pill-p3 { background: color-mix(in srgb, var(--ctp-green) 14%, transparent); color: var(--ctp-green); }
.ost-pill-p3 > i { background: var(--ctp-green); }
.ost-pill-p4 { background: color-mix(in srgb, var(--ctp-teal) 14%, transparent); color: var(--ctp-teal); }
.ost-pill-p4 > i { background: var(--ctp-teal); }
.ost-pill-p5 { background: color-mix(in srgb, var(--ctp-overlay0) 18%, transparent); color: var(--ctp-overlay1); }
.ost-pill-p5 > i { background: var(--ctp-overlay0); }
/* Rollup count cell — number over a tiny label, right-aligned to its column */
.ost-rollup { display: inline-flex; flex-direction: column; align-items: flex-end; gap: 1px; line-height: 1.1; }
.ost-rollup > b { font: 700 13px/1.1 var(--font-data); font-variant-numeric: tabular-nums; color: var(--ctp-text); }
.ost-rollup > em { font: 600 9px/1 var(--font-data); font-style: normal; text-transform: uppercase; letter-spacing: .05em; color: var(--ctp-overlay1); }
.ost-star { font: 700 12px/1 var(--font-data); color: var(--ctp-yellow); white-space: nowrap; }
/* Topic rows are click-to-expand. Hover = clear lavender wash + accent edge;
   element+class selector outranks the zebra utility so it wins regardless of
   source order. Open rows keep a faint wash so you can see which is expanded. */
tr.ost-kw-toggle:hover { background: color-mix(in srgb, var(--ctp-lavender) 13%, transparent); }
tr.ost-kw-toggle:hover > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
tr.ost-kw-open { background: color-mix(in srgb, var(--ctp-lavender) 7%, transparent); }
tr.ost-kw-open > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.ost-vol-bar {
  display: block;
  height: 6px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 3px;
  overflow: hidden;
  max-width: 420px;
}
.ost-vol-bar > i {
  display: block;
  height: 100%;
  background: var(--accent);
}
.ost-top3 {
  font: 500 11px/1.4 var(--font-ui);
  color: var(--ctp-overlay1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.ost-top3 b {
  color: var(--ctp-subtext1);
  font-weight: 600;
}
.ost-top3 .sep {
  color: var(--ctp-overlay0);
  margin: 0 5px;
}
.ost-top3 b {
  color: var(--ctp-subtext1);
  font-weight: 500;
}
.ost-top3 .sep {
  color: var(--ctp-overlay0);
  margin: 0 5px;
}
.ost-geo-grid {
  display: grid;
  grid-template-columns: repeat(22, 5px);
  grid-auto-rows: 5px;
  gap: 1px;
  min-width: 0;
  position: relative;
}
.ost-geo-grid > i {
  display: block;
  width: 5px;
  height: 5px;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-radius: 1px;
  cursor: pointer;
  position: relative;
  transition: transform 100ms ease, box-shadow 100ms ease;
}
.ost-geo-grid > i.lit {
  background: var(--accent);
}
.ost-geo-grid > i:hover {
  transform: scale(2.4);
  box-shadow: 0 0 0 1px var(--ctp-text);
  z-index: 5;
}
/* Custom hover label — pops out a small chip with the geo code + state
 * so users see WHICH geo each square represents on hover. */
.ost-geo-grid > i[data-label]:hover::after {
  content: attr(data-label);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%) scale(0.55);
  transform-origin: bottom center;
  padding: 3px 7px;
  background: var(--ctp-crust);
  border: 1px solid var(--ctp-surface1);
  color: var(--ctp-text);
  font: 600 11px/1 var(--font-data);
  font-variant-numeric: tabular-nums;
  border-radius: 4px;
  white-space: nowrap;
  pointer-events: none;
  z-index: 10;
}
/* Star/shortlist toggle for keywords + topics in Topics V2. Empty stars are
   always visible in muted grey; hover/focus brightens them. Filled stars are lit. */
.tl-star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: var(--ctp-overlay0);
  cursor: pointer;
  line-height: 1;
  transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease;
}
.tl-star:hover { background: color-mix(in oklab, var(--ctp-yellow) 12%, transparent); color: var(--ctp-yellow); }
.tl-star.empty { opacity: 1; color: var(--ctp-surface2); }
.tl-star.empty:hover { color: var(--ctp-yellow); }
.tl-star.empty:focus-visible { outline: 1px solid var(--ctp-blue); }
.tl-star.filled { color: var(--ctp-yellow); opacity: 1; }
.tl-star.filled:hover { color: var(--ctp-peach); }
/* Planned = saved to the framed site's Content Plan: green star + green row.
   Dedicated classes because (a) .tl-star.filled outranks the text-ctp-green
   utility, (b) ctp-palette /opacity variants (bg-ctp-green/10) don't compile. */
.tl-star.filled.planned { color: var(--ctp-green); }
.tl-star.filled.planned:hover { color: var(--ctp-teal); }
.ost-row-planned,
.ost-row-planned:hover { background: color-mix(in oklab, var(--ctp-green) 11%, transparent) !important; }
.ost-row-planned > td:first-child { box-shadow: inset 2px 0 0 var(--ctp-green); }

.ost-lede {
  position: relative;
  padding: 12px 18px 12px 22px;
  background: color-mix(in srgb, var(--accent) 10%, var(--ctp-base));
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  color: var(--ctp-subtext1);
  font: 500 12px/1.55 var(--font-ui);
}
.ost-lede::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--accent);
  opacity: 0.55;
}
.ost-lede .ost-lede-tag {
  color: var(--accent);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 11.5px;
  margin-right: 8px;
}
.ost-child > td:first-child {
  box-shadow: inset 4px 0 0 color-mix(in srgb, var(--accent) 65%, transparent);
}

/* ──────────────────────────────────────────────────────────────────────
   Content Gap — live progress timeline
   --------------------------------------------------------------------
   Ported from the deleted Action Loop tab's al-timeline. Compact log of
   phase events appended by the backend during a run: publisher resolve →
   ClickUp fetch → URL probes → topic load → task mapping → bucket compute
   → BigQuery enrichment → gap derivation. Newest event on top so the
   most recent activity is always visible without scrolling.
   ────────────────────────────────────────────────────────────────────── */
.cg-timeline {
  background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface1);
  border-radius: 8px;
  overflow: hidden;
  font-family: var(--font-data);
  font-size: 11.5px;
}
.cg-timeline-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--ctp-surface1);
  background: var(--ctp-crust);
}
.cg-timeline-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ctp-mauve);
  animation: cg-timeline-pulse 1.4s ease-in-out infinite;
  flex-shrink: 0;
}
.cg-timeline-dot.done   { background: var(--ctp-green); animation: none; }
.cg-timeline-dot.failed { background: var(--ctp-red);   animation: none; }
@keyframes cg-timeline-pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}
.cg-timeline-title {
  color: var(--ctp-text);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11.5px;
}
.cg-timeline-elapsed {
  margin-left: auto;
  color: var(--ctp-overlay1);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.cg-timeline-body {
  max-height: 280px;
  overflow-y: auto;
  padding: 6px 12px 8px;
  display: flex;
  flex-direction: column-reverse; /* newest on top */
}
.cg-timeline-row {
  display: grid;
  grid-template-columns: 56px 96px 1fr;
  gap: 10px;
  padding: 3px 0;
  color: var(--ctp-subtext0);
  line-height: 1.45;
  border-bottom: 1px dotted color-mix(in srgb, var(--ctp-surface1) 60%, transparent);
}
.cg-timeline-row:last-child { border-bottom: none; }
.cg-timeline-time {
  color: var(--ctp-overlay1);
  font-variant-numeric: tabular-nums;
}
.cg-timeline-scope {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--ctp-surface1);
  color: var(--ctp-text);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-align: center;
  align-self: center;
  text-transform: uppercase;
}
.cg-timeline-msg   { color: var(--ctp-text); word-break: break-word; }
.cg-timeline-empty { opacity: 0.55; font-style: italic; }

/* Phase chip colors — same hue family as the existing CG geo + bucket
   colors so the eye doesn't reroute when switching from the timeline
   to the results below. */
.cg-timeline-scope.scope-orchestrator { background: rgba(203, 166, 247, 0.22); color: var(--ctp-mauve); }      /* lifecycle */
.cg-timeline-scope.scope-publisher    { background: rgba(116, 199, 236, 0.22); color: var(--ctp-sapphire); }   /* ClickUp publisher resolve */
.cg-timeline-scope.scope-clickup      { background: rgba(137, 220, 235, 0.22); color: var(--ctp-sky); }        /* ClickUp tasks */
.cg-timeline-scope.scope-probe        { background: rgba(250, 179, 135, 0.22); color: var(--ctp-peach); }      /* live URL probes */
.cg-timeline-scope.scope-topics       { background: rgba(245, 194, 231, 0.22); color: var(--ctp-pink); }       /* topic snapshot */
.cg-timeline-scope.scope-mapping      { background: rgba(249, 226, 175, 0.22); color: var(--ctp-yellow); }     /* task → topic */
.cg-timeline-scope.scope-buckets      { background: rgba(166, 227, 161, 0.22); color: var(--ctp-green); }      /* covered/claimed/lost */
.cg-timeline-scope.scope-bigquery     { background: rgba(148, 226, 213, 0.22); color: var(--ctp-teal); }       /* BQ FTD enrichment */
.cg-timeline-scope.scope-gaps         { background: rgba(203, 166, 247, 0.22); color: var(--ctp-mauve); }      /* gap derivation */
.cg-timeline-scope.scope-default      { background: var(--ctp-surface1); color: var(--ctp-subtext0); }

/* ──────────────────────────────────────────────────────────────────────
   Content Gap — polished section + KPI card chrome
   --------------------------------------------------------------------
   Each section is a surface0 card with a 3px colored left bar that
   identifies its meaning at a glance. Inside, the dashboard KPI tiles
   drop into a slightly-darker mantle "well" with proper card chrome
   (border + bg + padding) so values pop instead of floating as bare
   text on flat surface. Mono-numeric values lend a data-instrument feel.
   ────────────────────────────────────────────────────────────────────── */
.cg-section {
  position: relative;
}
.cg-section::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--cg-accent, var(--ctp-overlay0));
}
.cg-section--teal      { --cg-accent: var(--ctp-teal); }
.cg-section--peach     { --cg-accent: var(--ctp-peach); }
.cg-section--peach-dim { --cg-accent: color-mix(in srgb, var(--ctp-peach) 65%, var(--ctp-overlay0)); }
.cg-section--mauve     { --cg-accent: var(--ctp-mauve); }
.cg-section--blue      { --cg-accent: var(--ctp-blue); }
.cg-section--red       { --cg-accent: var(--ctp-red); }
.cg-section--yellow    { --cg-accent: var(--ctp-yellow); }

.cg-section-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px 12px 18px;
  border-bottom: 1px solid var(--ctp-surface1);
  background: color-mix(in srgb, var(--cg-accent) 4%, var(--ctp-surface0));
}
.cg-section-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--cg-accent);
  letter-spacing: 0.005em;
}
.cg-section-count {
  font-family: var(--font-data);
  font-size: 12.5px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cg-accent) 14%, transparent);
  color: var(--cg-accent);
  font-variant-numeric: tabular-nums;
}
.cg-count-teal   { background: color-mix(in srgb, var(--ctp-teal) 14%, transparent);   color: var(--ctp-teal); }
.cg-count-peach  { background: color-mix(in srgb, var(--ctp-peach) 14%, transparent);  color: var(--ctp-peach); }
.cg-count-mauve  { background: color-mix(in srgb, var(--ctp-mauve) 14%, transparent);  color: var(--ctp-mauve); }
.cg-count-blue   { background: color-mix(in srgb, var(--ctp-blue) 14%, transparent);   color: var(--ctp-blue); }
.cg-count-red    { background: color-mix(in srgb, var(--ctp-red) 14%, transparent);    color: var(--ctp-red); }
.cg-count-yellow { background: color-mix(in srgb, var(--ctp-yellow) 14%, transparent); color: var(--ctp-yellow); }

.cg-section-chip {
  font-size: 11.5px;
  color: var(--ctp-subtext0);
  background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface1);
  padding: 3px 8px;
  border-radius: 4px;
  font-weight: 500;
}
.cg-section-toggle {
  margin-left: auto;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ctp-subtext0);
  background: transparent;
  border: 1px solid var(--ctp-surface1);
  border-radius: 6px;
  padding: 4px 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
  letter-spacing: 0.02em;
}
.cg-section-toggle:hover:not(:disabled) {
  color: var(--ctp-text);
  background: var(--ctp-mantle);
  border-color: var(--ctp-surface2);
}
.cg-section-toggle:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.cg-toggle-caret {
  display: inline-block;
  font-size: 11.5px;
  transition: transform 150ms ease-out;
  color: inherit;
}
.cg-toggle-caret.is-open { transform: rotate(90deg); }

/* KPI tile grid — sits in a darker well so each tile pops as its own card */
.cg-kpi-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  padding: 14px 16px;
  background: var(--ctp-mantle);
}
.cg-kpi-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cg-kpi-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 768px, not 720px: phone layout starts at 768px everywhere else, so a 720
   cutoff left 721-768px showing five letter-crushed KPI tiles. */
@media (max-width: 768px) {
  .cg-kpi-grid,
  .cg-kpi-grid--3,
  .cg-kpi-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.cg-kpi {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: 8px;
  padding: 10px 12px 11px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 64px;
  transition: border-color 120ms, box-shadow 120ms;
}
.cg-kpi:hover {
  border-color: var(--ctp-surface2);
  box-shadow: 0 1px 0 var(--ctp-surface1);
}
.cg-kpi--primary {
  border-color: color-mix(in srgb, var(--cg-accent) 35%, var(--ctp-surface1));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cg-accent) 12%, transparent);
}
.cg-kpi-label {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ctp-subtext0);
  font-weight: 600;
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.cg-kpi-window {
  text-transform: none;
  letter-spacing: 0;
  font-size: 10.5px;
  color: var(--ctp-overlay1);
  font-weight: 500;
}
.cg-kpi-value {
  font-family: var(--font-data);
  font-size: 22px;
  font-weight: 700;
  color: var(--ctp-text);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.cg-kpi-aside {
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ctp-overlay1);
  text-transform: none;
  letter-spacing: 0;
}

.cg-value-teal   { color: var(--ctp-teal); }
.cg-value-peach  { color: var(--ctp-peach); }
.cg-value-mauve  { color: var(--ctp-mauve); }
.cg-value-blue   { color: var(--ctp-blue); }
.cg-value-red    { color: var(--ctp-red); }
.cg-value-yellow { color: var(--ctp-yellow); }
.cg-value-green  { color: var(--ctp-green); }

/* ── Top summary block — same visual language as the section cards ── */
.cg-summary {
  background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1);
  border-radius: 12px;
  overflow: hidden;
}
.cg-summary-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 12px 18px;
  background: var(--ctp-mantle);
  border-bottom: 1px solid var(--ctp-surface1);
  font-size: 12.5px;
}
.cg-summary-label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ctp-overlay1);
}
.cg-summary-value {
  font-family: var(--font-data);
  font-size: 12.5px;
  color: var(--ctp-text);
  font-weight: 600;
}
.cg-summary-sep { color: var(--ctp-overlay0); }
.cg-summary-grid {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 0;
}
@media (max-width: 1100px) {
  .cg-summary-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .cg-summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.cg-summary-tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
  border-right: 1px solid var(--ctp-surface1);
  border-bottom: 1px solid var(--ctp-surface1);
  background: var(--ctp-surface0);
}
.cg-summary-tile:hover { background: var(--ctp-mantle); }
.cg-summary-tile:last-child { border-right: none; }
.cg-summary-grid > .cg-summary-tile:nth-last-child(-n+1) { border-bottom: none; }
@media (max-width: 1100px) {
  .cg-summary-tile:nth-child(4n) { border-right: none; }
  .cg-summary-grid > .cg-summary-tile:nth-last-child(-n+4) { border-bottom: none; }
}
@media (max-width: 640px) {
  .cg-summary-tile:nth-child(2n) { border-right: none; }
  .cg-summary-grid > .cg-summary-tile:nth-last-child(-n+2) { border-bottom: none; }
}
.cg-summary-name {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--ctp-subtext0);
}
.cg-summary-num {
  font-family: var(--font-data);
  font-size: 24px;
  font-weight: 700;
  color: var(--ctp-text);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.cg-summary-meta {
  font-size: 11.5px;
  color: var(--ctp-subtext0);
  padding: 10px 18px;
  border-top: 1px solid var(--ctp-surface1);
  background: color-mix(in srgb, var(--ctp-mantle) 50%, var(--ctp-surface0));
}
.cg-summary-meta strong {
  font-family: var(--font-data);
  color: var(--ctp-text);
  font-weight: 700;
}
.cg-summary-warn { color: var(--ctp-red); font-weight: 600; }
.cg-summary-warnings {
  padding: 8px 18px 12px;
  font-size: 11.5px;
  color: var(--ctp-yellow);
  background: color-mix(in srgb, var(--ctp-yellow) 6%, var(--ctp-surface0));
  border-top: 1px solid color-mix(in srgb, var(--ctp-yellow) 20%, var(--ctp-surface1));
}
.cg-summary-warnings > div { line-height: 1.5; }

/* --- Left vertical sidebar (nav moved off the top bar) --- */
.oasis-sidebar {
  height: 100vh;
  max-height: 100vh;
  align-self: flex-start;
}
.oasis-sidebar .nav-tab {
  display: flex;
  width: 100%;
  justify-content: flex-start;
  padding: 8px 12px;
  font-size: var(--text-sm);
}
.oasis-sidebar .nav-tab svg { flex-shrink: 0; }

/* Right-side drawer + backdrop transitions (Competitors company-details panel) */
.slide-right-enter-active, .slide-right-leave-active { transition: transform 0.24s ease; }
.slide-right-enter-from, .slide-right-leave-to { transform: translateX(100%); }
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; }

/* --- Geo flags — ONE fixed size everywhere ---------------------------------
   Every flag (country + US state) renders in the same fixed box so the geo
   column reads as a clean, aligned grid. object-fit:cover fills the box no
   matter each flag's native aspect ratio, so a wide flag (IE 2:1) and a tall
   one (CH 1:1) end up identical in size. Used app-wide. */
.geo-flag {
  display: inline-block;
  width: 20px;
  height: 14px;
  object-fit: cover;
  border-radius: 2px;
  flex-shrink: 0;
  vertical-align: middle;
  box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.25);
}
/* Compact flag for inside chips/tight table cells (geo code badges, Markets
   columns) where the full 20x14 flag would overpower the 10px text beside it. */
.geo-flag-sm {
  width: 14px;
  height: 10px;
}
/* Domain/platform favicon (Google favicon service, 64px source rendered at
   14px). Slight rounding so white-background icons don't read as raw squares. */
.domain-favicon {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  flex-shrink: 0;
  vertical-align: middle;
}
/* Even smaller variant for inside 10px-text chips (company Top-sites badges). */
.domain-favicon-xs {
  width: 12px;
  height: 12px;
}
/* Region buckets (LATAM / Global) have no flag image — the globe emoji sits in
   the same-width slot so it lines up with the flag column. */
.geo-flag-emoji {
  display: inline-block;
  width: 20px;
  text-align: center;
  font-size: 14px;
  line-height: 14px;
  flex-shrink: 0;
  vertical-align: middle;
}

/* Collapsed sidebar icon-rail (2026-07-07): uniform centered 32px chips so
   the rail reads as a tidy column instead of stretched full-width pills. */
.nav-rail .nav-tab,
.nav-rail .btn-ghost {
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  margin-left: auto;
  margin-right: auto;
  justify-content: center;
}

/* Collapsed rail: hard-center every column so no item hugs the left edge */
.nav-rail nav,
.nav-rail nav + div,
.nav-rail > div:first-child {
  align-items: center;
}

/* ── Sticky section chrome (2026-07-08) ─────────────────────────────────
   The lane sub-tab bar pins to the viewport top; each section's primary
   filter toolbar pins just below it; the browse rails pin and scroll their
   own (long) lists internally. The body is the scroll container — same
   trick the app sidebar already uses. Disabled on phones: sticky chrome
   eats too much of a small viewport and the rails become drawers there. */
:root { --lane-tabbar-h: 52px; --section-sticky-h: 96px; --scope-bar-h: 0px; --brand-header-h: 77px; --hero-h: 0px; --mode-sticky-h: 0px; }
/* The app frame NEVER scrolls sideways (Mark 2026-07-16: sidebar + browse
   rail + top bar must ALWAYS stay pinned). overflow-x: clip kills horizontal
   viewport scroll WITHOUT creating a scroll container — overflow-x: hidden
   here would silently break every position:sticky descendant. Anything wider
   than the page must own its scroll (.wide-table-scroll, .platform-table-scroll,
   overflow-x-auto wrappers). */
html { overflow-x: clip; }
/* No elastic bounce (Mark 2026-07-23 recording): macOS rubber-band drags the
   WHOLE document — pinned bars, sidebar, hero all lurch while the fixed
   .header-divider-line stays put and slices across the moving content.
   Killing overscroll on the root stops the page ever visually moving at its
   scroll boundaries; inner scrollers keep their own contained behavior. */
html, body { overscroll-behavior-y: none; }
/* Lane tab bar = the app header strip (2026-07-15, Mark): full-bleed outside
   <main>, flush against the sidebar, min-height mirrors the measured sidebar
   .brand-header (+1px divider) so the tabs sit vertically centered on the
   logo and the bottom hairline continues the sidebar's divider line. */
/* Same glass recipe as the sidebar (Mark 2026-07-16: "header and sidebar is
   1") — identical background + blur so the logo block and the tab bar read
   as one continuous header piece. */
.lane-tabbar-sticky { position: sticky; top: 0; z-index: 40;
  /* OPAQUE twin of the sidebar's glass (white 70% composited over the page
     bg): identical color so header+sidebar stay "1", but table rows can
     never ghost through while scrolling (Mark's recording, 2026-07-16 —
     translucent glass + content scrolling beneath read as a glitch). */
  background: color-mix(in srgb, #fff 70%, var(--ctp-base));
  min-height: var(--brand-header-h); align-content: center;
  padding: 6px 16px 6px 14px;
  box-shadow: 0 8px 12px -12px rgba(0,0,0,.35); }
/* THE header divider — one fixed full-width line under the logo/tab-bar row
   (a single element can't misalign with itself; replaced the two-segment
   sidebar+bar lines that kept drifting, Mark 2026-07-16). */
.header-divider-line { position: fixed; left: 0; right: 0; z-index: 51; height: 1px; /* above the z-50 sidebar — it must paint across it */
  top: calc(var(--brand-header-h, 77px) - 1px);
  background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-mauve) var(--nav-w, 270px), var(--ctp-surface1) 60%); }
@media (max-width: 768px) { .header-divider-line { display: none; } }
/* Apron: the hero pins 24px below the bar (Mark 2026-07-16: "always this
   spacing") — this opaque strip fills that gap so the table never shows
   through the slit while scrolling. */
.lane-tabbar-sticky::after { content: ''; position: absolute; left: 0; right: 0;
  top: 100%; height: 24px; background: var(--ctp-base); }
/* Universal header search — "gradient hairline" (Mark 2026-07-16): the header
   divider's blue→lavender gradient worn as the pill's border. Two stacked
   gradients: opaque fill clipped to padding-box, gradient clipped to
   border-box, so the transparent border reveals only the gradient.
   Rest = quiet 1px hairline, NO shadow (matches the site pill's weight —
   Mark 2026-07-16: it read as permanently focused); glow only on focus. */
.uni-search { height: 36px;
  background: linear-gradient(var(--ctp-surface0), var(--ctp-surface0)) padding-box,
              linear-gradient(90deg, rgb(var(--ctp-blue-rgb) / .55), rgb(var(--ctp-lavender-rgb) / .55)) border-box;
  border: 1px solid transparent; border-radius: 999px;
  transition: box-shadow .15s, background .15s; }
.uni-search:focus-within {
  background: linear-gradient(var(--ctp-surface0), var(--ctp-surface0)) padding-box,
              linear-gradient(90deg, var(--ctp-blue), var(--ctp-lavender)) border-box;
  box-shadow: 0 0 0 3px rgb(var(--ctp-lavender-rgb) / .18), 0 2px 6px rgb(var(--ctp-lavender-rgb) / .18); }
.uni-search-ico { color: var(--ctp-lavender); }
.uni-kbd { display: inline-flex; align-items: center; flex-shrink: 0;
  font-size: 10px; font-weight: 700; color: var(--ctp-overlay1);
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1);
  border-bottom-width: 2px; border-radius: 5px; padding: 2px 6px; }
/* +12px per preceding row = the space-y-3 gaps, which the measured heights
   don't include — without them the pinned rows overlap (Mark: "fix the
   spacing… they looked bad before too"). */
.section-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + 12px); z-index: 30; background: var(--ctp-base); box-shadow: 0 10px 14px -14px rgba(0,0,0,.3); }
/* -b offset uses the MEASURED section-sticky height (app.js publishes
   --section-sticky-h) — the Explorer header wraps to two rows on narrow
   windows, so a hardcoded 96px would slide this bar under the header. */
.section-sticky-b { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h)); z-index: 29; background: var(--ctp-base); }
/* Explorer main-table chrome stack (2026-07-15): toolbar (.section-sticky) →
   VIEWING scope bar → Table|Map + Mode row, each pinned under the last with
   MEASURED heights (app.js publishes --scope-bar-h; 0 when no scope chips).
   z order descends so toolbar dropdowns overlay the bars below them. */
.scope-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h) + 24px); z-index: 28; background: var(--ctp-base); }
/* "Topic not found · ClickUp decisions" card (2026-08-04, Mark: "fix the spacing
   between these 2 and make this sticky pls till they are fixed ie decided").

   Spacing: the card is a child of a .space-y-3 column, and Tailwind's
   `.space-y-3 > :not([hidden]) ~ :not([hidden])` is a 3-class selector, so a
   plain .mt-3 utility LOSES to it and the card kept sitting flush against the
   mode row. Hence !important here — it is beating a utility, not a hand rule.

   Sticky: it pins directly under the mode row, so it stays on screen while you
   scroll the table and only leaves when the list empties (the card is rendered
   v-if on v2TopicNotFound.length, so "till they are decided" needs no extra
   state — deciding a task drops it from the overlay payload).

   z-index 14 is deliberately BELOW the sticky table header (tr.ost-head-sticky
   > th is 15) and above the table rows. Any residual page scroll slides the
   table up under this card, and when the two meet exactly one can win: the
   column header has to, because you read every row against it, whereas losing
   10-20px off this card's bottom edge costs nothing. Getting this backwards is
   what hid the header entirely. */
/* FROZEN leading columns (2026-08-04, Mark's screen recording: scrolling the
   table sideways left "rows of numbers and tags with no idea which topic each
   is" — the checkbox AND the topic name both scrolled away, so the new select
   column was useless exactly when you needed it). Checkbox + Topic now pin to
   the left edge while the rest of the columns scroll under them.

   Opaque background is mandatory: without it the scrolling cells show straight
   through the pinned ones. The row-STATE tint is re-applied as a background
   IMAGE layer on top of that opaque base, because the tints live on the <tr> as
   semi-transparent !important colours and a sticky <td> cannot inherit them. */
.v2-freeze { position: sticky; z-index: 12; background-color: var(--ctp-base); }
/* `left` is an INLINE style now, summed from the live column widths
   (v2ColFreezeLeft). The old .v2-freeze-sel/.v2-freeze-topic pair hard-coded
   left:0 and left:34px, so resizing or hiding the checkbox column parked Topic
   in the wrong place — and only those two columns could ever be frozen. The
   boundary is a user setting (the pin in the Columns panel) as of 2026-08-04. */
/* The seam: the last frozen column shows where the scroll starts. */
.v2-freeze-edge { box-shadow: 1px 0 0 0 var(--ctp-surface1), 6px 0 8px -6px rgb(0 0 0 / .18); }
/* Pin in the Columns panel — quiet until it means something. */
.v2-freeze-pin { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
  border-radius: 4px; color: var(--ctp-overlay0); flex-shrink: 0; transition: color .12s, background-color .12s; }
.v2-freeze-pin svg { width: 12px; height: 12px; }
.v2-freeze-pin:hover { color: var(--ctp-text); background-color: rgb(var(--ctp-lavender-rgb) / .12); }
.v2-freeze-pin-on { color: var(--ctp-lavender); }
.v2-freeze-pin-edge { color: var(--ctp-lavender); background-color: rgb(var(--ctp-lavender-rgb) / .16); }
tr.ost-head-sticky > th.v2-freeze { z-index: 18; background-color: color-mix(in oklab, var(--ctp-surface0) 40%, var(--ctp-base)); }

/* Frozen cells must MIRROR every row background state (Mark's 2nd recording:
   hovering a row highlighted it from STATUS rightwards while the pinned checkbox
   and Topic cells stayed white — the highlight visibly stopped at the freeze).
   Cause: the row tints live on the <tr> as SEMI-TRANSPARENT colours, and a
   pinned cell needs an OPAQUE background or the scrolling cells show through it,
   so it cannot simply inherit them. Each state is therefore re-declared as the
   same mix over an opaque base.
   ADD NEW ROW STATES HERE TOO or they will stop at the frozen columns. */
tr.ost-kw-toggle:hover > td.v2-freeze { background-color: color-mix(in srgb, var(--ctp-lavender) 13%, var(--ctp-base)); }
tr.ost-kw-open > td.v2-freeze { background-color: color-mix(in srgb, var(--ctp-lavender) 7%, var(--ctp-base)); }
tr.ost-row-written > td.v2-freeze { background-color: color-mix(in oklab, var(--ctp-green) 10%, var(--ctp-base)); }
tr.ost-row-written:hover > td.v2-freeze { background-color: color-mix(in oklab, var(--ctp-green) 16%, var(--ctp-base)); }
tr.ost-row-planned:hover > td.v2-freeze { background-color: color-mix(in oklab, var(--ctp-surface0) 40%, var(--ctp-base)); }
tr.ost-focus-row > td.v2-freeze { background-color: color-mix(in srgb, rgb(249 226 175) 12%, var(--ctp-base)); }
.notfound-sticky { position: sticky;
  /* +10px: once BOTH this card and the mode row are pinned, their pinned
     positions come from `top`, not from margins — margin-top produced a gap of
     exactly 0 because the two offsets were adjacent. The gap has to live here. */
  top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h) + var(--scope-bar-h) + 36px + var(--mode-sticky-h) + 10px);
  z-index: 14;
  margin-top: 10px !important; margin-bottom: 10px !important;
  background: color-mix(in oklab, var(--ctp-red) 5%, var(--ctp-base));
  box-shadow: 0 8px 12px -10px rgb(0 0 0 / .35); }
.mode-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h) + var(--scope-bar-h) + 36px); z-index: 27; background: var(--ctp-base); padding: 6px 0; margin-top: -6px; }
/* NO --hero-h here: the browse rail sits BESIDE the hero (own column), so
   adding the hero height displaces the rail ~200px down even at scroll 0
   (sticky clamps to `top` immediately) — the "giant gap above BROWSE
   OPPORTUNITIES" bug, 2026-07-16. */
/* -48px, not -24px (2026-08-04): the 24px accounted for .main-content's TOP
   padding but not its equal bottom padding, so the rail ended exactly ON the
   viewport edge and the page stayed scrollable. A scrollable page is what let
   the table slide under the sticky Topic-not-found card and hide the column
   header behind it. The geo list inside already scrolls on its own, so the rail
   losing 24px costs nothing. */
.rail-sticky-panel { position: sticky; top: calc(var(--lane-tabbar-h) + 8px); max-height: calc(100vh - var(--lane-tabbar-h) - 48px); overflow-y: auto; overscroll-behavior: contain; }
/* Rail head level with the toolbar across the gap (2026-08-04, Mark: "make them
   start on the same line and centred to eachother"). Both columns are flex
   siblings, so they already START level — an 11px label just sits near the TOP of
   its line while the toolbar centres a 36px control row, leaving the heading
   reading ~12px high. Giving the heading the same 36px box and centring in it puts
   the two centres within a pixel (measured: 87.4 vs 88.0). The card's own 16px top
   padding is deliberately KEPT — zeroing it overshot the other way by 15.6px,
   because the toolbar's -my-1.5 already pulls its border box up 6px. The drawer
   inherits this happily: on a phone the rail is an overlay with nothing to line
   up against, and a 36px header there is just a normal card header. */
.rail-head-row { min-height: 36px; display: flex; align-items: center; }
@media (max-width: 768px) {
  .lane-tabbar-sticky, .section-sticky, .section-sticky-b, .scope-sticky, .mode-sticky { position: static; box-shadow: none; }
  .rail-sticky-panel { position: static; max-height: none; overflow: visible; }
}

/* Pinned table column headers — page-level leaderboards keep their column
   labels visible under the lane tab bar. Requires the table's wrapper NOT to
   be a scroll container (xl:overflow-x-visible on the two wide ones). */
.thead-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h)); z-index: 20; background: var(--ctp-surface0); }
@media (max-width: 768px) { .thead-sticky { position: static; } }

/* SERP Intel keywords card: clip its rounded corners WITHOUT overflow:hidden —
   a hidden ancestor becomes the thead's scrollport, which breaks .thead-sticky
   into the floating mid-table header (the pre-redesign SERP Intel bug). */
.serp-card-clip { overflow: clip; }

/* Seasonal section chrome — the fixtures panel header + its column row pin
   under the Explorer toolbar while you scroll THROUGH that section (sticky
   releases once the section passes); each event card's header does the same
   below the compact toolbar. Offsets stack measured heights (app.js):
   --section-sticky-h (Explorer header), --fixtures-head-h (fixtures title
   bar), --section-sticky-b-h (compact toolbar). Their containers must stay
   overflow-visible — an overflow-hidden ancestor disables position:sticky. */
.fixtures-head-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h)); z-index: 24;
  background: linear-gradient(rgb(var(--ctp-surface0-rgb) / 0.4), rgb(var(--ctp-surface0-rgb) / 0.4)), var(--ctp-base); }
.fixtures-thead-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h) + var(--fixtures-head-h, 36px)); z-index: 23; background: var(--ctp-base); }
/* Tint rides in --evtint (inline style per event status) composited over the
   opaque base — a translucent tailwind bg-* here would go see-through the
   moment rows scroll beneath the pinned header. */
.event-head-sticky { position: sticky; top: calc(var(--lane-tabbar-h) + var(--hero-h) + var(--section-sticky-h) + var(--section-sticky-b-h, 34px)); z-index: 22;
  background: linear-gradient(var(--evtint, transparent), var(--evtint, transparent)), var(--ctp-base); }
@media (max-width: 768px) {
  .fixtures-head-sticky, .fixtures-thead-sticky, .event-head-sticky { position: static; }
}

/* ── Tab hero banners (Option B: gradient hero, Mark picked 2026-07-09) ──
   Wrap a tab's title row in .tab-hero and set the section hue inline via
   --hero-ac. Watermark svg (.tab-hero-wm) must stay the FIRST child; every
   other child is lifted above it so the oversized icon never sits on text. */
.tab-hero { position: sticky; top: calc(var(--lane-tabbar-h) + 24px); z-index: 35; overflow: hidden; border-radius: 12px;
  border: 1px solid var(--ctp-surface1); padding: 12px 16px;
  background: linear-gradient(100deg,
    color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 15%, var(--ctp-surface0)) 0%,
    color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 4%, var(--ctp-surface0)) 55%,
    var(--ctp-surface0) 100%); }
.tab-hero > :not(.tab-hero-wm):not(.v2-flag-backdrop):not(.v2-flag-strip) { position: relative; z-index: 1; }
/* Uniform banner sizing (Mark 2026-07-19): every .tab-hero reads the same —
   a min-height floor, content vertically centered, and the description clamped
   so a long blurb can't balloon the banner past its neighbours. Two markup
   families coexist: heroes with `flex` set directly on .tab-hero (content +
   action side by side) and plain-block heroes (an inner flex row). */
.tab-hero { min-height: 104px; }
/* Opt-out for the Explorer banner only (2026-08-04, Mark: "push Topics Explorer
   up"). It lost its eyebrow and its stat trio shrank, so the 104px floor was
   padding empty air. Every OTHER hero keeps the uniform height. */
.tab-hero--slim { min-height: 0; padding-top: 10px; padding-bottom: 10px; }
/* Bare hero = JUST the breadcrumb line (2026-08-04, Mark: "we dont need these
   green banners anymore, we just need the breadcrumb"). Drops the box, not the
   content: no gradient, no border, no rounding, and the decorative watermark /
   flag backdrop go with it since they only ever existed to fill the banner.
   It stays STICKY, so it keeps an OPAQUE page-coloured background — a
   transparent sticky row lets table rows scroll through the breadcrumb, which
   is the exact ghosting the header bar was fixed for on 2026-07-16.
   Explorer only for now; every other tab keeps its banner. */
.tab-hero--bare { border: 0; border-radius: 0; padding: 6px 2px;
  background: var(--ctp-base); min-height: 0; }
.tab-hero--bare .tab-hero-wm,
.tab-hero--bare .v2-flag-backdrop,
.tab-hero--bare .v2-flag-strip,
.tab-hero--bare .tab-hero-ghost { display: none; }
.tab-hero:not(.flex) { display: flex; flex-direction: column; justify-content: center; }
.tab-hero.flex { align-items: center; }
.tab-hero p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Multi-market scope: a fading row of the selected countries' flags. */
.v2-flag-strip { position: absolute; right: 16px; top: 0; height: 100%; display: flex; align-items: center; gap: 10px;
  opacity: .30; pointer-events: none;
  -webkit-mask-image: linear-gradient(to left, #000 55%, transparent 100%); mask-image: linear-gradient(to left, #000 55%, transparent 100%); }
.v2-flag-strip img { height: 34px; width: auto; border-radius: 3px; object-fit: cover; }
.tab-hero-eyebrow { font-size: 10px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--hero-ac, var(--ctp-teal)); margin-bottom: 3px; }
.tab-hero-wm { position: absolute; right: -16px; bottom: -24px; width: 120px; height: 120px;
  opacity: .09; pointer-events: none; }
/* Hero "what is this page" icon (2026-08-04) — the description moved out of the
   banner and behind a hint. Quiet by default so it never competes with the
   title; 24px tap target on touch, which is why the padding is on the span and
   not the svg. */
.hero-info { display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-left: 1px; flex-shrink: 0; cursor: help;
  color: var(--ctp-overlay0); opacity: .75; transition: opacity .15s, color .15s; }
.hero-info svg { width: 15px; height: 15px; }
.hero-info:hover { opacity: 1; color: var(--hero-ac, var(--ctp-teal)); }
@media (hover: none) { .hero-info { width: 26px; height: 26px; opacity: .9; } }
/* Hero breadcrumbs (2026-08-04) — the trail replaces the bare tab title. The
   LEAF inherits the h2's size/weight/accent so the hero reads exactly as loud as
   before; parents step down to a quiet 13px so they recede into chrome. Phones
   drop the root crumb (and the separator that follows it) rather than wrapping a
   four-level trail onto two lines. */
/* CENTRED, not baseline-aligned (Mark 2026-08-04: "allign pls"). On a shared
   baseline the 13px parents hang off the bottom of the 20px leaf and read as
   dropped; optical centring is what makes the trail sit as one line. */
.hero-crumbs { display: inline-flex; align-items: center; gap: 5px; min-width: 0; flex-wrap: wrap; }
.hero-crumb { font-size: 13px; font-weight: 600; line-height: 1.2; color: var(--ctp-overlay1);
  background: none; border: 0; padding: 0; font-family: inherit; letter-spacing: 0;
  transition: color .15s; }
button.hero-crumb { cursor: pointer; }
button.hero-crumb:hover { color: var(--hero-ac, var(--ctp-teal)); text-decoration: underline;
  text-underline-offset: 2px; }
.hero-crumb-sep { font-size: 13px; font-weight: 400; color: var(--ctp-overlay0); }
.hero-crumb-cur { min-width: 0; overflow-wrap: anywhere; display: inline-flex; align-items: center; gap: 8px; }
/* Bigger than the standard 20x14 .geo-flag because it sits beside a 20px title. */
.hero-crumb-flag { width: 26px; height: 18px; flex-shrink: 0; }
@media (max-width: 640px) {
  .hero-crumb-root { display: none; }
  .hero-crumb-flag { width: 21px; height: 15px; }
}
/* One-line Explorer chrome (2026-08-04, Mark's mock) — the crumb trail shares the
   filter row instead of owning a banner row above it, so the leaf can't keep the
   old h2's 20px: at that size the trail alone pushed the filters into wrapping.
   14px leaf / 13px root sits level with the h-8 filter pills. The root is the
   teal one here (the leaf is the plain-text end of the trail) which inverts the
   banner version on purpose: on a busy row the ACCENT belongs on the thing you
   can click, and the leaf is already carried by its flag. */
.v2-crumbline { flex-shrink: 0; }
.v2-crumbline .hero-crumbs { flex-wrap: nowrap; }
.v2-crumbline .hero-crumb { font-size: 13px; color: var(--ctp-teal); font-weight: 700; }
.v2-crumbline button.hero-crumb:hover { color: var(--ctp-sapphire); }
.v2-crumbline .hero-crumb-sep { color: var(--ctp-overlay0); font-size: 14px; }
.v2-crumb-leaf { font-size: 14px; font-weight: 700; color: var(--ctp-text); white-space: nowrap; }
.v2-crumbline .hero-crumb-flag { width: 22px; height: 15px; }
/* Content Plan wears the same trail in the tab's own GREEN (Mark 2026-08-04:
   "make the breadcrumb for this like on topic explorer"). Its root is plain text,
   not a link — there is nothing above the page to climb to — so it takes the
   accent without a hover state. The leaf carries the site favicon, the job the
   flag does on the Explorer trail. */
.scp-crumbline { --hero-ac: var(--ctp-green); }
.scp-crumbline .hero-crumb { color: var(--ctp-green); }
.hero-crumb-favicon { width: 16px; height: 16px; border-radius: 3px; flex-shrink: 0;
  object-fit: contain; }
/* When the filter strip absorbs a squeeze it must move WHOLE controls to its
   second internal line, never break one up: unconstrained, the P0-P5 block split
   (P2-P4 on one line, P5 alone on the next) and "Saved to plan" stacked into three
   words. Both are legible-looking bugs, so they went unnoticed until the crumb
   trail and Add Topic joined the row and made the squeeze routine. */
.section-sticky [data-tour="filters"] .v2-pf { flex-wrap: nowrap; }
.section-sticky [data-tour="filters"] button { white-space: nowrap; }
/* Vertical-mode badge — the banner's eyebrow ("Sports mode") had a row to itself;
   inline it has to be a chip or it reads as another crumb. */
.v2-crumb-mode { font-size: 9.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ctp-teal); background: rgb(var(--ctp-teal-rgb) / .12); border-radius: 4px;
  padding: 2px 5px; flex-shrink: 0; }
/* Under ~1200px the row can't seat the words "Topics Explorer" as well as the
   filters, the chips and Add Topic, so the root crumb goes — but ONLY when a leaf
   is there to say where you are. Without that `:has` guard a multi-market or niche
   scope (which has no leaf, its scope is on the Viewing chips) collapsed the whole
   trail to a lone ⓘ floating on the row. The second rule undoes the blanket 640px
   `.hero-crumb-root` hide above for the same case. */
@media (max-width: 1200px) {
  .v2-crumbline .hero-crumbs:has(.v2-crumb-leaf) .hero-crumb-root { display: none; }
  /* Content Plan's toolbar is nowhere near as crowded as the Explorer's, and
     dropping its root leaves a bare domain with no page name attached — so it
     keeps "Content Plan" all the way down to the phone breakpoint. */
  .scp-crumbline .hero-crumbs:has(.v2-crumb-leaf) .hero-crumb-root { display: inline-block; }
}
@media (max-width: 640px) {
  .v2-crumbline .hero-crumbs:not(:has(.v2-crumb-leaf)) .hero-crumb-root { display: inline-block; }
}
@media (max-width: 768px) {
  /* The filter strip owns the phone line, so the trail keeps only its leaf. */
  .v2-crumbline { gap: 6px !important; }
  .v2-crumbline > svg { display: none; }
  .v2-crumb-leaf { font-size: 13px; }
}
/* ── oa-toolbar — the house ONE-LINE control bar ────────────────────────────
   Mark 2026-08-04: "should always be in line no matter which sidebar is
   selected/resize of it. You must make it responsive and amend the
   fonts/buttons", and "this is something you should take into account for the
   whole site too".

   What it replaces: the row only stopped wrapping when Viewing chips happened to
   be present (`.section-sticky:has(> .v2-scope-inline)`), so with no market
   selected Add Topic dropped onto a second line. A toolbar's line count must not
   depend on whether an optional chip is there. Width alone was also being fought
   one hardcoded pixel at a time (search 112px → 144px at 1600px), which fixes one
   viewport and breaks the next.

   Three layers, in the order things give way:
     1. The row NEVER wraps on desktop. One line is the contract.
     2. Controls SCALE — heights, font sizes, padding, the search field and the
        crumb text all clamp() with the viewport, so 320px of sidebar plus the
        Niches-mode Geos dropdown still fits without wrapping.
     3. If a genuinely narrow window still runs out, the FILTER GROUP scrolls
        sideways (the same trick the phone strip uses) rather than overflowing the
        row or shoving the primary action off-screen. The page identity on the left
        and the CTA on the right never move.

   Reusable on ANY tab: put `oa-toolbar` on the `.section-sticky` row, then
   `oa-tb-btn` (text control) / `oa-tb-icon` (square icon) / `oa-tb-cta` (the one
   primary action) on its children, and give the scrollable middle group
   `data-tour="filters"`. Listed on /brand. Phones (<769px) keep their own
   scrolling-strip rules below, untouched. */
.v2-filter-search { width: 112px; }
.oa-toolbar {
  --tb-h: clamp(26px, 0.85vw + 15px, 32px);
  --tb-fs: clamp(10.5px, 0.2vw + 8.5px, 12px);
  --tb-px: clamp(6px, 0.45vw + 1px, 12px);
  --tb-gap: clamp(3px, 0.28vw, 6px);
}
@media (min-width: 769px) {
  .oa-toolbar { flex-wrap: nowrap; }
  .oa-toolbar > [data-tour="filters"] {
    flex-wrap: nowrap; min-width: 0; gap: var(--tb-gap);
    overflow-x: auto; scrollbar-width: none;
  }
  .oa-toolbar > [data-tour="filters"]::-webkit-scrollbar { display: none; }
  .oa-toolbar > [data-tour="filters"] > * { flex-shrink: 0; }
  .oa-toolbar .oa-tb-btn { height: var(--tb-h); padding-inline: var(--tb-px); font-size: var(--tb-fs); }
  .oa-toolbar .oa-tb-icon { height: var(--tb-h); width: var(--tb-h); }
  /* The CTA stays a notch taller than the filters — it is the one create action
     on the row, not another filter. */
  .oa-toolbar .oa-tb-cta {
    height: calc(var(--tb-h) + 4px);
    padding-inline: clamp(9px, 0.6vw + 3px, 14px);
    font-size: clamp(11.5px, 0.2vw + 9.5px, 14px);
  }
  .oa-toolbar .v2-pf-btn {
    height: var(--tb-h); min-height: 0; font-size: var(--tb-fs);
    min-width: clamp(27px, 1.7vw + 6px, 38px);
    padding-inline: clamp(4px, 0.35vw + 1px, 10px);
  }
  .oa-toolbar .v2-filter-search {
    height: var(--tb-h); font-size: var(--tb-fs);
    width: clamp(76px, 6vw + 10px, 144px);
  }
  .oa-toolbar .v2-crumb-leaf { font-size: clamp(12px, 0.2vw + 10px, 14px); }
  .oa-toolbar .hero-crumb { font-size: clamp(11px, 0.18vw + 9.3px, 13px); }
  /* Chips hold their size — the scrollable filter group is the pressure valve. */
  .oa-toolbar .v2-scope-inline { flex-shrink: 0; align-self: center; }
  .oa-toolbar .v2-scope-inline button {
    font-size: clamp(10px, 0.15vw + 8.6px, 11px);
    padding-inline: clamp(6px, 0.35vw + 2px, 10px);
  }
}
/* Heroes pin under the lane tab bar on every page (2026-07-15, Mark: "banners
   + top parts always sticky"); app.js measures the visible hero into --hero-h
   so the toolbar/scope/mode/thead tiers stack below it. Static on phones like
   the rest of the sticky chrome (this reset must FOLLOW the base rule —
   source order, the earlier 768px block would lose to it). */
@media (max-width: 768px) { .tab-hero { position: static; } }
@media (max-width: 640px) {
  .tab-hero { padding: 13px 14px; }
  .tab-hero-wm { width: 88px; height: 88px; right: -12px; bottom: -18px; }
}

/* Info tooltip on the geo sidebar "All" row (select-all states/regions).
   Custom CSS rather than Tailwind utilities: tailwind.css is precompiled and
   new variant classes (hover-reveal) would silently not exist at runtime. */
.geo-all-tip { position: relative; }
.geo-all-tip .geo-all-tip-box {
  display: none; position: absolute; left: -64px; top: calc(100% + 8px); z-index: 60;
  width: 190px; padding: 9px 11px;
  background: var(--ctp-mantle); color: var(--ctp-subtext1);
  border: 1px solid var(--ctp-surface1); border-radius: 10px;
  font-size: 11px; font-weight: 500; line-height: 1.5;
  text-align: left; white-space: normal; text-transform: none; letter-spacing: normal;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22);
  pointer-events: none;
}
.geo-all-tip:hover .geo-all-tip-box { display: block; }

/* Faint mode word in the Topics Explorer hero (Sports / Casino modes).
   Sits behind the watermark icon; --hero-ac drives the tint. */
.tab-hero-ghost {
  font-family: var(--font-display, inherit); font-weight: 800;
  font-size: 34px; letter-spacing: .2em; line-height: 1; white-space: nowrap;
  background: linear-gradient(100deg, var(--hero-ac) 0%, #9fd4d6 55%, var(--hero-ac) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  opacity: .34; pointer-events: none; user-select: none;
  border-bottom: 3px solid color-mix(in srgb, var(--hero-ac) 30%, transparent);
  padding-bottom: 4px;
}
@media (max-width: 640px) { .tab-hero-ghost { display: none; } }

/* Column gridlines on EVERY table (Mark 2026-07-16: "table lines on ALL
   tables on oasis"). Soft alpha so they read as structure, not a cage;
   .pp-fixed's stronger spreadsheet lines win on specificity. */
table th, table td { border-right: 1px solid rgb(var(--ctp-surface1-rgb) / 0.45); }
table th:last-child, table td:last-child { border-right: none; }

/* Platform pages table — contained scroll. position:sticky cannot escape an
   overflow-x ancestor (it silently stops sticking and rows slid under the
   translucent lane bar — Mark's "can't see row #1", Jul 15). Giving the
   wrapper its own vertical scrollport makes the thead pin for real. */
/* Fallback for pre-JS paint — syncStickyChrome measures the real leftover
   viewport into a per-element --wide-table-h (same treatment as
   .wide-table-scroll; the static guess double-scrolled under tall chrome). */
.platform-table-scroll { max-height: var(--wide-table-h, calc(100vh - 140px)); }
.platform-table-scroll .thead-sticky { top: 0; }

/* Competitors SoV leaderboard — contained scroll (2026-07-16, Mark: NOTHING
   on the page moves, "the scroll only happens on the table"). app.js
   measures the wrapper's natural viewport offset into --wide-table-h so the
   table exactly fills the leftover screen — the page then has nothing to
   scroll, and hero/search/chips hold still. Fallback keeps it sane pre-JS. */
.wide-table-scroll { max-height: var(--wide-table-h, calc(100vh - var(--lane-tabbar-h) - var(--hero-h) - var(--section-sticky-h) - 40px)); }
.wide-table-scroll .thead-sticky { top: 0; }

/* Company/site comparisons stay dense; prose belongs on hover or in details.
   A contained scrollport prevents narrow screens from wrapping every cell. */
.co-compact-scroll {
  width: 100%; max-width: 100%; min-width: 0;
  overflow: auto;
  max-height: var(--wide-table-h, 70vh);
}
.co-compact-scroll > table.co-compact-table {
  display: table; width: max-content; min-width: 100%; max-width: none;
  table-layout: auto; border-collapse: collapse; overflow: visible;
}
.co-compact-table .thead-sticky { top: 0; }
.co-compact-table > thead > tr,
.co-compact-table > tbody > tr { height: 36px; }
.co-compact-table > thead > tr > th,
.co-compact-table > tbody > tr > td {
  box-sizing: border-box; height: 36px; padding: 0 10px;
  line-height: 18px; white-space: nowrap; vertical-align: middle;
  overflow: hidden; text-overflow: ellipsis; max-width: 360px;
}
.co-compact-table > thead > tr > th { font-size: 10px; }
.co-compact-table > tbody > tr > td { font-size: 12px; }
.co-compact-table > tbody > tr > td > * {
  max-height: 24px; white-space: nowrap; vertical-align: middle;
}
.co-compact-table td .block { display: inline !important; }
.co-compact-table td .flex-col { flex-direction: row !important; gap: 4px; align-items: center !important; }
.co-compact-table td .flex-wrap { flex-wrap: nowrap !important; }
.co-compact-table td .break-all { word-break: normal; }
.co-compact-table td .inline-flex { flex-shrink: 0; }
.co-compact-table td > .inline-flex { max-width: 340px; overflow: hidden; }
.co-compact-table .co-cell-detail { display: none !important; }

/* Wide-table scroll affordance (2026-07-20, Mark: wide tables "look cut
   off", zero hint that more columns exist). .pt-clip wraps each contained
   scroller; while the table can still travel right (.has-more-right, toggled
   by syncMoreRight in app.js) a right-edge fade + "more →" pill signal the
   hidden columns, and the scrollbar is always painted instead of macOS's
   hide-until-hover overlay. Desktop only — phones h-scroll naturally and an
   overlay would cover content. */
@media (min-width: 769px) {
  .pt-clip { position: relative; }
  .pt-clip::after { content: ''; position: absolute; top: 0; right: 0; bottom: 12px; width: 56px;
    pointer-events: none; opacity: 0; transition: opacity .15s; z-index: 21;
    background: linear-gradient(90deg, transparent, var(--ctp-surface0)); }
  .pt-clip.has-more-right::after { opacity: 1; }
  .pt-more-pill { display: none; position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
    z-index: 22; pointer-events: none; align-items: center; padding: 4px 10px; border-radius: 999px;
    font-size: 10.5px; font-weight: 700; color: var(--ctp-base); white-space: nowrap;
    background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-lavender));
    box-shadow: 0 2px 8px rgb(var(--ctp-lavender-rgb) / .4); }
  .pt-clip.has-more-right .pt-more-pill { display: inline-flex; }
  .wide-table-scroll::-webkit-scrollbar, .platform-table-scroll::-webkit-scrollbar { width: 12px; height: 12px; }
  .wide-table-scroll::-webkit-scrollbar-track, .platform-table-scroll::-webkit-scrollbar-track { background: var(--ctp-mantle); border-radius: 6px; }
  .wide-table-scroll::-webkit-scrollbar-thumb, .platform-table-scroll::-webkit-scrollbar-thumb { background: rgb(var(--ctp-lavender-rgb) / .5); border-radius: 6px; border: 2px solid var(--ctp-mantle); }
  .wide-table-scroll::-webkit-scrollbar-thumb:hover, .platform-table-scroll::-webkit-scrollbar-thumb:hover { background: var(--ctp-lavender); }
  .wide-table-scroll::-webkit-scrollbar-corner, .platform-table-scroll::-webkit-scrollbar-corner { background: var(--ctp-mantle); }
}
@media (max-width: 768px) { .pt-more-pill { display: none !important; } }
/* Explorer column header row — pins at the top of the contained box.
   Sticky lives on the CELLS (Chrome ignores position:sticky on <tr>);
   opaque composite bg so rows never ghost through. */
tr.ost-head-sticky > th { position: sticky; top: 0; z-index: 15;
  background: linear-gradient(rgb(var(--ctp-surface0-rgb) / 0.55), rgb(var(--ctp-surface0-rgb) / 0.55)), var(--ctp-base); }

/* Platforms pages table — spreadsheet mode. Fixed layout makes the header
   width the column width (so the resize grips actually reveal content);
   cells clip instead of stretching the row. */
.pp-fixed { table-layout: fixed; }
.pp-fixed td { overflow: hidden; text-overflow: ellipsis; }
.pp-th-resize { position: absolute; right: -4px; top: 0; height: 100%; width: 9px; cursor: col-resize; z-index: 5; }
.pp-th-resize:hover { background: rgb(var(--ctp-sapphire-rgb) / 0.35); }

/* Spreadsheet chrome: vertical gridlines + drag feedback. The insertion line
   paints the LEFT edge of the header you're hovering — that's where the
   dragged column lands; the dragged column itself dims. */
.pp-fixed th, .pp-fixed td { border-right: 1px solid rgb(var(--ctp-surface1-rgb) / 0.6); }
.pp-fixed th:last-child, .pp-fixed td:last-child { border-right: none; }
.pp-dragging { opacity: 0.35; }
th.pp-drop-target { box-shadow: inset 3px 0 0 var(--ctp-sapphire); background: rgb(var(--ctp-sapphire-rgb) / 0.08); }
div.pp-drop-target { box-shadow: inset 0 2px 0 var(--ctp-sapphire); }

/* Header info boxes — hover a column header for a styled explainer. Replaces
   the native title tooltips (which can't be styled). Right-aligned for the
   last few columns so the box never overflows the scrollport. */
.pp-th-tip { display: none; position: absolute; left: 0; top: 100%; margin-top: 6px; width: 250px; z-index: 40;
  background: var(--ctp-mantle); border: 1px solid rgb(var(--ctp-sapphire-rgb) / 0.35); border-radius: 8px;
  padding: 8px 10px; color: var(--ctp-subtext0); font-size: 11px; line-height: 1.5; font-weight: 400;
  text-transform: none; letter-spacing: normal; white-space: normal; text-align: left;
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.28); pointer-events: none; }
.pp-fixed thead th:hover .pp-th-tip { display: block; }
.pp-fixed thead th:nth-last-child(-n+5) .pp-th-tip { left: auto; right: 0; }
.pp-th-tip b { display: block; color: var(--ctp-text); margin-bottom: 3px; font-size: 12px; }
.pp-th-tip i { display: block; margin-top: 6px; color: var(--ctp-overlay1); font-style: normal; font-size: 10px; border-top: 1px solid rgb(var(--ctp-surface1-rgb) / 0.6); padding-top: 5px; }

/* Generic styled info tip — any control with class pp-tip-host + data-tip
   shows the same explainer box as the table headers on hover. pp-tip-right
   anchors it to the right edge (controls near the viewport edge). */
.pp-tip-host { position: relative; }
.pp-tip-host:hover::after { content: attr(data-oasistip); position: absolute; left: 0; top: 100%; margin-top: 6px; width: 240px; z-index: 60;
  background: var(--ctp-mantle); border: 1px solid rgb(var(--ctp-sapphire-rgb) / 0.35); border-radius: 8px;
  padding: 8px 10px; color: var(--ctp-subtext0); font-size: 11px; line-height: 1.5; font-weight: 400;
  letter-spacing: normal; text-transform: none; white-space: normal; text-align: left;
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.28); pointer-events: none; }
.pp-tip-host.pp-tip-right:hover::after { left: auto; right: 0; }

/* ══════════════════════════════════════════════════════════════════════
   Explorer "Quiet Grid" revamp (2026-07-18 — Mark's pick: option-1 calm
   + option-2 density). One accent, color only where it means something,
   real-table hairlines, actions revealed on the row you're pointing at.
   ══════════════════════════════════════════════════════════════════════ */

/* Priority badges — red is EARNED: solid on P0, tint on P1, quiet neutrals after. */
.v2-prio-0, .v2-prio-1, .v2-prio-rest { min-width: 44px; height: 20px; justify-content: center; }
.v2-prio-0 { background: var(--ctp-red); color: #fff; }
.v2-prio-1 { background: color-mix(in oklab, var(--ctp-red) 13%, transparent); color: var(--ctp-red); }
.v2-prio-rest { background: color-mix(in oklab, var(--ctp-overlay0) 12%, transparent); color: var(--ctp-overlay1); }

/* Two plan states, distinct colours (Mark, 2026-07-20):
   GREEN = written (already published on the framed site) — a soft green wash +
   green edge so "done" reads at a glance; AMBER = saved to the plan but not
   written yet — a warm edge, no wash. Green is reserved for written. */
.ost-row-written { background: color-mix(in oklab, var(--ctp-green) 10%, transparent) !important; }
.ost-row-written:hover { background: color-mix(in oklab, var(--ctp-green) 16%, transparent) !important; }
.ost-row-written > td:first-child { box-shadow: inset 2.5px 0 0 var(--ctp-green); }
.ost-row-planned,
.ost-row-planned:hover { background: transparent !important; }
.ost-row-planned:hover { background: color-mix(in oklab, var(--ctp-surface0) 40%, transparent) !important; }
.ost-row-planned > td:first-child { box-shadow: inset 2.5px 0 0 var(--ctp-peach); }

/* Column grid — clear vertical rules on headers + body so it reads as a
   spreadsheet, not stacked cards (Mark: "more table-y with lines"). */
.ost-head-sticky > th,
.ost-child > td { border-right: 1px solid color-mix(in oklab, var(--ctp-surface1) 70%, transparent); }
.ost-child > td:last-child,
.ost-head-sticky > th:last-child { border-right: none; }
/* Per-row rainbow accent stripe → whisper. */
.ost-child > td:first-child { box-shadow: inset 2px 0 0 color-mix(in srgb, var(--accent) 30%, transparent); }

/* Main column: icon + plain text (pill removed in the template). */
.v2-main-cell { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-size: 11.5px; font-weight: 600; color: var(--ctp-subtext0); }
.v2-main-both { color: var(--ctp-mauve); }

/* Topic tags inside the table: quiet text, hue kept, chrome dropped.
   (Sidebar niche chips keep the pill look — this is table-scoped.) */
.ost-child .tag-chip { min-width: 100px; justify-content: center; padding: 2.5px 8px; font-size: 10px; }
.ost-child .tag-chip.tag-hue-neutral { color: var(--ctp-overlay1); }

/* Translation column: reference info, not a competing voice. */
.v2-translation { color: var(--ctp-overlay1); font-size: 11.5px; font-style: italic; }

/* Modal stat band: labeled cells instead of a loose chip row.
   ONE row on desktop (Mark 2026-08-04): the band used to wrap into two rows
   (Priority/Geo/Main vol, then TP/Tags/Format/Intent) which read as two
   unrelated strips. nowrap + a tighter cell keeps every fact on one line;
   if a very long tag list still overruns, the band scrolls sideways instead
   of stacking. Phones keep the wrapping band (see the 768px block below). */
.v2-statband { border: 1px solid var(--ctp-surface1); border-radius: 8px;
  overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap;
  background: color-mix(in oklab, var(--ctp-mantle) 70%, transparent); width: max-content; max-width: 100%; }

/* Topic-detail toolbar — ONE line (2026-08-05, Mark: "this should be all in 1 line").
   The chip band and the action buttons share a single row: the band shrinks BELOW its
   content (flex-basis 0 beats its own width:max-content) and scrolls sideways instead of
   pushing the actions onto a second line — the same contract .oa-toolbar's filter group
   has. The scrollbar is hidden so the row keeps one line's height. Anything that can't
   read inline (the reader-intent paragraph, merged-in chips) takes its own full line. */
.v2-topicbar-chips { order: 1; flex: 1 1 0; min-width: 0; scrollbar-width: none; }
.v2-topicbar-chips::-webkit-scrollbar { display: none; }
.v2-topicbar-actions { order: 2; margin-left: auto; }
.v2-topicbar-below { order: 3; flex-basis: 100%; }
.v2-statband > .inline-flex { padding: 7px 9px; gap: 5px; border-right: 1px solid var(--ctp-surface0);
  align-self: stretch; flex: 0 0 auto; white-space: nowrap; }
.v2-statband > .inline-flex:last-child { border-right: none; }
/* Tags is the only elastic cell: a topic with five tags scrolls INSIDE its own
   cell instead of shoving Format and Intent off the end of the band. */
.v2-statband > .inline-flex.flex-wrap { flex: 0 1 auto; flex-wrap: nowrap; min-width: 96px;
  overflow-x: auto; overflow-y: hidden; }

/* Keyword-row actions: hidden until the row is hovered (or a popover is open),
   neutral ghost buttons — the red lives in the Remove ▾ menu where it's a choice. */
.group\/v2kw .kw-actions { opacity: 0; transition: opacity .12s ease; }
.group\/v2kw:hover .kw-actions,
.kw-actions:focus-within,
.kw-actions:has([class*="z-50"]) { opacity: 1; }
.kw-actions > button,
.kw-actions > span > button { background: transparent !important; border: 1px solid var(--ctp-surface1);
  color: var(--ctp-overlay1) !important; }
.kw-actions > button:hover,
.kw-actions > span > button:hover { color: var(--ctp-text) !important; border-color: var(--ctp-surface2);
  background: color-mix(in oklab, var(--ctp-surface0) 50%, transparent) !important; }

/* ── Peeko-inspired chrome (2026-07-18, Mark: "take styling/placement/pages ideas
   from Peeko"): hero stat trio, count-carrying filter pills, inline TP meters,
   plain-English page captions. Warmer hero wash à la Peeko's cream welcome card. ── */
/* Toned down 2026-08-04 (Mark: "make them smaller") — the trio is scope context,
   not the headline, so it sits on one tight line instead of shouting. */
.v2-hero-stats { display: flex; gap: 14px; margin-top: 5px; flex-wrap: wrap; align-items: baseline; }
.v2-hero-stat { display: inline-flex; align-items: baseline; gap: 4px; }
.v2-hero-stat b { font-size: 12.5px; font-weight: 750; line-height: 1.15;
  color: var(--ctp-text); font-variant-numeric: tabular-nums; }
.v2-hero-stat i { font-style: normal; font-size: 8px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ctp-overlay1); }
.v2-tp-meter { display: block; height: 2.5px; width: 54px; margin: 1px auto 0;
  border-radius: 2px; background: color-mix(in oklab, var(--ctp-surface1) 60%, transparent);
  overflow: hidden; }
.v2-tp-fill { display: block; height: 100%; border-radius: 2px;
  background: color-mix(in oklab, var(--ctp-lavender) 75%, var(--ctp-blue)); }

/* ── Priority filter boxes (2026-07-18, Mark's Higgsfield "Option 2" pick):
   separate rounded boxes with a small gap, universal HEAT scale — P0 hot red
   cooling to P5 gray, All = brand indigo. Solid fill + white text = selected;
   unselected stays a quiet outlined box so a multi-pick reads instantly. ── */
.v2-pf { display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; }
/* No counts inside these boxes, ever (Mark 2026-07-30: "remove the numbers to
   make the buttons smaller (always)") — the count lives in the hover tooltip,
   so min-width only has to hold "All". */
.v2-pf-btn { height: 30px; padding: 0 10px; border-radius: 8px; min-width: 38px; justify-content: center;
  border: 1px solid var(--ctp-surface1); background: color-mix(in oklab, var(--ctp-surface0) 40%, transparent);
  color: var(--ctp-overlay1); font-weight: 650; font-size: 12px;
  display: inline-flex; align-items: center; cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease, box-shadow .12s ease; }
.v2-pf-btn:hover { color: var(--ctp-text); border-color: var(--ctp-surface2); }
.v2-pf-btn.on { color: #fff; border-color: transparent;
  box-shadow: 0 1px 2.5px rgb(0 0 0 / .22); }
.v2-pf-btn.on:hover { color: #fff; filter: brightness(1.06); }
.v2-pf-all.on { background: #5B5BD6; }
.v2-pf-p0.on  { background: #D92D20; }
.v2-pf-p1.on  { background: #F04E23; }
.v2-pf-p2.on  { background: #F59E0B; }
.v2-pf-p3.on  { background: #12B76A; }
.v2-pf-p4.on  { background: #1E66F5; }
.v2-pf-p5.on  { background: #98A2B3; }

/* ── Excel-style columns (2026-07-18): resize handle on every header edge,
   drag-to-reorder highlight, and the per-user layout cursor states. ── */
/* GOAT cell (Mark 2026-08-04): "if it is goated it will be marked in colour,
   and without would mean it isnt goated". ONE glyph, two states — colour vs
   drained — so the column needs no legend and no second symbol. Greyscale
   rather than a lighter grey emoji, because the emoji's own colours can't be
   restyled with `color`. */
.v2-goat-glyph { width: 16px; height: 16px; display: block; object-fit: contain; }
.v2-goat-on .v2-goat-glyph { filter: none; opacity: 1; }
.v2-goat-off .v2-goat-glyph { filter: grayscale(1); opacity: .38; }
.v2-goat-off:hover .v2-goat-glyph { opacity: .7; }

/* Headers stay on ONE line (Mark 2026-08-04). A wrapped "MAIN KEYWORD VOLUME"
   made the whole header row two lines tall for one column's sake; the label
   now shortens itself instead (v2ColHeadFor in topics-v2.js). No overflow
   clipping here on purpose — it would cut off .v2-col-resizer, which lives
   4px outside the cell. */
.v2-col-th { position: relative; white-space: nowrap; }
.v2-col-resizer { position: absolute; top: 0; right: -4px; width: 9px; height: 100%;
  cursor: col-resize; z-index: 3; }
.v2-col-resizer::after { content: ""; position: absolute; top: 15%; bottom: 15%; left: 3.5px;
  width: 2px; border-radius: 1px; background: transparent; transition: background .12s ease; }
.v2-col-resizer:hover::after { background: var(--ctp-lavender); }
body.v2-col-resizing { cursor: col-resize !important; user-select: none; }
body.v2-col-resizing * { cursor: col-resize !important; }
.v2-col-drop { box-shadow: inset 2.5px 0 0 var(--ctp-lavender); }
/* Nicer star: the SVG breathes inside the same .tl-star hit target. */
.tl-star svg { display: block; }

/* Topic page (in-content, sidebar + header stay) — back bar pins under the lane tabs. */
.v2-topic-page { min-height: 0; }
.v2-topic-back { top: var(--lane-tabbar-h, 0px); }

/* Region flag as a soft BANNER BACKDROP (Mark: "always, so we know in which
   region we are") — fades in from the right, banner accent takes the geo hue. */
.v2-flag-backdrop { position: absolute; right: 0; top: 0; height: 100%; width: 44%;
  object-fit: cover; opacity: .30; pointer-events: none; user-select: none;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 45%);
  mask-image: linear-gradient(90deg, transparent, #000 45%); }

/* Topic page hero — the Explorer banner language per topic: geo-accent gradient,
   flag backdrop, eyebrow + display title. Children ride above the flag (same
   :not() exclusion lesson as .tab-hero). */
.v2-topic-hero { position: relative; overflow: hidden; border-radius: 12px;
  border: 1px solid var(--ctp-surface1); padding: 14px 16px; margin-bottom: 12px;
  background: linear-gradient(100deg,
    color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 15%, var(--ctp-surface0)) 0%,
    color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 4%, var(--ctp-surface0)) 55%,
    var(--ctp-surface0) 100%); }
.v2-topic-hero > :not(.v2-flag-backdrop) { position: relative; z-index: 1; }

/* Sidebar collapse/expand (2026-07-22, from Mark's luckylatvian recording):
   the toggle is now a first-class nav row at the TOP of the nav (replaces the
   bottom chevron card). One eased WIDTH transition — not transition-all — so
   only the fold animates and nothing else flickers during it. */
.oasis-sidebar.nav-anim { transition: width .28s cubic-bezier(.22,.61,.36,1); }
/* Quiet-until-touched nav (Mark 2026-07-22): the resting nav is NEUTRAL —
   icons desaturated, text grey. Hovering an item brings ITS color back
   (accent text + soft tint), and the active item KEEPS its color, so the one
   lit-up tab is always "you are here". Applies to the sidebar and the lane
   tab bar; the phone bottom bar keeps color (no hover on touch).
   Every nav item carries a permanent .nav-tab-<color> accent class (sidebar)
   or a --tab-accent inline var (lane tabs, from laneTabs[].accent). */
.nav-tab-blue { --tab-accent: var(--ctp-blue); }
.nav-tab-green { --tab-accent: var(--ctp-green); }
.nav-tab-teal { --tab-accent: var(--ctp-teal); }
.nav-tab-rosewater { --tab-accent: var(--ctp-rosewater); }
.nav-tab-sky { --tab-accent: var(--ctp-sky); }
.nav-tab-sapphire { --tab-accent: var(--ctp-sapphire); }
.nav-tab-lavender { --tab-accent: var(--ctp-lavender); }
.nav-tab-yellow { --tab-accent: var(--ctp-yellow); }
.nav-tab-peach { --tab-accent: var(--ctp-peach); }
.nav-tab-mauve { --tab-accent: var(--ctp-mauve); }
.nav-tab-red { --tab-accent: var(--ctp-red); }
.oasis-sidebar .nav-tab { transition: background-color .18s ease, color .18s ease; }
.oasis-sidebar .nav-tab .nav-ico { filter: grayscale(1) opacity(.72); transition: filter .18s ease; }
.oasis-sidebar .nav-tab:hover .nav-ico,
.oasis-sidebar .nav-tab.nav-tab-active .nav-ico { filter: none; }
.oasis-sidebar .nav-tab:hover:not(.nav-tab-active) {
  color: var(--tab-accent, var(--ctp-text));
  background: color-mix(in srgb, var(--tab-accent, var(--ctp-overlay1)) 10%, transparent); }
/* Active sidebar item = its accent (generalizes the per-color
   .nav-tab-active.nav-tab-X rules — rosewater never had one and silently
   fell back to blue). */
.oasis-sidebar .nav-tab.nav-tab-active { color: var(--tab-accent, var(--ctp-blue)) !important;
  background: color-mix(in srgb, var(--tab-accent, var(--ctp-blue)) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--tab-accent, var(--ctp-blue)) 20%, transparent); }
/* Lane tab bar — same behavior: grey at rest, accent on hover, the active
   tab wears its color as a bordered card. Transparent border at rest so
   activation never shifts layout. */
.lane-tab { border: 1px solid transparent;
  transition: color .18s ease, background-color .18s ease, border-color .18s ease; }
.lane-tab:hover:not(.lane-tab-active) {
  color: var(--tab-accent, var(--ctp-text));
  background: color-mix(in srgb, var(--tab-accent, var(--ctp-overlay1)) 10%, transparent); }
.lane-tab-active { color: var(--tab-accent, var(--ctp-text)); background: var(--ctp-base);
  border-color: color-mix(in srgb, var(--tab-accent, var(--ctp-surface1)) 40%, transparent);
  box-shadow: 0 1px 2px rgb(0 0 0 / .06); }

/* Fixed singleton info tip (replaces the clippable ::after pp-tips for v2-tip hosts). */
#oasis-tip { position: fixed; z-index: 9999; width: 260px; display: none;
  background: var(--ctp-mantle); border: 1px solid rgb(var(--ctp-sapphire-rgb) / 0.35);
  border-radius: 8px; padding: 8px 10px; color: var(--ctp-subtext0); font-size: 11px;
  line-height: 1.5; font-weight: 400; letter-spacing: normal; text-transform: none;
  white-space: normal; text-align: left; box-shadow: 0 10px 28px rgb(0 0 0 / .28); pointer-events: none; }
#oasis-tip.on { display: block; }
/* Collapsed rail: compact LABEL flyout (the recording's "Atvērt" pill) — same
   singleton, but auto-width + bolder text when showing a data-railtip label. */
#oasis-tip.rail { width: auto; max-width: 220px; padding: 5px 10px;
  font-size: 12px; font-weight: 600; color: var(--ctp-text); white-space: nowrap; }

/* Sidebar tracking card — quiet two-cell stat, replaces the loud blue/mauve numbers. */
.v2-side-stats { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  margin: 0 2px; border: 1px solid var(--ctp-surface1); border-radius: 10px; overflow: hidden;
  background: color-mix(in oklab, var(--ctp-mantle) 70%, transparent); text-decoration: none;
  transition: border-color .12s ease; }
.v2-side-stats:hover { border-color: var(--ctp-surface2); }
.v2-side-stat { padding: 7px 10px 6px; }
.v2-side-stat + .v2-side-stat { border-left: 1px solid var(--ctp-surface0); }
.v2-side-stat b { display: block; font-size: 14px; font-weight: 700; color: var(--ctp-text);
  font-variant-numeric: tabular-nums; line-height: 1.1; }
.v2-side-stat i { font-style: normal; font-size: 9px; font-weight: 650; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ctp-overlay1); }

/* Collapsed rail: the chevron card becomes a neat centered square. */

/* Priority badges follow the SAME heat scale as the filter pills (Mark Jul 19). */
.v2-prio-heat { min-width: 44px; height: 20px; justify-content: center; color: #fff; }
.v2-prio-p0 { background: #D92D20; }
.v2-prio-p1 { background: #F04E23; }
.v2-prio-p2 { background: #F59E0B; }
.v2-prio-p3 { background: #12B76A; }
.v2-prio-p4 { background: #1E66F5; }
.v2-prio-p5 { background: #98A2B3; }
.v2-prio-none { background: color-mix(in oklab, var(--ctp-overlay0) 12%, transparent); color: var(--ctp-overlay1); }

/* Intent chips: same-size boxes like everything else. */
.v2-intent-chip { display: inline-flex; justify-content: center; min-width: 96px; }

/* Daily movement chips on the sidebar tracking card. */
.v2-side-delta { display: block; font-style: normal; font-size: 9px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ctp-overlay0); margin-top: 1px; }
.v2-side-delta.up { color: var(--ctp-green); }
.v2-side-delta.down { color: var(--ctp-red); }
/* Clickable delta pill — opens the daily-changes drawer. */
.v2-side-delta-btn { cursor: pointer; border-radius: 4px; padding: 0 3px; margin-left: -3px;
  transition: background-color .12s; }
.v2-side-delta-btn:hover { background: var(--ctp-surface0); text-decoration: underline; }

/* Tracking card tracks the DRAGGED sidebar width (same container queries as
   the nav labels): narrow → cells stack; icon-rail → hidden (the numbers
   live in the Explorer hero anyway). */
@container (max-width: 169px) {
  .v2-side-stats { grid-template-columns: 1fr; }
  .v2-side-stat + .v2-side-stat { border-left: none; border-top: 1px solid var(--ctp-surface0); }
  .v2-side-stat { padding: 5px 8px 4px; text-align: center; }
  .v2-side-stat b { font-size: 12px; }
  .v2-side-stat i { font-size: 8px; letter-spacing: .06em; }
}
@container (max-width: 99px) {
  .v2-side-stats { display: none; }
}

/* Sidebar nav never scrolls sideways (rail widths were showing an x-scrollbar). */
.oasis-sidebar nav { overflow-x: hidden; }

/* Brand stays DEAD CENTER at every sidebar width (Mark: "centred and responsive
   at all times"): the tile and the lockup overlap in one centered grid cell, so
   the width-morph cross-fade can never push the visible mark sideways. */
.oasis-sidebar .brand-header > a { display: grid !important; place-items: center; width: 100%; }
.oasis-sidebar .brand-header > a > * { grid-area: 1 / 1; margin: 0 auto; }
.oasis-sidebar .brand-lockup { display: flex; justify-content: center; }

/* In-page TOC rail — the Explorer browse-panel silhouette for content pages. */
/* Matches the Explorer browse rail exactly (Mark: "like the rest where their
   sidebars are placed") — w-80 panel, surface0 card, same sticky metrics. */
.v2-toc { position: sticky; top: calc(var(--lane-tabbar-h, 0px) + 8px); width: 20rem;
  max-height: calc(100vh - var(--lane-tabbar-h, 0px) - 24px); overflow-y: auto; overscroll-behavior: contain;
  flex-shrink: 0; border: 1px solid var(--ctp-surface1); border-radius: 12px;
  background: var(--ctp-surface0); padding: 16px; }
.v2-toc-title { font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ctp-subtext0); padding: 2px 8px 8px; }
.v2-toc-item { display: flex; align-items: center; gap: 9px; padding: 8px 12px; border-radius: 8px;
  font-size: 13px; font-weight: 500; color: var(--ctp-text); text-decoration: none; cursor: pointer;
  transition: background .12s ease, color .12s ease; }
.v2-toc-item:hover { background: color-mix(in oklab, var(--ctp-surface1) 55%, transparent); color: var(--ctp-text); }
.v2-toc-item > .flex-1 { min-width: 0; }
.v2-toc-item:hover { background: color-mix(in oklab, var(--ctp-lavender) 10%, transparent); color: var(--ctp-text); }

/* Rail item active state (activity-log views etc.). */
.v2-toc-item-on { background: color-mix(in oklab, var(--ctp-mauve) 12%, transparent); color: var(--ctp-mauve); font-weight: 650; }

/* Intent chip doubles as a select for curators — chip look, no native chrome. */
.v2-intent-select { appearance: none; -webkit-appearance: none; border: none; cursor: pointer;
  text-align: center; font-weight: 600; }
.v2-intent-select:focus { outline: 1px solid var(--ctp-lavender); }

/* Rail watermark (Slack rail etc.) — faint brand mark bottom-right. */
.v2-rail-wm { position: absolute; right: -18px; bottom: -18px; width: 150px; height: 150px;
  opacity: .06; pointer-events: none; user-select: none; }

/* Center the Main cells within their column (inline-flex ignores td text-align widths). */
.ost-child .v2-main-cell { width: 100%; justify-content: center; }

/* ════════════════════════════════════════════════════════════════════
   Mobile usability pass (2026-07-19) — makes every page work on phones.
   Desktop untouched: everything below is ≤768px only (except the toggle
   default, hidden on desktop). Extends the Jul-8 mobile system (bottom
   tab bar + drawer rails) to the Quiet-Grid-era chrome: compact header,
   drawer .v2-toc rails, sideways-scrolling mode rows, viewport-sized
   contained tables, always-visible touch actions. */
.v2-rail-mtoggle { display: none; }
.m-brand { display: none; }
/* Desktop: the header is ONE row, always — a 6-tab lane (Competitors) must
   never fold its last tab under the brand (Insights wrapped, Mark
   2026-07-20). Squeeze order: the search pill shrinks to 180px first, then
   the tab strip scrolls sideways (scrollbar hidden, same as mobile). */
/* Squeeze order = the OTHERS shrink hard (factor 200, clamped by their own
   min-width), the tab strip shrinks LAST and then scrolls sideways.
   It must still have a REAL shrink factor: with `flex-shrink: 0.0001` the
   flexbox algorithm only absorbs 0.0001 of the deficit, so on the 8-tab
   Competitors lane the leftover width escaped the bar and the user chip fell
   off the right edge (Mark 2026-08-04: "the top bar doesn't show fully"). */
.lane-tabs-strip { flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none;
  /* the scrollbar is hidden, so a squeezed strip needs SOME hint that a tab
     continues past the edge: the last 8px fade out. 8px is the tab's own side
     padding at these widths, so when the row fits the fade lands on padding
     or empty space and is invisible; only a cut-off tab looks faded. */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 8px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 8px), transparent); }
.lane-tabs-strip::-webkit-scrollbar { display: none; }
.uni-wrap { flex: 0 200 288px; min-width: 160px; }
.uni-wrap .uni-search { width: 100%; }
/* The site pill's input is w-56 (224px) — far wider than any domain. Let the
   pill give back width too (domain truncates) so all 6 tabs stay visible
   down to a 1512 window with the sidebar expanded. */
.site-frame-wrap { flex: 0 200 auto; min-width: 190px; }
.site-frame-wrap > div:first-child { flex: 1 1 auto; min-width: 0; }
.site-frame-wrap input { width: 0; flex: 1 1 224px; min-width: 90px; text-overflow: ellipsis; }
/* Windows under ~1800: the nice-to-haves slim down so the WHOLE tab row still
   fits — the Competitors lane's tabs are ~890px on their own, which already
   overflowed a 1728 MacBook window (Mark 2026-08-04). Stats pill hides, the
   user chip drops to avatar-only, the ⌘K hint goes, search + site pill give
   back a little more, tabs sit tighter.
   Nothing here removes a control — every one of them stays clickable. */
@media (min-width: 769px) and (max-width: 1799px) {
  .hdr-stats { display: none; }
  /* Hide the NAME BLOCK only, never the circle. This used to read
     `.hdr-user a` back when the avatar was a <button>; the moment the avatar
     became a profile link (2026-08-04) that selector swallowed it too and the
     whole chip vanished on every window under 1800px. */
  .hdr-user-name { display: none; }
  .uni-kbd { display: none; }
  .uni-wrap { flex: 0 200 260px; min-width: 130px; }
  .site-frame-wrap { min-width: 150px; }
  /* 8px sides + a 3px gap instead of 12px + 4px: ~39px back across an 8-tab
     lane, which is the difference between the last tab fitting and not. */
  .lane-tab { padding-left: 8px; padding-right: 8px; }
  .lane-tabs-strip { gap: 3px; }
}

@media (max-width: 768px) {
  /* ── Header: PINNED TWO-ROW top nav bar (round 5, 2026-07-26 — Mark: the
     one-line version was "cramped / unusable"). Round 4 crammed brand + tabs +
     search + site pill + New + bell + help + apps into ONE sideways-scrolling
     strip: on a 390px phone that's ~500px of chrome, so half of it lived
     off-screen behind a scroll nobody discovers.
     Now: row 1 = brand + lane tabs (still sidescrolls — a lane can have 6),
     row 2 = search + site pill + the action icons, all visible at once.
     Nothing is hidden and nothing needs a horizontal swipe to be found.
     The Jul-8 block set html,body overflow-x:HIDDEN on phones — ANY
     overflow-x on body computes its overflow-y to auto, so body becomes a
     scroll container and silently kills every position:sticky descendant
     (the real scrolling happens on the viewport). html's clip already stops
     sideways scroll; body must stay fully visible. ── */
  html { overflow-x: clip; }
  body { overflow: visible !important; max-width: none; }
  .lane-tabbar-sticky { position: sticky; top: 0; z-index: 45;
    padding: 6px 8px 7px; gap: 5px; min-height: 0;
    /* wrap = the two rows; the bar itself never scrolls sideways any more */
    flex-wrap: wrap !important; overflow: visible;
    box-shadow: 0 8px 12px -12px rgba(0, 0, 0, 0.35); }
  .lane-tabbar-sticky::after { display: none; } /* the 24px apron is desktop spacing */
  .uni-kbd, .site-info-btn, .hdr-stats, .hdr-user { display: none !important; }
  /* Row 1 = the lane tabs, on their own full-width line, scrolling sideways
     when a lane has more tabs than fit. Pages with NO lane tabs (Home, Brand,
     Feedback…) must not burn a whole row on the lone brand tile, so the strip
     only claims a row when it actually holds tabs — otherwise the brand joins
     the controls row. */
  .lane-tabs-strip { flex: 0 0 auto; flex-wrap: nowrap !important;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain; scrollbar-width: none; }
  .lane-tabs-strip:has(.lane-tab) { flex: 0 0 100%; }
  .lane-tabs-strip::-webkit-scrollbar { display: none; }
  .m-brand { display: inline-flex; align-items: center; flex-shrink: 0; margin-right: 4px; }
  .m-brand img { width: 28px; height: 28px; border-radius: 9px; object-fit: cover;
    box-shadow: var(--glow-blue); }
  /* Row 2: search + site pill share the leftover width. Every width here is
     sized so the whole row — search + site + New + bell + ? + apps — lands
     inside 360px; one pixel over and it wraps to a third row. */
  /* The search box gives width away FIRST — tapping it opens a full-width panel,
     so the magnifier alone is enough of a target and its placeholder is a
     nice-to-have. The site pill takes what's left, because WHICH site is in
     focus has to stay readable (at 84px it truncated to "l."). */
  /* Both need a SMALL flex-basis, not `auto`: on a wrapping flex line the
     browser wraps before it shrinks, so a 288px base search box throws the
     icons onto a third row instead of giving width back. Bases are set so the
     whole row (54 + 96 + 4 icons + ✕ + gaps + padding) fits inside 360px, then
     the leftover space goes 1:2 in the site pill's favour. */
  .uni-wrap { flex: 0 1 54px; min-width: 54px; margin-left: 0 !important; }
  .uni-wrap .uni-search { width: 100%; }
  .site-frame-wrap { flex: 2 1 96px; min-width: 96px; }
  .site-frame-wrap > div:first-child { flex: 1 1 auto; min-width: 0; }
  .site-frame-wrap > button:first-of-type { flex: 1 1 auto; max-width: none; }
  /* Inside the pill: the domain takes the slack and the chevron stops hoarding
     it. The chevron's `ml-auto` was eating every spare pixel, so a 137px pill
     still showed "l…". */
  .site-frame-wrap > button > span.truncate { flex: 1 1 auto; }
  .site-frame-wrap > button > .ml-auto { margin-left: 2px; }
  /* Tighter internals buy the domain another ~14px — the difference between
     "lo…" and something you can actually read. */
  .site-frame-wrap > button:first-of-type { gap: 4px; padding-left: 8px; padding-right: 6px; }
  .site-frame-wrap input { width: 0; flex: 1 1 60px; min-width: 0; }
  .site-frame-label { display: none; }
  .create-wrap, .help-wrap, .app-grid-btn { flex: 0 0 auto; }
  /* Anchored dropdowns are positioned off the MEASURED bar height (two rows
     now), pinned to the viewport so the wrapping header can't clip them. */
  .uni-wrap > .absolute, .site-frame-wrap > .absolute {
    position: fixed; left: 10px; right: 10px; width: auto; max-width: none;
    top: calc(var(--lane-tabbar-h, 52px) + 6px); max-height: 60vh; }
  /* iOS zooms the page when a focused input is under 16px — keep header and
     toolbar text inputs at 16px on phones so focus never zooms. */
  .lane-tabbar-sticky input, .main-content input[type="text"], .main-content input:not([type]) { font-size: 16px; }

  /* ── Mode / toggle rows: sideways scroll strips, never letter-wrap.
     .m-hscroll is the reusable opt-in for any tab/segment strip. ── */
  .mode-sticky, .m-hscroll { flex-wrap: nowrap !important; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .mode-sticky::-webkit-scrollbar, .m-hscroll::-webkit-scrollbar { display: none; }
  .mode-sticky > *, .m-hscroll > * { flex-shrink: 0; }
  .mode-sticky button, .m-hscroll button, .m-hscroll label { white-space: nowrap; }

  /* ── Rails (.v2-toc) = slide-in drawers (the Jul-8 filter-rail pattern) ── */
  .v2-rail-mtoggle { display: flex; flex-basis: 100%; }
  div:has(> aside.v2-toc) { flex-wrap: wrap; }
  aside.v2-toc { position: fixed; top: 0; bottom: 0; left: -110%; width: min(320px, 86vw);
    max-height: none; z-index: 75; border-radius: 0; border-width: 0 1px 0 0;
    background: var(--ctp-base); transition: left 0.25s var(--ease-out-expo);
    padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
  aside.v2-toc.mobile-open { left: 0; box-shadow: 12px 0 40px rgba(0, 0, 0, 0.25); }

  /* ── Contained-scroll tables: size to the phone viewport (the measured
     desktop offsets assume pinned chrome, which is static here) ── */
  .wide-table-scroll { max-height: calc(100vh - 150px); max-height: calc(100dvh - 150px); }
  .platform-table-scroll { max-height: calc(100vh - 150px); max-height: calc(100dvh - 150px); }
  /* Tables living inside a scroll wrapper keep real table layout — the
     generic .main-content table{display:block} fallback is for BARE tables. */
  .main-content .wide-table-scroll table,
  .main-content .platform-table-scroll table,
  .main-content .overflow-x-auto table,
  .main-content .overflow-auto table { display: table; }

  /* ── Bottom nav (round 5): the frosted bar itself. Cells, sizing and the
     no-sidescroll rule live with .mobile-tabbar-item in the earlier phone
     block; the ☰ Menu cell opens `aside.oasis-sidebar` as the drawer, so the
     old hand-copied .mobile-more-sheet is gone. ── */
  .mobile-tabbar { background: color-mix(in srgb, #fff 82%, var(--ctp-base));
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }

  /* ── Touch: keyword-row actions have no hover — always visible ── */
  .group\/v2kw .kw-actions { opacity: 1; }
  /* Comfortable touch targets on the mode strip */
  .mode-sticky button { min-height: 34px; }

  /* ── Segmented controls (.m-seg opt-in): own full-width centered row,
     bigger equal-width buttons, labels never wrap (Mark: Profile "My
     activity" toggle was tiny and wrapping — "a bit bigger and centered") ── */
  .m-seg { flex: 1 0 100%; display: flex; justify-content: center; }
  .m-seg > button { flex: 1 1 0; display: inline-flex; align-items: center;
    justify-content: center; height: 34px; font-size: 12px;
    padding: 0 10px; white-space: nowrap; }

  /* ── Topic page: back bar pins to the very top (header is static here) ── */
  .v2-topic-back { top: 0; }

  /* ── Heroes: tighter on small screens ── */
  .tab-hero { padding: 11px 12px; }
  .v2-flag-strip { right: 8px; gap: 6px; opacity: 0.2; }
  .v2-flag-strip img { height: 24px; }
}

/* NEVER page-level horizontal scroll (Mark 2026-07-19, mobile-first hard rule).
   overflow-x: clip kills the side scroll WITHOUT creating a scroll container,
   so position: sticky and inner overflow-x-auto tables keep working. Anything
   wider than the viewport must scroll inside its own wrapper, never the page. */
html, body { overflow-x: clip; max-width: 100vw; }
#app { overflow-x: clip; }

/* ── Hover-revealed controls on touch ──────────────────────────────────
   `opacity: 0` until :hover is a fine desktop affordance and a dead end on a
   phone — there is no hover, so the control is simply invisible and its
   function unreachable. Two of these already had the patch (.fb-move,
   .ik-acts); these are the rest. Any NEW hover-revealed control belongs in
   this block too. ── */
@media (hover: none) {
  .group\/v2kw .kw-actions { opacity: 1; }
  .scp-det-edit-pen { opacity: 1; }
  /* The move button only exists for touch — on desktop the card is dragged. */
  .scp-card-move { display: flex; }
  /* Keep the foot's own content clear of the trigger's corner. */
  .scp-card-foot { padding-right: 34px; }
}

/* ── House confirm dialog (OasisConfirm.js) ────────────────────────────
   Replaces native confirm(), which shows browser chrome plus the raw hostname
   and reads as a phishing box. Buttons are 44px so the same markup works on a
   phone without a second mobile variant. ── */
.oc-backdrop {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(0, 0, 0, .45);
}
.oc-box {
  width: 100%; max-width: 420px; padding: 18px 18px 14px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: 14px; box-shadow: 0 24px 60px -16px rgba(0, 0, 0, .7);
}
.oc-title { font-size: 14px; font-weight: 800; color: var(--ctp-text); margin-bottom: 6px; }
.oc-msg { font-size: 13.5px; line-height: 1.5; color: var(--ctp-subtext1); white-space: pre-wrap; }
/* 16px: anything smaller force-zooms the page on iOS when focused. */
.oc-input {
  width: 100%; margin-top: 12px; min-height: 44px; padding: 0 12px;
  font-size: 16px; color: var(--ctp-text);
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 9px;
}
.oc-input:focus { outline: none; border-color: var(--ctp-blue); }
.oc-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.oc-btn {
  min-height: 44px; padding: 0 16px; border-radius: 9px;
  font-size: 13px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--ctp-surface1); background: transparent; color: var(--ctp-text);
}
.oc-cancel { color: var(--ctp-overlay1); }
.oc-cancel:hover { background: var(--ctp-surface0); }
.oc-ok { background: var(--ctp-blue); border-color: var(--ctp-blue); color: var(--ctp-base); }
.oc-danger { background: var(--ctp-red); border-color: var(--ctp-red); color: var(--ctp-base); }
.oc-ok:hover, .oc-danger:hover { filter: brightness(1.08); }

/* ── Move-without-dragging (touch) ─────────────────────────────────────
   Hidden on pointer devices: dragging is the desktop affordance and a second
   control would only clutter the card. On touch it is the ONLY way to move a
   card, since HTML5 drag events never fire from a finger.

   Two constraints shaped this: `.scp-card` is `overflow: hidden`, so a menu
   positioned inside the card is clipped invisible — hence a FIXED sheet, not
   a dropdown. And the top-right corner already holds the geo flag
   (.scp-card-geo, top:10px right:11px), so the trigger sits bottom-right with
   the card foot given clearance on touch. ── */
.scp-card-move {
  display: none;
  position: absolute; bottom: 2px; right: 2px; z-index: 2;
  width: 34px; height: 34px; align-items: center; justify-content: center;
  border: 0; background: transparent; cursor: pointer;
  color: var(--ctp-overlay1); font-size: 17px; line-height: 1; border-radius: 6px;
}
.scp-card-move:active { background: var(--ctp-surface1); }

/* Tap-outside-to-close. Fixed like the sheet so it covers the viewport, not
   the clipped card. */
.scp-move-backdrop {
  position: fixed; inset: 0; z-index: 89;
  background: rgba(0, 0, 0, .35);
}

/* Centred sheet — fixed, so no ancestor's overflow can clip it. */
.scp-move-menu {
  position: fixed; z-index: 90;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  min-width: 220px; max-width: calc(100vw - 48px); padding: 6px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: 12px; box-shadow: 0 18px 50px -12px rgba(0, 0, 0, .65);
  display: flex; flex-direction: column; gap: 2px;
}
.scp-move-menu-head {
  padding: 6px 10px 4px; font-size: 9.5px; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ctp-overlay0);
}
/* 44px: a move is a real edit, and a mis-tap costs one. */
.scp-move-opt {
  min-height: 44px; padding: 0 12px; text-align: left;
  font-size: 14px; font-weight: 600; color: var(--ctp-text);
  background: transparent; border: 0; border-radius: 8px; cursor: pointer;
}
.scp-move-opt:active { background: var(--ctp-surface0); }
.scp-move-opt:disabled { color: var(--ctp-overlay0); cursor: not-allowed; }
.scp-move-cancel { color: var(--ctp-overlay1); font-weight: 500; border-top: 1px solid var(--ctp-surface0); border-radius: 0 0 8px 8px; }

/* ── iOS zoom trap ─────────────────────────────────────────────────────
   Safari on iPhone force-zooms the whole page when a text field smaller
   than 16px receives focus, and never zooms back out. On a bottom-pinned
   input (the priority-bot ask box) that lands the keyboard on top of the
   very field being typed into. 14 template inputs and 4 CSS rules were
   under 16px, so this is set once for every text-entry field rather than
   patched per control. Buttons, labels and selects-as-popovers are not
   affected — only fields that take a caret. Phone widths only: desktop has
   no zoom behaviour and keeps its denser type. */
@media (max-width: 768px) {
  input[type="text"], input[type="search"], input[type="number"],
  input[type="email"], input[type="url"], input[type="password"],
  input[type="tel"], input[type="date"], input:not([type]), textarea {
    font-size: 16px;
  }
}

/* ── Rails, desktop (round 5, Mark): the panel itself NEVER scrolls — the
   chrome (Geos|Tags|Events / Markets|Niches toggle, search, selects) stays
   put and only the list scrolls ("this sidebar isn't needed... only the
   inner one", "this should be sticky and not moving"). The active view is
   always the runtime last child: every rail panel ends in its v-if/v-else
   view chain, and exactly one branch renders. ── */
@media (min-width: 769px) {
  .rail-sticky-panel { display: flex; flex-direction: column; overflow: hidden; }
  .rail-sticky-panel > * { flex: 0 0 auto; }
  .rail-sticky-panel > :last-child { flex: 1 1 auto; min-height: 0; }
  /* Views with their own scrolling list (Explorer geos/tags/events): the
     label + sport/sort selects stay fixed, the list takes leftover height. */
  .rail-sticky-panel > :last-child:has(.overflow-y-auto) { display: flex; flex-direction: column; overflow: hidden; }
  .rail-sticky-panel > :last-child > .overflow-y-auto { flex: 1 1 auto; min-height: 0; max-height: none !important; }
  /* Views without one (Competitors markets/niches, Sites/Platforms rails):
     the view itself becomes the one scroller. */
  .rail-sticky-panel > :last-child:not(:has(.overflow-y-auto)) { overflow-y: auto; overscroll-behavior: contain; }
}

/* Table headers are always centered app-wide (Mark, 2026-07-20) — the HEADINGS
   only; cell contents keep their own alignment. Covers both real <thead> tables
   and the Topics Explorer header row (a <tr> of .v2-col-th inside <tbody>).
   !important beats the inline text-left/right Tailwind utilities on the th. */
thead th,
.v2-col-th {
  text-align: center !important;
}

/* ── NUMBERS ARE ALWAYS CENTRED IN TABLES — every table, app-wide ──────────
   Mark 2026-07-30: "numbers should always be centred on tables. all of them."
   Companion to the headers rule above, and the same !important for the same
   reason: what it overrides is an inline `text-right` Tailwind utility on the
   cell, of which there are ~120.

   `tabular-nums` is the hook because it already marks every column of figures in
   this app (that's what it's for — fixed-width digits so a column doesn't
   shimmer). Two shapes are covered: the class on the cell itself, and the common
   `<td><span class="tabular-nums">` — matched with :has(> …), a DIRECT child
   only, so a cell that merely contains a number somewhere inside a card or a
   badge keeps its own alignment. If a number needs to stay right-aligned, put
   `.num-right` on the cell. */
table td.tabular-nums,
table th.tabular-nums,
table td:has(> .tabular-nums),
/* Flex-based "tables" have no <td> — their numeric columns are named. */
.kwc-volume, .kwc-kd, .kwc-cpc {
  text-align: center !important;
}
/* The inner span can carry its own text-right and, when it's inline-block with a
   width, that wins over the cell's alignment. */
table td:has(> .tabular-nums) > .tabular-nums { text-align: center !important; }
.num-right, .num-right > .tabular-nums { text-align: right !important; }

/* Hero watermark bleeds ~12px past the phone viewport (the absolute .tab-hero-wm
   svg) — page can't sidescroll (html clip) but the overflow still registers in
   scrollWidth. Contain it at the hero's own edge on phones. */
@media (max-width: 768px) {
  .tab-hero { overflow: clip; }
}

/* ── Content Plan BOARD (Kanban) — default view since 2026-07-20 ──────────
   Columns = the table's derived status buckets; cards = topics. Live cards
   carry the site-wide green written/live idiom (left accent + soft wash).
   All layout inline-flex/inline styles — no new Tailwind utilities needed. */
.scp-board { display: flex; align-items: flex-start; gap: 10px; padding: 10px 2px;
  overflow-x: auto; overflow-y: hidden; }
/* Jira-flat skin (Mark's pick over the tinted R2, 2026-07-20 collision
   tie-break): quiet flat mantle wells, no tint wash. The col keeps a
   TRANSPARENT border by default — the smart-drag affordances (.scp-col-can
   dashed invitation) paint on it while a card is airborne. */
.scp-board-col { flex: 1 1 0; min-width: 252px; max-width: 300px; display: flex;
  flex-direction: column; max-height: 100%; border: 1px solid transparent;
  border-radius: 8px; background: var(--ctp-mantle); }
.scp-board-col-head { display: flex; align-items: center; gap: 7px; padding: 10px 12px 6px;
  flex-shrink: 0; flex-wrap: nowrap; min-height: 32px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ctp-subtext0); }
/* One-line title, always — a wrapping label ("Needs update") made that column's
   head taller than its siblings (Mark, 2026-07-21: same height pls). */
.scp-board-col-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; flex: 0 1 auto; }
.scp-board-col-dot { width: 8px; height: 8px; border-radius: 99px; background: var(--col-ac);
  flex-shrink: 0; }
.scp-board-col-count { margin-left: auto; font-family: var(--font-data);
  font-variant-numeric: tabular-nums; font-size: 10px; font-weight: 600;
  color: var(--ctp-overlay1); background: var(--ctp-surface0); border-radius: 99px;
  padding: 1px 7px; }
.scp-board-cards { flex: 1 1 auto; overflow-y: auto; padding: 8px; display: flex;
  flex-direction: column; gap: 6px; min-height: 60px; }
/* Jira-clean card (2026-07-20, Mark: "cleaner blocks — see how they are on
   Jira / proper SaaS"): title leads, one quiet meta row of chips under it,
   hairline-free foot. No left accent bar and no divider — the column already
   carries the status; the card just states the work. */
.scp-card { border: 1px solid var(--ctp-surface0); border-radius: 6px;
  position: relative; /* anchors the top-right geo flag */
  height: 148px; padding: 13px 12px 11px; background: var(--ctp-base); cursor: pointer;
  /* flex-shrink 0 or the column (a flex container) SQUASHES the fixed-height
     cards when it holds many — lanes showed different card sizes per column. */
  flex-shrink: 0;
  display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box;
  transition: background .12s, border-color .12s, box-shadow .12s;
  box-shadow: 0 1px 1px rgb(0 0 0 / .05); }
.scp-card:hover { border-color: var(--ctp-surface2);
  box-shadow: 0 3px 8px -3px rgb(0 0 0 / .12);
  box-shadow: 0 2px 6px rgb(0 0 0 / .09), inset 2.5px 0 0 var(--col-ac); }
.scp-card-live { background: color-mix(in oklab, var(--ctp-green) 5%, var(--ctp-base)); }
.scp-card-readonly { cursor: default; opacity: .85; }
.scp-card-top { display: flex; align-items: center; gap: 6px; margin-top: 7px;
  font-size: 10px; font-weight: 600; color: var(--ctp-overlay1); min-width: 0; }
/* Geo flag pinned to the card's top-right corner (Mark 2026-07-21: flag only,
   no code text). Region buckets without a flag image get the tiny code chip. */
.scp-card-geo { position: absolute; top: 10px; right: 11px; }
.scp-card-geo-txt { font-size: 8.5px; font-weight: 700; letter-spacing: .03em;
  color: var(--ctp-overlay1); background: var(--ctp-surface0); border-radius: 3px;
  padding: 1.5px 4px; line-height: 1; }
/* Market SUGGESTION — dashed + '?' so nobody reads it as a set value. */
.scp-card-geo-sugg { background: transparent; color: var(--ctp-peach);
  border: 1px dashed rgb(var(--ctp-peach-rgb) / .55); }
.scp-det-tag-sugg { color: var(--ctp-peach); background: transparent;
  border: 1px dashed rgb(var(--ctp-peach-rgb) / .55); }
.scp-card-status { margin-left: auto; flex-shrink: 1; max-width: 50%; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; font-weight: 600; font-size: 9.5px;
  background: var(--ctp-surface0); border-radius: 4px; padding: 1.5px 6px; }
/* Which site the card belongs to — favicon + domain, on top of the title on
   EVERY card. Ellipsizes so long domains never wrap the fixed-height card. */
.scp-card-site { display: flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 650; letter-spacing: .01em;
  color: var(--ctp-subtext0); white-space: nowrap;
  overflow: hidden; min-width: 0; flex-shrink: 0;
  margin-bottom: 3px; padding-right: 28px; /* clear of the corner flag */ }
.scp-card-site img { width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0; }
.scp-card-site span { overflow: hidden; text-overflow: ellipsis; }
/* Compact cards are only 64px tall — no room for the line; the domain stays
   available on the card's hover tooltip. */
.scp-board-compact .scp-card-site { display: none; }
.scp-card-label { font-size: 13px; font-weight: 650; line-height: 1.4; color: var(--ctp-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-width: 0; flex: 0 0 auto; max-height: 2.8em;
  padding-right: 28px; /* keep the title clear of the corner flag */ }
/* Keyword target under the title (Mark 2026-07-21: every card shows the
   page's topic AND its target keyword — title dropped to 2 lines for room). */
.scp-card-kw { font-size: 10.5px; color: var(--ctp-overlay1); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; flex-shrink: 0; padding-right: 28px; }
.scp-card-foot { display: flex; align-items: center; gap: 7px; margin-top: auto; padding-top: 8px;
  font-size: 10.5px; color: var(--ctp-overlay1); min-width: 0; }
.scp-card-vol { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; min-width: 0; }
/* Redirect destination on Redirected-lane cards — ellipsizes, never wraps. */
.scp-card-redir { font-weight: 600; color: var(--ctp-peach); min-width: 0;
  flex-shrink: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scp-card-vol b { font-weight: 700; color: var(--ctp-text); font-variant-numeric: tabular-nums; }
/* Column-header total volume pill (R2 "smarter headers"). */
.scp-board-col-vol { font-family: var(--font-data); font-variant-numeric: tabular-nums;
  font-size: 9px; font-weight: 700; border-radius: 99px; padding: 1.5px 7px; flex-shrink: 0;
  background: rgb(var(--ctp-blue-rgb) / .10); color: var(--ctp-blue); }
/* Top-priority lane (R2): P0/P1 cards sit above a dashed cut with a soft red ring. */
.scp-lane-tag { font-size: 8.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ctp-red); padding: 0 2px; }
.scp-lane-cut { border-top: 1px dashed rgb(var(--ctp-red-rgb) / .35); margin: 1px 0; flex-shrink: 0; }
.scp-card-hot { box-shadow: 0 1px 1px rgb(0 0 0 / .05),
  0 0 0 1px rgb(var(--ctp-red-rgb) / .28); }
/* Multi-select: lavender ring + wash; the floating bulk-actions bar. */
.scp-card-sel { border-color: var(--ctp-lavender);
  box-shadow: 0 1px 1px rgb(0 0 0 / .05), 0 0 0 1.5px var(--ctp-lavender);
  background: color-mix(in oklab, var(--ctp-lavender) 6%, var(--ctp-base)); }
.scp-bulkbar { position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px;
  z-index: 46; display: flex; align-items: center; gap: 10px; padding: 9px 14px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1); border-radius: 10px;
  box-shadow: 0 12px 32px -12px rgb(0 0 0 / .38); }
.scp-bulkbar-n { font-size: 11.5px; font-weight: 700; color: var(--ctp-text);
  font-variant-numeric: tabular-nums; }
.scp-bulkbar-hint { font-size: 10px; color: var(--ctp-overlay0); }
/* Compact density (R2, ?density=compact): one-line labels, slim chrome, no vol word. */
.scp-board-compact .scp-board-cards { gap: 5px; }
.scp-board-compact .scp-card { height: 64px; padding: 5px 10px 6px; border-radius: 4px; }
.scp-board-compact .scp-card-top { font-size: 9.5px; }
.scp-board-compact .scp-card-label { -webkit-line-clamp: 1; font-size: 11.5px; flex-basis: 1.35em; }
.scp-board-compact .scp-card-kw { display: none; }
.scp-board-compact .scp-card-top { margin-top: 4px; }
.scp-board-compact .scp-card-foot { margin-top: auto; padding-top: 4px; font-size: 10px; }
.scp-board-compact .scp-vol-word { display: none; }
.scp-board-compact .scp-card-pri { width: 17px; height: 17px; font-size: 8.5px; border-radius: 4px; }
.scp-board-compact .scp-card-geo { top: 6px; right: 8px; }
/* Priority = the Explorer filter's solid HEAT blocks (Mark 2026-07-21: same
   colors + font as the P0–P5 filter boxes), shrunk to a square, centered,
   no counts. Same hexes as .v2-pf-p0…p5 — keep the two scales in sync. */
.scp-card-pri { flex-shrink: 0; font-weight: 650; font-size: 9.5px; color: #fff;
  width: 21px; height: 21px; border-radius: 5px; padding: 0; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 2px rgb(0 0 0 / .18); }
.pri-p0 { background: #D92D20; }
.pri-p1 { background: #F04E23; }
.pri-p2 { background: #F59E0B; }
.pri-p3 { background: #12B76A; }
.pri-p4 { background: #1E66F5; }
.pri-p5 { background: #98A2B3; }
.scp-card-badge { max-width: 110px; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; border: 1px solid var(--ctp-surface0); border-radius: 4px;
  padding: 0 4px; line-height: 14px; color: var(--ctp-overlay1); flex-shrink: 1; }
.scp-card-link { flex-shrink: 0; font-size: 11px; color: var(--ctp-blue); font-weight: 700;
  padding: 0 2px; border-radius: 3px; }
.scp-card-link:hover { background: var(--ctp-surface0); }
.scp-board-empty { text-align: center; font-size: 10px; color: var(--ctp-overlay0);
  font-style: italic; padding: 18px 0 22px; }
.scp-board-empty img { display: block; width: 72px; height: 72px; object-fit: contain;
  margin: 0 auto 6px; opacity: .9; }
/* Smart-drag affordances: while a card is airborne, columns it CAN land in get
   a dashed accent invitation (their own accent color), invalid ones dim; the
   hovered valid target lights up. The dragged card ghosts in place. */
.scp-card { cursor: grab; }
.scp-card:active { cursor: grabbing; }
.scp-card-dragging { opacity: 0.35; }
.scp-card-frozen { pointer-events: none; opacity: 0.6; }
.scp-col-can { border-style: dashed; border-color: color-mix(in oklab, var(--col-ac) 55%, transparent); }
.scp-col-no { opacity: 0.45; }
.scp-col-over { border-style: solid; border-color: var(--col-ac);
  box-shadow: inset 0 0 0 1px var(--col-ac), 0 0 14px -6px var(--col-ac);
  background: color-mix(in oklab, var(--col-ac) 7%, var(--ctp-mantle)); }
/* Card detail drawer — Jira-style issue panel. Shell = .scp-drag-panel
   (right rail desktop / bottom sheet phones, defined inline in index.html). */
.scp-det-head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center;
  gap: 8px; padding: 12px 16px; min-height: 52px; background: var(--ctp-base);
  border-bottom: 1px solid var(--ctp-surface0); flex-shrink: 0; }
.scp-det-state { font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; border-radius: 4px; padding: 3px 8px; }
.scp-det-x { width: 30px; height: 30px; border-radius: 6px; color: var(--ctp-overlay1);
  font-size: 14px; line-height: 1; flex-shrink: 0; }
.scp-det-x:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
/* De-clutter pass (Mark 2026-07-21): tinted canvas + SOLID card blocks, so
   each section reads as its own separate block without extra chrome. */
.scp-det-body { flex: 1 1 auto; overflow-y: auto; padding: 20px 20px 28px;
  background: color-mix(in oklab, var(--ctp-mantle) 70%, var(--ctp-base)); }
.scp-det-title { display: flex; align-items: flex-start; gap: 10px; font-size: 20px;
  font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; color: var(--ctp-text); }
.scp-det-title .geo-flag { margin-top: 6px; }
/* Keyword target line under the drawer title (pairs with .scp-card-kw). */
.scp-det-kw { font-size: 12px; color: var(--ctp-subtext0); margin-top: 4px; }
.scp-det-kw b { font-weight: 650; color: var(--ctp-text); }
.scp-det-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.scp-det-btn { height: 32px; padding: 0 14px; border-radius: 6px; font-size: 12px;
  font-weight: 600; color: var(--ctp-subtext0); background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1); transition: background .12s, color .12s;
  white-space: nowrap; }
.scp-det-btn:hover { background: var(--ctp-surface1); color: var(--ctp-text); }
.scp-det-btn:disabled { opacity: .55; pointer-events: none; }
/* Primary action, SaaS-style: one filled button per panel (Jira's blue). */
.scp-det-btn-go { color: var(--ctp-base); border-color: var(--ctp-mauve);
  background: var(--ctp-mauve); font-weight: 700; }
.scp-det-btn-go:hover { background: color-mix(in oklab, var(--ctp-mauve) 85%, var(--ctp-text));
  border-color: color-mix(in oklab, var(--ctp-mauve) 85%, var(--ctp-text)); color: var(--ctp-base); }
.scp-det-btn-red { color: var(--ctp-red); border-color: rgb(var(--ctp-red-rgb) / .35);
  background: transparent; }
.scp-det-btn-red:hover { background: rgb(var(--ctp-red-rgb) / .12); color: var(--ctp-red); }
.scp-det-grid { display: grid; grid-template-columns: 112px 1fr; gap: 13px 16px;
  margin-top: 18px; padding: 16px; border: 1px solid var(--ctp-surface0);
  border-radius: 8px; background: color-mix(in oklab, var(--ctp-mantle) 60%, transparent); align-items: start; }
/* Jira's "Details" panel label — spans both grid columns, hairline under it. */
.scp-det-grid-head { grid-column: 1 / -1; font-size: 12px; font-weight: 700;
  color: var(--ctp-text); padding-bottom: 9px; margin-bottom: 1px;
  border-bottom: 1px solid var(--ctp-surface0); }
/* Muted count bubble after a section title (Plan items 3, Keywords 14). */
.scp-det-count { font-size: 11px; font-weight: 600; color: var(--ctp-overlay1);
  background: var(--ctp-surface0); border-radius: 99px; padding: 1px 7px; }
/* Collapsible-section chevron (Keywords). */
.scp-det-chev { display: inline-block; font-size: 10px; color: var(--ctp-overlay1);
  transition: transform .15s; }
.scp-det-chev-open { transform: rotate(90deg); }
/* Keyword list: capped height with its own scroll + hairline rows. */
.scp-det-kw-list { max-height: 264px; overflow-y: auto; overscroll-behavior: contain;
  margin-top: 2px; }
.scp-det-k { font-size: 12px; font-weight: 600; color: var(--ctp-subtext0);
  text-transform: none; letter-spacing: 0; padding-top: 2px; }
.scp-det-v { display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: 13px; color: var(--ctp-text); min-width: 0; }
/* Drawer picker reuses the solid heat squares: unpicked = washed out,
   picked = full color + ring (currentColor borders went invisible once the
   chips turned solid-with-white-text). Slightly larger hit target here. */
.scp-det-pri { width: 26px; height: 26px; font-size: 11px; border: none;
  opacity: .35; filter: saturate(.75); cursor: pointer;
  transition: opacity .1s ease, filter .1s ease; }
.scp-det-pri:hover { opacity: .75; filter: none; }
.scp-det-pri-on { opacity: 1; filter: none;
  box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 3.5px var(--ctp-lavender); }
.scp-det-hint { font-size: 11.5px; color: var(--ctp-overlay0); }
.scp-det-select { height: 30px; font-size: 12.5px; color: var(--ctp-text);
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: 5px; padding: 0 8px; }
.scp-det-tag { font-size: 11px; font-weight: 600; color: var(--ctp-subtext0);
  background: var(--ctp-surface0); border-radius: 0; padding: 3px 9px;
  border: 1px solid var(--ctp-surface1);
  display: inline-flex; align-items: center; gap: 6px; line-height: 1.4; }
/* Drawer value chips — every Details value sits in the same square box
   (Mark 2026-07-22: "all in rectangle box components"). */
.scp-det-chip { display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 500; color: var(--ctp-text);
  background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1);
  border-radius: 0; padding: 3px 9px; line-height: 1.4; min-height: 26px; }
.scp-det-chip-strong { font-weight: 700; }
.scp-det-chip-dim { color: var(--ctp-overlay1); }
/* Tool icons inside chips/cards — ClickUp favicon, COVE wave, Oasis tile
   (Mark 2026-07-22: every Source/Links mention of a tool carries its icon). */
.scp-src-ico { width: 13px; height: 13px; border-radius: 3px; flex-shrink: 0; object-fit: cover; }
.scp-det-via { font-size: 10px; font-weight: 500; color: var(--ctp-overlay0); }
/* Thin divider between the Dragon status chip and its action buttons
   (Mark 2026-07-22: "separate the 2"). */
.scp-det-vr { width: 1px; height: 18px; background: var(--ctp-surface1); flex-shrink: 0; }
/* Links block: each link is its own quiet card row — icon, title, meta chips
   on a second line (Mark 2026-07-22: bare stacked lines read as clutter). */
.scp-link-card { display: flex; align-items: flex-start; gap: 8px;
  padding: 7px 9px; margin-top: 5px; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); }
.scp-link-card > .scp-src-ico { margin-top: 2px; }
.scp-link-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.scp-link-main .scp-det-url { overflow-wrap: anywhere; }
.scp-link-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 10.5px; color: var(--ctp-overlay1); }
.scp-link-card + .scp-det-sub { margin-top: 12px; }
/* Market picker: closed = flag + chevron only, no country letters. The
   native select is stretched invisibly over the box so the OPEN dropdown
   still lists full "flag emoji + code + name" labels. */
.scp-det-geo-pick { position: relative; display: inline-flex; align-items: center;
  gap: 7px; min-height: 26px; padding: 3px 9px; background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1); border-radius: 0; cursor: pointer; }
.scp-det-geo-pick:hover { border-color: var(--ctp-overlay0); }
.scp-det-geo-chev { width: 13px; height: 13px; color: var(--ctp-overlay0); flex-shrink: 0; }
.scp-det-geo-select { position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; }
/* Drawer custom dropdowns — Market + Top Picks (Mark 2026-07-22): compact
   Oasis-styled panel anchored right under the field, instead of the native
   select's OS-positioned giant list. Scrim closes on outside click. */
.scp-drop-wrap { position: relative; display: inline-flex; min-width: 0; }
.scp-drop-btn { cursor: pointer; justify-content: space-between; }
.scp-drop-scrim { position: fixed; inset: 0; z-index: 120; }
.scp-drop-panel { position: absolute; top: calc(100% + 4px); left: 0; z-index: 121;
  min-width: 210px; max-width: 300px; max-height: 250px; overflow-y: auto;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  box-shadow: 0 12px 28px rgb(0 0 0 / .18); padding: 3px; }
.scp-drop-item { display: flex; align-items: center; gap: 7px; width: 100%;
  text-align: left; padding: 5px 8px; font-size: 11.5px; color: var(--ctp-text);
  cursor: pointer; background: transparent; border: 0; }
.scp-drop-item:hover { background: var(--ctp-surface0); }
.scp-drop-item-on { background: color-mix(in srgb, var(--ctp-lavender) 12%, transparent); font-weight: 600; }
.scp-drop-item-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Combobox input inside a chip (Top Picks type-to-filter). */
.scp-drop-input { background: transparent; border: 0; outline: none; flex: 1;
  min-width: 0; font-size: 11px; color: var(--ctp-text); cursor: pointer; }
.scp-drop-input:focus { cursor: text; }
.scp-drop-input::placeholder { color: var(--ctp-overlay0); }
/* Green ✓ beside the Planned URL — the explicit "this page is LIVE now"
   confirm (Mark 2026-07-22). */
.scp-live-confirm { display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex-shrink: 0; cursor: pointer;
  color: var(--ctp-green); background: color-mix(in srgb, var(--ctp-green) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-green) 45%, transparent);
  transition: background .15s ease, transform .1s ease; }
.scp-live-confirm:hover { background: color-mix(in srgb, var(--ctp-green) 20%, transparent); transform: translateY(-1px); }
.scp-live-confirm:disabled { opacity: .4; cursor: not-allowed; }
/* Priority guide v2 (Mark 2026-07-22), respaced and put on the house system
   2026-07-27 ("use the oasis branding and spacing on it"): sections are cards in
   a gap-driven column — titles used to sit flush against the paragraph above —
   the rainbow top bar became the single peach accent this lane already owns, and
   the prose is measured instead of running the full width. */
.pg-accent { height: 3px; flex-shrink: 0; background: var(--ctp-peach); }
.pg-body { display: flex; flex-direction: column; gap: 16px; padding: 18px 20px 22px; }
.pg-lede { font-size: 13px; line-height: 1.65; color: var(--ctp-subtext1); max-width: 74ch; }
/* every section is a card, so spacing comes from ONE gap instead of per-element
   margins that collapse into each other */
.pg-sec { padding: 14px 15px; border-radius: 12px; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface0); }
.pg-sec > .pg-sec-title { margin: -14px -15px 12px; padding: 11px 15px 10px;
  border-bottom: 1px solid var(--ctp-surface0); }
.pg-hero-ico { display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 12px; flex-shrink: 0;
  background: color-mix(in srgb, var(--ctp-peach) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-peach) 30%, transparent); }
.pg-nav { display: flex; gap: 6px; padding: 10px 24px; border-bottom: 1px solid var(--ctp-surface0);
  overflow-x: auto; scrollbar-width: none; background: color-mix(in srgb, var(--ctp-mantle) 60%, var(--ctp-base)); }
.pg-nav::-webkit-scrollbar { display: none; }
.pg-nav-pill { flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--ctp-overlay1);
  padding: 4px 10px; border-radius: 7px; border: 0; background: transparent; cursor: pointer;
  transition: color .15s, background .15s; }
.pg-nav-pill:hover { color: var(--ctp-lavender);
  background: color-mix(in srgb, var(--ctp-lavender) 12%, transparent); }
.pg-sec-title { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em;
  color: var(--ctp-overlay0); margin-bottom: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pg-sec-hint { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--ctp-overlay0); font-size: 11px; }
.pg-clear { font-size: 10px; font-weight: 700; color: var(--ctp-lavender); border: 1px solid color-mix(in srgb, var(--ctp-lavender) 40%, transparent);
  border-radius: 999px; padding: 2px 9px; cursor: pointer; background: transparent; }
.pg-rail { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
/* Phones: 6 tiers across leaves ~62px per tile, which snaps "Solid commercial"
   mid-word. Two rows of three, and the labels fit. */
@media (max-width: 768px) { .pg-rail { grid-template-columns: repeat(3, 1fr); } }
.pg-tierbtn { display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 9px 4px 7px; border-radius: 10px; cursor: pointer; border: 2px solid transparent;
  min-width: 0; height: auto; transition: transform .12s ease, border-color .12s ease, box-shadow .12s ease; }
.pg-tierbtn:hover { filter: brightness(1.05); }
.pg-tierbtn-on { border-color: transparent;
  box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 4px color-mix(in srgb, var(--ctp-lavender) 65%, transparent); }
.pg-tierbtn-p { font-family: var(--font-mono, monospace); font-weight: 800; font-size: 14px; }
.pg-tierbtn-l { font-size: 9px; font-weight: 600; opacity: .95; text-align: center; line-height: 1.2; }
.pg-tierdesc { margin-top: 12px; font-size: 12px; line-height: 1.6; color: var(--ctp-subtext1);
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 10px; padding: 9px 12px; }
.pg-tierdesc.pg-dim { color: var(--ctp-overlay0); }
/* Each value class and hard rule carries its own icon (Mark 2026-07-29) so the
   guide is scannable instead of a wall of bold text. Slug → filename lives in
   static/js/data/priority-rubric.js. */
.pg-card { background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 10px; padding: 10px 12px;
  display: flex; align-items: flex-start; gap: 10px; }
.pg-card-ico { width: 26px; height: 26px; flex-shrink: 0; margin-top: 1px; }
.pg-card-tx { min-width: 0; }
.pg-rule-lead { display: flex; align-items: center; gap: 9px; min-width: 0; }
.pg-rule-ico { width: 20px; height: 20px; flex-shrink: 0; }
.pg-card-t { font-size: 12px; font-weight: 700; color: var(--ctp-text); }
.pg-card-w { font-family: var(--font-mono, monospace); font-weight: 600; font-size: 10px; color: var(--ctp-lavender); margin-left: 4px; }
.pg-card-d { font-size: 11px; color: var(--ctp-overlay1); line-height: 1.5; margin-top: 3px; }
.pg-rule { display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left;
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 10px;
  padding: 9px 12px; cursor: pointer; transition: border-color .15s ease; }
.pg-rule:hover { border-color: color-mix(in srgb, var(--ctp-red) 35%, transparent); }
.pg-rule-open { border-color: color-mix(in srgb, var(--ctp-red) 45%, transparent);
  background: color-mix(in srgb, var(--ctp-red) 4%, var(--ctp-mantle)); }
.pg-rule-t { font-size: 12px; font-weight: 700; color: var(--ctp-text); }
.pg-rule-chev { width: 14px; height: 14px; color: var(--ctp-overlay0); flex-shrink: 0; transition: transform .18s ease; }
.pg-rule-d { font-size: 11.5px; color: var(--ctp-subtext0); line-height: 1.6; }
.pg-gate { border: 1px solid color-mix(in srgb, var(--ctp-yellow) 35%, transparent);
  background: color-mix(in srgb, var(--ctp-yellow) 5%, transparent); border-radius: 12px; padding: 12px 14px; }
.pg-ex-row { display: flex; align-items: center; gap: 9px; padding: 6px 2px;
  border-bottom: 1px solid color-mix(in srgb, var(--ctp-surface0) 55%, transparent); }
.pg-ex-p { min-width: 32px; height: 18px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 5px; font-family: var(--font-mono, monospace); font-weight: 800; font-size: 10px; flex-shrink: 0; }
.pg-chat-list { display: flex; flex-direction: column; gap: 10px; max-height: 260px; overflow-y: auto;
  border: 1px solid var(--ctp-surface1); border-radius: 12px; background: var(--ctp-mantle); padding: 12px; }
.pg-chat-empty { font-size: 11.5px; color: var(--ctp-overlay0); font-style: italic; padding: 6px 2px; }
.pg-msg { display: flex; gap: 9px; align-items: flex-start; }
.pg-avatar { width: 28px; height: 28px; border-radius: 999px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 800; color: var(--ctp-peach);
  background: color-mix(in srgb, var(--ctp-peach) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-peach) 30%, transparent); }
.pg-msg-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pg-msg-head { display: flex; align-items: baseline; gap: 7px; }
.pg-msg-head b { font-size: 11.5px; color: var(--ctp-text); }
.pg-msg-head i { font-size: 10px; color: var(--ctp-overlay0); font-style: normal; }
.pg-msg-text { font-size: 12px; color: var(--ctp-subtext1); line-height: 1.55; overflow-wrap: anywhere; }
.pg-chat-input { display: flex; gap: 8px; margin-top: 10px; }
.pg-chat-input input { flex: 1; min-width: 0; height: 34px; padding: 0 12px; font-size: 12px;
  color: var(--ctp-text); background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 999px; }
.pg-chat-input input:focus { outline: none; border-color: var(--ctp-lavender);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ctp-lavender) 15%, transparent); }
.pg-chat-input button { height: 34px; padding: 0 16px; border-radius: 999px; font-size: 12px; font-weight: 700;
  color: #fff; background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-mauve));
  cursor: pointer; transition: opacity .15s ease, transform .1s ease; }
.pg-chat-input button:hover { transform: translateY(-1px); }
.pg-chat-input button:disabled { opacity: .45; cursor: not-allowed; transform: none; }

/* Width lives here, not in a Tailwind class: max-w-6xl was never compiled into
   tailwind.css, so the modal silently had NO max-width and went full-bleed on a
   wide screen (Mark 2026-07-27). Hand CSS can't be missing. */
.pg-modal { max-width: 1140px; }
/* Priority guide, two panes (Mark 2026-07-27): rubric left, answer bot right.
   Mobile first, one column and ONE scroll for the whole modal. From 1024px the
   panes split and each scrolls on its own so the bot stays put while you read
   the rules. */
/* grid-auto-rows: min-content is what makes the stacked layout scroll instead of
   squashing. Auto rows would split the modal's height between the two panes, and
   the rubric (far taller than its share) would paint straight over the bot. */
.pg-split { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr);
  grid-auto-rows: min-content; overflow-y: auto; }
.pg-pane { min-width: 0; min-height: 0; }
.pg-chat-pane { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px 16px;
  border-top: 1px solid var(--ctp-surface1); background: var(--ctp-crust); }
@media (min-width: 1024px) {
  .pg-split { grid-template-columns: minmax(0, 1fr) 400px; grid-auto-rows: minmax(0, 1fr); overflow: hidden; }
  .pg-split > .pg-pane { overflow-y: auto; }
  .pg-chat-pane { border-top: 0; border-left: 1px solid var(--ctp-surface1); overflow: hidden; }
  .pg-chat-pane > .pg-chat-list { flex: 1 1 auto; max-height: none; }
}
.pg-bot-head { display: flex; gap: 9px; align-items: flex-start; }
.pg-bot-head b { display: block; font-size: 12px; font-weight: 800; color: var(--ctp-text); }
.pg-bot-head i { display: block; margin-top: 2px; font-size: 10.5px; font-style: normal;
  line-height: 1.5; color: var(--ctp-overlay1); }
.pg-bot-ico { width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center; font-size: 13px; color: #fff;
  background: linear-gradient(135deg, var(--ctp-blue), var(--ctp-mauve)); }
.pg-msg-ai .pg-msg-text { color: var(--ctp-text); background: color-mix(in srgb, var(--ctp-mauve) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-mauve) 22%, transparent);
  border-radius: 10px; padding: 8px 10px; }
.pg-avatar-ai { color: #fff; border-color: transparent;
  background: linear-gradient(135deg, var(--ctp-blue), var(--ctp-mauve)); font-size: 12px; }
/* The bot's face is the OASIS palm smiley (Mark 2026-07-29: the old ✦ was a bare
   text glyph, so the bot read as generic AI). The PNG is the white mark only —
   the gradient behind it is still the tile above, so both themes stay correct. */
.pg-ai-mark { width: 18px; height: 18px; display: block; }
.pg-bot-ico .pg-ai-mark { width: 17px; height: 17px; }
/* A person's own photo, same map the rest of the app uses. Fills the circle;
   initials stay the fallback for anyone who hasn't uploaded one. */
.pg-avatar-img { width: 100%; height: 100%; border-radius: 999px; object-fit: cover; display: block; }
.pg-avatar:has(.pg-avatar-img) { background: var(--ctp-surface0); border-color: var(--ctp-surface1); }
/* "Here, look at this." The bot can end an answer with links to the screens that
   answer the question, so the guide stops being a dead end (Mark 2026-07-29). */
.pg-go { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.pg-go-btn { display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px;
  font-size: 11px; font-weight: 650; color: var(--ctp-blue); cursor: pointer;
  background: color-mix(in srgb, var(--ctp-blue) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-blue) 26%, transparent);
  border-radius: 999px; transition: background .15s ease, border-color .15s ease, transform .1s ease; }
.pg-go-btn:hover { transform: translateY(-1px);
  background: color-mix(in srgb, var(--ctp-blue) 16%, transparent);
  border-color: color-mix(in srgb, var(--ctp-blue) 45%, transparent); }
.pg-go-btn svg { width: 11px; height: 11px; flex-shrink: 0; }
.pg-typing { display: inline-flex; gap: 4px; padding: 9px 2px 3px; }
.pg-typing i { width: 5px; height: 5px; border-radius: 999px; background: var(--ctp-mauve);
  animation: pg-blink 1.1s ease-in-out infinite; }
.pg-typing i:nth-child(2) { animation-delay: .18s; }
.pg-typing i:nth-child(3) { animation-delay: .36s; }
@keyframes pg-blink { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }
.pg-ans-btn { align-self: flex-start; font-size: 10.5px; font-weight: 700; margin-left: 37px;
  color: var(--ctp-mauve); background: color-mix(in srgb, var(--ctp-mauve) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-mauve) 30%, transparent);
  border-radius: 999px; padding: 3px 10px; cursor: pointer; }
.pg-ans-btn:hover { background: color-mix(in srgb, var(--ctp-mauve) 18%, transparent); }
.pg-ans-err { font-size: 11px; color: var(--ctp-red); line-height: 1.5; }
/* Provenance under an answer: what it actually read. Quiet, but it's the
   difference between trusting the bot and guessing whether it saw the data. */
.pg-msg-src { font-size: 10px; font-family: var(--font-mono, monospace); color: var(--ctp-overlay0);
  margin-top: 3px; overflow-wrap: anywhere; }
.pg-caret { display: inline-block; width: 6px; height: 12px; margin-left: 2px; vertical-align: -2px;
  background: var(--ctp-mauve); animation: pg-blink 1s step-end infinite; }
.pg-sugg { display: flex; flex-direction: column; gap: 5px; margin-left: 37px; }
.pg-sugg-btn { text-align: left; font-size: 11px; font-weight: 600; color: var(--ctp-subtext1);
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1); border-radius: 10px;
  padding: 6px 10px; cursor: pointer; transition: border-color .15s ease, color .15s ease; }
.pg-sugg-btn:hover { border-color: color-mix(in srgb, var(--ctp-mauve) 45%, transparent); color: var(--ctp-text); }
.pg-sugg-btn:disabled { opacity: .5; cursor: not-allowed; }
.pg-bot-foot { font-size: 10px; color: var(--ctp-overlay0); line-height: 1.45; }
/* The old thread waits behind this. Deliberately quiet: a fresh conversation is
   the default, history is the exception you go and ask for. */
.pg-hist-row { display: flex; justify-content: center; padding-bottom: 2px; }
.pg-hist-btn { font-size: 10.5px; font-weight: 600; color: var(--ctp-overlay1);
  background: transparent; border: 1px solid var(--ctp-surface1); border-radius: 999px;
  padding: 3px 11px; cursor: pointer; transition: color .15s ease, border-color .15s ease; }
.pg-hist-btn:hover { color: var(--ctp-text); border-color: var(--ctp-surface2); }
.pg-hist-btn:disabled { opacity: .6; cursor: default; }

/* Phone layout (Mark 2026-07-27: "fully usable on mobile"). Stacked panes meant
   scrolling ~2,700px of rubric to reach the bot, so a phone gets two tabs and
   whichever is showing owns the whole modal. Desktop keeps both panes side by
   side and never sees the tabs — one set of markup, one set of state. */
.pg-tabs { display: flex; gap: 6px; padding: 10px 12px 0; }
.pg-tab { flex: 1; min-width: 0; height: 40px; border-radius: 11px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 12.5px; font-weight: 700; color: var(--ctp-overlay1);
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); }
.pg-tab-on { color: #fff; border-color: transparent;
  background: linear-gradient(90deg, var(--ctp-blue), var(--ctp-mauve)); }
.pg-tab-n { font-size: 10px; font-weight: 800; padding: 1px 6px; border-radius: 999px;
  background: color-mix(in srgb, #fff 28%, transparent); }
@media (max-width: 1023px) {
  .pg-split.pg-showing-guide > .pg-chat-pane { display: none; }
  .pg-split.pg-showing-ask > .pg-body { display: none; }
  /* The showing pane fills the modal and scrolls itself, so the bot's input
     stays pinned at the bottom instead of living below a page of rubric. */
  .pg-split.pg-showing-ask { grid-auto-rows: minmax(0, 1fr); overflow: hidden; }
  .pg-split.pg-showing-ask > .pg-chat-pane { overflow: hidden; border-top: 0; }
  .pg-split.pg-showing-ask .pg-chat-list { flex: 1 1 auto; max-height: none; }
  .pg-nav-hide { display: none; }
  /* Thumb-sized targets. The input's own size is set by the global 16px rule
     at the end of this block — see the iOS zoom note there. */
  .pg-chat-input input, .pg-chat-input button { height: 42px; font-size: 13px; }
  .pg-nav-pill { padding: 8px 12px; }
  .pg-sugg-btn { padding: 10px 12px; font-size: 12px; }
  .pg-ans-btn { padding: 6px 12px; font-size: 11px; }
  /* Fill the phone: a content-height sheet left dead space under the thread,
     and the three-line subtitle stole room from it (the lede repeats it). */
  .pg-modal { min-height: 84vh; }
  .pg-sub { display: none; }
}
@media (min-width: 1024px) { .pg-tabs { display: none; } }

/* Apps launcher — the Google-Suite-style 9-dot grid, top right
   before the profile chip (Mark 2026-07-22). Rounded floating panel,
   3-column app tiles, its own scroll. */
.app-grid-btn { width: 38px; height: 38px; border-radius: 999px; display: inline-flex;
  align-items: center; justify-content: center; color: var(--ctp-overlay1);
  cursor: pointer; transition: background .15s ease, color .15s ease; }
.app-grid-btn:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.app-grid-panel { position: fixed; top: calc(var(--brand-header-h, 77px) - 4px); right: 14px;
  z-index: 151; width: 352px; max-width: calc(100vw - 20px); max-height: min(74vh, 580px);
  overflow-y: auto; overscroll-behavior: contain; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 26px;
  box-shadow: 0 18px 50px -12px rgba(0,0,0,.35); padding: 16px 10px 12px; }
.app-grid-head { font-size: 15px; font-weight: 700; color: var(--ctp-text); padding: 2px 14px 10px; }
.app-grid-sub { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ctp-overlay0); padding: 14px 14px 8px; border-top: 1px solid var(--ctp-surface0);
  margin-top: 10px; }
.app-grid-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; }
.app-grid-item { display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 13px 6px 11px; border-radius: 16px; text-decoration: none;
  transition: background .12s ease; }
.app-grid-item:hover { background: var(--ctp-surface0); }
.app-grid-ico { width: 42px; height: 42px; border-radius: 11px; object-fit: cover; }
.app-grid-name { font-size: 11.5px; color: var(--ctp-text); text-align: center;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Favourites edit mode (the Google pencil): tap tiles to pin/unpin. */
.app-grid-edit { width: 34px; height: 34px; border-radius: 999px; display: inline-flex;
  align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer;
  color: var(--ctp-blue); background: color-mix(in srgb, var(--ctp-blue) 10%, transparent);
  border: 1px solid transparent; transition: background .15s ease; padding: 0 8px; min-width: 34px; width: auto; }
.app-grid-edit:hover { background: color-mix(in srgb, var(--ctp-blue) 18%, transparent); }
.app-grid-item-edit { cursor: pointer; }
.app-grid-item-edit:hover { background: color-mix(in srgb, var(--ctp-blue) 8%, transparent); }
.app-fav-badge { position: absolute; top: -5px; right: -7px; width: 17px; height: 17px;
  border-radius: 999px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 800; line-height: 1; color: #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.3); }
.app-fav-badge-add { background: var(--ctp-green); }
.app-fav-badge-rm { background: var(--ctp-red); }
.app-grid-empty { font-size: 11px; color: var(--ctp-overlay0); font-style: italic; padding: 4px 14px 8px; }
/* Drag-to-reorder + add-your-own (Mark 2026-07-22). */
.app-grid-drop { outline: 2px dashed var(--ctp-blue); outline-offset: -2px;
  background: color-mix(in srgb, var(--ctp-blue) 10%, transparent); }
.app-grid-item[draggable="true"] { cursor: grab; }
.app-fav-badge-del { left: -7px; right: auto; background: var(--ctp-surface2);
  border: 0; cursor: pointer; padding: 0; font-size: 10px; }
.app-fav-badge-del:hover { background: var(--ctp-red); }
button.app-fav-badge { border: 0; cursor: pointer; padding: 0; }
.app-grid-addbox { margin: 8px 14px 4px; padding: 10px; border-radius: 14px;
  border: 1px dashed var(--ctp-surface2); background: var(--ctp-mantle); }
.app-grid-addrow { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.app-grid-addinput { flex: 1 1 110px; min-width: 0; height: 30px; padding: 0 9px;
  border-radius: 8px; border: 1px solid var(--ctp-surface1); background: var(--ctp-base);
  color: var(--ctp-text); font-size: 11.5px; }
.app-grid-addinput:focus { outline: none; border-color: var(--ctp-blue); }
.app-grid-addbtn { height: 30px; padding: 0 13px; border-radius: 8px; border: 0;
  background: var(--ctp-blue); color: #fff; font-size: 11.5px; font-weight: 700;
  cursor: pointer; }
.app-grid-addbtn:hover { opacity: .9; }
.app-grid-adderr { font-size: 10.5px; color: var(--ctp-red); padding: 6px 2px 0; }
.app-fav-chip { display: inline-flex; align-items: center; gap: 8px; height: 34px;
  padding: 0 12px; border-radius: 999px; border: 1px solid var(--ctp-surface1);
  background: var(--ctp-base); color: var(--ctp-text); font-size: 12.5px;
  font-weight: 600; cursor: pointer; transition: border-color .12s ease; }
.app-fav-chip:hover { border-color: var(--ctp-blue); }
.app-fav-chip-del { display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 999px; background: var(--ctp-surface1);
  color: var(--ctp-overlay1); font-size: 9px; font-weight: 800; }
.app-fav-chip-del:hover { background: var(--ctp-red); color: #fff; }

/* Dragon fire modal stage (Mark 2026-07-22): the hero breathes while idle;
   an indeterminate ember bar runs while the fire request is in flight. */
.scp-dragon-breathe { animation: scp-breathe 3.4s ease-in-out infinite; transform-origin: 35% 65%; }
@keyframes scp-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
.scp-fire-bar { height: 100%; width: 40%; border-radius: 2px;
  background: linear-gradient(90deg, #fab387, #f38ba8);
  animation: scp-fire-bar .9s ease-in-out infinite alternate; }
@keyframes scp-fire-bar { 0% { margin-left: 0; } 100% { margin-left: 60%; } }
/* Dragon fire modal card (Mark 2026-07-23): the card CONTINUES the video's
   warm amber so video + body read as one storybook piece. Fixed warm palette
   on purpose — the dragon art is always yellow, so the card must not follow
   the app's light/dark theme (dark-theme vars on yellow were the bug). */
.scp-dragon-card { width: 480px; max-width: 90vw; border-radius: 16px;
  border: 1px solid rgba(120, 72, 10, .35);
  background: linear-gradient(180deg, #f6c471 0%, #f9d98f 36%, #fbe7b4 70%, #fdf1cf 100%);
  box-shadow: 0 26px 64px -16px rgba(60, 32, 4, .6); }
.sdc-body { padding: 16px 20px 18px; text-align: center; }
.sdc-title { font-weight: 800; font-size: 15px; color: #4a2c08; letter-spacing: .01em; }
.sdc-title-ok  { color: #15803d; }
.sdc-title-bad { color: #b91c1c; }
.sdc-sub { font-size: 11.5px; line-height: 1.6; color: #7c531b; margin-top: 6px; }
.sdc-strong { color: #4a2c08; font-weight: 700; }
.sdc-cmd { display: inline-block; font-size: 11px; color: #b45309;
  background: rgba(255, 252, 240, .85); border: 1px solid rgba(180, 120, 30, .4);
  border-radius: 8px; padding: 5px 11px; margin-top: 9px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9); }
.sdc-alert { margin-top: 12px; text-align: left; padding: 10px 12px; border-radius: 10px;
  border: 1px solid rgba(190, 50, 30, .35); background: rgba(255, 245, 240, .78); }
.sdc-alert-title { font-size: 11.5px; font-weight: 800; color: #b3372c; }
.sdc-alert-text { font-size: 11px; line-height: 1.55; color: #8a4a35; }
.sdc-field { height: 28px; font-size: 11.5px; border: 1px solid rgba(150, 95, 20, .4);
  border-radius: 7px; background: #fffdf6; color: #4a2c08; }
.sdc-field:focus { outline: 2px solid rgba(235, 108, 21, .35); }
.sdc-btn { height: 32px; border-radius: 9px; font-size: 12px; font-weight: 700;
  transition: transform .12s ease, background .12s ease; }
.sdc-btn-no { padding: 0 16px; color: #6b4a16; border: 1px solid rgba(150, 95, 20, .4);
  background: rgba(255, 253, 246, .9); }
.sdc-btn-no:hover { background: #fffdf6; }
.sdc-btn-yes { padding: 0 15px 0 11px; color: #2a1208;
  background: linear-gradient(180deg, #fdba5f, #f0653c);
  border: 1px solid rgba(140, 50, 10, .35);
  box-shadow: 0 4px 14px -4px rgba(200, 60, 20, .65); }
.sdc-btn-yes:hover:not(:disabled) { transform: translateY(-1px); }
.sdc-btn-yes:active { transform: translateY(0); }
.sdc-btn-save { height: 28px; padding: 0 12px; border-radius: 7px; font-size: 11px;
  font-weight: 700; color: #15803d; border: 1px solid rgba(21, 128, 61, .4);
  background: rgba(240, 253, 244, .9); }
.sdc-fade { position: absolute; left: 0; right: 0; bottom: 0; height: 30px;
  background: linear-gradient(to top, #f6c471, transparent); pointer-events: none; }
.sdc-bar-track { position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: rgba(74, 44, 8, .18); }
/* Title + keyword-target inline edit — pencil appears on hover. */
.scp-det-edit-pen { font-size: 13px; line-height: 1; color: var(--ctp-overlay0);
  background: none; border: none; cursor: pointer; padding: 2px 4px;
  opacity: 0; transition: opacity .12s ease, color .12s ease; }
.scp-det-title:hover .scp-det-edit-pen, .scp-det-kwrow:hover .scp-det-edit-pen { opacity: 1; }
.scp-det-edit-pen:hover { color: var(--ctp-text); }
.scp-det-edit-input { font: inherit; color: var(--ctp-text); background: var(--ctp-base);
  border: 1px solid var(--ctp-lavender); border-radius: 0; padding: 2px 8px;
  flex: 1; min-width: 0; outline: none; }
.scp-det-edit-input-sm { font-size: 13px; font-weight: 600; flex: 0 1 340px; }
.scp-det-kwrow { display: flex; align-items: center; gap: 8px; margin: 2px 0 12px; }
.scp-det-kwlab { font-size: 12px; font-weight: 600; color: var(--ctp-subtext0); }
/* External-origin marks — rows synced from the site (COVE), not created in
   Oasis: a quiet blue "synced" chip on the board card + Source tag in the
   drawer, so curators can spot what may need hand-editing. */
.scp-det-tag-ext { color: var(--ctp-blue);
  background: color-mix(in oklab, var(--ctp-blue) 12%, transparent); }
.scp-card-ext { font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ctp-blue); border-radius: 4px;
  padding: 1px 5px; flex-shrink: 0;
  background: color-mix(in oklab, var(--ctp-blue) 12%, transparent); }
.scp-det-sec { margin-top: 16px; padding: 14px 16px; border: 1px solid var(--ctp-surface0);
  border-radius: 10px; background: var(--ctp-base);
  box-shadow: 0 1px 2px rgb(0 0 0 / .05); }
/* Full-bleed hairline under every block title — the block reads as a card
   with a header, not a floating label. */
.scp-det-sec-title { display: flex; align-items: center; gap: 8px; font-size: 12px;
  font-weight: 600; text-transform: none; letter-spacing: 0;
  color: var(--ctp-subtext0); margin: 0 -16px 12px; padding: 0 16px 10px;
  border-bottom: 1px solid var(--ctp-surface0); }
/* Sub-group label inside a merged block (Links: Live page / ClickUp / Plan rows). */
.scp-det-sub { font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--ctp-overlay0); margin: 12px 0 4px; }
.scp-det-sub:first-of-type { margin-top: 0; }
.scp-det-sec-btn { cursor: pointer; width: 100%; text-align: left; }
.scp-det-sec-btn:hover { color: var(--ctp-text); }
.scp-det-url { font-size: 13px; color: var(--ctp-blue); word-break: break-all; }
.scp-det-url:hover { text-decoration: underline; }
.scp-det-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: 12.5px; color: var(--ctp-subtext0); margin-top: 5px; min-width: 0; }
.scp-det-item-title { max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--ctp-text); font-weight: 500; }
.scp-det-warn { color: var(--ctp-red); font-weight: 600; }
.scp-det-mini { width: 24px; height: 24px; border-radius: 5px; font-size: 12px;
  color: var(--ctp-mauve); }
.scp-det-mini-txt { width: auto; padding: 0 9px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  border: 1px solid rgb(var(--ctp-mauve-rgb) / .35); }
.scp-det-mini:hover { background: rgb(var(--ctp-mauve-rgb) / .12); }
.scp-det-mini:disabled { opacity: .5; pointer-events: none; }
.scp-det-why { font-size: 13px; line-height: 1.6; color: var(--ctp-text); }
.scp-det-dot { display: inline-block; width: 7px; height: 7px; border-radius: 99px;
  margin-right: 4px; background: var(--ctp-overlay0); }
.scp-det-dot-peer { background: var(--ctp-teal); }
.scp-det-dot-ai { background: var(--ctp-mauve); }
.scp-det-dot-gap { background: var(--ctp-peach); }
.scp-det-kw { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; font-size: 12.5px; color: var(--ctp-subtext0); padding: 5px 6px; min-width: 0;
  border-radius: 5px; border-bottom: 1px solid color-mix(in oklab, var(--ctp-surface0) 55%, transparent); }
.scp-det-kw:last-child { border-bottom: none; }
.scp-det-kw:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.scp-det-notes { width: 100%; font-size: 13px; line-height: 1.55; color: var(--ctp-text);
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1); border-radius: 6px;
  padding: 9px 11px; resize: vertical; }
.scp-det-notes:focus { outline: none; border-color: var(--ctp-lavender); }
/* ── Jira panel pass (2026-07-21) ─────────────────────────────────────── */
/* Header breadcrumb: hub topic / this sub-topic. */
.scp-det-crumb { font-size: 12px; color: var(--ctp-overlay1); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scp-det-crumb-sep { color: var(--ctp-overlay0); padding: 0 1px; }
.scp-det-crumb-cur { color: var(--ctp-subtext0); font-weight: 600; }
/* Control row: [Status ▾] [primary] [⚡ Actions ▾]. */
.scp-det-ctrl { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.scp-det-statuswrap { position: relative; display: inline-flex; }
.scp-det-status-btn { appearance: none; -webkit-appearance: none; height: 32px;
  padding: 0 28px 0 14px; border: none; border-radius: 6px; font-size: 12px;
  font-weight: 700; letter-spacing: .02em; color: var(--ctp-base); cursor: pointer;
  background: var(--det-ac, var(--ctp-blue)); }
.scp-det-status-btn:hover { filter: brightness(1.08); }
.scp-det-status-btn:focus { outline: 2px solid color-mix(in oklab, var(--det-ac, var(--ctp-blue)) 45%, transparent); outline-offset: 1px; }
.scp-det-status-chev { position: absolute; right: 9px; top: 50%; width: 13px; height: 13px;
  transform: translateY(-50%); pointer-events: none; color: var(--ctp-base); }
.scp-det-status-static { display: inline-flex; align-items: center; padding: 0 14px; cursor: default; }
.scp-det-chev-txt { font-size: 9px; margin-left: 2px; }
.scp-det-btn-open { background: var(--ctp-surface1); color: var(--ctp-text); }
/* ⚡ Actions dropdown. */
.scp-det-menu { position: absolute; top: calc(100% + 4px); left: 0; z-index: 30;
  min-width: 184px; padding: 5px; border-radius: 8px; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); box-shadow: 0 12px 28px -10px rgb(0 0 0 / .4); }
.scp-det-menu-item { display: block; width: 100%; text-align: left; padding: 8px 11px;
  border-radius: 5px; font-size: 12px; font-weight: 600; color: var(--ctp-subtext0);
  white-space: nowrap; }
.scp-det-menu-item:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.scp-det-menu-item-red { color: var(--ctp-red); }
.scp-det-menu-item-red:hover { background: rgb(var(--ctp-red-rgb) / .12); color: var(--ctp-red); }
/* Collapsible field boxes: Details (open) · More fields (tucked). The box
   owns the border — the grid inside drops its own. */
.scp-det-box { margin-top: 16px; border: 1px solid var(--ctp-surface0); border-radius: 10px;
  background: var(--ctp-base); overflow: hidden;
  box-shadow: 0 1px 2px rgb(0 0 0 / .05); }
.scp-det-box-head { display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 12px 16px; font-size: 12px; font-weight: 600;
  color: var(--ctp-subtext0); cursor: pointer; text-align: left; }
/* Open box: hairline between the header and its fields (matches sec-title). */
.scp-det-box-head:has(+ .scp-det-grid:not([style*="none"])) { border-bottom: 1px solid var(--ctp-surface0); }
.scp-det-box-head:hover { color: var(--ctp-text); background: var(--ctp-surface0); }
.scp-det-box-chev { width: 13px; height: 13px; flex-shrink: 0; transition: transform .15s; }
.scp-det-box-chev-open { transform: rotate(180deg); }
.scp-det-box .scp-det-grid { margin-top: 0; border: none; border-radius: 0;
  background: transparent; padding-top: 12px; }
/* Quiet timestamps footer. */
.scp-det-dates { display: flex; gap: 16px; margin-top: 16px; padding: 0 2px;
  font-size: 11.5px; color: var(--ctp-overlay0); }
/* Phones: the board becomes a horizontal snap strip — one column ≈ one screen. */
@media (max-width: 768px) {
  .scp-board { scroll-snap-type: x mandatory; }
  .scp-board-col { min-width: 78vw; scroll-snap-align: center; }
}

/* Topics-table "Columns" popover — per-user show/hide + drag-reorder. Quiet,
   surface0-bordered, same small-caps register as the .scp-det-* drawer. */
.scp-cols-pop { position: absolute; top: 2rem; right: 0; z-index: 50; width: 232px;
  max-height: min(70vh, 460px); overflow-y: auto; padding: 8px; border-radius: 10px;
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1);
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, .5); }
.scp-cols-head { display: flex; align-items: center; gap: 6px; padding: 2px 4px 6px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ctp-overlay1); }
.scp-cols-reset { font-size: 10px; font-weight: 600; text-transform: none;
  letter-spacing: 0; color: var(--ctp-lavender); }
.scp-cols-reset:hover { text-decoration: underline; }
.scp-cols-x { width: 18px; height: 18px; border-radius: 5px; font-size: 11px;
  color: var(--ctp-overlay1); }
.scp-cols-x:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.scp-cols-hint { padding: 0 4px 7px; font-size: 10px; line-height: 1.4; color: var(--ctp-overlay0);
  border-bottom: 1px solid var(--ctp-surface0); margin-bottom: 4px; }
.scp-cols-row { display: flex; align-items: center; gap: 7px; padding: 4px 5px;
  border-radius: 6px; cursor: grab; user-select: none; }
.scp-cols-row:hover { background: rgb(var(--ctp-surface1-rgb) / .4); }
.scp-cols-grip { color: var(--ctp-overlay0); font-size: 10px; line-height: 1; letter-spacing: -2px;
  cursor: grab; flex-shrink: 0; }
.scp-cols-label { flex: 1 1 auto; min-width: 0; font-size: 11.5px; color: var(--ctp-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scp-cols-off { color: var(--ctp-overlay0); text-decoration: line-through; }
.scp-cols-fixed { flex-shrink: 0; font-size: 8.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ctp-overlay0); background: var(--ctp-surface0);
  border-radius: 4px; padding: 1px 4px; }
.scp-cols-pinned { cursor: default; }
.scp-cols-pinned .scp-cols-grip { visibility: hidden; }
.scp-cols-dragging { opacity: .4; }
.scp-cols-drop { box-shadow: inset 0 2px 0 var(--ctp-lavender); }

/* Export menu (Topics Explorer toolbar) — same shell as the Columns popover,
   a touch wider because the rows carry a label plus a note. */
.v2exp-pop { width: 268px; }
.v2exp-item { display: flex; align-items: baseline; gap: 6px; width: 100%; text-align: left;
  padding: 6px 6px; border-radius: 6px; color: var(--ctp-text); }
.v2exp-item:hover:not(:disabled) { background: rgb(var(--ctp-green-rgb) / .12); color: var(--ctp-green); }
.v2exp-item:disabled { opacity: .5; cursor: not-allowed; }
.v2exp-label { font-size: 10.5px; font-weight: 700; text-transform: none; letter-spacing: 0;
  color: var(--ctp-text); }
.v2exp-note { flex: 1 1 auto; min-width: 0; font-size: 9px; font-weight: 500; text-transform: none;
  letter-spacing: 0; color: var(--ctp-overlay0); text-align: right; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* One block per thing you can export, with its three ways out underneath:
   icon + one word each, so all three fit on one row at 268px without wrapping. */
.v2exp-group { padding: 5px 6px 6px; border-radius: 6px; }
.v2exp-group + .v2exp-group { border-top: 1px solid var(--ctp-surface0); border-radius: 0 0 6px 6px; }
.v2exp-grouphead { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; }
.v2exp-acts { display: flex; align-items: center; gap: 4px; }
.v2exp-act { flex: 1 1 0; min-width: 0; display: inline-flex; align-items: center;
  justify-content: center; gap: 3px; padding: 4px; border-radius: 5px;
  border: 1px solid var(--ctp-surface1); background: var(--ctp-base);
  font-size: 9px; font-weight: 700; text-transform: none; letter-spacing: 0;
  color: var(--ctp-subtext0); white-space: nowrap; }
.v2exp-act:hover:not(:disabled) { border-color: rgb(var(--ctp-green-rgb) / .5);
  background: rgb(var(--ctp-green-rgb) / .12); color: var(--ctp-green); }
.v2exp-act:disabled { opacity: .45; cursor: not-allowed; }
.v2exp-ico { width: 11px; height: 11px; flex-shrink: 0; }
.v2exp-back { padding: 5px 6px 2px; font-size: 10px; font-weight: 600; text-transform: none;
  letter-spacing: 0; color: var(--ctp-overlay1); }
.v2exp-back:hover { color: var(--ctp-text); }
.v2exp-busy { padding: 6px 6px 2px; font-size: 10px; color: var(--ctp-overlay0); }
/* ── Export confirm sheet ────────────────────────────────────────────────────
   Oasis-themed rather than the generic modal it shipped as: the export accent is
   GREEN everywhere else in this feature (toolbar button, menu hover, progress
   bar), so the head icon, the chosen market and the Yes button carry it too, and
   the blue house send-button is deliberately not used here. */
.v2exc-modal { width: 480px; }
.v2exc-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border-bottom: 1px solid var(--ctp-surface0);
  background: linear-gradient(180deg, rgb(var(--ctp-green-rgb) / .10), transparent); }
.v2exc-headico { flex: 0 0 30px; height: 30px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgb(var(--ctp-green-rgb) / .16); color: var(--ctp-green); }
.v2exc-headico svg { width: 16px; height: 16px; }
.v2exc-headtxt { display: flex; flex-direction: column; line-height: 1.15; }
.v2exc-eyebrow { font-size: 9px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ctp-green); }
.v2exc-title { font-size: 13.5px; font-weight: 800; color: var(--ctp-text); }
.v2exc-body { padding: 12px 14px 14px; }
/* Scope readout: icon, label, value. A long tag list wraps inside its own cell
   rather than pushing the label around. */
.v2exc-rows { border: 1px solid var(--ctp-surface0); border-radius: 10px; overflow: hidden; }
.v2exc-row { display: flex; align-items: flex-start; gap: 8px; padding: 7px 10px; font-size: 11.5px; }
.v2exc-row + .v2exc-row { border-top: 1px solid color-mix(in srgb, var(--ctp-surface0) 70%, transparent); }
.v2exc-rowico { width: 13px; height: 13px; flex-shrink: 0; margin-top: 1px; color: var(--ctp-overlay0); }
.v2exc-k { flex: 0 0 84px; color: var(--ctp-overlay1); font-weight: 650; }
.v2exc-v { flex: 1 1 auto; min-width: 0; color: var(--ctp-text); font-weight: 600; word-break: break-word;
  display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.v2exc-vmuted { color: var(--ctp-overlay0); font-weight: 500; font-style: italic; }
.v2exc-prio { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 5px;
  font-size: 10px; font-weight: 800; letter-spacing: .02em; }
.v2exc-tag { display: inline-flex; align-items: center; gap: 4px; padding: 1.5px 7px 1.5px 5px;
  border-radius: 999px; background: var(--ctp-surface0); font-size: 10.5px; font-weight: 700;
  color: var(--ctp-subtext1); }
.v2exc-tagico { width: 13px; height: 13px; object-fit: contain; flex-shrink: 0; }
/* The market codes in the file. Tabular so a long list of states scans as a set
   rather than as a sentence. */
.v2exc-code { display: inline-flex; align-items: center; gap: 3px; padding: 1px 5px 1px 3px;
  border-radius: 4px; background: var(--ctp-surface0); color: var(--ctp-subtext1);
  font-size: 9.5px; font-weight: 750; letter-spacing: .02em; }
.v2exc-codeflag { width: 12px; height: 9px; object-fit: cover; border-radius: 1.5px; flex-shrink: 0; }
/* The market row leads with the market's own flag once one is picked — the chips
   below have flags, so a generic globe up here read as a different kind of thing. */
.v2exc-rowflag { width: 15px; height: 11px; object-fit: cover; border-radius: 2px;
  flex-shrink: 0; margin-top: 2px; }
/* Footnote: something true about the file, not a setting anyone chose. */
.v2exc-fine { display: flex; gap: 6px; margin-top: 9px; font-size: 10.5px; line-height: 1.5;
  color: var(--ctp-overlay1); }
.v2exc-fine svg { width: 13px; height: 13px; flex-shrink: 0; margin-top: 2px; color: var(--ctp-overlay0); }
.v2exc-fine b { color: var(--ctp-subtext1); font-weight: 750; }
.v2exc-pick { margin-top: 11px; }
.v2exc-picklbl { display: flex; align-items: center; gap: 6px; font-size: 10.5px; font-weight: 700;
  color: var(--ctp-overlay1); margin-bottom: 6px; }
/* Scrolls: an all-markets view can hold seventy of these. */
.v2exc-chips { display: flex; flex-wrap: wrap; gap: 5px; max-height: 132px; overflow-y: auto;
  padding: 2px; }
.v2exc-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px 3px 6px;
  border-radius: 999px; border: 1px solid var(--ctp-surface1);
  background: var(--ctp-base); font-size: 10.5px; font-weight: 750; color: var(--ctp-subtext0); }
.v2exc-chipflag { width: 14px; height: 10px; object-fit: cover; border-radius: 2px; flex-shrink: 0; }
/* Tick on a picked chip: several states can be on at once, so "selected" has to
   read as a state you toggled rather than a single highlighted choice. */
.v2exc-chiptick { width: 10px; height: 10px; flex-shrink: 0; margin-left: 1px; }
.v2exc-chip:hover { border-color: rgb(var(--ctp-green-rgb) / .5); color: var(--ctp-green); }
.v2exc-chip.on { border-color: rgb(var(--ctp-green-rgb) / .7);
  background: rgb(var(--ctp-green-rgb) / .18); color: var(--ctp-green);
  box-shadow: 0 0 0 1px rgb(var(--ctp-green-rgb) / .35); }
/* The country-wide chip for US / CA: states plus the national topics. Reads as
   the primary choice because for those two markets it usually is. */
.v2exc-chip.all { display: inline-flex; align-items: center; gap: 4px;
  border-color: var(--ctp-surface2); color: var(--ctp-text); font-weight: 800; }
.v2exc-chipico { width: 11px; height: 11px; flex-shrink: 0; }
.v2exc-sum { margin-top: 11px; font-size: 11.5px; line-height: 1.5; color: var(--ctp-subtext0); }
.v2exc-sum b { color: var(--ctp-text); font-weight: 800; }
.v2exc-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.v2exc-note { flex: 1 1 auto; min-width: 0; font-size: 10px; color: var(--ctp-overlay0); }
.v2exc-no { flex-shrink: 0; padding: 7px 12px; border-radius: 8px; font-size: 12.5px; font-weight: 700;
  color: var(--ctp-overlay1); }
.v2exc-no:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
/* nowrap + no shrink: the note beside it squeezed "Yes, export" onto two lines. */
.v2exc-yes { flex-shrink: 0; white-space: nowrap; display: inline-flex; align-items: center;
  gap: 6px; padding: 7px 14px; border-radius: 8px; font-size: 12.5px; font-weight: 800;
  color: var(--ctp-base); background: var(--ctp-green); }
.v2exc-yes svg { width: 14px; height: 14px; }
.v2exc-yes:hover:not(:disabled) { filter: brightness(1.06); }
.v2exc-yes:disabled { opacity: .45; cursor: default; }

/* ═══════════════════════════════════════════════════════════════════
   Vision / Mission — "living story" page (Mark 2026-07-21)
   Scroll-reveals (.vm-reveal + .vm-in via IntersectionObserver in app.js),
   aurora hero with live counters, auto-cycling workflow stepper, and the
   market pulse bars. Mobile-first; honors prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════════ */

/* Hero */
.vm-hero { position: relative; overflow: hidden; border-radius: 16px; border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle); padding: 28px 20px; }
.vm-hero-blob { position: absolute; width: 340px; height: 340px; border-radius: 50%; filter: blur(70px); opacity: .38; pointer-events: none; }
.vm-hero-blob-a { background: rgb(var(--ctp-yellow-rgb) / .55); top: -120px; left: -80px; animation: vmDriftA 22s ease-in-out infinite alternate; }
.vm-hero-blob-b { background: rgb(var(--ctp-sky-rgb) / .45); bottom: -140px; right: -60px; animation: vmDriftB 26s ease-in-out infinite alternate; }
.vm-hero-blob-c { background: rgb(var(--ctp-mauve-rgb) / .4); top: -60px; right: 25%; animation: vmDriftC 30s ease-in-out infinite alternate; }
@keyframes vmDriftA { to { transform: translate(70px, 40px) scale(1.15); } }
@keyframes vmDriftB { to { transform: translate(-60px, -35px) scale(1.1); } }
@keyframes vmDriftC { to { transform: translate(40px, 50px) scale(.9); } }
.vm-hero-grid { position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(rgb(var(--ctp-text-rgb) / .045) 1px, transparent 1px), linear-gradient(90deg, rgb(var(--ctp-text-rgb) / .045) 1px, transparent 1px);
  background-size: 34px 34px;
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, #000 30%, transparent 75%); }
.vm-hero-inner { position: relative; z-index: 1; }
.vm-hero-title { font-size: clamp(1.45rem, 4.5vw, 2.4rem); line-height: 1.15; font-weight: 700; color: var(--ctp-text); margin: 14px 0 10px; letter-spacing: -.01em; }
.vm-hero-grad { background: linear-gradient(92deg, var(--ctp-yellow), var(--ctp-peach) 55%, var(--ctp-rosewater)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.vm-hero-sub { font-size: 13px; line-height: 1.65; color: var(--ctp-subtext0); max-width: 640px; }

/* Live pill */
.vm-live-pill { display: inline-flex; align-items: center; gap: 7px; font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ctp-subtext0); border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-base-rgb) / .55); border-radius: 999px; padding: 4px 10px; }
.vm-live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ctp-green); box-shadow: 0 0 0 0 rgb(var(--ctp-green-rgb) / .6); animation: vmPulse 2.2s ease-out infinite; }
@keyframes vmPulse { 70% { box-shadow: 0 0 0 9px rgb(var(--ctp-green-rgb) / 0); } 100% { box-shadow: 0 0 0 0 rgb(var(--ctp-green-rgb) / 0); } }

/* Hero stat tiles */
.vm-stat-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 20px; }
.vm-stat { border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-base-rgb) / .5); border-radius: 12px; padding: 12px 14px; transition: border-color .25s ease, transform .25s ease; }
.vm-stat:hover { transform: translateY(-2px); }
.vm-stat-num { font-size: clamp(1.3rem, 3vw, 1.8rem); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.vm-stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: .07em; color: var(--ctp-overlay1); margin-top: 2px; }
.vm-stat-yellow .vm-stat-num { color: var(--ctp-yellow); }   .vm-stat-yellow:hover { border-color: rgb(var(--ctp-yellow-rgb) / .45); }
.vm-stat-sky .vm-stat-num { color: var(--ctp-sky); }         .vm-stat-sky:hover { border-color: rgb(var(--ctp-sky-rgb) / .45); }
.vm-stat-green .vm-stat-num { color: var(--ctp-green); }     .vm-stat-green:hover { border-color: rgb(var(--ctp-green-rgb) / .45); }
.vm-stat-mauve .vm-stat-num { color: var(--ctp-mauve); }     .vm-stat-mauve:hover { border-color: rgb(var(--ctp-mauve-rgb) / .45); }

/* Scroll reveal */
.vm-reveal { opacity: 0; transform: translateY(16px); transition: opacity .65s cubic-bezier(.22,.61,.36,1), transform .65s cubic-bezier(.22,.61,.36,1); }
.vm-reveal.vm-in { opacity: 1; transform: none; }
.vm-delay-1 { transition-delay: .12s; }
.vm-child { opacity: 0; transform: translateY(10px); transition: opacity .5s ease, transform .5s ease; }
.vm-in .vm-child { opacity: 1; transform: none; }
.vm-in .vm-child:nth-child(1) { transition-delay: .05s; }
.vm-in .vm-child:nth-child(2) { transition-delay: .13s; }
.vm-in .vm-child:nth-child(3) { transition-delay: .21s; }
.vm-in .vm-child:nth-child(4) { transition-delay: .29s; }
.vm-in .vm-child:nth-child(5) { transition-delay: .37s; }
.vm-in .vm-child:nth-child(6) { transition-delay: .45s; }

/* Cards */
.vm-card { position: relative; border-radius: 14px; border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-surface0-rgb) / .6); padding: 18px; scroll-margin-top: 90px; transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease, opacity .65s cubic-bezier(.22,.61,.36,1); }
.vm-card:hover { transform: translateY(-2px); border-color: var(--ctp-surface2); box-shadow: 0 10px 30px -18px rgb(0 0 0 / .8); }
.vm-card.vm-reveal { transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease, opacity .65s cubic-bezier(.22,.61,.36,1); }
.vm-card.vm-reveal:not(.vm-in) { transform: translateY(16px); }
.vm-card-yellow { border-color: rgb(var(--ctp-yellow-rgb) / .3); background: linear-gradient(160deg, rgb(var(--ctp-yellow-rgb) / .08), rgb(var(--ctp-surface0-rgb) / .5)); }
.vm-card-yellow:hover { border-color: rgb(var(--ctp-yellow-rgb) / .5); box-shadow: 0 10px 34px -16px rgb(var(--ctp-yellow-rgb) / .25); }
.vm-card-green { border-color: rgb(var(--ctp-green-rgb) / .3); background: linear-gradient(160deg, rgb(var(--ctp-green-rgb) / .08), rgb(var(--ctp-surface0-rgb) / .5)); }
.vm-card-green:hover { border-color: rgb(var(--ctp-green-rgb) / .5); box-shadow: 0 10px 34px -16px rgb(var(--ctp-green-rgb) / .25); }

/* Type */
.vm-kicker { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; color: var(--ctp-overlay1); margin-bottom: 6px; }
.vm-kicker-yellow { color: var(--ctp-yellow); }
.vm-kicker-green { color: var(--ctp-green); }
.vm-h4 { font-size: 17px; font-weight: 600; color: var(--ctp-text); margin-bottom: 12px; }
.vm-lede { font-size: 15px; line-height: 1.6; color: var(--ctp-text); }
.vm-body { font-size: 12.5px; line-height: 1.65; color: var(--ctp-subtext0); margin-top: 8px; }
.vm-mark { font-weight: 600; }
.vm-mark-yellow { color: var(--ctp-yellow); }
.vm-mark-green { color: var(--ctp-green); }
.vm-foot { font-size: 11px; color: var(--ctp-overlay1); margin-top: 12px; line-height: 1.6; }
.vm-lane-green { color: var(--ctp-green); font-weight: 600; }
.vm-lane-rosewater { color: var(--ctp-rosewater); font-weight: 600; }
.vm-link { color: var(--ctp-sky); text-decoration: underline; text-underline-offset: 2px; }

/* Question chips */
.vm-q { border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-base-rgb) / .45); border-radius: 10px; padding: 10px 12px; transition: border-color .2s ease, transform .2s ease; }
.vm-q p { font-size: 12.5px; line-height: 1.5; color: var(--ctp-subtext0); }
.vm-q-tab { display: inline-block; margin-top: 6px; font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ctp-overlay0); transition: color .2s ease; }
.vm-q:hover { border-color: rgb(var(--ctp-sky-rgb) / .45); transform: translateY(-1px); }
.vm-q:hover .vm-q-tab { color: var(--ctp-sky); }

/* Workflow stepper */
.vm-steps { display: grid; grid-template-columns: 1fr; gap: 8px; margin-bottom: 12px; }
.vm-step { position: relative; overflow: hidden; display: flex; align-items: center; gap: 10px; text-align: left; border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-base-rgb) / .4); border-radius: 10px; padding: 10px 12px; cursor: pointer; transition: border-color .2s ease, background .2s ease; }
.vm-step-num { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 8px; font-size: 12px; font-weight: 700; background: var(--ctp-surface1); color: var(--ctp-subtext0); flex-shrink: 0; transition: background .2s ease, color .2s ease; }
.vm-step-title { font-size: 13px; font-weight: 600; color: var(--ctp-subtext0); transition: color .2s ease; }
.vm-step-active .vm-step-title { color: var(--ctp-text); }
.vm-step-progress { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; transform-origin: left; transform: scaleX(0); background: currentColor; animation: vmProgress 5s linear forwards; }
@keyframes vmProgress { to { transform: scaleX(1); } }
.vm-step-green.vm-step-active { border-color: rgb(var(--ctp-green-rgb) / .5); background: rgb(var(--ctp-green-rgb) / .08); color: var(--ctp-green); }
.vm-step-green.vm-step-active .vm-step-num { background: rgb(var(--ctp-green-rgb) / .25); color: var(--ctp-green); }
.vm-step-lavender.vm-step-active { border-color: rgb(var(--ctp-lavender-rgb) / .5); background: rgb(var(--ctp-lavender-rgb) / .08); color: var(--ctp-lavender); }
.vm-step-lavender.vm-step-active .vm-step-num { background: rgb(var(--ctp-lavender-rgb) / .25); color: var(--ctp-lavender); }
.vm-step-teal.vm-step-active { border-color: rgb(var(--ctp-teal-rgb) / .5); background: rgb(var(--ctp-teal-rgb) / .08); color: var(--ctp-teal); }
.vm-step-teal.vm-step-active .vm-step-num { background: rgb(var(--ctp-teal-rgb) / .25); color: var(--ctp-teal); }
.vm-step-rosewater.vm-step-active { border-color: rgb(var(--ctp-rosewater-rgb) / .5); background: rgb(var(--ctp-rosewater-rgb) / .08); color: var(--ctp-rosewater); }
.vm-step-rosewater.vm-step-active .vm-step-num { background: rgb(var(--ctp-rosewater-rgb) / .25); color: var(--ctp-rosewater); }
.vm-step-panel { border-left: 2px solid var(--ctp-surface2); padding: 2px 0 2px 14px; min-height: 74px; animation: vmPanelIn .45s ease both; }
.vm-step-panel .vm-body { margin-top: 0; font-size: 13px; }
@keyframes vmPanelIn { from { opacity: 0; transform: translateX(8px); } }
.vm-resume { font-size: 11px; font-weight: 600; color: var(--ctp-overlay1); border: 1px solid var(--ctp-surface1); border-radius: 8px; padding: 4px 10px; transition: color .2s ease, border-color .2s ease; }
.vm-resume:hover { color: var(--ctp-text); border-color: var(--ctp-surface2); }

/* Market pulse bars */
.vm-markets { display: flex; flex-direction: column; gap: 7px; margin-top: 4px; }
.vm-market { display: flex; align-items: center; gap: 10px; }
.vm-market-flag { width: 18px; flex-shrink: 0; font-size: 12px; line-height: 1; }
.vm-market-flag img { width: 18px; height: 13px; object-fit: cover; border-radius: 2px; display: block; }
.vm-market-geo { font-size: 11px; font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ctp-subtext0); width: 34px; flex-shrink: 0; }
.vm-bar { flex: 1; height: 8px; border-radius: 999px; background: rgb(var(--ctp-surface1-rgb) / .6); overflow: hidden; }
.vm-bar-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--ctp-sky), var(--ctp-sapphire)); transform-origin: left; transform: scaleX(0); transition: transform 1s cubic-bezier(.22,.61,.36,1); }
.vm-in .vm-bar-fill { transform: scaleX(1); }
.vm-in .vm-market:nth-child(1) .vm-bar-fill { transition-delay: .05s; }
.vm-in .vm-market:nth-child(2) .vm-bar-fill { transition-delay: .12s; }
.vm-in .vm-market:nth-child(3) .vm-bar-fill { transition-delay: .19s; }
.vm-in .vm-market:nth-child(4) .vm-bar-fill { transition-delay: .26s; }
.vm-in .vm-market:nth-child(5) .vm-bar-fill { transition-delay: .33s; }
.vm-in .vm-market:nth-child(6) .vm-bar-fill { transition-delay: .4s; }
.vm-in .vm-market:nth-child(7) .vm-bar-fill { transition-delay: .47s; }
.vm-in .vm-market:nth-child(8) .vm-bar-fill { transition-delay: .54s; }
.vm-in .vm-market:nth-child(9) .vm-bar-fill { transition-delay: .61s; }
.vm-in .vm-market:nth-child(10) .vm-bar-fill { transition-delay: .68s; }
.vm-market-num { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--ctp-subtext0); width: 74px; text-align: right; flex-shrink: 0; }
.vm-market-unit { color: var(--ctp-overlay0); }

/* Personas */
.vm-persona { border: 1px solid var(--ctp-surface1); background: rgb(var(--ctp-base-rgb) / .45); border-radius: 12px; padding: 14px; transition: transform .2s ease, border-color .2s ease, opacity .5s ease; }
.vm-persona:hover { transform: translateY(-2px); }
.vm-persona-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.vm-persona-badge { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 10px; font-size: 11px; font-weight: 800; letter-spacing: .03em; flex-shrink: 0; }
.vm-persona-green .vm-persona-badge { background: rgb(var(--ctp-green-rgb) / .15); color: var(--ctp-green); }
.vm-persona-green:hover { border-color: rgb(var(--ctp-green-rgb) / .4); }
.vm-persona-lavender .vm-persona-badge { background: rgb(var(--ctp-lavender-rgb) / .15); color: var(--ctp-lavender); }
.vm-persona-lavender:hover { border-color: rgb(var(--ctp-lavender-rgb) / .4); }
.vm-persona-teal .vm-persona-badge { background: rgb(var(--ctp-teal-rgb) / .15); color: var(--ctp-teal); }
.vm-persona-teal:hover { border-color: rgb(var(--ctp-teal-rgb) / .4); }
.vm-persona-rosewater .vm-persona-badge { background: rgb(var(--ctp-rosewater-rgb) / .15); color: var(--ctp-rosewater); }
.vm-persona-rosewater:hover { border-color: rgb(var(--ctp-rosewater-rgb) / .4); }
.vm-persona-role { font-size: 13px; font-weight: 600; color: var(--ctp-text); }
.vm-persona-want { font-size: 11px; color: var(--ctp-overlay1); }
.vm-persona-list { display: flex; flex-direction: column; gap: 6px; font-size: 12px; line-height: 1.55; color: var(--ctp-subtext0); }
.vm-persona-list li { padding-left: 14px; position: relative; }
.vm-persona-list li::before { content: ''; position: absolute; left: 0; top: .55em; width: 5px; height: 5px; border-radius: 50%; background: var(--ctp-surface2); }

/* Desktop upgrades */
@media (min-width: 768px) {
  .vm-hero { padding: 36px 32px; }
  .vm-stat-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .vm-steps { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .vm-step { flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px 14px; }
}

/* Reduced motion: everything lands settled, nothing animates */
@media (prefers-reduced-motion: reduce) {
  .vm-hero-blob, .vm-live-dot, .vm-step-progress, .vm-step-panel { animation: none !important; }
  .vm-reveal, .vm-child, .vm-card.vm-reveal:not(.vm-in) { opacity: 1 !important; transform: none !important; transition: none !important; }
  .vm-bar-fill { transform: scaleX(1) !important; transition: none !important; }
  .vm-card, .vm-q, .vm-persona, .vm-stat { transition: none; }
}

/* SCP drawer keyword list — KEYWORD · GEO · VOLUME columns (2026-07-22) */
.scp-det-kw-grid { display: grid; grid-template-columns: minmax(0, 1fr) 56px 64px; gap: 8px; align-items: center; }
.scp-det-kw-head { font-size: 9px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ctp-overlay0); position: sticky; top: 0; background: var(--ctp-mantle); z-index: 1; }

/* ── HOME dashboard (Mark 2026-07-23): minimal navigational block grid. ── */
.home-wrap { max-width: 1060px; margin: 0 auto; padding: 20px 4px 48px; }
.home-hero { position: relative; overflow: hidden; display: flex; align-items: flex-end; gap: 14px;
  margin-bottom: 8px; padding: 26px 24px 22px; border-radius: 16px;
  border: 1px solid color-mix(in oklab, var(--ctp-mauve) 22%, var(--ctp-surface0));
  background: linear-gradient(120deg,
    color-mix(in oklab, var(--ctp-mauve) 14%, var(--ctp-mantle)),
    color-mix(in oklab, var(--ctp-blue) 12%, var(--ctp-mantle)) 55%,
    color-mix(in oklab, var(--ctp-teal) 10%, var(--ctp-mantle))); }
.home-hero-palm { position: absolute; right: -18px; bottom: -34px; width: 220px; height: 220px;
  color: var(--ctp-mauve); opacity: .16; transform: rotate(-6deg); pointer-events: none; }
.home-hero-tx { position: relative; }
.home-hero-title { font-size: 24px; font-weight: 800; color: var(--ctp-text); line-height: 1.15; margin-top: 12px; }
.home-sub { font-size: 13px; color: var(--ctp-overlay1); margin-top: 3px; }
.home-stats { position: relative; margin-left: auto; display: flex; gap: 14px; font-size: 12px; color: var(--ctp-overlay1); }
.home-stats b { color: var(--ctp-text); font-weight: 700; font-variant-numeric: tabular-nums; }
.home-group-label { font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin: 22px 2px 10px; }
.home-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 640px) { .home-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .home-grid { grid-template-columns: repeat(3, 1fr); } }
.home-card { display: flex; align-items: center; gap: 12px; padding: 14px; text-align: left;
  border: 1px solid var(--ctp-surface0); border-top: 3px solid color-mix(in oklab, var(--hc, var(--ctp-blue)) 55%, transparent);
  border-radius: 12px; background: color-mix(in oklab, var(--ctp-mantle) 60%, transparent);
  text-decoration: none; cursor: pointer; transition: border-color .12s ease, transform .12s ease, box-shadow .12s ease; }
.home-card:hover { border-color: color-mix(in oklab, var(--hc, var(--ctp-blue)) 55%, var(--ctp-surface1));
  transform: translateY(-1px); box-shadow: 0 6px 18px -12px var(--hc, var(--ctp-blue)); }
.home-ico { width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  background: color-mix(in oklab, var(--hc, var(--ctp-blue)) 14%, transparent); }
.home-ico svg { width: 22px; height: 22px; }
.home-card-tx { min-width: 0; }
.home-card-tx b { display: block; font-size: 13.5px; font-weight: 700; color: var(--ctp-text); line-height: 1.2; }
.home-card-tx i { display: block; font-style: normal; font-size: 11.5px; color: var(--ctp-overlay1); margin-top: 2px; }
.home-lockup { height: 34px; width: auto; display: block; }

/* Favourites strip: one scrollable row with edge arrows (profile editor). */
.app-fav-scrollwrap { position: relative; }
.app-fav-row { display: flex; gap: 10px; overflow-x: auto; scroll-behavior: smooth;
  padding: 2px; scrollbar-width: none; }
.app-fav-row::-webkit-scrollbar { display: none; }
.app-fav-row .app-grid-item { flex: 0 0 86px; }
.app-fav-arr { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 18px; line-height: 1; color: var(--ctp-text); background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); box-shadow: 0 2px 10px -4px rgba(0,0,0,.35); cursor: pointer; }
.app-fav-arr:hover { border-color: var(--ctp-blue); color: var(--ctp-blue); }
.app-fav-arr-l { left: -6px; }
.app-fav-arr-r { right: -6px; }

/* ── Activity log = contained scroll (Mark 2026-07-23) ──────────────────
   The page body NEVER scrolls on this tab: app sidebar, header, VIEW rail,
   hero, toolbar and the digest summary card all stay pinned; ONLY .al-scroll
   (chart + panels + day-by-day / the changes + commits tables) scrolls —
   the same contract as the Explorer's .wide-table-scroll.
   Desktop only: phones keep natural body scroll (the rail is a drawer). */
@media (min-width: 769px) {
  .al-wrap { height: calc(100vh - var(--lane-tabbar-h, 0px) - 48px); }
  .al-wrap > .flex-1 { display: flex; flex-direction: column; height: 100%; min-height: 0; }
  .al-wrap > .flex-1 > * { flex: 0 0 auto; }
  .al-wrap .al-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
}

/* Home: sticky welcome banner + giant palm backdrop (Mark 2026-07-23). */
.home-wrap { position: relative; }
.home-hero { position: sticky; top: 8px; z-index: 25; }
.home-palm-bg { position: fixed; left: calc(var(--nav-w, 224px) - 30px); bottom: 0; width: 330px; height: 82vh;
  color: var(--ctp-mauve); opacity: .07; z-index: 0; pointer-events: none; }
@media (max-width: 768px) { .home-palm-bg { width: 200px; left: -30px; height: 60vh; } }

/* Home contained scroll (Mark 2026-07-23: banner "SHOULDN'T MOVE UP") —
   hero pinned as real chrome, ONLY the block grid scrolls. Desktop only. */
@media (min-width: 769px) {
  .home-wrap { height: calc(100vh - var(--lane-tabbar-h, 0px) - 48px); display: flex; flex-direction: column; overflow: hidden; }
  .home-hero { position: relative; top: auto; flex: 0 0 auto; }
  .home-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-bottom: 24px; }
}
@media (max-width: 768px) { .home-hero { position: relative; top: auto; } }

/* ── Notifications bell (Mark 2026-07-23): everyone's latest activity in the
   header, right before the apps launcher. Badge on the bell; the
   panel floats like the apps launcher and only its list scrolls. ── */
.notif-dot { position: absolute; top: 2px; right: 1px; min-width: 15px; height: 15px;
  padding: 0 3px; border-radius: 999px; background: var(--ctp-red); color: #fff;
  font-size: 9px; font-weight: 800; line-height: 15px; text-align: center;
  box-shadow: 0 0 0 2px var(--ctp-base); }
.notif-panel { position: fixed; top: calc(var(--brand-header-h, 77px) - 4px); right: 14px;
  z-index: 151; width: 372px; max-width: calc(100vw - 20px); max-height: min(70vh, 540px);
  display: flex; flex-direction: column; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 18px;
  box-shadow: 0 18px 50px -12px rgba(0,0,0,.35); overflow: hidden; }
.notif-head { display: flex; align-items: center; gap: 8px; padding: 12px 14px 10px;
  font-size: 13.5px; color: var(--ctp-text); border-bottom: 1px solid var(--ctp-surface0); }
.notif-list { overflow-y: auto; overscroll-behavior: contain; flex: 1 1 auto; }
.notif-item { display: flex; align-items: flex-start; gap: 10px; padding: 9px 14px;
  font-size: 12px; border-bottom: 1px solid color-mix(in srgb, var(--ctp-surface0) 55%, transparent); }
.notif-item:last-child { border-bottom: 0; }
.notif-fresh { background: color-mix(in srgb, var(--ctp-peach) 6%, transparent); }
.notif-txt { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Action glyph + the topic line under it: a notification has to say what it
   happened TO, not just what happened (Mark 2026-07-28). */
.notif-ico { display: inline-block; width: 13px; margin-right: 3px; font-size: 11px; text-align: center; }
.notif-where { margin-top: 2px; font-size: 10.5px; font-weight: 600; color: var(--ctp-lavender);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.notif-go { margin-left: 6px; font-weight: 700; color: var(--ctp-blue); opacity: 0; transition: opacity .12s; }
.notif-item-go:hover .notif-go { opacity: 1; }
.notif-ava { width: 24px; height: 24px; border-radius: 999px; object-fit: cover;
  flex-shrink: 0; margin-top: 1px; }
.notif-ava-fb { display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--ctp-peach) 15%, transparent); color: var(--ctp-peach);
  font-size: 11px; font-weight: 800; }
.notif-foot { display: block; text-align: center; padding: 9px 14px; font-size: 11px;
  font-weight: 600; color: var(--ctp-blue); border-top: 1px solid var(--ctp-surface0);
  text-decoration: none; }
.notif-foot:hover { background: var(--ctp-surface0); }

/* ── Profile sections (Mark 2026-07-23): chevron collapse toggles per card +
   a tighter favourites row — the launcher-panel tile size read as huge,
   airy gaps at page width. ── */
.prof-sec-chev { width: 22px; height: 22px; border-radius: 6px; display: inline-flex;
  align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer;
  color: var(--ctp-overlay1); font-size: 12px; line-height: 1;
  transition: transform .15s ease, color .12s ease; }
.prof-sec-chev:hover { color: var(--ctp-text); }
.prof-sec-chev.closed { transform: rotate(-90deg); }
#prof-fav-row { gap: 6px; }
#prof-fav-row .app-grid-item { flex: 0 0 66px; padding: 8px 4px 7px; gap: 5px; border-radius: 12px; }
#prof-fav-row .app-grid-ico { width: 32px; height: 32px; border-radius: 9px; }
#prof-fav-row .app-grid-name { font-size: 10px; }

/* ═══ Header chrome pass — Mark 2026-07-25, built from his Slack + Google Suite
   reference captures: (1) the universal search box's flat dropdown becomes a
   GROUPED panel with a keyboard cursor and a Recent list, (2) a Create (+) menu,
   (3) toasts get action buttons + an explicit ✕ like Gmail's notification bar. ═══ */

/* ── (1) Search panel ── */
.uni-panel { position: absolute; right: 0; top: 100%; margin-top: 8px; z-index: 50;
  width: 384px; max-width: calc(100vw - 24px); max-height: min(70vh, 460px);
  overflow-y: auto; overscroll-behavior: contain; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 14px;
  box-shadow: 0 18px 50px -12px rgba(0,0,0,.35); padding: 6px 0 0; }
.uni-group-head { display: flex; align-items: center; gap: 8px; padding: 8px 14px 4px;
  font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ctp-overlay0); }
.uni-clear { margin-left: auto; font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ctp-overlay0); cursor: pointer; }
.uni-clear:hover { color: var(--ctp-red); }
.uni-row { display: flex; align-items: center; gap: 9px; padding: 7px 14px; cursor: pointer; }
/* the ↑/↓ cursor is a FILL, hover is a lighter wash — so a moused-over row never
   reads as the one Enter would open */
.uni-row-on { background: color-mix(in srgb, var(--ctp-lavender) 16%, transparent);
  box-shadow: inset 2px 0 0 var(--ctp-lavender); }
.uni-row:hover { background: var(--ctp-surface0); }
.uni-row-on:hover { background: color-mix(in srgb, var(--ctp-lavender) 22%, transparent); }
.uni-ico { width: 15px; height: 15px; flex-shrink: 0; color: var(--ctp-overlay1); }
.uni-ico-img { width: 15px; height: 15px; border-radius: 3px; flex-shrink: 0; object-fit: contain; }
/* Injected lane-tab icons ship with Tailwind's w-3.5 baked in (14px, sized for
   the tab bar) — !important because that utility class wins on specificity ties. */
.uni-ico-tab { display: inline-flex; flex-shrink: 0; }
.uni-ico-tab svg { width: 16px !important; height: 16px !important; }
.uni-ico-em { width: 15px; flex-shrink: 0; font-size: 13px; line-height: 1; text-align: center; }
.uni-row-label { font-size: 13px; color: var(--ctp-text); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.uni-row-sub { margin-left: auto; flex-shrink: 0; font-size: 9.5px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ctp-overlay0); }
.uni-foot { position: sticky; bottom: 0; display: flex; align-items: center; gap: 14px;
  padding: 7px 14px; margin-top: 4px; background: var(--ctp-base);
  border-top: 1px solid var(--ctp-surface0); font-size: 10px; color: var(--ctp-overlay0); }
.uni-foot b { color: var(--ctp-subtext0); font-weight: 700; }

/* ── (2) Create (+) menu ── */
.create-btn { display: inline-flex; align-items: center; gap: 6px; height: 36px;
  padding: 0 12px 0 10px; border-radius: 999px; flex-shrink: 0; cursor: pointer;
  font-size: 12.5px; font-weight: 700; color: var(--ctp-text);
  background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1);
  transition: background .15s ease, border-color .15s ease, color .15s ease; }
.create-btn:hover { border-color: var(--ctp-lavender); color: var(--ctp-lavender); }
.create-btn-on { background: color-mix(in srgb, var(--ctp-lavender) 16%, transparent);
  border-color: var(--ctp-lavender); color: var(--ctp-lavender); }
.create-panel { position: fixed; top: calc(var(--brand-header-h, 77px) - 4px); right: 14px;
  z-index: 151; width: 344px; max-width: calc(100vw - 20px); background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 18px; overflow: hidden;
  box-shadow: 0 18px 50px -12px rgba(0,0,0,.35); padding: 6px 0 0; }
.create-head { padding: 8px 16px; font-size: 14px; font-weight: 800; color: var(--ctp-text); }
.create-row { display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px 16px;
  text-align: left; cursor: pointer; }
.create-row:hover { background: var(--ctp-surface0); }
.create-tile { width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center; }
/* same !important note as .uni-ico-tab — the injected svg carries w-3.5 */
.create-tile svg { width: 19px !important; height: 19px !important; }
.create-tile-lavender { background: color-mix(in srgb, var(--ctp-lavender) 18%, transparent); color: var(--ctp-lavender); }
.create-tile-mauve    { background: color-mix(in srgb, var(--ctp-mauve) 18%, transparent);    color: var(--ctp-mauve); }
.create-tile-green    { background: color-mix(in srgb, var(--ctp-green) 18%, transparent);    color: var(--ctp-green); }
.create-tile-peach    { background: color-mix(in srgb, var(--ctp-peach) 18%, transparent);    color: var(--ctp-peach); }
.create-tile-blue     { background: color-mix(in srgb, var(--ctp-blue) 18%, transparent);     color: var(--ctp-blue); }
.create-tile-teal     { background: color-mix(in srgb, var(--ctp-teal) 18%, transparent);     color: var(--ctp-teal); }
.create-row-txt { display: flex; flex-direction: column; min-width: 0; }
.create-row-t { font-size: 13px; font-weight: 700; color: var(--ctp-text); }
.create-row-d { font-size: 11px; color: var(--ctp-overlay1); }
.create-foot { display: flex; align-items: center; gap: 8px; padding: 10px 16px; margin-top: 4px;
  border-top: 1px solid var(--ctp-surface0); font-size: 11.5px; font-weight: 600;
  color: var(--ctp-blue); text-decoration: none; }
.create-foot:hover { background: var(--ctp-surface0); }
/* Narrow headers drop the word and keep the + as a round icon button — same
   place, same tooltip (the header already sheds the ⌘K hint and stats here). */
@media (max-width: 1180px) {
  .create-btn { width: 36px; padding: 0; justify-content: center; }
  .create-btn-txt { display: none; }
}

/* Mobile: the Create panel becomes a BOTTOM SHEET (the top-anchored panel is
   wider than the phone header, so it covered the very + button that opened it),
   and the search panel goes edge-to-edge under the header. */
/* 768px = the breakpoint where the sidebar becomes .mobile-tabbar (58px tall,
   fixed at the bottom) — the sheet has to sit ABOVE that bar, not behind it. */
@media (max-width: 768px) {
  /* EVERY header pop-up is a bottom sheet on phones — the bell, the apps grid
     and the ? menu used to inherit the desktop `top: var(--brand-header-h)`.
     On a phone the sidebar (whose logo block that variable measures) is a
     drawer, so it measures ~0 and all three panels opened pinned off the top
     of the screen, behind the header. Same sheet treatment as Create. */
  .create-panel, .notif-panel, .app-grid-panel, .help-panel {
    top: auto; bottom: var(--mobile-tabbar-h, 58px);
    left: 0; right: 0; width: auto; max-width: none;
    max-height: calc(100vh - 150px); max-height: calc(100dvh - 150px);
    overflow-y: auto; overscroll-behavior: contain;
    border-radius: 18px 18px 0 0; padding-bottom: 6px;
    box-shadow: 0 -14px 40px -12px rgba(0,0,0,.35); }
  /* .notif-panel is a flex column with its own inner scroller — it must not
     become a scroll container itself or the list scrolls twice. */
  .notif-panel { overflow: hidden; padding-bottom: 0; }
  .uni-panel { position: fixed; top: calc(var(--lane-tabbar-h, 52px) + 6px);
    left: 8px; right: 8px; width: auto; max-width: none; margin-top: 0; }
  /* clear the bottom bar so a toast never lands on the phone nav */
  .toast-container { bottom: calc(66px + env(safe-area-inset-bottom)); }
}

/* ── (3) Toasts, Gmail-style ── */
.toast { background: var(--ctp-crust); border-color: var(--ctp-surface0);
  padding: 10px 10px 10px 14px; gap: 8px; max-width: 460px; }
/* NO flex-wrap: the actions and ✕ must stay on the message's row (wrapping
   dropped "Undo ✕" onto a second line, reading like a separate toast). The
   message itself is free to wrap over lines inside its own box. */
.toast-msg { flex: 1 1 auto; min-width: 0; color: var(--ctp-text); }
.toast-act { flex-shrink: 0; padding: 4px 8px; border-radius: 6px; cursor: pointer;
  font-size: 12px; font-weight: 700; white-space: nowrap; text-decoration: none;
  color: var(--ctp-sky); background: transparent; }
.toast-act:hover { background: color-mix(in srgb, var(--ctp-sky) 14%, transparent); }
.toast-act-quiet { color: var(--ctp-overlay1); }
.toast-act-quiet:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
/* Progress toast (long exports). The bar hugs the toast's bottom edge rather than
   joining the flex row, which would wrap the actions onto a second line. */
.toast { position: relative; }
.toast-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--ctp-surface1); border-radius: 0 0 var(--radius-md) var(--radius-md);
  overflow: hidden; }
.toast-bar > span { display: block; height: 100%; width: 0;
  background: var(--ctp-green); transition: width .25s ease; }
.toast-x { flex-shrink: 0; width: 24px; height: 24px; border-radius: 6px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; color: var(--ctp-overlay0); }
.toast-x:hover { background: var(--ctp-surface0); color: var(--ctp-text); }

/* ── Help & feedback menu (Mark 2026-07-25, Gmail help-menu reference) ──
   Deliberately plainer than the Create menu: Google's help menu is label-only,
   so rows get a small tone dot instead of an icon tile. */
.help-btn-on { background: color-mix(in srgb, var(--ctp-blue) 14%, transparent); color: var(--ctp-blue); }
.help-panel { position: fixed; top: calc(var(--brand-header-h, 77px) - 4px); right: 14px;
  z-index: 151; width: 268px; max-width: calc(100vw - 20px); background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 16px; overflow: hidden;
  box-shadow: 0 18px 50px -12px rgba(0,0,0,.35); padding: 6px 0; }
.help-head { padding: 8px 16px 8px; font-size: 13.5px; font-weight: 800; color: var(--ctp-text); }
.help-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 16px;
  text-align: left; cursor: pointer; font-size: 13px; color: var(--ctp-subtext1); }
.help-row:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.help-row-fb { margin-top: 4px; border-top: 1px solid var(--ctp-surface0);
  padding-top: 11px; font-weight: 650; color: var(--ctp-blue); }
.help-row-fb:hover { color: var(--ctp-blue); }
.help-ico { display: inline-flex; flex-shrink: 0; }
.help-ico svg { width: 17px; height: 17px; }

/* Shared shell for the two help dialogs (shortcuts + feedback). */
.help-modal { width: 440px; max-width: 100%; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface1); border-radius: 16px; overflow: hidden;
  box-shadow: 0 24px 60px -16px rgba(0,0,0,.45); }
.help-modal-head { display: flex; align-items: center; padding: 12px 16px;
  border-bottom: 1px solid var(--ctp-surface0); font-size: 14px; font-weight: 800;
  color: var(--ctp-text); }
.help-modal-x { margin-left: auto; width: 26px; height: 26px; border-radius: 7px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; color: var(--ctp-overlay0); }
.help-modal-x:hover { background: var(--ctp-surface0); color: var(--ctp-red); }
.help-keys { padding: 6px 8px 10px; }
.help-key-row { display: flex; align-items: center; gap: 12px; padding: 8px 8px; }
.help-key-row + .help-key-row { border-top: 1px solid color-mix(in srgb, var(--ctp-surface0) 60%, transparent); }
.help-key { flex: 0 0 96px; display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 6px; border-radius: 7px; background: var(--ctp-surface0);
  border: 1px solid var(--ctp-surface1); font-size: 11.5px; font-weight: 700;
  color: var(--ctp-text); white-space: nowrap; }
.help-key-what { font-size: 12.5px; color: var(--ctp-subtext0); }
.help-fb { padding: 14px 16px 16px; }
.help-fb-hint { font-size: 12px; color: var(--ctp-subtext0); margin-bottom: 10px; }
.help-fb-box { width: 100%; resize: vertical; padding: 10px 12px; border-radius: 10px;
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1);
  font-size: 13px; color: var(--ctp-text); font-family: inherit; }
.help-fb-box:focus { outline: none; border-color: var(--ctp-blue); }
.help-fb-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.help-fb-count { font-size: 10.5px; color: var(--ctp-overlay0); margin-right: auto; }
.help-fb-cancel { padding: 7px 12px; border-radius: 8px; font-size: 12.5px; font-weight: 650;
  color: var(--ctp-overlay1); cursor: pointer; }
.help-fb-cancel:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.help-fb-send { padding: 7px 16px; border-radius: 8px; font-size: 12.5px; font-weight: 750;
  color: var(--ctp-base); background: var(--ctp-blue); cursor: pointer; }
.help-fb-send:hover { filter: brightness(1.06); }
.help-fb-send:disabled { opacity: .55; cursor: default; }

/* Avatar framer (Mark 2026-07-28) — drag-to-position crop before the photo is
   saved. The viewport is a FIXED 240px square in both breakpoints because the
   drag/crop maths is done in those units (see AVA_VP in app.js) — resizing it
   responsively would need the element measured on every pointer move. */
.ava-crop { padding: 14px 16px 16px; }
.ava-crop-hint { font-size: 12px; color: var(--ctp-subtext0); margin-bottom: 10px; }
.ava-crop-vp { position: relative; width: 240px; height: 240px; margin: 0 auto;
  overflow: hidden; background: var(--ctp-crust); border: 1px solid var(--ctp-surface1);
  border-radius: 12px; cursor: grab; touch-action: none; user-select: none; }
.ava-crop-vp.is-dragging { cursor: grabbing; }
.ava-crop-img { position: absolute; left: 50%; top: 50%; max-width: none;
  pointer-events: none; -webkit-user-drag: none; }
/* Circle preview: everything outside the circle dims, so what you see inside is
   exactly what the round avatar will show. */
.ava-crop-mask { position: absolute; inset: 0; pointer-events: none;
  background: rgb(0 0 0 / .52);
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 0 118px, #000 119px);
  mask: radial-gradient(circle at 50% 50%, transparent 0 118px, #000 119px); }
/* Ring sits on the viewport, not on the mask — a mask that punches the circle out
   would eat half the border with it. */
.ava-crop-vp::after { content: ''; position: absolute; left: 50%; top: 50%;
  width: 236px; height: 236px; transform: translate(-50%, -50%);
  border-radius: 50%; border: 1px solid rgb(255 255 255 / .38); pointer-events: none; }
.ava-crop-zoom { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.ava-crop-zlab { font-size: 11px; font-weight: 650; color: var(--ctp-overlay1); }
.ava-crop-range { flex: 1; height: 4px; -webkit-appearance: none; appearance: none;
  background: var(--ctp-surface1); border-radius: 2px; cursor: pointer; }
.ava-crop-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%; background: var(--ctp-peach);
  border: 2px solid var(--ctp-base); cursor: grab; }
.ava-crop-range::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%;
  background: var(--ctp-peach); border: 2px solid var(--ctp-base); cursor: grab; }
.ava-crop-reset { font-size: 11px; font-weight: 650; color: var(--ctp-overlay1);
  padding: 4px 8px; border-radius: 7px; cursor: pointer; }
.ava-crop-reset:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.ava-crop-err { font-size: 12px; color: var(--ctp-red); line-height: 1.45; }
.ava-crop-btn { padding: 7px 14px; border-radius: 8px; font-size: 12.5px; font-weight: 650;
  color: var(--ctp-overlay1); cursor: pointer; margin-left: auto; }
.ava-crop-btn:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.ava-crop-btn.is-go { margin-left: 0; font-weight: 750; color: var(--ctp-base);
  background: var(--ctp-peach); }
.ava-crop-btn.is-go:hover { background: var(--ctp-peach); filter: brightness(1.06); }
.ava-crop-btn.is-go:disabled { opacity: .55; cursor: default; }

/* Profile identity card (Mark 2026-07-28: "make this page a bit nicer, space
   everything out") — avatar rail with its own Add/Crop buttons, details as
   spaced rows. Mobile-first: the rail is a full-width row until 640px, where it
   becomes the left column. */
.pid { border: 1px solid var(--ctp-surface0); border-radius: 14px;
  background: var(--ctp-base); overflow: hidden; display: flex; flex-wrap: wrap; }
.pid-rail { width: 100%; padding: 22px; text-align: center;
  background: linear-gradient(180deg, rgb(var(--ctp-peach-rgb) / .09), transparent);
  border-bottom: 1px solid var(--ctp-surface0); }
.pid-ava { width: 96px; height: 96px; border-radius: 50%; overflow: hidden; margin: 0 auto;
  border: 1px solid var(--ctp-surface1); background: var(--ctp-surface0); }
.pid-ava img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pid-ava-fb { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  font-size: 24px; font-weight: 800; color: var(--ctp-peach);
  background: rgb(var(--ctp-peach-rgb) / .15); }
.pid-rail-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px;
  max-width: 190px; margin-left: auto; margin-right: auto; }
.pid-body { flex: 1; min-width: 240px; padding: 22px; }
.pid-eyebrow { font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ctp-overlay0); }
.pid-name { font-size: 21px; font-weight: 800; color: var(--ctp-text); line-height: 1.15; margin-top: 3px; }
.pid-pill { display: inline-flex; align-items: center; gap: 6px; margin-top: 10px;
  padding: 4px 10px; border-radius: 999px; font-size: 10.5px; font-weight: 750;
  background: rgb(var(--ctp-green-rgb) / .14); color: var(--ctp-green); }
.pid-pill.is-view { background: var(--ctp-surface0); color: var(--ctp-overlay1); }
.pid-pill .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.pid-rows { margin-top: 20px; }
.pid-row { display: flex; gap: 20px; padding: 14px 0; flex-wrap: wrap;
  border-top: 1px solid var(--ctp-surface0); }
.pid-k { width: 108px; flex-shrink: 0; font-size: 11px; font-weight: 700; color: var(--ctp-overlay1); }
.pid-v { flex: 1; min-width: 0; font-size: 13px; color: var(--ctp-text); word-break: break-word; }
.pid-foot { display: flex; justify-content: flex-end; margin-top: 18px;
  padding-top: 16px; border-top: 1px solid var(--ctp-surface0); }
.pid-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 32px; padding: 0 13px; border-radius: 9px; font-size: 11.5px; font-weight: 700;
  border: 1px solid var(--ctp-surface1); color: var(--ctp-subtext0);
  background: var(--ctp-base); cursor: pointer; }
.pid-btn:hover { border-color: var(--ctp-peach); color: var(--ctp-text); }
.pid-btn.is-primary { border-color: transparent; background: var(--ctp-peach); color: var(--ctp-base); }
.pid-btn.is-primary:hover { filter: brightness(1.06); }
.pid-btn.is-quiet { border-color: var(--ctp-surface0); color: var(--ctp-overlay1); font-weight: 650; }
.pid-btn svg { width: 13px; height: 13px; flex-shrink: 0; }
@media (min-width: 640px) {
  .pid-rail { width: 208px; padding: 26px 22px; border-bottom: 0;
    border-right: 1px solid var(--ctp-surface0);
    background: linear-gradient(180deg, rgb(var(--ctp-peach-rgb) / .09), transparent); }
  .pid-body { padding: 26px; }
}

/* Phone: both dialogs go near-full-width and the menu clears the bottom bar. */
@media (max-width: 768px) {
  .help-modal { width: 100%; }
  /* Bottom sheet, same as the other header pop-ups. Repeated here (not only in
     the shared group above) because the .help-panel base rule is declared
     AFTER that block and would win the cascade on `top`/`right`/`width`. */
  .help-panel { top: auto; bottom: var(--mobile-tabbar-h, 58px);
    left: 0; right: 0; width: auto; max-width: none;
    max-height: calc(100vh - 150px); max-height: calc(100dvh - 150px);
    overflow-y: auto; overscroll-behavior: contain;
    border-radius: 18px 18px 0 0; padding-bottom: 6px;
    box-shadow: 0 -14px 40px -12px rgba(0,0,0,.35); }
}

/* ══ Feedback board — /feedback (Mark 2026-07-25). Everyone reads it; admins
   move cards. Same visual language as the Content Plan board, own classes so
   the two can drift apart without breaking each other. ══ */
.fb-hero { display: flex; align-items: flex-start; gap: 18px; flex-wrap: wrap;
  padding: 16px 18px; margin-bottom: 14px; border-radius: 16px;
  background: color-mix(in srgb, var(--ctp-peach) 7%, var(--ctp-base));
  border: 1px solid color-mix(in srgb, var(--ctp-peach) 24%, transparent); }
.fb-hero-kick { font-size: 10px; font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ctp-peach); }
.fb-hero-h1 { font-size: 22px; font-weight: 800; color: var(--ctp-text);
  font-family: var(--font-display); margin-top: 2px; }
.fb-hero-sub { font-size: 12.5px; color: var(--ctp-subtext0); margin-top: 4px; max-width: 62ch; }
.fb-hero-side { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.fb-hero-stat { font-size: 11.5px; color: var(--ctp-subtext0); }
.fb-hero-stat b { color: var(--ctp-text); font-weight: 800; font-variant-numeric: tabular-nums; }
.fb-hero-btn { padding: 8px 14px; border-radius: 999px; cursor: pointer;
  font-size: 12.5px; font-weight: 750; color: var(--ctp-base); background: var(--ctp-peach); }
.fb-hero-btn:hover { filter: brightness(1.05); }
.fb-hero-note { font-size: 10.5px; color: var(--ctp-overlay0); }
.fb-err { padding: 10px 14px; border-radius: 10px; font-size: 12.5px; margin-bottom: 12px;
  color: var(--ctp-red); background: color-mix(in srgb, var(--ctp-red) 10%, transparent); }
.fb-err-quiet { color: var(--ctp-overlay1); background: var(--ctp-surface0); }
/* height (not max-height) — the columns' max-height:100% only resolves against a
   definite height; with max-height the lanes grow past the clip and cards below
   the fold become unreachable (same bug the Content Plan board hit). */
.fb-board { display: flex; gap: 12px; align-items: flex-start;
  height: calc(100vh - var(--lane-tabbar-h, 52px) - 210px); min-height: 320px; }
.fb-col { flex: 1 1 0; min-width: 240px; height: 100%; display: flex; flex-direction: column;
  border-radius: 14px; background: var(--ctp-mantle); border: 1px solid var(--ctp-surface0);
  padding: 10px 10px 8px; }
.fb-col-live { border-style: dashed; border-color: var(--ctp-surface2); }
.fb-col-head { display: flex; align-items: center; gap: 7px; padding: 2px 4px 9px; }
.fb-col-dot { width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0; }
.fb-dot-blue  { background: var(--ctp-blue); }
.fb-dot-peach { background: var(--ctp-peach); }
.fb-dot-green { background: var(--ctp-green); }
.fb-dot-grey  { background: var(--ctp-overlay0); }
.fb-col-title { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ctp-subtext0); }
.fb-col-count { margin-left: auto; font-size: 11px; font-weight: 700; color: var(--ctp-overlay1);
  background: var(--ctp-surface0); border-radius: 999px; padding: 1px 8px; font-variant-numeric: tabular-nums; }
.fb-cards { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 8px; }
/* Bigger and calmer (Mark 2026-07-27): heading, the three chips, then who and
   when in the bottom-right. Nothing else — the description and the screenshots
   are what opening the ticket is for. */
.fb-card { background: var(--ctp-base); border: 1px solid var(--ctp-surface0);
  border-radius: 10px; padding: 13px 14px; box-shadow: 0 1px 1px rgb(0 0 0 / .05);
  transition: box-shadow .12s, border-color .12s; }
.fb-card:hover { border-color: var(--ctp-surface2); box-shadow: 0 2px 6px rgb(0 0 0 / .09); }
.fb-card-dragging { opacity: .5; }
.fb-card-msg { font-size: 12.5px; line-height: 1.45; color: var(--ctp-text); white-space: pre-wrap; }
.fb-card-reason { margin-top: 7px; padding: 6px 8px; border-radius: 7px; font-size: 11.5px;
  color: var(--ctp-subtext0); background: var(--ctp-surface0); }
.fb-card-reason b { color: var(--ctp-overlay1); font-weight: 700; }
.fb-card-foot { display: flex; align-items: flex-end; gap: 8px; margin-top: 12px; flex-wrap: nowrap;
  font-size: 10.5px; color: var(--ctp-overlay1); min-width: 0; }
.fb-card-ava { width: 18px; height: 18px; border-radius: 999px; object-fit: cover; flex-shrink: 0; }
.fb-card-ava-fb { display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--ctp-peach) 16%, transparent); color: var(--ctp-peach);
  font-size: 9.5px; font-weight: 800; }
.fb-card-who { font-weight: 650; color: var(--ctp-subtext0); white-space: nowrap; flex-shrink: 0; }
.fb-card-page { padding: 1px 6px; border-radius: 5px; background: var(--ctp-surface0);
  max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* who, and the day/time under them, hard right */
.fb-card-by { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end;
  gap: 2px; min-width: 0; }
.fb-card-by-top { display: flex; align-items: center; gap: 5px; min-width: 0; }
.fb-card-when { flex-shrink: 0; font-size: 10.5px; color: var(--ctp-overlay1); white-space: nowrap; }
.fb-move { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px;
  padding-top: 8px; border-top: 1px solid color-mix(in srgb, var(--ctp-surface0) 70%, transparent); }
.fb-move-btn { padding: 3px 8px; border-radius: 6px; cursor: pointer;
  font-size: 10.5px; font-weight: 700; color: var(--ctp-overlay1); background: var(--ctp-surface0); }
.fb-move-blue:hover  { background: color-mix(in srgb, var(--ctp-blue) 18%, transparent);  color: var(--ctp-blue); }
.fb-move-peach:hover { background: color-mix(in srgb, var(--ctp-peach) 18%, transparent); color: var(--ctp-peach); }
.fb-move-green:hover { background: color-mix(in srgb, var(--ctp-green) 18%, transparent); color: var(--ctp-green); }
.fb-move-grey:hover  { background: var(--ctp-surface1); color: var(--ctp-text); }
/* ── The opened ticket (Mark 2026-07-27) ────────────────────────────────────
   Deliberately built out of patterns that already exist elsewhere in Oasis
   rather than a third card style: the banner IS a .tab-hero (accent gradient,
   .14em eyebrow, watermark icon), and the rail boxes borrow .scp-card's edge and
   hover. Tokens only, so a theme change reaches this page for free. */
.fb-tk-wrap { position: fixed; inset: 0; z-index: 205; display: flex;
  align-items: center; justify-content: center; padding: 22px;
  background: rgb(0 0 0 / .5); backdrop-filter: blur(2px); }
.fb-tk { width: min(1060px, 100%); max-height: min(90vh, 980px); display: flex; flex-direction: column;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg); overflow: hidden;
  animation: fb-tk-in .16s ease-out; }
.fb-tk:focus { outline: none; }
@keyframes fb-tk-in { from { opacity: 0; transform: translateY(6px) scale(.995); } }
@media (prefers-reduced-motion: reduce) { .fb-tk { animation: none; } }
.fb-tk-id { font-weight: 800; color: var(--ctp-overlay0); font-variant-numeric: tabular-nums;
  letter-spacing: .02em; }
.fb-tk-lane { display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 999px; font-size: 10px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .05em; background: var(--ctp-surface0); color: var(--ctp-subtext0); }
.fb-tk-lane .fb-col-dot { width: 6px; height: 6px; }
/* Actions live in the banner now, top-right, aligned with the eyebrow. */
.fb-tk-acts { position: relative; z-index: 2; display: flex; align-items: center; gap: 4px; flex-shrink: 0; margin: -3px -4px 0 6px; }
.fb-tk-act { display: inline-flex; align-items: center; gap: 5px; height: 28px; padding: 0 10px;
  border-radius: 7px; cursor: pointer; font-size: 11px; font-weight: 700;
  color: var(--ctp-subtext0); }
.fb-tk-act:hover { color: var(--ctp-text); background: var(--ctp-surface0); }
.fb-tk-x { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 7px;
  cursor: pointer; color: var(--ctp-overlay1); flex-shrink: 0; }
.fb-tk-x:hover { color: var(--ctp-text); background: var(--ctp-surface0); }
/* Every interactive thing in here shows the keyboard where it is. */
.fb-tk-act:focus-visible, .fb-tk-x:focus-visible, .fb-tk-lane-go:focus-visible,
.fb-tk-shot:focus-visible, .fb-mention-row:focus-visible, .fb-card:focus-visible {
  outline: 2px solid var(--ctp-blue); outline-offset: 2px; }

.fb-tk-body { display: grid; grid-template-columns: minmax(0, 1fr) 304px; gap: 0 22px;
  padding: 0 18px 20px; overflow-y: auto; align-content: start; }
.fb-tk-main { min-width: 0; grid-column: 1; }
/* ── The header: one banner, the .tab-hero recipe, tinted by KIND ──────────
   It is the modal's top chrome AND its hero, so it pins while the body scrolls
   (the house rule: banners and top parts are always sticky). Negative margins
   let it sit flush to the modal edge while the body keeps its padding. */
/* A BOX, not a full-bleed strip (Mark 2026-07-27: "just want it to be a box…
   closer to the h1 text"). It still pins, so the gradient is layered over the
   base colour — a transparent sticky element would let the thread scroll
   through it. */
.fb-tk .tab-hero.fb-tk-hero { position: sticky; top: 12px; z-index: 4; min-height: 0;
  grid-column: 1 / -1; display: flex; flex-direction: row; align-items: flex-start; gap: 11px;
  margin: 14px 0 18px; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--ctp-surface1);
  background:
    linear-gradient(100deg,
      color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 15%, var(--ctp-surface0)) 0%,
      color-mix(in oklab, var(--hero-ac, var(--ctp-teal)) 4%, var(--ctp-surface0)) 55%,
      var(--ctp-surface0) 100%),
    var(--ctp-base); }
.fb-tk-wm { right: -14px; bottom: -22px; width: 92px; height: 92px; opacity: .07; }
.fb-tk-wm svg { width: 92px !important; height: 92px !important; }
.fb-tk-hero-ico { position: relative; z-index: 1; flex-shrink: 0; width: 34px; height: 34px;
  display: grid; place-items: center; border-radius: 9px; background: var(--ctp-base);
  border: 1px solid color-mix(in oklab, var(--hero-ac, var(--ctp-surface1)) 40%, transparent);
  box-shadow: 0 1px 2px rgb(0 0 0 / .05); }
.fb-tk-hero-ico svg { width: 22px !important; height: 22px !important; }
.fb-tk-hero-red      { --hero-ac: var(--ctp-red); }
.fb-tk-hero-peach    { --hero-ac: var(--ctp-peach); }
.fb-tk-hero-lavender { --hero-ac: var(--ctp-lavender); }
.fb-tk-hero-yellow   { --hero-ac: var(--ctp-yellow); }
.fb-tk-hero-sapphire { --hero-ac: var(--ctp-sapphire); }
.fb-tk-hero-green    { --hero-ac: var(--ctp-green); }
.fb-tk-hero-grey     { --hero-ac: var(--ctp-overlay0); }
/* The heading now leads the banner, so it gets the top margin the eyebrow had. */
.fb-tk .tab-hero.fb-tk-hero .fb-tk-h1 { margin-top: 1px; }
.fb-tk-kick-dot { color: color-mix(in oklab, var(--hero-ac, var(--ctp-overlay0)) 45%, transparent); }
.fb-tk-h1 { font-size: 20px; line-height: 1.28; font-weight: 800; color: var(--ctp-text); letter-spacing: -.012em; }
.fb-tk-h1-edit { width: 100%; font-size: 20px; line-height: 1.28; font-weight: 800; letter-spacing: -.012em;
  color: var(--ctp-text); background: var(--ctp-base); padding: 5px 9px;
  border: 1px solid var(--ctp-blue); border-radius: var(--radius-sm); }
.fb-tk-h1-edit:focus { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--ctp-blue) 22%, transparent); }
.fb-tk-hero-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 12px; margin-top: 6px; }
.fb-tk-hero-m { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--ctp-subtext0); }
.fb-tk-hero-m .fb-cmt-ava { width: 18px; height: 18px; }
.fb-tk-edited { font-style: italic; color: var(--ctp-overlay1); }
/* ── Sections in the story column ────────────────────────────────────────── */
.fb-tk-sec { margin-bottom: 22px; }
.fb-tk-sec-h { display: flex; align-items: center; gap: 7px; margin-bottom: 9px;
  font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: var(--ctp-subtext0); }
.fb-tk-sec-ico { display: inline-flex; align-items: center; flex-shrink: 0; width: 15px; height: 15px; }
.fb-tk-sec-ico svg { width: 15px !important; height: 15px !important; }
/* A measure, not a full-bleed line: long descriptions have to stay readable. */
.fb-tk-msg { max-width: 68ch; font-size: 13.5px; line-height: 1.65; color: var(--ctp-subtext1); white-space: pre-wrap; }
.fb-tk-shots { display: flex; flex-wrap: wrap; gap: 9px; }
.fb-tk-shot { border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--ctp-surface1);
  cursor: zoom-in; transition: border-color .12s, transform .12s; }
.fb-tk-shot:hover { border-color: var(--ctp-surface2); transform: translateY(-1px); }
.fb-tk-shot img { display: block; width: 148px; height: 100px; object-fit: cover; }
.fb-tk-decided { display: block; margin-top: 4px; font-size: 10.5px; color: var(--ctp-overlay1); }
.fb-tk-waiting { margin-bottom: 20px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ctp-peach) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-peach) 32%, transparent);
  font-size: 11.5px; line-height: 1.5; color: var(--ctp-subtext1); }
.fb-tk-waiting b { color: var(--ctp-peach); display: block; margin-bottom: 2px; }
.fb-tk-waiting span { display: block; }
/* ── The rail: .scp-card's edge, a fact sheet inside ─────────────────────── */
/* Rows: hero spans the width, then the story and the thread stack in column 1
   while the rail spans BOTH of their rows. Without the span the thread starts
   below the rail's row and leaves a dead gap the height of the rail. */
.fb-tk-side { grid-column: 2; grid-row: 2 / span 2; min-width: 0; display: flex; flex-direction: column; gap: 10px;
  position: sticky; top: 16px; align-self: start; margin-top: 16px; }
.fb-tk-card { border: 1px solid var(--ctp-surface0); border-radius: var(--radius-sm);
  background: var(--ctp-mantle); overflow: hidden; transition: border-color .12s; }
.fb-tk-card:hover { border-color: var(--ctp-surface1); }
.fb-tk-card-h { padding: 8px 12px; font-size: 9.5px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .1em; color: var(--ctp-subtext0);
  background: var(--ctp-surface0); border-bottom: 1px solid var(--ctp-surface1); }
/* Long values wrap onto a second line instead of being sliced: "mark.russo,
   Today, 7:39 PM" and a page name both outgrow one line at this width. */
.fb-tk-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 12px; min-width: 0; }
.fb-tk-row + .fb-tk-row { border-top: 1px solid color-mix(in srgb, var(--ctp-surface0) 75%, transparent); }
.fb-tk-k { display: flex; align-items: center; gap: 6px; flex-shrink: 0; min-height: 18px;
  font-size: 10px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .04em; color: var(--ctp-overlay1); }
.fb-tk-kico { display: inline-flex; align-items: center; flex-shrink: 0; width: 15px; height: 15px; }
.fb-tk-kico svg { width: 15px !important; height: 15px !important; }
.fb-tk-v { display: flex; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: 4px 6px;
  min-width: 0; font-size: 11.5px; font-weight: 600; color: var(--ctp-text); line-height: 1.45; }
.fb-tk-row > .fb-tk-v, .fb-tk-row > .fb-pri, .fb-tk-row > .fb-kind, .fb-tk-row > .fb-card-pagechip {
  margin-left: auto; text-align: right; }
/* the page chip gets the full width of the rail before it ever ellipsises */
.fb-tk-v .fb-card-pagechip { max-width: 100%; }
.fb-tk-va { width: 17px; height: 17px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
  font-size: 9px; display: inline-grid; place-items: center; }
/* The page's own duotone icon, the same one the sidebar and the cards use. */
.fb-tk-pico { display: inline-flex; align-items: center; flex-shrink: 0; width: 14px; height: 14px; }
.fb-tk-pico svg { width: 14px !important; height: 14px !important; }
.fb-tk-go { display: inline-grid; place-items: center; width: 20px; height: 20px; flex-shrink: 0;
  border-radius: 5px; color: var(--ctp-overlay1); }
.fb-tk-go:hover { color: var(--ctp-blue); background: var(--ctp-surface0); }
.fb-tk-go:focus-visible { outline: 2px solid var(--ctp-blue); outline-offset: 1px; }
.fb-tk-link { font-size: 11.5px; font-weight: 700; color: var(--ctp-blue); }
.fb-tk-link:hover { text-decoration: underline; }
/* lanes: where it is, and where it can go */
.fb-tk-lanes { padding: 5px; display: flex; flex-direction: column; gap: 2px; }
.fb-tk-lane-row { position: relative; display: flex; align-items: center; gap: 8px;
  min-height: 32px; padding: 6px 9px; border-radius: 6px;
  font-size: 11.5px; font-weight: 700; color: var(--ctp-subtext0); }
.fb-tk-lane-go { cursor: pointer; transition: background .12s, color .12s; }
.fb-tk-lane-go:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
.fb-tk-lane-go .fb-tk-lane-arrow { width: 13px; height: 13px; margin-left: auto; opacity: 0; flex-shrink: 0;
  transition: opacity .12s; }
.fb-tk-lane-go:hover .fb-tk-lane-arrow { opacity: .65; }
/* the lane it is in reads as selected, with the lane's own colour on the edge */
.fb-tk-lane-now { background: var(--ctp-surface0); color: var(--ctp-text); cursor: default; padding-left: 12px; }
.fb-tk-lane-now::before { content: ""; position: absolute; left: 4px; top: 7px; bottom: 7px; width: 2.5px;
  border-radius: 2px; background: currentColor; opacity: .5; }
.fb-tk-lane-blue.fb-tk-lane-now  { color: var(--ctp-blue); }
.fb-tk-lane-peach.fb-tk-lane-now { color: var(--ctp-peach); }
.fb-tk-lane-green.fb-tk-lane-now { color: var(--ctp-green); }
.fb-tk-lane-grey.fb-tk-lane-now  { color: var(--ctp-subtext1); }
.fb-tk-lane-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fb-tk-lane-badge { margin-left: auto; flex-shrink: 0; padding: 1px 6px; border-radius: 999px;
  font-size: 8.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  background: var(--ctp-base); color: var(--ctp-overlay1); border: 1px solid var(--ctp-surface1); }
.fb-tk-hint { padding: 0 12px 10px; font-size: 10.5px; line-height: 1.45; color: var(--ctp-overlay1); }
/* Triage pickers reuse the Send-feedback form's own controls (.fbk-chip,
   .fbk-pri, .fbk-pages) — re-judging a ticket should look like filing one. Only
   the density changes here: the rail is 268px, not a 560px form. */
.fb-tk-pick { padding: 0 10px 10px; gap: 4px; }
/* Keep the form's own 152px + fade: the fade is what stops a half-sliced row
   from reading as a rendering bug, and a shorter clip loses it. */
.fb-tk-pick.fbk-pages { padding: 2px 10px 12px; }
.fb-tk-pick .fbk-chip, .fb-tk-pick .fbk-page { font-size: 10.5px; padding: 3px 8px; }
/* The urgency blocks are white-on-colour by design, which the form's dark field
   carries and this light rail does not: unselected ones were white on white.
   Selected keeps the real colour, the rest read as quiet chips. */
.fb-tk-pick .fbk-pri { font-size: 10.5px; padding: 3px 10px; border-radius: 7px; }
.fb-tk-pick .fbk-pri:not(.fbk-pri-on) { background: var(--ctp-surface0); color: var(--ctp-subtext0);
  opacity: 1; filter: none; }
.fb-tk-pick .fbk-pri:not(.fbk-pri-on):hover { background: var(--ctp-surface1); color: var(--ctp-text); }
.fb-tk-pick .fbk-pri-on { box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 3.5px color-mix(in srgb, var(--ctp-blue) 55%, transparent); }
.fb-tk-editing { margin-left: 7px; padding: 1px 6px; border-radius: 999px;
  font-size: 8.5px; font-weight: 800; letter-spacing: .06em;
  background: color-mix(in srgb, var(--ctp-blue) 16%, transparent); color: var(--ctp-blue); }
.fb-tk-toggle { margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px 3px 5px; border-radius: 999px; cursor: pointer;
  font-size: 10.5px; font-weight: 700; color: var(--ctp-subtext0); background: var(--ctp-surface0); }
.fb-tk-toggle-k { width: 9px; height: 9px; border-radius: 50%; background: var(--ctp-surface2); }
.fb-tk-toggle-on { color: var(--ctp-peach); background: color-mix(in srgb, var(--ctp-peach) 12%, transparent); }
.fb-tk-toggle-on .fb-tk-toggle-k { background: var(--ctp-peach); }
/* ── Comments: a conversation, not a table ──────────────────────────────────
   No dividers between comments — spacing does that job, and a hairline every
   three lines is what made this read as a spreadsheet. */
.fb-tk-cmts { grid-column: 1; min-width: 0; margin-top: 2px; padding-top: 18px;
  border-top: 1px solid var(--ctp-surface0); }
.fb-cmt { display: flex; gap: 10px; padding: 0 0 16px; }
.fb-cmt-ava { width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; object-fit: cover;
  font-size: 10px; display: inline-grid; place-items: center; }
.fb-cmt-top { display: flex; align-items: baseline; gap: 6px; }
.fb-cmt-who { font-size: 12px; font-weight: 800; color: var(--ctp-text); }
/* "you" is a word, not a chip: a grey box beside every one of your own lines is
   noise, and there are a lot of them. */
.fb-cmt-you { font-size: 10.5px; font-weight: 600; color: var(--ctp-overlay1); }
.fb-cmt-sep { font-size: 10px; color: var(--ctp-surface2); }
.fb-cmt-when { font-size: 10.5px; color: var(--ctp-overlay1); }
.fb-cmt-del { margin-left: auto; font-size: 10px; font-weight: 700; color: var(--ctp-overlay0); cursor: pointer;
  opacity: 0; transition: opacity .12s; }
.fb-cmt:hover .fb-cmt-del, .fb-cmt:focus-within .fb-cmt-del { opacity: 1; }
.fb-cmt-del:hover { color: var(--ctp-red); }
.fb-cmt-body { max-width: 68ch; font-size: 13px; line-height: 1.6; color: var(--ctp-subtext1);
  white-space: pre-wrap; margin-top: 2px; }
/* A tagged name is coloured text, not a chunky pill: the pill broke the line
   rhythm of the sentence it sits in. */
.fb-cmt-tag { font-weight: 700; color: var(--ctp-blue); }
/* empty and loading are directions, not moods */
.fb-cmt-none { display: flex; align-items: center; gap: 9px; padding: 11px 13px; margin-bottom: 14px;
  border-radius: var(--radius-sm); border: 1px dashed var(--ctp-surface1);
  font-size: 11.5px; line-height: 1.5; color: var(--ctp-subtext0); }
.fb-cmt-none b { color: var(--ctp-text); }
.fb-cmt-none-ico { display: inline-flex; flex-shrink: 0; width: 18px; height: 18px; }
.fb-cmt-none-ico svg { width: 18px !important; height: 18px !important; }
.fb-cmt-skel { padding: 0 0 12px; }
.fb-cmt-skel-row { display: flex; gap: 10px; padding: 0 0 16px; }
.fb-cmt-skel-ava, .fb-cmt-skel-lines i { background: var(--ctp-surface0); border-radius: 4px;
  animation: fb-skel 1.1s ease-in-out infinite; }
.fb-cmt-skel-ava { width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; }
.fb-cmt-skel-lines { flex: 1; display: flex; flex-direction: column; gap: 6px; padding-top: 4px; }
.fb-cmt-skel-lines i { height: 9px; display: block; }
.fb-cmt-skel-lines i:first-child { width: 30%; }
.fb-cmt-skel-lines i:last-child { width: 74%; }
@keyframes fb-skel { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .fb-cmt-skel-ava, .fb-cmt-skel-lines i { animation: none; } }
/* ── Composer: ONE box that holds the field and its actions ─────────────────
   The avatar sits beside that box. Before this it wrapped onto a line of its own
   above a bare grey slab with a resize grabber in the corner. */
.fb-cmt-new { display: flex; align-items: flex-start; gap: 10px; margin-top: 2px; }
.fb-cmt-new-ava { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
  margin-top: 6px; font-size: 10px; display: inline-grid; place-items: center; }
.fb-cbox { position: relative; flex: 1; min-width: 0;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-sm); transition: border-color .12s, box-shadow .12s; }
.fb-cbox:focus-within { border-color: var(--ctp-blue);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ctp-blue) 16%, transparent); }
.fb-cbox-ta { display: block; width: 100%; min-height: 62px; max-height: 260px; padding: 10px 12px;
  background: transparent; border: 0; resize: none; font: inherit;
  font-size: 13px; line-height: 1.55; color: var(--ctp-text); }
.fb-cbox-ta:focus { outline: none; }
.fb-cbox-ta::placeholder { color: var(--ctp-overlay0); }
.fb-cbox-foot { display: flex; align-items: center; gap: 9px; padding: 6px 8px 7px 12px;
  border-top: 1px solid var(--ctp-surface0); }
.fb-cbox-hint { font-size: 10.5px; color: var(--ctp-overlay0); }
.fb-cbox-hint b { color: var(--ctp-overlay1); font-weight: 800; }
.fb-cbox-count { margin-left: auto; font-size: 10.5px; font-weight: 700; color: var(--ctp-peach); }
.fb-cbox-kbd { margin-left: auto; font-size: 10px; font-weight: 700; color: var(--ctp-overlay0);
  padding: 1px 5px; border-radius: 4px; background: var(--ctp-surface0); }
.fb-cbox-count + .fb-cbox-kbd { margin-left: 0; }
.fb-cbox-send { height: 27px; padding: 0 13px; border-radius: 7px; cursor: pointer;
  font-size: 11.5px; font-weight: 800; color: #fff; background: var(--ctp-blue);
  transition: opacity .12s; }
.fb-cbox-send:hover { opacity: .9; }
.fb-cbox-send:disabled { opacity: .4; cursor: default; }
.fb-cbox-send:focus-visible { outline: 2px solid var(--ctp-blue); outline-offset: 2px; }
/* @mention picker — over the field, keyboard-driven */
.fb-mention { position: absolute; left: 6px; right: 6px; bottom: calc(100% - 4px); z-index: 6;
  max-height: 236px; overflow-y: auto; padding: 4px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); }
.fb-mention-row { display: flex; align-items: center; gap: 9px; width: 100%;
  min-height: 34px; padding: 6px 9px; border-radius: 6px; cursor: pointer; text-align: left; }
.fb-mention-on, .fb-mention-row:hover { background: var(--ctp-surface0); }
.fb-mention-ava { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; object-fit: cover;
  font-size: 9px; display: inline-grid; place-items: center; }
.fb-mention-name { font-size: 12px; font-weight: 700; color: var(--ctp-text); }
.fb-mention-handle { margin-left: auto; font-size: 10.5px; color: var(--ctp-overlay1); }
/* A chip that is also a control says so on hover, and only for people who may
   actually change it. */
.fb-chip-live { cursor: pointer; }
.fb-chip-live:hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--ctp-blue) 45%, transparent); }
.fb-chip-live:focus-visible { outline: 2px solid var(--ctp-blue); outline-offset: 1px; }
/* the pop-over */
.fb-chipmenu { position: fixed; z-index: 207; width: 234px; padding: 7px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); }
.fb-chipmenu-up { transform: translateY(-100%); }
.fb-chipmenu-h { padding: 1px 3px 7px; font-size: 9.5px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ctp-overlay1); }
.fb-chipmenu-list { display: flex; flex-wrap: wrap; gap: 4px; }
.fb-chipmenu-list .fbk-chip, .fb-chipmenu-list .fbk-page { font-size: 10.5px; padding: 3px 8px; }
.fb-chipmenu-list .fbk-pri { font-size: 10.5px; padding: 3px 10px; border-radius: 7px; }
/* same white-on-white trap as the rail: a light popover needs quiet unselected blocks */
.fb-chipmenu-list .fbk-pri:not(.fbk-pri-on) { background: var(--ctp-surface0); color: var(--ctp-subtext0);
  opacity: 1; filter: none; }
.fb-chipmenu-list .fbk-pri:not(.fbk-pri-on):hover { background: var(--ctp-surface1); color: var(--ctp-text); }
.fb-chipmenu-list.fbk-pages { max-height: 176px; }
@media (max-width: 768px) {
  .fb-chipmenu { width: min(266px, calc(100vw - 24px)); }
}

/* comment count on the board card */
.fb-card-cc { display: inline-flex; align-items: center; gap: 3px; font-size: 10.5px; font-weight: 700; color: var(--ctp-overlay1); }
/* a notification that goes somewhere */
.notif-item-go { cursor: pointer; }
.notif-item-go:hover { background: var(--ctp-surface0); }
/* the card's one line of prose: its heading, up to three lines */
.fb-card-h { font-size: 13.5px; font-weight: 800; line-height: 1.38; color: var(--ctp-text);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
@media (max-width: 900px) {
  .fb-tk-wrap { padding: 0; align-items: stretch; backdrop-filter: none; }
  .fb-tk { width: 100%; max-height: 100%; border-radius: 0; border: 0; animation: none; }
  .fb-tk-head { padding: 8px 8px 8px 12px; }
  .fb-tk-x { width: 40px; height: 40px; }   /* thumb-sized, not mouse-sized */
  .fb-tk-act { height: 34px; }
  .fb-tk-body { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0 13px 16px;
    padding-bottom: calc(16px + var(--tabbar-h, 63px)); }
  .fb-tk .tab-hero.fb-tk-hero { position: static; margin: 11px 0 15px; padding: 11px 12px; gap: 10px; }
  .fb-tk-act .fb-tk-act-t { display: none; }   /* icons only, thumb-sized */
  .fb-tk-act { width: 34px; padding: 0; justify-content: center; }
  .fb-tk-hero-ico { width: 34px; height: 34px; }
  .fb-tk-hero-ico svg { width: 21px !important; height: 21px !important; }
  .fb-tk-wm { width: 84px; height: 84px; }
  .fb-tk-wm svg { width: 84px !important; height: 84px !important; }
  .fb-tk-h1, .fb-tk-h1-edit { font-size: 17.5px; }
  /* story → facts → thread: nobody scrolls a thread to reach the lane controls */
  .fb-tk-main { order: 1; }
  .fb-tk-side { order: 2; grid-column: 1; grid-row: auto; position: static; margin-top: 0; padding-bottom: 4px; }
  .fb-tk-cmts { order: 3; margin-top: 18px; }
  .fb-tk-lane-row { min-height: 40px; }
  .fb-tk-shot img { width: 132px; height: 90px; }
}

/* Not a board admin: the chips still work, they just look like a request rather
   than a command. A dashed edge is the app's "you can ask for this" tell. */
.fb-move-locked .fb-move-btn { background: transparent; border: 1px dashed var(--ctp-surface1);
  padding: 2px 7px; color: var(--ctp-overlay0); }
.fb-move-locked .fb-move-btn:hover { border-style: solid; color: var(--ctp-text); background: var(--ctp-surface0); }
/* The you-are-not-an-admin modal. */
.fb-blocked-line { font-size: 12px; line-height: 1.5; color: var(--ctp-subtext0); margin-bottom: 8px; }
.fb-blocked-line b { color: var(--ctp-text); font-weight: 700; }
.fb-blocked-good { color: var(--ctp-green); }
.fb-blocked-quiet { color: var(--ctp-overlay0); }
.fb-blocked-who { color: var(--ctp-text); font-weight: 600; }
.fb-col-empty { padding: 14px 6px; text-align: center; font-size: 11px; color: var(--ctp-overlay0); }
/* Phone: lanes become a horizontal swipe (m-hscroll) at readable width, and the
   board gets its own height so the page — not the lane — scrolls. */
@media (max-width: 768px) {
  .fb-board { height: auto; min-height: 0; }
  .fb-col { flex: 0 0 84vw; max-height: none; }
  .fb-cards { overflow: visible; }
  .fb-hero-side { margin-left: 0; }
}

/* ── Feedback triage (Mark 2026-07-25): the sender picks kind / urgency / page,
   so the board arrives sorted. Chip vocabularies are shared with the card
   badges below — same tone names, same meaning. ── */
.fbk-field { margin-bottom: 12px; }
.fbk-lab { display: block; margin-bottom: 6px; font-size: 10px; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ctp-overlay0); }
.fbk-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.fbk-chip { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px;
  border-radius: 999px; cursor: pointer; font-size: 11.5px; font-weight: 650;
  color: var(--ctp-subtext0); background: var(--ctp-surface0);
  border: 1px solid transparent; transition: background .12s, color .12s, border-color .12s; }
.fbk-chip-ico { display: inline-flex; }
.fbk-chip-ico svg { width: 13px; height: 13px; }
.fbk-chip:hover { color: var(--ctp-text); }
/* selected = the kind's own colour, so the form and the card badge read alike */
.fbk-chip-on.fbk-chip-red       { background: color-mix(in srgb, var(--ctp-red) 16%, transparent);       color: var(--ctp-red);       border-color: color-mix(in srgb, var(--ctp-red) 45%, transparent); }
.fbk-chip-on.fbk-chip-peach     { background: color-mix(in srgb, var(--ctp-peach) 16%, transparent);     color: var(--ctp-peach);     border-color: color-mix(in srgb, var(--ctp-peach) 45%, transparent); }
.fbk-chip-on.fbk-chip-lavender  { background: color-mix(in srgb, var(--ctp-lavender) 16%, transparent);  color: var(--ctp-lavender);  border-color: color-mix(in srgb, var(--ctp-lavender) 45%, transparent); }
.fbk-chip-on.fbk-chip-yellow    { background: color-mix(in srgb, var(--ctp-yellow) 18%, transparent);    color: var(--ctp-yellow);    border-color: color-mix(in srgb, var(--ctp-yellow) 45%, transparent); }
.fbk-chip-on.fbk-chip-sapphire  { background: color-mix(in srgb, var(--ctp-sapphire) 16%, transparent);  color: var(--ctp-sapphire);  border-color: color-mix(in srgb, var(--ctp-sapphire) 45%, transparent); }
.fbk-chip-on.fbk-chip-green     { background: color-mix(in srgb, var(--ctp-green) 16%, transparent);     color: var(--ctp-green);     border-color: color-mix(in srgb, var(--ctp-green) 45%, transparent); }
.fbk-chip-on.fbk-chip-grey      { background: var(--ctp-surface1); color: var(--ctp-text); border-color: var(--ctp-surface2); }
/* Urgency = solid heat blocks, the same language as the Explorer's P-filter
   (deliberately NOT P0–P5 labels — feedback urgency is not topic priority). */
.fbk-pri { padding: 5px 12px; border-radius: 8px; cursor: pointer; font-size: 11.5px;
  font-weight: 750; color: #fff; opacity: .38; filter: saturate(.5);
  transition: opacity .12s, filter .12s, box-shadow .12s; }
.fbk-pri-blocker { background: #D92D20; }
.fbk-pri-high    { background: #F04E23; }
.fbk-pri-normal  { background: #1E66F5; }
.fbk-pri-low     { background: #98A2B3; }
.fbk-pri-on { opacity: 1; filter: none; box-shadow: 0 0 0 2px var(--ctp-base), 0 0 0 4px color-mix(in srgb, var(--ctp-lavender) 60%, transparent); }
.fbk-pages { max-height: 152px; overflow-y: auto; padding: 2px;
  /* whole rows only — a half-sliced row read as a rendering bug */
  mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent); }
.fbk-page { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
  border-radius: 8px; cursor: pointer; font-size: 11.5px; font-weight: 600;
  color: var(--ctp-subtext0); background: var(--ctp-surface0); border: 1px solid transparent; }
.fbk-page-ico { display: inline-flex; }
.fbk-page-ico svg { width: 14px !important; height: 14px !important; }
.fbk-page-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--ctp-overlay0); }
.fbk-page:hover { color: var(--ctp-text); }
.fbk-page-on { background: color-mix(in srgb, var(--ctp-blue) 14%, transparent);
  color: var(--ctp-blue); border-color: color-mix(in srgb, var(--ctp-blue) 40%, transparent); }

/* ── Card v2: a kind stripe down the left edge, badges above the words, and the
   admin move chips only on hover (they were shouting on every card). ── */
.fb-card { position: relative; padding-left: 13px; }
.fb-card::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  border-radius: 10px 0 0 10px; background: var(--ctp-surface2); }
.fb-card-red::before      { background: var(--ctp-red); }
.fb-card-peach::before    { background: var(--ctp-peach); }
.fb-card-lavender::before { background: var(--ctp-lavender); }
.fb-card-yellow::before   { background: var(--ctp-yellow); }
.fb-card-sapphire::before { background: var(--ctp-sapphire); }
.fb-card-green::before    { background: var(--ctp-green); }
.fb-card-grey::before     { background: var(--ctp-overlay0); }
.fb-card-top { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-bottom: 7px; }
/* the chip row under the heading */
.fb-card-chips { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 9px; }
.fb-kind { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px;
  border-radius: 6px; font-size: 10px; font-weight: 800; letter-spacing: .02em; }
.fb-kind-ico { display: inline-flex; }
.fb-kind-ico svg { width: 11px; height: 11px; }
.fb-kind-red      { background: color-mix(in srgb, var(--ctp-red) 14%, transparent);      color: var(--ctp-red); }
.fb-kind-peach    { background: color-mix(in srgb, var(--ctp-peach) 16%, transparent);    color: var(--ctp-peach); }
.fb-kind-lavender { background: color-mix(in srgb, var(--ctp-lavender) 16%, transparent); color: var(--ctp-lavender); }
.fb-kind-yellow   { background: color-mix(in srgb, var(--ctp-yellow) 18%, transparent);   color: var(--ctp-yellow); }
.fb-kind-sapphire { background: color-mix(in srgb, var(--ctp-sapphire) 16%, transparent); color: var(--ctp-sapphire); }
.fb-kind-green    { background: color-mix(in srgb, var(--ctp-green) 16%, transparent);    color: var(--ctp-green); }
.fb-kind-grey     { background: var(--ctp-surface0); color: var(--ctp-overlay1); }
.fb-pri { padding: 2px 7px; border-radius: 6px; font-size: 9.5px; font-weight: 800;
  color: #fff; letter-spacing: .03em; }
/* Normal/Low are the default — quieter, so blockers stand out on a full lane. */
.fb-pri.fbk-pri-normal, .fb-pri.fbk-pri-low { opacity: .55; }
.fb-card-pagechip { display: inline-flex; align-items: center; gap: 4px; margin-left: auto;
  padding: 2px 7px; border-radius: 6px; background: var(--ctp-surface0);
  font-size: 10px; color: var(--ctp-overlay1); max-width: 55%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fb-page-ico { display: inline-flex; flex-shrink: 0; }
.fb-page-ico svg { width: 12px !important; height: 12px !important; }
/* Move chips: hidden until hover on pointer devices, always visible on touch
   (where there is no hover and drag is out of the question). */
.fb-move { opacity: 0; transition: opacity .12s; }
.fb-card:hover .fb-move, .fb-card:focus-within .fb-move { opacity: 1; }
@media (hover: none) { .fb-move { opacity: 1; } }

/* ── Switch (Mark 2026-07-25: "those checkboxes are terrible — better u just do
   green red toggle for on and off"). The house on/off control: green = on,
   red = off, state readable as a WORD, not only a colour. ── */
.oa-switch { position: relative; display: inline-flex; align-items: center; flex: none;
  width: 62px; height: 26px; border-radius: 999px; cursor: pointer;
  transition: background .16s ease, box-shadow .16s ease; }
.oa-switch-txt { position: absolute; font-size: 9.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: #fff; transition: left .16s ease, right .16s ease; }
.oa-switch-knob { position: absolute; width: 20px; height: 20px; border-radius: 999px;
  background: #fff; box-shadow: 0 1px 3px rgb(0 0 0 / .28); transition: left .16s ease; }
.oa-switch-on  { background: var(--ctp-green); }
.oa-switch-off { background: var(--ctp-red); }
.oa-switch-on  .oa-switch-knob { left: 38px; }
.oa-switch-off .oa-switch-knob { left: 4px; }
.oa-switch-on  .oa-switch-txt { left: 9px; }
.oa-switch-off .oa-switch-txt { right: 9px; }
.oa-switch:hover { box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 0%, transparent), 0 2px 6px rgb(0 0 0 / .16); }

/* ── The house TABLE (Mark 2026-07-25: "make sure the table is styled to the new
   oasis brand guidelines"). Documented on /brand — use this instead of
   hand-rolling table classes. ── */
.oasis-table-wrap { border: 1px solid var(--ctp-surface0); border-radius: 12px;
  overflow: auto; background: var(--ctp-base); }
.oasis-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 11.5px; }
.oasis-table thead th { position: sticky; top: 0; z-index: 1; text-align: left;
  padding: 8px 12px; background: var(--ctp-mantle); color: var(--ctp-overlay0);
  font-size: 9px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  border-bottom: 1px solid var(--ctp-surface0); white-space: nowrap; }
.oasis-table tbody td { padding: 9px 12px; color: var(--ctp-subtext0);
  border-bottom: 1px solid color-mix(in srgb, var(--ctp-surface0) 55%, transparent); }
.oasis-table tbody tr:last-child td { border-bottom: 0; }
.oasis-table tbody tr:hover td { background: color-mix(in srgb, var(--ctp-surface0) 45%, transparent); }
/* first cell is the row's identity — it carries the weight */
.oasis-table tbody td:first-child { color: var(--ctp-text); font-weight: 650; white-space: nowrap; }
/* .num was right-aligned; numbers are centred app-wide now (see the rule beside
   the headers-always-centred one). The tabular figures are the point of the class
   either way. */
.oasis-table .num { text-align: center; font-variant-numeric: tabular-nums; }
.oasis-table .mono { font-family: var(--font-mono); color: var(--ctp-overlay1); }
.oasis-table .right { text-align: right; }
.oasis-table .center { text-align: center; }
/* status pill inside a table row */
.oa-pill { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;
  font-size: 9.5px; font-weight: 800; letter-spacing: .04em; white-space: nowrap; }
.oa-pill-on  { background: color-mix(in srgb, var(--ctp-green) 16%, transparent); color: var(--ctp-green); }
.oa-pill-off { background: var(--ctp-surface1); color: var(--ctp-overlay0); }

/* ── API keys panel (Profile → API tokens) ───────────────────────────────────
   Rebuilt 2026-08-03 on the app's OWN idioms after three passes of invented
   styling were rejected ("its not oasis branded", "no icons, no allignment, no
   centring"). It is now the Team & rights shape: a definition-card grid for what
   a key may be given, then the house .oasis-table for the keys themselves. Only
   two new classes survive, both because the alternatives were named as ugly:
   the state marker (a filled Active block next to a red Revoke word) and the
   selected state of a permission card. */
.oa-state { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 750;
  white-space: nowrap; }
.oa-state::before { content: ''; width: 6px; height: 6px; border-radius: 999px; background: currentColor;
  box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 15%, transparent); flex: none; }
.oa-state-on { color: var(--ctp-green); }
.oa-state-off { color: var(--ctp-overlay0); }
/* This panel reads one step larger than the app default: it is a settings screen
   people visit rarely, not a dense data table they scan all day (Mark 2026-08-03:
   "why is the text all round so small"). Scoped, so no other table moves. */
.tok-panel { font-size: 12.5px; }
.tok-panel .oasis-table { font-size: 12.5px; }
.tok-panel .oasis-table thead th { font-size: 10px; padding: 9px 12px; }
.tok-panel .oasis-table tbody td { padding: 11px 12px; }

/* A real row button instead of a bare red word (Mark 2026-08-03: "we need better
   buttons"). Quiet until hovered, then it goes red, so the destructive action
   never shouts from across the table. */
.tok-btn { display: inline-flex; align-items: center; height: 25px; padding: 0 11px; border-radius: 7px;
  border: 1px solid color-mix(in srgb, var(--ctp-red) 40%, transparent);
  background: color-mix(in srgb, var(--ctp-red) 7%, transparent); font-size: 10.5px; font-weight: 800;
  letter-spacing: .02em; color: var(--ctp-red); white-space: nowrap; transition: .14s; }
.tok-btn:hover { background: var(--ctp-red); border-color: var(--ctp-red); color: #fff; }
/* Quiet variant for the non-destructive twins (Cancel, Copy). */
.tok-btn-q { border-color: var(--ctp-surface1); background: var(--ctp-base); color: var(--ctp-overlay1); }
.tok-btn-q:hover { background: var(--ctp-surface0); border-color: var(--ctp-surface2); color: var(--ctp-text); }
/* State is green / red, the same on-off language as the switches elsewhere in the
   app (Mark 2026-08-03: "give color red and green"). A word, never a filled block. */
.tok-on { font-size: 11px; font-weight: 800; color: var(--ctp-green); white-space: nowrap; }
.tok-gone { font-size: 11px; font-weight: 800; color: var(--ctp-red); white-space: nowrap; }
/* The panel's one filled primary. */
.tok-new { display: inline-flex; align-items: center; height: 28px; padding: 0 12px; border-radius: 8px;
  background: var(--ctp-mauve); color: #fff; font-size: 11px; font-weight: 800; }
.tok-new:hover { filter: brightness(1.06); }

/* A permission card that is switched on: the app's selection tint, nothing else. */
.tok-pick-on { border-color: color-mix(in srgb, var(--ctp-mauve) 55%, transparent) !important;
  background: color-mix(in srgb, var(--ctp-mauve) 8%, transparent) !important; }
.tok-mono { font-family: var(--font-mono); }

/* Per-key site grants (2026-09-12). A continuation of the key's own row, not a
   second card: no border between them, no new colour, the same chip language
   the app already uses for parent topics. */
.tok-grant-row > td { padding-top: 0 !important; border-top: 0 !important; }
.tok-grant { display: flex; flex-direction: column; gap: 7px; }
.tok-grant-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tok-grant-lbl { font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ctp-overlay0); }
.tok-grant-note { font-size: 11.5px; color: var(--ctp-overlay1); }
.tok-grant-add { margin-left: auto; }
.tok-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tok-chip { display: inline-flex; align-items: center; gap: 6px; height: 23px; padding: 0 4px 0 9px;
  border-radius: 7px; border: 1px solid var(--ctp-surface1); background: var(--ctp-base);
  font-size: 11.5px; color: var(--ctp-subtext0); }
.tok-chip-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
  border-radius: 5px; font-size: 9px; font-weight: 800; color: var(--ctp-overlay0); transition: .14s; }
.tok-chip-x:hover { background: var(--ctp-red); color: #fff; }
.tok-grant-form { display: flex; flex-direction: column; gap: 7px; max-width: 520px; }
.tok-grant-ta { font-family: var(--font-mono); font-size: 11.5px; resize: vertical; }
.tok-grant-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tok-grant-actions .tok-grant-add { margin-left: 0; }
.tok-grant-err { font-size: 11.5px; color: var(--ctp-red); }

/* free-text page/feature (the "New" and "Other" picks) */
.fbk-other { width: 100%; margin-top: 7px; padding: 7px 10px; border-radius: 8px;
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1);
  font-size: 12.5px; color: var(--ctp-text); font-family: inherit; }
.fbk-other:focus { outline: none; border-color: var(--ctp-blue); }
/* Unselected urgency reads as a neutral chip with a heat dot — the washed-out
   coloured version looked muddy (Mark 2026-07-25 "better style"). */
.fbk-pri { position: relative; opacity: 1; filter: none; background: var(--ctp-surface0);
  color: var(--ctp-subtext0); padding-left: 24px; }
.fbk-pri::before { content: ''; position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 8px; height: 8px; border-radius: 999px; background: currentColor; opacity: .5; }
.fbk-pri-blocker::before { background: #D92D20; opacity: 1; }
.fbk-pri-high::before    { background: #F04E23; opacity: 1; }
.fbk-pri-normal::before  { background: #1E66F5; opacity: 1; }
.fbk-pri-low::before     { background: #98A2B3; opacity: 1; }
.fbk-pri:hover { color: var(--ctp-text); }
.fbk-pri-on { color: #fff; padding-left: 12px; box-shadow: none; }
.fbk-pri-on::before { display: none; }
.fbk-pri-on.fbk-pri-blocker { background: #D92D20; }
.fbk-pri-on.fbk-pri-high    { background: #F04E23; }
.fbk-pri-on.fbk-pri-normal  { background: #1E66F5; }
.fbk-pri-on.fbk-pri-low     { background: #98A2B3; }
/* bigger, duotone kind icons */
.fbk-chip-ico svg { width: 15px; height: 15px; }
.fb-kind-ico svg { width: 12px; height: 12px; }
/* build request, not a fix on something we shipped */
.fb-newtag { padding: 2px 6px; border-radius: 5px; font-size: 9px; font-weight: 900;
  letter-spacing: .08em; color: var(--ctp-base); background: var(--ctp-teal); }
.fb-card-pagechip-new { color: var(--ctp-teal);
  background: color-mix(in srgb, var(--ctp-teal) 14%, transparent); }

/* ── Header crowding fix (Mark 2026-07-25 screenshot: on the 6-tab Competitors
   lane the site pill ran UNDER the + New button). The pill and tab strip must
   shrink before anything overlaps, and + New drops its word early — it is the
   newest arrival in a row that was already full.
   min-width: 769px — these are NARROW-DESKTOP rules. Unscoped they also hit
   phones, where they out-cascaded the phone block's own sizing (declared
   earlier in this file) and forced the search pill back to 220px, pushing the
   ? and apps icons onto a third header row. ── */
@media (min-width: 769px) and (max-width: 1799px) {
  .create-btn { width: 36px; padding: 0; justify-content: center; }
  .create-btn-txt { display: none; }
}
@media (min-width: 769px) and (max-width: 1400px) {
  .site-frame-wrap { min-width: 120px; }
  /* shrink factor stays 200 — see the squeeze-order note above; a plain `1`
     here would let the tab strip give width before the search pill does. */
  .uni-wrap { flex: 0 200 220px; }
}
/* Clip the TRIGGER, never the wrapper (fixed 2026-07-26). `overflow: hidden` on
   .site-frame-wrap kept a long domain inside the pill, but the wrapper is also
   the `relative` ancestor of the site-picker popover — so the panel was clipped
   to the pill's 36px-tall box: the search row survived as a sliver and every
   domain row below it was invisible AND unhittable ("can't pick a site, can't
   even tap the ones showing", Mark 2026-07-26, desktop + mobile). The pill's
   own text is what needs clipping, and the button is where it lives. */
.site-frame-wrap > button { min-width: 0; overflow: hidden; }

/* ── Feedback attachments (Mark 2026-07-25: "allow attachments to be dropped") ── */
.fbk-drop { border-radius: 10px; transition: box-shadow .12s, background .12s; }
.fbk-drop-on { box-shadow: 0 0 0 2px var(--ctp-blue); background: color-mix(in srgb, var(--ctp-blue) 6%, transparent); }
.fbk-drop-row { display: flex; align-items: center; gap: 8px; margin-top: 7px; flex-wrap: wrap; }
.fbk-attach { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 8px;
  cursor: pointer; font-size: 11.5px; font-weight: 650; color: var(--ctp-blue);
  background: color-mix(in srgb, var(--ctp-blue) 10%, transparent); }
.fbk-attach:hover { background: color-mix(in srgb, var(--ctp-blue) 18%, transparent); }
.fbk-drop-hint { font-size: 10.5px; color: var(--ctp-overlay0); }
.fbk-thumbs { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }
.fbk-thumb { position: relative; width: 88px; height: 62px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle); }
.fbk-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fbk-thumb-x { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; border-radius: 999px;
  background: rgb(0 0 0 / .6); color: #fff; font-size: 13px; line-height: 1; cursor: pointer; }
.fbk-thumb-kb { position: absolute; bottom: 0; left: 0; right: 0; padding: 1px 4px;
  background: rgb(0 0 0 / .5); color: #fff; font-size: 8.5px; font-weight: 700; text-align: right; }
/* on the card */
.fb-shots { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.fb-shot { width: 62px; height: 44px; border-radius: 6px; overflow: hidden; cursor: zoom-in;
  border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle); }
.fb-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fb-shot:hover { border-color: var(--ctp-blue); }
.fb-shot-full { max-width: min(94vw, 1400px); max-height: 88vh; border-radius: 10px;
  box-shadow: 0 30px 80px -20px rgb(0 0 0 / .7); background: var(--ctp-base); }
.fb-shot-close { position: fixed; top: 16px; right: 20px; width: 34px; height: 34px; border-radius: 999px;
  background: rgb(255 255 255 / .16); color: #fff; font-size: 20px; line-height: 1; cursor: pointer; }
.fb-shot-close:hover { background: rgb(255 255 255 / .28); }

/* ══ Brand & media library — /brand (Mark 2026-07-25). The page documents the
   system, so its own styling stays deliberately plain: cards, hairlines, one
   accent (rosewater) for the page identity. ══ */
.brand-page { padding-bottom: 40px; }
.brand-hero { display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 18px 20px; margin-bottom: 16px; border-radius: 16px;
  background: color-mix(in srgb, var(--ctp-rosewater) 8%, var(--ctp-base));
  border: 1px solid color-mix(in srgb, var(--ctp-rosewater) 26%, transparent); }
.brand-kick { font-size: 10px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ctp-rosewater); }
.brand-h1 { font-size: 22px; font-weight: 800; color: var(--ctp-text); margin-top: 2px; }
.brand-sub { font-size: 12.5px; color: var(--ctp-subtext0); margin-top: 5px; max-width: 74ch; }
.brand-hero-side { margin-left: auto; }
.brand-lock { height: 40px; width: auto; }
.brand-sec { padding: 16px 18px; margin-bottom: 14px; border-radius: 14px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface0); }
.brand-sec-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 12px;
  padding-bottom: 10px; border-bottom: 1px solid var(--ctp-surface0); }
.brand-note { font-size: 11.5px; color: var(--ctp-overlay1); }
.brand-grp { margin-bottom: 14px; }
.brand-grp:last-child { margin-bottom: 0; }
.brand-grp-lab { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin-bottom: 8px; }
.brand-grp-lab i { font-style: normal; font-weight: 500; letter-spacing: 0; text-transform: none;
  color: var(--ctp-overlay1); margin-left: 6px; }
.brand-row { display: flex; gap: 12px; flex-wrap: wrap; }
.brand-asset { display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  padding: 14px; border-radius: 12px; background: var(--ctp-mantle); border: 1px solid var(--ctp-surface0); }
.brand-asset img { height: 44px; width: auto; }
.brand-asset-wide img { height: 40px; }
/* The AI mark is a white-only cutout, so it needs its tile to be visible at all. */
.brand-asset-ai img { padding: 7px; border-radius: 10px;
  background: linear-gradient(135deg, var(--ctp-blue), var(--ctp-mauve)); }
.brand-path { font-family: var(--font-mono); font-size: 10px; color: var(--ctp-overlay1);
  cursor: pointer; padding: 3px 6px; border-radius: 6px; background: var(--ctp-surface0); }
.brand-path:hover { color: var(--ctp-text); }
.brand-swatches { display: flex; gap: 8px; flex-wrap: wrap; }
.brand-sw { display: flex; align-items: center; gap: 8px; padding: 6px 10px 6px 6px; border-radius: 10px;
  cursor: pointer; background: var(--ctp-mantle); border: 1px solid var(--ctp-surface0); }
.brand-sw:hover { border-color: var(--ctp-surface2); }
.brand-sw-chip { width: 26px; height: 26px; border-radius: 7px; flex-shrink: 0;
  border: 1px solid rgb(0 0 0 / .08); }
.brand-sw-name { font-size: 11.5px; font-weight: 650; color: var(--ctp-text); display: flex; flex-direction: column; }
.brand-sw-name i { font-style: normal; font-weight: 500; font-size: 9.5px; color: var(--ctp-overlay0);
  font-family: var(--font-mono); }
.brand-type { display: flex; flex-direction: column; }
.brand-type-row { display: flex; align-items: baseline; gap: 14px; padding: 9px 2px;
  border-bottom: 1px solid color-mix(in srgb, var(--ctp-surface0) 60%, transparent); }
.brand-type-row:last-child { border-bottom: 0; }
.brand-type-spec { margin-left: auto; font-family: var(--font-mono); font-size: 10px; color: var(--ctp-overlay0); }
.brand-t1 { font-size: 22px; font-weight: 800; color: var(--ctp-text); }
.brand-t2 { font-size: 15px; font-weight: 800; color: var(--ctp-text); }
.brand-t3 { font-size: 13px; font-weight: 700; color: var(--ctp-text); }
.brand-t4 { font-size: 12.5px; color: var(--ctp-text); }
.brand-t5 { font-size: 11.5px; color: var(--ctp-subtext0); }
.brand-t6 { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--ctp-overlay0); }
.brand-t7 { font-size: 13px; font-weight: 700; color: var(--ctp-text); font-variant-numeric: tabular-nums; }
.brand-icons { display: flex; gap: 8px; flex-wrap: wrap; }
.brand-ico { width: 96px; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 6px; border-radius: 10px; background: var(--ctp-mantle); border: 1px solid var(--ctp-surface0); }
.brand-ico span { display: inline-flex; }
.brand-ico svg { width: 22px !important; height: 22px !important; }
.brand-ico img { width: 24px; height: 24px; object-fit: contain; }
.brand-ico em { font-style: normal; font-size: 9.5px; color: var(--ctp-overlay1); text-align: center;
  line-height: 1.3; word-break: break-word; }
.brand-comp { display: flex; align-items: flex-start; gap: 16px; padding: 12px 2px; flex-wrap: wrap;
  border-bottom: 1px solid color-mix(in srgb, var(--ctp-surface0) 60%, transparent); }
.brand-comp:last-child { border-bottom: 0; }
.brand-comp-lab { flex: 0 0 220px; font-size: 12px; font-weight: 700; color: var(--ctp-text); }
.brand-comp-lab code { display: block; margin-top: 2px; font-family: var(--font-mono); font-size: 10px;
  font-weight: 500; color: var(--ctp-overlay1); }
.brand-comp-demo { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.brand-rules { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.brand-rule { padding: 11px 12px; border-radius: 10px; background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface0); }
.brand-rule-t { display: block; font-size: 12px; font-weight: 750; color: var(--ctp-text); margin-bottom: 3px; }
.brand-rule-d { font-size: 11.5px; color: var(--ctp-subtext0); line-height: 1.5; }
@media (max-width: 768px) {
  .brand-hero-side { margin-left: 0; }
  .brand-comp-lab { flex: 1 1 100%; }
}

/* ── /vision-mission workflow: a flow you can read without clicking ── */
.vm-wf-tools { display: flex; align-items: center; gap: 8px; }
.vm-tour-btn { padding: 6px 12px; border-radius: 999px; cursor: pointer; font-size: 11.5px;
  font-weight: 750; color: var(--ctp-base); background: var(--ctp-lavender); }
.vm-tour-btn:hover { filter: brightness(1.06); }
.vm-flow { display: flex; align-items: stretch; gap: 6px; margin-top: 14px; }
.vm-fstep { flex: 1 1 0; min-width: 0; position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 4px; text-align: left; cursor: pointer;
  padding: 12px 13px 13px; border-radius: 12px; background: var(--ctp-base);
  border: 1px solid var(--ctp-surface0); transition: border-color .14s, box-shadow .14s, background .14s; }
.vm-fstep:hover { border-color: var(--ctp-surface2); }
.vm-fstep-top { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.vm-fstep-num { width: 20px; height: 20px; border-radius: 999px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 800; color: var(--ctp-overlay1); background: var(--ctp-surface0); }
.vm-fstep-ico { display: inline-flex; margin-left: auto; }
.vm-fstep-ico svg { width: 20px !important; height: 20px !important; }
.vm-fstep-title { font-size: 13px; font-weight: 750; color: var(--ctp-text); }
.vm-fstep-does { font-size: 11.5px; color: var(--ctp-subtext0); line-height: 1.4; }
.vm-fstep-where { font-size: 9.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin-top: 2px; }
/* the live step wears its own colour — one accent per meaning */
.vm-fstep-on { background: var(--ctp-base); box-shadow: 0 2px 10px -4px rgb(0 0 0 / .18); }
.vm-fstep-on.vm-fstep-green     { border-color: var(--ctp-green); }
.vm-fstep-on.vm-fstep-lavender  { border-color: var(--ctp-lavender); }
.vm-fstep-on.vm-fstep-teal      { border-color: var(--ctp-teal); }
.vm-fstep-on.vm-fstep-rosewater { border-color: var(--ctp-rosewater); }
.vm-fstep-on .vm-fstep-num { color: var(--ctp-base); }
.vm-fstep-on.vm-fstep-green .vm-fstep-num     { background: var(--ctp-green); }
.vm-fstep-on.vm-fstep-lavender .vm-fstep-num  { background: var(--ctp-lavender); }
.vm-fstep-on.vm-fstep-teal .vm-fstep-num      { background: var(--ctp-teal); }
.vm-fstep-on.vm-fstep-rosewater .vm-fstep-num { background: var(--ctp-rosewater); }
.vm-flow-arrow { display: flex; align-items: center; color: var(--ctp-overlay0); flex-shrink: 0; }
.vm-flow-arrow svg { width: 16px; height: 16px; }
.vm-step-go { display: inline-flex; align-items: center; gap: 6px; margin-top: 10px;
  font-size: 12px; font-weight: 700; color: var(--ctp-blue); text-decoration: none; }
.vm-step-go:hover { text-decoration: underline; }
.vm-lanes { display: grid; gap: 10px; grid-template-columns: 1fr 1fr; margin-top: 14px; }
.vm-lane { padding: 11px 13px; border-radius: 12px; background: var(--ctp-mantle);
  border-left: 3px solid var(--ctp-surface2); }
.vm-lane-c { border-left-color: var(--ctp-green); }
.vm-lane-x { border-left-color: var(--ctp-rosewater); }
.vm-lane-name { display: block; font-size: 12.5px; font-weight: 800; color: var(--ctp-text); }
.vm-lane-c .vm-lane-name { color: var(--ctp-green); }
.vm-lane-x .vm-lane-name { color: var(--ctp-rosewater); }
.vm-lane-pages { display: block; font-size: 11px; color: var(--ctp-subtext0); margin-top: 2px; }
.vm-lane-role { display: block; font-size: 11px; color: var(--ctp-overlay1); margin-top: 4px; line-height: 1.45; }
@media (max-width: 900px) {
  .vm-flow { flex-direction: column; }
  /* flex-basis is the MAIN axis, which is now vertical: `flex: 1 1 0` (set for
     the side-by-side desktop row) means "start from zero HEIGHT" here, so every
     step collapsed to its padding and .vm-fstep's own `overflow: hidden` clipped
     the title, the description and the destination away — you got a number badge
     sliced in half and nothing else (Mark 2026-07-26: "chopped off").
     Stacked steps size to their content. */
  .vm-fstep { flex: 0 0 auto; }
  .vm-flow-arrow { transform: rotate(90deg); align-self: center; }
  .vm-lanes { grid-template-columns: 1fr; }
}

/* Priority guide: an honest failure instead of an eternal "Loading…" */
.pg-err { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px;
  border-radius: 10px; font-size: 12.5px; color: var(--ctp-peach);
  background: color-mix(in srgb, var(--ctp-peach) 10%, transparent); }
.pg-retry { padding: 5px 12px; border-radius: 8px; cursor: pointer; font-size: 11.5px;
  font-weight: 750; color: var(--ctp-base); background: var(--ctp-peach); }
.pg-retry:hover { filter: brightness(1.05); }



/* ══ Intake log — Mark 2026-07-25, second pass. The first was a table crammed
   into a drawer: "very poor design… not easily readable". Now: card rows in the
   drawer (sticky to the sidebar, for a peek mid-task) and a full-screen /intake
   page grouped by topic. Same tokens and components as the rest of the app. ══ */

/* ── shared pieces ── */
.ik-pools { display:inline-flex; gap:3px; padding:3px; border-radius:10px;
  background:var(--ctp-mantle); border:1px solid var(--ctp-surface0); }
.ik-pool { padding:5px 11px; border-radius:7px; cursor:pointer; font-size:11.5px;
  font-weight:650; color:var(--ctp-overlay1); white-space:nowrap; }
.ik-pool b { font-weight:800; font-variant-numeric:tabular-nums; }
.ik-pool:hover { color:var(--ctp-text); }
.ik-pool-on { background:var(--ctp-base); color:var(--ctp-text); box-shadow:0 1px 2px rgb(0 0 0 / .07); }
.ik-f { padding:4px 9px; border-radius:7px; cursor:pointer; font-size:11px; font-weight:600;
  color:var(--ctp-overlay1); white-space:nowrap; }
.ik-f:hover { background:var(--ctp-surface0); color:var(--ctp-text); }
.ik-f-on { background:color-mix(in srgb, var(--ctp-lavender) 15%, transparent); color:var(--ctp-lavender); }
.ik-tag { padding:1px 6px; border-radius:5px; font-size:8.5px; font-weight:800;
  letter-spacing:.05em; text-transform:uppercase; flex:none; }
.ik-tag-kw { background:color-mix(in srgb, var(--ctp-blue) 14%, transparent); color:var(--ctp-blue); }
.ik-tag-topic { background:color-mix(in srgb, var(--ctp-teal) 16%, transparent); color:var(--ctp-teal); }
.ik-new { padding:1px 6px; border-radius:5px; font-size:8.5px; font-weight:900; letter-spacing:.06em;
  text-transform:uppercase; background:var(--ctp-teal); color:var(--ctp-base); flex:none; }
.ik-moved { padding:1px 6px; border-radius:5px; font-size:8.5px; font-weight:800; letter-spacing:.05em;
  text-transform:uppercase; background:var(--ctp-surface0); color:var(--ctp-overlay1); flex:none; }
.ik-src { display:inline-flex; align-items:center; gap:4px; padding:1px 6px; border-radius:5px;
  font-size:8.5px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; flex:none; }
.ik-src-ico { display:inline-flex; }
.ik-src-ico svg { width:12px; height:12px; }
.ik-src-green    { background:color-mix(in srgb, var(--ctp-green) 15%, transparent);    color:var(--ctp-green); }
.ik-src-sapphire { background:color-mix(in srgb, var(--ctp-sapphire) 15%, transparent); color:var(--ctp-sapphire); }
.ik-src-lavender { background:color-mix(in srgb, var(--ctp-lavender) 16%, transparent); color:var(--ctp-lavender); }
.ik-src-grey     { background:var(--ctp-crust); color:var(--ctp-overlay1); }
.ik-who { font-size:10.5px; color:var(--ctp-subtext0); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ik-via { font-size:10px; color:var(--ctp-overlay0); }
.ik-flag { width:14px; height:11px; border-radius:2px; object-fit:contain; vertical-align:-1px; margin-left:5px; }
.ik-geo { margin-left:5px; font-size:9px; font-weight:700; color:var(--ctp-overlay0); }
.ik-when, .ik-gwhen { font-size:10.5px; color:var(--ctp-overlay0); font-variant-numeric:tabular-nums; white-space:nowrap; }
.ik-into { font-size:11.5px; color:var(--ctp-subtext0); margin-top:3px; }
.ik-into b { color:var(--ctp-text); font-weight:650; }
.ik-acts { display:inline-flex; gap:5px; align-items:center; flex:none; }
.ik-ok, .ik-no { padding:3px 9px; border-radius:6px; cursor:pointer; font-size:10.5px; font-weight:750; color:var(--ctp-base); }
.ik-ok { background:var(--ctp-green); } .ik-no { background:var(--ctp-red); }
.ik-ok:hover, .ik-no:hover { filter:brightness(1.06); }
.ik-ok:disabled, .ik-no:disabled { opacity:.5; cursor:default; }
.ik-state { font-size:10.5px; color:var(--ctp-overlay1); white-space:nowrap; }
.ik-undo { margin-left:5px; font-size:10px; color:var(--ctp-blue); text-decoration:underline; cursor:pointer; }
.ik-msg { padding:22px 18px; font-size:12.5px; color:var(--ctp-overlay1); line-height:1.6;
  display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.ik-msg-err { color:var(--ctp-peach); }

/* ── the drawer: sticky to the sidebar, card rows, expandable ── */
.ik-drawer { width:100%; max-width:420px; height:100%; display:flex; flex-direction:column;
  background:var(--ctp-base); border-left:1px solid var(--ctp-surface1);
  box-shadow:-18px 0 50px -22px rgb(0 0 0 / .32); }
.ik-dhead { display:flex; align-items:flex-start; gap:8px; padding:15px 16px 12px;
  border-bottom:1px solid var(--ctp-surface1); flex:none; }
.ik-dtitle { font-size:15px; font-weight:800; color:var(--ctp-text); }
.ik-dsub { font-size:11px; color:var(--ctp-overlay1); margin-top:1px; }
.ik-expand, .ik-x { width:28px; height:28px; border-radius:8px; flex:none; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; color:var(--ctp-overlay1);
  text-decoration:none; }
.ik-expand { margin-left:auto; }
.ik-expand:hover { background:var(--ctp-surface0); color:var(--ctp-sapphire); }
.ik-x:hover { background:var(--ctp-surface0); color:var(--ctp-red); }
.ik-dbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:9px 16px;
  border-bottom:1px solid var(--ctp-surface0); flex:none; }
.ik-dbody { flex:1 1 auto; overflow-y:auto; overscroll-behavior:contain; padding:10px 0; }
.ik-card { margin:0 12px 9px; padding:11px 12px; border-radius:11px; background:var(--ctp-surface0);
  border:1px solid var(--ctp-surface1); }
.ik-card:hover { border-color:var(--ctp-surface2); }
.ik-card-top { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.ik-card-top .ik-when { margin-left:auto; }
.ik-val { font-size:13.5px; font-weight:700; color:var(--ctp-text); margin-top:5px; line-height:1.35;
  overflow-wrap:anywhere; }
.ik-card-foot { display:flex; align-items:center; gap:7px; margin-top:9px; padding-top:8px;
  border-top:1px solid color-mix(in srgb, var(--ctp-surface1) 60%, transparent); flex-wrap:wrap; }
.ik-card-foot .ik-acts, .ik-card-foot .ik-state { margin-left:auto; }

/* ── the full-screen page ── */
.ik-hero { display:flex; align-items:flex-start; gap:20px; flex-wrap:wrap; padding:17px 19px;
  margin-bottom:14px; border-radius:16px;
  background:color-mix(in srgb, var(--ctp-sapphire) 7%, var(--ctp-base));
  border:1px solid color-mix(in srgb, var(--ctp-sapphire) 24%, transparent); }
.ik-hero-k { font-size:10px; font-weight:800; letter-spacing:.16em; text-transform:uppercase; color:var(--ctp-sapphire); }
.ik-hero-h { font-size:22px; font-weight:800; color:var(--ctp-text); margin-top:2px; }
.ik-hero-s { font-size:12.5px; color:var(--ctp-subtext0); margin-top:4px; max-width:78ch; }
.ik-hero-side { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-left:auto; }
.ik-stat { font-size:11.5px; color:var(--ctp-subtext0); white-space:nowrap; }
.ik-stat b { color:var(--ctp-text); font-weight:800; font-variant-numeric:tabular-nums; }
.ik-ro { font-size:10.5px; color:var(--ctp-overlay0); }
.ik-bar { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
  padding:10px 14px; margin-bottom:12px; border-radius:12px; background:var(--ctp-base);
  border:1px solid var(--ctp-surface0); }
.ik-fset { display:flex; align-items:center; gap:3px; flex-wrap:wrap; }
/* With four trays, the Topics/Keywords switch and five dropdowns, the bar wraps.
   Columns then landed alone at the LEFT of a second line, reading as an orphan;
   this keeps it at the far right of whatever line it ends up on. */
.ik-bar > .ik-drop:last-child { margin-left:auto; }
.ik-group { margin-bottom:12px; border-radius:12px; overflow:hidden; background:var(--ctp-base);
  border:1px solid var(--ctp-surface1); }
.ik-ghead { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:10px 13px;
  background:var(--ctp-mantle); border-bottom:1px solid var(--ctp-surface1); }
.ik-gname { font-size:14px; font-weight:750; color:var(--ctp-text); }
.ik-gcount { font-size:10.5px; font-weight:700; padding:1px 8px; border-radius:999px;
  color:var(--ctp-sapphire); background:color-mix(in srgb, var(--ctp-sapphire) 12%, transparent); }
.ik-ghead .ik-gwhen { margin-left:auto; }
.ik-gwhy { padding:7px 13px; font-size:11.5px; color:var(--ctp-subtext0);
  background:color-mix(in srgb, var(--ctp-lavender) 6%, transparent);
  border-bottom:1px solid var(--ctp-surface0); }
.ik-kw-v { font-size:13px; font-weight:650; color:var(--ctp-text); min-width:0; overflow-wrap:anywhere; }

/* ══ The keyword table (Mark 2026-07-27: "a column for each") ══════════════════
   House table idiom, same as .oasis-table on /brand: crust header, 10px 800
   .12em uppercase labels, hairline row rules, tabular numbers. It lives inside
   the topic card, so it has no border or radius of its own. */
/* ══ The intake table wears the TOPICS EXPLORER's clothes (2026-07-27, Mark:
   "cant we just have the same design as we do on [the Explorer] with accept or
   reject"). Same density, same pinned header, same idea that a row's STATE is
   read from the row itself rather than hunted for in a status column. ══ */
.ik-tbl-wrap { overflow-x:auto; max-height:calc(100vh - 320px); overflow-y:auto; }
/* Header pins while you scroll the group, exactly like .ost-head-sticky does in
   the Explorer — a 62-keyword group otherwise loses its labels immediately. */
.ik-tbl thead th { position:sticky; top:0; z-index:2; }
/* Explorer density: 11.5px cells, 6px vertical padding, hairline rows. */
.ik-tbl tbody td { padding:6px 10px !important; font-size:11.5px; }
.ik-tbl thead th { padding:5px 10px !important; font-size:9.5px; letter-spacing:.09em; }
.ik-tbl .ik-kw-v { font-size:12.5px; font-weight:650; color:var(--ctp-text); }
/* State as a row tint + left accent, the Explorer's .ost-row-written idea:
   kept reads green, rejected reads red, waiting stays plain so it stands out as
   the thing needing a decision. */
.ik-tbl tbody tr.ik-tr-accepted > td:first-child { box-shadow:inset 2.5px 0 0 var(--ctp-green); }
.ik-tbl tbody tr.ik-tr-accepted td { background:color-mix(in oklab, var(--ctp-green) 6%, transparent); }
.ik-tbl tbody tr.ik-tr-accepted:hover td { background:color-mix(in oklab, var(--ctp-green) 12%, transparent); }
.ik-tbl tbody tr.ik-tr-rejected > td:first-child { box-shadow:inset 2.5px 0 0 var(--ctp-red); }
.ik-tbl tbody tr.ik-tr-rejected td { background:color-mix(in oklab, var(--ctp-red) 6%, transparent); opacity:.75; }
.ik-tbl tbody tr.ik-tr-new > td:first-child { box-shadow:inset 2.5px 0 0 var(--ctp-peach); }
/* Explorer column rhythm: labels LEFT-aligned over their data, numbers right,
   nothing stretched across dead space. The old table centred "Keyword" over a
   700px column and printed two columns of identical boilerplate. */
.ik-tbl th, .ik-tbl td { text-align:left; }
.ik-tbl .ik-c-kw { width:auto; min-width:220px; }
.ik-tbl .ik-c-geo { width:52px; }
.ik-tbl .ik-c-geo .geo-flag { width:18px; height:13px; border-radius:2px; }
.ik-tbl .ik-c-num, .ik-tbl th.ik-c-num { width:84px; text-align:right; font-variant-numeric:tabular-nums; }
.ik-tbl .ik-c-src { width:118px; }
.ik-tbl .ik-c-when { width:132px; color:var(--ctp-overlay1); }
.ik-tbl .ik-c-st { width:96px; }
.ik-tbl .ik-c-act { width:104px; text-align:right; }
/* ── The flat table: topic is a column, and the row ends in a decision toggle ── */
/* ══ The header row (Mark 2026-07-27: "ugly header and you cant sort") ════════
   Was crust-on-grey with centred 9.5px caps floating over empty cells. Now it
   reads as a control strip: quiet surface, left-aligned labels over their own
   data, the sorted column lit, and every heading a drag handle. */
/* Type scale, spacing and header treatment copied from the Topics Explorer
   (Mark 2026-07-28: "why is the table not the same size more or less as the one
   on content explorer? (text size, font and spacing). same oasis theme style").
   The Explorer is `text-xs` cells with `py-1 px-2.5`, a 10px uppercase
   tracking-wider header over surface0/55, hairline row rules and a whisper
   zebra — so this is those numbers, not an approximation of them. */
.ik-tbl-flat thead th {
  background:linear-gradient(rgb(var(--ctp-surface0-rgb) / .55), rgb(var(--ctp-surface0-rgb) / .55)), var(--ctp-base) !important;
  color:var(--ctp-overlay0) !important; font-size:10px !important; font-weight:600 !important;
  letter-spacing:.05em !important; text-transform:uppercase !important;
  padding:6px 10px !important; border-bottom:1px solid var(--ctp-surface1) !important; }
.ik-tbl-flat tbody td { padding:4px 10px !important; font-size:12px !important;
  border-bottom:0 !important; border-top:1px solid color-mix(in oklab, var(--ctp-surface0) 30%, transparent) !important; }
.ik-tbl-flat tbody tr:nth-child(even) td { background:color-mix(in oklab, var(--ctp-base) 20%, transparent); }
.ik-tbl-flat .ik-kw-v { font-size:12px; }
.ik-th { cursor:pointer; user-select:none; }
.ik-th:hover { background:var(--ctp-surface1) !important; color:var(--ctp-text) !important; }
.ik-th-on { color:var(--ctp-lavender) !important; }
.ik-th-drag { opacity:.45; }
.ik-th-a { margin-left:4px; font-size:9px; }
/* Widen a column by its edge. 9px of grab area, invisible until you are on it,
   and it must never start the header's own drag-to-reorder. */
.ik-th { position:relative; }
.ik-th-rz { position:absolute; top:0; right:-4px; width:9px; height:100%; cursor:col-resize; z-index:3; }
.ik-th-rz:hover { background:color-mix(in srgb, var(--ctp-lavender) 45%, transparent); }
body.ik-rzing { cursor:col-resize; user-select:none; }
/* A resized column has to hold its width, which only a fixed layout guarantees. */
/* A resized column has to hold the width you gave it. At width:100% a fixed
   layout treats every width as a PROPORTION of the table, so widening one
   column just squeezed the others back and the drag appeared to only work one
   way (Mark 2026-07-28: "lets me make smaller but not wider"). Sizing the table
   to its own columns makes the px real; min-width keeps it filling the screen
   when the columns are narrow, and the wrapper already scrolls sideways. */
.ik-tbl-flat { table-layout:fixed; width:max-content; min-width:100%; }
.ik-tbl-flat .ik-c-chk { width:34px; }

/* ── Main category · tags · priority: the Explorer's own vocabulary ── */
.ik-vert { display:inline-flex; align-items:center; padding:1px 7px; border-radius:5px; font-size:9.5px; font-weight:800;
  letter-spacing:.05em; text-transform:uppercase; white-space:nowrap; }
.ik-vert-casino { color:var(--ctp-mauve); background:color-mix(in srgb, var(--ctp-mauve) 15%, transparent); }
.ik-vert-sports { color:var(--ctp-teal); background:color-mix(in srgb, var(--ctp-teal) 15%, transparent); }
.ik-vert-both { color:var(--ctp-peach); background:color-mix(in srgb, var(--ctp-peach) 16%, transparent); }
.ik-tagc { display:inline-flex; align-items:center; gap:3px; margin-right:4px;
  font-size:9.5px; white-space:nowrap; max-width:100%; overflow:hidden; }
/* 16px + contain, matching the tag chips everywhere else (Mark 2026-07-29: the icon
   was barely visible). object-fit matters because some tag art is a padded/animated
   gif (crypto.gif) that a bare width/height squashes. */
.ik-tagc-i { width:16px; height:16px; object-fit:contain; flex-shrink:0; }
.ik-tagc-f { border-radius:2px; }
.ik-tagc-more { cursor:help; }
.ik-tx { color:var(--ctp-subtext0); font-style:italic; }
.ik-pri-m { margin-left:3px; font-size:8px; opacity:.8; }
.ik-vert-i { width:13px; height:13px; flex-shrink:0; margin-right:4px; vertical-align:-2px; }
.ik-vert-i2 { margin-left:-5px; }
/* Priority wears the Explorer's own heat chip (.v2-prio-*), applied through
   v2PrioClass — one priority, one look, everywhere. */
.ik-pri { display:inline-flex; align-items:center; padding:1px 6px; border-radius:5px;
  font-size:10px; font-weight:800; font-variant-numeric:tabular-nums; }

/* ── Status: what was decided, and whether a person did the deciding ── */
.ik-st-m { margin-right:4px; font-weight:900; }
.ik-st-auto { color:var(--ctp-overlay1); background:color-mix(in srgb, var(--ctp-surface1) 60%, transparent); }
.ik-st-move { color:var(--ctp-lavender); background:color-mix(in srgb, var(--ctp-lavender) 15%, transparent); }
.ik-dby { display:inline-flex; flex-direction:column; line-height:1.25; min-width:0; }
.ik-dby-n { font-size:11.5px; color:var(--ctp-text); font-weight:650;
  overflow:hidden; text-overflow:ellipsis; }
.ik-dby-w { font-size:9.5px; color:var(--ctp-overlay0); }

/* ── The educated guess, kept visibly apart from the recorded facts ── */
.ik-guess { margin:10px 0 4px; padding:10px 12px; border-radius:10px;
  background:color-mix(in srgb, var(--ctp-lavender) 8%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-lavender) 22%, transparent); }
.ik-guess-h { display:flex; align-items:center; gap:8px; }
.ik-guess-k { font-size:9.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ctp-lavender); }
.ik-guess-c { padding:1px 7px; border-radius:5px; font-size:9px; font-weight:800;
  letter-spacing:.05em; text-transform:uppercase; }
.ik-guess-high { color:#fff; background:var(--ctp-green); }
.ik-guess-medium { color:var(--ctp-yellow); background:color-mix(in srgb, var(--ctp-yellow) 20%, transparent); }
.ik-guess-low { color:var(--ctp-overlay1); background:color-mix(in srgb, var(--ctp-surface1) 70%, transparent); }
.ik-guess-n { margin-top:3px; font-size:16px; font-weight:800; color:var(--ctp-text); }
.ik-guess-e { margin:7px 0 0; padding-left:15px; list-style:disc; }
.ik-guess-e li { font-size:11.5px; color:var(--ctp-subtext0); margin-bottom:3px; }
.ik-guess-e b { color:var(--ctp-text); font-weight:700; }
.ik-guess-f { margin-top:7px; font-size:10.5px; color:var(--ctp-overlay0); font-style:italic; }
.ik-guess-empty { background:transparent; border-color:var(--ctp-surface1); }
/* the drag affordance only appears on hover, so the strip stays calm at rest */
.ik-th::before { content:'⋮⋮'; margin-right:5px; font-size:8px; letter-spacing:-1px;
  color:var(--ctp-overlay0); opacity:0; transition:opacity .12s ease; }
.ik-th:hover::before { opacity:.75; }

/* ── Filter bar ── */
.ik-search { position:relative; display:inline-flex; align-items:center; }
.ik-search-i { position:absolute; left:8px; width:13px; height:13px; color:var(--ctp-overlay0); pointer-events:none; }
.ik-search-in { width:230px; height:28px; padding:0 24px 0 26px; border-radius:8px;
  font-size:11.5px; color:var(--ctp-text); background:var(--ctp-base);
  border:1px solid var(--ctp-surface1); }
.ik-search-in:focus { outline:none; border-color:var(--ctp-lavender); }
.ik-search-x { position:absolute; right:6px; color:var(--ctp-overlay0); font-size:15px;
  line-height:1; cursor:pointer; background:none; border:0; }
.ik-search-x:hover { color:var(--ctp-red); }
.ik-seg { display:inline-flex; padding:2px; border-radius:9px; background:var(--ctp-base);
  border:1px solid var(--ctp-surface1); }
.ik-seg-b { padding:3px 9px; border-radius:7px; border:0; background:transparent; cursor:pointer;
  font-size:11px; font-weight:650; color:var(--ctp-overlay1); white-space:nowrap; text-transform:capitalize; }
.ik-seg-b:hover { color:var(--ctp-text); }
.ik-seg-on { background:var(--ctp-surface1); color:var(--ctp-text); box-shadow:0 1px 2px rgb(0 0 0 / .07); }
.ik-drop { position:relative; display:inline-flex; }
.ik-drop-p { position:absolute; top:calc(100% + 5px); left:0; z-index:130; min-width:200px;
  padding:5px; border-radius:10px; background:var(--ctp-base);
  border:1px solid var(--ctp-surface1); box-shadow:0 10px 30px rgb(0 0 0 / .22);
  max-height:60vh; overflow-y:auto; }
.ik-drop-r { left:auto; right:0; }
/* The row-level Main / Tags / Priority pickers are FIXED, not absolute.
   .ik-tbl-wrap scrolls on both axes, so an absolute panel is clipped by that
   wrapper and never appears — the button reacted, no menu showed (Mark
   2026-08-03: "these dont let me select"). top/left come from the clicked
   button's rect, clamped into the viewport and flipped above when there is no
   room below. Same escape the Explorer's tag + priority editors use. */
.ik-drop-fx { position:fixed; top:auto; left:auto; z-index:130; }
/* Revert (Mark 2026-08-03). Peach, not red: red is Reject, and confusing the two
   on the same button group is how the wrong one gets pressed. */
.ik-tog-rv { color:var(--ctp-peach); }
.ik-tog-rv:hover:not(:disabled) { background:color-mix(in srgb, var(--ctp-peach) 18%, transparent); }
.ik-rv-name { color:var(--ctp-text); font-weight:800; }
.ik-rv-list { margin:8px 0 2px; padding-left:16px; display:flex; flex-direction:column; gap:5px;
  font-size:11.5px; line-height:1.5; color:var(--ctp-subtext0); list-style:disc; }
.ik-rv-go { background:color-mix(in srgb, var(--ctp-peach) 22%, transparent);
  color:var(--ctp-peach); border-color:color-mix(in srgb, var(--ctp-peach) 45%, transparent); }
/* Step two reads as the point of no return: heavier ring, red action. */
.ik-rv-modal2 { box-shadow:0 0 0 1px color-mix(in srgb, var(--ctp-red) 40%, transparent),
  0 24px 60px rgb(0 0 0 / .5); }
.ik-rv-head2 { color:var(--ctp-red); }
.ik-rv-warn { margin-top:8px; padding:8px 10px; border-radius:8px; font-size:11.5px; line-height:1.55;
  color:var(--ctp-subtext1); background:color-mix(in srgb, var(--ctp-red) 10%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-red) 28%, transparent); }
.ik-rv-go2 { background:color-mix(in srgb, var(--ctp-red) 24%, transparent);
  color:var(--ctp-red); border-color:color-mix(in srgb, var(--ctp-red) 50%, transparent); }
/* Accept, when the topic is not described yet (Mark 2026-08-03). Dimmed and
   struck through rather than hidden: a missing button reads as a broken board,
   a disabled one with a reason in the tooltip reads as a rule. */
/* Blocked, not dead. The strike-through alone read as "this row can never be
   approved again" (Mark 2026-08-04), so it is a dimmed peach "waiting on you"
   instead, with the .ik-need chip next to it naming what is missing. */
.ik-tog-need { color:var(--ctp-peach) !important; opacity:.7; cursor:not-allowed;
  background:color-mix(in srgb, var(--ctp-peach) 10%, transparent); }
/* Its own line: the decision column is 222px and the toggle group already fills it. */
.ik-need { display:block; width:fit-content; max-width:100%; margin:4px 0 0; padding:1px 6px;
  border-radius:5px; cursor:help;
  border:1px dashed color-mix(in srgb, var(--ctp-peach) 45%, transparent);
  background:color-mix(in srgb, var(--ctp-peach) 10%, transparent);
  color:var(--ctp-peach); font-size:9.5px; font-weight:800; line-height:1.5; }
.ik-ok-part { background:color-mix(in srgb, var(--ctp-yellow) 18%, transparent);
  color:var(--ctp-yellow); }
.ik-ok-warn { margin-left:6px; padding:0 5px; border-radius:4px; font-size:9.5px; font-weight:800;
  color:var(--ctp-base); background:var(--ctp-yellow); }
.ik-drop-away { position:fixed; inset:0; z-index:125; }
.ik-drop-i { display:flex; align-items:center; gap:7px; width:100%; padding:5px 8px;
  border:0; border-radius:7px; background:transparent; cursor:pointer;
  font-size:11.5px; color:var(--ctp-subtext0); text-align:left; }
.ik-drop-i:hover { background:color-mix(in srgb, var(--ctp-lavender) 14%, transparent); color:var(--ctp-text); }
.ik-drop-on { color:var(--ctp-lavender); font-weight:700; }
.ik-drop-lock { opacity:.75; cursor:default; }
.ik-drop-lockn { margin-left:auto; font-size:9px; color:var(--ctp-overlay0); }
.ik-drop-reset { margin-top:3px; border-top:1px solid var(--ctp-surface0); border-radius:0 0 7px 7px;
  color:var(--ctp-overlay1); }
.ik-drop-hint { padding:3px 8px 1px; font-size:10px; color:var(--ctp-overlay0); }
.ik-by-wrap { display:inline-flex; align-items:center; min-width:0; }

/* ══ Arrived today (Mark 2026-07-28: "mark it with a 'new' tag and in green the
   row") ═══════════════════════════════════════════════════════════════════════
   Green had to be taken back first: after the history import nearly every row
   is 'kept', so the whole board sat under a pale green wash and a green NEW row
   would have been invisible in it. Kept keeps its green LEFT EDGE — the state is
   still readable — and the green FILL now means one thing only: this landed
   today. */
.ik-tbl tbody tr.ik-tr-accepted td { background:transparent; }
.ik-tbl tbody tr.ik-tr-accepted:hover td { background:color-mix(in oklab, var(--ctp-green) 9%, transparent); }
.ik-tbl tbody tr.ik-tr-today td { background:color-mix(in oklab, var(--ctp-green) 15%, transparent) !important; }
.ik-tbl tbody tr.ik-tr-today:hover td { background:color-mix(in oklab, var(--ctp-green) 24%, transparent) !important; }
.ik-tbl tbody tr.ik-tr-today > td:first-child { box-shadow:inset 3px 0 0 var(--ctp-green) !important; }
.ik-new { margin-left:6px; padding:1px 6px; border-radius:5px; font-size:8.5px; font-weight:800;
  letter-spacing:.07em; text-transform:uppercase; vertical-align:1px; white-space:nowrap;
  color:#fff; background:var(--ctp-green); }
.ik-when-approx { color:var(--ctp-overlay0); cursor:help; }

/* ══ Packed columns (Mark 2026-07-27: "everything is so whideee") ═════════════
   Before this, Keyword was the only auto-width column, so on a 27" screen it
   swallowed ~1,100px and every label floated centred over a sea of empty cell.
   Now each column is sized to its content and a trailing spacer eats the slack,
   so the board reads as one tight block on any monitor. */
.ik-tbl-flat .ik-c-sp { width:auto; padding:0 !important; }
.ik-tbl-flat th, .ik-tbl-flat td { text-align:left !important; }
.ik-tbl-flat .ik-c-num, .ik-tbl-flat th.ik-c-num { text-align:right !important; }
/* Nothing but the keyword itself may wrap — a wrapped cell makes the whole row
   two or three lines tall, which is the other half of "so wide". */
.ik-tbl-flat td, .ik-tbl-flat th { white-space:nowrap; }
.ik-tbl-flat .ik-c-kw { width:340px; min-width:0; white-space:normal; }
.ik-tbl-flat .ik-c-by { width:150px; overflow:hidden; }
.ik-tbl-flat .ik-by-name { display:inline-block; max-width:100%; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; vertical-align:middle; }
.ik-tbl-flat .ik-c-topic { width:170px; overflow:hidden; text-overflow:ellipsis; }
.ik-tbl-flat .ik-c-when { width:118px; }
.ik-tbl-flat .ik-c-tx { width:190px; }
/* Every real column in the flat table is a fixed width (the last cell is the
   slack eater), so a new column needs one or it steals the slack. */
.ik-tbl-flat .ik-c-now { width:170px; }
.ik-tbl-flat .ik-c-main { width:96px; }
.ik-tbl-flat .ik-c-tag { width:150px; }
/* ── Column spacing (Mark 2026-07-31: "make sure the column spacing is good and
   dont overlap") ────────────────────────────────────────────────────────────
   `Priority` clipped to "PRIORIT" at 64px: the heading is wider than its data, and
   the flat table sets every column to a fixed width, so the heading had nowhere to
   go. These widths fit the HEADING, not just the value. `white-space:nowrap` on the
   headings stops a two-word label wrapping into the row above it, and the ellipsis
   on the text columns means a long value truncates cleanly instead of sliding under
   its neighbour. */
.ik-tbl-flat .ik-c-pri { width:86px; }
/* `table-layout: fixed` means a column with no declared width gets ZERO. The
   heading and the cell were both in the page, one pixel wide, which reads exactly
   like a column that was never added. */
.ik-tbl-flat .ik-c-want { width:104px; }
.ik-tbl-flat th { white-space:nowrap; }
.ik-tbl-flat .ik-c-st { width:104px; }
.ik-tbl-flat .ik-c-dby { width:140px; }
.ik-tbl-flat .ik-c-now { width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* ── Nothing escapes its cell (Mark 2026-08-01: "overlap?") ─────────────────
   Every column here is a FIXED width, but the cells were `overflow:visible`, so
   anything wider simply drew on top of the next column — three tag chips landed
   across Status reading "CARRIED OVER". Containment belongs on the cell, not on
   each thing that might one day be too wide for it.
   `td` only: the `th` carries the drag-to-resize handle at right:-4px, which
   clipping would remove. */
.ik-tbl-flat tbody td { overflow:hidden; }
.ik-tbl-flat tbody td.ik-c-tag, .ik-tbl-flat tbody td.ik-c-main { white-space:nowrap; }
/* A single tag name can be wider than the column on its own, so the chip
   truncates rather than the row growing a scrollbar. */
.ik-tbl-flat .ik-tagc { max-width:100%; overflow:hidden; }
/* "Where it lives NOW" is weaker evidence than "where this event filed it", so it
   reads quieter and carries a marker saying which it is. */
.ik-topic-now { display:inline-flex; align-items:center; gap:5px; font-weight:600;
  color:var(--ctp-subtext0); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
.ik-topic-now-k { flex:0 0 auto; padding:0 4px; border-radius:4px; font-size:8.5px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase; background:var(--ctp-surface1); color:var(--ctp-overlay1); }
.ik-tbl-flat .ik-tagc-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ik-tbl-flat .ik-c-tx { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ik-tbl-flat .ik-c-by { text-overflow:ellipsis; white-space:nowrap; }
/* Who-asked is a button now, so it must not look or space like one. */
/* Filter dropdowns (Mark 2026-08-01: "i want drop downs"). Branded popover, never
   a native <select> — house rule. The button carries a small key so the bar reads
   as "Market: PL" rather than a bare value with no idea what it filters. */
.ik-empty { display:flex; flex-direction:column; align-items:flex-start; gap:7px; max-width:62ch; }
/* "Go look over there" is a signpost, not a call to action. `pg-retry` renders a
   solid orange button, which on an empty screen reads as the thing you are
   supposed to press — louder than the tray it points at deserves. */
.ik-empty-go .pg-retry { background:var(--ctp-surface0); color:var(--ctp-subtext1);
  border:1px solid var(--ctp-surface1); font-weight:700; }
.ik-empty-go .pg-retry:hover { background:var(--ctp-surface1); color:var(--ctp-text); filter:none; }
.ik-empty-h { font-size:14px; font-weight:800; color:var(--ctp-text); }
.ik-empty-b { font-size:12.5px; color:var(--ctp-subtext0); line-height:1.6; }
.ik-empty-go { display:flex; gap:7px; flex-wrap:wrap; margin-top:2px; }
.ik-fdd { display:inline-flex; align-items:center; gap:6px; }
.ik-fdd-k { font-size:9px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ctp-overlay1); }
.ik-f-on .ik-fdd-k { color:inherit; opacity:.75; }
.ik-drop-on { background:color-mix(in srgb, var(--ctp-sapphire) 14%, transparent) !important;
  color:var(--ctp-sapphire) !important; font-weight:700; }
/* A market or topic list can be long; the panel scrolls rather than running off
   the bottom of the screen. */
.ik-drop-scroll { max-height:320px; overflow-y:auto; }
.ik-drop-wide { min-width:280px; }
.ik-who-btn { background:none; border:0; padding:0; margin:0; font:inherit; color:inherit;
  cursor:pointer; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  display:inline-block; vertical-align:bottom; text-align:left; }
.ik-who-btn:hover .ik-by-name { color:var(--ctp-sapphire); text-decoration:underline; }
/* The scoreboard's numeric headings are long ("Already in a topic") and its cells
   are short, so the column takes its width from the heading. */
.ik-tbl th.ik-num, .ik-tbl td.ik-num { min-width:96px; }
.ik-tbl-flat .ik-c-st { width:118px; }
.ik-tbl-flat .ik-c-dby { width:130px; }
.ik-tbl-flat .ik-c-src { width:108px; }
.ik-tbl-flat .ik-topic-v { font-weight:700; color:var(--ctp-text); font-size:12px; }
.ik-tbl-flat .ik-c-kw { min-width:200px; }
.ik-tbl-flat .ik-c-dec { width:222px; }
/* ── Frozen decision column (Mark 2026-07-31) ───────────────────────────────
   "add the accept and reject pls at the far left pls and keep that one sticky —
   the rest can be moved." The tick box and the Accept/Reject/Move buttons stay
   under the cursor however far right the table is scrolled.

   Desktop only: under 768px the table becomes stacked blocks and the tick box is
   absolutely positioned in the gutter, so freezing anything there would fight the
   layout rather than help it.

   The cells need an OPAQUE background. Every other cell here is a color-mix over
   `transparent`, which would let the scrolling columns show straight through the
   frozen ones. That costs the zebra striping on these two cells, so a right-edge
   shadow marks where the frozen region ends instead. */
@media (min-width: 769px) {
  .ik-tbl-flat .ik-frz { position:sticky; z-index:3; background:var(--ctp-base); }
  .ik-tbl-flat thead th.ik-frz { z-index:5; }
  .ik-tbl-flat .ik-frz-1 { left:0; }
  .ik-tbl-flat .ik-frz-2 { left:34px; box-shadow:6px 0 6px -6px color-mix(in srgb, var(--ctp-crust) 55%, transparent); }
  /* !important, reluctantly and deliberately: the row-state tints
     (.ik-tr-today at least) are themselves !important, and every one of them is a
     color-mix over `transparent`. A frozen cell MUST be opaque or the columns
     scrolling underneath show straight through it, so these win on purpose. The
     tints are re-mixed over --ctp-base instead of transparent, which keeps the
     state readable in the frozen region rather than flattening it to grey. */
  .ik-tbl-flat tbody td.ik-frz { background:var(--ctp-base) !important; }
  .ik-tbl-flat tbody tr.ik-tr-today td.ik-frz { background:color-mix(in oklab, var(--ctp-green) 15%, var(--ctp-base)) !important; }
  .ik-tbl-flat tbody tr:hover td.ik-frz { background:var(--ctp-surface0) !important; }
  .ik-tbl-flat tbody tr.ik-tr-on td.ik-frz { background:color-mix(in srgb, var(--ctp-sapphire) 12%, var(--ctp-base)) !important; }
  /* A heading that is not draggable must not offer a drag cursor or a sort click. */
  .ik-tbl-flat th.ik-th-plain { cursor:default; }
  .ik-tbl-flat th.ik-th-plain:hover { background:var(--ctp-base) !important; }
}
/* Segmented Accept | Reject: the row's decision is legible without a separate
   status column, and one tap changes it. Neither side is pre-selected on a
   waiting row — that is the point of it waiting. */
.ik-tog { display:inline-flex; border:1px solid var(--ctp-surface1); border-radius:8px;
  overflow:hidden; background:var(--ctp-surface0); }
.ik-tog-b { padding:3px 10px; font-size:11px; font-weight:700; color:var(--ctp-overlay1);
  cursor:pointer; background:transparent; border:0; white-space:nowrap; }
.ik-tog-b + .ik-tog-b { border-left:1px solid var(--ctp-surface1); }
.ik-tog-b:hover:not(:disabled) { background:var(--ctp-mantle); color:var(--ctp-text); }
.ik-tog-b:disabled { opacity:.5; cursor:default; }
.ik-tog-ok { background:var(--ctp-green) !important; color:#fff !important; }
.ik-tog-no { background:var(--ctp-red) !important; color:#fff !important; }
/* Move is the third way out, not a third state: it stays quiet (never filled)
   because a moved row still has a keep/reject decision waiting on it. */
.ik-tog-mv:hover:not(:disabled) { background:color-mix(in srgb, var(--ctp-lavender) 22%, transparent); color:var(--ctp-lavender); }
/* ── The topic picker ── */
.ik-mv-q { width:100%; height:32px; padding:0 10px; margin-bottom:8px; border-radius:8px;
  font-size:12px; color:var(--ctp-text); background:var(--ctp-base);
  border:1px solid var(--ctp-surface1); }
.ik-mv-q:focus { outline:none; border-color:var(--ctp-lavender); }
.ik-mv-list { max-height:46vh; overflow-y:auto; display:flex; flex-direction:column; gap:2px; }
/* ── What you are moving, and out of where ── */
.ik-mv-head { display:flex; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:8px; }
.ik-mv-what { min-width:0; }
.ik-mv-kw { font-size:14px; font-weight:750; color:var(--ctp-text); overflow-wrap:anywhere; }
.ik-mv-kv { margin-left:7px; font-size:11px; color:var(--ctp-overlay1); font-variant-numeric:tabular-nums; }
.ik-mv-geo { display:flex; align-items:center; gap:6px; margin-left:auto; padding:4px 9px;
  border-radius:8px; font-size:11.5px; color:var(--ctp-subtext0);
  background:var(--ctp-surface0); border:1px solid var(--ctp-surface1); white-space:nowrap; }
.ik-mv-geo b { color:var(--ctp-text); font-weight:800; }
.ik-mv-geo-n { color:var(--ctp-overlay0); font-size:10.5px; }
.ik-mv-from { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; padding:7px 10px;
  border-radius:8px; background:color-mix(in srgb, var(--ctp-red) 7%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-red) 18%, transparent); }
.ik-mv-from-k { font-size:9.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ctp-red); }
.ik-mv-from-v { font-size:12.5px; font-weight:700; color:var(--ctp-text); }
.ik-mv-from-m { font-size:10.5px; color:var(--ctp-overlay1); font-variant-numeric:tabular-nums; }
.ik-mv-note { margin:8px 0 6px; }
.ik-mv-item { display:flex; flex-direction:column; gap:2px; width:100%; padding:7px 9px;
  border-radius:7px; border:0; background:transparent; cursor:pointer; text-align:left; }
.ik-mv-item-top { display:flex; align-items:center; gap:8px; }
.ik-mv-item-sub { display:flex; align-items:center; gap:10px; font-size:10.5px;
  color:var(--ctp-overlay0); font-variant-numeric:tabular-nums; min-width:0; }
.ik-mv-item-lead { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-style:italic; }
.ik-mv-item:hover:not(:disabled) { background:color-mix(in srgb, var(--ctp-lavender) 14%, transparent); }
.ik-mv-item:disabled { opacity:.5; cursor:default; }
.ik-mv-item-l { font-size:12px; color:var(--ctp-text); min-width:0; overflow-wrap:anywhere; }
.ik-mv-item-p { margin-left:auto; font-size:9.5px; font-weight:800; letter-spacing:.06em;
  padding:1px 6px; border-radius:5px; color:var(--ctp-subtext0);
  background:color-mix(in srgb, var(--ctp-surface1) 70%, transparent); }
.ik-mv { padding:5px 12px; border-radius:8px; font-size:11.5px; font-weight:700; cursor:pointer;
  color:var(--ctp-lavender); background:color-mix(in srgb, var(--ctp-lavender) 15%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-lavender) 32%, transparent); }
.ik-mv:hover:not(:disabled) { background:color-mix(in srgb, var(--ctp-lavender) 26%, transparent); }
.ik-mv:disabled { opacity:.5; cursor:default; }
/* Added by: a face for a person, plain text for a stage/key/job. */
.ik-tbl .ik-c-by { width:158px; }
.ik-by-ava { width:16px; height:16px; border-radius:999px; object-fit:cover; margin-right:5px;
  vertical-align:-3px; }
.ik-by-name { font-size:11.5px; color:var(--ctp-subtext0); }
/* "codex" is a tool, not a colleague. The chip carries that distinction and is
   the way in to what is actually known about who ran it. */
.ik-agent { margin-left:5px; padding:1px 6px; border-radius:5px; cursor:pointer;
  font-size:8.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
  font-style:normal; white-space:nowrap; color:var(--ctp-lavender);
  background:color-mix(in srgb, var(--ctp-lavender) 14%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-lavender) 26%, transparent); }
.ik-agent:hover { background:color-mix(in srgb, var(--ctp-lavender) 24%, transparent); }
/* A row that just arrived on the live tail says so for a few seconds, then
   settles into the list — otherwise new rows appear silently while you read. */
@keyframes ik-fresh-in { from { background:color-mix(in srgb, var(--ctp-sapphire) 22%, transparent); } to { background:transparent; } }
.ik-tr-fresh td { animation:ik-fresh-in 6s ease-out both; }
@media (prefers-reduced-motion: reduce) { .ik-tr-fresh td { animation:none; box-shadow:inset 0 0 0 99px color-mix(in srgb, var(--ctp-sapphire) 10%, transparent); } }
/* Keep / Reject sit quietly until the row is hovered — same restraint as the
   Explorer's keyword-row actions — but stay ALWAYS visible on touch, where
   there is no hover to reveal them. */
.ik-tbl tbody tr .ik-acts { opacity:.35; transition:opacity .12s ease; }
.ik-tbl tbody tr:hover .ik-acts, .ik-tbl tbody tr.ik-tr-new .ik-acts { opacity:1; }
@media (hover: none) { .ik-tbl tbody tr .ik-acts { opacity:1; } }
.ik-tbl { width:100%; border-collapse:separate; border-spacing:0; }
/* headings stay centred: the app-wide `thead th` rule above wins, by design */
.ik-tbl thead th { padding:7px 12px; white-space:nowrap;
  font-size:9.5px; font-weight:800; letter-spacing:.12em; text-transform:uppercase;
  color:var(--ctp-overlay0); background:var(--ctp-crust);
  border-bottom:1px solid var(--ctp-surface1); }
.ik-tbl tbody td { padding:8px 12px; font-size:11.5px; color:var(--ctp-subtext0);
  vertical-align:middle; border-bottom:1px solid color-mix(in srgb, var(--ctp-surface1) 55%, transparent); }
.ik-tbl tbody tr:last-child td { border-bottom:0; }
.ik-tbl tbody tr:hover td { background:color-mix(in srgb, var(--ctp-surface0) 50%, transparent); }
.ik-tbl tbody tr.ik-tr-on td { background:color-mix(in srgb, var(--ctp-sapphire) 9%, transparent); }
.ik-c-chk { width:32px; padding-right:0 !important; }
.ik-c-kw { min-width:180px; }
.ik-c-num { text-align:right; width:82px; font-variant-numeric:tabular-nums;
  font-weight:700; color:var(--ctp-subtext1); }
.ik-c-when { white-space:nowrap; font-variant-numeric:tabular-nums; color:var(--ctp-overlay1); }
.ik-c-org { max-width:280px; }
.ik-c-act { text-align:right; white-space:nowrap; width:1%; }
.ik-dash { font-size:10.5px; color:var(--ctp-overlay0); }
/* square by house rule, three-state (on / half / off) */
.ik-gchk { display:inline-flex; align-items:center; cursor:pointer; }
.ik-chk { appearance:none; -webkit-appearance:none; width:15px; height:15px; flex:none;
  border-radius:2px; border:1.5px solid var(--ctp-surface2); background:var(--ctp-base);
  cursor:pointer; position:relative; }
.ik-chk:hover { border-color:var(--ctp-sapphire); }
.ik-chk:checked, .ik-chk:indeterminate { background:var(--ctp-sapphire); border-color:var(--ctp-sapphire); }
.ik-chk:checked::after { content:''; position:absolute; left:4px; top:1px; width:4px; height:8px;
  border:solid var(--ctp-base); border-width:0 2px 2px 0; transform:rotate(42deg); }
.ik-chk:indeterminate::after { content:''; position:absolute; left:2.5px; top:5.5px;
  width:8px; height:2px; background:var(--ctp-base); }
.ik-chk:focus-visible { outline:2px solid var(--ctp-sapphire); outline-offset:2px; }
/* status pills: one shape, four states */
.ik-st { padding:1px 7px; border-radius:5px; font-size:9px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; white-space:nowrap; }
.ik-st-new { color:var(--ctp-peach); background:color-mix(in srgb, var(--ctp-peach) 14%, transparent); }
.ik-st-ok  { color:var(--ctp-green); background:color-mix(in srgb, var(--ctp-green) 15%, transparent); }
.ik-st-no  { color:var(--ctp-red);   background:color-mix(in srgb, var(--ctp-red) 14%, transparent); }
/* the topic's own arrival line, above its keywords */
.ik-trow { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:8px 13px;
  border-bottom:1px solid var(--ctp-surface0);
  background:color-mix(in srgb, var(--ctp-teal) 5%, transparent); }
.ik-trow-t { font-size:11.5px; color:var(--ctp-subtext0); }
.ik-trow .ik-gwhen { margin-left:auto; }
/* bulk bar, floats above the page while a selection is live */
.ik-bulk { position:sticky; bottom:14px; z-index:6; display:flex; align-items:center; gap:9px;
  flex-wrap:wrap; margin-top:12px; padding:10px 14px; border-radius:12px;
  background:var(--ctp-mantle); border:1px solid var(--ctp-surface2);
  box-shadow:0 10px 28px rgb(0 0 0 / .20); }
.ik-bulk-n { font-size:12px; font-weight:750; color:var(--ctp-text); }
.ik-bulk-x { margin-left:auto; font-size:11px; font-weight:600; color:var(--ctp-subtext0);
  cursor:pointer; padding:3px 7px; border-radius:6px; }
.ik-bulk-x:hover { background:var(--ctp-surface0); color:var(--ctp-text); }

/* ── phone: the drawer becomes a full-height sheet, the page stacks, and nothing
   scrolls sideways (Mark: "make sure it can be used on mobile too, cleanly") ── */
@media (max-width: 768px) {
  .ik-drawer { max-width:100%; border-left:0; }
  .ik-hero-side { margin-left:0; }
  .ik-hero-h { font-size:19px; }
  .ik-bar { align-items:stretch; flex-direction:column; }
  .ik-fset { overflow-x:auto; flex-wrap:nowrap; scrollbar-width:none; padding-bottom:2px; }
  .ik-fset::-webkit-scrollbar { display:none; }
  .ik-pools { width:100%; }
  .ik-pool { flex:1 1 0; text-align:center; }
  /* .ik-via stays VISIBLE on phones: it is the "where did this come from" line,
     which is the reason the log exists. Clamped to one line instead of hidden. */
  .ik-via { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%; }
  /* The table becomes stacked rows: a phone cannot show eight columns, and a
     sideways-scrolling table is not "cleanly" usable. Each cell keeps its column
     name via data-l, so nothing is lost, and the tick-box sits in the gutter. */
  .ik-tbl thead { display:none; }
  .ik-tbl, .ik-tbl tbody, .ik-tbl tr, .ik-tbl td { display:block; width:auto; }
  .ik-tbl tbody tr { position:relative; padding:10px 12px 11px 36px;
    border-bottom:1px solid color-mix(in srgb, var(--ctp-surface1) 55%, transparent); }
  .ik-tbl tbody tr:last-child { border-bottom:0; }
  .ik-tbl tbody td { padding:1px 0; border-bottom:0; }
  .ik-tbl tbody tr:hover td, .ik-tbl tbody tr.ik-tr-on td { background:none; }
  .ik-tbl tbody tr.ik-tr-on { background:color-mix(in srgb, var(--ctp-sapphire) 9%, transparent); }
  .ik-tbl td[data-l]::before { content:attr(data-l); margin-right:6px;
    font-size:8.5px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
    color:var(--ctp-overlay0); }
  /* This table opts OUT of the app-wide phone table rules near the top of this
     file (`.main-content table` → block scroller at max-content width, with
     `tbody td:first-child` pinned sticky). Those exist so a wide columnar table
     scrolls itself; a stacked layout wants neither. `:first-child` counts as a
     class, so that rule is (0,2,3) and outranked my (0,2,1) — hence the longer
     selectors here. Measured, not assumed: the tick-box read `position: sticky`. */
  .main-content .ik-tbl { min-width:0; overflow-x:visible; }
  .main-content .ik-tbl tbody td.ik-c-chk:first-child { position:absolute; left:11px; top:11px;
    width:auto; padding:0 !important; background:none; z-index:auto; }
  .ik-tbl td.ik-c-kw { font-size:13.5px; font-weight:700; color:var(--ctp-text); margin-bottom:3px; }
  .ik-tbl td.ik-c-num { text-align:left; width:auto; }
  .ik-tbl td.ik-c-org { max-width:100%; }
  .ik-tbl td.ik-c-act { text-align:left; width:auto; margin-top:7px; }
  .ik-bulk { bottom:70px; }   /* clears the 58px mobile tab bar */
}
.ik-import { padding:4px 10px; border-radius:7px; cursor:pointer; font-size:11px; font-weight:700;
  color:var(--ctp-base); background:var(--ctp-sapphire); white-space:nowrap; }
.ik-import:hover { filter:brightness(1.06); }
.ik-import:disabled { opacity:.55; cursor:default; }
/* a row the import reconstructed: never confusable with a real decision */
.ik-imported { padding:1px 7px; border-radius:5px; font-size:9px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; color:var(--ctp-overlay1); background:var(--ctp-crust);
  border:1px dashed var(--ctp-surface2); white-space:nowrap; }

/* ══ Phone header sizing — LAST WORD (2026-07-26) ══
   The header's action buttons (+ New, bell, ?, apps) are declared far apart in
   this file, each after the phone block, so their desktop 36-38px sizes won
   the cascade and the row overflowed to a third line. This block is last on
   purpose: one place that decides the phone row's arithmetic. Keep it here. */
@media (max-width: 768px) {
  .create-btn, .app-grid-btn { width: 32px; height: 32px; }
  .uni-search { height: 34px; }
}

/* ══ Phone table (2026-07-27) ══
   Four columns instead of thirteen (topics-v2.js v2VisibleCols), so the table
   fits the viewport and the sideways swipe disappears. These rules only tidy
   what the narrower table implies; the column CHOICE is made in JS. */
.m-colchips { display: none; }
@media (max-width: 768px) {
  /* The swap-a-column chip row (concept E) */
  .m-colchips {
    display: flex; align-items: center; gap: 5px; margin-bottom: 6px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    overscroll-behavior-x: contain;
  }
  .m-colchips::-webkit-scrollbar { display: none; }
  .m-colchips-lbl {
    flex-shrink: 0; font-size: 9.5px; font-weight: 800; letter-spacing: .1em;
    text-transform: uppercase; color: var(--ctp-overlay0);
  }
  .m-colchip {
    flex: 0 0 auto; min-height: 28px; padding: 4px 10px; border-radius: 8px;
    font-size: 11.5px; font-weight: 650; white-space: nowrap;
    color: var(--ctp-overlay1); background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
  }
  .m-colchip-on {
    color: var(--ctp-base); background: var(--ctp-blue); border-color: var(--ctp-blue);
    font-weight: 750;
  }
  /* The four-column table now fits, so it must NOT be a scroll container: the
     generic `.main-content table { display:block; overflow-x:auto }` fallback
     turns every cell into its own scrollport (min-content ≈ 0), which is what
     letter-crushed the expanded topic detail into vertical columns of single
     characters ("T O P I C", "Head vol" 12px wide × 108 tall). */
  /* m-explorer-table, NOT .wide-table-scroll: that class is shared with the
     Competitors share-of-voice leaderboard, whose 13 nowrap columns are MEANT
     to overflow and scroll inside their wrapper. `table-layout: fixed` there
     would divide the viewport into ~30px columns and crush it — the very bug
     this block exists to fix, transplanted onto another tab. */
  .main-content .m-explorer-table table { display: table; width: 100%; table-layout: fixed; }
  /* The topic cell holds a checkbox + label in an inline-flex, whose natural
     width (168px) overflowed the 148px column and ran the topic name UNDER the
     priority chip. Clip the cell and let the label wrap inside it. */
  .main-content .m-explorer-table td:first-child { white-space: normal; overflow: hidden; }
  .main-content .m-explorer-table td:first-child > * { max-width: 100%; }
  .main-content .m-explorer-table td:first-child * { min-width: 0; overflow-wrap: anywhere; }
  /* The market flag watermark is a 167px-wide image at 30% opacity — on desktop
     it sits in the margin, on a 390px phone it washes 43% of the screen pink
     right behind the numbers. The market is already named in the hero band. */
  .v2-flag-backdrop { display: none !important; }
  /* The expanded topic detail lives INSIDE a cell — give it the full width and
     let its own bands wrap, rather than inheriting a column's 44px. */
  .main-content .m-explorer-table td[colspan] { padding-left: 6px; padding-right: 6px; }
  /* The desktop band is one nowrap line; a phone can't scroll-hunt seven cells,
     so here it wraps as before. */
  .v2-statband { gap: 8px 14px; flex-wrap: wrap; overflow: hidden; }
  .v2-statband > div { flex: 0 0 auto; }
  /* Detail back-bar: "Back" broke into "Bac / k" at 46px. */
  .v2-topic-back button { flex-shrink: 0; white-space: nowrap; }
  /* Keyword rows inside the detail: keyword takes the slack, numbers stay put. */
  .v2-kwrow-num { font-variant-numeric: tabular-nums; }
}

/* ══ Phone chrome diet (2026-07-27, Mark: "on mobile these sections must be
   more compact because it takes up too much of the screen") ══
   At 390 × 844 the Explorer spent 615px — three quarters of the screen — on
   header, hero, filter pills, scope bar and mode row before the first topic:
   2.5 rows visible. Everything below is phone-only; desktop is untouched. */
@media (max-width: 768px) {
  /* Hero: one compact band. The blurb is onboarding copy you've read, and
     104px of minimum height is a third of the screen. */
  .tab-hero { min-height: 0; padding: 9px 11px; border-radius: 10px; }
  .tab-hero p { display: none; }
  .tab-hero h2, .tab-hero h1 { font-size: 16px !important; line-height: 1.15; }
  .tab-hero-eyebrow { font-size: 8.5px; letter-spacing: .12em; }
  .tab-hero-wm { display: none; }
  /* Stats become an inline strip on the hero's own line, not a 3-up grid. */
  .v2-hero-stats { gap: 10px; margin-top: 4px; }
  .v2-hero-stat b { font-size: 11.5px; }
  .v2-hero-stat i { font-size: 7.5px; letter-spacing: .05em; }
  /* Filter rows: tighter targets (still ≥30px tall), one scrolling line each
     instead of wrapping to two or three. */
  .section-sticky { padding-top: 2px; padding-bottom: 2px; gap: 6px !important; }
  /* "Browse / filters" shares the line with the filter strip instead of owning
     its own 38px row. */
  .section-sticky > .mobile-sidebar-toggle { padding: 6px 10px; font-size: 11.5px; min-height: 30px; }
  /* The filter strip is one scrolling line (see .m-hscroll on it in the
     markup): its own children must not shrink or the pills letter-wrap. */
  .section-sticky [data-tour="filters"] { flex: 1 1 auto; min-width: 0; }
  .section-sticky [data-tour="filters"] > * { flex-shrink: 0; }
  .section-sticky [data-tour="filters"] input { width: 96px; }
  .v2-prio-pill, .v2-pf-btn { min-height: 30px !important; padding: 4px 9px !important; font-size: 11.5px !important; }
  .m-hscroll, .v2-prio-rail { flex-wrap: nowrap !important; overflow-x: auto; scrollbar-width: none; }
  .m-hscroll::-webkit-scrollbar, .v2-prio-rail::-webkit-scrollbar { display: none; }
  /* Scope chips ("VIEWING United Kingdom") ride on the toolbar row since
     2026-08-04. On a phone the filter strip owns the whole line, so they wrap
     to their own line underneath — where ml-auto would shove them to the right
     edge on their own. Left-align them there instead. */
  .v2-scope-inline { margin-left: 0 !important; width: 100%; }
  .v2-scope-inline .uppercase { font-size: 8.5px; }
  /* Mode row: smaller pills, single scrolling line. */
  /* min-height: 34px is set for .mode-sticky button earlier in this file and
     beats `height` whatever the specificity — it has to be lowered too. */
  .mode-sticky button { height: 28px !important; min-height: 28px !important; padding: 0 9px !important; font-size: 11px !important; }
  /* Table header row: 10px uppercase in a 44px column needs no extra padding. */
  .v2-col-th { padding-left: 6px !important; padding-right: 6px !important; white-space: nowrap; }
  .v2-col-resizer { display: none; } /* drag-to-resize is a mouse affordance */
}

/* ══ Variations column headers (2026-07-30) ══
   Drag a heading to move the column, click it to sort, drag its edge to resize.
   The row cells and the heading share one flex `order` per column, which is how
   a reorder happens without rebuilding the row markup. */
.kwc-gut { order: -1; }              /* ✕ / 🚫 / crown stay pinned left */
/* nowrap, but deliberately NOT overflow:hidden — that is exactly what clipped
   the resize grip into uselessness on the Blacklist table. Narrow columns carry
   a `short` label instead (KD, CPC, Geo) so nothing needs clipping. */
.kwc-th { position: relative; user-select: none; white-space: nowrap; }
.kwc-th-sort { cursor: pointer; }
.kwc-th-sort:hover { color: var(--ctp-text); }
.kwc-th-on { color: var(--ctp-lavender); }
.kwc-th-drag { opacity: .45; }
.kwc-th-drop { box-shadow: inset 2px 0 0 var(--ctp-lavender); }
/* The grip gets the same resting divider as the Blacklist headers — without it
   the hit zone is invisible and nobody discovers resizing at all. */
.kwc-th .v2-col-resizer::after { background: var(--ctp-surface1); }
.kwc-th .v2-col-resizer:hover::after { background: var(--ctp-lavender); width: 3px; }
/* Headings are centred app-wide (.v2-col-th), but a centred "KEYWORD" over a
   400px column of left-aligned keywords reads as a stray label. */
.kwc-th.kwc-keyword { text-align: left !important; }

/* ══ Keyword list inside a topic, on a phone (2026-07-27) ══
   The list is a flex row of FIXED, non-shrinking columns — Volume, Translation,
   KD, CPC, Cluster, Actions ≈ 300px of the 314px available — so the keyword
   itself was left with 11px and its header rendered as a vertical column of
   single letters. Mark asked the right question: "if i click the topic to see,
   how will the keywords show up then?" Like this: keyword takes the room,
   volume and difficulty stay, the rest waits for a wider screen (or the
   keyword's own row actions, which are always visible on touch anyway). */
@media (max-width: 768px) {
  .kwc-translation, .kwc-cpc, .kwc-cluster { display: none !important; }
  /* The tab-strip hint ("one topic only" / "shared across topics") is a desktop
     aside; at 7px wide it rendered as a 198px-tall column of letters. */
  .v2-detail-tabs .ml-auto, .v2-kwhint { display: none !important; }
  /* Volume and KD shrink to what the numbers actually need. */
  /* "Head" in a 24px column rendered as "HEA / D" — the crown speaks for itself. */
  .kwc-head { font-size: 0 !important; }
  /* Keyword rows stay on ONE line (the detail-wide wrap rule must not reach
     them, or ACTIONS drops to its own row under every keyword). */
  .grid.gap-px > .flex.items-center { flex-wrap: nowrap !important; }
  .kwc-volume { width: 58px !important; }
  .kwc-kd { width: 34px !important; }
  /* The merged Actions cell (✕ 🚫 ⋯). ✕ + 🚫 alone need 44px; the ⋯ hides at
     this width because a phone row has the controls visible anyway. */
  .kwc-acts { width: 46px !important; }
  .kwc-acts .kw-actions { display: none !important; }
  /* …and the keyword gets a real minimum, so it can never be squeezed to
     letters again no matter what a future column adds. */
  /* .kwc-keyword covers both the heading (a span) and the row cell (the Ahrefs
     link) since 2026-07-30 — the row's keyword used to be a bare flex-1 spacer,
     which the old selectors matched by shape. */
  .group\/v2kw > .flex-1, .kwc-volume ~ span, .group\/v2kw span.flex-1, .kwc-keyword { min-width: 0; }
  .group\/v2kw, .grid.gap-px > .flex.items-center { gap: 6px !important; }
  .grid.gap-px > .flex.items-center > span.flex-1, .kwc-keyword { flex: 1 1 auto; min-width: 96px; }
  /* Market is the same for every row of a topic — the first thing to go. */
  .kwc-geo { display: none !important; }
}

/* ══ Topic detail on a phone (2026-07-27) ══
   The detail page is built from horizontal flex rows sized for a wide screen:
   eyebrow + title + stat band, the tab strip, the keyword header. With the
   default `flex-shrink: 1` and no wrapping, a 356px viewport compressed each
   child to ~10px and the browser broke every label into a vertical column of
   single letters ("T O P I C · G B", "V a r i…"). Letting those rows WRAP is
   the structural fix — an item keeps its natural width and moves to the next
   line instead of being crushed — so it holds for rows added later too. */
@media (max-width: 768px) {
  .v2-topic-page .flex, .v2-topic-page .inline-flex { flex-wrap: wrap; }
  .v2-topic-page .flex > *, .v2-topic-page .inline-flex > * { max-width: 100%; }
  /* Long single words (a head keyword, a URL) still have to give somewhere. */
  .v2-topic-page { overflow-wrap: anywhere; }
  /* Rows that must stay on ONE line keep their own scroll instead of wrapping:
     the tab strip reads as tabs, not as a stack. */
  .v2-topic-page .v2-detail-tabs, .v2-topic-page .m-hscroll {
    flex-wrap: nowrap !important; overflow-x: auto; scrollbar-width: none;
  }
  .v2-topic-page .v2-detail-tabs::-webkit-scrollbar { display: none; }
  .v2-topic-page .v2-detail-tabs > * { flex-shrink: 0; }
}

/* ══ Wide leaderboards on a phone (2026-07-27) ══
   Competitors, Sites, Companies, Platforms and the Content Plan board are
   genuinely wide tables — 12+ columns of numbers that are MEANT to overflow
   and scroll inside their wrapper (the Explorer is the one cut to four columns,
   because it is the daily driver). Two things make that survivable on a phone:
   the first column stays pinned (already the case) and columns land instead of
   drifting mid-swipe. `proximity`, not `mandatory` — mandatory fights the
   sticky first column and hijacks short flicks. */
@media (max-width: 768px) {
  /* :has(> table) — .overflow-x-auto is a Tailwind utility, and one of its seven
     uses in index.html (line ~3487) is the market-flag chip strip, not a table.
     Snapping that would make flicking through flags feel sticky. Only wrappers
     that actually hold a table snap. */
  .main-content .wide-table-scroll,
  .main-content .platform-table-scroll,
  .main-content .overflow-x-auto:has(> table) { scroll-snap-type: x proximity; }
  .main-content .wide-table-scroll td, .main-content .wide-table-scroll th,
  .main-content .platform-table-scroll td, .main-content .platform-table-scroll th { scroll-snap-align: start; }
  /* The Explorer's four columns already fit — nothing to snap to. */
  .main-content .m-explorer-table { scroll-snap-type: none; }
  /* NB: the sticky first column already gets an opaque --ctp-mantle background
     from the phone rule further up this file (~line 1001). Don't restate it —
     a second declaration here would also mask the row-state tints
     (.ost-row-written / .ost-row-planned) on the first cell. */
}

/* ── Intake "load more" (2026-07-27, Mark: "as you scroll down it should be able
   to load more keywords and topics"). The sentinel is a real button as well as an
   observer target, so a failed observer degrades to a tap rather than a dead end. ── */
/* ── Intake: section headings, the scoreboard and its drill-through ─────────
   Two tables per view (topics, keywords) need a heading each, or the second one
   reads as a continuation of the first — the exact confusion the split is here to
   fix. The count sits in the heading, never inside a filter button (house rule).
   A drill number is a BUTTON: clicking it filters the keywords table below. The
   warn tone is reserved for "already in a topic", the only pile that can take a
   keyword away from a lane that already works. */
.ik-sec-h { display:flex; align-items:center; gap:7px; margin:16px 0 7px;
  font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ctp-subtext0); }
.ik-sec-n { padding:1px 7px; border-radius:5px; font-size:10px; font-weight:800;
  background:var(--ctp-surface1); color:var(--ctp-subtext1); letter-spacing:0; }
.ik-num { text-align:right; font-variant-numeric:tabular-nums; }
.ik-drill { min-width:34px; padding:2px 7px; border-radius:5px; border:1px solid transparent;
  font-size:12px; font-weight:700; font-variant-numeric:tabular-nums;
  background:var(--ctp-surface0); color:var(--ctp-text); cursor:pointer; }
.ik-drill:hover { background:var(--ctp-surface1); border-color:var(--ctp-overlay0); }
.ik-drill-warn { background:color-mix(in srgb, var(--ctp-peach) 18%, transparent);
  color:var(--ctp-peach); }
.ik-drill-warn:hover { background:color-mix(in srgb, var(--ctp-peach) 28%, transparent);
  border-color:var(--ctp-peach); }

.ik-more { display:flex; justify-content:center; padding:14px 0 4px; }
.ik-more-btn { padding:8px 18px; border-radius:999px; font-size:12.5px; font-weight:700;
  color:var(--ctp-blue); background:color-mix(in srgb, var(--ctp-blue) 10%, transparent);
  border:1px solid color-mix(in srgb, var(--ctp-blue) 22%, transparent); cursor:pointer; }
.ik-more-btn:hover:not(:disabled) { background:color-mix(in srgb, var(--ctp-blue) 16%, transparent); }
.ik-more-btn:disabled { opacity:.6; cursor:default; }
.ik-end { text-align:center; padding:14px 0 4px; font-size:11.5px; color:var(--ctp-overlay0); }

/* ── Push to ClickUp dialog ───────────────────────────────────────────────
   Same shell as .help-modal (base surface, 16px radius, soft shadow) rather
   than the plain bordered box it shipped as. Centred, because a 4-step
   wizard pinned to the top of the viewport reads as a dropped panel. */
.scp-push-ov { position: fixed; inset: 0; z-index: 70; display: flex;
  align-items: center; justify-content: center; padding: 24px 16px;
  background: color-mix(in srgb, var(--ctp-crust) 62%, transparent);
  backdrop-filter: blur(3px); }
.scp-push-modal { width: 540px; max-width: 100%; max-height: calc(100vh - 48px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: 16px; box-shadow: 0 24px 60px -16px rgba(0,0,0,.45); }
.scp-push-head { display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  padding: 12px 16px; border-bottom: 1px solid var(--ctp-surface0); }
.scp-push-mark { width: 22px; height: 22px; border-radius: 6px; flex-shrink: 0;
  object-fit: contain; background: var(--ctp-surface0); padding: 2px; }
.scp-push-title { font-size: 14px; font-weight: 800; color: var(--ctp-text); line-height: 1.2; }
.scp-push-sub { font-size: 11px; color: var(--ctp-overlay1); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Four dots, not "step 1 of 4" — the shape of the journey at a glance. */
.scp-push-dots { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; flex-shrink: 0; }
.scp-push-dot { width: 6px; height: 6px; border-radius: 999px; background: var(--ctp-surface1); }
.scp-push-dot.on { background: var(--ctp-mauve); }
.scp-push-dot.done { background: color-mix(in srgb, var(--ctp-mauve) 45%, transparent); }
.scp-push-body { padding: 14px 16px; overflow-y: auto; }
.scp-push-hint { font-size: 11.5px; color: var(--ctp-subtext0); margin-bottom: 10px; }
/* Choice cards. Lavender is the selection accent house-wide. */
.scp-push-opt { display: block; width: 100%; text-align: left; padding: 11px 13px;
  border-radius: 11px; border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle);
  cursor: pointer; transition: border-color .12s, background .12s; }
.scp-push-opt + .scp-push-opt { margin-top: 8px; }
.scp-push-opt:hover { border-color: color-mix(in srgb, var(--ctp-lavender) 55%, transparent);
  background: color-mix(in srgb, var(--ctp-lavender) 9%, var(--ctp-mantle)); }
.scp-push-opt-off { opacity: .55; cursor: not-allowed; }
.scp-push-opt-off:hover { border-color: var(--ctp-surface1); background: var(--ctp-mantle); }
.scp-push-opt-t { font-size: 12.5px; font-weight: 700; color: var(--ctp-text); }
.scp-push-opt-d { font-size: 11px; color: var(--ctp-overlay1); margin-top: 2px; line-height: 1.4; }
.scp-push-soon { font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 999px;
  background: var(--ctp-surface1); color: var(--ctp-overlay1); }
/* Footer: exactly one filled primary, everything else quiet until hovered. */
.scp-push-foot { display: flex; align-items: center; gap: 8px; flex-shrink: 0;
  padding: 11px 16px; border-top: 1px solid var(--ctp-surface0); background: var(--ctp-mantle); }
.scp-push-go { padding: 7px 16px; border-radius: 8px; font-size: 12.5px; font-weight: 750;
  color: var(--ctp-base); background: var(--ctp-mauve); cursor: pointer; }
.scp-push-go:hover:not(:disabled) { filter: brightness(1.06); }
.scp-push-go:disabled { opacity: .55; cursor: default; }
.scp-push-quiet { padding: 7px 12px; border-radius: 8px; font-size: 12.5px; font-weight: 650;
  color: var(--ctp-overlay1); cursor: pointer; }
.scp-push-quiet:hover:not(:disabled) { background: var(--ctp-surface0); color: var(--ctp-text); }
.scp-push-quiet:disabled { opacity: .45; cursor: default; }
.scp-push-dest { margin-left: auto; font-size: 10.5px; color: var(--ctp-overlay0); }
/* Phone: full-bleed sheet, same as the other dialogs. */
@media (max-width: 768px) {
  .scp-push-ov { padding: 12px; align-items: flex-end; }
  .scp-push-modal { width: 100%; max-height: calc(100vh - 80px); border-radius: 16px 16px 12px 12px; }
  .scp-push-body { padding: 12px 13px; }
  .scp-push-foot { padding: 10px 13px; }
}

/* ── Re-point at a different ClickUp task ─────────────────────────────────
   Shares the .scp-push-* shell; only the look-up and verify panels are new. */
.scp-relink-now { display: flex; align-items: center; gap: 8px; margin-bottom: 9px;
  padding: 7px 11px; border-radius: 9px; background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface0); }
.scp-relink-now-k { font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ctp-overlay0); }
.scp-relink-now-v { font-size: 11.5px; color: var(--ctp-subtext0); }
.scp-relink-input { width: 100%; height: 34px; padding: 0 11px; border-radius: 9px;
  font-size: 12px; color: var(--ctp-text); background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface1); }
.scp-relink-input::placeholder { color: var(--ctp-overlay0); }
.scp-relink-input:focus { outline: none; border-color: var(--ctp-mauve); }
/* The verify panel. Reading the task name back IS the second validation, so it
   gets the most weight on the screen. */
.scp-relink-card { padding: 11px 13px; border-radius: 11px; background: var(--ctp-mantle);
  border: 1px solid var(--ctp-surface1); }
.scp-relink-name { font-size: 13px; font-weight: 750; color: var(--ctp-text); line-height: 1.35; }
.scp-relink-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 7px; }
.scp-relink-chip { font-size: 9.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--ctp-teal) 18%, transparent); color: var(--ctp-teal); }
.scp-relink-chip-dim { background: var(--ctp-surface1); color: var(--ctp-overlay1); font-weight: 600; }
.scp-relink-where { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 9px;
  font-size: 11px; color: var(--ctp-subtext0); }
.scp-relink-where b { font-weight: 700; color: var(--ctp-overlay0); margin-right: 4px; }
.scp-relink-open { display: inline-block; margin-top: 9px; font-size: 11px; font-weight: 650;
  color: var(--ctp-mauve); }
.scp-relink-open:hover { text-decoration: underline; }
.scp-relink-ok, .scp-relink-warn { margin-top: 9px; padding: 8px 11px; border-radius: 9px;
  font-size: 11px; line-height: 1.5; }
.scp-relink-ok { color: var(--ctp-green); border: 1px solid color-mix(in srgb, var(--ctp-green) 38%, transparent);
  background: color-mix(in srgb, var(--ctp-green) 10%, transparent); }
.scp-relink-warn { color: var(--ctp-peach); border: 1px solid color-mix(in srgb, var(--ctp-peach) 42%, transparent);
  background: color-mix(in srgb, var(--ctp-peach) 12%, transparent); }
.scp-relink-err { margin-top: 9px; font-size: 11px; color: var(--ctp-red); }

/* ══ Blacklist page (Mark's Option B, 2026-07-28) ═══════════════════════════
   The market accordion is retired: ONE house .oasis-table for every selected
   market, market as a flag COLUMN, flag chips as the filter. Everything below
   is chrome AROUND the table — the table itself is .oasis-table and must stay
   that way (its sticky thead is the whole point; hand-rolled classes here were
   rejected once already). Red = blocked, matching the sidebar's blacklist mark. */
.bl-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; border: 1px solid var(--ctp-surface0); border-bottom: 0; border-radius: 12px 12px 0 0;
  background: linear-gradient(90deg, color-mix(in srgb, var(--ctp-red) 10%, var(--ctp-mantle)), var(--ctp-mantle) 60%); }
.bl-bar-title { display: inline-flex; align-items: center; gap: 7px; font-weight: 800; font-size: 13px; color: var(--ctp-red); }
.bl-bar-title svg { width: 16px; height: 16px; flex-shrink: 0; }
.bl-bar-sub { font-size: 11px; color: var(--ctp-overlay1); }
/* the count reads as a stat, not as grey micro-text in a corner */
.bl-count { margin-left: auto; display: inline-flex; align-items: baseline; gap: 5px;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.bl-count b { font-size: 17px; font-weight: 800; color: var(--ctp-red); line-height: 1; }
.bl-count span { font-size: 9px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ctp-overlay0); }

.bl-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; border: 1px solid var(--ctp-surface0); border-bottom: 0; background: var(--ctp-base); }
.bl-search { position: relative; flex: 1 1 220px; min-width: 0; }
.bl-search svg { position: absolute; left: 8px; top: 50%; transform: translateY(-50%); width: 13px; height: 13px; }
.bl-search input { width: 100%; height: 30px; padding: 0 10px 0 27px; border-radius: 8px;
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); color: var(--ctp-text);
  font-size: 12px; font-family: inherit; }
.bl-search input:focus { outline: none; border-color: var(--ctp-lavender); }

/* Market chips FILTER (they don't expand anything). One row, scrolls sideways
   inside itself — the page itself never side-scrolls. */
.bl-chips { display: flex; gap: 6px; overflow-x: auto; padding: 8px 12px;
  border: 1px solid var(--ctp-surface0); border-bottom: 0; background: var(--ctp-base); scrollbar-width: thin; }
.bl-chip { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; height: 28px; padding: 0 9px;
  border-radius: 8px; border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle);
  font-size: 11px; font-weight: 700; color: var(--ctp-subtext0); cursor: pointer; white-space: nowrap; }
.bl-chip:hover { border-color: var(--ctp-lavender); color: var(--ctp-text); }
.bl-chip-on { background: color-mix(in srgb, var(--ctp-red) 14%, var(--ctp-mantle));
  border-color: color-mix(in srgb, var(--ctp-red) 45%, transparent); color: var(--ctp-red); }
.bl-chip-n { font-family: var(--font-mono); font-size: 9.5px; font-weight: 800; color: var(--ctp-overlay0); }
.bl-chip-on .bl-chip-n { color: var(--ctp-red); }

.bl-wrap { border-radius: 0 0 12px 12px; max-height: min(62vh, 720px); }
/* HARD: fixed layout at every width. With auto layout a long machine-written
   reason ("non-igaming: buckshot/dirty/russian roulette cleanup 2026-05-25")
   sets the column's min-content width, the table grows past its wrapper, and
   the Blocked + Un-block columns end up off-screen behind a sideways scroll —
   the un-block button is the only reason the row exists (Mark 2026-07-28: "not
   very user friendly right?"). Fixed layout + ellipsis keeps every column in
   view; the full text lives in the hover tip. */
.bl-table { table-layout: fixed; width: 100%; }
.bl-table td, .bl-table th { overflow: hidden; text-overflow: ellipsis; }
.bl-th { cursor: pointer; user-select: none; position: relative; }
.bl-th:hover { color: var(--ctp-text); }
/* Drag-to-resize needs its OWN handle geometry here (Mark 2026-07-29: "why cant
   I adjust the width of the columns to my liking by dragging from one end").
   The shared .v2-col-resizer sits at right:-4px and pokes past the cell, which
   is fine on the Explorer's headers — but every cell in THIS table carries
   `overflow: hidden` (the rule that stops a long reason widening the column),
   so the 9px handle was clipped to a ~5px sliver and its hover indicator sat
   almost entirely outside the cell. The grab zone existed and was invisible.
   So: keep the whole handle inside the cell, widen it to 14px, and show a
   resting divider so the boundary is something you can SEE before reaching. */
.bl-th .v2-col-resizer { right: 0; width: 14px; }
.bl-th .v2-col-resizer::after { left: auto; right: 4px; top: 20%; bottom: 20%;
  background: var(--ctp-surface1); }
.bl-th .v2-col-resizer:hover::after { right: 3px; width: 3px; background: var(--ctp-lavender); }
/* The last column's divider would draw a line against the table's own border. */
.bl-th:last-child .v2-col-resizer::after { background: transparent; }
.bl-th:last-child .v2-col-resizer:hover::after { background: var(--ctp-lavender); }
/* Drag-to-reorder feedback, matching the Explorer headers: the column being
   dragged dims, the one it would land before takes a lavender edge. */
.bl-th-drag { opacity: .45; }
.bl-th-drop { box-shadow: inset 2px 0 0 var(--ctp-lavender); }
/* Un-block wears the SAME 🚫 as the Explorer keyword rows — the row IS the
   blocked state. Red at rest so it reads as "blocked", green on hover because
   that's what clicking does. The rules live BELOW the .bl-undo base block (see
   further down) because same-specificity order decides the colour, and .bl-undo's
   grey `color` was winning — every icon rendered grey (Mark 2026-07-29:
   "since they are blocked please make sure to mark them in red"). */
/* The grow-on-hover label made sense when Action was a 104px column on the far
   right. Leading the row at 56px it had nowhere to grow, so it clipped to
   "Un-blo" and spilled over the keyword cell (Mark 2026-07-29: screenshot).
   Icon only — the hover tip already names the keyword and says what a click
   does, which the label never did. */
.bl-undo-blocked .bl-undo-lbl { display: none; }
.bl-undo-blocked { justify-content: center; padding: 0; width: 26px; }
.bl-none { color: var(--ctp-overlay0); font-style: italic; }
/* "not recorded" is two words in a 130px column — let it ellipsise on one line
   rather than wrap and double every unattributed row's height. */
.bl-table .bl-c-by { white-space: nowrap; }
/* The un-block label is collapsed to max-width:0 until row hover, and the
   table-wide `text-overflow: ellipsis` was rendering that nothing as a stray
   "…" beside every icon. Nothing in this cell should ever ellipsise. */
.bl-table .bl-c-action { text-overflow: clip; }
/* Just-blacklisted (last 24h): a green wash + left edge, so "did my click land?"
   is answered without reading a date column. Tint only — the text keeps its own
   colour, or the keyword stops being the thing you read first. */
.bl-table tr.bl-fresh > td { background: color-mix(in srgb, var(--ctp-green) 9%, transparent); }
.bl-table tr.bl-fresh > td:first-child { box-shadow: inset 3px 0 0 var(--ctp-green); }
.bl-fresh-tag { margin-left: 7px; padding: 1px 6px; border-radius: 999px; font-size: 9px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; vertical-align: middle; white-space: nowrap;
  color: var(--ctp-green); background: color-mix(in srgb, var(--ctp-green) 16%, transparent); }
.oa-pill-batch { background: var(--ctp-surface1); color: var(--ctp-overlay1); font-weight: 700; letter-spacing: 0;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block;
  vertical-align: middle; cursor: help; }
.bl-empty { padding: 34px 12px; text-align: center; font-size: 12px; color: var(--ctp-overlay1);
  border: 1px solid var(--ctp-surface0); border-radius: 0 0 12px 12px; background: var(--ctp-base); }

/* Un-block: icon ALWAYS visible, label grows on row hover (Mark's pick) — the
   action is never hidden (phones have no hover) but the rows stay quiet. */
.bl-undo { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 8px; border-radius: 7px;
  border: 1px solid var(--ctp-surface1); background: var(--ctp-mantle); color: var(--ctp-overlay1);
  font-size: 10px; font-weight: 800; cursor: pointer; white-space: nowrap; font-family: inherit; }
.bl-undo:hover { color: var(--ctp-green); border-color: color-mix(in srgb, var(--ctp-green) 50%, transparent);
  background: color-mix(in srgb, var(--ctp-green) 12%, transparent); }
.bl-undo svg { width: 12px; height: 12px; flex-shrink: 0; }
.bl-undo-lbl { max-width: 0; overflow: hidden; transition: max-width .14s; }
.oasis-table tbody tr:hover .bl-undo-lbl, .bl-undo:hover .bl-undo-lbl { max-width: 70px; }

/* Blocked state, ordered AFTER .bl-undo so its grey doesn't win the cascade.
   Full red + a red-tinted chip: the icon is the row's status, not a neutral
   control. Hover flips the whole chip green, which is what the click does. */
.bl-undo-blocked { color: var(--ctp-red);
  border-color: color-mix(in srgb, var(--ctp-red) 38%, transparent);
  background: color-mix(in srgb, var(--ctp-red) 10%, transparent); }
.bl-undo-blocked:hover { color: var(--ctp-green) !important;
  border-color: color-mix(in srgb, var(--ctp-green) 50%, transparent) !important;
  background: color-mix(in srgb, var(--ctp-green) 12%, transparent) !important; }
/* The English gloss is support, not the thing you scan for — quieter than the
   keyword itself so the keyword column still leads the row. */
.bl-tr { color: var(--ctp-subtext0); font-style: italic; }
/* Centred volumes still want tabular figures, or 1.2K and 990 shift width. */
.bl-table .bl-c-volume { font-variant-numeric: tabular-nums; }
/* The infinite-scroll footer: a status line, deliberately quiet — it's the only
   row you're allowed to skim past. */
.bl-more { display: inline-block; padding: 3px 0; font-size: 10.5px; color: var(--ctp-overlay0); }
.bl-more b { color: var(--ctp-overlay1); font-weight: 800; }
/* Hidden by default: it only stands in for the Translation COLUMN once that
   folds away at 620 (same lane as .bl-sub-mv), or the gloss shows twice. */
.bl-sub-tr { display: none; font-style: italic; }

/* Phone: two lines per row (keyword above, market + volume + reason below) so
   nothing scrolls sideways. Mobile-first is a hard rule. */
/* The second line under the keyword is the OVERFLOW lane: it carries whichever
   columns had to be dropped. Two independent triggers, because the table lives in
   a narrow column inside the Explorer — measuring the WINDOW is wrong (at a
   1280px window the table only gets ~680px, and the reason truncated to
   "non-igaming: buc…"). So the reason folds down on a CONTAINER query, and the
   phone breakpoint additionally folds market + volume. */
.bl-sub2 { display: none; }
.bl-wrap { container-type: inline-size; }
/* BOTH breakpoints are CONTAINER queries now. Mixing a @container fold with a
   @media one left a dead band: at a 900-1030px container the media query hadn't
   fired and the container one had already passed, so all 8 columns rendered into
   a box that needs 982px — and the table side-scrolled, which is the one thing
   this table isn't allowed to do. Measured minimums: 9 columns ~1050px, 6 columns
   ~370px. The fold therefore happens at 1100, not 880 — it was 1040 for the old
   8-column set and the Translation column pushed the minimum past it. */
@container (max-width: 1100px) {
  /* Hide BY COLUMN, never by nth-child: the headers are draggable, so a
     positional rule starts addressing whichever column got moved there.
     Reason / Blocked by / How fold onto the keyword's second line (.bl-sub2). */
  .bl-table .bl-c-note, .bl-table .bl-c-by, .bl-table .bl-c-via { display: none; }
  .bl-table tbody td.bl-c-keyword { white-space: normal; }
  /* Same reason as the narrow tier: with the reason/by/how columns folded away,
     their declared width is freed and only an auto column will claim it. */
  .bl-table .bl-c-keyword { width: auto !important; }
  .bl-sub2 { display: block; margin-top: 3px; font-size: 10px; font-weight: 400; line-height: 1.45;
    color: var(--ctp-overlay1); }
  .bl-sub-mv { display: none; }
}
@container (max-width: 620px) {
  /* Narrow: keyword + un-block only. Everything else moves to .bl-sub2 — the
     gloss included, in quotes under the keyword it belongs to. */
  .bl-table .bl-c-geo, .bl-table .bl-c-volume, .bl-table .bl-c-when,
  .bl-table .bl-c-tr { display: none; }
  .bl-sub2 { white-space: normal; }
  /* Long keywords and the folded reason line need a break opportunity here. */
  .bl-table td.bl-c-keyword, .bl-sub2 { overflow-wrap: anywhere; }
  .bl-table td.bl-c-keyword { overflow: visible; text-overflow: clip; }
  /* Keyword takes ALL the leftover width. With table-layout:fixed every visible
     column has a declared width, so once the other six fold away nothing claims
     the freed space: the keyword stayed at its declared 25% (114px of a 466px
     table) and hard-wrapped mid-word while 300px sat empty to the right.
     `auto` needs !important because the width is an inline style binding. */
  .bl-table .bl-c-keyword { width: auto !important; }
  /* "UNDO" is 38px of letter-spaced caps and the cell's own padding is 24px, so
     the header clipped in a 46px column. */
  .bl-table .bl-c-action { padding-left: 4px !important; padding-right: 4px !important; }
  .bl-sub-mv { display: inline; }
  .bl-sub-tr { display: inline; }
  .bl-sub-r::before { content: " · "; }
  .bl-table .bl-c-action { width: 46px !important; padding-left: 0; }
}
@media (max-width: 640px) {
  .bl-bar-sub { display: none; }   /* outside .bl-wrap — not in the container */
  .bl-undo-lbl { display: none; }
  /* Without table-layout:fixed the keyword column takes its max-content width
     (the whole second line as one string) and pushes the un-block button off
     the edge of the phone — the one control the row exists for. */
  .bl-table .bl-c-action { width: 46px !important; padding-left: 0; }
}

/* A REQUEST is not a receipt (Mark 2026-08-03: "why 'moved here'"). Rows that
   arrived today are tinted green on Mark's own instruction, which on a row that
   had not happened yet read as "done" — so a row still waiting keeps the green
   arrival tint but takes a PEACH edge, the same colour the waiting tray and the
   Explorer strip use. Beats .ik-tr-today, which is itself !important. */
.ik-tbl tbody tr.ik-tr-request > td:first-child { box-shadow:inset 3px 0 0 var(--ctp-peach) !important; }
.ik-tbl-flat tbody tr.ik-tr-request td.ik-frz { box-shadow:inset 3px 0 0 var(--ctp-peach) !important; }

/* Filter dropdowns must LOOK like controls (Mark 2026-08-03: "these dorp downs …
   arent even oasis styled"). They were bare text with a caret, indistinguishable
   from a label, so nobody could tell there was anything to press. Same bordered
   surface as every other Oasis control; the active one keeps its lavender fill. */
.ik-fdd { padding:4px 8px; border:1px solid var(--ctp-surface1); border-radius:8px;
  background:var(--ctp-base); transition:border-color .12s, background .12s; }
.ik-fdd:hover { border-color:color-mix(in srgb, var(--ctp-lavender) 55%, transparent);
  background:var(--ctp-surface0); }
.ik-fdd.ik-f-on { border-color:color-mix(in srgb, var(--ctp-lavender) 55%, transparent); }
.ik-fdd .ik-fdd-c { margin-left:2px; font-size:9px; opacity:.6; }
.ik-drop-hint { margin:4px 6px 2px; font-size:10.5px; line-height:1.45; color:var(--ctp-overlay0); }

/* Dropdown rows carry the same marks as the table they filter (Mark 2026-08-03:
   "not using any flags, icons and the full list") — a market without its flag and
   a channel without its icon read as a different app two rows apart. */
.ik-drop-q { width:100%; margin:0 0 4px; padding:5px 8px; border:1px solid var(--ctp-surface1);
  border-radius:7px; background:var(--ctp-base); color:var(--ctp-text); font-size:11.5px; }
.ik-drop-q:focus { outline:none; border-color:color-mix(in srgb, var(--ctp-lavender) 55%, transparent); }
.ik-drop-q::placeholder { color:var(--ctp-overlay0); }
.ik-drop-code { font-family:var(--font-mono, ui-monospace, monospace); font-size:10.5px; font-weight:800;
  color:var(--ctp-subtext1); }
.ik-drop-sub { color:var(--ctp-overlay0); font-size:10.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ik-drop-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ik-drop-n { margin-left:auto; padding-left:8px; font-size:10px; font-weight:700; color:var(--ctp-overlay0); }
.ik-drop-av { width:16px; height:16px; border-radius:50%; object-fit:cover; flex:none; }
.ik-drop-av-t { display:inline-flex; align-items:center; justify-content:center; color:var(--ctp-sapphire); }
.ik-drop-av-t svg { width:14px; height:14px; }
.ik-src-i { display:inline-flex; align-items:center; justify-content:center; flex:none; }
.ik-src-i svg { width:14px; height:14px; }
.ik-src-person { color:var(--ctp-green); } .ik-src-api { color:var(--ctp-sapphire); }
.ik-src-ai { color:var(--ctp-lavender); } .ik-src-pipeline { color:var(--ctp-overlay1); }
.ik-pile-dot { width:8px; height:8px; border-radius:50%; flex:none; }
.ik-pile-new { background:var(--ctp-green); }
.ik-pile-floating { background:var(--ctp-sapphire); }
.ik-pile-held { background:var(--ctp-peach); }
.ik-drop-scroll { max-height:min(60vh, 420px); overflow-y:auto; }

/* Main, Tags and Priority are single marks, not sentences, so they centre under
   their headings (Mark 2026-08-03: "centre these 3 pls") — left-aligned they read
   as the start of a column of text that never comes. */
.ik-tbl th.ik-c-main, .ik-tbl td.ik-c-main,
.ik-tbl th.ik-c-tag,  .ik-tbl td.ik-c-tag,
.ik-tbl th.ik-c-pri,  .ik-tbl td.ik-c-pri,
.ik-tbl th.ik-c-want, .ik-tbl td.ik-c-want { text-align:center; }
/* The flat table forces `text-align:left !important` on every cell, so an
   alignment there needs its own !important twin — the same shape the right-aligned
   number column already uses. Found the hard way: the plain rule above matched,
   won on specificity, and still lost. */
.ik-tbl-flat th.ik-c-main, .ik-tbl-flat td.ik-c-main,
.ik-tbl-flat th.ik-c-tag,  .ik-tbl-flat td.ik-c-tag,
.ik-tbl-flat th.ik-c-pri,  .ik-tbl-flat td.ik-c-pri,
.ik-tbl-flat th.ik-c-want, .ik-tbl-flat td.ik-c-want { text-align:center !important; }
.ik-tbl td.ik-c-main, .ik-tbl td.ik-c-tag, .ik-tbl td.ik-c-pri, .ik-tbl td.ik-c-want { position:relative; }
.ik-tbl th.ik-c-main .ik-th-l, .ik-tbl th.ik-c-tag .ik-th-l,
.ik-tbl th.ik-c-pri .ik-th-l, .ik-tbl th.ik-c-want .ik-th-l { width:100%; text-align:center; }

/* An editable cell has to look pressable, or nobody presses it. */
.ik-pick { display:inline-flex; align-items:center; gap:5px; padding:1px 5px; border:1px dashed transparent;
  border-radius:7px; background:transparent; cursor:pointer; }
.ik-pick:hover { border-color:color-mix(in srgb, var(--ctp-lavender) 55%, transparent);
  background:color-mix(in srgb, var(--ctp-lavender) 8%, transparent); }
.ik-pick:disabled { opacity:.5; cursor:progress; }
.ik-pick-empty { font-size:10.5px; font-weight:700; color:var(--ctp-overlay0); }
.ik-pick:hover .ik-pick-empty { color:var(--ctp-lavender); }
/* Still waiting on a person. A visible dashed slot, because this is the one thing
   standing between the row and being approved (Mark 2026-08-04). */
.ik-pick-need { border-color:color-mix(in srgb, var(--ctp-peach) 45%, transparent);
  background:color-mix(in srgb, var(--ctp-peach) 7%, transparent); }
.ik-pick-need .ik-pick-empty { color:var(--ctp-peach); }
/* Multi-pick menus: the add / remove mark in front of each row. */
.ik-drop-m { flex:none; width:13px; text-align:center; font-size:10px; font-weight:800;
  color:var(--ctp-overlay0); }
.ik-drop-m-on { color:var(--ctp-red); }
.ik-drop-multi { padding-bottom:3px; font-style:italic; }
.ik-drop-mini { min-width:150px; }
.ik-want { padding:1px 7px; border:1px solid color-mix(in srgb, var(--ctp-sapphire) 40%, transparent);
  border-radius:7px; background:color-mix(in srgb, var(--ctp-sapphire) 12%, transparent);
  color:var(--ctp-sapphire); font-size:11px; font-weight:800; cursor:pointer; }
.ik-want:hover { background:color-mix(in srgb, var(--ctp-sapphire) 22%, transparent); }

/* ── Explorer batch bar on phones ─────────────────────────────────────────
   The bar is a single nowrap row of buttons centred with left-1/2 and a
   -50% translate, so it has no relationship to the viewport at all: at 500px
   it measured 742px wide and hung 126px off BOTH edges, and it sat UNDER the
   mobile tab bar because bottom-4 is below the 63px tabbar. Measured while
   testing multi-merge (feedback #7) — note it already overflowed at 615px
   without that button, so this is the pre-existing bar, not one control.
   Phones get: full width inside the viewport, wrapping rows, and a floor
   above the tabbar. Desktop is untouched. */
@media (max-width: 768px) {
  .oa-batchbar {
    left: 8px;
    right: 8px;
    transform: none;
    max-width: none;
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 8px;
    bottom: calc(var(--mobile-tabbar-h, 58px) + 8px);
  }
  /* The site combobox is w-56 (224px) and was forcing the row wider than the
     screen on its own — let it take the leftover width instead. */
  .oa-batchbar > input { flex: 1 1 140px; width: auto; min-width: 0; }
}
/* Popovers open UPWARD out of that bar (keeper picker, bulk priority). Both
   are anchored to their button with a fixed w-80/w-64, which runs off-screen
   once the button sits near an edge — the keeper panel was cut mid-word on a
   phone. Clamp to the viewport at every size; harmless on desktop. */
.oa-batchpop { max-width: calc(100vw - 16px); }
@media (max-width: 768px) {
  /* Anchor to the BAR, not the button, so a wrapped row can't push it out. */
  .oa-batchpop { position: fixed; left: 8px; right: 8px; width: auto;
    bottom: calc(var(--mobile-tabbar-h, 58px) + 56px); }
}

/* Industry Feed */
.industry-feed-hero {
  position: relative;
  overflow: hidden;
  border: 1px solid color-mix(in oklab, var(--ctp-mauve) 22%, var(--ctp-surface1));
  background:
    radial-gradient(circle at 88% 8%, color-mix(in oklab, var(--ctp-mauve) 20%, transparent), transparent 34%),
    linear-gradient(135deg, color-mix(in oklab, var(--ctp-surface0) 92%, var(--ctp-base)), var(--ctp-mantle));
}
.industry-feed-source-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
}
.industry-feed-source-card {
  min-width: 0;
  padding: 11px;
  border: 1px solid var(--ctp-surface1);
  border-radius: 10px;
  background: color-mix(in oklab, var(--ctp-surface0) 72%, transparent);
  text-align: left;
  transition: border-color .14s ease, transform .14s ease, background .14s ease;
}
.industry-feed-source-card:hover {
  transform: translateY(-1px);
  border-color: color-mix(in oklab, var(--ctp-mauve) 48%, var(--ctp-surface1));
}
.industry-feed-source-card.active {
  border-color: var(--ctp-mauve);
  background: color-mix(in oklab, var(--ctp-mauve) 10%, var(--ctp-surface0));
}
.industry-feed-source-mark {
  width: 31px;
  height: 31px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: 8px;
  background: color-mix(in oklab, var(--ctp-mauve) 16%, var(--ctp-surface1));
  color: var(--ctp-mauve);
  font: 800 10px/1 var(--font-display, sans-serif);
}
.industry-feed-item-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.industry-feed-item {
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--ctp-surface1);
  border-radius: 12px;
  background: color-mix(in oklab, var(--ctp-surface0) 76%, transparent);
  transition: border-color .14s ease, transform .14s ease, box-shadow .14s ease;
}
.industry-feed-item:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--ctp-blue) 48%, var(--ctp-surface1));
  box-shadow: 0 12px 30px rgb(0 0 0 / .16);
}
.industry-feed-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  display: block;
  object-fit: cover;
  background: var(--ctp-mantle);
}
.industry-feed-thumb-empty {
  width: 100%;
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ctp-overlay0);
  background:
    linear-gradient(135deg, color-mix(in oklab, var(--ctp-blue) 11%, var(--ctp-mantle)), var(--ctp-mantle));
}
.industry-feed-description {
  display: -webkit-box;
  overflow: hidden;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.industry-feed-subnav {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--ctp-surface1);
  border-radius: 9px;
  background: color-mix(in oklab, var(--ctp-mantle) 80%, transparent);
}
.industry-feed-subnav-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  border-radius: 6px;
  color: var(--ctp-overlay1);
  font-size: 11px;
  font-weight: 700;
  transition: color .12s ease, background .12s ease;
}
.industry-feed-subnav-btn span {
  color: var(--ctp-overlay0);
  font-family: var(--font-mono, monospace);
  font-size: 9px;
}
.industry-feed-subnav-btn:hover { color: var(--ctp-text); }
.industry-feed-subnav-btn.active {
  color: var(--ctp-text);
  background: var(--ctp-surface1);
  box-shadow: 0 1px 2px rgb(0 0 0 / .12);
}
.industry-feed-domain-filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border: 1px solid var(--ctp-surface1);
  border-radius: 6px;
  color: var(--ctp-overlay1);
  background: color-mix(in oklab, var(--ctp-surface0) 65%, transparent);
  font-size: 10px;
  font-weight: 700;
}
.industry-feed-domain-filter span { color: var(--ctp-overlay0); font: 700 9px/1 var(--font-mono, monospace); }
.industry-feed-domain-filter:hover { border-color: var(--ctp-surface2); color: var(--ctp-text); }
.industry-feed-domain-filter.active { border-color: var(--ctp-mauve); color: var(--ctp-mauve); background: color-mix(in oklab, var(--ctp-mauve) 9%, transparent); }
.industry-feed-kol-list {
  overflow: hidden;
  border: 1px solid var(--ctp-surface1);
  border-radius: 12px;
  background: color-mix(in oklab, var(--ctp-surface0) 70%, transparent);
}
.industry-feed-kol-row {
  width: 100%;
  display: grid;
  grid-template-columns: 34px minmax(220px, 1fr) minmax(170px, auto) 62px 78px 18px;
  gap: 12px;
  align-items: center;
  padding: 11px 14px;
  text-align: left;
  transition: background .12s ease;
}
.industry-feed-kol-row + .industry-feed-kol-row { border-top: 1px solid color-mix(in oklab, var(--ctp-surface1) 72%, transparent); }
.industry-feed-kol-row:hover { background: color-mix(in oklab, var(--ctp-mauve) 8%, transparent); }
.industry-feed-kol-avatar {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  border-radius: 50%;
  color: var(--ctp-mauve);
  background: color-mix(in oklab, var(--ctp-mauve) 15%, var(--ctp-surface1));
  font: 800 10px/1 var(--font-display, sans-serif);
}
.industry-feed-kol-avatar.large { width: 44px; height: 44px; font-size: 12px; }
.industry-feed-kol-person { min-width: 0; }
.industry-feed-kol-name { display: block; color: var(--ctp-text); font-size: 12.5px; font-weight: 700; }
.industry-feed-kol-headline { display: block; overflow: hidden; margin-top: 2px; color: var(--ctp-overlay1); font-size: 10.5px; text-overflow: ellipsis; white-space: nowrap; }
.industry-feed-kol-domains { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; }
.industry-feed-domain {
  display: inline-flex;
  align-items: center;
  min-height: 17px;
  padding: 0 5px;
  border: 1px solid;
  border-radius: 4px;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: .04em;
}
.industry-feed-domain.seo { color: var(--ctp-blue); background: color-mix(in oklab, var(--ctp-blue) 8%, transparent); }
.industry-feed-domain.igaming { color: var(--ctp-green); background: color-mix(in oklab, var(--ctp-green) 8%, transparent); }
.industry-feed-domain.ai { color: var(--ctp-mauve); background: color-mix(in oklab, var(--ctp-mauve) 8%, transparent); }
.industry-feed-kol-stat { display: flex; flex-direction: column; align-items: flex-end; color: var(--ctp-overlay1); }
.industry-feed-kol-stat b { color: var(--ctp-text); font: 700 12px/1.1 var(--font-mono, monospace); }
.industry-feed-kol-stat small { margin-top: 2px; font-size: 8.5px; }
.industry-feed-kol-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  justify-content: flex-end;
  background: rgb(0 0 0 / .55);
  backdrop-filter: blur(2px);
}
.industry-feed-kol-drawer {
  width: min(620px, 94vw);
  height: 100%;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--ctp-surface1);
  background: var(--ctp-base);
  box-shadow: -20px 0 60px rgb(0 0 0 / .35);
  animation: industry-feed-drawer-in .16s ease-out;
}
@keyframes industry-feed-drawer-in { from { transform: translateX(24px); opacity: .7; } to { transform: translateX(0); opacity: 1; } }
.industry-feed-kol-drawer-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--ctp-surface1); }
.industry-feed-kol-close { width: 28px; height: 28px; flex: none; border-radius: 6px; color: var(--ctp-overlay1); font-size: 22px; line-height: 1; }
.industry-feed-kol-close:hover { color: var(--ctp-text); background: var(--ctp-surface0); }
.industry-feed-kol-drawer-body { flex: 1; overflow-y: auto; padding: 20px; }
.industry-feed-kol-section-title { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; color: var(--ctp-subtext0); font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }
.industry-feed-kol-section-title span { color: var(--ctp-overlay0); font-family: var(--font-mono, monospace); }
.industry-feed-kol-channel,
.industry-feed-kol-appearance {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--ctp-surface1);
  border-radius: 8px;
  background: color-mix(in oklab, var(--ctp-surface0) 62%, transparent);
  color: var(--ctp-overlay1);
  transition: border-color .12s ease, background .12s ease;
}
.industry-feed-kol-channel:hover,
.industry-feed-kol-appearance:hover { border-color: color-mix(in oklab, var(--ctp-blue) 45%, var(--ctp-surface1)); background: color-mix(in oklab, var(--ctp-blue) 5%, var(--ctp-surface0)); }
.industry-feed-kol-channel b,
.industry-feed-kol-appearance b { display: block; overflow: hidden; color: var(--ctp-text); font-size: 11.5px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
.industry-feed-kol-channel small,
.industry-feed-kol-appearance small { display: block; margin-top: 2px; color: var(--ctp-overlay1); font-size: 9.5px; text-transform: capitalize; }
.industry-feed-kol-channel-icon { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; flex: none; border-radius: 6px; color: var(--ctp-mauve); background: color-mix(in oklab, var(--ctp-mauve) 12%, var(--ctp-surface1)); font: 800 8px/1 var(--font-display, sans-serif); }
.industry-feed-kol-empty { padding: 16px; border: 1px dashed var(--ctp-surface1); border-radius: 8px; color: var(--ctp-overlay1); font-size: 10.5px; line-height: 1.55; }
@media (max-width: 1180px) {
  .industry-feed-source-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .industry-feed-item-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .industry-feed-kol-row { grid-template-columns: 34px minmax(180px, 1fr) minmax(140px, auto) 70px 18px; }
  .industry-feed-kol-stat:first-of-type { display: none; }
}
@media (max-width: 680px) {
  .industry-feed-source-grid { display: flex; overflow-x: auto; padding-bottom: 4px; }
  .industry-feed-source-card { min-width: 190px; }
  .industry-feed-item-grid { grid-template-columns: minmax(0, 1fr); }
  .industry-feed-kol-row { grid-template-columns: 34px minmax(0, 1fr) 18px; gap: 9px; padding: 10px; }
  .industry-feed-kol-domains,
  .industry-feed-kol-stat { display: none; }
  .industry-feed-kol-drawer { width: 100%; }
  .industry-feed-kol-drawer-head { padding: 14px; }
  .industry-feed-kol-drawer-body { padding: 14px; }
}

/* ── Site GROUP pill (Mark 2026-08-22: "first always pick group") ──────────
   Lives immediately left of the SITE pill and gates it: O&O / Auctions /
   Sponsored / Trustpilot. Tinted per group so which set you're in
   is readable at a glance without opening anything — the site pill's green
   already means "a site is framed", so the group carries its own three
   colours rather than borrowing that one.
   Sizes track the h-9 (36px) header family. */
.site-group-wrap { flex: 0 0 auto; }
.group-pill { flex: 0 0 auto;
  transition: background-color .12s ease, border-color .12s ease; }
.group-ico { width: 1.25rem; height: 1.25rem; flex-shrink: 0; }
.group-pill-short { display: none; }
.group-pill-on.group-pill-portfolio { background: color-mix(in srgb, var(--ctp-green) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-green) 40%, transparent); color: var(--ctp-text); }
.group-pill-on.group-pill-portfolio:hover { background: color-mix(in srgb, var(--ctp-green) 18%, transparent); }
.group-pill-on.group-pill-auction { background: color-mix(in srgb, var(--ctp-peach) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-peach) 40%, transparent); color: var(--ctp-text); }
.group-pill-on.group-pill-auction:hover { background: color-mix(in srgb, var(--ctp-peach) 18%, transparent); }
.group-pill-on.group-pill-sp { background: color-mix(in srgb, var(--ctp-sapphire) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-sapphire) 40%, transparent); color: var(--ctp-text); }
.group-pill-on.group-pill-sp:hover { background: color-mix(in srgb, var(--ctp-sapphire) 18%, transparent); }
.group-pill-on.group-pill-trustpilot { background: color-mix(in srgb, var(--ctp-teal) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ctp-teal) 40%, transparent); color: var(--ctp-text); }
.group-pill-on.group-pill-trustpilot:hover { background: color-mix(in srgb, var(--ctp-teal) 18%, transparent); }
/* Step two, greyed until step one is done: with no group picked the site pill
   has nothing it could offer, so it reads as waiting rather than as broken.
   Still clickable — clicking it opens the GROUP menu, which is the next move. */
.site-pill-wait { opacity: .55; }
.site-pill-wait:hover { opacity: .8; }

/* Menu rows: a tinted tile per group carrying that group's own duotone glyph,
   the same grammar the Create (+) menu rows use. The tile is what gives the
   group colour a job — as a loose dot it just floated beside the label. */
.group-tile { width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center; }
.group-tile svg { width: 19px; height: 19px; }
.group-tile-portfolio { background: color-mix(in srgb, var(--ctp-green) 16%, transparent); }
.group-tile-auction { background: color-mix(in srgb, var(--ctp-peach) 16%, transparent); }
.group-tile-sp { background: color-mix(in srgb, var(--ctp-sapphire) 16%, transparent); }
.group-tile-trustpilot { background: color-mix(in srgb, var(--ctp-teal) 16%, transparent); }

/* The dot stays for places that name a group INLINE and have no room for a
   tile — the site picker's "AUCTIONS · 4 auction sites" header, /brand's key. */
.group-row { transition: background-color .12s ease; }
.group-row:hover { background: color-mix(in srgb, var(--ctp-surface0) 55%, transparent); }
.group-row-on { background: color-mix(in srgb, var(--ctp-surface0) 80%, transparent); }
.group-dot { width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0; }
.group-dot-portfolio { background: var(--ctp-green); box-shadow: 0 0 5px var(--ctp-green); }
.group-dot-auction { background: var(--ctp-peach); box-shadow: 0 0 5px var(--ctp-peach); }
.group-dot-sp { background: var(--ctp-sapphire); box-shadow: 0 0 5px var(--ctp-sapphire); }
.group-dot-trustpilot { background: var(--ctp-teal); box-shadow: 0 0 5px var(--ctp-teal); }

/* Under 1800 the group pill drops its chevron — the whole pill is the target,
   and that's ~14px back for the tab row (same trade as the other pills here). */
@media (min-width: 769px) and (max-width: 1799px) {
  .group-pill > svg:last-child { display: none; }
  .group-pill { padding-left: 10px; padding-right: 10px; }
  /* ~40px back for the site pill: the crosshair glyph plus the labelled GROUP
     pill right next to it already say what this control is, so the empty
     state's SITE badge is redundant text ("pick a s…" was the cost). */
  .site-frame-label { display: none; }
}

/* Phones: the header is already a two-row budget where every pixel is spoken
   for, so the group pill shows its GLYPH + a 4-letter short label (O&O / AUCT
   / SPON) and nothing else. Full labels live in the menu. The site pill keeps
   its width — WHICH site is framed still has to stay readable. */
@media (max-width: 768px) {
  .group-pill-long { display: none; }
  .group-pill-short { display: inline; }
  .group-pill { gap: 4px; padding-left: 7px; padding-right: 7px; }
  /* Row 2 of the phone header is EXACTLY full at 390px (measured: 54 search +
     72 group + 127 site + 3 icons + gaps + padding = 390), so the group pill
     has to buy its own space or the bell wraps to a third row.
     It buys it in two different ways depending on the state:
     · no group yet — the site pill is dimmed and unusable anyway, so IT gives
       the width up ("pick a site" truncating costs nothing);
     · group picked — the pill drops to its glyph alone, because the icon plus
       its tint already answer "which group", and the site pill needs every
       pixel back for the DOMAIN, which does have to stay readable. */
  .site-frame-wait { flex: 0 1 88px !important; min-width: 88px !important; }
  .group-pill-on .group-pill-short { display: none; }
  .group-pill-on { padding-left: 8px; padding-right: 8px; }
  /* Even with the group pill down to its glyph, row 2 is exactly full at 390px
     and the DOMAIN was the thing paying for it (19px of text = "m."). Three
     cuts, none of which removes a control, hand it ~42px back:
     the search box keeps only its magnifier (the block above already says that
     is target enough), the framed pill drops its chevron (the ✕ beside it
     already reads as interactive), and the crown scope loses 6px. */
  .uni-wrap { flex: 0 1 36px !important; min-width: 36px !important; height: 36px; }
  /* The compact search is a circle, not a squeezed text field. Let the input
     cover the full target; on focus, expand it so the query stays readable. */
  .uni-wrap .uni-search { position: relative; height: 36px; padding: 0;
    gap: 0; justify-content: center; }
  .uni-search > input { position: absolute; inset: 0; width: 100%; height: 100%;
    border-radius: inherit; opacity: 0; cursor: pointer; }
  .uni-wrap:focus-within .uni-search { position: fixed; left: 8px; right: 8px;
    width: auto; z-index: 60; padding: 0 12px; gap: 8px; justify-content: flex-start; }
  .uni-wrap:focus-within .uni-search > input { position: static; width: 0;
    opacity: 1; cursor: text; }
  .site-pill-on > svg:last-child { display: none; }
  .site-pill-on > div:first-child { width: 26px !important; height: 29px !important; }
  .group-pill > svg:last-child { display: none; }
  .group-pill-badge { display: none; }
  .group-ico { width: 1.05rem; height: 1.05rem; }
  /* Anchored like the other header dropdowns: pinned to the viewport so the
     wrapping two-row header can never clip the menu. */
  .site-group-wrap > .absolute {
    position: fixed; left: 10px; right: 10px; width: auto; max-width: none;
    top: calc(var(--lane-tabbar-h, 52px) + 6px); max-height: 60vh; }
}
/* Operator registry: two-pane directory (Mark 2026-08-22, "in a table and
   everything has its own column???"). The data has no fixed shape — one brand
   has a single licence holder, another has two parents plus a platform — so a
   grid went ragged and unreadable. A picker list plus one full record is the
   right form for a lookup. Hand-written because Tailwind is precompiled here
   and an arbitrary grid-cols value never reaches the built stylesheet. The
   breakpoint is 1100px, not Tailwind's lg: the lane sidebar eats ~224px, so
   the content area is always well under the window width. */
.opreg-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-items: start; }
@media (min-width: 1100px) {
  .opreg-split { grid-template-columns: minmax(300px, 360px) minmax(0, 1fr); }
  .opreg-split > .opreg-detail { position: sticky; top: calc(var(--lane-tabbar-h) + 16px); }
}
/* The picker scrolls inside itself so the record beside it stays in view;
   an arbitrary Tailwind max-h never reaches the built stylesheet either. */
.opreg-list { max-height: calc(100vh - var(--lane-tabbar-h) - 250px); min-height: 340px; overflow-y: auto; }

/* Registry counts, riding the toolbar instead of five identical stat tiles
   (Mark 2026-08-24: "doesnt look like a product, but looks like AI"). A row of
   equal-weight boxes is the giveaway; these are one line of text, and the two
   that correspond to a filter are buttons that apply it. */
.opreg-counts { display: inline-flex; align-items: center; gap: 10px; margin-left: 4px;
  font-size: 11.5px; color: var(--ctp-overlay1); white-space: nowrap; }
.opreg-counts b { color: var(--ctp-text); font-weight: 700; }
.opreg-count-btn { color: var(--ctp-overlay1); border-bottom: 1px dotted var(--ctp-surface2); cursor: pointer; }
.opreg-count-btn:hover { color: var(--ctp-text); }
.opreg-count-btn.on { color: var(--ctp-sapphire); border-bottom-color: var(--ctp-sapphire); }
.opreg-count-btn.on b { color: var(--ctp-sapphire); }
.opreg-count-mute { color: var(--ctp-overlay0); }
@media (max-width: 1500px) { .opreg-counts .opreg-count-mute { display: none; } }
@media (max-width: 1280px) { .opreg-counts { display: none; } }

/* Evidence state reuses the house priority-chip control, so the registry has no
   bespoke pill of its own. Colours follow meaning, not the P0-P5 heat scale. */
.v2-pf-btn.opreg-st-all.on { background: #5B5BD6; }
.v2-pf-btn.opreg-st-verified.on { background: #12B76A; }
.v2-pf-btn.opreg-st-needs_review.on { background: #F59E0B; }
.v2-pf-btn.opreg-st-candidate.on { background: #98A2B3; }
.v2-pf-btn.opreg-st-excluded.on { background: #D92D20; }
/* `.input` is full-width by design, which is right in a form and wrong on a
   toolbar — the market select ate the row and pushed the state chips off it. */
.oa-toolbar .opreg-ctl { width: auto; min-width: 132px; max-width: 200px;
  height: var(--tb-h); padding: 0 8px; font-size: var(--tb-fs); }
.oa-toolbar input.opreg-ctl { min-width: 170px; max-width: 240px; }

/* Registry rows, on a grid (Mark 2026-08-24: "makes it very claudey... make it
   not look like random"). Two faults, both structural rather than cosmetic:
   nothing lined up down the list because each element simply followed the one
   before it, and the strength label repeated verbatim on nearly every row, so
   seven consecutive lines read "Direct evidence" and the words stopped meaning
   anything. Fixed columns give the eye a spine to scan; the dots carry the
   strength and the word only appears when it is NOT the common case. */
.opreg-row { display: grid; grid-template-columns: 22px minmax(0, 1fr) 40px 78px;
  column-gap: 10px; align-items: center; padding: 8px 12px; width: 100%; text-align: left;
  border-left: 2px solid transparent; transition: background .12s ease; }
.opreg-row:hover { background: color-mix(in oklab, var(--ctp-surface0) 50%, transparent); }
.opreg-row.on { background: color-mix(in oklab, var(--ctp-sapphire) 10%, transparent);
  border-left-color: var(--ctp-sapphire); }
.opreg-row-mark { width: 22px; height: 22px; border-radius: 5px; object-fit: contain;
  background: color-mix(in oklab, var(--ctp-surface0) 60%, transparent); }
.opreg-row-mark-letter { display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; color: var(--ctp-overlay0); }
.opreg-row-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
.opreg-row-name > span:first-child { font-size: 12.5px; font-weight: 600; color: var(--ctp-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opreg-row-co { margin-top: 1px; font-size: 10.5px; color: var(--ctp-overlay1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opreg-row-co.none { color: var(--ctp-yellow); }
/* Dots sit in their own column so all four align down the list even when a row
   only has one lit. */
.opreg-dots { display: inline-flex; gap: 2.5px; justify-content: flex-start; }
.opreg-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ctp-surface1); display: block; }
.opreg-dots i.lit { background: currentColor; }
.opreg-flags { display: inline-flex; gap: 3px; align-items: center; justify-content: flex-end; }
.opreg-flags .geo-flag { flex: none; }
/* "+3" on its own said a number and nothing else. The whole market list is on
   the row's hover title now, and the counter is legible rather than a whisper. */
.opreg-flags-more { font-size: 9.5px; font-weight: 700; color: var(--ctp-overlay1);
  cursor: default; }
.opreg-tag { flex: none; border-radius: 4px; border: 1px solid; padding: 0 4px;
  font-size: 8.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; line-height: 15px; }

/* Registry record. The pane used to write a four-line paragraph per item and
   repeat the same caveat sentence on every row, so a brand listed in 19 markets
   produced 19 near-identical blocks (Mark 2026-08-24: "they look claudey").
   Rules now belong to the SECTION and are said once; the items are facts. */
.opreg-rec-head { display: grid; grid-template-columns: 32px minmax(0,1fr) auto; gap: 10px;
  align-items: center; padding: 13px 16px; border-bottom: 1px solid var(--ctp-surface1); }
.opreg-rec-mark { width: 32px; height: 32px; border-radius: 7px; object-fit: contain;
  background: color-mix(in oklab, var(--ctp-surface0) 60%, transparent); }
.opreg-rec-name { font-size: 17px; font-weight: 700; color: var(--ctp-text); line-height: 1.2; }
.opreg-rec-sub { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 2px;
  font-size: 10.5px; color: var(--ctp-overlay1); }
.opreg-rec-sub .cap { text-transform: capitalize; }
.opreg-rec-sub > span + span::before { content: '·'; margin-right: 10px; color: var(--ctp-surface2); }
.opreg-rec-body { padding: 4px 16px 16px; }

/* One heading weight, and the lead group gets a rule so the answer to "who runs
   it" is not visually equal to an affiliate programme. */
.opreg-grp { padding: 13px 0 3px; border-top: 1px solid var(--ctp-surface1); }
.opreg-grp:first-child { border-top: 0; }
.opreg-grp-title { font-size: 11px; font-weight: 700; color: var(--ctp-subtext0); margin-bottom: 6px; }
.opreg-grp.lead .opreg-grp-title { color: var(--ctp-sapphire); }
.opreg-grp-count { margin-left: 5px; font-weight: 600; color: var(--ctp-overlay0); }
.opreg-grp.lead .opreg-fact { border-left: 2px solid var(--ctp-sapphire); padding-left: 9px; }

.opreg-fact { padding: 5px 0; }
.opreg-fact + .opreg-fact { border-top: 1px dashed color-mix(in oklab, var(--ctp-surface1) 70%, transparent); }
.opreg-fact-main { display: flex; align-items: center; gap: 6px; min-width: 0; }
.opreg-fact-name { font-size: 12.5px; font-weight: 600; color: var(--ctp-text); }
.opreg-fact-name.mono { font-family: var(--font-mono, monospace); font-size: 11.5px; font-weight: 500; }
.opreg-fact-role { margin-left: auto; font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ctp-overlay0); white-space: nowrap; }
.opreg-fact-meta { display: flex; flex-wrap: wrap; gap: 4px 9px; margin-top: 1px; font-size: 10px; color: var(--ctp-overlay1); }
.opreg-fact-meta a { color: var(--ctp-sapphire); font-weight: 600; }
.opreg-fact-meta a:hover { text-decoration: underline; }

.opreg-empty { padding: 9px 0; font-size: 11px; color: var(--ctp-overlay1); }
.opreg-sub { margin-top: 9px; }
.opreg-sub-note { font-size: 10px; color: var(--ctp-overlay0); margin-bottom: 5px; }
.opreg-chiprow { display: flex; flex-wrap: wrap; gap: 4px; }
.opreg-mono-chip { font-family: var(--font-mono, monospace); font-size: 10px; color: var(--ctp-overlay1);
  background: color-mix(in oklab, var(--ctp-surface0) 55%, transparent); border-radius: 4px; padding: 2px 6px; }

/* 19 markets as a dense grid, not 19 paragraphs. */
.opreg-sightings { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.opreg-sighting { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px 2px 5px;
  border: 1px solid var(--ctp-surface1); border-radius: 5px; font-size: 10px; color: var(--ctp-overlay1); }
.opreg-sighting b { font-weight: 650; color: var(--ctp-subtext0); }
.opreg-sighting em { font-style: normal; font-family: var(--font-mono, monospace); color: var(--ctp-overlay0); }

/* Registry record as an actual record: label column, value column, evidence
   column (Mark 2026-08-24, "we need to do better here"). What was wrong: the
   role sat at `margin-left:auto` on an ~800px pane, so "888 UK Limited" and its
   own "LICENCE HOLDER" label were a thousand pixels apart, and four kinds of
   fact each got a heading above one row. The label column does that naming job,
   so every heading came out and the values share one left edge. */
/* Capped, and that cap is the point. Moving the role out of the right gutter
   fixed one gulf and left another: on a ~1900px pane the evidence column still
   sat at the far edge, ~1300px from the value it described (Mark 2026-08-24,
   "smtg is off"). Alignment cannot rescue a row that wide. A record has a
   readable measure and empty space to the right of it, the way every real
   register does. */
.opreg-rec-table { display: grid; grid-template-columns: 124px minmax(220px, 460px) minmax(0, 260px);
  column-gap: 20px; align-items: baseline; max-width: 900px; }
.opreg-rr { display: contents; }
.opreg-rr > dt { grid-column: 1; padding: 7px 0; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--ctp-overlay0); white-space: nowrap; }
.opreg-rr > dd { padding: 7px 0; min-width: 0; }
.opreg-rr-val { grid-column: 2; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.opreg-rr-ev { grid-column: 3; display: flex; align-items: center; gap: 8px; font-size: 10.5px; }
.opreg-rr-name { font-size: 13px; font-weight: 600; color: var(--ctp-text); }
.opreg-rr-name.mono { font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 500; }
.opreg-rr-src { color: var(--ctp-overlay0); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opreg-rr-ev a { color: var(--ctp-sapphire); font-weight: 700; text-decoration: none; }
.opreg-rr-ev a:hover { text-decoration: underline; }
/* Hairline between records, drawn across all three columns. */
.opreg-rr + .opreg-rr > dt, .opreg-rr + .opreg-rr > dd {
  border-top: 1px solid color-mix(in oklab, var(--ctp-surface1) 65%, transparent); }
/* The first row is the answer to "who runs this", so it carries a little weight.
   No heading needed to say so. */
.opreg-rr.lead > dt { color: var(--ctp-sapphire); }
.opreg-rr.lead .opreg-rr-name { font-size: 14px; font-weight: 700; }
@media (max-width: 1200px) {
  .opreg-rec-table { grid-template-columns: 110px minmax(0, 1fr); max-width: 100%; }
  .opreg-rr-ev { grid-column: 2; padding-top: 0; }
  .opreg-rr + .opreg-rr > .opreg-rr-ev { border-top: 0; }
}

/* Registry record sections. Each kind of evidence gets its OWN table because
   they do not share columns (Mark 2026-08-24: "different sections have their
   own tables ... not all data is showing"). Squeezing companies, domains and
   sightings into one label/value list could only render the fields all three
   had in common, which quietly dropped the per-row evidence note, the state,
   the validity window and the observed date. Tables are the house .oasis-table,
   so this adds no new table styling of its own. */
.opreg-sec { margin-top: 14px; }
.opreg-sec:first-of-type { margin-top: 6px; }
.opreg-sec-h { font-size: 11px; font-weight: 700; color: var(--ctp-subtext0); margin-bottom: 6px; }
.opreg-sec-h span { margin-left: 5px; font-weight: 600; color: var(--ctp-overlay0); }
.opreg-rec-note { margin: 10px 0 2px; font-size: 11.5px; color: var(--ctp-subtext0); }
/* A fact that is not current is flagged inline, so a historical parent can
   never read as the live one. */
.opreg-state { margin-left: 6px; padding: 1px 5px; border-radius: 4px; font-size: 8.5px;
  font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  background: color-mix(in oklab, var(--ctp-yellow) 16%, transparent); color: var(--ctp-yellow); }
.opreg-geo { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.opreg-note-cell { color: var(--ctp-subtext0); min-width: 130px; }
.opreg-limit-cell { color: var(--ctp-yellow); min-width: 130px; }
.oasis-table a { color: var(--ctp-sapphire); font-weight: 600; }
.oasis-table a:hover { text-decoration: underline; }
/* Dates, sources, ratings and role names are short fixed phrases: breaking them
   over three lines ("Aug / 22, / 2026") wastes more room than it saves. The
   note column is the one allowed to wrap and absorb the squeeze. */
.oasis-table td.opreg-nw { white-space: nowrap; }
.opreg-note-cell, .opreg-limit-cell { white-space: normal; }
.opreg-sec .oasis-table-wrap { max-height: none; }

/* A "NEEDS REVIEW" pill on the name line ate the column, so Stake rendered as
   "St..." and Boomerang as "B..." (Mark 2026-08-24: "we need to be able to read
   because a lot of things u cant see"). The status is one bit of information
   and does not need eleven characters to say it: a coloured dot carries it,
   names the state on hover, and costs 11px instead of ~90px. Verified is the
   ordinary case and shows nothing at all. */
.opreg-st-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; display: block; }
.opreg-st-dot.is-needs_review { background: var(--ctp-yellow); }
.opreg-st-dot.is-candidate { background: var(--ctp-overlay0); }
.opreg-st-dot.is-excluded { background: var(--ctp-red); }

/* Registry list: search and sort sit ON the list they act on (Mark 2026-08-24:
   "add a serch here pls", "and sorting too"), and the pager is gone in favour
   of loading the next page at the bottom of the scroll ("no pagination here
   pls"). The toolbar search still exists and still searches companies and
   domains; this one is the quick brand filter, right where you are looking. */
.opreg-listbar { display: grid; grid-template-columns: minmax(0, 1fr) 132px; gap: 6px;
  padding: 8px 10px; border-bottom: 1px solid var(--ctp-surface1); }
.opreg-listsearch, .opreg-listsort { width: 100%; height: 28px; padding: 0 8px; font-size: 11.5px; }
.opreg-listcount { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 5px 12px; border-bottom: 1px solid var(--ctp-surface1);
  font-size: 10.5px; color: var(--ctp-overlay1); }
.opreg-listcount b { color: var(--ctp-text); }
.opreg-listexport { font-size: 10px; font-weight: 700; color: var(--ctp-overlay1); }
.opreg-listexport:hover { color: var(--ctp-sapphire); }
.opreg-listexport:disabled { opacity: .4; }
.opreg-listfoot { padding: 8px; text-align: center; font-size: 10px; color: var(--ctp-overlay0);
  border-top: 1px solid var(--ctp-surface1); }

/* Registry record tables: ONE shape across all three (Mark 2026-08-24, "all
   different and sizing and placemnets are off"). Each table was sizing itself
   from its own content, so Market, Evidence, Source and Seen landed at a
   different x in every table and they read as three unrelated grids instead of
   one record. Fixed layout with shared column widths pins them into a single
   column rhythm: identity | qualifier | market | evidence | source | note | seen */
/* With the three sections on tabs they are never on screen together, so they no
   longer need identical geometry and the table can size itself. Fixed widths
   were the cause of both faults: they overflowed a narrow pane (the side scroll
   Mark hates) and, because the source column is conditional, position-based
   rules handed "Seen" the flexible width and crushed "What it says" to a sliver
   (Mark 2026-08-24: "you cant even see fuckign aything"). Short columns hold
   their content; the note takes what is left and wraps. */
.opreg-tbl { table-layout: auto; width: 100%; }
.opreg-tbl .c-id     { min-width: 130px; max-width: 260px; }
.opreg-tbl .c-qual   { min-width: 110px; max-width: 240px; }
.opreg-tbl .c-market { width: 1%; white-space: nowrap; }
.opreg-tbl .c-seen   { width: 1%; white-space: nowrap; }
.opreg-tbl .c-note   { width: auto; min-width: 150px; }
.opreg-tbl td.opreg-nw { text-overflow: ellipsis; overflow: hidden; }
.opreg-tbl td.c-id, .opreg-tbl td.c-qual { white-space: normal; word-break: break-word; }
/* No wrapper scroll: the table fits whatever width the pane gives it. */
.opreg-sec .oasis-table-wrap { overflow: hidden; }

/* Sortable headers, in the house table's own idiom. */
.opreg-tbl th.sortable { cursor: pointer; user-select: none; }
.opreg-tbl th.sortable:hover { color: var(--ctp-text); }
.opreg-tbl th.sortable i { font-style: normal; font-size: 8px; color: var(--ctp-sapphire); }

/* A cell is not a place for a paragraph. The clamp sits on a span inside the
   cell: applied to the <td> it fought the cell's own overflow and sheared the
   next line in half, which is the bleeding text under each row. */
.opreg-clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; white-space: normal; line-height: 1.45; }

/* Record tabs. Reuses the house chip control, so the record introduces no new
   control of its own. An empty tab stays clickable but reads as empty. */
.opreg-rectabs { margin: 10px 0 2px; }
.opreg-rectabs .v2-pf-btn { min-width: 0; }
.opreg-rectabs .v2-pf-btn b { margin-left: 5px; font-weight: 700; opacity: .75; }
.opreg-rectabs .v2-pf-btn.on { background: var(--ctp-sapphire); }
.opreg-rectabs .v2-pf-btn.is-empty { opacity: .45; }

/* The shared source moves onto the heading when every row cites the same one. */
.opreg-sec-h em { font-style: normal; font-weight: 500; margin-left: 8px;
  font-size: 10.5px; color: var(--ctp-overlay0); }
.opreg-sec-h em a { color: var(--ctp-sapphire); font-weight: 600; }
.opreg-sec-h em a:hover { text-decoration: underline; }
/* The tab carries the section name and its count, so the heading keeps only
   what the tab cannot say: where every row in it came from. */
.opreg-sec-h--slim { margin-bottom: 4px; min-height: 0; }
.opreg-sec-h--slim:empty { display: none; }
/* Evidence and source share a cell: they answer one question between them, and
   two columns for it is what pushed the table past the pane. */
.opreg-src-line { display: block; margin-top: 2px; font-size: 10px; color: var(--ctp-overlay0); }
.opreg-src-line a { color: var(--ctp-sapphire); font-weight: 600; }
.opreg-src-line a:hover { text-decoration: underline; }
.opreg-tbl .c-ev { min-width: 118px; white-space: normal; }

/* ── Niche switcher ───────────────────────────────────────────────────────
 * The top-level scope, above the lanes. It borrows .nav-tab / .nav-ico so it
 * matches every other sidebar row and keeps matching through the rail's
 * container queries (170px / 300px) — hardcoding a size here would drift.
 * Only the parts a lane does not have are declared. */
.mobile-brand-scope { display: none; }
.lane-tabs-links { display: contents; }
.nsw-wrap { position: relative; padding: 6px 8px 8px; border-bottom: 1px solid var(--glass-border); }
.nsw-btn { cursor: pointer; text-align: left; }
.nsw-btn .nsw-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Lanes grey their icon out when inactive. This row is a state readout rather
 * than a destination, so it keeps its colour. */
.oasis-sidebar .nav-tab.nsw-btn .nsw-ico { filter: none; }
.nsw-btn .nsw-ico svg { width: 100%; height: 100%; display: block; }
.nsw-car { width: 11px; height: 11px; flex: none; color: var(--ctp-overlay0); opacity: .55; }
.nsw-btn:hover .nsw-car { opacity: 1; }
/* No right to switch: no chevron, no hover, nothing to press. The missing
 * affordance is the signal — a padlock would be louder than the control. */
.nsw-btn.nsw-locked { cursor: default; }
.nsw-btn.nsw-locked:hover { background: transparent; }
.nav-rail .nsw-wrap { padding: 6px; }
.nav-rail .nsw-btn { justify-content: center; padding-left: 0; padding-right: 0; }

.nsw-scrim { display: none; }
.nsw-menu {
  position: absolute; left: calc(100% + 10px); top: 4px; width: 300px; z-index: 100;
  background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1);
  border-radius: 14px; box-shadow: 0 16px 44px rgba(45, 63, 78, .17); overflow: hidden;
}
.nsw-menu h4 {
  font-size: 9.5px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ctp-overlay0); padding: 13px 15px 8px; margin: 0;
}
.nsw-find {
  margin: 0 12px 8px; width: calc(100% - 24px); padding: 7px 10px; font: inherit; font-size: 13px;
  border: 1px solid var(--ctp-surface1); border-radius: 8px; background: var(--ctp-base); color: var(--ctp-text);
}
.nsw-find:focus { outline: none; border-color: var(--ctp-blue); background: var(--ctp-surface0); }
.nsw-list { max-height: 56vh; overflow-y: auto; padding: 0 8px 10px; }
.nsw-row {
  width: 100%; display: flex; align-items: center; gap: 10px; padding: 9px; margin-bottom: 1px;
  border: 1px solid transparent; border-radius: 10px; background: none; font: inherit;
  cursor: pointer; text-align: left;
}
.nsw-row:hover:not(:disabled) { background: var(--surface-hover); }
.nsw-row.cur { background: color-mix(in srgb, var(--ctp-blue) 10%, transparent);
               border-color: color-mix(in srgb, var(--ctp-blue) 22%, transparent); }
.nsw-row:disabled { cursor: default; }
.nsw-row.off .nsw-row-t b { color: var(--ctp-overlay0); }
.nsw-row.off .nsw-row-ico { opacity: .5; }
.nsw-row-ico { width: 22px; height: 22px; flex: none; }
.nsw-row-ico svg { width: 100%; height: 100%; display: block; }
.nsw-row-t { flex: 1; min-width: 0; }
.nsw-row-t b { display: block; font-size: 13px; font-weight: 600; color: var(--ctp-text); }
.nsw-row-t small { display: block; font-size: 10.5px; color: var(--ctp-overlay0); line-height: 1.35;
                   margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nsw-pill {
  flex: none; font-size: 9.5px; font-weight: 500; padding: 2px 6px; border-radius: 5px;
  border: 1px solid var(--ctp-surface1); color: var(--ctp-subtext0); background: var(--ctp-base);
}
.nsw-pill-empty { color: var(--ctp-overlay0); background: transparent; border-style: dashed; }
.nsw-tick { flex: none; font-size: 13px; font-weight: 700; color: var(--ctp-blue); }
.nsw-foot { border-top: 1px solid var(--ctp-crust); padding: 9px 15px; font-size: 10.5px; color: var(--ctp-overlay0); }

@media (max-width: 820px) {
  .nsw-menu { position: fixed; left: 8px; right: 8px; top: auto; bottom: 0; width: auto;
              border-radius: 14px 14px 0 0; }
  /* Mobile only: the sheet gets a dimmer behind it. */
  .nsw-scrim { display: block; position: fixed; inset: 0; z-index: 90; background: rgba(45, 63, 78, .4); }
}
/* Keep the sidebar's existing niche control visible when that sidebar becomes
   a drawer. Only the lane tabs scroll; the active niche stays beside the logo. */
@media (max-width: 768px) {
  .lane-tabs-strip { flex: 0 0 100%; overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .mobile-brand-scope { display: flex; align-items: center; gap: 5px; flex: none; }
  .lane-tabs-links { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0;
    overflow-x: auto; scrollbar-width: none; }
  .lane-tabs-links::-webkit-scrollbar { display: none; }
  .nsw-header.nsw-wrap { padding: 0; border: 0; }
  .nsw-header .nsw-btn { display: flex; align-items: center; gap: 6px; min-height: 34px;
    padding: 6px 9px; border: 1px solid var(--ctp-surface1); border-radius: 9px;
    background: var(--ctp-surface0); color: var(--ctp-text); font-size: 12px; font-weight: 600; }
  .nsw-header .nsw-btn .nsw-ico { width: 18px; height: 18px; flex: none; }
  .nsw-header .nsw-name { flex: none; }
  .nsw-mobile-overlay.nsw-menu { padding-bottom: env(safe-area-inset-bottom); }
}
@media (min-width: 769px) {
  .nsw-mobile-overlay { display: none; }
}

/* ── Operator coupon opportunities ────────────────────────────────────────
   This is a commercial decision surface inside the evidence registry: compact
   shortlist on the left, exact competitor proof and follow-up state on the
   right. It deliberately shares the registry's two-pane grammar. */
.opreg-mode { margin-left: 4px; flex: none; }
.opreg-mode .v2-pf-btn.on { background: var(--ctp-sapphire); }
.opreg-code, .opcoupon-codes code, .opcoupon-offer-top code {
  display: inline-flex; align-items: center; border-radius: 5px;
  border: 1px solid color-mix(in oklab, var(--ctp-teal) 35%, var(--ctp-surface1));
  background: color-mix(in oklab, var(--ctp-teal) 10%, transparent);
  color: var(--ctp-teal); font-family: var(--font-mono, monospace); font-size: 10px;
  font-weight: 700; line-height: 1; padding: 3px 6px;
}

.opcoupon-summary { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px; }
.opcoupon-stat { min-width: 0; padding: 10px 12px; border: 1px solid var(--ctp-surface1);
  border-radius: 10px; background: var(--ctp-mantle); }
.opcoupon-stat > span { display: block; color: var(--ctp-overlay0); font-size: 8.5px;
  font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
.opcoupon-stat > b { display: block; margin-top: 2px; color: var(--ctp-text);
  font-size: 21px; font-weight: 750; line-height: 1.1; font-variant-numeric: tabular-nums; }
.opcoupon-stat > em { display: block; margin-top: 3px; color: var(--ctp-overlay0);
  font-size: 9.5px; font-style: normal; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opcoupon-stat.is-ask { border-color: color-mix(in oklab, var(--ctp-teal) 28%, var(--ctp-surface1));
  background: color-mix(in oklab, var(--ctp-teal) 5%, var(--ctp-mantle)); }
.opcoupon-stat.is-ask > b { color: var(--ctp-teal); }
.opcoupon-stat.is-secured { border-color: color-mix(in oklab, var(--ctp-green) 25%, var(--ctp-surface1)); }
.opcoupon-stat.is-secured > b { color: var(--ctp-green); }

.opcoupon-controls { display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 7px 10px; border: 1px solid var(--ctp-surface1); border-radius: 9px;
  background: color-mix(in oklab, var(--ctp-mantle) 88%, transparent); }
.opcoupon-controls .v2-pf-btn.on { background: var(--ctp-sapphire); }
.opcoupon-limit { margin-left: auto; max-width: 430px; color: var(--ctp-overlay0);
  font-size: 9.5px; line-height: 1.3; text-align: right; }

.opcoupon-split { display: grid; grid-template-columns: minmax(480px, 1.1fr) minmax(420px, .9fr);
  gap: 12px; align-items: start; }
.opcoupon-detail { position: sticky; top: calc(var(--lane-tabbar-h) + 16px); }
.opcoupon-listbar { min-height: 43px; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 7px 11px; border-bottom: 1px solid var(--ctp-surface1);
  color: var(--ctp-overlay1); font-size: 10.5px; }
.opcoupon-listbar b { color: var(--ctp-text); font-weight: 700; }
.opcoupon-listbar em { color: var(--ctp-overlay0); font-style: normal; }
.opcoupon-sort { width: 180px; height: 28px; padding: 0 7px; font-size: 10.5px; }
.opcoupon-list { max-height: calc(100vh - var(--lane-tabbar-h) - 300px); min-height: 360px;
  overflow-y: auto; }
.opcoupon-row { width: 100%; display: grid; grid-template-columns: 30px minmax(0,1fr) 132px;
  gap: 9px; align-items: start; padding: 10px 11px; border-bottom: 1px solid var(--ctp-surface1);
  text-align: left; transition: background .12s ease, box-shadow .12s ease; }
.opcoupon-row:hover { background: color-mix(in oklab, var(--ctp-surface0) 50%, transparent); }
.opcoupon-row.on { background: color-mix(in oklab, var(--ctp-sapphire) 9%, transparent);
  box-shadow: inset 3px 0 0 var(--ctp-sapphire); }
.opcoupon-mark { width: 30px; height: 30px; border-radius: 7px; object-fit: contain;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1); }
.opcoupon-letter { display: inline-flex; align-items: center; justify-content: center;
  color: var(--ctp-sapphire); font-weight: 800; background: color-mix(in oklab, var(--ctp-sapphire) 11%, var(--ctp-base)); }
.opcoupon-name { display: block; color: var(--ctp-text); font-size: 12.5px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.opcoupon-why { display: block; margin-top: 1px; color: var(--ctp-overlay1); font-size: 10px; }
.opcoupon-why b { color: var(--ctp-subtext0); font-weight: 700; }
.opcoupon-codes { min-height: 17px; display: flex; align-items: center; flex-wrap: wrap; gap: 3px;
  margin-top: 5px; }
.opcoupon-codes em { color: var(--ctp-overlay0); font-size: 9px; font-style: normal; }
.opcoupon-row-side { display: flex; align-items: flex-end; flex-direction: column; gap: 4px; min-width: 0; }
.opcoupon-signal, .opcoupon-status { display: inline-flex; align-items: center; max-width: 100%;
  border-radius: 999px; padding: 2px 7px; font-size: 8.5px; font-weight: 750;
  line-height: 1.25; white-space: nowrap; }
.opcoupon-signal.is-repeatable { color: var(--ctp-green); background: color-mix(in oklab, var(--ctp-green) 11%, transparent); }
.opcoupon-signal.is-proven { color: var(--ctp-teal); background: color-mix(in oklab, var(--ctp-teal) 10%, transparent); }
.opcoupon-signal.is-candidate { color: var(--ctp-yellow); background: color-mix(in oklab, var(--ctp-yellow) 9%, transparent); }
.opcoupon-status { color: var(--ctp-overlay1); border: 1px solid var(--ctp-surface1); }
.opcoupon-status.is-shortlisted, .opcoupon-status.is-requested { color: var(--ctp-sapphire); border-color: color-mix(in oklab, var(--ctp-sapphire) 35%, var(--ctp-surface1)); }
.opcoupon-status.is-negotiating { color: var(--ctp-yellow); border-color: color-mix(in oklab, var(--ctp-yellow) 35%, var(--ctp-surface1)); }
.opcoupon-status.is-secured { color: var(--ctp-green); border-color: color-mix(in oklab, var(--ctp-green) 35%, var(--ctp-surface1)); }
.opcoupon-status.is-declined, .opcoupon-status.is-not_fit { color: var(--ctp-red); border-color: color-mix(in oklab, var(--ctp-red) 30%, var(--ctp-surface1)); }
.opcoupon-seen { color: var(--ctp-overlay0); font-family: var(--font-mono, monospace); font-size: 8.5px; }
.opcoupon-empty { min-height: 170px; display: flex; align-items: center; justify-content: center;
  flex-direction: column; gap: 4px; padding: 25px; text-align: center; }
.opcoupon-empty b { color: var(--ctp-subtext0); font-size: 12px; }
.opcoupon-empty span { color: var(--ctp-overlay0); font-size: 10px; }

.opcoupon-head { min-height: 56px; display: grid; grid-template-columns: 32px minmax(0,1fr) auto;
  gap: 9px; align-items: center; padding: 10px 12px; border-bottom: 1px solid var(--ctp-surface1); }
.opcoupon-head h2 { color: var(--ctp-text); font-size: 16px; font-weight: 750; line-height: 1.15; }
.opcoupon-head p { margin-top: 2px; color: var(--ctp-overlay1); font-size: 9.5px; }
.opcoupon-detail-body { max-height: calc(100vh - var(--lane-tabbar-h) - 115px); overflow-y: auto; }
.opcoupon-detail-body > section { padding: 12px; }
.opcoupon-detail-body > section + section { border-top: 1px solid var(--ctp-surface1); }
.opcoupon-sec-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-bottom: 7px; }
.opcoupon-sec-head h3 { color: var(--ctp-subtext0); font-size: 10.5px; font-weight: 750; }
.opcoupon-sec-head > span { max-width: 60%; color: var(--ctp-overlay0); font-size: 9px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opcoupon-evidence { display: grid; gap: 6px; }
.opcoupon-offer { padding: 8px 9px; border: 1px solid var(--ctp-surface1); border-radius: 8px;
  background: var(--ctp-base); }
.opcoupon-offer.is-candidate { border-left: 2px solid var(--ctp-yellow); }
.opcoupon-offer.is-current { border-left: 2px solid var(--ctp-teal); }
.opcoupon-offer.is-expired, .opcoupon-offer.is-historical { opacity: .72; }
.opcoupon-offer-top { display: flex; align-items: center; flex-wrap: wrap; gap: 5px 9px;
  color: var(--ctp-overlay1); font-size: 9px; }
.opcoupon-offer-top > span:nth-of-type(1) { color: var(--ctp-subtext0); font-family: var(--font-mono, monospace); }
.opcoupon-offer p { margin-top: 6px; color: var(--ctp-subtext0); font-size: 10.5px; line-height: 1.45; }
.opcoupon-offer-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  margin-top: 6px; color: var(--ctp-overlay0); font-size: 8.5px; }
.opcoupon-offer-foot a { margin-left: auto; color: var(--ctp-sapphire); font-weight: 650; }
.opcoupon-offer-foot a:hover { text-decoration: underline; }

.opcoupon-outreach { background: color-mix(in oklab, var(--ctp-surface0) 20%, transparent); }
.opcoupon-flow { display: flex; flex-wrap: wrap; gap: 4px; }
.opcoupon-flow-btn { min-height: 25px; padding: 3px 8px; border: 1px solid var(--ctp-surface1);
  border-radius: 6px; color: var(--ctp-overlay1); background: var(--ctp-base); font-size: 9px; font-weight: 650; }
.opcoupon-flow-btn:hover { color: var(--ctp-text); border-color: var(--ctp-surface2); }
.opcoupon-flow-btn.on { color: var(--ctp-base); border-color: transparent; background: var(--ctp-sapphire); }
.opcoupon-flow-btn.is-secured.on { background: var(--ctp-green); }
.opcoupon-flow-btn.is-declined.on, .opcoupon-flow-btn.is-not_fit.on { background: var(--ctp-red); }
.opcoupon-outreach > label { display: block; margin-top: 9px; }
.opcoupon-outreach label > span { display: block; margin-bottom: 3px; color: var(--ctp-overlay0);
  font-size: 8.5px; font-weight: 750; letter-spacing: .06em; text-transform: uppercase; }
.opcoupon-outreach textarea, .opcoupon-outreach input[type='date'] { width: 100%; border: 1px solid var(--ctp-surface1);
  border-radius: 7px; background: var(--ctp-base); color: var(--ctp-text); font-size: 10.5px; outline: none; }
.opcoupon-outreach textarea { padding: 7px 8px; resize: vertical; }
.opcoupon-outreach input[type='date'] { height: 34px; padding: 0 8px; }
.opcoupon-outreach textarea:focus, .opcoupon-outreach input[type='date']:focus { border-color: var(--ctp-sapphire); }
.opcoupon-save-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; margin-top: 8px; }
.opcoupon-save-row label { width: 165px; }
.opcoupon-save-row button:disabled, .opcoupon-flow-btn:disabled { opacity: .5; cursor: wait; }

@media (max-width: 1280px) {
  .opcoupon-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .opcoupon-split { grid-template-columns: minmax(400px, 1fr) minmax(360px, .9fr); }
  .opcoupon-limit { display: none; }
}
@media (max-width: 940px) {
  .opreg-mode { display: none; }
  .opcoupon-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .opcoupon-split { grid-template-columns: minmax(0, 1fr); }
  .opcoupon-detail { position: static; }
  .opcoupon-list { max-height: 520px; }
  .opcoupon-controls { align-items: flex-start; flex-direction: column; }
}
@media (max-width: 600px) {
  .opcoupon-summary { grid-template-columns: minmax(0, 1fr); }
  .opcoupon-row { grid-template-columns: 28px minmax(0,1fr); }
  .opcoupon-row-side { grid-column: 2; align-items: flex-start; flex-direction: row; flex-wrap: wrap; }
  .opcoupon-listbar { align-items: flex-start; flex-direction: column; }
  .opcoupon-head { grid-template-columns: 32px minmax(0,1fr); }
  .opcoupon-head > .opcoupon-signal { grid-column: 2; justify-self: start; }
}

/* ── Row delete (Topics Explorer) ────────────────────────────────────────
   The only way to delete a topic used to be a trash icon buried inside the
   expanded topic panel, so from the table there was no delete at all
   (Mark 2026-08-30). The bin now sits on the row itself, quiet until the
   row is hovered. Hand-written rather than Tailwind's group-hover/: the
   named-group variants are PRECOMPILED, and a fresh group name compiles to
   nothing, which would have left the button stuck at opacity 0 forever. */
.v2-row-del { opacity: 0; transition: opacity .12s ease; }
tr:hover > td .v2-row-del,
.v2-row-del:focus-visible { opacity: 1; }
/* Touch has no hover, so on a phone or tablet it is simply always there. */
@media (hover: none) {
  .v2-row-del { opacity: .6; }
}

/* ── Content Planner v2 cards (field test 2026-09-01) ───────────────────────
   The v1 .scp-card is a FIXED 148px with overflow:hidden — right for uniform
   plan-row cards, wrong here: a v2 card carries a variable number of warning
   boxes (status drift, lineup, no-URL) and clipping them hides the entire
   point of the board. So this card grows with its content and every text run
   truncates on its own axis instead. */
/* FIXED height, so every card in every lane is the same block and the rows line
   up across columns. This became possible once the warning boxes moved into the
   drawer — while they were on the card face the height had to grow with them.
   Each row therefore reserves its space rather than collapsing when empty, and
   nothing is allowed to wrap into a second line. */
.pv2-card { border: 1px solid var(--ctp-surface0); border-radius: 6px;
  height: 158px; padding: 10px 11px 26px; background: var(--ctp-base); cursor: pointer;
  flex-shrink: 0; display: flex; flex-direction: column; gap: 4px;
  min-width: 0; box-sizing: border-box; overflow: hidden;
  transition: background .12s, border-color .12s, box-shadow .12s;
  box-shadow: 0 1px 1px rgb(0 0 0 / .05); }
.pv2-card:hover { border-color: var(--ctp-surface2);
  box-shadow: 0 2px 6px rgb(0 0 0 / .09), inset 2.5px 0 0 var(--col-ac); }
/* Chip rows wrap — a long topic label plus P-chip plus volume must never push
   the row into an overflow that the card then clips. */
.pv2-chips { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px;
  min-width: 0; overflow: hidden; flex-shrink: 0; }
.pv2-chip { font-size: 9.5px; font-weight: 600; line-height: 1.5; border-radius: 99px;
  padding: 1px 7px; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.pv2-chip-topic { background: rgb(var(--ctp-mauve-rgb) / .12); color: var(--ctp-mauve); }
.pv2-chip-info { border: 1px solid var(--ctp-surface1); color: var(--ctp-overlay1); }
.pv2-chip-ask { border: 1px solid var(--ctp-yellow); color: var(--ctp-yellow); }
.pv2-chip-pri { background: rgb(var(--ctp-peach-rgb) / .15); color: var(--ctp-peach);
  font-weight: 700; font-variant-numeric: tabular-nums; }
.pv2-chip-num { color: var(--ctp-overlay1); font-variant-numeric: tabular-nums;
  font-weight: 500; padding: 1px 2px; }
.pv2-chip-st { background: var(--ctp-surface0); color: var(--ctp-subtext0);
  border-radius: 4px; max-width: 100%; }
.pv2-title { font-size: 12.5px; font-weight: 650; line-height: 1.35; color: var(--ctp-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-width: 0; text-decoration: none; word-break: break-word;
  height: 2.7em; flex: 1 1 auto; }
a.pv2-title:hover { color: var(--ctp-mauve); }
.pv2-kw { font-size: 10px; color: var(--ctp-overlay1); min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  height: 1.4em; flex-shrink: 0; }
/* Meta row: health · market · ages. Wraps rather than squeezing, and each cell
   ellipsizes, so nothing is ever half-rendered. */
.pv2-foot { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px;
  font-size: 10px; color: var(--ctp-overlay1); min-width: 0;
  margin-top: auto; overflow: hidden; flex-shrink: 0; }
.pv2-foot > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv2-h-ok { color: var(--ctp-green); font-weight: 700; font-variant-numeric: tabular-nums; }
.pv2-h-gone { color: var(--ctp-red); font-weight: 700; font-variant-numeric: tabular-nums; }
.pv2-h-redir { color: var(--ctp-peach); font-weight: 600; max-width: 100%; }
.pv2-h-none { color: var(--ctp-overlay0); }
.pv2-age { font-variant-numeric: tabular-nums; }
.pv2-warn { font-size: 10px; line-height: 1.4; border-radius: 5px; padding: 4px 7px;
  white-space: normal; word-break: break-word;
  border: 1px solid rgb(var(--ctp-yellow-rgb) / .5); color: var(--ctp-yellow);
  background: rgb(var(--ctp-yellow-rgb) / .07); }
.pv2-warn-red { border-color: rgb(var(--ctp-red-rgb) / .5); color: var(--ctp-red);
  background: rgb(var(--ctp-red-rgb) / .07); }
/* Drawer field grid — label column fixed, value wraps. */
.pv2-row { display: grid; grid-template-columns: 104px 1fr; gap: 10px;
  font-size: 12px; padding: 5px 0; border-bottom: 1px solid rgb(var(--ctp-surface0-rgb) / .6); }
.pv2-row-k { color: var(--ctp-overlay1); font-weight: 600; font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .04em; padding-top: 1px; }
.pv2-row-v { color: var(--ctp-text); min-width: 0; word-break: break-word; }
.pv2-sec { font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin: 14px 0 4px; }
/* Drawer chrome — own classes rather than tailwind opacity utilities, which are
   only compiled if already used somewhere (a missing /40 tint renders nothing). */
.pv2-scrim { position: fixed; inset: 0; z-index: 55; background: rgb(var(--ctp-crust-rgb) / .45); }
.pv2-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 56;
  width: 420px; max-width: 92vw; display: flex; flex-direction: column;
  background: var(--ctp-base); border-left: 1px solid var(--ctp-surface1);
  box-shadow: -14px 0 40px -12px rgb(0 0 0 / .45); }
.pv2-drawer-head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center;
  gap: 8px; padding: 11px 14px; background: var(--ctp-mantle);
  border-bottom: 1px solid var(--ctp-surface1); flex-shrink: 0; }
.pv2-drawer-body { overflow-y: auto; padding: 14px; flex: 1 1 auto; }
.pv2-drawer-title { font-size: 14px; font-weight: 700; line-height: 1.35;
  color: var(--ctp-text); margin-bottom: 10px; word-break: break-word; }
.pv2-x { flex-shrink: 0; margin-left: auto; width: 26px; height: 26px; border-radius: 6px;
  border: 1px solid var(--ctp-surface1); background: transparent; cursor: pointer;
  color: var(--ctp-overlay1); font-size: 13px; line-height: 1; }
.pv2-x:hover { color: var(--ctp-text); background: var(--ctp-surface1); }
.pv2-lane-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.pv2-link { color: var(--ctp-mauve); text-decoration: none; font-size: 12px; font-weight: 600; }
.pv2-link:hover { text-decoration: underline; }
/* Toolbar filter pills for the v2 board. */
.pv2-pill { font-size: 11px; font-weight: 600; border-radius: 99px; padding: 4px 12px;
  cursor: pointer; background: transparent; border: 1px solid var(--ctp-surface1);
  color: var(--ctp-overlay1); transition: background .12s, color .12s, border-color .12s; }
.pv2-pill:hover { color: var(--ctp-text); border-color: var(--ctp-surface2); }
.pv2-pill-on { background: rgb(var(--ctp-mauve-rgb) / .15); border-color: var(--ctp-mauve);
  color: var(--ctp-mauve); }
.pv2-pill-n { font-variant-numeric: tabular-nums; opacity: .8; margin-left: 3px; }
.pv2-bar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 9px; }
.pv2-bar-note { font-size: 10.5px; color: var(--ctp-overlay0); }
/* v2 drawer — priority scale, tag/format chips, keyword table, plan rows. */
.pv2-pri-scale { display: flex; gap: 3px; }
.pv2-pri-scale .scp-card-pri { opacity: .22; }
.pv2-pri-scale .scp-card-pri.on { opacity: 1; }
.pv2-tag { font-size: 9.5px; font-weight: 600; border-radius: 4px; padding: 1px 6px;
  background: var(--ctp-surface0); color: var(--ctp-subtext0); }
.pv2-why { font-size: 11.5px; line-height: 1.5; color: var(--ctp-subtext1);
  background: var(--ctp-mantle); border: 1px solid var(--ctp-surface0);
  border-radius: 6px; padding: 8px 10px; }
.pv2-why-src { font-size: 9.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ctp-mauve); }
.pv2-kwtoggle { font-size: 11px; font-weight: 600; color: var(--ctp-mauve); background: none;
  border: none; cursor: pointer; padding: 0; }
.pv2-kwtoggle:hover { text-decoration: underline; }
.pv2-kwtbl { width: 100%; border-collapse: collapse; font-size: 11px; margin-top: 6px; }
.pv2-kwtbl th { text-align: left; font-size: 9px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ctp-overlay0); padding: 3px 6px;
  border-bottom: 1px solid var(--ctp-surface1); position: sticky; top: 0; background: var(--ctp-base); }
.pv2-kwtbl td { padding: 3px 6px; border-bottom: 1px solid rgb(var(--ctp-surface0-rgb) / .5);
  color: var(--ctp-subtext0); }
.pv2-kwtbl td:last-child { text-align: right; font-variant-numeric: tabular-nums; color: var(--ctp-text); }
.pv2-kwwrap { max-height: 260px; overflow-y: auto; border: 1px solid var(--ctp-surface0);
  border-radius: 6px; margin-top: 6px; }
.pv2-planrow { border: 1px solid var(--ctp-surface0); border-radius: 6px; padding: 7px 9px;
  margin-top: 5px; font-size: 11.5px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.pv2-vol-big { font-size: 15px; font-weight: 700; color: var(--ctp-text);
  font-variant-numeric: tabular-nums; }
.pv2-vol-sub { font-size: 10.5px; color: var(--ctp-overlay1); }
/* v2 card in the Oasis card idiom: site line, corner flag, ClickUp button. */
.pv2-card { position: relative; } /* anchors the corner flag + task button */
.pv2-site { display: flex; align-items: center; gap: 5px; font-size: 10px; font-weight: 650;
  color: var(--ctp-subtext0); white-space: nowrap; overflow: hidden; min-width: 0;
  height: 14px; flex: 0 0 14px;   /* never squeezed by the card's fixed height */
  padding-right: 56px; /* clear of the corner flag + ClickUp button */ }
.pv2-site img { width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0; }
.pv2-site span { overflow: hidden; text-overflow: ellipsis; }
.pv2-corner { position: absolute; top: 9px; right: 10px; display: flex; align-items: center;
  gap: 5px; }
/* Open-in-ClickUp button — the app's own ClickUp mark (already used by the
   push dialog as .scp-push-mark), not a generic arrow. */
.pv2-cu { width: 20px; height: 20px; border-radius: 5px; border: 1px solid var(--ctp-surface1);
  background: var(--ctp-mantle); display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; flex-shrink: 0; padding: 0;
  transition: border-color .12s, background .12s, transform .12s; }
.pv2-cu img { width: 12px; height: 12px; display: block; opacity: .82; }
.pv2-cu:hover { border-color: var(--ctp-mauve); background: var(--ctp-base); transform: scale(1.08); }
.pv2-cu:hover img { opacity: 1; }
.pv2-geo-txt { font-size: 8.5px; font-weight: 700; letter-spacing: .03em; color: var(--ctp-overlay1);
  background: var(--ctp-surface0); border-radius: 3px; padding: 1.5px 4px; line-height: 1; }
/* Title row: priority block sits left of the task name, like the plan cards. */
.pv2-titlerow { display: flex; gap: 7px; align-items: flex-start; min-width: 0;
  overflow: hidden; flex-shrink: 0; }
.pv2-titlerow .scp-card-pri { flex: 0 0 auto; }
.pv2-dates { display: flex; flex-wrap: nowrap; gap: 10px; font-size: 9.5px;
  color: var(--ctp-overlay0); font-variant-numeric: tabular-nums;
  overflow: hidden; flex-shrink: 0; padding-right: 26px; }
.pv2-dates b { font-weight: 600; color: var(--ctp-overlay1); }
/* Horizontal-scroll affordance for the v2 board. The lanes overflow to the
   right (301 sits off-screen at common widths) and a bare overflow gives no
   hint that anything is there, so each edge that has more content fades out
   under a clickable arrow. */
.pv2-boardwrap { position: relative; }
.pv2-scrollhint { position: absolute; top: 0; bottom: 0; width: 96px; z-index: 4;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px; pointer-events: none; }
.pv2-scrollhint-r { right: 0;
  background: linear-gradient(to right, rgb(var(--ctp-base-rgb) / 0), rgb(var(--ctp-base-rgb) / .92)); }
.pv2-scrollhint-l { left: 0;
  background: linear-gradient(to left, rgb(var(--ctp-base-rgb) / 0), rgb(var(--ctp-base-rgb) / .92)); }
/* Lane-scroll button. Purple by DEFAULT (this used to be the hover state and
   the resting state was a pale circle, which read as decoration rather than a
   control) with a stroked chevron so the arrow is legibly bold at a glance. */
.pv2-scrollbtn { pointer-events: auto; cursor: pointer; width: 42px; height: 42px;
  margin: 0 8px; border-radius: 50%; border: 1px solid var(--ctp-mauve);
  background: var(--ctp-mauve); color: var(--ctp-base); line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px -3px rgb(var(--ctp-mauve-rgb) / .55);
  transition: transform .12s, background .12s, box-shadow .12s;
  animation: pv2-nudge 2.4s ease-in-out infinite; }
.pv2-scrollbtn svg { width: 22px; height: 22px; }
.pv2-scrollhint-l .pv2-scrollbtn { animation-direction: reverse; }
/* Hover deepens rather than inverting again — an inverted hover on an already
   filled button reads as the control switching off. */
.pv2-scrollbtn:hover { transform: scale(1.1); animation: none;
  box-shadow: 0 6px 18px -3px rgb(var(--ctp-mauve-rgb) / .7);
  background: color-mix(in oklab, var(--ctp-mauve) 88%, black); }
.pv2-scrollbtn:focus-visible { outline: 2px solid var(--ctp-text); outline-offset: 2px; }
.pv2-scrollbtn-n { font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  color: var(--ctp-subtext0); pointer-events: none; white-space: nowrap;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1);
  border-radius: 99px; padding: 2px 7px; box-shadow: 0 2px 6px -2px rgb(0 0 0 / .2); }
@keyframes pv2-nudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) { .pv2-scrollbtn { animation: none; } }
/* Per-card health light. Replaces the "needs attention" filter: the signal
   belongs on every card, not behind a toggle. Red = unresolved issues, green =
   clean or everything disregarded. The issues themselves live in the drawer. */
.pv2-light { width: 11px; height: 11px; border-radius: 50%; flex-shrink: 0;
  position: relative; cursor: help; }
.pv2-light-ok { background: var(--ctp-green);
  box-shadow: 0 0 0 2px rgb(var(--ctp-green-rgb) / .22); }
.pv2-light-bad { background: var(--ctp-peach);
  box-shadow: 0 0 0 2px rgb(var(--ctp-peach-rgb) / .25);
  animation: pv2-pulse 2.2s ease-in-out infinite; }
@keyframes pv2-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgb(var(--ctp-peach-rgb) / .25); }
  50%      { box-shadow: 0 0 0 5px rgb(var(--ctp-peach-rgb) / .12); }
}
@media (prefers-reduced-motion: reduce) { .pv2-light-bad { animation: none; } }
/* Drawer issue rows with a disregard control. */
.pv2-issue { display: flex; align-items: flex-start; gap: 8px; font-size: 11.5px;
  line-height: 1.45; border-radius: 6px; padding: 7px 9px; margin-top: 5px;
  border: 1px solid rgb(var(--ctp-peach-rgb) / .5); color: var(--ctp-peach);
  background: rgb(var(--ctp-peach-rgb) / .07); }
.pv2-issue-off { border-color: var(--ctp-surface1); color: var(--ctp-overlay0);
  background: transparent; }
.pv2-issue-txt { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.pv2-issue-off .pv2-issue-txt { text-decoration: line-through; }
.pv2-issue-btn { flex-shrink: 0; cursor: pointer; background: transparent;
  border: 1px solid currentColor; border-radius: 5px; padding: 1px 7px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: inherit; opacity: .75; }
.pv2-issue-btn:hover { opacity: 1; }
.pv2-clean { font-size: 11.5px; color: var(--ctp-green); display: flex; align-items: center; gap: 7px; }
/* Light inline in a section heading. */
.pv2-sec .pv2-light { display: inline-block; vertical-align: middle; margin-left: 6px; }
.pv2-issue-count { font-size: 9.5px; font-weight: 700; letter-spacing: .04em; color: var(--ctp-peach);
  margin-left: 5px; text-transform: none; }

/* Dragon masterlist rows in the drawer. */
.pv2-dragon { max-height: 240px; overflow-y: auto; border: 1px solid var(--ctp-surface0);
  border-radius: 6px; margin-top: 6px; }
.pv2-dg-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 8px; padding: 5px 8px;
  border-bottom: 1px solid rgb(var(--ctp-surface0-rgb) / .5); font-size: 11px; align-items: center; }
.pv2-dg-row:last-child { border-bottom: none; }
.pv2-dg-brand { font-weight: 600; color: var(--ctp-text); min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.pv2-dg-page { font-size: 9.5px; color: var(--ctp-overlay1); white-space: nowrap; }
.pv2-dg-links { grid-column: 1 / -1; display: flex; gap: 10px; font-size: 10px; }
.pv2-dg-links a { color: var(--ctp-mauve); text-decoration: none; }
.pv2-dg-links a:hover { text-decoration: underline; }
.pv2-match-ok { color: var(--ctp-green); font-weight: 600; }
.pv2-match-off { color: var(--ctp-peach); font-weight: 600; }
/* Dragon masterlist: the LINK is the content, so it gets the full row width and
   wraps rather than being hidden behind the word "link". */
.pv2-dg-url { grid-column: 1 / -1; font-size: 10px; font-family: var(--font-data, ui-monospace, monospace);
  color: var(--ctp-mauve); text-decoration: none; word-break: break-all; line-height: 1.35; }
.pv2-dg-url:hover { text-decoration: underline; }
.pv2-dg-raw { color: var(--ctp-overlay1); }
.pv2-dg-raw:hover { color: var(--ctp-subtext0); }

/* Keyword target: the primary keyword leads, the rest support it. */
.pv2-kw-main { font-size: 13px; font-weight: 700; color: var(--ctp-text); line-height: 1.35;
  word-break: break-word; }
.pv2-kw-rest { display: flex; flex-wrap: wrap; gap: 3px 5px; margin-top: 5px; }
.pv2-kw-rest span { font-size: 10.5px; color: var(--ctp-subtext0); background: var(--ctp-surface0);
  border-radius: 4px; padding: 1px 6px; }
.pv2-kw-more { font-size: 10px; font-weight: 600; color: var(--ctp-overlay1); }
/* "Fields from this list" — the task's ClickUp properties, laid out like
   ClickUp's own field panel: label above a boxed value. Read-only inputs rather
   than plain text so a value can be selected and copied, and so an empty field
   still reads as a field (ClickUp shows "–" too). */
.pv2-cuf-toggle { display: flex; align-items: center; gap: 6px; width: 100%; background: none;
  border: none; padding: 6px 0 2px; cursor: pointer; color: var(--ctp-subtext0);
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.pv2-cuf-toggle:hover { color: var(--ctp-text); }
.pv2-cuf-group { margin-top: 8px; }
.pv2-cuf-group-h { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin: 8px 0 4px; }
.pv2-cuf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px 8px; }
.pv2-cuf-f { min-width: 0; }
.pv2-cuf-lab { font-size: 9.5px; color: var(--ctp-overlay1); margin-bottom: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pv2-cuf-in { width: 100%; height: 24px; padding: 2px 7px; font-size: 11px;
  background: var(--ctp-base); border: 1px solid var(--ctp-surface1); border-radius: 5px;
  color: var(--ctp-text); font-family: inherit; }
.pv2-cuf-in:focus { outline: none; border-color: var(--ctp-mauve); }
.pv2-cuf-in[data-empty="1"] { color: var(--ctp-overlay0); }
.pv2-cuf-wide { grid-column: 1 / -1; }
/* ClickUp status chip — painted with ClickUp's OWN colour for that status, so a
   status reads the same here as it does on the task. The hex arrives per task in
   status.color; the tint is applied inline because the palette is ClickUp's, not
   ours, and there are 26 of them on the work-zone ladder alone. */
/* ClickUp status pill.
   Rewritten 2026-09-04: it was a squarish box stretched to whatever width the
   footer had left, so the label floated at the left of an oversized container
   and no two pills were the same size. It now HUGS its text, centres it, and
   holds one fixed height so a column of statuses lines up. */
.pv2-st {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 1 auto;                 /* hug the text; shrink only when it must */
  height: 17px; padding: 0 8px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 9.5px; font-weight: 650; line-height: 1;
  letter-spacing: .01em;
  max-width: 100%; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  box-sizing: border-box;
}
/* Drawer version: same shape, one step up, so the two read as one component. */
.pv2-st-lg { height: 22px; padding: 0 11px; font-size: 11px; font-weight: 600; }
/* Status shown above the live URL, as its own labelled line. */
.pv2-liverow { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
/* Drawer header stays on ONE line: the ClickUp button and close never wrap, and
   a long status ("📃 | *live - seo review") ellipsizes instead of pushing them
   onto a second row. */
.scp-det-head .pv2-st { flex: 0 1 auto; min-width: 0; }
.pv2-head-btn { flex-shrink: 0; white-space: nowrap; display: inline-flex; align-items: center; gap: 5px; }
.pv2-head-btn img { width: 13px; height: 13px; flex-shrink: 0; }
/* Notes — autosaves, so it needs to say so without shouting. */
.pv2-notes-row { display: flex; align-items: center; gap: 8px; }
.pv2-save { font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ctp-overlay0); margin-left: auto; }
.pv2-save-on { color: var(--ctp-green); }
.pv2-save-busy { color: var(--ctp-peach); }
/* Scoped to the v2 drawer so the plan drawer's own header is untouched: one
   line, with the status chip as the only part allowed to shrink. */
.pv2-head { flex-wrap: nowrap !important; }
.pv2-head .scp-det-state { flex-shrink: 0; }
.pv2-head .pv2-st { overflow: hidden; text-overflow: ellipsis; }
.pv2-head .scp-det-btn { height: 24px; padding: 0 8px; font-size: 10.5px; }
.pv2-head .scp-det-x { flex-shrink: 0; }
/* Editable ClickUp fields. An edited box is ringed so the pending change is
   obvious before saving, and the save bar only exists while something is dirty. */
.pv2-cuf-in:not([readonly]) { cursor: text; }
.pv2-cuf-in:not([readonly]):hover { border-color: var(--ctp-surface2); }
.pv2-cuf-dirty { border-color: var(--ctp-peach) !important;
  box-shadow: 0 0 0 2px rgb(var(--ctp-peach-rgb) / .18); }
.pv2-cuf-ro { background: var(--ctp-mantle); color: var(--ctp-overlay1); }
.pv2-cuf-bar { position: sticky; bottom: 0; display: flex; align-items: center; gap: 8px;
  margin-top: 10px; padding: 8px 0 2px; background: var(--ctp-base);
  border-top: 1px solid var(--ctp-surface1); }
.pv2-cuf-bar-n { font-size: 10.5px; font-weight: 700; color: var(--ctp-peach); }
.pv2-cuf-save { cursor: pointer; border-radius: 6px; padding: 4px 12px; font-size: 11px;
  font-weight: 700; border: 1px solid var(--ctp-green); color: var(--ctp-base);
  background: var(--ctp-green); }
.pv2-cuf-save:disabled { opacity: .55; cursor: default; }
.pv2-cuf-discard { cursor: pointer; border-radius: 6px; padding: 4px 10px; font-size: 11px;
  font-weight: 600; border: 1px solid var(--ctp-surface1); background: transparent;
  color: var(--ctp-overlay1); }
.pv2-cuf-discard:hover { color: var(--ctp-text); }
.pv2-cuf-res { font-size: 10.5px; line-height: 1.5; margin-top: 6px; }
.pv2-cuf-res-ok { color: var(--ctp-green); }
.pv2-cuf-res-bad { color: var(--ctp-red); }
/* Idle save bar: present but visibly inert, so the control is discoverable
   before there is anything to save. */
.pv2-cuf-bar-idle { color: var(--ctp-overlay0); font-weight: 600; }
.pv2-cuf-save:disabled { background: var(--ctp-surface1); border-color: var(--ctp-surface1);
  color: var(--ctp-overlay0); }
.pv2-cuf-discard:disabled { opacity: .45; }
/* Status as a picker: keeps the chip look while being a real select. */
.pv2-stwrap { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.pv2-stsel { appearance: none; -webkit-appearance: none; border-width: 1px; border-style: solid;
  cursor: pointer; max-width: 260px; padding-right: 22px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 12px) 55%, calc(100% - 7px) 55%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
.pv2-stsel:disabled { cursor: default; opacity: .7; }
/* A typed option that is not on the board's list. */
.pv2-cuf-bad { border-color: var(--ctp-red) !important;
  box-shadow: 0 0 0 2px rgb(var(--ctp-red-rgb) / .18); }
/* Explicit "open" affordance, bottom-right of every card. The whole card has
   always been clickable, but nothing said so — a card that responds to a click
   without advertising it is a card people never click. Space is reserved at the
   card's foot so the button never sits on top of a warning box. */
.pv2-card { padding-bottom: 26px; }
.pv2-open { position: absolute; right: 7px; bottom: 6px; width: 22px; height: 22px;
  border-radius: 7px; border: 1px solid var(--ctp-surface1); background: var(--ctp-base);
  color: var(--ctp-overlay0); cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 2px rgb(0 0 0 / .04);
  transition: background .14s ease, color .14s ease, border-color .14s ease,
              box-shadow .14s ease, transform .14s ease; }
.pv2-open svg { width: 13px; height: 13px; display: block; }
/* Quiet until the card is hovered, then it invites the click. */
.pv2-card:hover .pv2-open { border-color: rgb(var(--ctp-mauve-rgb) / .55); color: var(--ctp-mauve);
  background: rgb(var(--ctp-mauve-rgb) / .07); }
.pv2-open:hover { background: var(--ctp-mauve); border-color: var(--ctp-mauve);
  color: var(--ctp-base); transform: translateX(1.5px);
  box-shadow: 0 3px 8px -2px rgb(var(--ctp-mauve-rgb) / .45); }
.pv2-open:active { transform: translateX(0) scale(.94); }
.pv2-open:focus-visible { outline: 2px solid var(--ctp-mauve); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .pv2-open { transition: none; }
  .pv2-open:hover, .pv2-open:active { transform: none; } }
/* Keep text clear of the button on the last row of a card. */

/* An informational note in the Issues list: readable, deliberately not alarming. */
.pv2-issue-info {
  border-left-color: rgba(var(--ctp-overlay0-rgb), 0.55);
  background: rgba(var(--ctp-surface0-rgb), 0.45);
}
.pv2-issue-info .pv2-issue-txt { color: var(--ctp-subtext0); }

/* Auctopus phase chip in the board bar — states a fact, so it stays quiet. */
.pv2-phase {
  display: inline-block;
  padding: 1px 6px;
  margin-right: 6px;
  border-radius: 999px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.5);
  background: rgba(var(--ctp-surface0-rgb), 0.6);
  color: var(--ctp-subtext1);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* Live-sync indicator in the board bar. Green pulse while the ClickUp watch is
   running; grey when it is off and refresh is the only way forward. */
.pv2-livedot {
  display: inline-block; vertical-align: middle; margin-right: 5px;
  width: 7px; height: 7px; border-radius: 50%;
}
.pv2-livedot-on { background: var(--ctp-green); animation: pv2-livepulse 2.4s ease-in-out infinite; }
.pv2-livedot-off { background: var(--ctp-overlay0); }
@keyframes pv2-livepulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgb(var(--ctp-green-rgb) / .45); }
  50%      { opacity: .65; box-shadow: 0 0 0 4px rgb(var(--ctp-green-rgb) / 0); }
}
@media (prefers-reduced-motion: reduce) { .pv2-livedot-on { animation: none; } }

/* A 200 that is deliberately out of the index. Amber, not green: the page is
   serving, but it is not working for us, and green would say otherwise. */
.pv2-h-noindex {
  background: rgb(var(--ctp-peach-rgb) / .16);
  color: var(--ctp-peach);
  border-color: rgb(var(--ctp-peach-rgb) / .38);
}

/* Niche filter in the board bar. Reads as a control, not a chip: these change
   what the whole board shows, so they should not look like the count pills. */
.pv2-nichesel {
  appearance: none;
  padding: 3px 22px 3px 8px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.45);
  border-radius: 6px;
  background: var(--ctp-mantle) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%236c7086' stroke-width='3' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>") no-repeat right 6px center;
  color: var(--ctp-text);
  font-size: 11px;
  font-weight: 500;
  max-width: 190px;
  cursor: pointer;
}
.pv2-nichesel:hover { border-color: rgba(var(--ctp-lavender-rgb), 0.7); }
.pv2-nichesel:focus-visible { outline: 2px solid var(--ctp-lavender); outline-offset: 1px; }
.pv2-sep {
  display: inline-block; width: 1px; height: 15px; vertical-align: middle;
  background: rgba(var(--ctp-overlay0-rgb), 0.35); margin: 0 2px;
}

/* ── V2 table: the board's cards as rows ──────────────────────────────────
   Dense and scannable — this view exists to compare many cards at once, so
   the row height is tight and numeric columns align on their digits. */
.pv2-tablewrap {
  border: 1px solid rgba(var(--ctp-surface0-rgb), 0.6);
  border-radius: 10px;
  background: rgba(var(--ctp-mantle-rgb), 0.35);
  overflow-x: auto;               /* wide table scrolls itself, never the page */
}
.pv2-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 11.5px; }
.pv2-table thead th {
  position: sticky; top: 0; z-index: 2;
  padding: 7px 9px;
  text-align: left;
  white-space: nowrap;
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ctp-subtext0);
  background: var(--ctp-mantle);
  border-bottom: 1px solid rgba(var(--ctp-surface0-rgb), 0.75);
  cursor: pointer; user-select: none;
}
.pv2-table thead th:hover { color: var(--ctp-text); }
.pv2-th-on { color: var(--ctp-mauve) !important; }
.pv2-table tbody td {
  padding: 6px 9px;
  border-bottom: 1px solid rgba(var(--ctp-surface0-rgb), 0.4);
  vertical-align: middle;
  white-space: nowrap;
}
.pv2-table tbody tr { cursor: pointer; }
.pv2-table tbody tr:hover { background: rgba(var(--ctp-surface0-rgb), 0.35); }
.pv2-tr-on { background: rgba(var(--ctp-mauve-rgb), 0.12) !important; }
/* The task name is the one column allowed to be long, so it takes the slack
   and truncates rather than pushing every other column off-screen. */
.pv2-td-name {
  white-space: normal !important;
  min-width: 240px; max-width: 420px;
  font-weight: 600; color: var(--ctp-text);
}
.pv2-num { text-align: right; font-variant-numeric: tabular-nums; }
.pv2-dim { color: var(--ctp-overlay0); }
.pv2-lanechip {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 700; white-space: nowrap;
}
.pv2-issuen {
  display: inline-block; min-width: 17px; padding: 0 5px; border-radius: 999px;
  background: rgba(var(--ctp-peach-rgb), 0.18); color: var(--ctp-peach);
  font-size: 10px; font-weight: 700; text-align: center;
}
.pv2-tablefoot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px;
  border-top: 1px solid rgba(var(--ctp-surface0-rgb), 0.6);
  font-size: 11px; color: var(--ctp-overlay1);
}

/* ── Topic picker in the drawer ────────────────────────────────────────────
   A decision surface: each row carries its own score and reason, so the
   choice is informed rather than a guess at a guess. */
.pv2-pick {
  margin-top: 6px; padding: 8px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.4);
  border-radius: 8px;
  background: rgba(var(--ctp-mantle-rgb), 0.5);
  display: flex; flex-direction: column; gap: 5px;
  max-height: 340px; overflow-y: auto;
}
.pv2-pick-q {
  width: 100%; padding: 5px 8px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.45);
  border-radius: 6px; background: var(--ctp-base);
  color: var(--ctp-text); font-size: 11.5px;
}
.pv2-pick-q:focus-visible { outline: 2px solid var(--ctp-lavender); outline-offset: 1px; }
.pv2-pick-row {
  display: flex; align-items: flex-start; gap: 8px; width: 100%;
  padding: 5px 7px; border-radius: 6px; text-align: left;
  border: 1px solid transparent; background: rgba(var(--ctp-surface0-rgb), 0.4);
  cursor: pointer;
}
.pv2-pick-row:hover { background: rgba(var(--ctp-lavender-rgb), 0.14); border-color: rgba(var(--ctp-lavender-rgb), 0.5); }
.pv2-pick-row:disabled { opacity: 0.5; cursor: default; }
.pv2-pick-other { background: rgba(var(--ctp-surface0-rgb), 0.22); }
.pv2-pick-score {
  flex: 0 0 34px; text-align: center;
  padding: 1px 0; border-radius: 4px;
  font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: rgba(var(--ctp-overlay0-rgb), 0.25); color: var(--ctp-subtext0);
}
.pv2-pick-hi  { background: rgba(var(--ctp-green-rgb), 0.22);  color: var(--ctp-green); }
.pv2-pick-mid { background: rgba(var(--ctp-yellow-rgb), 0.22); color: var(--ctp-yellow); }
.pv2-pick-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pv2-pick-label { font-size: 11.5px; font-weight: 600; color: var(--ctp-text); }
.pv2-pick-why { font-size: 10px; color: var(--ctp-overlay1); }
.pv2-pick-sep {
  margin-top: 4px; padding-top: 5px;
  border-top: 1px solid rgba(var(--ctp-overlay0-rgb), 0.3);
  font-size: 10px; font-weight: 600; color: var(--ctp-overlay1);
}

/* ── Card, restructured into four bands (2026-09-04) ──────────────────────
   Scan order is: identity strip → title → what it targets → verified state.
   Only two things stay coloured — the health verdict and ClickUp's own status
   — because when the topic chip, the volume, the priority and both clocks all
   carried colour, nothing stood out and the card read as a wall. */
.pv2-strip { display: flex; align-items: center; gap: 5px; min-width: 0;
  height: 16px; flex: 0 0 16px; }
.pv2-strip-gap { flex: 1 1 auto; min-width: 0; }
.pv2-site-alt { display: inline-flex; align-items: center; gap: 3px;
  font-size: 9.5px; color: var(--ctp-overlay1); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv2-site-alt img { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 10px; }
/* Target line: the topic leads, the keyword only appears when it differs. */
.pv2-target { display: flex; align-items: baseline; gap: 6px; min-width: 0;
  height: 15px; flex: 0 0 15px; font-size: 10.5px; overflow: hidden; }
.pv2-target > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.pv2-target-topic { color: var(--ctp-subtext0); font-weight: 600; }
.pv2-target-kw { color: var(--ctp-overlay0); font-size: 9.5px; flex-shrink: 2; }
.pv2-target-none { color: var(--ctp-overlay0); }
.pv2-target-ask { color: var(--ctp-yellow); font-weight: 600; }
.pv2-target-vol { color: var(--ctp-overlay1); font-variant-numeric: tabular-nums;
  flex: 0 0 auto; font-size: 9.5px; }
/* Footer sits on a hairline and keeps clear of the open button. */
.pv2-foot { border-top: 1px solid rgb(var(--ctp-surface0-rgb) / .8);
  padding-top: 5px; padding-right: 24px; margin-top: auto; }
.pv2-ages { color: var(--ctp-overlay0); font-variant-numeric: tabular-nums;
  font-size: 9.5px; flex: 0 0 auto; }
/* Fewer bands means less height, and the space that is left is breathing room
   rather than another row of text. */
.pv2-card { height: 132px; padding: 9px 10px 8px; gap: 6px; }
.pv2-title { font-size: 13px; font-weight: 680; line-height: 1.32; height: 2.64em; }

/* ── Sizing corrected against the real content (2026-09-04) ───────────────
   Measured across 1,230 cards: titles are median 37 chars but p75 82, p90 112.
   Two clamped lines hold about 74, so more than a quarter of titles were being
   cut — that was the "…" everywhere. Three lines covers p90. Topics max out at
   40 chars and fit a full line comfortably, so they get one to themselves;
   the volume moved to the identity strip, where it costs four characters. */
.pv2-title { -webkit-line-clamp: 3; height: 3.96em; }
.pv2-strip-vol { font-size: 9.5px; color: var(--ctp-overlay1);
  font-variant-numeric: tabular-nums; flex: 0 0 auto; }
/* The topic keeps its width; only the (optional, secondary) keyword gives way. */
.pv2-target-topic { flex: 0 1 auto; }
.pv2-target-kw { flex: 0 1 auto; min-width: 0; }
/* Health + status share a line (12 + 24 chars fits); the clocks get their own,
   rather than three cells fighting over 232px. */
.pv2-foot { padding-right: 0; }
.pv2-ages { display: block; padding-right: 24px; line-height: 1.3; }
.pv2-card { height: 158px; }

/* The topic gets the whole line when the keyword adds nothing, and keeps
   priority over it when both are shown — measured: 60 of 134 topics were being
   clipped purely because a near-duplicate keyword sat beside them. */
.pv2-target-topic { flex: 1 1 auto; }
.pv2-target-kw { flex: 0 1 auto; }
.pv2-target-none, .pv2-target-ask { flex: 0 0 auto; }
/* Health is just the code now, so the status has room — but it must hug its
   own text rather than expand into the gap. */
.pv2-foot > .pv2-st { flex: 0 1 auto; min-width: 0; }
.pv2-h-ok, .pv2-h-gone, .pv2-h-redir, .pv2-h-none, .pv2-h-noindex { flex: 0 0 auto; }

/* ── Target block: a line each, because they are different facts ──────────
   Measured on 134 cards: after de-duplicating reorderings, 35 still clipped
   because the remaining pairs genuinely differ — topic "best online casino"
   with keyword "best online casinos ireland" adds a market. Two facts do not
   fit on one 232px line, so they get a line each and the space is reserved so
   every card stays the same height. */
.pv2-target-2 { flex-direction: column; align-items: stretch; gap: 1px;
  height: 30px; flex: 0 0 30px; }
.pv2-target-2 > span { display: block; width: 100%; line-height: 1.35; }
.pv2-target-2 .pv2-target-kw { color: var(--ctp-overlay0); }
.pv2-card { height: 172px; }

/* ── Topic picker: the topic itself is the control ────────────────────────
   It was a static chip plus a separate "change topic" pill sitting among
   other pills, so the way to change the topic was neither obvious nor next
   to the thing it changed. Now the topic reads as a select. */
.pv2-topicbtn {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 4px 8px 4px 10px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.45);
  border-radius: 7px;
  background: var(--ctp-base);
  color: var(--ctp-text);
  font-size: 12px; font-weight: 600;
  cursor: pointer; text-align: left;
  transition: border-color .12s, background .12s;
}
.pv2-topicbtn:hover { border-color: var(--ctp-mauve); background: rgba(var(--ctp-mauve-rgb), 0.06); }
.pv2-topicbtn:focus-visible { outline: 2px solid var(--ctp-mauve); outline-offset: 1px; }
.pv2-topicbtn-empty { border-style: dashed; border-color: var(--ctp-yellow); color: var(--ctp-yellow); }
.pv2-topicbtn-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv2-topicbtn-chev { width: 14px; height: 14px; flex: 0 0 14px;
  color: var(--ctp-overlay1); transition: transform .14s; }
.pv2-topicbtn-chev-open { transform: rotate(180deg); }
/* Search sits on its own row with the escape hatch beside it. */
.pv2-pick-head { display: flex; align-items: center; gap: 6px; }
.pv2-pick-q { flex: 1 1 auto; }
.pv2-pick-reset {
  flex: 0 0 auto; padding: 4px 8px; border-radius: 6px;
  border: 1px solid rgba(var(--ctp-overlay0-rgb), 0.4);
  background: var(--ctp-base); color: var(--ctp-subtext0);
  font-size: 10px; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.pv2-pick-reset:hover { border-color: var(--ctp-mauve); color: var(--ctp-mauve); }
/* Rows read as a list: the label leads, the meta is one muted line, and the
   scoring reason moved to the tooltip so the row is scannable. */
.pv2-pick-label { display: flex; align-items: center; gap: 6px; }
.pv2-pick-cur {
  padding: 0 5px; border-radius: 999px; font-size: 8.5px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  background: rgba(var(--ctp-green-rgb), 0.18); color: var(--ctp-green);
}
/* Other markets fold away — the wrong market by definition. */
.pv2-pick-sep {
  display: block; width: 100%; text-align: left; cursor: pointer;
  margin-top: 4px; padding: 4px 2px 0;
  border: 0; border-top: 1px solid rgba(var(--ctp-overlay0-rgb), 0.3);
  background: none; font-size: 10px; font-weight: 600; color: var(--ctp-overlay1);
}
.pv2-pick-sep:hover { color: var(--ctp-mauve); }

/* ── Details grid: a chip and its sub-line share one text edge ────────────
   A chip's text sits inside a 1px border plus 9px of padding, while the hint
   under it started at the raw column edge — a 10px jog, visible on Topic and
   Niche where the value carries an "id … · matched by …" line beneath it.
   Indenting the stacked hint lines by the same amount lines the column up. */
.scp-det-v-col > .scp-det-hint,
.scp-det-v-col > span > .scp-det-hint { padding-left: 10px; }
/* The topic control hugs its label like every other value in the grid; it was
   stretching to the full 317px column, which read as a different kind of row. */
.pv2-topicbtn { align-self: flex-start; }

/* ── Card spacing: content-sized, not worst-case-sized ───────────────────
   The gaps came from reserving space for the tallest possible card on every
   card: a 3-line title slot, a 2-line target slot, and a footer pinned to the
   bottom of a fixed 172px. A one-line title therefore left a hole beneath it,
   and a card with no keyword line left another above the footer. Each band now
   takes the height it needs and the card follows, so a short card is short.
   Cards no longer share one height — that is the trade for having no holes. */
.pv2-card { height: auto; min-height: 104px; gap: 5px; padding: 9px 10px 8px; }
.pv2-title { height: auto; max-height: 3.96em; }        /* still clamped at 3 lines */
.pv2-target-2 { height: auto; flex: 0 0 auto; gap: 2px; }
.pv2-foot { margin-top: 3px; }                          /* sits under the content, not pushed down */
.pv2-ages { margin-top: 1px; }
/* "Keyword:" prefix — quiet, so the keyword itself still reads first. */
.pv2-kwlab { color: var(--ctp-overlay0); font-weight: 600; }

/* Now the card is content-sized, the keyword line can simply take a second
   line instead of ellipsising — three fixture keywords ("Keyword: Argentina vs
   Switzerland predictions & odds") needed 243px of 212px, and there is no
   longer a fixed slot to protect. Capped at two lines so one long keyword
   cannot stretch a card indefinitely. */
.pv2-target-2 .pv2-target-kw {
  white-space: normal;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Card target line is single again — the ClickUp keyword moved out, so the
   topic has the full width and nothing to compete with. */
.pv2-target { height: auto; flex: 0 0 auto; }
