@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
  --bg: #0a0a0f;
  --bg-1: #0f0f16;
  --bg-2: #16161f;
  --bg-3: #1e1e2a;
  --bg-4: #262635;
  --line: #2a2a3a;
  --line-2: #3a3a50;
  --text: #f0f0f8;
  --muted: #8888a0;
  --accent: #00e5a0;
  --accent-2: #00ffc8;
  --accent-glow: rgba(0, 229, 160, 0.4);
  --secondary: #ff6b9d;
  --secondary-2: #ff8bb8;
  --tertiary: #6b8bff;
  --tertiary-2: #8ba8ff;
  --good: #00e5a0;
  --warn: #ffc46a;
  --bad: #ff6b9d;
  --shadow: 0 8px 32px rgba(0,0,0,0.5);
  --shadow-glow: 0 0 40px rgba(0, 229, 160, 0.15);
  --radius: 14px;
  --radius-sm: 8px;
  --radius-lg: 20px;
  font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
}

/* Global keyboard-focus ring — WCAG 2.4.7. Mouse clicks won't show it
   (because :focus-visible only fires for keyboard nav); Tab key reveals
   the accent ring on every interactive element. */
:focus { outline: none; }
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[role="tab"]:focus-visible,
.tool:focus-visible,
.seg:focus-visible,
.tab:focus-visible,
.material:focus-visible,
.preset:focus-visible,
.panel-collapse:focus-visible,
.panel-pop:focus-visible,
.panel-close:focus-visible,
.topbar-more:focus-visible,
.zen-toggle:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 229, 160, 0.18);
  border-radius: var(--radius-sm);
}
input[type="range"]:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }

body {
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Removed ambient background gradients to prevent compositing flicker */

button { font: inherit; color: inherit; cursor: pointer; }
input, button { font: inherit; }

/* Scrollbars — dark-themed, matches accent on hover */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.10); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: rgba(0,229,160,0.38); }
::-webkit-scrollbar-corner { background: transparent; }
* { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.10) transparent; }

/* Collapse button — hidden base; desktop overrides to inline-flex */
.panel-collapse { display: none; }

/* Overlay panels are always floating — hide their collapse+dock buttons */
#infoOverlay .panel-collapse,
#lessonOverlay .panel-collapse { display: none !important; }

/* ======================= TOPBAR ======================= */
.topbar {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 20px;
  padding: 12px 20px;
  padding-right: 60px;
  background: var(--bg-1);
  border-bottom: 1px solid var(--line);
  z-index: 10;
  /* `layout style` only — NOT `paint`. Paint containment clips everything to
     the topbar box (even fixed descendants, which is why the ⋯ menu lives at
     body level), which truncated the control tooltips that render just below
     their buttons. Dropping paint lets those tooltips show in full over the
     canvas; layout/style containment keeps the perf isolation. */
  contain: layout style;
  will-change: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-right: 8px;
}

.logo {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border-radius: 10px;
  color: var(--bg);
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.1),
    0 4px 20px var(--accent-glow),
    inset 0 1px 0 rgba(255,255,255,0.2);
}
.logo svg { width: 24px; height: 24px; }

.title {
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.3px;
  white-space: nowrap;
  background: linear-gradient(135deg, var(--text) 30%, var(--accent-2) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* Brand never shrinks below its content (mobile topbar is overflow-x: auto,
   so the topbar can scroll instead of cramming the brand). */
.topbar .brand { flex-shrink: 0; }

.bar-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  min-width: 0;
}

.bar-group label {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  font-weight: 500;
}

.bar-group input[type=range] {
  width: 100px;
  min-width: 60px;
  flex: 1 1 auto;
  max-width: 140px;
}

.bar-group.btns {
  margin-left: auto;
  gap: 6px;
  flex-wrap: wrap;
}

.seg-label {
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
}

.segment {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 3px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 10px;
}

/* Following pill — lock-follow state + unlock, under the camera segment. */
.follow-pill {
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 10px;
  background: rgba(0, 229, 160, 0.08);
  border: 1px solid rgba(0, 229, 160, 0.35);
  border-radius: 999px;
  font-size: 11px;
  color: var(--accent);
}
.follow-pill[hidden] { display: none; }
.follow-pill span {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.follow-pill button {
  flex: 0 0 auto;
  display: flex;
  background: transparent;
  border: 0;
  padding: 2px;
  color: var(--accent);
  opacity: 0.6;
  transition: opacity 120ms ease;
}
.follow-pill button:hover { opacity: 1; }
.follow-pill button svg { width: 12px; height: 12px; pointer-events: none; }

/* Recenter — camera escape hatch under the Free/Wide segment. */
.recenter-btn {
  margin-top: 6px;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 11px;
  padding: 6px 8px;
}
.recenter-btn svg { width: 13px; height: 13px; pointer-events: none; }

.seg {
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: 6px 10px;
  font-size: 11px;
  font-weight: 500;
  border-radius: 7px;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.seg:hover {
  color: var(--text);
  background: rgba(255,255,255,0.04);
}

.seg.active {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.2), rgba(0, 255, 200, 0.1));
  color: var(--accent-2);
  box-shadow: 
    inset 0 0 0 1px rgba(0, 229, 160, 0.4),
    0 2px 12px rgba(0, 229, 160, 0.15);
}

.btn {
  background: var(--bg-3);
  border: 1px solid var(--line);
  padding: 8px 14px;
  border-radius: 10px;
  color: var(--text);
  font-weight: 500;
  font-size: 13px;
  transition: all 0.15s ease;
}

.btn:hover {
  border-color: var(--line-2);
  background: var(--bg-4);
  transform: translateY(-1px);
}

.btn:active {
  transform: translateY(0);
}

/* Inline Lucide icons — one stroke set across all controls. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
}
.btn > svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  pointer-events: none;
}
.btn.icon-btn { padding: 8px 10px; }

/* Unified styled tooltips — EVERY topbar icon button carries data-tip and
   gets the same pill below it (one mechanism, position, and styling across
   the whole bar). Replaces native `title` (slow + inconsistent) and the old
   per-button rules. Un-clipped because the topbar no longer paint-contains.
   Gated to hover-capable pointers: tooltips are hover-only, and `position:
   relative` here must NOT override the mobile ⋯ FAB's `position: fixed`
   (touch devices have hover:none, so they skip this block entirely). */
@media (hover: hover) {
  .topbar [data-tip] { position: relative; }
  .topbar [data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: 4px 8px;
    background: var(--bg-2);
    border: 1px solid var(--line-2);
    border-radius: 6px;
    font-size: 10px;
    font-weight: 500;
    color: var(--text);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease;
    z-index: 50;
  }
  .topbar [data-tip]:hover::after { opacity: 1; transition-delay: 200ms; }
}
/* Reset/Clear armed-confirm state (the data-tip swaps to "Click again…"). */
#quickResetBtn.confirm,
#quickClearBtn.confirm {
  border-color: var(--warn);
  color: var(--warn);
  font-size: 11px;
  font-weight: 600;
}

/* ===================== Quick-control band ===================== */
.quick-controls { gap: 6px 10px; align-items: center; }
.qc-group { display: flex; align-items: center; gap: 6px; }
.qc-sep { width: 1px; height: 24px; align-self: center; background: var(--line); flex: 0 0 auto; }
/* Speed segment: standard segmented control, slightly tighter than the big ones. */
.qc-speed { padding: 2px; gap: 2px; }
.qc-speed .seg { padding: 5px 9px; font-size: 11px; }
/* Toggle/step buttons match the pause/reset/clear icon-btn cluster (active
   state below; tooltip handled by the unified .topbar [data-tip] rule). */
.qc-btn.active {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.2), rgba(0, 255, 200, 0.1));
  color: var(--accent-2);
  border-color: rgba(0, 229, 160, 0.4);
  box-shadow: inset 0 0 0 1px rgba(0, 229, 160, 0.4), 0 2px 12px rgba(0, 229, 160, 0.15);
}

.topbar-more > svg { width: 18px; height: 18px; pointer-events: none; }
.panel-header button svg { width: 14px; height: 14px; pointer-events: none; }
.hint-close svg { width: 13px; height: 13px; pointer-events: none; }
.zen-toggle svg { width: 16px; height: 16px; pointer-events: none; }
.ctx-item svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  vertical-align: -2px;
  pointer-events: none;
}

.btn.ghost {
  background: transparent;
}

.btn.ghost:hover {
  background: var(--bg-3);
}

.btn.danger:hover {
  color: var(--bad);
  border-color: rgba(255, 107, 157, 0.5);
  background: rgba(255, 107, 157, 0.1);
}

.btn.pro-btn {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.15), rgba(0, 255, 200, 0.08));
  border: 1px solid rgba(0, 229, 160, 0.4);
  color: var(--accent-2);
  font-weight: 600;
  font-size: 12px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  box-shadow: 0 0 15px rgba(0, 229, 160, 0.2);
}

.btn.pro-btn:hover {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.25), rgba(0, 255, 200, 0.15));
  border-color: rgba(0, 229, 160, 0.6);
  box-shadow: 0 0 25px rgba(0, 229, 160, 0.35), 0 2px 12px rgba(0, 229, 160, 0.2);
  transform: translateY(-2px);
}

.btn.pro-btn:active {
  transform: translateY(0);
}

/* ======================= TOGGLES ======================= */
/* ======================= TOPBAR DIVIDER ======================= */
.topbar-divider {
  width: 100%;
  height: 8px;
  background: var(--bg-1);
  border-bottom: 1px solid var(--line);
  cursor: ns-resize;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  contain: strict;
  isolation: isolate;
}

.topbar-divider:hover {
  background: var(--bg-2);
}

.divider-handle-h {
  width: 48px;
  height: 4px;
  background: linear-gradient(90deg, var(--accent), var(--secondary));
  border-radius: 2px;
  opacity: 0.35;
}

.topbar-divider:hover .divider-handle-h,
.topbar-divider.dragging .divider-handle-h {
  opacity: 1;
  box-shadow: 0 0 16px var(--accent-glow);
}

.topbar-divider.dragging {
  background: var(--bg-3);
}



/* ======================= RANGE INPUTS ======================= */
input[type=range] {
  -webkit-appearance: none;
  height: 5px;
  background: var(--bg-4);
  border-radius: 6px;
  outline: none;
}

input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 
    0 0 0 3px rgba(0, 229, 160, 0.2),
    0 2px 10px rgba(0, 229, 160, 0.3);
  cursor: pointer;
}

input[type=range]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border: 0;
  cursor: pointer;
}

/* ======================= TOOLS DIVIDER ======================= */
.tools-divider {
  position: relative;
  width: 12px;
  min-width: 12px;
  height: 100%;
  background: var(--bg-1);
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  cursor: ew-resize;
  touch-action: none;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  contain: strict;
  isolation: isolate;
}

.tools-divider:hover {
  background: var(--bg-2);
}

.divider-handle {
  width: 4px;
  height: 60px;
  background: linear-gradient(180deg, var(--accent), var(--secondary));
  border-radius: 3px;
  opacity: 0.5;
}

.tools-divider:hover .divider-handle,
.tools-divider.dragging .divider-handle {
  opacity: 1;
  box-shadow: 0 0 20px var(--accent-glow);
}

.tools-divider.dragging {
  background: var(--bg-3);
}

/* Tools collapse button */
.tools-collapse-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 10;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  transition: all 0.2s ease;
}

