/* =========================================================================
   Nikon E800 Filter Cube Database
   Aesthetic: laboratory instrument panel — deep neutrals, spectrum accents,
   monospaced numerics. Distinctive, not AI-generic.
   ========================================================================= */

:root {
  /* Type scale */
  --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
  --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --text-lg: clamp(1.125rem, 1rem + 0.75vw, 1.5rem);
  --text-xl: clamp(1.5rem, 1.2rem + 1.25vw, 2.25rem);
  --text-2xl: clamp(2rem, 1.2rem + 2.5vw, 3.5rem);

  /* Spacing */
  --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem;
  --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem;
  --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem;
  --space-16: 4rem; --space-20: 5rem; --space-24: 6rem;

  /* Radii */
  --radius-sm: 0.375rem; --radius-md: 0.5rem;
  --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-full: 9999px;
  --transition-interactive: 180ms cubic-bezier(0.16, 1, 0.3, 1);

  /* Layout */
  --content-wide: 1280px;

  /* Fonts */
  --font-body: 'Satoshi', 'Helvetica Neue', sans-serif;
  --font-mono: 'JetBrains Mono', 'Menlo', monospace;

  /* Spectrum palette — mapped to excitation wavelength families */
  --spec-uv:  #8b5cf6;   /* ~360 nm — violet   */
  --spec-v:   #6366f1;   /* ~400 nm — indigo   */
  --spec-bv:  #3b82f6;   /* ~430 nm — blue     */
  --spec-b:   #22d3ee;   /* ~480 nm — cyan     */
  --spec-g:   #84cc16;   /* ~540 nm — lime     */
  --spec-y:   #f59e0b;   /* ~570 nm — amber    */
  --spec-r:   #ef4444;   /* ~620 nm — red      */
  --spec-ir:  #be185d;   /* ~700 nm — crimson  */
  --spec-multi: linear-gradient(90deg, #8b5cf6, #22d3ee, #84cc16, #f59e0b, #ef4444);
}

/* LIGHT MODE */
:root, [data-theme='light'] {
  --color-bg: #f4f5f3;
  --color-surface: #ffffff;
  --color-surface-2: #fafbf8;
  --color-surface-offset: #eceee8;
  --color-surface-dynamic: #e3e6df;
  --color-divider: #dfe2db;
  --color-border: #c9cdc3;

  --color-text: #15181a;
  --color-text-muted: #5c6267;
  --color-text-faint: #9ca2a3;
  --color-text-inverse: #fafbf8;

  --color-primary: #0d7a73;         /* spectral teal */
  --color-primary-hover: #085e59;
  --color-primary-active: #054743;
  --color-primary-highlight: #cfe2df;

  --color-warning: #b8611d;
  --color-error: #b03252;
  --color-success: #2f7a3a;

  --shadow-sm: 0 1px 2px oklch(0.25 0.02 180 / 0.08);
  --shadow-md: 0 4px 14px oklch(0.25 0.02 180 / 0.1);
  --shadow-lg: 0 16px 40px oklch(0.2 0.02 180 / 0.18);
}

/* DARK MODE — the primary/default aesthetic for this tool */
[data-theme='dark'] {
  --color-bg: #0b0d0e;
  --color-surface: #13171a;
  --color-surface-2: #181d20;
  --color-surface-offset: #1c2226;
  --color-surface-dynamic: #252b30;
  --color-divider: #222a2e;
  --color-border: #2e363b;

  --color-text: #e4e6e3;
  --color-text-muted: #8f9590;
  --color-text-faint: #5a6066;
  --color-text-inverse: #0b0d0e;

  --color-primary: #4fd1c5;
  --color-primary-hover: #6ee0d4;
  --color-primary-active: #81e9de;
  --color-primary-highlight: #1f3936;

  --color-warning: #f2a45b;
  --color-error: #e97092;
  --color-success: #7cc78a;

  --shadow-sm: 0 1px 2px oklch(0 0 0 / 0.25);
  --shadow-md: 0 4px 14px oklch(0 0 0 / 0.35);
  --shadow-lg: 0 16px 40px oklch(0 0 0 / 0.5);
}

/* ---- Base reset ---- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;
}
body {
  min-height: 100dvh;
  line-height: 1.55;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-bg);
  overflow-x: hidden;
}
img, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }
h1, h2, h3 { text-wrap: balance; line-height: 1.1; font-weight: 700; letter-spacing: -0.015em; }
p { text-wrap: pretty; }
a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-hover); }
code { font-family: var(--font-mono); font-size: 0.92em; background: var(--color-surface-offset); padding: 0.1em 0.35em; border-radius: var(--radius-sm); }
::selection { background: color-mix(in oklab, var(--color-primary) 28%, transparent); color: var(--color-text); }
:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

a, button, [role='button'], input, textarea, select {
  transition:
    color var(--transition-interactive),
    background var(--transition-interactive),
    border-color var(--transition-interactive),
    box-shadow var(--transition-interactive),
    transform var(--transition-interactive);
}

/* ---- Container ---- */
.container { width: 100%; max-width: var(--content-wide); margin: 0 auto; padding: 0 var(--space-6); }
@media (min-width: 768px) { .container { padding: 0 var(--space-8); } }

/* ---- Spectrum background strip ---- */
.spectrum-bg {
  position: fixed; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg,
    #2e003e 0%, #4c1d95 8%, #4f46e5 18%, #3b82f6 28%, #22d3ee 38%,
    #34d399 48%, #84cc16 58%, #eab308 68%, #f59e0b 76%, #ef4444 86%,
    #be185d 94%, #2e003e 100%);
  z-index: 100; opacity: 0.85;
}

/* ---- Header ---- */
.site-header {
  position: sticky; top: 3px; z-index: 40;
  background: color-mix(in oklab, var(--color-bg) 88%, transparent);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--color-divider);
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 68px; gap: var(--space-4);
}
.logo {
  display: inline-flex; align-items: center; gap: var(--space-3);
  color: var(--color-text); font-weight: 700;
}
.logo svg { color: var(--color-primary); }
.logo-text { display: flex; flex-direction: column; line-height: 1.1; }
.logo-title { font-size: var(--text-sm); letter-spacing: 0.02em; }
.logo-sub { font-size: var(--text-xs); color: var(--color-text-muted); font-weight: 500; }
.site-nav { display: flex; align-items: center; gap: var(--space-5); }
.site-nav a {
  color: var(--color-text-muted); font-size: var(--text-sm); font-weight: 500;
}
.site-nav a:hover { color: var(--color-text); }
@media (max-width: 720px) {
  .site-nav a { display: none; }
}

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-full);
  border: 1px solid var(--color-border); color: var(--color-text-muted);
}
.theme-toggle:hover { color: var(--color-text); border-color: var(--color-primary); }

/* ---- Hero ---- */
.hero {
  padding: clamp(var(--space-16), 9vw, var(--space-24)) 0 var(--space-12);
  position: relative;
}
.hero::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(ellipse 80% 60% at 10% 0%, color-mix(in oklab, var(--spec-uv) 22%, transparent) 0%, transparent 60%),
    radial-gradient(ellipse 80% 60% at 90% 10%, color-mix(in oklab, var(--spec-b) 18%, transparent) 0%, transparent 55%),
    radial-gradient(ellipse 60% 40% at 50% 100%, color-mix(in oklab, var(--spec-g) 14%, transparent) 0%, transparent 60%);
  opacity: 0.55;
}
.eyebrow {
  text-transform: uppercase; letter-spacing: 0.18em;
  font-size: var(--text-xs); font-weight: 500;
  color: var(--color-primary); margin-bottom: var(--space-4);
  font-family: var(--font-mono);
}
.hero h1 {
  font-size: var(--text-2xl); max-width: 22ch;
  margin-bottom: var(--space-5);
}
.hero-lede {
  font-size: var(--text-lg); color: var(--color-text-muted);
  max-width: 58ch; margin-bottom: var(--space-10);
}
.hero-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-5);
  padding: var(--space-5) 0;
  border-top: 1px solid var(--color-divider);
  border-bottom: 1px solid var(--color-divider);
}
.hero-stats > div { display: flex; flex-direction: column; gap: var(--space-1); }
.hero-stats strong {
  font-family: var(--font-mono); font-size: var(--text-xl);
  font-weight: 500; color: var(--color-text); letter-spacing: -0.02em;
}
.hero-stats span {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--color-text-muted);
}

/* ---- Sections ---- */
.section {
  padding: clamp(var(--space-12), 7vw, var(--space-20)) 0;
}
.section-alt { background: var(--color-surface-2); border-top: 1px solid var(--color-divider); border-bottom: 1px solid var(--color-divider); }
.section-head { margin-bottom: var(--space-8); max-width: 60ch; }
.section-head h2 {
  font-size: var(--text-xl); margin-bottom: var(--space-2);
  display: inline-flex; align-items: baseline; gap: var(--space-3);
}
.section-head h2::before {
  content: ''; display: inline-block;
  width: 18px; height: 2px; background: var(--color-primary);
  transform: translateY(-4px);
}
.section-head p { color: var(--color-text-muted); font-size: var(--text-base); }

/* ---- Toolbar / chips ---- */
.toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
  margin-bottom: var(--space-5);
}
.chip-group { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-full);
  font-size: var(--text-xs); font-weight: 500; letter-spacing: 0.03em;
  color: var(--color-text-muted); background: transparent;
  text-transform: uppercase;
}
.chip:hover { border-color: var(--color-primary); color: var(--color-text); }
.chip-active {
  background: var(--color-primary); border-color: var(--color-primary);
  color: var(--color-text-inverse);
}

.inline-search {
  flex: 0 0 260px;
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm); color: var(--color-text);
}
.inline-search:focus-visible { border-color: var(--color-primary); outline: none; box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 25%, transparent); }

/* ---- Search bar (main) ---- */
.search-bar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: border-color 180ms, box-shadow 180ms;
}
.search-bar:focus-within {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--color-primary) 18%, transparent);
}
.search-bar svg { color: var(--color-text-muted); flex-shrink: 0; }
.search-bar input {
  flex: 1; border: 0; outline: 0; background: transparent;
  font-size: var(--text-base); padding: var(--space-2) 0;
}
#clear-search {
  width: 28px; height: 28px; border-radius: var(--radius-full);
  color: var(--color-text-muted); font-size: 1.2rem; line-height: 1;
}
#clear-search:hover { background: var(--color-surface-offset); color: var(--color-text); }

.search-results {
  margin-top: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}
.search-result {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-divider);
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-4);
  align-items: center; cursor: pointer;
}
.search-result:last-child { border-bottom: 0; }
.search-result:hover { background: var(--color-surface-offset); }
.search-result .sr-id { font-family: var(--font-mono); font-weight: 500; font-size: var(--text-sm); }
.search-result .sr-type {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--color-text-muted);
}
.search-result .sr-spec { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text-muted); }

/* ---- Checker ---- */
.checker-grid {
  display: grid; grid-template-columns: minmax(280px, 1fr) 2fr;
  gap: var(--space-6);
}
@media (max-width: 860px) { .checker-grid { grid-template-columns: 1fr; } }

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label {
  font-size: var(--text-xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--color-text-muted);
}
.field input, .field select {
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  color: var(--color-text);
}
.field input:focus-visible, .field select:focus-visible {
  border-color: var(--color-primary); outline: none;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 25%, transparent);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.divider-or {
  display: flex; align-items: center; gap: var(--space-3);
  margin: var(--space-5) 0;
  color: var(--color-text-faint); font-size: var(--text-xs);
  text-transform: uppercase; letter-spacing: 0.16em;
}
.divider-or::before, .divider-or::after {
  content: ''; flex: 1; height: 1px; background: var(--color-divider);
}
.btn-primary {
  margin-top: var(--space-5);
  padding: var(--space-3) var(--space-5);
  background: var(--color-primary);
  color: var(--color-text-inverse);
  font-weight: 600;
  border-radius: var(--radius-md);
  font-size: var(--text-sm); letter-spacing: 0.02em;
}
.btn-primary:hover { background: var(--color-primary-hover); transform: translateY(-1px); }
.btn-primary:active { transform: translateY(0); }

.checker-results .muted { color: var(--color-text-muted); }
.match-row {
  display: grid; grid-template-columns: 44px 1fr auto;
  gap: var(--space-4); align-items: center;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-divider);
}
.match-row:last-child { border-bottom: 0; }
.match-score {
  font-family: var(--font-mono); font-size: var(--text-sm); font-weight: 500;
  text-align: center;
  padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
}
.match-good  { background: color-mix(in oklab, var(--color-success) 18%, transparent); color: var(--color-success); }
.match-ok    { background: color-mix(in oklab, var(--color-warning) 18%, transparent); color: var(--color-warning); }
.match-poor  { background: color-mix(in oklab, var(--color-error) 18%, transparent); color: var(--color-error); }

