/* urdfbase — dark and light, one accent, everything else neutral.
   Mobile first; the rail and floating panels take their final shape only once
   there is room for them. */

:root {
  /* Dark is the default for a 3D tool: a model reads better against it and an
     hour of work is easier on the eyes. Light exists for documentation
     screenshots and for people who work in bright rooms. */
  --bg: #0a0b0d;
  --surface: #131519;
  --surface-2: #191c21;
  --line: #23262d;
  --line-soft: #1a1d22;
  --text: #e8eaed;
  --text-dim: #9aa0a6;
  --text-faint: #6b7176;
  --accent: #22d3ee;
  --accent-ink: #04191d;
  --accent-hover: #4ae0f5;
  --danger: #f87171;
  --warn: #fbbf24;
  --ok: #4ade80;
  --shadow: 0 16px 40px rgb(0 0 0 / 0.55);

  /* Scene colours the renderer reads, so 3D follows the interface. */
  --scene-bg: #0a0b0d;
  --scene-fog: #15181d;
  --scene-grid: #23262d;
  --scene-grid-soft: #16191e;
  --scene-part: #9aa4ad;

  --rail: 60px;
  --radius: 10px;
  --radius-lg: 14px;

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, monospace;

  --section: 64px;
  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg: #fbfbfc;
  --surface: #ffffff;
  --surface-2: #f1f3f5;
  --line: #e3e6ea;
  --line-soft: #eef0f3;
  --text: #14171a;
  --text-dim: #5b6470;
  --text-faint: #8a939f;
  --accent: #0b7f96;
  --accent-ink: #ffffff;
  --accent-hover: #0a6d80;
  --danger: #c0392f;
  --warn: #a06800;
  --ok: #1a7f3c;
  --shadow: 0 14px 34px rgb(15 23 42 / 0.14);

  --scene-bg: #eceff2;
  --scene-fog: #f6f7f9;
  --scene-grid: #cfd5dc;
  --scene-grid-soft: #e2e6eb;
  --scene-part: #8d97a1;

  color-scheme: light;
}

@media (min-width: 900px) {
  :root { --section: 96px; }
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* The faint technical grid both references and the 3D scene share. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(to right, var(--line-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 90% 70% at 50% 30%, #000 40%, transparent 100%);
}

h1, h2, h3 {
  letter-spacing: -0.025em;
  line-height: 1.15;
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(2rem, 6vw, 3.4rem); }
h2 { font-size: clamp(1.35rem, 3vw, 1.9rem); }
h3 { font-size: 1.0625rem; }

p { margin: 0 0 1rem; }
a { color: inherit; }

code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.1em 0.35em;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 0.6rem 1rem;
  z-index: 100;
}
.skip:focus { left: 0; }

/* ------------------------------------------------------------------ rail */

.rail {
  position: fixed;
  z-index: 20;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border-top: 1px solid var(--line);
  backdrop-filter: blur(12px);
  overflow-x: auto;
}

.rail-mark { display: none; }

.rail-list {
  display: flex;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.rail-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 60px;
  min-height: 44px;
  padding: 6px 8px;
  border-radius: var(--radius);
  color: var(--text-dim);
  text-decoration: none;
  transition: color 140ms ease, background-color 140ms ease;
}

.rail-link:hover { color: var(--text); background: var(--surface-2); }
.rail-link.is-current { color: var(--accent); }

.rail-icon { display: block; width: 22px; height: 22px; }
.rail-icon svg { width: 100%; height: 100%; display: block; }
.rail-label { font-size: 0.6875rem; letter-spacing: 0.01em; }

@media (min-width: 900px) {
  .rail {
    inset: 0 auto 0 0;
    width: var(--rail);
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 14px 0;
    border-top: 0;
    border-right: 1px solid var(--line);
    overflow: visible;
  }
  .rail-mark {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    margin-bottom: 14px;
    color: var(--accent);
    border-radius: var(--radius);
  }
  .rail-list { flex-direction: column; }
  .rail-link { min-width: 44px; width: 44px; padding: 7px 0; }
  .rail-label {
    position: absolute;
    left: calc(var(--rail) - 4px);
    padding: 5px 10px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: 0.8125rem;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transform: translateX(-4px);
    transition: opacity 120ms ease, transform 120ms ease;
  }
  .rail-link:hover .rail-label,
  .rail-link:focus-visible .rail-label { opacity: 1; transform: translateX(0); }
}

/* --------------------------------------------------------------- content */

.shell {
  position: relative;
  z-index: 1;
  padding: 40px 20px 96px;
  max-width: 1180px;
  margin: 0 auto;
}

@media (min-width: 900px) {
  .shell { padding: 72px 40px 120px calc(var(--rail) + 40px); }
}

.hero { max-width: 40rem; }

.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1rem;
}

.lede {
  margin-top: 1.25rem;
  font-size: 1.0625rem;
  color: var(--text-dim);
  max-width: 62ch;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 1.75rem; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.9375rem;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 140ms ease, background-color 140ms ease, color 140ms ease;
}
.btn:hover { border-color: color-mix(in srgb, var(--line) 60%, var(--text-faint)); background: var(--surface-2); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 550;
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-small { min-height: 34px; padding: 0 12px; font-size: 0.8125rem; }
.btn-ghost { background: transparent; }

/* ----------------------------------------------------------------- stage */

.stage-wrap { margin-top: var(--section); }

.stage {
  position: relative;
  aspect-ratio: 16 / 10;
  max-height: 60vh;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: radial-gradient(ellipse 60% 50% at 50% 45%, var(--scene-fog) 0%, var(--scene-bg) 70%);
  overflow: hidden;
}
.stage canvas { display: block; width: 100%; height: 100%; }

.stage-fallback { padding: 2rem; color: var(--text-faint); }

.stage-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px;
  margin-top: 10px;
  font-size: 0.8125rem;
  color: var(--text-faint);
}
.stage-facts { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------------- tools */

.tools { margin-top: var(--section); }

.tool-grid {
  display: grid;
  gap: 1px;
  margin: 2rem 0 0;
  padding: 0;
  list-style: none;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

@media (min-width: 720px) { .tool-grid { grid-template-columns: 1fr 1fr; } }

.tool {
  padding: 24px;
  background: var(--surface);
  transition: background-color 140ms ease;
}
.tool:hover { background: var(--surface-2); }
.tool h3 { margin-bottom: 0.5rem; }
.tool h3 a { text-decoration: none; }
.tool h3 a::after { content: ""; position: absolute; inset: 0; }
.tool { position: relative; }
.tool p { margin: 0; color: var(--text-dim); font-size: 0.9375rem; }

/* ----------------------------------------------------------------- prose */

.prose { margin-top: var(--section); max-width: 68ch; }
.prose h2 { margin-top: 2.5rem; margin-bottom: 1rem; }
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--text-dim); }
.prose a { color: var(--accent); text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.prose em { color: var(--text); font-style: normal; font-weight: 550; }

.faq { margin-top: var(--section); max-width: 68ch; }
.qa { padding: 20px 0; border-bottom: 1px solid var(--line); }
.qa:first-of-type { border-top: 1px solid var(--line); }
.qa h3 { margin-bottom: 0.5rem; }
.qa p { margin: 0; color: var(--text-dim); }
.qa a { color: var(--accent); }

/* ------------------------------------------------------------------ foot */

.foot {
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--line);
  padding: 40px 20px 96px;
}

@media (min-width: 900px) {
  .foot { padding: 48px 40px 48px calc(var(--rail) + 40px); }
  
}