.tools-collapse-btn svg {
  width: 14px;
  height: 14px;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.tools-collapse-btn:hover {
  background: var(--bg-4);
  color: var(--accent);
  border-color: var(--accent);
}

.tools.collapsed .tools-collapse-btn svg {
  transform: rotate(180deg);
}

/* Collapsed tools state */
.tools.collapsed {
  --tools-size: 48px !important;
  width: 48px;
  min-width: 48px;
  padding: 8px 6px;
  padding-top: 44px;
}

.tools.collapsed .tool-section {
  flex-direction: column;
  align-items: center;
}

.tools.collapsed .section-title {
  display: none;
}

.tools.collapsed .tool-grid {
  flex-direction: column;
  gap: 4px;
}

.tools.collapsed .tool {
  width: 36px;
  min-width: 36px;
  height: 36px;
  padding: 6px;
  justify-content: center;
}

.tools.collapsed .tool span {
  display: none;
}

.tools.collapsed .tool svg {
  width: 18px;
  height: 18px;
}

.tools.collapsed .preset {
  width: 36px;
  min-width: 36px;
  padding: 6px;
  font-size: 8px;
  text-overflow: ellipsis;
  overflow: hidden;
}

.tools.collapsed .check {
  width: 36px;
  min-width: 36px;
  padding: 6px;
  justify-content: center;
  font-size: 0;
}

.tools.collapsed .check input {
  margin: 0;
}

.tools.collapsed .slider-row {
  flex-direction: column;
  padding: 4px 0;
}

.tools.collapsed .slider-row span {
  display: none;
}

.tools.collapsed .slider-row input {
  width: 32px;
  transform: rotate(-90deg);
  transform-origin: center;
}

/* ======================= CARD COLLAPSE ======================= */
.card-title { cursor: pointer; user-select: none; }
.card-chevron {
  margin-left: 8px;
  font-size: 11px;
  color: var(--muted);
  transition: transform 200ms;
  display: inline-block;
}
.card.collapsed .card-chevron { transform: rotate(-90deg); }
.card.collapsed > :not(.card-title) { display: none; }
.card.collapsed { padding-bottom: 14px; }
.card.collapsed .card-title { margin-bottom: 0; }
.level-badge { margin-left: auto; }




/* ======================= LAYOUT ======================= */
.layout {
  flex: 1;
  position: relative;
  display: grid;
  grid-template-columns: var(--tools-size, 200px) auto 1fr;
  min-height: 0;
  contain: layout style;
  isolation: isolate;
}

/* Disable transitions during active dock-edge resize (body class from layout.js) */
body.resizing-dock,
body.resizing-dock * {
  transition: none !important;
}

/* ======================= ANTI-FLICKER RULES ======================= */
/* Freeze transitions on the layout-critical containers ONLY. The old rule
   also froze `.topbar *` (plus a dead `.stage` selector), which silently
   killed the entire polish layer (hover-lift / tap-scale, below) for every
   topbar control — and for the ⋯ menu, which lives inside .topbar.
   Resize-time flicker stays covered by `body.resizing-dock *` and
   `.panel.dragging`; the polish layer animates transform/filter/color
   only, so nothing here can trigger layout. */
.topbar,
#canvas {
  transition: none !important;
  animation: none !important;
}

/* Prevent any rendering glitches with GPU layers - but NOT on stage (breaks pointer coords) */
.topbar {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}


/* Tool/preset/material hover transitions are intentional micro-feedback;
   no suppression needed here since only .topbar and .stage are transition-frozen. */

/* Destruction mode toggle - special styling */
.destruction-toggle {
  background: var(--bg-2);
  border-color: var(--line);
}

.destruction-toggle:has(input:checked) {
  background: rgba(255, 80, 80, 0.15);
  border-color: rgba(255, 80, 80, 0.5);
  color: #ff6b6b;
}

.destruction-toggle input:checked + *::before,
.destruction-toggle:has(input:checked)::after {
  color: #ff6b6b;
}

/* Grab strength slider in physics section */
.grab-strength {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  background: var(--bg-2);
  border-radius: 6px;
}

.grab-strength span {
  font-size: 10px;
  color: var(--muted);
  min-width: 32px;
}

.grab-strength input[type=range] {
  flex: 1;
  accent-color: var(--accent);
}

/* ======================= TOOLS ======================= */
.tools {
  position: relative;
  background: linear-gradient(180deg, var(--bg-1), var(--bg));
  border-right: 1px solid var(--line);
  padding: 12px 10px;
  padding-top: 44px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  contain: layout style paint;
  isolation: isolate;
}

.tool-section {
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Tool grid container - wraps when narrow with smooth settling */
.tool-section .tool-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  transition: gap 0.2s ease;
}

/* Smooth color + border transitions on tool items (no layout props to avoid jank) */
.tool-grid > * {
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.section-title {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--muted);
  margin-bottom: 6px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}

.section-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line), transparent);
}

.tool {
  flex: 1 1 auto;
  min-width: 65px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 8px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
  position: relative;
  overflow: hidden;
}
/* PRO PAYWALL ------------------------------------------------------------- */
.pro-badge {
  display: none;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  color: #0b0d12;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.6px;
  padding: 2px 6px;
  border-radius: 999px;
  margin-left: 4px;
  vertical-align: middle;
}
.pro-badge.inline { display: inline-block; }
body.pro .pro-badge { display: inline-block; }
body.pro .btn.pro-btn { display: none; }
.locked::before {
  content: '🔒';
  font-size: 10px;
  margin-right: 4px;
  opacity: 0.7;
  vertical-align: baseline;
}
body.pro .locked::before { content: none; }

.upgrade-dialog {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  padding: 22px 24px;
  max-width: 420px;
  width: calc(100% - 32px);
  box-shadow: var(--shadow);
}
.upgrade-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }
/* Status banner shown when Pro is already active for the user — tells them
   which email is bound and offers a sign-out (deactivate on this device). */
.pro-status-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 12px;
  padding: 8px 12px;
  background: rgba(0, 229, 160, 0.08);
  border: 1px solid rgba(0, 229, 160, 0.3);
  border-radius: 8px;
  font-size: 13px;
  color: var(--text);
}
.pro-status-banner[hidden] { display: none; }
.pro-status-check { color: var(--accent); font-weight: 700; font-size: 16px; }
.pro-status-banner b { color: var(--accent-2); font-weight: 600; }
.pro-status-signout {
  margin-left: auto;
  padding: 4px 10px;
  font-size: 11px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--muted);
  cursor: pointer;
}
.pro-status-signout:hover { color: var(--text); border-color: var(--accent); }
.upgrade-dialog h2 {
  margin: 0 0 6px;
  font-size: 18px;
  color: var(--text);
}
.upgrade-sub {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 13px;
}
.upgrade-features {
  margin: 0 0 16px;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text);
}
.upgrade-features li { margin-bottom: 6px; }
.upgrade-features strong { color: var(--accent-2); }
.upgrade-price {
  font-size: 24px;
  font-weight: 600;
  color: var(--accent-2);
  margin: 0 0 14px;
}
.upgrade-price-period { font-size: 13px; color: var(--muted); font-weight: 400; margin-left: 4px; }
.upgrade-actions {
  display: flex;
  gap: 8px;
  margin-bottom: 14px;
}
.upgrade-cta {
  background: linear-gradient(180deg, rgba(106,166,255,0.28), rgba(106,166,255,0.10));
  border: 1px solid rgba(106,166,255,0.5);
  color: var(--accent-2);
  flex: 1;
  font-weight: 600;
}
.upgrade-cta:hover { background: linear-gradient(180deg, rgba(106,166,255,0.36), rgba(106,166,255,0.14)); }
.upgrade-actions .btn.ghost { flex: 0 0 auto; }
.upgrade-redeem {
  border-top: 1px solid var(--line);
  padding-top: 12px;
  font-size: 12px;
}
.upgrade-redeem summary {
  cursor: pointer;
  color: var(--muted);
  margin-bottom: 8px;
  user-select: none;
}
.upgrade-redeem summary:hover { color: var(--text); }
.redeem-row { display: flex; gap: 6px; }
.redeem-row input {
  flex: 1;
  background: var(--bg-3);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  font: inherit;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.redeem-msg { margin-top: 8px; font-size: 11px; min-height: 14px; }
.redeem-msg.ok { color: var(--good); }
.redeem-msg.err { color: var(--bad); }

.tool:hover {
  background: var(--bg-3);
  border-color: var(--accent);
}

.tool:active {
  transform: scale(0.97);
}

.tool.active {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.15), rgba(0, 255, 200, 0.08));
  border-color: rgba(0, 229, 160, 0.5);
  color: var(--accent-2);
  box-shadow: 
    0 0 20px rgba(0, 229, 160, 0.1),
    inset 0 0 0 1px rgba(0, 229, 160, 0.2);
}

.tool.active::after {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 60%;
  background: var(--accent);
  border-radius: 0 3px 3px 0;
}

.tool svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  position: relative;
  z-index: 1;
  transition: transform 0.2s;
}

.tool:hover svg {
  transform: scale(1.1);
}

.tool span {
  position: relative;
  z-index: 1;
}

.tool[data-tool=circle] svg,
.tool[data-tool=box] svg,
.tool[data-tool=polygon] svg,
.tool[data-tool=wall] svg,
.tool[data-tool=triangle] svg {
  fill: rgba(0, 229, 160, 0.15);
}

.preset {
  flex: 1 1 auto;
  min-width: 75px;
  text-align: center;
  background: transparent;
  border: 1px dashed var(--line);
  color: var(--muted);
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 500;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.preset:hover {
  color: var(--text);
  border-color: var(--accent);
  border-style: solid;
  background: rgba(0, 229, 160, 0.08);
}

/* Material buttons */
.material {
  padding: 5px 8px;
  font-size: 10px;
  background: var(--bg-2);
  border: 1px dashed var(--line);
  border-radius: 5px;
  color: var(--muted);
  cursor: pointer;
  transition: all 0.15s;
  font-weight: 500;
}

.material:hover {
  color: var(--text);
  border-color: var(--accent);
  border-style: solid;
  background: rgba(0, 229, 160, 0.08);
}

.material.active {
  background: rgba(0, 229, 160, 0.2);
  border-color: var(--accent);
  border-style: solid;
  color: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}

/* Material color indicators */
.material[data-material="rubber"] { border-left: 3px solid #ff6b9d; }
.material[data-material="ice"] { border-left: 3px solid #88ddff; }
.material[data-material="metal"] { border-left: 3px solid #9ba8c0; }
.material[data-material="wood"] { border-left: 3px solid #c49a6c; }
.material[data-material="bouncy"] { border-left: 3px solid #00e5a0; }
.material[data-material="heavy"] { border-left: 3px solid #5a5a6e; }
.material[data-material="light"] { border-left: 3px solid #ffeaa7; }

.section-hint {
  font-size: 9px;
  color: var(--muted);
  font-weight: 400;
  opacity: 0.7;
}

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--muted);
  padding: 5px 8px;
  background: var(--bg-2);
  border-radius: 6px;
  user-select: none;
  cursor: pointer;
  transition: all 0.15s;
  flex: 1 1 auto;
  min-width: fit-content;
}

.check:hover {
  color: var(--text);
  background: var(--bg-3);
}

.check input {
  accent-color: var(--accent);
  width: 14px;
  height: 14px;
}

.slider-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  color: var(--muted);
  padding: 6px 0;
}

.slider-row span {
  min-width: 56px;
  font-weight: 500;
}

.slider-row input[type=range] {
  flex: 1;
  accent-color: var(--accent);
}

/* ======================= STAGE ======================= */
.stage {
  position: relative;
  background: var(--bg);
  overflow: hidden;
  contain: layout style paint;
  isolation: isolate;
}

/* Removed stage gradient overlay to prevent compositing flicker */

#canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  will-change: contents;
  contain: strict;
  }

/* Default (desktop): show mouse hint, hide touch hint. Mobile @media flips these. */
.hint-touch { display: none; }
.hint-mouse { display: inline; }

.hint {
  position: absolute;
  left: 18px;
  bottom: 18px;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100% - 36px);
  background: rgba(15, 15, 22, 0.9);
  border: 1px solid var(--line);
  padding: 8px 10px 8px 14px;
  border-radius: 12px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
  backdrop-filter: blur(12px);
  transition: all 0.3s ease;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

/* Allow the hint text to shrink and wrap so the × stays inside the hint. */
.hint > span { min-width: 0; flex: 1 1 auto; }

.hint:hover {
  border-color: var(--accent);
  background: rgba(0, 229, 160, 0.05);
}

.hint.dismissed {
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
}

/* Bounds edit mode hides the spawn hint: it's irrelevant while resizing the
   boundary, and its wide bottom pill overlaps the floor open/close pip. Kept
   independent of .dismissed so a permanently-dismissed hint isn't resurrected. */
.hint.bounds-mode-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
}

.hint-close {
  background: var(--bg-3);
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 14px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  cursor: pointer;
  flex: 0 0 auto;
  transition: all 0.15s;
}

.hint-close:hover {
  background: var(--bad);
  border-color: var(--bad);
  color: var(--bg);
}

.hint kbd {
  background: var(--bg-3);
  border: 1px solid var(--line-2);
  padding: 2px 6px;
  border-radius: 5px;
  font-size: 10px;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 500;
}

/* ======================= PANEL ======================= */
.panel {
  position: relative;
  background: linear-gradient(180deg, var(--bg-1), var(--bg));
  border-left: 1px solid var(--line);
  padding: 16px;
  padding-top: 54px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  transition: all 0.2s ease;
}

.card:hover {
  border-color: var(--line-2);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}

.card.mini {
  padding: 12px 14px;
}

.card-title {
  display: flex;
  align-items: center;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--muted);
  margin-bottom: 12px;
  font-weight: 600;
}

.level-badge {
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.15), rgba(0, 255, 200, 0.1));
  color: var(--accent-2);
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: none;
  border: 1px solid rgba(0, 229, 160, 0.3);
}

.readout-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(85px, 1fr));
  gap: 8px;
}

.metric {
  background: var(--bg-3);
  border: 1px solid var(--line);
  padding: 8px 10px;
  border-radius: 8px;
  transition: all 0.2s ease;
}

.metric:hover {
  border-color: var(--accent);
  background: rgba(0, 229, 160, 0.05);
}