.match-id { font-family: var(--font-mono); font-size: var(--text-base); font-weight: 500; }
.match-spec { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text-muted); margin-top: var(--space-1); }
.match-verdict { font-size: var(--text-xs); color: var(--color-text-muted); text-align: right; max-width: 22ch; }

/* ---- Data tables ---- */
.table-wrap {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: auto;
  box-shadow: var(--shadow-sm);
}
.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: 2;
  background: var(--color-surface-offset);
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-size: var(--text-xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}
.data-table thead th[data-sort] { cursor: pointer; user-select: none; }
.data-table thead th[data-sort]:hover { color: var(--color-text); }
.data-table thead th[data-sort]::after {
  content: '↕'; margin-left: var(--space-2); opacity: 0.4; font-size: 0.9em;
}
.data-table thead th.sort-asc::after { content: '↑'; opacity: 1; color: var(--color-primary); }
.data-table thead th.sort-desc::after { content: '↓'; opacity: 1; color: var(--color-primary); }

.data-table tbody td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
  vertical-align: top;
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr { transition: background 140ms; }
.data-table tbody tr:hover { background: var(--color-surface-offset); }

.data-table tbody tr.highlight {
  background: color-mix(in oklab, var(--color-primary) 12%, transparent);
  animation: flash 1.5s ease-out;
}
@keyframes flash {
  0%, 30% { background: color-mix(in oklab, var(--color-primary) 25%, transparent); }
  100% { background: color-mix(in oklab, var(--color-primary) 8%, transparent); }
}

.mono { font-family: var(--font-mono); font-size: 0.94em; }
.cube-id { font-weight: 600; display: inline-flex; align-items: center; gap: var(--space-2); }
.band-dot {
  display: inline-block; width: 10px; height: 10px; border-radius: var(--radius-full);
  flex-shrink: 0;
}
.band-UV  { background: var(--spec-uv); }
.band-V   { background: var(--spec-v); }
.band-BV  { background: var(--spec-bv); }
.band-B   { background: var(--spec-b); }
.band-G   { background: var(--spec-g); }
.band-Y   { background: var(--spec-y); }
.band-R   { background: var(--spec-r); }
.band-Multi { background: var(--spec-multi); }

.band-label {
  display: inline-block; padding: 2px 8px;
  font-family: var(--font-mono); font-size: var(--text-xs);
  border-radius: var(--radius-full);
  background: var(--color-surface-offset);
  color: var(--color-text);
  border: 1px solid var(--color-border);
}

.fluor-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.tag {
  font-size: var(--text-xs); padding: 2px 8px;
  background: var(--color-surface-offset);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.tag.clickable { cursor: pointer; }
.tag.clickable:hover { border-color: var(--color-primary); color: var(--color-text); }

/* Numeric columns right-aligned for readability */
.data-table td.num, .data-table th.num { text-align: right; font-family: var(--font-mono); }

/* ---- Notes grid ---- */
.notes-grid {
  display: grid; grid-template-columns: 2fr 1fr;
  gap: var(--space-10);
}
@media (max-width: 860px) { .notes-grid { grid-template-columns: 1fr; } }
.notes-grid h2 { font-size: var(--text-lg); margin-bottom: var(--space-4); }
.plain-list { list-style: none; display: flex; flex-direction: column; gap: var(--space-3); }
.plain-list li { color: var(--color-text-muted); font-size: var(--text-sm); padding-left: var(--space-4); position: relative; }
.plain-list li::before {
  content: ''; position: absolute; left: 0; top: 0.6em;
  width: 8px; height: 1px; background: var(--color-primary);
}
.plain-list.small li { font-size: var(--text-xs); }

/* ---- Footer ---- */
.site-footer {
  padding: var(--space-10) 0;
  border-top: 1px solid var(--color-divider);
  background: var(--color-surface);
}
.footer-inner {
  display: flex; flex-direction: column; gap: var(--space-2);
  font-size: var(--text-xs); color: var(--color-text-muted);
}
.muted { color: var(--color-text-muted); }

/* Fade-in on table load */
.data-table tbody tr { animation: fadein 400ms ease-out both; }
@keyframes fadein {
  from { opacity: 0; transform: translateY(2px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ============================================================================
   Cube & Imaging Planner — additions on top of the Nexus base
   ============================================================================ */

/* Shell layout --------------------------------------------------------- */
/* .spectrum-bar removed in v=47b — the 4 px rainbow strip sat in normal
   flow at the top of the page, so it scrolled out of view as soon as the
   user scrolled down, producing a small but perceptible "bounce" each
   time. The accent did not justify the layout shift. */

.wrap {
  width: 100%; max-width: var(--content-wide);
  margin: 0 auto; padding: 0 var(--space-6);
}
@media (min-width: 768px) { .wrap { padding: 0 var(--space-8); } }

.app-header {
  border-bottom: 1px solid var(--color-divider);
  background: var(--color-surface);
  position: sticky; top: 0; z-index: 50;
  backdrop-filter: saturate(140%) blur(8px);
}
.app-header .wrap.header-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  /* Compact header — the application is for laptop bench use. Brand on the
     left, a small grid of toggles and tab-specific actions on the right.
     One row only. */
  padding-top: var(--space-2); padding-bottom: var(--space-2);
}

/* Byline under the brand tagline */
.brand-byline {
  margin-top: 1px;
  font-size: 0.72rem;
  color: var(--color-text-muted);
  font-weight: 500;
  letter-spacing: 0.01em;
}

.brand { display: flex; align-items: center; gap: var(--space-3); color: var(--color-primary); flex-shrink: 0; }
.brand-mark { flex-shrink: 0; width: 36px; height: 36px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; gap: 2px; }
.brand-name {
  font-weight: 700;
  font-size: 1.2rem; line-height: 1.15;
  color: var(--color-text); letter-spacing: -0.01em;
}
.brand-tag {
  font-size: 0.74rem; color: var(--color-text-muted);
  font-weight: 500; letter-spacing: 0.02em;
}

/* ============================================================
   Header grid — compact rectangles to the right of the brand,
   organised as a strict 2-row layout. Row 1 holds the cross-tab
   toggles (Camera + the two checkboxes). Row 2 holds the
   tab-specific actions (Save / Load / Delete scene) plus the
   theme toggle. The two rows are visually centered as a block to
   the right of the brand so the chrome reads balanced rather
   than ragged.
   ============================================================ */
.header-grid {
  display: grid;
  grid-template-rows: auto auto;
  row-gap: 6px;
  align-content: center;
  justify-items: center;
  justify-content: end;
  flex: 0 1 auto;
  min-width: 0;
}
.hg-row {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: stretch;
  justify-content: center;
  max-width: 100%;
}
.hg-row-1 { gap: 10px; }
.hg-cell {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  line-height: 1.2;
  white-space: nowrap;
  min-height: 28px;
  box-sizing: border-box;
  font-family: inherit;
  transition: border-color var(--transition-interactive), color var(--transition-interactive);
}
.hg-cell:hover { border-color: var(--color-primary); }
.hg-cell .hg-label { font-weight: 600; color: var(--color-text-muted); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; }
.hg-cell select,
.hg-cell .hg-select {
  font-size: 0.75rem;
  padding: 1px 4px;
  border: none;
  background: transparent;
  color: var(--color-text);
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  max-width: 160px;
}
.hg-cell select:focus,
.hg-cell .hg-select:focus { outline: 1px solid var(--color-primary); outline-offset: 1px; }
.hg-cell input[type="checkbox"] { width: 12px; height: 12px; accent-color: var(--color-primary); margin: 0; }

/* Camera cell — small but the dropdown stays wide enough to read the
   currently-selected camera name. Less dominant than before. */
.hg-camera select { min-width: 130px; max-width: 200px; }
.hg-camera { background: var(--color-surface); }

/* Colored dot beside the header camera dropdown. Color matches the camera
   curve on the spectrum graph so the user can confirm at a glance which
   body is active. Updated by refreshCameraColorDot() in app.js. */
.camera-select-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.camera-color-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #a78bfa;
  border: 1px solid rgba(0, 0, 0, 0.25);
  flex: 0 0 auto;
}

.hg-btn {
  background: transparent; border: none; padding: 0;
  font: inherit; color: inherit; cursor: pointer;
  font-weight: 500;
}

.hg-scene { background: var(--color-surface); }

/* Scene group keeps Save / Load / Delete on one line as a unit. */
.hg-scene-group {
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 6px;
  align-items: stretch;
}

.hg-theme {
  padding: 3px 7px;
  color: var(--color-text-muted);
  background: var(--color-surface);
}
.hg-theme:hover { color: var(--color-text); }

/* ============================================================
   Header Contact button — outlined teal, sits to the right of
   the camera/scene controls cluster. Spans the full height of
   both rows for visual balance.
   ============================================================ */
.header-contact-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: 10px 18px;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  align-self: stretch;
  white-space: nowrap;
  transition: background-color var(--transition-interactive), color var(--transition-interactive);
}
.header-contact-btn:hover {
  background: var(--color-primary);
  color: var(--color-bg);
}
.header-contact-btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.header-contact-btn svg { flex: 0 0 auto; }

/* ============================================================
   Contact modal — suggestion / feedback form
   ============================================================ */
.contact-modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-4);
}
.contact-modal-overlay[hidden] { display: none; }
.contact-modal {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
  position: relative;
}
.contact-modal h2 {
  margin: 0 0 4px;
  font-size: 1.25rem;
  color: var(--color-text);
}
.contact-modal .contact-intro {
  margin: 0 0 var(--space-4);
  color: var(--color-text-muted);
  font-size: 0.85rem;
  line-height: 1.4;
}
.contact-modal-close {
  position: absolute; top: 12px; right: 12px;
  background: transparent; border: none;
  color: var(--color-text-muted);
  cursor: pointer; padding: 6px;
  border-radius: var(--radius-sm);
  line-height: 0;
}
.contact-modal-close:hover { color: var(--color-text); background: var(--color-surface-2); }
.contact-modal-close svg { width: 18px; height: 18px; }