.foot-in { max-width: 1180px; margin: 0 auto; }
.foot-brand { font-family: var(--mono); color: var(--accent); margin-bottom: 1rem; }
.foot-nav { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-bottom: 1rem; }
.foot-nav a { color: var(--text-dim); text-decoration: none; font-size: 0.875rem; }
.foot-nav a:hover { color: var(--text); text-decoration: underline; }
.foot-note { color: var(--text-faint); font-size: 0.8125rem; max-width: 62ch; margin: 0; }

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

/* ============================================================== tool shell

   Layout follows what a 3D tool needs: panels pinned to the edges so the tree
   and properties are always readable, and the scene taking everything else.
   The look is ours — panels are inset cards with soft edges rather than boxes
   welded to the window, one accent, numbers in a tabular mono. */

.app {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-rows: 48px 1fr;
  grid-template-columns: 1fr;
  height: 100dvh;
  overflow: hidden;
  background: var(--bg);
}

/* -------------------------------------------------------------- top bar */

.topbar {
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.topbar-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.topbar-mid { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.topbar-right { display: flex; align-items: center; gap: 6px; }

.model-name {
  font-size: 0.875rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 32vw;
}

.model-format {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  padding: 2px 7px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 5px;
}

/* ------------------------------------------------------- mark as menu */

.menu { position: relative; }

.menu-btn {
  display: flex;
  align-items: center;
  gap: 3px;
  width: 46px;
  height: 34px;
  padding: 0 6px;
  border-radius: 8px;
  color: var(--accent);
  cursor: pointer;
  list-style: none;
  transition: background-color 140ms ease;
}
.menu-btn::-webkit-details-marker { display: none; }
.menu-btn:hover { background: var(--surface-2); }
.menu-caret { color: var(--text-faint); transition: transform 160ms ease; }
.menu[open] .menu-caret { transform: rotate(180deg); }

.menu-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  min-width: 190px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.menu-brand {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 4px 10px 8px;
}

.menu-list a {
  display: block;
  padding: 8px 10px;
  border-radius: 7px;
  font-size: 0.875rem;
  color: var(--text-dim);
  text-decoration: none;
  transition: color 120ms ease, background-color 120ms ease;
}
.menu-list a:hover { color: var(--text); background: var(--surface-2); }

/* --------------------------------------------------------------- panels */

.app {
  grid-template-areas:
    "top top top"
    "left canvas right";
  grid-template-columns: auto 1fr auto;
}

.topbar { grid-area: top; }
.canvas { grid-area: canvas; }
.side-left { grid-area: left; }
.side-right { grid-area: right; }

.side {
  display: none;
  flex-direction: column;
  width: 264px;
  min-height: 0;
  padding: 8px;
  gap: 8px;
  background: var(--bg);
  overflow: hidden;
}

@media (min-width: 1080px) { .side { display: flex; } }

.side[data-collapsed="true"] { display: none; }

.side-sec {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.side-sec-grow { flex: 1; }

.side-h {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 8px;
  padding: 0 2px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.side-count {
  font-family: var(--mono);
  letter-spacing: 0;
  color: var(--accent);
}

.side-empty {
  margin: 0;
  padding: 6px 2px;
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.side-toggle {
  display: none;
  align-self: center;
  width: 14px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text-faint);
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}
.side-toggle:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }

@media (min-width: 1080px) {
  .side-toggle { display: block; }
  .app { grid-template-columns: auto auto 1fr auto auto; }
  .app {
    grid-template-areas:
      "top top top top top"
      "left lt canvas rt right";
  }
  .side-toggle-left { grid-area: lt; }
  .side-toggle-right { grid-area: rt; }
}

/* --------------------------------------------------------------- canvas */

.canvas {
  position: relative;
  min-width: 0;
  min-height: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 45%, var(--scene-fog) 0%, var(--scene-bg) 78%);
}
.canvas canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* The gesture belongs to the robot, not to the page. Left at `auto`, a
     vertical swipe on a phone scrolls down to the text under the tool instead
     of turning the model, and the orbit never gets the move at all. */
  touch-action: none;
}

.drop {
  position: absolute;
  inset: 10px;
  display: grid;
  place-items: center;
  z-index: 8;
  border: 1.5px dashed var(--accent);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--accent) 9%, rgb(10 11 13 / 0.6));
  font-size: 1rem;
}

.floating {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 6;
  width: 208px;
  padding: 8px;
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(16px);
  box-shadow: var(--shadow);
}

.floating-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2px 4px 8px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.floating-x {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-faint);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.floating-x:hover { color: var(--text); background: var(--surface-2); }

.check {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 5px;
  border-radius: 7px;
  font-size: 0.8125rem;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.check:hover { color: var(--text); background: var(--surface-2); }

.check input {
  appearance: none;
  width: 15px;
  height: 15px;
  margin: 0;
  border: 1.5px solid var(--line);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.check input:checked {
  border-color: var(--accent);
  background: var(--accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m3.5 8.3 3 3 6-6.6' fill='none' stroke='%23ffffff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-size: contain;
}
.check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.hint {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  margin: 0;
  padding: 5px 12px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  border: 1px solid var(--line-soft);
  backdrop-filter: blur(10px);
  font-size: 0.6875rem;
  color: var(--text-faint);
  white-space: nowrap;
  pointer-events: none;
}

.axes {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 78px;
  height: 78px;
  pointer-events: none;
}
.axes canvas { width: 100% !important; height: 100% !important; }

.status {
  position: absolute;
  left: 50%;
  top: 12px;
  transform: translateX(-50%);
  z-index: 7;
  max-width: min(520px, calc(100% - 240px));
  margin: 0;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(12px);
  font-size: 0.8125rem;
  color: var(--text-dim);
  text-align: center;
}
.status[data-kind="error"] { border-color: color-mix(in srgb, var(--danger) 50%, transparent); color: var(--danger); }
.status[data-kind="warn"]  { border-color: color-mix(in srgb, var(--warn) 45%, transparent); color: var(--warn); }

/* ----------------------------------------------------- tree, joints, props */

.tree, .joints, #props { overflow: auto; min-height: 0; }

.tree { font-family: var(--mono); font-size: 0.75rem; line-height: 1.6; }

.tree button {
  display: block;
  width: 100%;
  padding: 3px 6px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  text-align: left;
  white-space: pre;
  cursor: pointer;
  transition: color 110ms ease, background-color 110ms ease;
}
.tree button:hover { color: var(--text); background: var(--surface-2); }
.tree button.is-sel { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tree .is-joint { color: var(--text-faint); }

.joint-row {
  display: grid;
  grid-template-columns: 1fr 68px;
  align-items: center;
  gap: 4px 8px;
  padding: 5px 6px;
  border-radius: 7px;
  transition: background-color 110ms ease;
}
.joint-row:hover { background: var(--surface-2); }

/* A joint with no motor of its own: the row stays visible because the joint
   really does move, but nothing about it invites you to drag it. */
.joint-row[data-driven] { grid-template-columns: 1fr auto 68px; }
.joint-row[data-driven] input[type="range"] {
  grid-column: 1 / -1;
  opacity: 0.45;
  cursor: not-allowed;
}
.joint-follows {
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: transparent;
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.625rem;
  max-width: 96px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.joint-follows:hover { color: var(--accent); border-color: var(--accent); }

.joint-name {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.joint-value {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--text);
}

/* ---------------------------------------------------------------- slider
   One slider for the whole tool. This lived in two places and the timeline
   got neither, so the scrubber came out as the browser's stock blue bar in
   the middle of a dark interface. Anything that needs a slider styles it by
   being an <input type=range>; the places below only say where it sits.

   The filled part is a gradient rather than a second element, because the
   track is a pseudo-element and cannot hold one. JS sets --fill (0…1) as the
   value changes; a slider that never sets it just shows an empty track. */
input[type="range"] {
  appearance: none;
  width: 100%;
  height: 16px;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(to right,
    var(--accent) calc(var(--fill, 0) * 100%), var(--line) 0);
}
input[type="range"]::-moz-range-track {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(to right,
    var(--accent) calc(var(--fill, 0) * 100%), var(--line) 0);
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 11px;
  height: 11px;
  margin-top: -4px;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 30%, transparent);
  transition: box-shadow 140ms ease, transform 140ms ease;
}
input[type="range"]::-moz-range-thumb {
  width: 11px; height: 11px; border: 0; border-radius: 50%;
  background: var(--accent);
}
input[type="range"]:hover::-webkit-slider-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent); }
input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.15); box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 26%, transparent); }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--accent); }
input[type="range"]:disabled { cursor: default; }
input[type="range"]:disabled::-webkit-slider-thumb { background: var(--text-faint); }
input[type="range"]:disabled::-moz-range-thumb { background: var(--text-faint); }