.metric-label {
  font-size: 9px;
  text-transform: uppercase;
  color: var(--muted);
  letter-spacing: 0.8px;
  font-weight: 600;
}

.metric-val {
  font-size: 16px;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--accent-2);
}

.metric-val .unit {
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
}

.totals {
  margin-top: 10px;
  border-top: 1px dashed var(--line);
  padding-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  font-size: 11px;
  color: var(--muted);
  }
  
.totals > div {
  flex: 1 1 auto;
  min-width: 80px;
  display: flex;
  justify-content: space-between;
  gap: 6px;
  }

.totals b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
  }

.lesson-body {
  margin: 0;
  font-size: 14px;
  line-height: 1.65;
  color: var(--text);
}

.formula {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-3);
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  color: var(--accent-2);
  white-space: pre-wrap;
  position: relative;
  overflow: hidden;
}

.formula::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--accent), var(--secondary));
}

/* KaTeX-typeset math — when KaTeX renders into .formula, drop the mono
   fallback styling and let the typeset output breathe. */
.formula:has(.katex) {
  white-space: normal;
  font-family: inherit;
  overflow-x: auto;
  overflow-y: hidden;
}
.formula .katex { font-size: 1.15em; }
.formula .katex-display { margin: 2px 0; }

/* Derivation block — University/Expert multi-step derivations. */
.derivation {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-2);
  position: relative;
  overflow: hidden;
}
.derivation::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--secondary), var(--accent));
}
.derivation-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 4px;
}
.derivation-note {
  margin: 8px 0 2px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text);
}
.derivation-math {
  margin: 4px 0;
  color: var(--accent-2);
  overflow-x: auto;
  overflow-y: hidden;
}
.derivation-math .katex { font-size: 1.05em; }
.derivation-math .katex-display { margin: 2px 0; }

.lesson-tags {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.lesson-tag {
  font-size: 10px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  color: var(--muted);
  font-weight: 500;
  transition: all 0.15s;
}

.lesson-tag:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Live gauge variant — momentum/energy tags read out the selected body
   (system totals when nothing is selected); app.js drives value + fill. */
.lesson-tag.lesson-gauge {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  overflow: hidden;
}
.lesson-gauge .gauge-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 0%;
  background: linear-gradient(90deg, rgba(0, 229, 160, 0.10), rgba(0, 229, 160, 0.30));
  transition: width 120ms linear;
  pointer-events: none;
}
.lesson-gauge .gauge-label { position: relative; }
.lesson-gauge .gauge-val {
  position: relative;
  color: var(--text);
  font-weight: 600;
  font-feature-settings: "tnum";
}

.concept-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.concept-pill {
  font-size: 11px;
  padding: 5px 12px;
  border-radius: 999px;
  background: linear-gradient(135deg, rgba(0, 229, 160, 0.12), rgba(0, 255, 200, 0.08));
  border: 1px solid rgba(0, 229, 160, 0.3);
  color: var(--accent-2);
  font-weight: 500;
  transition: all 0.2s ease;
}

.concept-pill:hover {
  background: rgba(0, 229, 160, 0.2);
  transform: scale(1.05);
}

.concept-pill.empty {
  background: transparent;
  border-style: dashed;
  color: var(--muted);
  border-color: var(--line);
}

/* ======================= HELP DIALOG ======================= */
.help {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  color: var(--text);
  padding: 24px 28px;
  max-width: 450px;
  width: calc(100% - 32px);
  box-sizing: border-box;
  box-shadow: var(--shadow), var(--shadow-glow);
}

.help::backdrop {
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
}

.help h2 {
  margin: 0 0 14px;
  font-size: 18px;
  font-weight: 600;
  background: linear-gradient(135deg, var(--text), var(--accent-2));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.help ul {
  margin: 0;
  padding-left: 20px;
  line-height: 1.8;
  font-size: 13px;
}

.help kbd {
  background: var(--bg-3);
  border: 1px solid var(--line-2);
  padding: 2px 6px;
  border-radius: 5px;
  font-size: 11px;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 500;
}

.help .btn {
  margin-top: 16px;
  width: 100%;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border: none;
  color: var(--bg);
  font-weight: 600;
  padding: 10px 16px;
}

.help .btn:hover {
  box-shadow: 0 4px 20px var(--accent-glow);
}

/* ======================= SCROLLBARS ======================= */
.tools::-webkit-scrollbar,
.panel::-webkit-scrollbar {
  width: 8px;
}

.tools::-webkit-scrollbar-track,
.panel::-webkit-scrollbar-track {
  background: transparent;
}

.tools::-webkit-scrollbar-thumb,
.panel::-webkit-scrollbar-thumb {
  background: var(--bg-4);
  border-radius: 8px;
}

.tools::-webkit-scrollbar-thumb:hover,
.panel::-webkit-scrollbar-thumb:hover {
  background: var(--line);
}

/* ======================= ANIMATIONS ======================= */
/* float-pop-in / float-pop-out defined in the desktop @media block below. */

/* =====================================================================
   LAYOUT REWORK 2026-05-08 — bottom-tab/sheet (mobile) + derived
   sidebars (desktop). Single breakpoint at 768px.
   ===================================================================== */

/* --- Hide legacy resize/collapse affordances (removed in rework). --- */
.topbar-divider,
.tools-divider,
.tools-collapse-btn,
.floating-overlay {
  display: none !important;
}

/* --- Body becomes the layout grid. The `body { display:flex; ... }`
   from the top of this file is overridden here. --- */
body {
  display: grid;
  flex-direction: unset;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr;
  grid-template-areas:
    "topbar"
    "main";
  height: 100vh;
  width: 100vw;
}

.topbar { grid-area: topbar; }

.canvas-host {
  position: relative;
  grid-area: main;
  background: var(--bg);
  overflow: hidden;
  isolation: isolate;
  min-width: 0;
  min-height: 0;
}

.canvas-host #canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

.panel-host {
  display: contents;
}

.panel {
  position: relative;
  background:
    linear-gradient(180deg, rgba(15, 15, 22, 0.62), rgba(10, 10, 15, 0.56));
  -webkit-backdrop-filter: blur(18px) saturate(1.35);
  backdrop-filter: blur(18px) saturate(1.35);
  border-left: 0;
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0;
  isolation: isolate;
  min-width: 0;
  min-height: 0;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 0 0 1px rgba(255, 255, 255, 0.02);
}
.panel.floating {
  box-shadow:
    0 24px 64px -12px rgba(0, 0, 0, 0.65),
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    inset 0 0 0 1px rgba(0, 229, 160, 0.06);
}

.panel-grabber {
  flex: 0 0 auto;
  height: 14px;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: grab;
  touch-action: none;
}
.panel-grabber::before {
  content: '';
  width: 42px;
  height: 4px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  opacity: 0.5;
}
.panel.dragging .panel-grabber::before { opacity: 1; }
.panel.dragging { transition: none !important; }

.panel-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px 8px;
  border-bottom: 1px solid var(--line);
}

/* Hidden by default; shown as a close affordance on mobile. */
.panel-close { display: none; }
/* Overlay close buttons hidden on desktop — panel-pop serves that role. */
.overlay-close { display: none; }
.panel-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent);
  font-weight: 600;
}
.panel-header .level-badge { margin-left: auto; }

.panel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
}

#toolsPanel .panel-body { gap: 16px; }
#toolsPanel .tool-section { display: flex; flex-direction: column; gap: 6px; margin: 0; }

#infoOverlay .panel-body { gap: 10px; }
#infoOverlay .readout-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
}
#infoOverlay .totals-mini {
  display: flex; flex-wrap: wrap; gap: 6px 16px; padding-top: 8px;
  border-top: 1px dashed var(--line);
  font-size: 12px; color: var(--muted);
}
#infoOverlay .totals-mini b { color: var(--accent-2); font-weight: 600; }

#lessonOverlay .lesson-body { font-size: 14px; line-height: 1.6; color: var(--text); margin: 0; }
#lessonOverlay .formula {
  background: var(--bg-3); padding: 10px 12px; border-radius: 8px;
  margin: 0;
  font-family: "JetBrains Mono", monospace;
  font-size: 12px; color: var(--accent-2);
  border-left: 3px solid var(--accent);
}
#lessonOverlay .lesson-tags { display: flex; flex-wrap: wrap; gap: 5px; }
#lessonOverlay .concepts-touched {
  margin-top: 4px; padding-top: 10px;
  border-top: 1px dashed var(--line);
}
#lessonOverlay .concepts-title {
  font-size: 9px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); margin-bottom: 8px; font-weight: 600;
}
#lessonOverlay .concept-list { display: flex; flex-wrap: wrap; gap: 5px; }

.panel.flash { box-shadow: 0 0 0 2px var(--accent), 0 0 24px var(--accent-glow); }

/* ⋯-menu "Tools" toggle is mobile-only — Tools is always docked on desktop. */
.mobile-only-btn { display: none; }

/* ======================= TABBAR (retired — see ⋯ menu) =======================
   Tools/Readings/Lesson now live inside the .topbar-overflow dropdown. Tabbar
   rules kept (display:none base) so the design can be revived later. */
.tabbar {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 30;
  height: 60px;
  padding: 6px max(8px, env(safe-area-inset-left)) max(6px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-right));
  background: rgba(10, 10, 15, 0.94);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-top: 1px solid var(--line);
}

.tabbar .tab {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: transparent;
  border: 0;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  padding: 4px 6px;
  border-radius: 10px;
  transition: color 0.15s, background 0.15s;
  min-height: 48px;
  white-space: nowrap;
}
.tabbar .tab svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.tabbar .tab.active {
  color: var(--accent-2);
  background: rgba(0, 229, 160, 0.12);
}
.tabbar .tab:active { transform: scale(0.96); }