.contact-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: var(--space-3); }
.contact-field label {
  font-size: 0.78rem; font-weight: 600;
  color: var(--color-text);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.contact-field label .req { color: var(--color-primary); margin-left: 2px; }
.contact-field label .opt {
  font-weight: 400; color: var(--color-text-muted);
  text-transform: none; letter-spacing: 0; margin-left: 6px; font-size: 0.72rem;
}
.contact-field input,
.contact-field select,
.contact-field textarea {
  font: inherit;
  font-size: 0.9rem;
  padding: 8px 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  color: var(--color-text);
  width: 100%;
  box-sizing: border-box;
  font-family: inherit;
}
.contact-field textarea { resize: vertical; min-height: 120px; line-height: 1.4; }
.contact-field input:focus,
.contact-field select:focus,
.contact-field textarea:focus {
  outline: 2px solid var(--color-primary);
  outline-offset: -1px;
  border-color: var(--color-primary);
}
.contact-field input:invalid:not(:placeholder-shown),
.contact-field textarea:invalid:not(:placeholder-shown) {
  border-color: var(--color-danger, #e57373);
}

/* Honeypot — visually hidden, but not display:none so bots will still fill it */
.contact-honeypot {
  position: absolute;
  left: -9999px;
  top: -9999px;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}

.contact-actions {
  display: flex; gap: var(--space-3); justify-content: flex-end;
  margin-top: var(--space-4);
}
.contact-btn-primary {
  background: var(--color-primary);
  color: var(--color-bg);
  border: none;
  border-radius: var(--radius-md);
  padding: 10px 20px;
  font-size: 0.9rem; font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}
.contact-btn-primary:hover { filter: brightness(1.08); }
.contact-btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }
.contact-btn-secondary {
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 10px 18px;
  font-size: 0.9rem; font-weight: 500;
  font-family: inherit;
  cursor: pointer;
}
.contact-btn-secondary:hover { border-color: var(--color-primary); color: var(--color-primary); }

.contact-status {
  margin-top: var(--space-3);
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  line-height: 1.4;
}
.contact-status.success {
  background: rgba(79, 209, 197, 0.12);
  border: 1px solid var(--color-primary);
  color: var(--color-text);
}
.contact-status.error {
  background: rgba(229, 115, 115, 0.12);
  border: 1px solid #e57373;
  color: var(--color-text);
}

/* ============================================================
   Migration notice modal — ONLY appears on the OLD site to
   inform visitors the app has moved. Not used on the new site.
   ============================================================ */
.migration-overlay {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-4);
}
.migration-overlay[hidden] { display: none; }
.migration-modal {
  background: var(--color-surface);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-6) var(--space-5);
  width: 100%;
  max-width: 600px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
  position: relative;
}
.migration-modal h2 {
  margin: 0 0 var(--space-2);
  font-size: 1.4rem;
  color: var(--color-text);
}
.migration-modal .mig-lede {
  margin: 0 0 var(--space-4);
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.45;
}
.migration-modal .mig-notice-heading {
  display: inline-block;
  background: var(--color-primary);
  color: var(--color-bg);
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 var(--space-2);
}
.migration-modal h3 {
  font-size: 1rem;
  margin: 0 0 var(--space-2);
  color: var(--color-text);
}
.migration-modal ol { margin: 0 0 var(--space-4) 1.2em; padding: 0; }
.migration-modal ol li { margin-bottom: 6px; line-height: 1.45; font-size: 0.9rem; }
.migration-modal .mig-skip {
  font-size: 0.85rem; color: var(--color-text-muted);
  margin: 0 0 var(--space-4); line-height: 1.45;
}
.migration-modal .mig-url-box {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin: 0 0 var(--space-4);
}
.migration-modal .mig-url-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  margin: 0 0 4px;
  font-weight: 600;
}
.migration-modal .mig-url {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.95rem;
  color: var(--color-primary);
  word-break: break-all;
  margin: 0 0 var(--space-3);
}
.migration-modal .mig-buttons { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.migration-modal .mig-btn {
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: 9px 16px;
  font-size: 0.85rem; font-weight: 600;
  font-family: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  text-decoration: none;
}
.migration-modal .mig-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.migration-modal .mig-btn-primary {
  background: var(--color-primary); color: var(--color-bg); border-color: var(--color-primary);
}
.migration-modal .mig-btn-primary:hover { filter: brightness(1.08); color: var(--color-bg); }
.migration-modal .mig-footer {
  font-size: 0.82rem;
  color: var(--color-text-muted);
  margin: var(--space-4) 0 var(--space-3);
  line-height: 1.45;
}
.migration-modal .mig-dismiss {
  background: transparent; border: none;
  color: var(--color-text-muted);
  cursor: pointer;
  font: inherit; font-size: 0.82rem;
  text-decoration: underline;
  padding: 4px 0;
}
.migration-modal .mig-dismiss:hover { color: var(--color-text); }
.migration-modal .mig-copy-feedback {
  display: inline-block;
  font-size: 0.78rem;
  color: var(--color-primary);
  margin-left: var(--space-2);
  opacity: 0;
  transition: opacity 0.2s;
}
.migration-modal .mig-copy-feedback.shown { opacity: 1; }
.hg-theme .icon-sun { display: none; }
.hg-theme .icon-moon { display: inline-block; }
[data-theme='dark'] .hg-theme .icon-sun { display: inline-block; }
[data-theme='dark'] .hg-theme .icon-moon { display: none; }

/* Hide tab-specific controls (currently only the scene group) when the
   user is not on the matching tab. Keeps the header consistent in shape
   but only shows actions that apply to the visible content. */
body:not([data-active-tab="photo-planner"]) [data-tab-only="photo-planner"] {
  display: none;
}

.theme-toggle {
  background: transparent; border: 1px solid var(--color-border);
  color: var(--color-text-muted); padding: var(--space-2);
  border-radius: var(--radius-md); cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
  transition: all var(--transition-interactive);
}
.theme-toggle:hover { color: var(--color-text); border-color: var(--color-primary); }
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: inline-block; }
[data-theme='dark'] .theme-toggle .icon-sun { display: inline-block; }
[data-theme='dark'] .theme-toggle .icon-moon { display: none; }

.tab-nav {
  border-bottom: 1px solid var(--color-divider);
  background: var(--color-surface-2);
  /* Sticks just below the app-header. The header height varies with the
     viewport (brand-name uses clamp()), so app.js measures it on load and
     resize and writes it into --header-height. */
  position: sticky; top: var(--header-height, 144px); z-index: 40;
}
.tab-nav .wrap {
  /* Allow tabs to wrap onto a second row when the viewport is too narrow to
     fit all of them on one line. Keeps every tab visible without needing
     horizontal scrolling. */
  display: flex; flex-wrap: wrap; gap: 2px;
  padding-top: 0; padding-bottom: 0;
}
.tab-btn {
  background: transparent; border: none;
  border-bottom: 2px solid transparent;
  color: var(--color-text-muted);
  font-family: var(--font-body);
  /* Tight sizing so all 10 tab labels fit on a single row at the centered
     1280-wide content column (the smallest the wrap can go). Shrinking the
     font and padding keeps every tab visible without wrapping. */
  font-size: var(--text-sm);
  font-weight: 500;
  padding: var(--space-3) 8px;
  cursor: pointer; white-space: nowrap;
  transition: color var(--transition-interactive),
              border-color var(--transition-interactive);
}
.tab-btn:hover { color: var(--color-text); }
.tab-btn.active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

/* Dark-mode tab readability
   Some browsers / OS-level high-contrast settings override the muted CSS var
   with white-on-white text, which makes inactive tabs invisible. Lock down
   the dark-mode tab bar to specific high-contrast colors so the labels are
   always readable regardless of the system theme. */
[data-theme='dark'] .app-header {
  background: #14181b;
  border-bottom: 1px solid #2a3137;
  /* Drop the backdrop-filter in dark mode — some browsers brighten through it */
  backdrop-filter: none;
}
[data-theme='dark'] .tab-nav {
  background: #1a2024;
  border-bottom: 1px solid #2a3137;
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.04);
}
[data-theme='dark'] .tab-btn {
  color: #c8d0d4;
  font-weight: 500;
}
[data-theme='dark'] .tab-btn:hover {
  color: #ffffff;
  background: rgba(79, 209, 197, 0.08);
}
[data-theme='dark'] .tab-btn.active {
  color: #6ee0d4;
  font-weight: 600;
  border-bottom-color: #6ee0d4;
  background: rgba(79, 209, 197, 0.06);
}

.app-main { padding: var(--space-6) 0 var(--space-16); }
.tab-panel { display: none; }
.tab-panel.active { display: block; animation: fadeIn 250ms ease-out; }

/* Planner tabs (Cube & Imaging, Palette, Inverse): the controls + spectrum
   graph live in a sticky wrapper so they stay pinned at the top of the
   viewport while the user scrolls through the result cards. The page itself
   scrolls (no nested scroll containers) so card rings are never clipped and
   every card has full-page-height room to render its hover/selected state.

   Sticky offset:
   - app-header sticky ~146px (top:0) — brand row + toolbar row
   - tab-nav sticky ~60px (top:144px), overlap ~2px → stack ~204px
   So .planner-stick docks at top:204px just under the tab nav.

   The wrapper paints its own background so the cards scrolling beneath it
   never bleed through. A small bottom shadow gives a subtle separation. */
.planner-stick {
  position: sticky;
  /* Stacks below header + tab-nav. Both heights are written to CSS vars
     by app.js so this offset adapts to whatever the chrome ends up at. */
  top: calc(var(--header-height, 144px) + var(--tabnav-height, 60px));
  z-index: 5;
  background: var(--color-bg);
  padding-bottom: var(--space-2);
  /* The selected result card paints a 2px teal ring as box-shadow which
     extends slightly outside the card's own rectangle. Without horizontal
     padding here, those edge pixels poke out to the left and right of the
     sticky wrapper as the card scrolls behind it — looking like the graph
     card itself was highlighted. Pad the wrapper a few pixels each side and
     pull its content back with negative margins so the bg covers the bleed
     without changing the visual width of the controls or graph. */
  padding-left: 4px;
  padding-right: 4px;
  margin-left: -4px;
  margin-right: -4px;
  /* A subtle shadow under the sticky block so the cards passing beneath it
     have a clear visual edge instead of bleeding into the graph. */
  box-shadow: 0 8px 12px -8px rgba(0, 0, 0, 0.45);
}
[data-theme='light'] .planner-stick {
  box-shadow: 0 8px 12px -8px rgba(0, 0, 0, 0.18);
}
/* COMPACT BY DEFAULT — these rules apply at every viewport size.
   The application is for laptop bench use, where the user wants the
   spectrum graph pinned in the upper half of the screen at all times
   while scrolling through ranked result cards. The chrome (header,
   tab-nav, controls, scene row) is intentionally compact across every
   viewport — there is no "spacious" mode for taller screens, because
   even at 1080p+ the graph staying visible matters more than spacious
   chrome. */

/* Inside the sticky wrapper, the spectrum-card no longer needs its big
   bottom margin — the wrapper itself provides the gap. */
.planner-stick > .spectrum-card {
  margin-bottom: 0;
  margin-top: 0;
  padding: var(--space-2) var(--space-3);
}

/* The panel title (e.g. "Cube & Imaging — best filters for a single dye")
   reads as a small subtitle inside the sticky block, not a billboard. Sized
   to match the field labels (Dye / Light source / etc.) so it reads as a
   section heading without dominating the row. */
.planner-stick > .panel-title {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 var(--space-1) 0;
  line-height: 1.3;
  /* Allow the title to wrap on narrow viewports rather than overflowing the
     visible width of the sticky block. */
  white-space: normal;
  overflow-wrap: anywhere;
}

/* The long descriptive paragraph above the controls is redundant once
   you understand the planner — hide it inside the sticky block on every
   viewport so it never pushes the graph below the fold. */
.planner-stick > .panel-desc { display: none; }

/* Tighter padding inside the controls panel; the scene controls have moved
   to the header grid so they no longer occupy a separate row here. */
.planner-stick > .planner-controls {
  padding: var(--space-2) var(--space-3);
  margin-top: 0;
  margin-bottom: var(--space-2);
  gap: var(--space-2) var(--space-3);
  row-gap: var(--space-1);
}
.planner-stick > .planner-controls select,
.planner-stick > .planner-controls input {
  padding: 4px 8px; font-size: var(--text-sm);
}
.planner-stick > .planner-controls label {
  font-size: var(--text-xs); margin-bottom: 2px;
}

/* Spectrum card header: tight legend, small title, no wasted vertical space */
.planner-stick .spectrum-header {
  padding: 0 0 var(--space-1) 0;
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  gap: var(--space-1);
}
.planner-stick .spectrum-header > * { line-height: 1.2; }
.planner-stick .spectrum-header .spectrum-title { font-size: var(--text-sm); }
.planner-stick .spectrum-header .spectrum-toggles {
  gap: 3px 10px; font-size: var(--text-xs);
}
.planner-stick .spectrum-header .spectrum-toggle { padding: 0; }
.planner-stick .spectrum-header .spectrum-collapse-btn {
  padding: 2px 6px; font-size: var(--text-xs);
}

/* Graph height across viewports. Goal: graph pinned and visible in the
   upper half of the screen at every laptop size. */
.planner-stick .spectrum-card svg { height: 160px; }
@media (max-height: 900px)  { .planner-stick .spectrum-card svg { height: 140px; } }
@media (max-height: 800px)  { .planner-stick .spectrum-card svg { height: 120px; } }
@media (max-height: 720px)  { .planner-stick .spectrum-card svg { height: 110px; } }

/* Tab nav: keep all 10 tabs on a single centered row. Let this row stretch
   beyond the standard 1280-wide content column when needed (the tabs need
   slightly more horizontal space than other content), capped by the actual
   viewport. Tight padding and small font keep the row compact even on a
   13" laptop. */
.tab-nav .wrap {
  max-width: none;
  padding-top: 0; padding-bottom: 0;
  padding-left: var(--space-3);
  padding-right: var(--space-3);
  flex-wrap: wrap;
  justify-content: center;
  row-gap: 0;
  gap: 0;
}
.tab-nav .tab-btn { flex-shrink: 0; }

.app-main { padding-top: var(--space-2); padding-bottom: var(--space-8); }

/* Phones / very short windows: drop sticky so the user gets one normal
   scroll — there isn't enough room to pin anything and still see content. */
@media (max-width: 720px), (max-height: 600px) {
  .planner-stick {
    position: static;
    box-shadow: none;
    padding-bottom: 0;
  }
}
/* Give the result list a little top breathing room from the sticky block,
   and keep its bottom margin so the page-end hint isn't crammed against
   the next section. */
#tab-photo-planner > .results-grid,
#tab-palette-planner > .results-grid,
#tab-inverse-planner > .results-grid {
  margin-top: var(--space-3);
}

.inventory-toggle {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); color: var(--color-text-muted);
  cursor: pointer; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); background: var(--color-surface-2);
  border: 1px solid var(--color-border);
}
.inventory-toggle input { accent-color: var(--color-primary); }
.inventory-toggle:hover { border-color: var(--color-primary); }

.header-controls { display: flex; gap: var(--space-3); align-items: center; }