.joint-row input[type="range"] { grid-column: 1 / -1; }

.facts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0; }
.facts div { display: flex; flex-direction: column; gap: 1px; }
.facts dt { font-size: 0.625rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); }
.facts dd { margin: 0; font-family: var(--mono); font-size: 0.8125rem; font-variant-numeric: tabular-nums; }

.prop-group { margin-bottom: 12px; }
.prop-group:last-child { margin-bottom: 0; }
.prop-h {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--accent);
  margin: 0 0 6px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.prop-row {
  display: grid;
  grid-template-columns: 74px 1fr;
  gap: 8px;
  padding: 3px 0;
  font-size: 0.75rem;
  border-bottom: 1px solid var(--line-soft);
}
.prop-row:last-child { border-bottom: 0; }
.prop-row dt { color: var(--text-faint); }
.prop-row dd {
  margin: 0;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  overflow-wrap: anywhere;
}

.viewer-seo {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}



/* Below the panel breakpoint the tool still works: panels fold away and the
   scene keeps the full width, which is what a phone visitor came for. */
/* The narrow-window layout lives at the very end of this file. Order is
   load-bearing there: three separate rules further down were quietly
   outranking it and switching the panels back on. */


/* The tool owns the first screen; the page continues below it. A viewer with
   nothing underneath has no content to rank on, and this page is aimed at a
   real search query. */
.app { height: 100dvh; }
.app + .shell-below { padding-top: 56px; }
.shell-below { max-width: 1180px; margin: 0 auto; padding-left: 20px; padding-right: 20px; }
.shell-below h1 { margin-bottom: 0.75rem; }
.shell-below .prose { margin-top: var(--section); }
.shell-below .tools { margin-top: var(--section); margin-bottom: var(--section); }
.app + .shell-below ~ .foot, .app ~ .foot { display: block; }

@media (min-width: 900px) {
  .shell-below { padding-left: 40px; padding-right: 40px; }
}

.btn-select {
  padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='m7 10 5 5 5-5' stroke='%239aa0a6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 6px center;
  background-size: 14px;
  appearance: none;
}

/* A CSS display rule outranks the hidden attribute, so anything that toggles
   via `hidden` needs this or it never disappears. Losing this line is what
   left the drop overlay permanently covering the scene. */
[hidden] { display: none !important; }

/* A 15-link robot fills the structure panel and squeezes the joints out of
   sight. Cap the tree and let the joints keep the rest — sliders are what you
   actually reach for, the tree is for orientation. */
.side-left .side-sec:first-child { max-height: 42%; }
.side-left .side-sec:first-child .tree { overflow: auto; }
.side-left .side-sec-grow { min-height: 180px; }

/* -------------------------------------------------------------- scrollbars */

* {
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  border: 2px solid transparent;
  border-radius: 8px;
  background-clip: content-box;
  background-color: var(--line);
}
*::-webkit-scrollbar-thumb:hover { background-color: color-mix(in srgb, var(--line) 50%, var(--text-faint)); }
*::-webkit-scrollbar-corner { background: transparent; }

/* ---------------------------------------------------------- draggable panel */

.floating-h {
  cursor: grab;
  user-select: none;
  touch-action: none;
}
.floating[data-dragging] { cursor: grabbing; box-shadow: var(--shadow); }
.floating[data-dragging] .floating-h { cursor: grabbing; }

/* A grip so the header reads as something you can pick up. */
.floating-h::before {
  content: "";
  width: 18px;
  height: 8px;
  margin-right: 2px;
  background-image: radial-gradient(circle, var(--text-faint) 1px, transparent 1px);
  background-size: 5px 4px;
  opacity: 0.55;
}

/* The tool page is exactly one screen: no scrollbar, no footer, nothing below
   the fold. Reading material about the viewer lives on /learn/ instead, so the
   tool stays a tool. */
html:has(.app), body:has(.app) { height: 100%; overflow: hidden; }
body:has(.app) .foot { display: none; }
body:has(.app)::before { display: none; }

/* ------------------------------------------------------------------ about

   Reading material for the tool page. It ships in the markup rather than
   being fetched on demand: a crawler runs no click handlers, so anything a
   script inserts later does not exist as far as search is concerned. */

.about {
  position: absolute;
  inset: 0;
  z-index: 20;
  overflow: auto;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(20px);
}

.about-in {
  position: relative;
  max-width: 62ch;
  margin: 0 auto;
  padding: 56px 24px 72px;
}

.about-x {
  position: absolute;
  top: 14px;
  right: 8px;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-dim);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}
.about-x:hover { color: var(--text); border-color: color-mix(in srgb, var(--line) 60%, var(--text-faint)); }

.about h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); margin-bottom: 1rem; }
.about h2 { font-size: 1.25rem; margin: 2.25rem 0 0.75rem; }
.about h3 { font-size: 0.9375rem; margin: 1.25rem 0 0.375rem; }
.about p { color: var(--text-dim); font-size: 0.9375rem; }
.about strong { color: var(--text); font-weight: 550; }
.about a { color: var(--accent); }
.about-lede { font-size: 1.0625rem; }

.about-links { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 2rem; }

.btn-icon { width: 34px; padding: 0; justify-content: center; }

/* ---------------------------------------------------------------- dropdown */

.dd { position: relative; }

.dd-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 34px;
  padding: 0 10px 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.8125rem;
  cursor: pointer;
  transition: border-color 140ms ease, background-color 140ms ease;
}
.dd-btn:hover { border-color: #33383f; background: var(--surface-2); }
.dd-btn[aria-expanded="true"] { border-color: color-mix(in srgb, var(--accent) 55%, transparent); }

.dd-caret { color: var(--text-faint); transition: transform 160ms ease; }
.dd-btn[aria-expanded="true"] .dd-caret { transform: rotate(180deg); }

.dd-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  min-width: 100%;
  margin: 0;
  padding: 5px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  max-height: 300px;
  overflow: auto;
}

.dd-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px 7px 8px;
  border-radius: 7px;
  font-size: 0.8125rem;
  color: var(--text-dim);
  white-space: nowrap;
  cursor: pointer;
  transition: color 110ms ease, background-color 110ms ease;
}
.dd-option.is-active { color: var(--text); background: var(--surface-2); }
.dd-option[aria-selected="true"] { color: var(--accent); }

.dd-tick { flex: none; opacity: 0; color: var(--accent); }
.dd-option[aria-selected="true"] .dd-tick { opacity: 1; }

/* ------------------------------------------------------- measure & loading */