/* ===================== DESKTOP (>=768px) ===================== */
@media (min-width: 769px) {
  :root {
    --tools-w: 280px;
    --readings-w: 320px;
    --educator-h: 180px;
  }
  body {
    grid-template-columns: var(--tools-w) 1fr var(--readings-w);
    grid-template-rows: auto 1fr var(--educator-h);
    grid-template-areas:
      "topbar topbar topbar"
      "tools  main   readings"
      "tools  educator educator";
  }
  /* When Readings is floating, collapse the right column so canvas + educator
     expand into the freed space. */
  body[data-readings-floating="true"] {
    grid-template-columns: var(--tools-w) 1fr 0;
    grid-template-areas:
      "topbar topbar topbar"
      "tools  main   main"
      "tools  educator educator";
  }
  /* When Educator is floating, collapse the bottom row. */
  body[data-educator-floating="true"] {
    grid-template-rows: auto 1fr 0;
    grid-template-areas:
      "topbar topbar topbar"
      "tools  main   readings"
      "tools  main   readings";
  }
  /* Both floating: full-bleed canvas + tools column only. */
  body[data-readings-floating="true"][data-educator-floating="true"] {
    grid-template-columns: var(--tools-w) 1fr 0;
    grid-template-rows: auto 1fr 0;
    grid-template-areas:
      "topbar topbar topbar"
      "tools  main   main"
      "tools  main   main";
  }
  .canvas-host { grid-area: main; }
  #toolsPanel { grid-area: tools; border-right: 1px solid var(--line); }
  #infoOverlay { grid-area: readings; border-left: 1px solid var(--line); }
  #lessonOverlay {
    grid-area: educator;
    border-top: 1px solid var(--line);
  }
  /* Hide docked panels' grid-area presence when they're floating
     (the grid still allocates 0 for the row/column thanks to the body data attrs). */
  body[data-readings-floating="true"] #infoOverlay:not(.floating) { display: none; }
  body[data-educator-floating="true"] #lessonOverlay:not(.floating) { display: none; }

  /* The ● HUD view-cycler is mobile-only (styled + wired in the mobile
     block / layout.js `if (mobile)`). On desktop it's inert and unstyled,
     so the UA default button rendered as a blank full-width pill above the
     Readings panel header. */
  .hud-view-toggle { display: none; }

  /* Dock-edge resize handles. Placed absolutely so they sit ON the boundary
     between dock + canvas without disturbing the grid math. JS injects three
     of these into <body> on desktop and updates them on breakpoint change. */
  .dock-edge {
    position: fixed;
    z-index: 12;
    background: transparent;
    transition: background 120ms ease;
  }
  .dock-edge::before {
    content: '';
    position: absolute;
    background: var(--line-2);
    transition: background 120ms ease, transform 120ms ease;
  }
  .dock-edge:hover::before,
  .dock-edge.dragging::before {
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-glow);
  }
  .dock-edge[data-edge="tools-edge"],
  .dock-edge[data-edge="readings-edge"] {
    width: 8px;
    cursor: col-resize;
    top: 0;
    bottom: 0;
  }
  .dock-edge[data-edge="tools-edge"]::before,
  .dock-edge[data-edge="readings-edge"]::before {
    left: 50%;
    transform: translateX(-50%);
    top: 0;
    bottom: 0;
    width: 1px;
  }
  .dock-edge[data-edge="tools-edge"]:hover::before,
  .dock-edge[data-edge="tools-edge"].dragging::before,
  .dock-edge[data-edge="readings-edge"]:hover::before,
  .dock-edge[data-edge="readings-edge"].dragging::before {
    width: 2px;
  }
  .dock-edge[data-edge="educator-edge"] {
    height: 8px;
    cursor: row-resize;
    left: 0;
    right: 0;
  }
  .dock-edge[data-edge="educator-edge"]::before {
    top: 50%;
    transform: translateY(-50%);
    left: 0;
    right: 0;
    height: 1px;
  }
  .dock-edge[data-edge="educator-edge"]:hover::before,
  .dock-edge[data-edge="educator-edge"].dragging::before {
    height: 2px;
  }
  /* Hide handles whose adjacent panel is floating (no boundary to drag). */
  body[data-readings-floating="true"] .dock-edge[data-edge="readings-edge"],
  body[data-educator-floating="true"] .dock-edge[data-edge="educator-edge"] {
    display: none;
  }

  /* Floating panels — absolute inside .canvas-host so they're clipped to the
     canvas region (does not bleed under topbar or into the tools column). */
  .canvas-host .panel.floating {
    position: absolute;
    z-index: 30;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.07);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.50), 0 2px 8px rgba(0, 0, 0, 0.30);
    background: rgba(8, 12, 20, 0.82);
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* Width/height/top/left set inline by layout.js. */
    animation: float-pop-in 220ms cubic-bezier(0.32, 0.72, 0, 1);
    transform-origin: center center;
  }
  .canvas-host .panel.floating.dock-exit {
    animation: float-pop-out 180ms cubic-bezier(0.32, 0.72, 0, 1) forwards;
    pointer-events: none;
  }
  @keyframes float-pop-in {
    from { opacity: 0; transform: scale(0.92); }
    to   { opacity: 1; transform: scale(1); }
  }
  @keyframes float-pop-out {
    from { opacity: 1; transform: scale(1); }
    to   { opacity: 0; transform: scale(0.94); }
  }
  /* Smooth dock collapse / expand when a panel pops out or back in.
     Animating grid-template-* requires concrete numeric tracks, which we have
     (var --tools-w, --readings-w, --educator-h are all px values). */
  body {
    transition: grid-template-columns 240ms cubic-bezier(0.32, 0.72, 0, 1),
                grid-template-rows 240ms cubic-bezier(0.32, 0.72, 0, 1);
  }
  /* Skip the body-grid transition while user is actively dragging a dock-edge
     handle — those drags should track the cursor 1:1, not lag behind. */
  body.resizing-dock { transition: none; }
  .canvas-host .panel.floating.active { z-index: 31; }
  .canvas-host .panel.floating .panel-header {
    cursor: grab;
    user-select: none;
    background: rgba(255, 255, 255, 0.04);
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  }
  /* When user is actively dragging or resizing, boost opacity slightly so the
     panel boundary is extra crisp while moving. */
  .canvas-host .panel.floating.dragging,
  .canvas-host .panel.floating.resizing {
    background: rgba(8, 12, 20, 0.92);
  }
  .canvas-host .panel.floating.dragging .panel-header { cursor: grabbing; }
  .canvas-host .panel.floating .resize-corner {
    display: block;
    position: absolute;
    right: 0;
    bottom: 0;
    width: 14px;
    height: 14px;
    cursor: nwse-resize;
    z-index: 2;
    background:
      linear-gradient(135deg, transparent 0 50%, var(--line-2) 50% 60%, transparent 60% 70%, var(--line-2) 70% 80%, transparent 80%);
  }
  .canvas-host .panel.floating .resize-edge {
    display: block;
    position: absolute;
    z-index: 1;
    background: transparent;
  }
  .canvas-host .panel.floating .resize-edge[data-edge="n"] { left: 0; right: 14px; top: 0; height: 5px; cursor: ns-resize; }
  .canvas-host .panel.floating .resize-edge[data-edge="s"] { left: 0; right: 14px; bottom: 0; height: 5px; cursor: ns-resize; }
  .canvas-host .panel.floating .resize-edge[data-edge="e"] { top: 0; bottom: 14px; right: 0; width: 5px; cursor: ew-resize; }
  .canvas-host .panel.floating .resize-edge[data-edge="w"] { top: 0; bottom: 0; left: 0; width: 5px; cursor: ew-resize; }
  /* ===========================================================
     READINGS — ambient HUD overlay (MSI Afterburner / net_graph vibe).
     The floating readings card recedes into a single translucent strip:
     low-alpha black, light blur, hairline border, compact type, 75%
     opacity that lifts to full on hover/drag/resize. Default rect
     (top-right, 290×232) lives in layout.js DEFAULTS.readingsRect.
     =========================================================== */
  .canvas-host #infoOverlay.floating {
    z-index: 8; /* ambient — sits under the educator card (30) by default */
    background: rgba(6, 8, 12, 0.18);
    backdrop-filter: blur(6px) saturate(1.15);
    -webkit-backdrop-filter: blur(6px) saturate(1.15);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 8px;
    box-shadow: none;
    opacity: 0.75;
    transition: opacity 160ms ease;
  }
  .canvas-host #infoOverlay.floating:hover,
  .canvas-host #infoOverlay.floating.dragging,
  .canvas-host #infoOverlay.floating.resizing {
    opacity: 1;
  }
  /* Click/drag/resize still lifts it above other floats so it never gets
     stuck under the educator card while being moved. */
  .canvas-host #infoOverlay.floating.active,
  .canvas-host #infoOverlay.floating.dragging,
  .canvas-host #infoOverlay.floating.resizing {
    z-index: 31;
  }
  .canvas-host #infoOverlay.floating.dragging,
  .canvas-host #infoOverlay.floating.resizing {
    background: rgba(6, 8, 12, 0.40);
  }
  .canvas-host #infoOverlay.floating .panel-header {
    padding: 5px 10px 4px;
    gap: 8px;
    background: transparent;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  }
  .canvas-host #infoOverlay.floating .panel-title {
    font-size: 9px;
    letter-spacing: 1.2px;
    color: rgba(0, 229, 160, 0.6); /* passive label, not a CTA */
  }
  /* Close (✕) — bare glyph, no chip chrome. */
  .canvas-host #infoOverlay.floating .panel-pop {
    background: transparent;
    border: 0;
    width: auto;
    height: auto;
    padding: 2px;
    border-radius: 4px;
    color: rgba(225, 228, 240, 0.9);
    opacity: 0.5;
    transition: opacity 120ms ease;
  }
  .canvas-host #infoOverlay.floating .panel-pop:hover {
    background: transparent;
    border: 0;
    color: rgba(225, 228, 240, 1);
    opacity: 1;
    transform: none;
  }
  .canvas-host #infoOverlay.floating .panel-header button svg { width: 14px; height: 14px; }
  .canvas-host #infoOverlay.floating .panel-body {
    padding: 7px 10px 8px;
    gap: 5px;
  }
  .canvas-host #infoOverlay.floating .readout-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 2px 12px;
  }
  /* Cells lose their card chrome — bare label+value stacks. */
  .canvas-host #infoOverlay.floating .metric {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 2px 0;
  }
  .canvas-host #infoOverlay.floating .metric:hover {
    background: transparent;
    border-color: transparent;
    transform: none;
  }
  .canvas-host #infoOverlay.floating .metric-label {
    font-size: 9px;
    letter-spacing: 1px;
    color: rgba(170, 175, 195, 0.55);
  }
  .canvas-host #infoOverlay.floating .metric-val {
    font-size: 13px;
    margin-top: 1px;
    color: rgba(0, 255, 200, 0.85);
  }
  .canvas-host #infoOverlay.floating .metric-val .unit {
    font-size: 9px;
    color: rgba(136, 136, 160, 0.7);
  }
  .canvas-host #infoOverlay.floating .totals-mini {
    padding-top: 5px;
    border-top: 1px dashed rgba(255, 255, 255, 0.08);
    font-size: 10px;
    color: rgba(170, 175, 195, 0.6);
  }
  .canvas-host #infoOverlay.floating .hud-empty {
    font-size: 11px;
    color: rgba(225, 228, 240, 0.92); /* the CTA — brightest line in empty state */
    padding: 0 0 2px;
  }
  /* Empty state collapses to instruction + footer — the float machinery's
     inline height would otherwise leave a blank slab where the grid was.
     :not(.resizing) keeps the s-edge drag honest while nothing is selected. */
  .canvas-host #infoOverlay.floating[data-no-selection="true"]:not(.resizing) {
    height: auto !important;
  }
  .canvas-host #infoOverlay.floating[data-no-selection="true"] .totals-mini {
    opacity: 0.5; /* readings dim, instruction leads */
  }

  /* Educator floating overrides the desktop row-mode panel-body (which is
     flex-row + horizontal scroll). Reset to vertical for compact float. */
  .canvas-host #lessonOverlay.floating .panel-body {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    gap: 10px;
  }
  .canvas-host #lessonOverlay.floating .panel-body > * { max-width: none; }
  .canvas-host #lessonOverlay.floating .concepts-touched {
    border-top: 1px dashed var(--line);
    padding-top: 10px;
    padding-left: 0;
    border-left: 0;
    margin-top: 4px;
  }

  /* Dock-zone highlight — appears along the panel's native dock edge while the
     user drags the floating panel close to it. Two variants for the two panels. */
  .dock-zone {
    position: absolute;
    background: var(--accent);
    box-shadow: 0 0 16px var(--accent-glow);
    z-index: 29;
    pointer-events: none;
    opacity: 0;
    transition: opacity 120ms ease;
  }
  .dock-zone.active { opacity: 1; }
  .dock-zone[data-zone="readings"] {
    top: 0; bottom: 0; right: 0;
    width: 4px;
  }
  .dock-zone[data-zone="educator"] {
    left: 0; right: 0; bottom: 0;
    height: 4px;
  }

  /* Pop-out / dock button in panel headers. */
  .panel-pop {
    margin-left: auto;
    background: rgba(0, 229, 160, 0.10);
    border: 1px solid var(--accent);
    color: var(--accent-2);
    width: 32px;
    height: 26px;
    padding: 0;
    border-radius: 6px;
    font-size: 15px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color 120ms ease, border-color 120ms ease, background 120ms ease, transform 120ms ease;
  }
  .panel-pop:hover {
    color: var(--bg);
    border-color: var(--accent);
    background: var(--accent);
    transform: scale(1.08);
  }
  .panel-pop:active { transform: scale(0.96); }

  /* Topbar overlay toggle buttons */
  .panel-vis-btn {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.4px;
    opacity: 0.5;
    transition: opacity 120ms ease, color 120ms ease, background 120ms ease, border-color 120ms ease;
  }
  .panel-vis-btn.active {
    opacity: 1;
    border-color: var(--accent);
    color: var(--accent-2);
    background: rgba(0, 229, 160, 0.10);
  }
  /* Hidden overlay panels */
  .panel-overlay-hidden { display: none !important; }

  /* When a level-badge is present (educator), keep it before the pop button. */
  .panel-header .level-badge + .panel-pop { margin-left: 6px; }
  /* The first-of-class margin-left rule above pushes the badge right; reset
     so badge stays adjacent to title and only the pop button is far-right. */
  .panel-header .level-badge { margin-left: auto; }
  #lessonOverlay .panel-body {
    flex-direction: row;
    align-items: stretch;
    gap: 18px;
    overflow-x: scroll;
    background: linear-gradient(to right, transparent 85%, rgba(0,0,0,0.12) 100%);
    background-attachment: local;
  }
  #lessonOverlay .panel-body > * { flex: 0 0 auto; max-width: 360px; }
  #lessonOverlay .lesson-body { max-width: 480px; flex: 1 1 320px; }
  #lessonOverlay .concepts-touched {
    flex: 1 1 280px;
    min-width: 200px;
    max-width: none;
    border-top: 0;
    padding-top: 0;
    padding-left: 14px;
    border-left: 1px dashed var(--line);
    margin-top: 0;
  }

  .tabbar { display: none; }

  /* =========================================================
     COLLAPSE BUTTONS — desktop only
     ========================================================= */
  .panel-collapse {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 28px;
    height: 22px;
    padding: 0;
    background: rgba(0, 229, 160, 0.07);
    border: 1px solid rgba(0, 229, 160, 0.22);
    color: var(--accent-2);
    border-radius: 5px;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease, transform 120ms ease;
  }
  .panel-collapse:hover {
    background: var(--accent);
    color: var(--bg);
    border-color: var(--accent);
  }
  .panel-collapse:active { transform: scale(0.96); }

  #toolsPanel .panel-collapse { margin-left: auto; }
  #infoOverlay .panel-collapse,
  #lessonOverlay .panel-collapse { margin-left: 4px; }

  /* Floating panels don't use the dock-collapse affordance */
  .canvas-host .panel.floating .panel-collapse { display: none; }

  /* =========================================================
     COLLAPSED DOCK STATES
     ========================================================= */
  .panel.dock-collapsed .panel-body,
  .panel.dock-collapsed .panel-grabber { display: none; }

  .panel.dock-collapsed .panel-pop,
  .panel.dock-collapsed .panel-close,
  .panel.dock-collapsed .overlay-close,
  .panel.dock-collapsed .level-badge { display: none; }

  /* Sidebar strips (tools + readings): vertical layout */
  #toolsPanel.dock-collapsed .panel-header,
  #infoOverlay.dock-collapsed .panel-header {
    flex-direction: column;
    height: 100%;
    align-items: center;
    justify-content: flex-start;
    padding: 12px 4px 10px;
    gap: 10px;
    border-bottom: none;
    overflow: hidden;
  }

  #toolsPanel.dock-collapsed .panel-title,
  #infoOverlay.dock-collapsed .panel-title {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: 9px;
    letter-spacing: 1.5px;
    overflow: hidden;
    max-height: 140px;
    flex-shrink: 1;
  }

  #toolsPanel.dock-collapsed .panel-collapse,
  #infoOverlay.dock-collapsed .panel-collapse { margin: auto 0 0 0; }

  /* Educator bottom strip */
  #lessonOverlay.dock-collapsed .panel-header {
    height: 100%;
    padding: 0 14px;
    border-bottom: none;
    align-items: center;
  }
  #lessonOverlay.dock-collapsed .panel-collapse { margin-left: auto; }

  /* Hide dock-edge handles whose panel is collapsed */
  body[data-tools-collapsed="true"] .dock-edge[data-edge="tools-edge"],
  body[data-readings-collapsed="true"] .dock-edge[data-edge="readings-edge"],
  body[data-educator-collapsed="true"] .dock-edge[data-edge="educator-edge"] { display: none; }
}