.panel-title {
  font-family: var(--font-body); font-weight: 600;
  font-size: var(--text-base); margin: var(--space-3) 0 var(--space-1);
  color: var(--color-text); letter-spacing: 0;
}
.panel-desc {
  color: var(--color-text-muted); font-size: var(--text-sm);
  margin: var(--space-1) 0 var(--space-2); max-width: 80ch; line-height: 1.45;
}
/* When the helper paragraph sits inside the sticky planner header (between
   the panel title and the controls box), tighten the surrounding spacing so
   it reads as a subtitle rather than a standalone block. */
.planner-stick > .panel-desc {
  margin: var(--space-1) 0 var(--space-3);
}
.section-subtitle {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--text-base); margin: var(--space-8) 0 var(--space-3);
  color: var(--color-text); letter-spacing: 0.01em;
}

.tab-header-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4); margin-top: var(--space-6);
}
.tab-header-row .panel-title { margin-top: 0; }

.planner-controls {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2) var(--space-3); padding: var(--space-2) var(--space-3);
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); margin-bottom: var(--space-2);
}
.control-group { display: flex; flex-direction: column; gap: var(--space-2); }
/* Stronger field labels: bigger, brighter, heavier so the Dye / Light source /
   Illumination mode / Results shown headings catch the eye and read as the
   primary controls of the panel rather than tiny captions. */
.control-group label {
  font-size: var(--text-sm); color: var(--color-text);
  text-transform: uppercase; letter-spacing: 0.1em; font-weight: 700;
}
.control-group select, .control-group input[type="text"],
.control-group input[type="number"], .control-group input[type="search"] {
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-body); font-size: var(--text-sm);
  background: var(--color-surface-2); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  transition: border-color var(--transition-interactive);
}
.control-group select:focus, .control-group input:focus {
  outline: none; border-color: var(--color-primary);
}

/* v=47e: inline mode label rendered next to the Inverse Planner Light Source
   dropdown. The mode is fully determined by the chosen light source, so we
   show it as read-only text instead of a redundant dropdown the user can't
   change. */
.inline-mode-label {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-style: italic;
  letter-spacing: 0.02em;
}

/* v=47e: keep removed-but-still-referenced selects out of layout. The JS
   reads their .value to feed scoring; they must not display. */
.hidden-controls { display: none !important; }

.search-input {
  width: 100%; max-width: 400px; padding: var(--space-3);
  font-family: var(--font-body); font-size: var(--text-base);
  background: var(--color-surface-2); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  margin-bottom: var(--space-4);
}

.results-grid {
  display: grid; grid-template-columns: 1fr; gap: var(--space-3);
  margin-bottom: var(--space-10);
}

/* v=47: Inverse Planner scope explanatory banner */
.inv-scope-banner {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  color: var(--color-text);
  font-size: var(--text-sm);
  line-height: 1.5;
  margin-bottom: var(--space-2);
}
.inv-scope-banner strong { color: var(--color-text); font-weight: 600; }

.result-card {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-5);
  display: grid; grid-template-columns: auto 1fr auto auto;
  gap: var(--space-4); align-items: start;
  transition: border-color var(--transition-interactive), transform var(--transition-interactive);
  position: relative;
  /* Floor the card height so the right-rail toggles always have room and the
     Compare ▾ menu has a useful interior to open into. Cards now sit at a
     comfortable reading height with vertical padding above and below the
     content rather than feeling cramped. */
  min-height: 200px;
}
/* Right rail holding the cube toggle row (Recommended / Closest / Compare ▾).
   Anchored to the right side of the card so the Compare ▾ menu opens within
   the card's own footprint instead of overlapping the card below. */
.cube-toggle-rail {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-start;
  min-width: 0;
  position: relative;
}
.cube-toggle-rail .cube-match-row,
.cube-toggle-rail .cube-toggle-group {
  margin-top: 0;
  justify-content: flex-end;
}
/* Narrow viewports: collapse the right rail under the result body so the
   card doesn't overflow horizontally. The Compare ▾ menu still anchors to
   the rail and stays inside the card vertically. */
@media (max-width: 860px) {
  .result-card {
    grid-template-columns: auto 1fr auto;
  }
  .cube-toggle-rail {
    grid-column: 1 / -1;
    align-items: flex-start;
  }
  .cube-toggle-rail .cube-match-row,
  .cube-toggle-rail .cube-toggle-group {
    justify-content: flex-start;
  }
}
.result-card:hover { border-color: var(--color-primary); }
/* Top-pick used to share the teal ring with .selected, which made the
   auto-highlighted top result look identical to a user-selected card.
   The top result already auto-applies .selected at render time, so we no
   longer need a separate visual treatment for .top-pick. Reset to default. */
.result-card.top-pick {
  border-color: var(--color-border);
  box-shadow: none;
}

.score-bubble {
  display: flex; flex-direction: column; align-items: center;
  min-width: 70px; padding: var(--space-3) var(--space-2);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.score-value {
  font-family: var(--font-mono); font-weight: 500;
  font-size: var(--text-lg); color: var(--color-primary);
  line-height: 1;
}
.score-label {
  font-size: 10px; color: var(--color-text-muted);
  text-transform: uppercase; letter-spacing: 0.1em; margin-top: 4px;
}
.score-bubble.excellent .score-value { color: var(--color-success); }
.score-bubble.good .score-value { color: var(--color-primary); }
.score-bubble.ok .score-value { color: var(--color-warning); }
.score-bubble.poor .score-value { color: var(--color-error); }

.result-main { display: flex; flex-direction: column; gap: var(--space-2); }
.result-title {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--text-base); color: var(--color-text);
}
.result-meta {
  font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--color-text-muted); line-height: 1.6;
}
.result-detail {
  font-size: var(--text-sm); color: var(--color-text-muted);
  line-height: 1.55;
}

.color-swatch {
  width: 36px; height: 36px; border-radius: var(--radius-md);
  border: 1px solid var(--color-border); flex: none;
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.1);
}
/* In .result-card, the swatch sits in the third grid column next to the
   tall cube-toggle-rail. Center it vertically so the small square aligns
   visually with the middle of the Recommended/Closest/Compare buttons
   instead of being orphaned at the top of the row. */
.result-card > .color-swatch {
  align-self: center;
}

.spectrum-card {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-4);
  margin-bottom: var(--space-6);
}
.spectrum-plot { width: 100%; height: 180px; display: block; }
.spectrum-legend {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-top: var(--space-3); font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.spectrum-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-swatch { width: 12px; height: 2px; border-radius: 1px; }

.chip-list {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.empty-hint {
  flex-basis: 100%;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-style: italic;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  line-height: 1.5;
}
.empty-hint strong { color: var(--color-text); font-weight: 500; font-style: normal; }

/* Footer version line */
.app-footer {
  padding: var(--space-6) 0;
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  margin-top: var(--space-10);
}
.app-footer p {
  font-size: var(--text-xs); color: var(--color-text-muted);
  text-align: center; margin: 0;
}
.app-footer p + p { margin-top: var(--space-2); }
.app-footer .app-credit {
  font-size: var(--text-sm);
  color: var(--color-text);
  font-weight: 500;
}
.app-footer .app-version {
  font-family: var(--font-mono);
  color: var(--color-text-faint);
}
.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--color-text);
  transition: all var(--transition-interactive);
}
.chip.unavailable { opacity: 0.4; }
.chip input[type="checkbox"] { accent-color: var(--color-primary); cursor: pointer; }
.chip .chip-meta {
  font-family: var(--font-body); color: var(--color-text-muted);
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
}

.card-list { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-10); }

/* Entity cards (fluorophores, filters, dichroics, light sources). Available
   items get a stronger border and a thin teal accent stripe on the left so
   the user can quickly scan their working library; unavailable items fade
   back. Hover still highlights any card. */
/* v=47a: equalize box dimensions between available and unavailable cards so
   toggling "Show only checked boxes" doesn't shift the checkbox column.
   Every card now reserves the same 2px border + extra left padding. The
   available variant only changes color and adds the inset accent shadow. */
.entity-card {
  background: var(--color-surface);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  padding-left: calc(var(--space-5) + 2px);
  transition: border-color var(--transition-interactive),
              box-shadow var(--transition-interactive);
  position: relative;
}
.entity-card:not(.unavailable) {
  border-color: var(--color-border-strong, var(--color-border));
  box-shadow: inset 4px 0 0 var(--color-primary);
}
.entity-card:hover { border-color: var(--color-primary); }
.entity-card.unavailable { opacity: 0.55; }
.entity-head {
  display: flex; gap: var(--space-3); align-items: center; justify-content: space-between;
  margin-bottom: var(--space-3); flex-wrap: wrap;
}
.entity-head-left { display: flex; gap: var(--space-3); align-items: center; }
.entity-title {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--text-base); color: var(--color-text);
}
.entity-tag {
  font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--color-text-muted); padding: 2px 8px;
  border-radius: var(--radius-full); background: var(--color-surface-2);
}
.entity-meta {
  font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--color-text-muted); line-height: 1.7;
}
.entity-notes {
  font-size: var(--text-sm); color: var(--color-text-muted);
  line-height: 1.6; margin-top: var(--space-3);
}
.entity-section { margin-top: var(--space-4); }
.entity-section-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--color-text-faint); font-weight: 500;
  margin-bottom: var(--space-2);
}

.protocol-panel {
  margin-top: var(--space-4); padding: var(--space-4);
  background: var(--color-surface-2); border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
}
.protocol-row {
  display: grid; grid-template-columns: 140px 1fr auto;
  gap: var(--space-3); padding: var(--space-2) 0;
  border-bottom: 1px dashed var(--color-divider); align-items: baseline;
}
.protocol-row:last-child { border-bottom: none; }
.protocol-label {
  font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--color-text-muted); font-weight: 500;
}
.protocol-value {
  font-size: var(--text-sm); color: var(--color-text); line-height: 1.6;
  white-space: pre-wrap;
}
.protocol-edit-btn {
  font-size: var(--text-xs); padding: 4px 10px;
  background: transparent; border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); color: var(--color-text-muted);
  cursor: pointer;
}
.protocol-edit-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.protocol-history {
  margin-top: var(--space-2); font-size: var(--text-xs);
  color: var(--color-text-faint); font-style: italic;
}

.affinity-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.affinity-tag {
  font-family: var(--font-mono); font-size: 10px;
  padding: 2px 8px; border-radius: var(--radius-full);
  background: var(--color-surface-offset); color: var(--color-text-muted);
  text-transform: capitalize;
}
.affinity-tag.strong { background: var(--color-primary-highlight); color: var(--color-primary); }

.checkbox-label {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); color: var(--color-text);
  font-weight: 500; letter-spacing: 0.01em;
  cursor: pointer;
}
.checkbox-label input { accent-color: var(--color-primary); width: 16px; height: 16px; }
/* Inside an available entity card the Available label reads as the
   active state, so brighten it to the primary color. */
.entity-card:not(.unavailable) .checkbox-label { color: var(--color-primary); }

.btn {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--text-sm); padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md); cursor: pointer;
  transition: all var(--transition-interactive);
  border: 1px solid var(--color-border);
}
.btn-primary {
  background: var(--color-primary); color: var(--color-text-inverse);
  border-color: var(--color-primary);
}
.btn-primary:hover { background: var(--color-primary-hover); }
.btn-secondary {
  background: var(--color-surface-2); color: var(--color-text);
}
.btn-secondary:hover { border-color: var(--color-primary); color: var(--color-primary); }
.btn-danger {
  background: transparent; color: var(--color-error);
  border-color: var(--color-border);
}
.btn-danger:hover { border-color: var(--color-error); }

.button-row { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* Modal */
.modal-host {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0,0,0,0.6); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-4);
}
.modal-host.hidden { display: none; }
.modal {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-6);
  max-width: 600px; width: 100%; max-height: 85vh; overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.modal-title {
  font-size: var(--text-lg); font-weight: 500; margin: 0 0 var(--space-4);
  color: var(--color-text);
}
.modal-form { display: flex; flex-direction: column; gap: var(--space-4); }
.modal-form label {
  font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--color-text-muted); font-weight: 500;
  margin-bottom: 4px; display: block;
}
.modal-form input, .modal-form select, .modal-form textarea {
  width: 100%; padding: var(--space-2) var(--space-3);
  font-family: var(--font-body); font-size: var(--text-sm);
  background: var(--color-surface-2); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
.modal-form textarea { min-height: 80px; font-family: var(--font-body); resize: vertical; }
.modal-actions {
  display: flex; justify-content: flex-end; gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Glossary */
.glossary-list { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-10); }
.glossary-entry {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-4) var(--space-5);
}
.glossary-entry-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--space-3); margin-bottom: var(--space-2);
}
.glossary-term {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--text-base); color: var(--color-text);
}
.glossary-category {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--color-text-faint);
}
.glossary-short {
  font-size: var(--text-sm); color: var(--color-primary);
  font-style: italic; margin-bottom: var(--space-2);
}
.glossary-full {
  font-size: var(--text-sm); color: var(--color-text-muted);
  line-height: 1.6; white-space: pre-line;
}
.glossary-related {
  font-size: var(--text-xs); color: var(--color-text-faint);
  margin-top: var(--space-3); display: flex; gap: var(--space-2); flex-wrap: wrap;
}
.glossary-related-term {
  padding: 2px 8px; background: var(--color-surface-2);
  border-radius: var(--radius-full); cursor: pointer;
  border: 1px solid var(--color-border);
}
.glossary-related-term:hover { border-color: var(--color-primary); color: var(--color-primary); }