.measure-out {
  position: absolute;
  left: 50%;
  bottom: 44px;
  transform: translateX(-50%);
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 8px 7px 14px;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(12px);
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}
.measure-delta { color: var(--text-faint); font-size: 0.75rem; }
.measure-x {
  width: 22px; height: 22px; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--text-faint);
  font-size: 0.9375rem; line-height: 1; cursor: pointer;
}
.measure-x:hover { color: var(--text); background: var(--surface-2); }

.loadbar {
  position: absolute;
  left: 50%;
  top: 12px;
  transform: translateX(-50%);
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 190px;
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(12px);
  overflow: hidden;
}
.loadbar-fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  transition: width 180ms ease;
}
.loadbar-text {
  position: relative;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.btn.is-on {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* -------------------------------------------------------------- findings */

.side-sec-check { max-height: 44%; }

.score {
  margin-left: auto;
  padding: 1px 7px;
  border-radius: 5px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0;
  border: 1px solid var(--line);
  color: var(--text-dim);
}
.score[data-level="good"] { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.score[data-level="warn"] { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.score[data-level="bad"]  { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }

.findings { display: flex; flex-direction: column; gap: 3px; overflow: auto; min-height: 0; }

.finding {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 8px;
  border: 0;
  border-left: 2px solid var(--line);
  border-radius: 0 6px 6px 0;
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 110ms ease;
}
.finding:hover:not(:disabled) { background: var(--surface-2); }
.finding:disabled { cursor: default; }

.finding.is-error   { border-left-color: var(--danger); }
.finding.is-warning { border-left-color: var(--warn); }
.finding.is-info    { border-left-color: var(--text-faint); }

.finding-head {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.finding-body {
  font-size: 0.6875rem;
  line-height: 1.45;
  color: var(--text-faint);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.finding.is-error .finding-head   { color: var(--danger); }
.finding.is-warning .finding-head { color: var(--warn); }

/* --------------------------------------------------------- views & search */

.views {
  position: absolute;
  right: 14px;
  bottom: 104px;
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(12px);
}
.views button {
  width: 30px;
  height: 26px;
  padding: 0;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 110ms ease, background-color 110ms ease;
}
.views button:hover { color: var(--text); background: var(--surface-2); }
.views button.is-on { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.views-sep { height: 1px; margin: 3px 3px; background: var(--line); }
.views-icon svg { display: block; }

.side-search {
  width: 100%;
  margin-bottom: 6px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
}
.side-search::placeholder { color: var(--text-faint); }
.side-search:focus-visible { outline: 1px solid var(--accent); outline-offset: 0; border-color: var(--accent); }

.side-act {
  margin-left: auto;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: transparent;
  color: var(--text-faint);
  font: inherit;
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.side-act:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }

@media (max-width: 1079px) { .views { bottom: 84px; right: 10px; } }


/* ------------------------------------------------------------------ paint */

/* Colours live in the properties panel, next to the link they apply to —
   floating over the scene they collided with the view buttons and covered
   the model. */
.paint {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line-soft);
}

.paint [data-paint]:not(.paint-reset) {
  width: 17px;
  height: 17px;
  padding: 0;
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: 50%;
  background: var(--c);
  cursor: pointer;
  transition: transform 120ms ease, border-color 120ms ease;
}
.paint [data-paint]:hover { transform: scale(1.18); border-color: rgb(255 255 255 / 0.55); }
.paint [data-paint]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.paint-reset {
  margin-left: auto;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: transparent;
  color: var(--text-faint);
  font: inherit;
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.paint-reset:hover { color: var(--text); border-color: color-mix(in srgb, var(--line) 60%, var(--text-faint)); }

/* --------------------------------------------------------- segmented tools */

/* Four modes as one control: they are mutually exclusive, so grouping them
   says that visually and takes a third of the width four buttons needed. */
.seg {
  display: inline-flex;
  align-items: center;
  padding: 2px;
  gap: 1px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.seg button {
  display: grid;
  place-items: center;
  width: 30px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.seg button:hover { color: var(--text); background: var(--surface-2); }
.seg button.is-on { color: var(--accent-ink); background: var(--accent); }
.seg button svg { display: block; }

.topbar-mid { gap: 5px; }
.topbar-mid .btn-icon { width: 32px; }
.btn-icon:disabled { opacity: 0.4; cursor: default; }
.btn-icon:disabled:hover { border-color: var(--line); background: var(--surface); }

/* Play and stop share one button; only the matching icon is shown. */
#play .i-stop { display: none; }
#play.is-playing .i-play { display: none; }
#play.is-playing .i-stop { display: block; }

/* ------------------------------------------------------------ split view */

.split-labels {
  position: absolute;
  top: 12px;
  left: 0;
  right: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: 1fr 1fr;
  pointer-events: none;
}
.split-labels span {
  justify-self: center;
  padding: 3px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 80%, transparent);
  border: 1px solid var(--line);
  backdrop-filter: blur(10px);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.split-labels span:last-child { color: var(--accent); }

/* The seam between the two viewports. */
.canvas:has(.split-labels:not([hidden]))::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
  z-index: 4;
  pointer-events: none;
}

/* --------------------------------------------------------------- ranges */

.range-row {
  display: grid;
  grid-template-columns: 62px 1fr;
  align-items: center;
  gap: 8px;
  padding: 5px 5px;
  font-size: 0.8125rem;
  color: var(--text-dim);
}
/* Layout only — the slider itself is the component above. */

/* ------------------------------------------------------------------ theme */

.theme-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}
.theme-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.theme-btn svg { display: none; }

/* Two states: show the one you are in. */
:root[data-theme="light"] .theme-btn .i-sun,
:root[data-theme="dark"] .theme-btn .i-moon { display: block; }

.rail-foot { margin-top: auto; padding: 8px 0; }

@media (max-width: 899px) {
  .rail-foot { margin: 0 0 0 auto; padding: 0 4px; }
  .theme-btn { width: 40px; height: 40px; }
}

/* In light mode the grid overlay and glass panels need less contrast, or the
   page reads as grey soup. */
:root[data-theme="light"] body::before { opacity: 0.55; }
:root[data-theme="light"] .drop { background: color-mix(in srgb, var(--accent) 10%, rgb(255 255 255 / 0.7)); }

/* ------------------------------------------------------------------ poses */

.frames { display: flex; flex-direction: column; gap: 2px; overflow: auto; min-height: 0; max-height: 22vh; }

.frame-row {
  display: grid;
  grid-template-columns: 1fr 20px 20px;
  align-items: center;
  gap: 2px;
  border-radius: 6px;
  transition: background-color 110ms ease;
}
.frame-row:hover { background: var(--surface-2); }

.frame-go {
  padding: 5px 6px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-family: var(--mono);
  font-size: 0.75rem;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.frame-go:hover { color: var(--text); }

.frame-act {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
}
.frame-act:hover { color: var(--accent); background: var(--surface); }

.frames-bar { display: flex; align-items: center; gap: 4px; margin-top: 8px; }
.frames-bar .btn { min-height: 28px; padding: 0 8px; font-size: 0.75rem; }
.frames-bar .btn-icon { width: 28px; padding: 0; }

#frames-play .i-stop { display: none; }
#frames-play.is-playing .i-play { display: none; }
#frames-play.is-playing .i-stop { display: block; }

/* ------------------------------------------------------- resizable panels */

.side-grip {
  flex: 0 0 8px;
  margin: -3px 0;
  border-radius: 4px;
  cursor: row-resize;
  position: relative;
  touch-action: none;
}
.side-grip::after {
  content: "";
  position: absolute;
  left: 24%;
  right: 24%;
  top: 50%;
  height: 2px;
  border-radius: 2px;
  background: var(--line);
  transform: translateY(-50%);
  transition: background-color 120ms ease;
}
.side-grip:hover::after,
.side-grip:focus-visible::after { background: var(--accent); }
.side[data-resizing] { user-select: none; }
.side[data-resizing] .side-grip::after { background: var(--accent); }

/* Scrolling happens inside a block, not around it.
   Each section is a flex column: heading and toolbars keep their height, the
   body takes the rest and scrolls on its own. `overflow: hidden` on the
   section is what keeps content inside the rounded card — without it a block
   squeezed by the drag handles spills its rows over whatever is underneath. */
.side-sec {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* A floor, not a ceiling. Without it flex hands the leftover to whichever
   block grows and squeezes the rest down to a heading with nothing under it —
   which is how Properties ended up 22px tall. Below this a block shows no
   content, so it is better for the column to scroll than for every block to
   become useless at once. */
.side-sec:not([data-folded]) { min-height: 108px; }
.side-sec > .side-h,
.side-sec > .side-search,
.side-sec > .paint,
.side-sec > .frames-bar { flex: 0 0 auto; }

.side-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The column itself only scrolls when even the collapsed minimums do not fit —
   a last resort on very short windows, not the normal way to reach a block.
   Deliberately no `display` here: whether a column is shown is a layout
   decision made further up, and repeating it here would silently outrank the
   narrow-window rules that turn these columns into sheets. */
.side {
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Fixed caps were fighting the drag handles: the column decides now. */
.side-left .side-sec:first-child { max-height: none; }
.side-sec-check { max-height: none; }
.frames { max-height: none; }

/* ------------------------------------------------------- collapsible blocks */

.side-fold {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
}
.side-fold:hover { color: var(--text); }
.side-fold:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.side-caret {
  flex: 0 0 auto;
  color: var(--text-faint);
  transform: rotate(90deg);
  transition: transform 160ms ease;
}
.side-sec[data-folded] .side-caret { transform: rotate(0deg); }

/* Folded: only the heading remains, and it stops competing for column space. */
.side-sec[data-folded] {
  flex: 0 0 auto !important;
  min-height: 0;
}
.side-sec[data-folded] > *:not(.side-h) { display: none; }
.side-sec[data-folded] .side-h { margin-bottom: 0; }

/* A handle next to a folded block has nothing left to resize. */
.side-grip[data-idle] { display: none; }

/* --------------------------------------------------------- custom colour */

/* The rainbow ring is what marks this swatch as "anything you like". It stays
   after a colour is chosen, because a picked blue sitting next to the preset
   blue is otherwise indistinguishable — which is exactly how the old plain
   circle read. Chosen colour fills the middle; the ring keeps saying custom. */
.paint-custom {
  position: relative;
  width: 17px;
  height: 17px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: conic-gradient(from 0.25turn, #e5484d, #ffb224, #46a758, #22d3ee, #8e8ef5, #e5484d);
  cursor: pointer;
  transition: transform 120ms ease;
}
.paint-custom::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.22);
  transition: inset 120ms ease;
}
/* Nothing picked yet: a plus, so the swatch reads as an action rather than a
   colour that happens to be the panel background. */
.paint-custom::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--text-faint);
  clip-path: polygon(46% 30%, 54% 30%, 54% 46%, 70% 46%, 70% 54%, 54% 54%, 54% 70%, 46% 70%, 46% 54%, 30% 54%, 30% 46%, 46% 46%);
}
.paint-custom[data-picked]::before { display: none; }
.paint-custom[data-picked]::after { background: var(--picked); }
.paint-custom:hover { transform: scale(1.18); }
.paint-custom:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Which colour is on the selected link. Without this the palette gives no
   feedback at all — you paint, and the row looks exactly as it did before. */
.paint [data-paint][data-on],
.paint-custom[data-on] { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--text); }
.paint-custom[data-on]::after { inset: 4px; }

/* ------------------------------------------------------------ colour picker */

.cp {
  position: fixed;
  z-index: 210;
  width: 224px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--shadow);
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transform-origin: top center;
  transition: opacity 130ms ease, transform 130ms ease;
}
.cp[data-open] { opacity: 1; transform: none; }

.cp-area {
  position: relative;
  height: 132px;
  margin-bottom: 10px;
  border-radius: 8px;
  cursor: crosshair;
  touch-action: none;
  overflow: hidden;
}
/* Saturation left-to-right, brightness top-to-bottom, over the hue underneath.
   Two overlays instead of a canvas: it scales to any size and costs nothing. */
.cp-area-white,
.cp-area-black {
  position: absolute;
  inset: 0;
}
.cp-area-white { background: linear-gradient(to right, #fff, transparent); }
.cp-area-black { background: linear-gradient(to top, #000, transparent); }
.cp-area:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.cp-dot {
  position: absolute;
  width: 13px;
  height: 13px;
  margin: -7px 0 0 -7px;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.45), 0 1px 3px rgb(0 0 0 / 0.5);
  pointer-events: none;
}

.cp-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cp-row + .cp-row { margin-top: 10px; }

.cp-eyedrop {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.cp-eyedrop:hover { color: var(--text); border-color: var(--accent); }

.cp-preview {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.25);
}

.cp-hue {
  position: relative;
  flex: 1 1 auto;
  height: 12px;
  border-radius: 6px;
  background: linear-gradient(to right, #f00, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00);
  cursor: pointer;
  touch-action: none;
}
.cp-hue:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.cp-hue-dot {
  position: absolute;
  top: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: transparent;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.45);
  pointer-events: none;
}

.cp-fields { gap: 6px; }
.cp-fields label {
  display: flex;
  flex-direction: column-reverse; /* input first visually, caption beneath */
  gap: 3px;
  min-width: 0;
}
.cp-hex { flex: 1.5 1 0; }
.cp-num { flex: 1 1 0; }
.cp-fields span {
  font-size: 0.5625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: center;
}
.cp-fields input {
  width: 100%;
  min-width: 0;
  padding: 4px 2px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-2, transparent);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.cp-fields input:focus { outline: none; border-color: var(--accent); }
/* Spinners steal width the digits need. */
.cp-fields input::-webkit-outer-spin-button,
.cp-fields input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.cp-fields input[type="number"] { appearance: textfield; }

/* ---------------------------------------------------------------- tooltip */

.tip {
  position: fixed;
  z-index: 200;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 260px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 0.75rem;
  line-height: 1.45;
  pointer-events: none;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity 120ms ease, transform 120ms ease;
}
.tip[data-show] { opacity: 1; transform: translateY(0); }
.tip[data-side="above"] { transform: translateY(2px); }
.tip[data-side="above"][data-show] { transform: translateY(0); }

.tip kbd {
  flex: none;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.6875rem;
}

/* ----------------------------------------------------------------- toasts */

.toasts {
  position: fixed;
  left: 50%;
  bottom: 20px;
  z-index: 210;
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 8px;
  transform: translateX(-50%);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(460px, calc(100vw - 32px));
  padding: 9px 10px 9px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow);
  font-size: 0.8125rem;
  color: var(--text);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(10px) scale(0.98);
  transition: opacity 180ms ease, transform 180ms ease;
}
.toast[data-in] { opacity: 1; transform: translateY(0) scale(1); }
.toast[data-out] { opacity: 0; transform: translateY(6px) scale(0.98); }

.toast-icon { flex: none; width: 17px; height: 17px; color: var(--text-faint); }
.toast-icon svg { width: 100%; height: 100%; display: block; }
.toast-text { min-width: 0; }

.toast.is-success .toast-icon { color: var(--ok); }
.toast.is-warn .toast-icon { color: var(--warn); }
.toast.is-error .toast-icon { color: var(--danger); }
.toast.is-warn { border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.toast.is-error { border-color: color-mix(in srgb, var(--danger) 45%, transparent); }

.toast-count {
  flex: none;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.6875rem;
}

.toast-action {
  flex: none;
  padding: 3px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 999px;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.toast-action:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

.toast-close {
  flex: none;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.9375rem;
  line-height: 1;
  cursor: pointer;
}
.toast-close:hover { color: var(--text); background: var(--surface-2); }

@media (max-width: 899px) {
  .toasts { bottom: 74px; }
}

/* ------------------------------------------------- narrow windows and phones

   Below this width there is no room for two columns beside the scene, so the
   panels leave the layout entirely and come back as sheets over the bottom of
   the scene. The scene keeps the whole screen, which is what a phone is good
   for: turning the robot around and looking at it. */

@media (max-width: 1079px) {
  .app { grid-template-areas: "top" "canvas"; grid-template-columns: 1fr; }
  .status { max-width: calc(100% - 24px); }

  /* The toolbar scrolls sideways rather than losing its buttons. Hiding tools
     on the platform where the menu is already smallest is the wrong trade. */
  .topbar {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    gap: 6px;
  }
  .topbar::-webkit-scrollbar { display: none; }
  .topbar-left, .topbar-mid, .topbar-right { flex: 0 0 auto; }
  .topbar-mid { margin-left: 8px; }
  /* Fingers are not cursors. Every control in the bar gets a real target —
     stated broadly on purpose, since the bar holds plain buttons, dropdown
     buttons, segmented groups and file labels, and naming them one by one is
     how three of them stayed 26px tall. */
  .topbar button,
  .topbar label.btn { min-height: 40px; min-width: 40px; }
  .views button { width: 38px; height: 34px; }

  /* Panels become sheets. */
  .side-toggle { display: none; }
  .side,
  .side[data-collapsed="true"] { display: none; }
  .side[data-sheet="open"] {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(52px + env(safe-area-inset-bottom, 0px));
    z-index: 60;
    width: auto;
    height: min(60dvh, 520px);
    padding: 10px 10px 12px;
    border-top: 1px solid var(--line);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    background: var(--bg);
    box-shadow: 0 -18px 40px rgb(0 0 0 / 0.35);
    animation: sheet-up 180ms ease;
  }
  @keyframes sheet-up { from { transform: translateY(12px); opacity: 0; } }

  .sheet-bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 61;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    border-top: 1px solid var(--line);
    background: var(--surface);
  }

  /* Scene furniture steps aside for the bar. */
  .views { bottom: calc(64px + env(safe-area-inset-bottom, 0px)); right: 10px; }
  .hint { display: none; }
  .floating { width: min(240px, calc(100vw - 24px)); }
}

/* Touch has no hover and no right button, so the desktop hint is not just
   useless there, it is wrong. */
@media (pointer: coarse) {
  .hint { display: none; }
}

/* Written as a min-width rule on purpose. A plain `display: none` here would
   sit after the narrow-window block and quietly outrank it — the same trap
   that hid the panels themselves. */
@media (min-width: 1080px) {
  .sheet-bar { display: none; }
}

.sheet-tab {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 52px;
  padding: 0 12px;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.8125rem;
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.sheet-tab + .sheet-tab { border-left: 1px solid var(--line); }
.sheet-tab[aria-expanded="true"] {
  color: var(--accent);
  background: var(--surface-2);
}
.sheet-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

/* ------------------------------------------------------------ no WebGL */

/* Shown instead of the scene when the browser cannot draw it. Placed at the
   end of the file with the other layout that must not be outranked. */
.no-webgl {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 10px;
  padding: 24px;
  text-align: center;
}
.no-webgl h2 {
  margin: 0;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  color: var(--text);
}
.no-webgl p {
  max-width: 46ch;
  margin: 0;
  color: var(--text-dim);
  font-size: 0.9375rem;
}
.no-webgl-how { color: var(--text-faint); font-size: 0.875rem; }
:root[data-no-webgl] .floating,
:root[data-no-webgl] .views,
:root[data-no-webgl] .hint,
:root[data-no-webgl] .seg,
:root[data-no-webgl] #split,
:root[data-no-webgl] #snapshot,
:root[data-no-webgl] #play { display: none; }

/* --------------------------------------------------------------- the lab */

/* Simulated time, next to the run controls. Monospaced because it changes
   sixty times a second and proportional digits would jitter. */
.sim-clock {
  margin-left: 4px;
  padding: 0 8px;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* One button, two states: running shows stop. */
.seg #sim-run .i-stop { display: none; }
.seg #sim-run.is-running .i-play { display: none; }
.seg #sim-run.is-running .i-stop { display: block; }
.seg #sim-run.is-running { color: var(--accent); }

.floating-body { padding: 10px 12px 12px; }

.range-row {
  display: grid;
  grid-template-columns: 1fr 58px;
  align-items: center;
  gap: 2px 8px;
  margin-bottom: 10px;
  font-size: 0.8125rem;
  color: var(--text-dim);
}
.range-row span { grid-column: 1; }
.range-row output {
  grid-column: 2;
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-align: right;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.range-row input[type="range"] { grid-column: 1 / -1; }

.check-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8125rem;
  color: var(--text-dim);
  cursor: pointer;
}

/* The verdict is the point of the page, so it gets the weight of a headline
   and a colour that says what happened without being read. */
.verdict { padding: 2px; }
.verdict-title {
  margin: 0 0 6px;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
.verdict-note {
  margin: 0 0 8px;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-dim);
}
.verdict[data-state="good"] .verdict-title { color: #46a758; }
.verdict[data-state="warn"] .verdict-title { color: #ffb224; }
.verdict[data-state="bad"] .verdict-title { color: #e5484d; }

.score[data-state="good"] { color: #46a758; border-color: color-mix(in srgb, #46a758 40%, transparent); }
.score[data-state="warn"] { color: #ffb224; border-color: color-mix(in srgb, #ffb224 40%, transparent); }
.score[data-state="bad"] { color: #e5484d; border-color: color-mix(in srgb, #e5484d 40%, transparent); }

.prose-small p { margin: 0 0 8px; font-size: 0.8125rem; line-height: 1.55; color: var(--text-dim); }
.prose-small strong { color: var(--text); font-weight: 600; }
/* Readings are a short list, not a layout: without this the rows spread to
   fill whatever height the column gave the block. */
#readings, #facts { align-content: start; }
.select-row {
  display: grid;
  gap: 4px;
  margin-bottom: 10px;
  font-size: 0.8125rem;
  color: var(--text-dim);
}
.select-row select {
  padding: 5px 7px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
}

/* ============================================================ the lab IDE

   Files, editor, simulation, output — the shape control work actually has.
   One grid, four regions, and a dock that can be dragged taller. */

.ide {
  position: relative;
  z-index: 1;
  display: grid;
  /* The splitters are real grid tracks rather than something floating on top:
     a handle positioned over a boundary drifts the moment anything else
     resizes, and this way the browser keeps them attached for us. */
  grid-template-rows: 48px minmax(0, 1fr) 6px minmax(90px, var(--dock, 210px));
  grid-template-columns:
    var(--files, 210px) 6px minmax(240px, var(--code, 1fr)) 6px minmax(280px, 1.05fr);
  grid-template-areas:
    "top    top    top    top   top"
    "files  splitf editor splitc sim"
    "splith splith splith splith splith"
    "dock   dock   dock   dock  dock";
  height: 100dvh;
  overflow: hidden;
  background: var(--bg);
}
.split[data-split="files"] { grid-area: splitf; }
.split[data-split="code"] { grid-area: splitc; }
.split[data-split="dock"] { grid-area: splith; }

.split {
  position: relative;
  background: var(--line);
  transition: background-color 120ms ease;
}
.split-v { cursor: col-resize; }
.split-h { cursor: row-resize; }
/* The visible line is thin; the grab area is not. Three pixels of target is a
   fight, and this is a boundary people move often. */
.split::after {
  content: "";
  position: absolute;
  inset: -4px;
}
.split:hover, .split:focus-visible, .split[data-dragging] { background: var(--accent); outline: none; }
.ide[data-resizing] { user-select: none; }
.ide[data-resizing] * { cursor: inherit; }
.ide > .topbar { grid-area: top; }
.ide-files { grid-area: files; }
.ide-editor { grid-area: editor; }
.ide-sim { grid-area: sim; }
.ide-dock { grid-area: dock; }

.ide-files, .ide-editor, .ide-sim, .ide-dock {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.ide-files { border-right: 1px solid var(--line); }
.ide-editor { border-right: 1px solid var(--line); }
.ide-dock { border-top: 1px solid var(--line); }

/* ------------------------------------------------------------ tab strips */

.ide-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  min-height: 34px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  overflow-x: auto;
  scrollbar-width: none;
}
.ide-tabs::-webkit-scrollbar { display: none; }

.ide-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 0 12px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--text-faint);
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color 120ms ease, border-color 120ms ease, background-color 120ms ease;
}
.ide-tab:hover { color: var(--text-dim); background: var(--surface-2); }
.ide-tab.is-on { color: var(--text); border-bottom-color: var(--accent); }
.ide-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

.ide-tabs-files .ide-tab { font-family: var(--mono); font-size: 0.6875rem; }

.dock-badge {
  padding: 0 5px;
  border-radius: 6px;
  background: color-mix(in srgb, #ffb224 20%, transparent);
  color: #ffb224;
  font-family: var(--mono);
  font-size: 0.625rem;
}
.dock-note {
  margin-left: auto;
  align-self: center;
  padding: 0 12px;
  color: var(--text-faint);
  font-size: 0.6875rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ------------------------------------------------------------ file tree */

.ide-tree { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 8px 6px; }
.tree-root {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  color: var(--text);
  font-size: 0.75rem;
  font-weight: 600;
}
.tree-caret { color: var(--text-faint); font-size: 0.625rem; }
.tree-list { padding-left: 12px; }
.tree-file {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 4px 7px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-align: left;
  cursor: pointer;
}
.tree-file:hover { background: var(--surface-2); color: var(--text); }
.tree-file.is-on { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); }
.tree-icon { display: grid; place-items: center; color: var(--text-faint); }
.tree-file.is-on .tree-icon { color: var(--accent); }

/* --------------------------------------------------------------- editor */

.code {
  position: relative;
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  background: var(--bg);
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.65;
}
.code-gutter {
  flex: 0 0 auto;
  width: 44px;
  padding: 10px 8px 10px 0;
  border-right: 1px solid var(--line-soft);
  color: var(--text-faint);
  text-align: right;
  user-select: none;
  overflow: hidden;
}
.code-gutter span { display: block; }

/* The painted layer and the textarea are laid out identically and stacked, so
   the caret lands exactly on the glyphs it is colouring. */
.code-scroll { position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden; }
.code-paint, .code-input {
  margin: 0;
  padding: 10px 12px;
  font: inherit;
  line-height: inherit;
  white-space: pre;
  tab-size: 2;
}
.code-paint {
  position: absolute;
  inset: 0;
  pointer-events: none;
  color: var(--text);
  overflow: hidden;
}
.code-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  resize: none;
  background: transparent;
  color: transparent;
  caret-color: var(--accent);
  overflow: auto;
}
.code-input:focus { outline: none; }
.code-input::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }

.c-key { color: #c792ea; }
.c-str { color: #8bd49c; }
.c-num { color: #f7a35c; }
.c-com { color: var(--text-faint); font-style: italic; }
.c-fn  { color: #62b6ff; }
.c-punct { color: var(--text-faint); }
:root[data-theme="light"] .c-key { color: #8250df; }
:root[data-theme="light"] .c-str { color: #0a7c42; }
:root[data-theme="light"] .c-num { color: #b35309; }
:root[data-theme="light"] .c-fn  { color: #0a58ca; }

.code-foot {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 24px;
  padding: 0 12px;
  border-top: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.625rem;
}
.code-dirty { margin-left: auto; color: #ffb224; }

/* ----------------------------------------------------------- simulation */

.ide-simbar {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 0 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.ide-simtitle {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ide-state {
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 5px;
  font-family: var(--mono);
  font-size: 0.625rem;
  color: var(--text-faint);
}
.ide-state[data-state="running"] { color: #46a758; border-color: color-mix(in srgb, #46a758 40%, transparent); }
.ide-state[data-state="error"] { color: #e5484d; border-color: color-mix(in srgb, #e5484d 40%, transparent); }
.ide-sim .canvas { flex: 1 1 auto; min-height: 0; }
.views-inline { position: static; margin-left: auto; flex-direction: row; padding: 2px; }

.btn-run { gap: 7px; color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.btn-run .i-stop { display: none; }
.btn-run.is-running .i-play { display: none; }
.btn-run.is-running .i-stop { display: block; }
.btn-run kbd {
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.5625rem;
  color: var(--text-faint);
}

/* ----------------------------------------------------------------- dock */

.dock-body { position: relative; flex: 1 1 auto; min-height: 0; }
.dock-panel { position: absolute; inset: 0; display: none; overflow: auto; }
.dock-panel.is-on { display: block; }

.console { padding: 6px 0; font-family: var(--mono); font-size: 0.6875rem; }
.con-row { display: flex; gap: 10px; padding: 2px 12px; }
.con-row:hover { background: var(--surface-2); }
.con-time { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.con-text { color: var(--text-dim); white-space: pre-wrap; }
.con-row[data-kind="error"] .con-text { color: #e5484d; }
.con-row[data-kind="warn"] .con-text { color: #ffb224; }
.con-row[data-kind="out"] .con-text { color: var(--text); }

.watch { padding: 8px 12px; display: grid; gap: 2px; align-content: start; }
.watch-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  padding: 3px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.75rem;
}
.watch-name { color: var(--text-dim); }
.watch-val { font-family: var(--mono); color: var(--text); font-variant-numeric: tabular-nums; }

.metrics { display: block; width: 100%; height: 100%; }

.diagnostics { padding: 8px 12px; display: grid; gap: 6px; align-content: start; }
.diag {
  padding: 7px 10px;
  border-left: 2px solid var(--line);
  border-radius: 0 6px 6px 0;
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.75rem;
  line-height: 1.5;
}
.diag[data-kind="warn"] { border-left-color: #ffb224; }

@media (max-width: 1279px) {
  .ide {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "top    top"
      "editor sim"
      "dock   dock";
  }
  .ide-files { display: none; }
}
@media (max-width: 900px) {
  .ide {
    grid-template-rows: 48px minmax(220px, 40dvh) minmax(0, 1fr) minmax(120px, 180px);
    grid-template-columns: 1fr;
    grid-template-areas: "top" "sim" "editor" "dock";
  }
  .ide-editor { border-right: 0; border-top: 1px solid var(--line); }
}

/* --------------------------------------------------------- tool switch */

/* The one control that moves you between tools. Same place on every page,
   because hunting for it in a different corner each time is the thing it
   exists to stop. */
/* Named for what it is, not for what it holds: `.tools` was already taken by
   the cards on the marketing pages, and inheriting their 96px top margin
   dropped this out of the bar and onto the panel below it. */
.tool-switch {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--bg);
}
.tool {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border-radius: 7px;
  color: var(--text-faint);
  font-size: 0.75rem;
  text-decoration: none;
  white-space: nowrap;
  transition: color 120ms ease, background-color 120ms ease;
}
.tool:hover { color: var(--text); background: var(--surface-2); }
.tool.is-on { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tool-icon { display: grid; place-items: center; }
.tool-icon svg { width: 15px; height: 15px; }

/* On a narrow bar the labels go and the icons stay: the shape is recognisable
   enough, and the title still says what each one is. */
@media (max-width: 1100px) {
  .tool-label { display: none; }
  .tool { padding: 4px 7px; }
}

/* Findings carry a severity and a first-seen time: the moment something went
   wrong is usually more useful than the fact that it did. */
.diag-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 3px;
  color: var(--text);
  font-size: 0.8125rem;
  font-weight: 600;
}
.diag-when {
  margin-left: auto;
  flex: 0 0 auto;
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 400;
}
.diag-detail { margin: 0; font-size: 0.75rem; line-height: 1.5; color: var(--text-dim); }
.diag[data-kind="bad"] { border-left-color: #e5484d; }
.diag[data-kind="bad"] .diag-title { color: #e5484d; }
.dock-badge[data-kind="bad"] { background: color-mix(in srgb, #e5484d 20%, transparent); color: #e5484d; }

/* ------------------------------------------------------------- the bench */

.tests { display: flex; flex-direction: column; height: 100%; }
.tests-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.tests-note { color: var(--text-faint); font-size: 0.6875rem; }
.tests-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 8px 12px; display: grid; gap: 6px; align-content: start; }

.test {
  padding: 8px 11px;
  border-left: 2px solid var(--line);
  border-radius: 0 7px 7px 0;
  background: var(--surface);
}
.test[data-state="pass"] { border-left-color: #46a758; }
.test[data-state="fail"] { border-left-color: #e5484d; }
.test-head { display: flex; align-items: baseline; gap: 10px; }
.test-name { font-size: 0.8125rem; font-weight: 600; color: var(--text); }
.test-value {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.test-mark {
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 5px;
  font-family: var(--mono);
  font-size: 0.625rem;
  text-transform: uppercase;
  color: var(--text-faint);
}
.test[data-state="pass"] .test-mark { color: #46a758; border-color: color-mix(in srgb, #46a758 40%, transparent); }
.test[data-state="fail"] .test-mark { color: #e5484d; border-color: color-mix(in srgb, #e5484d 40%, transparent); }
.test-asks { margin: 4px 0 0; font-size: 0.75rem; color: var(--text-faint); }
.test-note { margin: 3px 0 0; font-size: 0.75rem; line-height: 1.5; color: var(--text-dim); }
.dock-badge[data-kind="good"] { background: color-mix(in srgb, #46a758 20%, transparent); color: #46a758; }
.ide-hint { color: var(--text-faint); font-size: 0.6875rem; white-space: nowrap; }
@media (max-width: 1400px) { .ide-hint { display: none; } }

/* ------------------------------------------------------------- timeline */

/* Under the scene, above the dock: the run itself, scrubbable. Watching a
   robot fall tells you it fell; going back half a second tells you why. */
.timeline {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 7px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.time-btn {
  display: grid;
  place-items: center;
  width: 26px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
}
.time-btn:hover { color: var(--text); border-color: var(--accent); }
/* The bar carries its own caption. A slider with nothing under it is a slider
   nobody can name: it read as a progress bar sitting at 100%, when what it
   actually holds is the last few seconds of the run. */
.time-track {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 0;
}
.time-scrub { width: 100%; }
.time-scrub:disabled { opacity: 0.45; }
.time-hint {
  margin-top: -3px;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.time-at {
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 52px;
  text-align: right;
}
/* The speed picker is the shared dropdown, shrunk to fit a 34px strip. */
.time-speed .dd-btn {
  min-width: 0;
  padding: 2px 6px;
  font-family: var(--mono);
  font-size: 0.625rem;
}
.time-speed .dd-label { min-width: 26px; text-align: left; }
.time-speed .dd-list { min-width: 82px; right: 0; left: auto; }
.time-speed .dd-option { padding: 5px 9px; font-family: var(--mono); font-size: 0.6875rem; }
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------- conversion pages
   New classes only — nothing here overrides an existing rule, and the
   narrow-screen rules sit inside this block at the end of the file, where
   nothing below can quietly win by being later. */

.crumbs {
  margin-top: 28px;
  font-size: 0.8125rem;
  color: var(--text-faint);
}
.crumbs a { color: var(--text-dim); text-decoration: none; }
.crumbs a:hover { color: var(--accent); }
.crumbs span { margin: 0 6px; }

.hero-tight { margin-top: 20px; }

/* The converter itself. */
.conv { margin-top: var(--section); }
.conv-box {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  overflow: hidden;
}
.conv-drop {
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 44px 24px;
  border-bottom: 1px solid transparent;
  text-align: center;
  transition: background-color 160ms ease, border-color 160ms ease;
}
.conv-drop.is-over {
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
  border-color: var(--accent);
}
.conv-drop-h { margin: 0; font-size: 1.125rem; font-weight: 600; letter-spacing: -0.02em; }
.conv-drop-p { margin: 0; max-width: 46ch; color: var(--text-dim); font-size: 0.9375rem; }
.conv-drop-note { margin: 6px 0 0; color: var(--text-faint); font-size: 0.75rem; }

.conv-status {
  margin: 0;
  padding: 12px 20px;
  border-top: 1px solid var(--line);
  color: var(--text-dim);
  font-size: 0.875rem;
}
.conv-status[data-kind="error"] { color: #f87171; }

.conv-out { border-top: 1px solid var(--line); }
.conv-out-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 20px;
}
.conv-name { margin: 0; font-family: var(--mono); font-size: 0.9375rem; color: var(--text); }
.conv-stats { margin: 3px 0 0; font-size: 0.8125rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }

.conv-reports {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
}
.conv-report { padding: 16px 20px; background: var(--surface); }
.conv-report h3 {
  margin: 0 0 10px;
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.conv-report ul { margin: 0; padding-left: 18px; }
.conv-report li { margin-bottom: 7px; font-size: 0.875rem; line-height: 1.5; color: var(--text-dim); }
.conv-none { margin: 0; font-size: 0.875rem; color: var(--text-faint); }

.conv-peek { border-top: 1px solid var(--line); }
.conv-peek summary {
  padding: 12px 20px;
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--text-dim);
}
.conv-peek summary:hover { color: var(--text); }
.conv-preview {
  margin: 0;
  padding: 0 20px 18px;
  max-height: 380px;
  overflow: auto;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--text-dim);
  white-space: pre;
}

/* What the conversion costs, and where else to go. */
.loss-list { margin: 0 0 8px; padding-left: 20px; }
.loss-list li { margin-bottom: 10px; color: var(--text-dim); line-height: 1.6; }

.fmt-pair { margin: 0; }
.fmt-pair dt {
  margin-top: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
.fmt-pair dt:first-child { margin-top: 0; }
.fmt-pair dd { margin: 4px 0 0; color: var(--text-dim); line-height: 1.6; }
.fmt-ext {
  margin-left: 6px;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--text-faint);
}

.pair-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pair-card {
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
}
.pair-card h3 { margin: 0 0 6px; font-size: 1rem; letter-spacing: -0.01em; }
.pair-card h3 a { color: var(--text); text-decoration: none; }
.pair-card h3 a:hover { color: var(--accent); }
.pair-card p { margin: 0; font-size: 0.8125rem; color: var(--text-faint); }

.pair-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pair-list a { font-size: 0.9375rem; }

@media (max-width: 720px) {
  .conv-reports { grid-template-columns: 1fr; }
  .conv-drop { padding: 32px 16px; }
  .conv-out-head { align-items: flex-start; }
}