/* ===================== MOBILE (<=768px) ===================== */
@media (max-width: 768px) {
  /* === Body: flex column. Three regions stacked. === */
  body {
    display: flex !important;
    flex-direction: column !important;
    grid-template-rows: none !important;
    grid-template-columns: none !important;
    grid-template-areas: none !important;
    height: 100vh;
    height: 100svh;
    height: 100dvh;
    padding-bottom: env(safe-area-inset-bottom);
    overflow: hidden;
  }

  /* === Topbar: CSS Grid with explicit columns so wrap is impossible.
     Columns: [atom] [chips:1fr] [pause] [Pro]. Hidden items take 0 space. */
  .topbar {
    order: 0;
    flex: 0 0 auto;
    display: grid !important;
    grid-template-columns: auto minmax(0, 1fr) auto auto !important;
    align-items: center;
    gap: 6px 8px;
    padding: 6px 10px;
    padding-top: max(6px, env(safe-area-inset-top));
    contain: none !important;
  }
  /* Explicit column assignment so no surprise auto-placement. */
  .topbar .brand { grid-column: 1; }
  .topbar aside.panel-host { grid-column: 2; min-width: 0; }
  .topbar .bar-group.btns { grid-column: 3; }
  .topbar .btn.pro-btn { grid-column: 4; }
  /* Anything else (mode/gravity/time/level segments, overflow menu, FAB)
     auto-flows or has its own positioning — none of them are visible inline. */
  .topbar .seg { padding: 6px 10px; font-size: 11px; }
  .topbar .btn { padding: 7px 12px; font-size: 12px; min-height: 36px; }
  .topbar .bar-group { flex-shrink: 0; flex-wrap: wrap; }
  .topbar .bar-group.btns { margin-left: 0; }
  .topbar .seg-label,
  .topbar .brand .title,
  .topbar > .bar-group:has(#modeSegment),
  .topbar > .bar-group:has(#levelSegment) { display: none; }
  /* Quick-control band → own full-width second row (the 4-col row 1 has no
     room for 6 more controls at 390px). Centered, compact, no horizontal
     overflow. */
  .topbar #quickControls {
    grid-column: 1 / -1;
    grid-row: 2;            /* explicit row 2 — keep [brand|chips|btns|pro] on row 1 */
    display: flex;
    justify-content: center;
    flex-wrap: nowrap;
    gap: 8px;
    margin: 0;
    padding-top: 2px;
  }
  .topbar #quickControls .qc-btn { padding: 6px 8px; min-height: 32px; }
  .topbar #quickControls .qc-speed .seg { padding: 4px 7px; font-size: 11px; }
  .topbar #quickControls .qc-sep { height: 20px; }
  /* Keep pause button visible but compact; level segment hidden (use Educator
     chip's level-badge to track current level instead). Order it AFTER the
     chip strip so the row reads [atom] [chips] [pause] [Pro]. */
  .topbar .bar-group.btns {
    display: flex !important;
    flex: 0 0 auto;
    padding: 0;
    gap: 4px;
    margin-left: 4px;
    order: 50;
    align-items: center;
  }
  /* Explicit visibility — defensive against any cascade that might hide it. */
  .topbar #pauseBtn {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    min-height: 32px;
    padding: 4px 10px;
    font-size: 14px;
  }
  /* Quick reset + Recenter ride the same btns group as Pause — explicit
     sizing parity (same defensive pattern as #pauseBtn above). Recenter is
     relocated here from the Tools camera section by layout.js on mobile,
     where the chip strip buried it two taps deep; desktop keeps it under
     the camera segment. */
  .topbar #quickResetBtn,
  .topbar .recenter-btn {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    min-height: 32px;
    padding: 4px 10px;
  }
  .topbar #quickResetBtn.confirm { font-size: 11px; }
  /* Icon-only — font-size 0 swallows the "Recenter" text node, svg keeps
     its explicit size. Kills the Tools-dock width/margin too. */
  .topbar .recenter-btn { width: auto; margin: 0; font-size: 0; gap: 0; }
  .topbar .recenter-btn svg { width: 15px; height: 15px; }
  /* Lock topbar to a single row so Pro can't wrap on a narrow viewport. */
  .topbar { flex-wrap: nowrap !important; }

  /* Educator chip — collapsed shows static "LESSON" instead of the dynamic
     lesson title + level badge (which together overflow the chip width).
     Both reappear when the chip is expanded into an overlay. */
  #lessonOverlay:not(.panel-expanded) .panel-title,
  #lessonOverlay:not(.panel-expanded) .level-badge {
    display: none !important;
  }
  #lessonOverlay:not(.panel-expanded) .panel-header::before {
    content: "LESSON";
    font-size: 11px;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    font-weight: 700;
  }
  /* Ghost-button pause glyph — force the pause/play character to inherit
     the button's text color instead of rendering as the default colored
     emoji. font-variant-emoji is the proper solution on Chrome 120+. */
  .topbar #pauseBtn {
    font-family: inherit;
    color: var(--text);
    font-variant-emoji: text;
    letter-spacing: -1px;
  }
  /* Pro button stays visible on mobile so a paid customer has a clear way
     to open "Already paid? Enter your email". Hide in free/embed since
     those modes intentionally render the app as a non-paid surface. */
  .topbar .btn.pro-btn {
    display: inline-flex;
    order: 99;
    margin-left: auto;
  }
  body.free-mode .topbar .btn.pro-btn,
  body.embed-mode .topbar .btn.pro-btn { display: none !important; }
  .topbar .brand { order: -2; }
  .topbar .bar-group:has(#levelSegment) { order: -1; }

  /* === Panel strip: layout.js appends panel-host into the topbar on mobile,
     so chips flow inline with the atom logo and the Pro button. No more
     second row taking up vertical space. === */
  aside.panel-host {
    flex: 1 1 auto !important;
    min-width: 0 !important;
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    gap: 6px;
    padding: 0 6px;
    height: auto;
    overflow-x: auto !important;
    overflow-y: visible !important;
    background: transparent !important;
    border: 0 !important;
    z-index: auto;
    position: relative !important;
    pointer-events: auto !important;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    order: 1;
  }
  aside.panel-host::-webkit-scrollbar { display: none; }

  /* === Canvas: takes remaining space, never resized by panels. === */
  .canvas-host {
    order: 2;
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
  }

  /* === Each panel: chip in the strip by default. === */
  #toolsPanel,
  #infoOverlay,
  #lessonOverlay {
    flex: 0 0 auto !important;
    display: flex !important;
    flex-direction: column !important;
    visibility: visible !important;
    opacity: 1 !important;
    position: relative !important;
    inset: auto !important;
    transform: none !important;
    transition: none !important;
    width: auto !important;
    min-width: 96px !important;
    flex-basis: auto !important;
    flex-shrink: 0 !important;
    height: 36px !important;
    max-height: 36px !important;
    pointer-events: auto !important;
    container-type: inline-size !important;
    background: rgba(14, 16, 24, 0.94);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 8px;
    box-shadow: none;
    z-index: auto;
    overflow: hidden;
  }
  /* Dynamic titles (educator carries the active concept name like "Newton's
     Laws of Motion") truncate with ellipsis instead of widening the chip
     past the visible viewport. */
  #toolsPanel:not(.panel-expanded) .panel-title,
  #infoOverlay:not(.panel-expanded) .panel-title,
  #lessonOverlay:not(.panel-expanded) .panel-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    max-width: 100%;
  }

  /* === Expanded panel: draggable, translucent floating overlay. === */
  #toolsPanel.panel-expanded,
  #lessonOverlay.panel-expanded {
    position: fixed !important;
    top: calc(env(safe-area-inset-top) + 110px) !important;
    left: 8px !important;
    right: auto !important;
    width: min(280px, calc(100vw - 16px)) !important;
    min-width: 0 !important;
    height: auto !important;
    max-height: 56vh !important;
    z-index: 100;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 10px;
    background: rgba(14, 16, 24, 0.72);
    backdrop-filter: blur(10px) saturate(1.2);
    -webkit-backdrop-filter: blur(10px) saturate(1.2);
  }
  /* Draggable headers (grab cursor + dragging state). */
  #toolsPanel.panel-expanded .panel-header,
  #lessonOverlay.panel-expanded .panel-header {
    cursor: grab !important;
    touch-action: none;
  }
  .panel.panel-dragging .panel-header { cursor: grabbing !important; }
  /* Smaller fonts in expanded panels. */
  #toolsPanel.panel-expanded .panel-title,
  #lessonOverlay.panel-expanded .panel-title { font-size: 10px !important; }
  #toolsPanel.panel-expanded .section-title { font-size: 9px !important; }
  #toolsPanel.panel-expanded .tool span { font-size: 9px !important; }
  #toolsPanel.panel-expanded .preset,
  #toolsPanel.panel-expanded .material { font-size: 10px !important; padding: 6px 8px !important; }
  #toolsPanel.panel-expanded .check { font-size: 10px !important; }
  #lessonOverlay.panel-expanded .lesson-body { font-size: 11px !important; line-height: 1.45 !important; }
  #lessonOverlay.panel-expanded .concepts-title { font-size: 9px !important; }
  #lessonOverlay.panel-expanded .concept-pill { font-size: 9px !important; }

  /* ===========================================================
     LIVE READINGS — always-on, FPS-style HUD pinned to top-LEFT.
     Tiny fonts, narrow card, draggable header. =========================================================== */
  #infoOverlay {
    position: fixed !important;
    top: auto !important;
    bottom: calc(env(safe-area-inset-bottom) + 12px) !important;
    left: 12px !important;
    right: auto !important;
    width: 128px !important;
    min-width: 0 !important;
    max-width: 128px !important;
    height: auto !important;
    max-height: 40vh !important;
    transform: none;
    /* Body-level on mobile (layout.js re-homes it out of the topbar's
       translateZ containing block) — z sits above canvas/hint, below the
       topbar (10) and the ⋯ FAB (200). */
    z-index: 9;
    /* HUD sits in the background — canvas interactions pass straight through
       it so the user can tap/drag shapes that visually overlap the HUD area.
       The ● drag handle below is the one exception (pointer-events: auto). */
    pointer-events: none !important;
    visibility: visible !important;
    opacity: 1 !important;
    transition: none !important;
    display: flex !important;
    flex-direction: column !important;
    background: transparent !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    overflow: hidden;
    container-type: inline-size !important;
    transform-origin: top left;
  }
  /* All HUD children inherit pointer-events: none. */
  #infoOverlay > * { pointer-events: none; }
  /* Hidden state — toggleable via ⋯ → Readings. */
  body[data-hud-hidden="true"] #infoOverlay { display: none !important; }
  /* Idle fade so the HUD truly stays in the background. */
  #infoOverlay:not(.panel-dragging) {
    opacity: 0.55;
    transition: opacity 200ms ease;
  }
  #infoOverlay.panel-dragging { opacity: 1; transition: none; }
  /* Hide the header entirely — user wants only letters/nums/symbols. */
  #infoOverlay .panel-header { display: none !important; }
  /* Body always visible — naked text only, no chrome. Small left padding
     so labels don't crowd the viewport edge. */
  #infoOverlay .panel-body {
    display: flex !important;
    flex-direction: column !important;
    padding: 0 2px 0 0 !important;
    gap: 0;
    overflow-y: auto;
    max-height: 50vh;
    background: transparent !important;
    border: 0 !important;
  }
  #infoOverlay .readout-grid {
    display: grid !important;
    grid-template-columns: 1fr !important;
    gap: 0 !important;
    background: transparent !important;
    border: 0 !important;
  }
  #infoOverlay .metric {
    display: flex !important;
    flex-direction: row !important;
    justify-content: space-between !important;
    align-items: baseline !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
    gap: 4px;
  }
  #infoOverlay .metric:hover { background: transparent !important; }
  #infoOverlay .metric-label {
    font-size: 8px !important;
    text-transform: uppercase;
    color: rgba(180, 180, 200, 0.42) !important;
    letter-spacing: 0.3px !important;
    line-height: 1.25 !important;
    background: transparent !important;
    border: 0 !important;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.8), 0 0 1px rgba(0, 0, 0, 0.9);
  }
  #infoOverlay .metric-val {
    font-size: 10px !important;
    color: rgba(255, 255, 255, 0.52) !important;
    font-weight: 600 !important;
    font-feature-settings: "tnum";
    margin: 0 !important;
    line-height: 1.25 !important;
    white-space: nowrap;
    background: transparent !important;
    border: 0 !important;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.8), 0 0 1px rgba(0, 0, 0, 0.9);
  }
  #infoOverlay .metric-val .unit {
    font-size: 8px !important;
    color: rgba(180, 180, 200, 0.45) !important;
    margin-left: 1px;
    background: transparent !important;
    text-shadow: 0 0 3px rgba(0, 0, 0, 0.7);
  }
  #infoOverlay .totals-mini {
    font-size: 9px !important;
    padding: 2px 0 0 !important;
    margin: 2px 0 0 !important;
    border: 0 !important;
    display: flex;
    flex-direction: row;
    gap: 8px;
    color: rgba(180, 180, 200, 0.6);
    background: transparent !important;
    text-shadow: 0 0 3px rgba(0, 0, 0, 0.7);
  }
  #infoOverlay .totals-mini b {
    color: rgba(0, 229, 160, 0.7);
    font-size: 9px !important;
    background: transparent !important;
  }

  /* The ● is both the mode-cycler (tap) AND the drag handle for the HUD
     (press-and-drag). It's the only HUD element with pointer-events: auto so
     the canvas can be interacted with everywhere else. */
  .hud-view-toggle {
    position: absolute;
    top: -2px;
    right: -2px;
    width: 30px;
    height: 30px;
    /* Bare dim glyph — the old filled+bordered chip read as stray debris
       floating over the canvas (it sat right on top of the hint text).
       Hit box stays 30px; only the visual shrinks. */
    background: transparent;
    border: 0;
    border-radius: 50%;
    color: rgba(0, 229, 160, 0.45);
    font-size: 11px;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
    cursor: grab;
    z-index: 3;
    line-height: 1;
    padding: 0;
    pointer-events: auto !important;
    touch-action: none;
  }
  .hud-view-toggle:hover,
  .hud-view-toggle:active { background: rgba(0, 229, 160, 0.18); color: rgba(0, 229, 160, 1); }
  #infoOverlay.panel-dragging .hud-view-toggle { cursor: grabbing; }

  /* Minimal mode — only show the totals row + bodies count, hide the
     8-metric grid. */
  #infoOverlay[data-hud-mode="minimal"] .readout-grid { display: none !important; }

  /* Hidden mode — collapse the readings content but keep the ● toggle
     visible at the same spot so the user can re-expand. */
  #infoOverlay[data-hud-mode="hidden"] .panel-body { display: none !important; }
  #infoOverlay[data-hud-mode="hidden"] .panel-header { display: none !important; }
  /* Reserve the toggle's 30×30 footprint so it doesn't jump when the body
     collapses to zero. */
  #infoOverlay[data-hud-mode="hidden"] {
    width: 30px !important;
    max-width: 30px !important;
    height: 30px !important;
    max-height: 30px !important;
  }
  /* Legacy body[data-hud-hidden] rule is intentionally NOT used anymore — it
     hid the entire HUD including the toggle. Override here so any leftover
     attribute doesn't kill the toggle. */
  body[data-hud-hidden="true"] #infoOverlay { display: flex !important; }
  /* No chevron, no close on the HUD. */
  #infoOverlay .panel-header::after { display: none !important; }
  #infoOverlay .panel-close,
  #infoOverlay .overlay-close { display: none !important; }
  /* Override the (min-width: 380px) container query so big-font rule never fires. */
  #infoOverlay .metric-val { font-size: 11px !important; }

  /* Empty-state collapse — nothing selected: the global data-no-selection
     rule already hides the zero grid; the "Tap a body to inspect" line is
     desktop's instruction. On phones it duplicated the onboarding hint and
     the two overlapped at bottom-left, so the HUD collapses all the way to
     the live System/Bodies row instead. */
  #infoOverlay .hud-empty { display: none !important; }

  /* === Build-3 measurement graph — re-homed to <body> by layout.js (see the
     note there). Standalone anchored card, top-right under the topbar, clear of
     the System/Bodies readout (bottom-left HUD) and the hint bar (bottom).
     Collapses to just its controls when the graph is off. Desktop keeps the
     in-panel layout — this whole block is mobile-only (@media). === */
  #ov-graphWrap {
    position: fixed !important;
    /* Clears the two-row mobile topbar (~89px) — the quick-control band added
       a second row, so the graph sits below it instead of overlapping. */
    top: calc(env(safe-area-inset-top) + 95px) !important;
    right: 8px !important;
    left: auto !important;
    bottom: auto !important;
    width: min(62vw, 226px) !important;
    margin: 0 !important;
    padding: 8px !important;
    border: 1px solid var(--line) !important;
    border-radius: 10px !important;
    background: rgba(16, 16, 23, 0.93) !important;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5) !important;
    z-index: 11 !important;
    pointer-events: auto !important;
    opacity: 0.9;
    transition: opacity 160ms ease;
  }
  #ov-graphWrap:focus-within,
  #ov-graphWrap[data-graph-on="true"] { opacity: 1; }
  /* Controls are interactive (the HUD's pointer-events:none doesn't reach here
     anymore, but be explicit). */
  #ov-graphWrap * { pointer-events: auto; }
  #ov-graphWrap .hud-graph-controls {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
    margin: 0 0 6px 0 !important;
  }
  #ov-graphWrap .hud-graph-tog {
    font-size: 12px !important;
    display: inline-flex; align-items: center; gap: 5px;
    min-height: 30px;
  }
  #ov-graphWrap .hud-graph-tog input { width: 16px; height: 16px; }
  #ov-graphWrap .hud-graph-qty {
    font-size: 12px !important;
    padding: 5px 6px !important;
    min-height: 30px;
    flex: 1 1 84px; min-width: 78px;
  }
  #ov-graphWrap .hud-graph-clear {
    margin-left: auto !important;
    font-size: 11px !important;
    padding: 6px 10px !important;
    min-height: 30px;
  }
  #ov-graphWrap .hud-graph-canvas {
    width: 100% !important;
    height: auto !important;            /* keep the 320:96 aspect, no squish */
    border: 1px solid var(--line) !important;
    border-radius: 6px !important;
    background: var(--bg-1) !important;
    opacity: 1 !important;
  }
  #ov-graphWrap .hud-graph-foot { font-size: 10px !important; margin-top: 4px !important; line-height: 1.3; }
  /* Collapse cleanly when off — only the control row shows. */
  #ov-graphWrap[data-graph-on="false"] .hud-graph-canvas,
  #ov-graphWrap[data-graph-on="false"] .hud-graph-foot { display: none !important; }
  #ov-graphWrap[data-graph-on="false"] .hud-graph-controls { margin-bottom: 0 !important; }

  /* === Headers — chip form by default, panel form when expanded. === */
  .panel-header {
    display: flex !important;
    align-items: center !important;
    gap: 6px;
    padding: 8px 12px;
    min-height: 36px;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    flex-shrink: 0;
    white-space: nowrap;
    background: transparent;
    border-bottom: 0;
  }
  .panel.panel-expanded .panel-header {
    padding: 12px 14px;
    min-height: 44px;
    background: rgba(255, 255, 255, 0.04);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  }
  .panel-title {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--accent);
    font-weight: 700;
  }
  .panel-header::after {
    content: '▸';
    margin-left: auto;
    font-size: 11px;
    color: var(--muted);
  }
  .panel.panel-expanded .panel-header::after { content: '▾'; }
  #lessonOverlay .panel-header .level-badge {
    font-size: 10px;
    padding: 2px 8px;
    margin-left: auto;
  }
  #lessonOverlay .panel-header::after { margin-left: 6px; }

  /* === Bodies — hidden when collapsed, scrollable when expanded. === */
  .panel .panel-body { display: none !important; }
  .panel.panel-expanded .panel-body {
    display: flex !important;
    flex-direction: column;
    flex: 1 1 auto;
    padding: 10px 14px 14px;
    overflow-y: auto;
    max-height: calc(60vh - 50px);
  }

  /* === Hide decoration we don't need on mobile. === */
  .panel-grabber,
  .panel-pop,
  .panel-collapse,
  .mobile-only-btn,
  .tabbar { display: none !important; }

  /* === Inline close (×) — only visible on expanded panel. === */
  .panel-close,
  .overlay-close {
    display: none;
  }
  .panel.panel-expanded .panel-close,
  .panel.panel-expanded .overlay-close {
    display: flex !important;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 5px;
    color: var(--muted);
    font-size: 13px;
    margin-left: 4px;
    flex-shrink: 0;
  }

  /* === Tools panel content === */
  #toolsPanel .panel-body { gap: 12px; }
  #toolsPanel .tool-section { display: flex; flex-direction: column; gap: 6px; margin: 0; }
  #toolsPanel .tool-grid { display: flex; flex-wrap: wrap; gap: 6px; }
  #toolsPanel .tool {
    flex: 1 1 calc(33.33% - 6px);
    min-width: 78px;
    padding: 9px 6px;
    flex-direction: column;
    gap: 4px;
  }
  #toolsPanel .tool svg { width: 20px; height: 20px; }
  #toolsPanel .tool span { font-size: 10px; }
  #toolsPanel .preset,
  #toolsPanel .material {
    flex: 1 1 calc(50% - 6px);
    padding: 8px 10px;
    font-size: 11px;
  }

  /* === Readings panel content === */
  #infoOverlay .readout-grid { grid-template-columns: repeat(2, 1fr); gap: 4px; }
  #infoOverlay .metric { padding: 4px 0; }
  #infoOverlay .metric-label { font-size: 10px; }
  #infoOverlay .metric-val { font-size: 13px; margin-top: 1px; }
  #infoOverlay .metric-val .unit { font-size: 10px; }
  #infoOverlay .totals-mini {
    font-size: 11px;
    padding-top: 6px;
    gap: 14px;
    flex-direction: row;
    align-items: center;
  }

  /* === Educator panel layout === */
  #lessonOverlay .panel-body {
    padding: 6px 12px 12px;
    flex-direction: column;
    max-height: 32vh;
    overflow-y: auto;
  }
  #lessonOverlay .lesson-body {
    max-width: none;
    font-size: 13px;
    line-height: 1.55;
  }
  #lessonOverlay .formula { font-size: 11px; }
  #lessonOverlay .concepts-touched { min-width: 0; }

  /* Hint pinned at canvas bottom-left. */
  .canvas-host .hint {
    left: 12px;
    bottom: calc(env(safe-area-inset-bottom) + 16px);
    max-width: calc(100% - 24px);
  }
  .hint-touch { display: inline; }
  .hint-mouse { display: none; }
}