/* Tooltips and in-context glossary linking have been removed from the app.
   The rules below force any leftover .gloss-link or .gloss-tooltip elements
   to render as plain inline text with no special cursor, underline, or
   floating popup, even if some legacy code path still produced them.
   Override every visual state and force the tooltip bubble to be
   non-existent in the layout. */
.gloss-link,
.gloss-link:hover,
.gloss-link:focus,
.gloss-link:active {
  color: inherit;
  cursor: inherit;
  text-decoration: none;
  border-bottom: 0 none;
  transition: none;
  pointer-events: none;
}

.gloss-tooltip,
.gloss-tooltip.visible {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

.category-chip {
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); font-size: var(--text-xs);
  color: var(--color-text); cursor: pointer;
}
.category-chip.active {
  background: var(--color-primary-highlight);
  border-color: var(--color-primary); color: var(--color-primary);
}

.help-section {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-5);
  margin-bottom: var(--space-4);
}
.help-section h3 {
  font-size: var(--text-base); font-weight: 500; margin: 0 0 var(--space-3);
  color: var(--color-primary);
}
.help-section p {
  font-size: var(--text-sm); color: var(--color-text);
  line-height: 1.6; margin: 0 0 var(--space-3);
}
.help-section p:last-child { margin-bottom: 0; }
.help-section code {
  font-family: var(--font-mono); font-size: var(--text-xs);
  background: var(--color-surface-2); padding: 2px 6px;
  border-radius: var(--radius-sm); color: var(--color-primary);
}

/* Manual lists */
.help-section ul,
.help-section ol {
  font-size: var(--text-sm); color: var(--color-text);
  line-height: 1.6; margin: 0 0 var(--space-3); padding-left: 1.4em;
}
.help-section li { margin-bottom: var(--space-2); }
.help-section li:last-child { margin-bottom: 0; }
.manual-substeps { margin-top: var(--space-2); padding-left: 1.2em; }
.help-section ul ul,
.help-section ol ul,
.help-section ol ol { margin-top: var(--space-2); }
.manual-hint {
  font-size: var(--text-xs); color: var(--color-text-faint);
  font-style: italic; margin-bottom: var(--space-3);
}

/* Quick-start panel — highlight at top */
.help-quickstart {
  border-color: var(--color-primary);
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--color-primary) 6%, var(--color-surface)),
    var(--color-surface)
  );
}
.help-quickstart h3 { color: var(--color-primary); }

/* Collapsible per-tab manual blocks */
.manual-details {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  background: var(--color-surface-2);
  overflow: hidden;
  transition: border-color 120ms ease, background 120ms ease;
}
.manual-details:last-child { margin-bottom: 0; }
.manual-details[open] {
  border-color: var(--color-primary);
  background: var(--color-surface);
}
.manual-details > summary {
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text);
  list-style: none;
  display: flex; align-items: center; gap: var(--space-3);
  user-select: none;
}
.manual-details > summary::-webkit-details-marker { display: none; }
.manual-details > summary::before {
  content: "›";
  display: inline-block;
  width: 14px;
  font-size: 16px;
  color: var(--color-primary);
  transition: transform 150ms ease;
}
.manual-details[open] > summary::before { transform: rotate(90deg); }
.manual-details > summary:hover { color: var(--color-primary); }
.manual-body {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
}
.manual-body p,
.manual-body ul,
.manual-body ol { font-size: var(--text-sm); }

/* Session log */
.session-entry {
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-5);
  display: grid; grid-template-columns: 1fr auto; gap: var(--space-4);
}
.session-date { font-family: var(--font-mono); color: var(--color-text-muted); font-size: var(--text-xs); }
.session-title { font-weight: 500; font-size: var(--text-base); margin: 4px 0; }
.session-rating { color: var(--color-warning); font-size: var(--text-base); letter-spacing: 2px; }
.session-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-3); margin-top: var(--space-3);
  font-size: var(--text-xs); color: var(--color-text-muted);
}
.session-grid strong { color: var(--color-text); font-weight: 500; display: block; margin-bottom: 2px; text-transform: uppercase; font-size: 10px; letter-spacing: 0.08em; }
.session-notes { margin-top: var(--space-3); font-size: var(--text-sm); color: var(--color-text); line-height: 1.5; }

/* Tooltip */
.tooltip {
  position: absolute; z-index: 200;
  background: var(--color-surface); border: 1px solid var(--color-primary);
  border-radius: var(--radius-md); padding: var(--space-3);
  max-width: 320px; font-size: var(--text-xs);
  color: var(--color-text); line-height: 1.5;
  box-shadow: var(--shadow-lg);
  pointer-events: none;
}

.empty-state {
  padding: var(--space-10); text-align: center;
  color: var(--color-text-faint); font-size: var(--text-sm);
  background: var(--color-surface); border: 1px dashed var(--color-border);
  border-radius: var(--radius-lg);
}


/* =============================================================================
   v2 additions — clickable result cards, spectrum toggles, filter graph chips
   ============================================================================= */

/* Hint shown below result lists */
.results-hint {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin: var(--space-3) 0 var(--space-8);
  text-align: center;
  font-style: italic;
}

/* Clickable result cards & selected state
   Hover semantics: only ONE card shows the highlighted/selected look at a
   time. While the user is hovering any card in a results list, the previously
   selected card temporarily releases its ring so the hovered card alone
   reads as the candidate. Clicking commits the selection. */
.result-card.clickable {
  cursor: pointer;
  user-select: none;
  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease, transform 120ms ease;
}
.result-card.clickable:hover {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary), 0 4px 16px rgba(79,209,197,0.18);
  background: linear-gradient(0deg, rgba(79,209,197,0.06), rgba(79,209,197,0.03));
  transform: translateY(-1px);
}
.result-card.selected {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary), 0 4px 16px rgba(79,209,197,0.18);
  background: linear-gradient(0deg, rgba(79,209,197,0.06), rgba(79,209,197,0.03));
}
/* When a card is both top-pick AND selected, selected wins */
.result-card.selected.top-pick {
  box-shadow: 0 0 0 2px var(--color-primary), 0 4px 16px rgba(79,209,197,0.22);
}
/* While the user is hovering any card in a results list, release the selected
   ring on the non-hovered card so only one rectangle reads as highlighted.
   Suppressed while a Compare ▾ menu is open so the menu — which overlaps the
   card below — doesn't appear to transfer the selection.
   Also suppressed when the only card being hovered is .hover-locked (the
   lower card the menu just collapsed onto). The :has(...:not(.hover-locked))
   guard means: only release the ring if a non-locked card is being hovered. */
body:not(.compare-menu-open) #pp-results:has(.result-card.clickable:not(.hover-locked):hover) .result-card.selected:not(:hover),
body:not(.compare-menu-open) #pal-results:has(.result-card.clickable:not(.hover-locked):hover) .result-card.selected:not(:hover),
body:not(.compare-menu-open) #inv-results:has(.result-card.clickable:not(.hover-locked):hover) .result-card.selected:not(:hover) {
  border-color: var(--color-border);
  box-shadow: none;
  background: transparent;
}

/* A hover-locked card neutralises its own :hover styling so the visual cue
   doesn't read as "this is the active rectangle" until the user re-engages. */
.result-card.clickable.hover-locked:hover {
  border-color: var(--color-border);
  box-shadow: none;
  background: transparent;
  transform: none;
}
.result-card.clickable.hover-locked.selected:hover {
  /* Selected + locked: keep the selected ring — the lock only suppresses the
     hover treatment, not the locked-in selection. */
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary), 0 4px 16px rgba(79,209,197,0.18);
  background: linear-gradient(0deg, rgba(79,209,197,0.06), rgba(79,209,197,0.03));
}

/* While a Compare ▾ menu is open, freeze hover styling on result cards so
   moving the cursor through the menu (which sits over the next card) doesn't
   visually highlight the underlying card. */
body.compare-menu-open .result-card.clickable:hover {
  border-color: var(--color-border);
  box-shadow: none;
  background: transparent;
  transform: none;
}
body.compare-menu-open .result-card.selected {
  /* Re-assert the selected ring even if a card below is being hovered through
     the menu. */
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary), 0 4px 16px rgba(79,209,197,0.18);
  background: linear-gradient(0deg, rgba(79,209,197,0.06), rgba(79,209,197,0.03));
}

/* Spectrum card header & toggles
   The graph and the planner controls above it stay fixed at the top of
   the panel; only the result cards scroll. The collapse toggle lets the
   user shrink the graph to a slim strip when they want more room. */
.spectrum-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
  flex: none;
  /* Lock a stable footprint so hover-preview redraws don't change card
     height. Without this, varying title-wrap (Leica 455LP, DAPI dichroic
     names) and toggle-row wrap counts shove adjacent cards up/down and
     create rapid mouseenter/mouseleave flicker when the cursor moves
     between list items. 380px accommodates: title 22px + toggles wrapped
     to 3 rows ~80px + 180px plot + legend ~24px + padding/gaps ~74px. */
  min-height: 380px;
  transition: max-height 180ms ease, padding 180ms ease;
}
/* Stop the toggle bar from collapsing/expanding as label widths change.
   Reserves space for up to 3 wrapped rows so the plot below it stays at a
   fixed Y offset regardless of which cube/filter is being previewed. */
.spectrum-card .spectrum-toggles {
  min-height: 78px;
  align-content: flex-start;
}
.spectrum-card.is-collapsed {
  /* Collapsed state: just the header strip with the title and a re-open button. */
  min-height: 0;
  max-height: 56px;
  overflow: hidden;
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
}
.spectrum-card:empty {
  display: none;
}
.spectrum-header {
  display: flex; flex-direction: column; gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.spectrum-header-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
}
.spectrum-collapse-btn {
  flex: none;
  appearance: none;
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--text-xs);
  padding: 4px 10px;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  transition: color 120ms, border-color 120ms, background 120ms;
}
.spectrum-collapse-btn:hover {
  color: var(--color-text);
  border-color: var(--color-text-muted);
  background: rgba(148, 163, 184, 0.08);
}
.spectrum-collapse-btn .chev {
  display: inline-block;
  transition: transform 180ms ease;
}
.spectrum-card.is-collapsed .spectrum-collapse-btn .chev {
  transform: rotate(-90deg);
}
.spectrum-title {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text);
  font-weight: 500;
}
/* v=47a: anchor the seven spectrum-header toggles into stable grid lanes so
   changing a filter, dichroic, or camera doesn't slide the toggles to its
   right by 3-6 px. Each column reserves enough room for its widest realistic
   label; long labels wrap inside their own toggle instead of pushing siblings.
   On viewports narrower than ~1180px the grid falls back to flex-wrap so the
   layout stays usable on smaller screens. */
.spectrum-toggles {
  display: grid;
  grid-template-columns: 175px 175px 180px 120px 95px 95px 330px;
  gap: var(--space-2) var(--space-3);
  align-items: start;
}
/* Inventory preview card: 5 columns (no lamp, no camera). */
.spectrum-toggles.spectrum-toggles-5 {
  grid-template-columns: 175px 180px 120px 95px 95px;
}
@media (max-width: 1180px) {
  .spectrum-toggles {
    display: flex; flex-wrap: wrap;
  }
}
/* Sticky planner header uses a tighter compact layout — keep the flex-wrap
   behavior there since the grid columns are sized for the full card. */
.planner-stick .spectrum-toggles {
  display: flex; flex-wrap: wrap;
}
.spectrum-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  cursor: pointer;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  transition: background 120ms;
}
.spectrum-toggle:hover { background: var(--color-surface-2); }
.spectrum-toggle.disabled {
  opacity: 0.4; cursor: not-allowed;
}
.spectrum-toggle input[type="checkbox"] {
  margin: 0;
  accent-color: var(--color-primary);
}
.spectrum-toggle .legend-swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  flex: 0 0 auto;
  /* Align swatch with the top label line when a sublabel pushes the chip
     to two rows. Without this it ends up vertically centered between the
     two lines, which looks misaligned next to the camera name. */
  margin-top: 3px;
  align-self: flex-start;
}
.spectrum-toggle-text {
  display: inline-flex;
  flex-direction: column;
  line-height: 1.15;
}
.spectrum-toggle-label {
  /* v=47a: allow labels longer than the grid column to wrap inside the
     toggle rather than forcing the row taller via nowrap+overflow. Most
     labels still fit on one line; only edge cases like very long dye
     combinations wrap. */
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.spectrum-toggle-sublabel {
  font-size: 10px;
  opacity: 0.72;
  font-style: italic;
  white-space: nowrap;
}

/* Filter chip with separated check + clickable body */
.chip.clickable {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding-left: 6px;
}
.chip.clickable .chip-check {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding: 2px;
  margin: 0;
}
.chip.clickable .chip-check input {
  margin: 0;
  cursor: pointer;
  accent-color: var(--color-primary);
}
.chip.clickable .chip-body {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.chip.clickable .chip-body:hover {
  background: var(--color-surface-2);
}
.chip.selected {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary-highlight);
  background: rgba(79,209,197,0.10);
}

/* Pre-made cube match badges (Cube & Imaging Planner + Palette Planner) */
.cube-match-row {
  margin-top: var(--space-2);
}
.cube-match {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  letter-spacing: 0.02em;
}
.cube-match.exact {
  color: #34d399;
  border-color: rgba(52, 211, 153, 0.45);
  background: rgba(52, 211, 153, 0.10);
  font-weight: 600;
}
.cube-match.partial {
  color: #fbbf24;
  border-color: rgba(251, 191, 36, 0.40);
  background: rgba(251, 191, 36, 0.08);
}
.cube-match.custom {
  color: var(--color-text-muted);
  border-color: var(--color-border);
  background: var(--color-surface-2);
}

/* Cube toggle group — replaces the static .cube-match badges with an
   interactive [Recommended] / [≈ Closest] / [Compare ▾] switcher. */
.cube-toggle-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 6px;
  margin-top: var(--space-2);
}
.cube-toggle-btn {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  cursor: pointer;
  letter-spacing: 0.02em;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
/* The Recommended button carries the longest, most descriptive label of
   the three slots. Allow it to wrap onto multiple lines so the full phrase
   “Recommended excitation, dichroic, and emission” fits without forcing
   the entire row wider. The Closest slot uses the same wrapping rules and
   a slightly narrower fixed width so its label (“Closest Nikon cube: …”
   or one of the disabled placeholder messages) renders as a uniform
   two-line rectangle next to the green Recommended button. align-items:
   stretch on the group keeps both buttons the same height. */
.cube-toggle-btn.recommended,
.cube-toggle-btn.closest {
  white-space: normal;
  line-height: 1.25;
  text-align: left;
}
.cube-toggle-btn.recommended {
  width: 22ch;
}
.cube-toggle-btn.closest {
  width: 18ch;
}
.cube-toggle-btn:hover {
  border-color: var(--color-accent);
  color: var(--color-text);
}
/* Recommended (primary teal) */
.cube-toggle-btn.recommended.active {
  background: rgba(45, 212, 191, 0.14);
  border-color: rgba(45, 212, 191, 0.55);
  color: #5eead4;
  font-weight: 600;
}
/* Closest pre-made cube (yellow) */
.cube-toggle-btn.closest {
  border-color: rgba(251, 191, 36, 0.40);
  color: #fbbf24;
}
.cube-toggle-btn.closest.active {
  background: rgba(251, 191, 36, 0.16);
  border-color: rgba(251, 191, 36, 0.65);
  color: #fcd34d;
  font-weight: 600;
}
.cube-toggle-btn.closest.unavailable {
  opacity: 0.7;
  font-style: italic;
}
/* Compare dropdown trigger */
.cube-toggle-btn.compare {
  border-style: dashed;
}
/* Placeholder buttons rendered when a slot has no data, so every card has
   the same three buttons in the same positions. Visually de-emphasized
   (dashed border, italic text) but still legible — the message itself
   tells the user why the slot is empty. */
.cube-toggle-btn.disabled,
.cube-toggle-btn:disabled {
  cursor: not-allowed;
  background: transparent;
  color: #b8bec4;
  border-color: var(--color-border);
  border-style: dashed;
  font-style: italic;
}
[data-theme='light'] .cube-toggle-btn.disabled,
[data-theme='light'] .cube-toggle-btn:disabled {
  color: #6b7280;
}
.cube-toggle-btn.disabled:hover,
.cube-toggle-btn:disabled:hover {
  background: transparent;
  border-color: var(--color-border);
}
.cube-toggle-btn.compare.active {
  background: rgba(167, 139, 250, 0.14);
  border-color: rgba(167, 139, 250, 0.55);
  border-style: solid;
  color: #c4b5fd;
  font-weight: 600;
}

/* Manual cube builder row — sits beneath the toggle row inside the rail.
   Three small dropdowns (Ex / DM / Em) plus a live score readout. Lets users
   build any combination of individual filters and see the result instantly. */
.manual-builder-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-top: 6px;
  padding: 6px 8px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.02);
  font-size: var(--text-xs);
}
[data-theme='light'] .manual-builder-row {
  background: rgba(0, 0, 0, 0.02);
}
.manual-builder-title {
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
  padding-right: 4px;
}
.manual-builder-field {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.manual-builder-label {
  color: var(--color-text-muted);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.manual-builder-sel {
  font-size: var(--text-xs);
  padding: 2px 4px;
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  max-width: 180px;
}
.manual-builder-score {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--color-primary);
  margin-left: auto;
  padding: 2px 6px;
  background: rgba(79, 209, 197, 0.08);
  border: 1px solid rgba(79, 209, 197, 0.25);
  border-radius: 4px;
  font-size: 11px;
}
@media (max-width: 860px) {
  .manual-builder-row {
    width: 100%;
  }
  .manual-builder-score {
    margin-left: 0;
  }
}

/* Compare dropdown menu */
.cube-compare-wrap {
  position: relative;
  display: inline-block;
}
/* Compare ▾ menu anchors to the right edge of its trigger so it grows
   leftward into the card's own interior, never spilling onto the card below.
   Height is capped tight and an internal scrollbar handles longer lists — the
   menu stays fully inside the card. JS sets a precise max-height at open time
   based on the card's bounds. */
.cube-compare-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  left: auto;
  z-index: 30;
  min-width: 240px;
  max-height: 90px;
  overflow-y: auto;
  /* Prevent the page from scrolling when the wheel reaches the end of the
     menu's internal scroll. Without this, scroll events "leak" outward and
     the whole page starts to scroll — jarring UX. */
  overscroll-behavior: contain;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  padding: 4px 0;
  /* Make the scrollbar visible against the dark surface. Default WebKit
     scrollbars are near-invisible (black thumb on near-black track). We
     intentionally avoid the standard `scrollbar-width: thin` /
     `scrollbar-color` properties here because Chromium prefers them over
     the explicit ::-webkit-scrollbar pseudo styling below — and the thin
     variant doesn't render a clearly visible thumb on every platform. */
}
.cube-compare-menu::-webkit-scrollbar {
  width: 12px;
}
.cube-compare-menu::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  margin: 4px 0;
}
.cube-compare-menu::-webkit-scrollbar-thumb {
  background: #94a3b8;
  border-radius: 6px;
  border: 2px solid var(--color-surface);
}
.cube-compare-menu::-webkit-scrollbar-thumb:hover {
  background: #cbd5e1;
}
/* Firefox fallback only — Firefox does not support the WebKit pseudos. */
@supports (-moz-appearance: none) {
  .cube-compare-menu {
    scrollbar-width: thin;
    scrollbar-color: #94a3b8 rgba(255, 255, 255, 0.06);
  }
  [data-theme='light'] .cube-compare-menu {
    scrollbar-color: #475569 rgba(0, 0, 0, 0.06);
  }
}
/* Light theme variant: darker thumb against the light surface */
[data-theme='light'] .cube-compare-menu::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.06);
}
[data-theme='light'] .cube-compare-menu::-webkit-scrollbar-thumb {
  background: #475569;
}
[data-theme='light'] .cube-compare-menu::-webkit-scrollbar-thumb:hover {
  background: #1e293b;
}
/* When the trigger sits in the lower half of its card so opening downward
   would clip below, .up flips the menu upward. */
.cube-compare-menu.up {
  top: auto;
  bottom: calc(100% + 4px);
}
.cube-compare-hdr {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding: 6px 12px 4px;
  border-bottom: 1px solid var(--color-border);
}
.cube-compare-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  background: transparent;
  border: 0;
  color: var(--color-text);
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 6px 12px;
  cursor: pointer;
}
.cube-compare-item:hover {
  background: var(--color-surface-2);
}
.cube-compare-name { flex: 1; }
.cube-compare-score {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  min-width: 28px;
  text-align: right;
}
.cube-compare-owned   { color: #34d399; font-size: 10px; }
.cube-compare-not-owned { color: var(--color-text-muted); font-size: 10px; }

/* Palette mode label under the assembly description */
.pal-mode-label {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--color-text-muted);
  letter-spacing: 0.04em;
}

/* Inverse Planner — recommended cube row inside each dye card */
.inv-best-row {
  margin-top: var(--space-2);
  padding: 6px 10px;
  background: var(--color-surface-2);
  border-left: 2px solid rgba(45, 212, 191, 0.55);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.5;
}
.inv-best-row:empty {
  display: none;
}
.inv-best-label {
  display: inline-block;
  color: var(--color-text-muted);
  margin-right: 6px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 10px;
}
.inv-mode-label {
  display: inline-block;
  margin-left: 6px;
  color: var(--color-text-muted);
  font-size: 11px;
}

/* Filters tab — section hint paragraph (under "Pre-made cubes" header) */
.section-hint {
  margin: -2px 0 var(--space-3);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  max-width: 760px;
}

/* Pre-made cube chips */
.cube-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.cube-chip-name {
  font-weight: 600;
}
.cube-chip .chip-body {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 6px 10px;
}
.cube-chip .chip-body > .chip-meta { font-size: 11px; }
.chip-dm {
  font-size: 11px;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.cube-chip.selected .chip-dm { color: #ddd6fe; }
.chip-dm-none { font-style: italic; opacity: 0.7; }

/* "None" chip at end of each section. When a section has any selection it
   sits muted; when nothing is selected in the section it shows .selected,
   so users always see which mode that slot is in. */
.chip-none {
  border-style: dashed;
  opacity: 0.75;
}
.chip-none:hover { opacity: 1; }
.chip-none.selected {
  border-style: solid;
  opacity: 1;
}
.chip-none .chip-body em {
  font-style: normal;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 11px;
  color: var(--color-text-muted);
}
.chip-none.selected .chip-body em {
  color: var(--color-text);
}
.cube-chip-own {
  font-size: 11px;
  color: var(--color-text-muted);
}
.cube-chip:not(.unavailable) .cube-chip-own {
  color: #34d399;
}
/* v=47a: legend line under the Pre-made Cubes heading. Mirrors the dot
   styling used inside cube chips so users can match the symbol to its
   meaning at a glance. */
.cube-legend-hint {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: -4px;
  margin-bottom: var(--space-3);
}
.cube-chip-own-legend {
  font-size: 11px;
  font-weight: 700;
}
.cube-chip-own-legend.filled { color: #34d399; }
.cube-chip-own-legend.hollow { color: var(--color-text-muted); }
/* Selected cube chip uses the violet accent so it's clearly distinct from
   the teal-green selected state of individual filter chips. */
.cube-chip.selected {
  background: rgba(167, 139, 250, 0.18);
  border-color: rgba(167, 139, 250, 0.65);
  color: #c4b5fd;
}
.cube-chip.selected .cube-chip-name { color: #c4b5fd; }
.cube-chip.unavailable {
  opacity: 0.55;
}

/* ---------------------------------------------------------------------------
   Path C — Stain sequence panel (palette planner cards)
   --------------------------------------------------------------------------- */
.sequence-panel {
  margin-top: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  overflow: hidden;
}
.sequence-panel[open] { border-color: var(--color-primary); }
.sequence-panel.untested[open] { border-color: var(--color-warning); }
.sequence-panel > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--color-text);
  display: flex; align-items: center; gap: var(--space-2);
  user-select: none;
}
.sequence-panel > summary::-webkit-details-marker { display: none; }
.sequence-panel > summary::before {
  content: "›";
  display: inline-block;
  width: 12px;
  color: var(--color-primary);
  transition: transform 150ms ease;
}
.sequence-panel[open] > summary::before { transform: rotate(90deg); }
.sequence-panel.untested > summary::before { color: var(--color-warning); }
.seq-summary-label { flex: 1; }
.seq-badge {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
}
.seq-badge.tested {
  color: #34d399;
  border-color: rgba(52, 211, 153, 0.4);
  background: rgba(52, 211, 153, 0.08);
}
.seq-badge.untested {
  color: var(--color-warning);
  border-color: color-mix(in srgb, var(--color-warning) 40%, transparent);
  background: color-mix(in srgb, var(--color-warning) 10%, transparent);
}
.sequence-body {
  padding: var(--space-3);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}
.sequence-note {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-style: italic;
  margin: 0 0 var(--space-2);
  padding: var(--space-2);
  background: color-mix(in srgb, var(--color-warning) 8%, transparent);
  border-left: 2px solid var(--color-warning);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.sequence-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sequence-steps li {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}
.seq-step-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 4px;
}
.seq-step-num {
  font-family: var(--font-mono);
  font-size: 10px;
  background: var(--color-primary);
  color: var(--color-bg);
  width: 18px; height: 18px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
}
.seq-swatch {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  flex-shrink: 0;
}
.seq-step-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text);
}
.seq-step-why {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0;
  line-height: 1.5;
}