/* ======================= EMBED MODE (preserved) =======================
   The landing page hosts the free demo via /app/?free=1 (no longer iframed
   post-rework, but embed-mode stays in case anyone still embeds /app/?embed=1). */
body.embed-mode .topbar {
  display: none !important;
}
/* Adjust grid when topbar is hidden in embed mode */
body.embed-mode {
  grid-template-rows: 1fr;
  grid-template-areas: "main";
}
body.embed-mode .brand .by,
body.embed-mode .brand .pro-badge,
body.embed-mode #saveBtn,
body.embed-mode #derivationsBtn,
body.embed-mode #upgradeDialog {
  display: none !important;
}
body.embed-mode .brand .title { font-size: 13px; opacity: 0.9; }

/* Upgrade dialog footer — privacy/terms links under the redeem details. */
.upgrade-foot {
  margin: 14px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 11px;
  color: var(--muted);
  text-align: center;
}
.upgrade-foot a { color: var(--muted); text-decoration: none; }
.upgrade-foot a:hover { color: var(--accent-2); }

/* Resize handles default to hidden — they only show when their parent panel
   is `.floating` (see desktop @media). */
.resize-corner,
.resize-edge { display: none; }

/* ======================= LONG-PRESS CONTEXT MENU ======================= */
.ctx-menu {
  position: fixed;
  z-index: 200;
  background: rgba(14, 16, 24, 0.97);
  backdrop-filter: blur(12px) saturate(1.2);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  border: 1px solid rgba(0, 229, 160, 0.3);
  border-radius: 10px;
  padding: 6px;
  min-width: 168px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  box-shadow: 0 14px 38px rgba(0, 0, 0, 0.65);
}
.ctx-menu[hidden] { display: none; }
.ctx-item {
  background: transparent;
  border: 0;
  text-align: left;
  padding: 9px 12px;
  font-size: 13px;
  color: var(--text);
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ctx-item:hover,
.ctx-item:active { background: rgba(255, 255, 255, 0.07); }
.ctx-item.ctx-danger { color: #ff7799; }
.ctx-item.ctx-danger:hover { background: rgba(255, 95, 130, 0.12); }
.ctx-mats { padding: 0; }
.ctx-mats summary {
  padding: 9px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  list-style: none;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.ctx-mats summary::-webkit-details-marker { display: none; }
.ctx-mats summary::after {
  content: '▸';
  margin-left: 6px;
  font-size: 11px;
  color: var(--muted);
}
.ctx-mats[open] summary::after { content: '▾'; }
.ctx-mat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 4px;
  padding: 4px 4px 6px;
}
.ctx-mat-grid button {
  font-size: 11px;
  padding: 7px 8px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.ctx-mat-grid button:hover,
.ctx-mat-grid button:active {
  border-color: var(--accent);
  background: rgba(0, 229, 160, 0.06);
}

/* =====================================================================
   CONTAINER QUERIES — internal fluidity for resized + floating panels.
   Each .panel becomes its own inline-size container; child layouts adapt
   to the panel's width independent of the viewport breakpoint.
   ===================================================================== */
.panel {
  container-type: inline-size;
}
/* Tools queries height too (compact-mode at short heights), needs `size`. */
#toolsPanel { container-type: size; container-name: tools-panel; }
#infoOverlay { container-name: readings-panel; }
/* Educator queries height too (compact-mode hint when float is short), so it
   needs `container-type: size` rather than `inline-size`. */
#lessonOverlay { container-type: size; container-name: educator-panel; }

/* Material grid adapts to available width. */
#toolsPanel .material-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 5px;
}

/* TOOLS — narrow + wide adaptations. */
@container tools-panel (max-width: 240px) {
  #toolsPanel .tool-section .tool-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 4px;
  }
  #toolsPanel .tool { min-width: 0; padding: 6px 4px; }
  #toolsPanel .tool svg { display: none; }
  #toolsPanel .tool span { font-size: 10.5px; }
}
@container tools-panel (min-width: 360px) {
  #toolsPanel .tool-section .tool-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
    gap: 6px;
  }
  #toolsPanel .tool svg { width: 26px; height: 26px; }
}