/* ---------------------------------------------------------------------------
   Print stylesheet
   Optimized for printing the active tab (typically Help/Manual or Session Log).
   Forces light backgrounds, hides chrome, and expands collapsible sections.
   --------------------------------------------------------------------------- */
@media print {
  @page {
    margin: 16mm 14mm;
    size: auto;
  }

  /* Reset to print-friendly colors regardless of theme */
  :root, [data-theme="dark"], [data-theme="light"] {
    --color-bg: #ffffff !important;
    --color-surface: #ffffff !important;
    --color-surface-2: #f6f6f6 !important;
    --color-text: #111111 !important;
    --color-text-muted: #444444 !important;
    --color-border: #cccccc !important;
    --color-primary: #006666 !important;
    --color-warning: #b06000 !important;
  }

  html, body {
    background: #ffffff !important;
    color: #111111 !important;
  }

  /* Hide chrome that shouldn't appear on paper */
  .app-nav,
  .tab-nav,
  .tab-bar,
  .site-header .header-controls,
  .header-controls,
  .header-grid,
  .header-toggles,
  .inventory-toggle,
  .theme-toggle,
  #toggle-tooltips-wrap,
  .add-btn,
  .planner-controls select,
  .planner-controls,
  .gloss-popover,
  .results-hint,
  button,
  .chip-add,
  .clickable-hint,
  .session-actions,
  .session-export-import,
  .compare-toggle,
  .cube-toggle-group,
  .cube-chip-own {
    display: none !important;
  }

  /* Print only the currently visible tab. Other panels stay hidden so the
     printed output matches what the user is looking at. */
  .tab-panel {
    page-break-inside: auto;
  }
  .tab-panel.active {
    display: block !important;
  }

  /* Expand all collapsible sections so content is fully readable */
  details {
    display: block !important;
  }
  details > summary {
    list-style: none;
    font-weight: 600;
    margin-bottom: 6pt;
  }
  details > summary::before { display: none !important; }
  details > *:not(summary) {
    display: block !important;
  }
  /* Force-open all <details> via the open-pseudoclass via JS isn't needed;
     we explicitly show their bodies with the rule above. */

  /* Avoid orphaned headings */
  h1, h2, h3, h4 {
    page-break-after: avoid;
    color: #111111 !important;
  }

  /* Print URLs after links inside the manual */
  .manual-body a[href^="http"]::after,
  .help-quickstart a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 90%;
    color: #555555;
  }

  /* Spectrum cards: keep them, but flatten dark backgrounds */
  .spectrum-card {
    background: #ffffff !important;
    border: 1px solid #cccccc !important;
    page-break-inside: avoid;
  }
  .spectrum-card svg {
    background: #ffffff !important;
  }

  /* Result/cube cards keep visual distinction without shadows */
  .result-card, .session-entry, .chip {
    background: #ffffff !important;
    border: 1px solid #cccccc !important;
    box-shadow: none !important;
    page-break-inside: avoid;
  }

  /* Sequence panel — force open and visible */
  .sequence-panel {
    background: #ffffff !important;
    border: 1px solid #999999 !important;
    page-break-inside: avoid;
  }
  .sequence-panel > summary { color: #111111 !important; }
  .sequence-steps li {
    background: #f6f6f6 !important;
    border: 1px solid #cccccc !important;
  }

  /* Footer */
  .app-footer {
    border-top: 1px solid #cccccc !important;
    margin-top: 18pt;
    padding-top: 6pt;
  }
  .app-version { color: #555555 !important; }

  /* Don't print background images/decorations */
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    text-shadow: none !important;
  }

  /* Glossary tooltips never show in print */
  .gloss-popover, .glossary-tooltip { display: none !important; }
}

/* ---------------------------------------------------------------------------
   Recommended-assembly title row: "Excitation filter: 360/40 BP →
   Dichroic mirror: 400 LP → Emission filter: 460/50 BP"
   --------------------------------------------------------------------------- */
.asm-part {
  white-space: nowrap;
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.asm-role {
  color: var(--color-text-muted);
  font-weight: 400;
  font-size: 0.92em;
}
.asm-value {
  color: var(--color-text);
  font-weight: 600;
  font-family: var(--font-mono);
  font-size: 0.96em;
}
.asm-arrow {
  color: var(--color-primary);
  margin: 0 4px;
  font-weight: 600;
}
.result-title {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 4px;
  align-items: baseline;
  line-height: 1.5;
}

/* ===== My Inventory tab ===== */
.inventory-callout {
  margin: var(--space-4) 0 var(--space-8);
  padding: var(--space-4) var(--space-5);
  border: 1px solid color-mix(in oklab, var(--color-accent) 35%, transparent);
  background: color-mix(in oklab, var(--color-accent) 10%, var(--color-surface));
  border-radius: var(--radius-md);
  line-height: 1.55;
  color: var(--color-text);
}
.inventory-callout > strong:first-child {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--text-md);
}
.link-like {
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--color-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.link-like:hover,
.link-like:focus-visible {
  color: var(--color-accent-strong, var(--color-accent));
  text-decoration-thickness: 2px;
  outline: none;
}
.inventory-section {
  margin-bottom: var(--space-10);
}
.inventory-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.inventory-section-head .section-subtitle {
  margin: 0;
}
.inventory-count {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--radius-full);
  background: var(--color-surface-offset);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: 500;
}
.inventory-cube-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-bottom: var(--space-2);
}
.inv-cube-family {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.inv-cube-family-label {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
  padding-bottom: 4px;
}
.inv-cube-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-3);
}
.inv-cube-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  cursor: pointer;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease;
}
.inv-cube-card:hover {
  border-color: var(--color-primary);
}
.inv-cube-card.owned {
  border: 2px solid var(--color-primary);
  padding: calc(var(--space-3) - 1px) calc(var(--space-4) - 1px);
  box-shadow: inset 4px 0 0 var(--color-primary);
  background: var(--color-primary-highlight);
}
.inv-cube-toggle {
  margin-top: 3px;
  flex: 0 0 auto;
  accent-color: var(--color-primary);
  cursor: pointer;
}
.inv-cube-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.inv-cube-name {
  font-weight: 600;
  color: var(--color-text);
  line-height: 1.25;
}
.inv-cube-brand {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.inv-cube-parts {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: 4px;
  word-break: break-word;
}
.inv-cube-card.owned .inv-cube-parts {
  color: var(--color-text);
}
.inv-cube-remove {
  align-self: flex-start;
  white-space: nowrap;
}


/* Camera picker in header toolbar */
.camera-picker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.camera-picker > span { font-weight: 500; }
.camera-picker select {
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: var(--text-sm);
  font-family: inherit;
  min-width: 220px;
}

/* Scene controls on Cube & Imaging tab — flat row, no box, sits inline
   above the spectrum graph as a thin utility row. */
.scene-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: var(--space-2);
  padding: 0;
  border: none;
  background: transparent;
}
.scene-controls .btn-ghost {
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text);
  padding: 6px 12px;
  font-size: var(--text-sm);
  border-radius: 6px;
  cursor: pointer;
}
.scene-controls .btn-ghost:hover { border-color: var(--color-accent); color: var(--color-accent); }
.scene-load-select {
  flex: 1;
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: var(--text-sm);
  font-family: inherit;
  max-width: 360px;
}

/* Coverage view ("What can I see with what I own?") */
.coverage-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
}
.coverage-row {
  display: grid;
  grid-template-columns: 110px 1fr auto 56px;
  gap: 12px;
  align-items: center;
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-surface);
  font-size: var(--text-sm);
}
.coverage-tier {
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 4px;
  text-align: center;
}
.coverage-tier-excellent { background: #15803d; color: #fff; }
.coverage-tier-good      { background: #2563eb; color: #fff; }
.coverage-tier-partial   { background: #b45309; color: #fff; }
.coverage-tier-none      { background: #4b5563; color: #fff; }
.coverage-name { font-weight: 500; color: var(--color-text); }
.coverage-via  { color: var(--color-text-muted); font-size: 12px; }
.coverage-pct  {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  color: var(--color-text-muted);
  text-align: right;
}
.coverage-row.coverage-none { opacity: 0.65; }

@media (max-width: 720px) {
  .coverage-row { grid-template-columns: 90px 1fr 50px; }
  .coverage-via { grid-column: 1 / -1; }
  .camera-picker select { min-width: 160px; }
}

/* Hide print button when actually printing the page */
@media print { .no-print { display: none !important; } }

/* =====================================================================
   Ownership indicators (green dot + "from <cube>" lines).

   Surfaces "you own this part" across all three planners and the
   Filters & Dichroics tab. Computed cheaply in the render layer from
   state.ownedCubes / state.customCubes via getInventorySources(); no
   recomputation, no cache invalidation, no impact on scoring.

   Visual:
   - .own-dot          7px filled circle, calm green (#5fc88a)
   - .own-prefix       inline ● prefix used inside <option> dropdowns
                       (option text can't render styled HTML)
   - .own-from         small grey footer line "from <cube>" with the
                       cube name in green; placed under the cube
                       toggle rail in result cards
   - .own-legend       quiet inline legend shown under planner headers
                       only when at least one item is owned
   ===================================================================== */
:root {
  --color-owned: #5fc88a;
  --color-owned-soft: rgba(95, 200, 138, 0.18);
}
[data-theme="light"] {
  /* Slightly deeper green on light backgrounds for AA contrast on text */
  --color-owned: #2e8b57;
  --color-owned-soft: rgba(46, 139, 87, 0.18);
}

.own-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--color-owned);
  box-shadow: 0 0 0 1.5px var(--color-owned-soft);
  flex-shrink: 0;
  vertical-align: 1px;
  margin-right: 5px;
}
/* When the dot sits inside a flex chip, .asm-part, etc., the explicit
   right-margin can collide with existing gap rules — strip it then. */
.chip .own-dot,
.asm-part .own-dot,
.cube-compare-name .own-dot,
.manual-builder-row .own-dot {
  margin-right: 4px;
}

/* "From <cube>" footer block placed inside the cube-toggle rail under
   the toggle buttons. One or more short lines, each prefixed with a
   tiny green dot to visually rhyme with the chip dots. */
.own-from {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 6px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  line-height: 1.35;
}
.own-from-line {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}
.own-from-line .own-dot {
  width: 5px; height: 5px;
  margin-right: 0;
  vertical-align: 0;
}
.own-from-line b {
  color: var(--color-owned);
  font-weight: 500;
}

/* Legend: small line under each planner header. Hidden when the user
   hasn't checked anything in My Inventory. */
.own-legend {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-3);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.own-legend .own-dot {
  margin-right: 2px;
}
.own-legend.hidden { display: none; }

/* ---------------------------------------------------------------------------
   Inventory tab: sticky cube spectral preview
   ---------------------------------------------------------------------------
   Sticky panel above the cube grid. Hover any cube card below to overlay
   that cube's ex / dichroic / em on the selected dye's curves. */
/* Dotted divider line above the Compare Cubes Against Dye block, so the
   paragraph above it is visually separated from the sticky rectangle. */
.inv-preview-divider {
  border: 0;
  border-top: 1px dotted var(--color-text-muted);
  opacity: 0.55;
  margin: var(--space-4) 0 var(--space-3) 0;
  width: 100%;
}

.inv-preview-stick {
  position: sticky;
  /* Stack below the app-header AND the tab-nav. app.js measures both and
     writes --header-height and --tabnav-height; fall back to sensible
     defaults if those haven't been set yet. The previous rule used a
     misspelled variable (--tab-nav-height) and only the tab-nav height,
     so the sticky panel docked too high and the section title was hidden
     behind the chrome on scroll. */
  top: calc(var(--header-height, 144px) + var(--tabnav-height, 60px));
  z-index: 5;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.inv-preview-controls {
  display: flex;
  gap: var(--space-3);
  align-items: end;
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}
.inv-preview-controls .control-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 220px;
}
.inv-preview-controls label {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-weight: 500;
}
.inv-preview-controls select {
  padding: 6px 8px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--text-sm);
}
.inv-preview-hint {
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  margin: 0;
  flex: 1;
  min-width: 200px;
  align-self: end;
}

/* Per-cube fit score chip on each card. Colors track scoreClassFor tiers. */
.inv-cube-fit {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.4;
  margin-left: 6px;
  vertical-align: middle;
  border: 1px solid transparent;
}
.inv-cube-fit.fit-excellent {
  background: color-mix(in srgb, var(--color-success) 15%, transparent);
  color: var(--color-success);
  border-color: color-mix(in srgb, var(--color-success) 35%, transparent);
}
.inv-cube-fit.fit-good {
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
  color: var(--color-primary);
  border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
}
.inv-cube-fit.fit-ok {
  background: color-mix(in srgb, var(--color-warning) 15%, transparent);
  color: var(--color-warning);
  border-color: color-mix(in srgb, var(--color-warning) 30%, transparent);
}
.inv-cube-fit.fit-poor {
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
  color: var(--color-error);
  border-color: color-mix(in srgb, var(--color-error) 25%, transparent);
}

@media (max-width: 720px) {
  .inv-preview-stick { position: static; }
  .inv-preview-hint { width: 100%; }
}

/* Inventory baseline controls (Save / Reset to baseline). */
.inv-baseline-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-3) 0 var(--space-2);
}
.inv-baseline-status {
  font-size: var(--text-sm);
  color: var(--color-text-soft);
}
.inv-baseline-row .btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
.inv-baseline-hint {
  margin-top: 0;
  margin-bottom: var(--space-4);
}