/* READINGS — value typography scales with panel width; on narrow we drop to
   single column. */
@container readings-panel (max-width: 260px) {
  #infoOverlay .readout-grid {
    grid-template-columns: 1fr;
  }
  #infoOverlay .metric-val { font-size: 13px; }
  #infoOverlay .metric-val .unit { font-size: 10px; }
}
@container readings-panel (min-width: 380px) {
  #infoOverlay .metric-val { font-size: 17px; }
}

/* EDUCATOR — when short (compact float height), tighten the body and hide the
   "Concepts explored" header so the pill row fills its space without title
   chrome. The pill list itself stays scrollable. */
@container educator-panel (max-height: 220px) {
  #lessonOverlay .lesson-body { line-height: 1.45; }
  #lessonOverlay .concepts-title { display: none; }
  #lessonOverlay .concepts-touched { padding-top: 6px; margin-top: 0; }
  #lessonOverlay .concept-list {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 4px;
  }
  #lessonOverlay .concept-pill { white-space: nowrap; flex: 0 0 auto; }
}

/* TOOLS — compact mode at short heights (e.g. low-snap mobile sheet at 30vh).
   Each tool-section becomes a horizontal scroller so users still get the full
   tool palette in a small slice. Tool buttons stay >=44px tall (touch floor). */
@container tools-panel (max-height: 300px) {
  #toolsPanel .panel-body { gap: 8px; }
  #toolsPanel .tool-section { gap: 4px; }
  #toolsPanel .section-title {
    font-size: 10px;
    font-weight: 500;
    white-space: nowrap;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin: 0;
  }
  #toolsPanel .tool-grid,
  #toolsPanel .material-grid {
    display: flex;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    gap: 6px;
    padding-bottom: 2px;
    scrollbar-width: thin;
  }
  #toolsPanel .tool-grid > *,
  #toolsPanel .material-grid > * {
    flex: 0 0 auto;
  }
  #toolsPanel .tool {
    min-height: 44px;
    min-width: 56px;
    padding: 6px 8px;
    flex-direction: column;
    gap: 2px;
  }
  #toolsPanel .tool svg { width: 18px; height: 18px; }
  #toolsPanel .tool span { font-size: 10px; }
  #toolsPanel .preset,
  #toolsPanel .material {
    min-height: 44px;
    min-width: 64px;
    padding: 6px 10px;
    font-size: 10px;
    white-space: nowrap;
  }
  #toolsPanel .check {
    min-height: 44px;
    min-width: 88px;
    padding: 6px 8px;
    font-size: 11px;
    white-space: nowrap;
  }
  #toolsPanel .slider-row {
    min-height: 44px;
    min-width: 140px;
    padding: 4px 8px;
  }
  /* Custom slim scrollbar for the inline horizontal scrollers. */
  #toolsPanel .tool-grid::-webkit-scrollbar,
  #toolsPanel .material-grid::-webkit-scrollbar {
    height: 2px;
  }
  #toolsPanel .tool-grid::-webkit-scrollbar-track,
  #toolsPanel .material-grid::-webkit-scrollbar-track {
    background: transparent;
  }
  #toolsPanel .tool-grid::-webkit-scrollbar-thumb,
  #toolsPanel .material-grid::-webkit-scrollbar-thumb {
    background: var(--bg-4);
    border-radius: 2px;
  }
}

/* =====================================================================
   SCROLL LOCK + OVERSCROLL — when sheet is open, the body is frozen so
   browser-level rubber-band scroll on the topbar / canvas-host doesn't
   compete with the sheet drag. .canvas-host stays interactive (pan/zoom).
   ===================================================================== */
body[data-sheet-locked="true"] {
  overflow: hidden;
  overscroll-behavior: none;
}

@media (max-width: 768px) {
  html { overscroll-behavior: none; }
}

/* =====================================================================
   PANEL ANIMATION POLISH — sheet open/close + height/width snap easing.
   Keeps `.panel.dragging` snappy (existing rule disables transition).
   ===================================================================== */
.panel {
  transition:
    transform 220ms cubic-bezier(0.32, 0.72, 0, 1),
    height 220ms cubic-bezier(0.32, 0.72, 0, 1),
    width 220ms cubic-bezier(0.32, 0.72, 0, 1);
}

/* =====================================================================
   ACTIVE TAB PILL — animated accent under the active tab in the mobile
   tabbar. ::before sits behind the icon+label and pulses with a glow.
   ===================================================================== */
.tabbar .tab {
  position: relative;
  isolation: isolate;
}
.tabbar .tab::before {
  content: '';
  position: absolute;
  inset: 4px;
  border-radius: 8px;
  background: var(--accent);
  opacity: 0;
  z-index: -1;
  transform: scale(0.92);
  transition: opacity 180ms ease, transform 180ms ease, box-shadow 180ms ease;
  pointer-events: none;
}
.tabbar .tab.active::before {
  opacity: 0.16;
  transform: scale(1);
  box-shadow: 0 0 12px var(--accent-glow);
}
.tabbar .tab svg {
  transition: transform 180ms ease;
}
.tabbar .tab.active svg {
  transform: scale(1.05);
}

/* =====================================================================
   HINT CLOSE — bigger touch target without changing the visual chip size.
   Visual stays 26x26; ::before extends the hit area to 44x44. (Spec G.)
   ===================================================================== */
.hint-close {
  position: relative;
  min-width: 26px;
  min-height: 26px;
}
.hint-close::before {
  content: '';
  position: absolute;
  inset: -9px;
  border-radius: 12px;
}

/* =====================================================================
   ZEN MODE — hides all UI chrome so only the canvas is visible.
   Toggle with the floating button (bottom-right) or press Z.
   ===================================================================== */
.zen-toggle {
  position: fixed;
  /* On mobile, sit above the tabbar + safe area instead of overlapping it. */
  bottom: calc(12px + env(safe-area-inset-bottom));
  right: 12px;
  z-index: 200;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: rgba(10, 10, 15, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.5);
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.2s, color 0.2s, border-color 0.2s;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  padding: 0;
}
.zen-toggle:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.3);
}

body.zen-mode .topbar,
body.zen-mode .panel,
body.zen-mode .tabbar,
body.zen-mode .hint {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}

body.zen-mode .zen-toggle {
  color: rgba(255, 255, 255, 0.3);
}

body.zen-mode .canvas-host {
  position: fixed;
  inset: 0;
  z-index: 1;
}

/* =====================================================================
   PAN TOOL — cursor and canvas-panning state styles.
   ===================================================================== */
body.space-pan canvas { cursor: grab; }
body.space-pan.panning canvas,
canvas.panning { cursor: grabbing !important; }

/* =====================================================================
   TOPBAR OVERFLOW MENU — progressive disclosure on every viewport.
   The topbar keeps ≤5 controls: Mode · Level · Pause · ⋯ · Pro. Everything
   else (sim sliders, panel toggles, scene actions) lives in the ⋯ dropdown,
   shown when body[data-topbar-overflow-open="true"], positioned by
   layout.js next to the ⋯ button. On mobile the ⋯ is a draggable FAB.
   ===================================================================== */
.topbar-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  min-width: 38px;
  padding: 0;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.topbar-more:hover,
.topbar-more:focus-visible {
  background: var(--bg-4);
  border-color: var(--accent);
  color: var(--accent);
}
body[data-topbar-overflow-open="true"] .topbar-more {
  background: rgba(0, 229, 160, 0.12);
  border-color: var(--accent);
  color: var(--accent-2);
}
.topbar-overflow {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  flex-direction: column;
  align-items: stretch;
  min-width: 230px;
  max-width: 290px;
  max-height: calc(100dvh - 24px);
  overflow-y: auto;
  padding: 8px;
  background: rgba(15, 15, 22, 0.97);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border: 1px solid var(--line-2);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
  gap: 4px;
}
body[data-topbar-overflow-open="true"] .topbar-overflow {
  display: flex;
}
.topbar-overflow .btn {
  width: 100%;
  min-height: 40px;
  padding: 10px 14px;
  font-size: 13px;
  text-align: left;
  justify-content: flex-start;
  border-radius: 8px;
}
.menu-sect {
  font-size: 10px;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--muted);
  padding: 4px 6px 2px;
}
.topbar-overflow .menu-sect:not(:first-child) {
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.topbar-overflow .bar-group {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 2px 6px 6px;
}
.topbar-overflow .bar-group label {
  font-size: 12px;
  color: var(--text);
}
.topbar-overflow .bar-group input[type="range"] {
  width: 100%;
}

@media (max-width: 768px) {
  /* FAB — floats near bottom-right, user can drag it anywhere on screen. */
  .topbar-more {
    width: 40px;
    height: 40px;
    min-width: 40px;
    position: fixed;
    right: 16px;
    bottom: calc(env(safe-area-inset-bottom) + 16px);
    top: auto;
    transform: none;
    z-index: 200;
    touch-action: none;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
  }
  .topbar-more.ps-dragging {
    opacity: 0.75;
    cursor: grabbing;
    transition: none;
  }
}

/* Zen-toggle has no purpose on mobile (no sidebars to collapse). */
@media (max-width: 768px) {
  .zen-toggle { display: none !important; }
  /* Strip topbar to: [PS] [C|S|U|E] [⏸]  — everything else is in ⋯ menu. */
  .topbar > .bar-group:has(#modeSegment) { display: none !important; }
  /* ⋯ is a fixed FAB — no reserved right padding needed. */
  .topbar { padding-right: 14px !important; }
}

/* =====================================================================
   LANDSCAPE MOBILE — phones held sideways. Sheet pulls from the right,
   tabbar moves to a vertical strip on the right edge, topbar shrinks.
   Triggered via body[data-layout-mode="landscape-mobile"] from layout.js
   (matched against (max-width:768px) + landscape + max-height:600px).
   ===================================================================== */
@media (max-width: 768px) and (orientation: landscape) and (max-height: 600px) {
  body {
    grid-template-rows: auto 1fr;
    grid-template-columns: 1fr;
  }

  /* Compress topbar — shorter, tighter, kill the slider <label> text since
     the slider track itself communicates value. */
  .topbar {
    gap: 10px;
    padding: 4px 10px;
    padding-top: max(4px, env(safe-area-inset-top));
    padding-right: 50px;
    min-height: 36px;
  }
  .topbar .btn {
    padding: 6px 10px;
    font-size: 11px;
    min-height: 32px;
  }
  .topbar .seg { padding: 5px 8px; font-size: 11px; }
  .topbar .bar-group label { font-size: 10px; }
  .topbar .bar-group:not(:has(.segment)) label {
    display: none;
  }
  .topbar .seg-label { display: none; }
  .topbar .bar-group input[type=range] {
    width: 80px;
    max-width: 100px;
  }
  .topbar-more {
    width: 36px;
    height: 36px;
    min-width: 36px;
    font-size: 16px;
  }

  /* Tabbar retired — landscape-mobile uses the ⋯ menu like portrait. */
  .tabbar { display: none; }

  /* Hint pinned bottom-left; no right-side tabbar to clear. */
  .canvas-host .hint {
    left: 12px;
    bottom: 12px;
    right: 52px;
    max-width: calc(100% - 64px);
  }

  /* Sheet — pulls from the right, vertical drag handle on its left edge,
     respects 40px tabbar gutter. */
  .panel {
    top: 0;
    bottom: 0;
    right: 40px;
    left: auto;
    height: 100%;
    width: var(--sheet-width, 60vw);
    max-height: none;
    max-width: 95vw;
    border-radius: 16px 0 0 16px;
    border-top: 0;
    border-left: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: -10px 0 40px rgba(0, 0, 0, 0.55);
    transform: translateX(100%);
  }
  body[data-sheet-height="low"] .panel { --sheet-width: 30vw; }
  body[data-sheet-height="mid"] .panel { --sheet-width: 60vw; }
  body[data-sheet-height="high"] .panel { --sheet-width: 80vw; }
  body[data-sheet-height="default"] .panel { --sheet-width: 60vw; }
  body[data-sheet-height="tall"] .panel { --sheet-width: 80vw; }
  .panel.visible {
    transform: translateX(0);
  }

  /* Vertical grabber — left edge of the sheet. */
  .panel-grabber {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 14px;
    height: auto;
  }
  .panel-grabber::before {
    width: 4px;
    height: 42px;
    background: linear-gradient(180deg, var(--accent), var(--accent-2));
  }
  .panel-header { padding-left: 22px; }
}

/* Mode-aware tool sections — visible only when body[data-mode] matches. */
body[data-mode="2d"] [data-mode-only="3d"] { display: none !important; }
body[data-mode="3d"] [data-mode-only="2d"] { display: none !important; }


/* ======================= 3D MODE =======================
   3D mode reuses the 2D grid layout. The toolsPanel/infoOverlay/lessonOverlay
   each carry their own data-mode-only="3d" sections, so the panels stay
   useful in 3D. The WebGL canvas is clipped to canvas-host (overflow: hidden),
   so docked-panel borders don't bleed. */

/* ===================== NARROW PHONES (<=480px) =====================
   On 412px Galaxy portrait the full level segment won't fit alongside
   the brand. Collapse the brand to logo-only and abbreviate level
   buttons to single letters so key controls appear without scrolling. */
@media (max-width: 480px) {
  .topbar .brand .title { display: none; }
  #levelSegment .seg {
    font-size: 0;
    padding: 8px 5px;
    min-width: 28px;
  }
  #levelSegment [data-level="1"]::after { content: "C"; font-size: 11px; font-weight: 600; }
  #levelSegment [data-level="2"]::after { content: "S"; font-size: 11px; font-weight: 600; }
  #levelSegment [data-level="3"]::after { content: "U"; font-size: 11px; font-weight: 600; }
  #levelSegment [data-level="4"]::after { content: "E"; font-size: 11px; font-weight: 600; }
}

/* =====================================================================
   POLISH LAYER (2026-05-18) — universal reactivity
   Springy hover-lift + tap-scale on every interactive control, applied
   on top of existing per-element rules. Transform/filter only so it never
   triggers layout. Skips elements actively being dragged.
   ===================================================================== */
:root {
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-out-soft: cubic-bezier(0.32, 0.72, 0, 1);
}

.btn,
.seg,
.tool,
.preset,
.material,
.tab,
.tabbar .tab,
.cta,
.panel-collapse,
.panel-pop,
.panel-close,
.topbar-more {
  transition:
    transform 160ms var(--spring),
    background 180ms var(--ease-out-soft),
    border-color 180ms var(--ease-out-soft),
    box-shadow 180ms var(--ease-out-soft),
    color 180ms var(--ease-out-soft),
    filter 180ms var(--ease-out-soft);
  will-change: transform;
}

@media (hover: hover) {
  .btn:not(:active):hover,
  .seg:not(.active):not(:active):hover,
  .tool:not(.active):not(:active):hover,
  .preset:not(:active):hover,
  .material:not(.active):not(:active):hover,
  .cta:not(:active):hover,
  .panel-collapse:not(:active):hover,
  .panel-pop:not(:active):hover {
    transform: translateY(-1.5px) scale(1.015);
    filter: brightness(1.08);
  }
}

.btn:active,
.seg:active,
.tool:active,
.preset:active,
.material:active,
.tab:active,
.tabbar .tab:active,
.cta:active,
.panel-collapse:active,
.panel-pop:active,
.panel-close:active,
.topbar-more:active {
  transform: scale(0.94);
  filter: brightness(0.92);
  transition-duration: 90ms;
}

/* Glassier topbar — match the panel translucency boost. */
.topbar {
  background: rgba(10, 10, 15, 0.55);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
}

/* Glassier dialogs (help + upgrade) — pull canvas through. */
dialog.help,
dialog.upgrade-dialog {
  background:
    linear-gradient(180deg, rgba(15, 15, 22, 0.78), rgba(10, 10, 15, 0.72));
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  backdrop-filter: blur(24px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
dialog.help::backdrop,
dialog.upgrade-dialog::backdrop {
  background: rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Panel header — slightly more transparent border so canvas reads through. */
.panel-header {
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), transparent);
}

/* Springy panel size + position animation. */
.panel:not(.dragging) {
  transition:
    transform 280ms var(--ease-out-soft),
    height 280ms var(--ease-out-soft),
    width 280ms var(--ease-out-soft),
    opacity 200ms var(--ease-out-soft);
}

/* Metric cards (live readings): subtle pulse on value change handled in JS,
   but baseline gets a sheen-on-hover. */
.metric {
  transition:
    transform 200ms var(--spring),
    background 200ms var(--ease-out-soft),
    border-color 200ms var(--ease-out-soft);
}
@media (hover: hover) {
  .metric:hover {
    transform: translateY(-1px);
  }
}

/* Respect motion-reduced users — strip the springs entirely. */
@media (prefers-reduced-motion: reduce) {
  .btn, .seg, .tool, .preset, .material, .tab, .cta,
  .panel-collapse, .panel-pop, .panel-close,
  .topbar-more, .metric, .panel {
    transition-duration: 0ms !important;
  }
  .btn:hover, .seg:hover, .tool:hover, .preset:hover, .material:hover,
  .cta:hover, .metric:hover {
    transform: none !important;
  }
}

/* HUD empty state — when nothing is selected the per-body grid hides and a
   one-line prompt shows instead; System/Bodies totals stay live. Driven by
   data-no-selection from updateInfoOverlay. */
.hud-empty {
  display: none;
  font-size: 12px;
  color: var(--muted);
  padding: 2px 0 4px;
}
/* !important matches the mobile always-on-HUD block, which forces
   display:grid !important (same pattern as the hud-mode=minimal override). */
#infoOverlay[data-no-selection="true"] .readout-grid { display: none !important; }
#infoOverlay[data-no-selection="true"] .hud-empty { display: block; }

/* Educator-panel level segment — the mobile home of the level toggle (the
   chip strip hides the topbar segment on phones). Desktop keeps the topbar
   segment only. */
.edu-level-segment {
  display: flex;
  margin: 0 0 10px;
}
@media (min-width: 769px) {
  .edu-level-segment { display: none; }
}

/* ======================= ONBOARDING TOUR (driver.js) ======================= */
/* Dark-theme overrides for the .ps-tour popover — driver.css ships light. */
.driver-popover.ps-tour {
  background: rgba(18, 18, 26, 0.98);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
  max-width: min(300px, calc(100vw - 24px));
}
.driver-popover.ps-tour .driver-popover-title {
  color: var(--accent-2);
  font-size: 15px;
  font-weight: 700;
}
.driver-popover.ps-tour .driver-popover-description {
  color: var(--text);
  font-size: 13px;
  line-height: 1.55;
}
.driver-popover.ps-tour .driver-popover-progress-text {
  color: var(--muted);
  font-size: 11px;
}
.driver-popover.ps-tour .driver-popover-navigation-btns button {
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 12px;
  font-size: 12px;
  text-shadow: none;
}
.driver-popover.ps-tour .driver-popover-navigation-btns button:hover {
  background: var(--bg-4);
  border-color: var(--accent);
  color: var(--accent);
}
.driver-popover.ps-tour .driver-popover-close-btn {
  color: var(--muted);
}
.driver-popover.ps-tour .driver-popover-close-btn:hover {
  color: var(--text);
}
/* Match the popover arrow to the dark background on all four sides. */
.driver-popover.ps-tour .driver-popover-arrow-side-top { border-top-color: rgba(18, 18, 26, 0.98); }
.driver-popover.ps-tour .driver-popover-arrow-side-bottom { border-bottom-color: rgba(18, 18, 26, 0.98); }
.driver-popover.ps-tour .driver-popover-arrow-side-left { border-left-color: rgba(18, 18, 26, 0.98); }
.driver-popover.ps-tour .driver-popover-arrow-side-right { border-right-color: rgba(18, 18, 26, 0.98); }

/* ============================ Build 3: exploration ============================ */
/* Measurement graph inside the Live Readings HUD */
.hud-graph {
  margin-top: 10px;
  border-top: 1px solid var(--line);
  padding-top: 8px;
}
.hud-graph-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.hud-graph-tog { font-size: 11px; }
.hud-graph-qty {
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 11px;
  padding: 3px 6px;
}
.hud-graph-clear {
  margin-left: auto;
  background: transparent;
  border: 1px dashed var(--line);
  color: var(--muted);
  border-radius: var(--radius-sm);
  font-size: 10px;
  padding: 3px 8px;
  transition: all 0.15s ease;
}
.hud-graph-clear:hover { color: var(--accent); border-color: var(--accent); }
.hud-graph-canvas {
  display: block;
  width: 100%;
  height: 96px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.hud-graph[data-graph-on="false"] .hud-graph-canvas { opacity: 0.4; }
.hud-graph-foot {
  margin-top: 4px;
  font-size: 10px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Scenario library list */
.scenario-list { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.scenario-item {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 4px 6px;
}
.scenario-item .scenario-load {
  flex: 1 1 auto;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--text);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scenario-item .scenario-load:hover { color: var(--accent); }
.scenario-item .scenario-meta { font-size: 9px; color: var(--muted); }
.scenario-item .scenario-del {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}
.scenario-item .scenario-del:hover { color: var(--secondary, #ff6b9d); }
.scenario-empty { font-size: 10px; color: var(--muted); padding: 2px; }
.scenario-save-btn { border-style: solid; color: var(--accent); border-color: var(--accent); }

/* Challenge banner */
.challenge-banner {
  position: fixed;
  left: 50%;
  bottom: 64px;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(680px, 92vw);
  background: linear-gradient(135deg, rgba(22,22,31,0.97), rgba(30,30,42,0.97));
  border: 1px solid var(--accent);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5), 0 0 12px var(--accent-glow);
  border-radius: 12px;
  padding: 10px 14px;
  font-size: 13px;
  color: var(--text);
  animation: challengeIn 0.25s ease;
}
@keyframes challengeIn { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }
.challenge-banner[hidden] { display: none; }
.challenge-banner-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bg);
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  padding: 3px 8px;
  border-radius: 999px;
  flex: 0 0 auto;
}
.challenge-banner-title { font-weight: 700; flex: 0 0 auto; }
.challenge-banner-prompt { color: var(--muted); flex: 1 1 auto; }
.challenge-banner-close {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 14px;
  cursor: pointer;
  flex: 0 0 auto;
  padding: 2px 4px;
}
.challenge-banner-close:hover { color: var(--text); }
@media (max-width: 560px) {
  .challenge-banner { flex-wrap: wrap; bottom: 78px; font-size: 12px; }
  .challenge-banner-prompt { flex-basis: 100%; }
}