/* ===== Welcome modal (first-visit overlay) ===== */
.welcome-modal {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.welcome-modal.hidden { display: none; }
.welcome-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.70);
  backdrop-filter: blur(6px);
}
.welcome-modal-card {
  position: relative;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
  animation: welcome-pop 220ms ease-out;
}
@keyframes welcome-pop {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.welcome-modal-title {
  margin: 0 0 var(--space-3);
  font-size: 1.35rem;
  color: var(--color-text);
  line-height: 1.25;
}
.welcome-modal-body {
  margin: 0 0 var(--space-3);
  color: var(--color-text);
  font-size: 0.95rem;
  line-height: 1.55;
}
.welcome-modal-enjoy {
  margin: var(--space-4) 0 var(--space-4);
  color: var(--color-text-muted);
  font-style: italic;
  font-size: 0.95rem;
  text-align: center;
}
.welcome-modal-actions {
  display: flex;
  gap: var(--space-3);
  /* v=44: bookend layout — Read Me on the left, Open App on the right,
     same size for visual balance. */
  justify-content: space-between;
  flex-wrap: wrap;
  align-items: center;
}
/* Match button widths so the two action buttons feel balanced. The
   outline vs filled distinction still tells the user which is the
   primary action. */
.welcome-action-btn {
  min-width: 140px;
  justify-content: center;
}
.welcome-modal-close {
  position: absolute;
  top: 12px;
  right: 14px;
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: 1.6rem;
  line-height: 1;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
}
.welcome-modal-close:hover { color: var(--color-text); background: var(--color-surface-2); }

/* Quickstart highlight pulse when Open Read Me is clicked */
.help-quickstart.welcome-highlight {
  animation: quickstart-pulse 1.6s ease-out 1;
  border-radius: var(--radius-md);
}
@keyframes quickstart-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(79, 209, 197, 0.55); background: rgba(79, 209, 197, 0.06); }
  50%  { box-shadow: 0 0 0 10px rgba(79, 209, 197, 0.0);  background: rgba(79, 209, 197, 0.10); }
  100% { box-shadow: 0 0 0 0 rgba(79, 209, 197, 0.0);  background: transparent; }
}

.help-welcome-replay { border-top: 1px dashed var(--color-border); padding-top: var(--space-4); }

/* ===== Help tab "Ready to explore the app?" call to action ===== */
.help-cta {
  text-align: center;
  background: linear-gradient(180deg, rgba(79, 209, 197, 0.08), rgba(79, 209, 197, 0.02));
  border: 1px solid rgba(79, 209, 197, 0.25);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-4);
  margin: var(--space-4) 0;
}
.help-cta-prompt {
  margin: 0 0 var(--space-3);
  font-size: 1.05rem;
  color: var(--color-text);
  font-style: italic;
}
.help-cta-btn {
  font-size: 0.98rem;
  padding: 10px 22px;
}

/* ===== Graph orientation guide (Filters & Dichroics tab) ===== */
.graph-guide {
  background: linear-gradient(180deg, rgba(79, 209, 197, 0.07), rgba(79, 209, 197, 0.02));
  border: 1px solid rgba(79, 209, 197, 0.25);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin: var(--space-4) 0 var(--space-2);
}
.graph-guide-title {
  margin: 0 0 var(--space-3);
  font-size: 1.05rem;
  color: var(--color-text);
  font-weight: 700;
}
.graph-guide-list {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: var(--space-4);
  row-gap: var(--space-2);
  align-items: baseline;
}
.graph-guide-list dt {
  font-weight: 700;
  color: var(--color-text);
  font-size: 0.92rem;
  white-space: nowrap;
}
.graph-guide-list dd {
  margin: 0;
  color: var(--color-text);
  font-size: 0.92rem;
  line-height: 1.5;
}
.graph-guide-tip {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px dashed rgba(79, 209, 197, 0.25);
  color: var(--color-text-muted);
  font-size: 0.88rem;
  font-style: italic;
}
@media (max-width: 640px) {
  .graph-guide-list { grid-template-columns: 1fr; row-gap: var(--space-3); }
  .graph-guide-list dt { margin-bottom: -4px; }
}

/* v=44 startup loading indicator.
   Rik Littlefield noted the worst UX was a UI that paints but is silently
   non-responsive. This pill in the lower right tells the user that work is
   still happening in background so they know the page hasn't frozen. */
#loading-indicator {
  position: fixed;
  bottom: 14px;
  right: 14px;
  z-index: 9999;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  pointer-events: none;
  opacity: 0.9;
  transition: opacity 0.4s ease;
}

/* v=45 per-tab "Show Only Checked Boxes" toggle.
   Sits at the top of each list-bearing tab. Off by default so new users
   see the full Nikon catalog while they explore. */
.compact-toggle-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: var(--space-2) 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2);
  border-radius: var(--radius-md);
  font-size: 0.9rem;
}
/* v=45: section-subtitle layout that places the per-section
   "Show Only Checked Boxes" toggle next to the title with a comfortable gap. */
.section-subtitle-row {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
}
.inline-compact-toggle {
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--color-text-muted);
  letter-spacing: 0;
  text-transform: none;
}
.inline-compact-toggle input[type="checkbox"] {
  margin: 0;
}
.compact-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-weight: 500;
  color: var(--color-text);
}
.compact-toggle input[type="checkbox"] {
  cursor: pointer;
  accent-color: var(--color-primary);
}
.compact-toggle-hint {
  color: var(--color-text-muted);
  font-size: 0.82rem;
  font-style: italic;
}
/* Empty-state message when compact view is on with nothing checked. */
.compact-empty {
  padding: var(--space-3);
  margin: var(--space-2) 0;
  background: var(--color-surface-2);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: 0.95rem;
  text-align: center;
}

/* v=45f: Custom-cubes collapsible block sits above the built-in cube grid
   on the My Inventory tab. Put the "+ Add a custom cube" button inline
   inside the <summary> on the right so it's always reachable without
   expanding the section. The button must not collapse/expand the details
   element when clicked. */
.inv-custom-cubes-block > summary {
  justify-content: space-between;
}
.inv-custom-cubes-block > summary > span {
  font-weight: 600;
  font-size: var(--text-base);
}
.inv-custom-add-inline {
  margin-left: auto;
}
/* Brand-level collapsible block ("Nikon cubes", "Leica cubes") */
.inv-brand-block {
  margin-bottom: var(--space-3);
}
.inv-brand-block > summary > span {
  font-weight: 600;
  font-size: var(--text-base);
}
/* "Built-in cubes" subheading row, with the inline compact toggle on the right */
.inv-builtin-cubes-head {
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
}

/* v=45g: small count badge in brand-level summary rows */
.inv-brand-count {
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 0.85em;
  font-weight: 500;
}
.inv-brand-block[open] .inv-brand-count {
  background: var(--color-surface);
}
/* v=45g: Right-aligned group inside the brand summary row (count + Hide). */
.inv-brand-summary-right {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
/* v=45i: "Brand cubes" launcher row at the bottom of the brand strip.
   Single persistent row with a dropdown that lists every brand not in the
   strip. Picking one opens it as a new block above. */
.inv-brand-launcher {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2);
  border: 1px dashed var(--color-border);
  border-radius: 8px;
}
.inv-brand-launcher-label {
  font-size: 0.95em;
  font-weight: 600;
  color: var(--color-text);
}
.inv-brand-launcher-select {
  padding: 4px 10px;
  font-family: inherit;
  font-size: 0.9em;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
}
.inv-brand-launcher-hint {
  font-size: 0.85em;
  color: var(--color-text-muted);
  flex: 1 1 0;
  min-width: 200px;
}
/* v=45i: "+ Add a <brand> cube" button inside each brand block. */
.inv-brand-block-actions {
  margin-top: var(--space-3);
  display: flex;
  justify-content: flex-end;
}
/* v=45h: "Brand cubes" section divider that sits between the last
   built-in brand block (Nikon/Leica/etc.) and the Custom cubes <details>
   block. Acts as the entry point for adding more built-in brands and also
   visually separates the three logical groups of cubes (Nikon, other
   built-ins, and custom). */
.inv-section-divider {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-top: 1px dashed var(--color-border);
}
.inv-section-subhead {
  margin: 0;
  font-size: 0.95em;
  font-weight: 600;
  color: var(--color-text);
  letter-spacing: 0.01em;
}
.inv-section-actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.inv-section-hint {
  font-size: 0.85em;
  color: var(--color-text-muted);
}
/* v=45g: "+ Brand" picker buttons in the inv-hidden-bar (top of cube grid).
   Lets the user re-add a built-in brand block (e.g. Leica) when disabled. */
.inv-brand-enable {
  display: inline-block;
  margin-left: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  border-radius: 6px;
  padding: 2px 10px;
  font-size: 0.85em;
  cursor: pointer;
  font-family: inherit;
}
.inv-brand-enable:hover {
  background: var(--color-surface-2);
  border-color: var(--color-text-muted);
}
/* v=45g: "Hide brand" / "Remove block" affordance — visually quiet. */
.inv-brand-hide,
.inv-brand-remove {
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  border-radius: 6px;
  padding: 2px 8px;
  font-size: 0.8em;
  cursor: pointer;
  font-family: inherit;
}
.inv-brand-hide:hover,
.inv-brand-remove:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
  border-color: var(--color-text-muted);
}
/* v=45g: Per-cube "Hide" button on each inv-cube-card. Pinned to the top-right
   corner. Translucent until the card is hovered so it doesn't distract. */
.inv-cube-card {
  position: relative;
}
.inv-cube-hide {
  position: absolute;
  top: 4px;
  right: 4px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--color-text-muted);
  border-radius: 6px;
  padding: 1px 6px;
  font-size: 0.75em;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
  font-family: inherit;
}
.inv-cube-card:hover .inv-cube-hide,
.inv-cube-hide:focus-visible {
  opacity: 1;
  border-color: var(--color-border);
  background: var(--color-surface);
}
.inv-cube-hide:hover {
  color: var(--color-text);
  border-color: var(--color-text-muted);
}
/* v=45g: "Show all hidden" bar at the top of My Inventory grid. */
.inv-hidden-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  background: var(--color-surface-2);
  border: 1px dashed var(--color-border);
  border-radius: 8px;
  font-size: 0.9em;
  color: var(--color-text-muted);
}
.inv-hidden-bar .inv-hidden-summary {
  flex: 1 1 auto;
}
.btn-link {
  background: transparent;
  border: none;
  color: var(--color-accent, var(--color-text));
  text-decoration: underline;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  padding: 0;
}
.btn-link:hover {
  text-decoration: none;
}
/* v=45g: "+ Add a brand block" + "+ Add a custom cube" cluster on the right
   side of the Custom cubes summary row. */
.inv-custom-actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
/* v=45g: Hide + Remove cluster on each custom cube card. */
.entity-head-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* v=46 perf: Computing… status placeholder shown while the planner scores
   combinations. Keeps the UI honest about background work and prevents the
   user from thinking the page has frozen. */
.computing-status {
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-lg);
  color: var(--color-text-muted, #888);
  font-style: italic;
  text-align: center;
}
