/* play.css — the catalog grid, the feature-page skeleton, and the ONE shared knob panel.
 * The knob panel is the affordability trick: 31 features, one editor chrome, rendered from the registry.
 */

/* ============================ CATALOG ============================ */
.hero { padding: 62px 0 30px; }
.hero h1 { max-width: 15ch; }
.hero .lede { max-width: 62ch; margin-top: 18px; }
.hero-stats { display: flex; gap: 26px; flex-wrap: wrap; margin-top: 26px; }
.hero-stats div { font-size: 0.83rem; color: var(--ink-3); }
.hero-stats b { display: block; font-size: 1.5rem; color: var(--ink); font-weight: 660; letter-spacing: -0.02em; line-height: 1.2; }

.filters {
  position: sticky; top: 58px; z-index: 50; margin: 26px 0 8px; padding: 12px 0;
  background: linear-gradient(rgba(238,241,251,.86), rgba(238,241,251,.62) 70%, rgba(238,241,251,0));
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.filter-row { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.chip {
  font-family: var(--sans); font-size: 0.8rem; font-weight: 560; cursor: pointer;
  border: 1px solid var(--line); background: rgba(255,255,255,.72); color: var(--ink-2);
  padding: 6px 12px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px;
}
.chip:hover { border-color: rgba(22,24,31,.26); color: var(--ink); }
.chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip .n { font-family: var(--mono); font-size: 0.72em; opacity: .6; }
.chip[aria-pressed="true"] .n { opacity: .78; }
html:not(.js) .filters { display: none; }  /* filters are a JS affordance; without JS every card is shown */

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; margin-top: 12px; align-items: start; }

/* the pointer-lit grid IS feature 8.1, used on its own catalog. one listener on the container. */
.grid { --gx: 50%; --gy: -300px; }
.fcard {
  position: relative; display: flex; flex-direction: column; text-decoration: none; color: inherit;
  background: var(--card); border: 1px solid var(--line-2); border-radius: var(--radius);
  backdrop-filter: blur(10px) saturate(130%); -webkit-backdrop-filter: blur(10px) saturate(130%);
  box-shadow: var(--shadow); overflow: hidden; isolation: isolate;
  transition: transform .22s cubic-bezier(.2,.7,.3,1), box-shadow .22s;
}
.fcard::before {   /* the torch: a radial mask lighting the border near the pointer */
  content: ''; position: absolute; inset: -1px; border-radius: inherit; z-index: 2; pointer-events: none;
  padding: 1px; opacity: 0; transition: opacity .3s;
  background: radial-gradient(240px circle at var(--gx) var(--gy), rgba(63,79,216,.85), rgba(180,64,138,.5) 40%, transparent 68%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
.grid:hover .fcard::before { opacity: 1; }
.fcard:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
html:not(.anim) .fcard { transition: none; }
html:not(.anim) .fcard:hover { transform: none; }

.fcard-top { padding: 16px 17px 0; display: flex; align-items: center; gap: 8px; }
.fcard-num { font-family: var(--mono); font-size: 0.7rem; color: var(--ink-3); }
.fcard-body { padding: 9px 17px 17px; flex: 1; display: flex; flex-direction: column; }
.fcard h3 { font-size: 1.03rem; letter-spacing: -0.022em; margin-bottom: 5px; }
/* the naming trick, made structural: .is is what it IS, .like is what it's LIKE */
.fcard .like { font-size: 0.88rem; color: var(--ink-2); line-height: 1.5; flex: 1; }
.fcard-foot { display: flex; gap: 6px; align-items: center; margin-top: 13px; flex-wrap: wrap; }
.fcard .knobcount { font-family: var(--mono); font-size: 0.68rem; color: var(--ink-3); margin-left: auto; }

/* preview tile: LIVE on the few cards that declare it (gradientlab runs 4-6, never all 19);
 * STATIC (a real screenshot of the feature's own stage, taken by thumbs.cjs) on every other card,
 * so the catalog SHOWS each feature instead of describing it. Images work without JS — the static
 * tiles are part of the floor, not gated behind it. */
.fcard-prev { position: relative; height: 132px; min-height: 0; margin: 13px 0 0; border-top: 1px solid var(--line-2); background: #f4f5fa; overflow: hidden; }
.fcard-prev canvas, .fcard-prev .prev-dom { position: absolute; inset: 0; width: 100%; height: 100%; }
.fcard-prev.static img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 32%; }
.fcard-prev .prev-badge {
  position: absolute; right: 7px; bottom: 6px; z-index: 3; font-family: var(--mono); font-size: 0.6rem;
  letter-spacing: .06em; text-transform: uppercase; color: rgba(255,255,255,.9); background: rgba(22,24,31,.4);
  padding: 2px 6px; border-radius: 4px; backdrop-filter: blur(4px);
}
html:not(.js) .fcard-prev:not(.static) { display: none; }  /* live tiles need JS; static screenshots do not */

.cat-head { margin: 42px 0 4px; display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.cat-head h2 { letter-spacing: -0.028em; }
.cat-head .blurb { color: var(--ink-3); font-size: 0.9rem; }
.cat-sec[hidden] { display: none; }

/* ============================ FEATURE PAGE ============================ */
.feat-nav {
  position: sticky; top: 58px; z-index: 50; padding: 9px 0;
  background: linear-gradient(rgba(238,241,251,.88), rgba(238,241,251,.55) 72%, rgba(238,241,251,0));
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid transparent;
}
.feat-nav .wrap { display: flex; gap: 3px; align-items: center; overflow-x: auto; scrollbar-width: none; }
.feat-nav .wrap::-webkit-scrollbar { display: none; }
.feat-nav a {
  font-size: 0.78rem; color: var(--ink-3); text-decoration: none; padding: 5px 11px; border-radius: 7px;
  white-space: nowrap; font-weight: 540;
}
.feat-nav a:hover { background: rgba(22,24,31,.06); color: var(--ink); }
.feat-nav a.on { background: rgba(63,79,216,.1); color: var(--accent); }

.feat-hero { padding: 44px 0 6px; }
.feat-hero h1 { max-width: 17ch; }
.feat-like { font-size: 1.2rem; color: var(--ink-2); max-width: 56ch; margin-top: 16px; font-style: italic; }
.feat-sub { font-size: 1rem; color: var(--ink-3); max-width: 62ch; margin-top: 12px; }

section.fs { padding: 46px 0 0; scroll-margin-top: 108px; }
section.fs > .wrap > h2 { margin-bottom: 14px; }
.prose { max-width: 68ch; }
.prose p { color: var(--ink-2); }

/* ---- THE ONE SHARED EDITOR: stage + knob rail ------------------------------------------------------ */
.editor {
  display: grid; grid-template-columns: 1fr 296px; gap: 0; margin-top: 8px;
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  background: var(--card-solid); box-shadow: var(--shadow-lg);
}
.stage {
  position: relative; min-height: clamp(440px, 60vh, 660px); background: #f4f5fa; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* the no-WebGL / no-JS poster. A real field, never a blank rectangle. */
.stage .poster {
  position: absolute; inset: 0; z-index: 0;
  background:
    radial-gradient(900px 760px at 22% 18%, rgba(122,178,235,.75), transparent 72%),
    radial-gradient(820px 700px at 78% 30%, rgba(126,124,220,.7), transparent 72%),
    radial-gradient(760px 660px at 38% 74%, rgba(178,120,206,.62), transparent 72%),
    radial-gradient(700px 620px at 84% 82%, rgba(226,140,110,.5), transparent 72%),
    linear-gradient(160deg, #cfe0f4, #d6d3f0 46%, #f0dbe4);
}
html.js .stage.has-live .poster { display: none; }
.stage-scroll { position: absolute; inset: 0; overflow-y: auto; overscroll-behavior: contain; z-index: 2; }
.stage-note {
  position: absolute; left: 12px; bottom: 11px; z-index: 8; font-family: var(--mono); font-size: 0.66rem;
  color: rgba(255,255,255,.92); background: rgba(22,24,31,.44); padding: 3px 8px; border-radius: 5px;
  backdrop-filter: blur(4px); pointer-events: none; max-width: calc(100% - 24px);
}
.stage-note.warn { background: rgba(156,68,21,.9); }

/* the expand control + the knob HUD: both live ON the stage so your eyes never leave the picture */
.stage-expand {
  position: absolute; right: 10px; top: 10px; z-index: 9; cursor: pointer;
  font-family: var(--mono); font-size: 0.66rem; letter-spacing: .05em; text-transform: uppercase;
  color: rgba(255,255,255,.92); background: rgba(22,24,31,.44); border: 1px solid rgba(255,255,255,.18);
  padding: 4px 9px; border-radius: 6px; backdrop-filter: blur(4px);
}
.stage-expand:hover { background: rgba(22,24,31,.66); }
html:not(.js) .stage-expand { display: none; }
.stage-hud {
  position: absolute; left: 12px; top: 11px; z-index: 8; pointer-events: none;
  font-family: var(--mono); font-size: 0.78rem; font-weight: 600; font-variant-numeric: tabular-nums;
  color: #fff; background: rgba(22,24,31,.55); padding: 5px 10px; border-radius: 7px;
  backdrop-filter: blur(4px); opacity: 0; transition: opacity .25s;
}
/* appears INSTANTLY (transition:none), fades only on the way out. A HUD that needs an animation
   frame to become visible never shows up on a struggling renderer — feedback must not be deferrable. */
.stage-hud.on { opacity: 1; transition: none; }
html:not(.anim) .stage-hud { transition: none; }

/* fullscreen: the WHOLE editor goes up (stage grows huge, the knob rail comes along and keeps working) */
.editor:fullscreen { border-radius: 0; border: 0; }
.editor:fullscreen .stage { min-height: 100%; }
.editor.theater { position: fixed; inset: 0; z-index: 200; border-radius: 0; margin: 0; }
.editor.theater .stage { min-height: 100%; }

.knobs { border-left: 1px solid var(--line); background: #fbfbfd; display: flex; flex-direction: column; }
.knobs-head {
  display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--line-2);
}
.knobs-head h3 { font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3); font-weight: 680; }
.btn-reset {
  margin-left: auto; font-family: var(--sans); font-size: 0.73rem; font-weight: 580; cursor: pointer;
  border: 1px solid var(--line); background: #fff; color: var(--ink-2); padding: 4px 10px; border-radius: 7px;
}
.btn-reset:hover { border-color: var(--ink-3); color: var(--ink); }
.knobs-body { padding: 6px 14px 14px; overflow-y: auto; flex: 1; }
html:not(.js) .knobs { display: none; }        /* knobs are JS. Without JS the stage shows the static poster. */
html:not(.js) .editor { grid-template-columns: 1fr; }

.knob { padding: 13px 0; border-bottom: 1px solid var(--line-2); }
.knob:last-child { border-bottom: 0; }
.knob-top { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.knob-label { font-size: 0.82rem; font-weight: 600; color: var(--ink); font-family: var(--mono); letter-spacing: -0.01em; }
.knob-val {
  margin-left: auto; font-family: var(--mono); font-size: 0.76rem; color: var(--accent);
  background: rgba(63,79,216,.09); padding: 1px 6px; border-radius: 5px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.knob-src { font-family: var(--mono); font-size: 0.58rem; letter-spacing: .05em; text-transform: uppercase; padding: 1px 5px; border-radius: 4px; }
.knob-src.sourced { background: rgba(31,122,77,.13); color: #14603a; }
.knob-src.ours { background: rgba(63,79,216,.1); color: #2f3aa8; }
.knob-teach { font-size: 0.74rem; color: var(--ink-3); line-height: 1.45; margin-top: 7px; }

input[type=range] {
  width: 100%; -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; height: 18px; margin: 0;
}
input[type=range]::-webkit-slider-runnable-track { height: 4px; border-radius: 3px; background: rgba(22,24,31,.14); }
input[type=range]::-moz-range-track { height: 4px; border-radius: 3px; background: rgba(22,24,31,.14); }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 15px; height: 15px; border-radius: 50%; background: var(--accent);
  border: 2.5px solid #fff; box-shadow: 0 1px 4px rgba(22,24,31,.3); margin-top: -5.5px;
}
input[type=range]::-moz-range-thumb {
  width: 15px; height: 15px; border-radius: 50%; background: var(--accent); border: 2.5px solid #fff;
  box-shadow: 0 1px 4px rgba(22,24,31,.3);
}
select, input[type=text] {
  width: 100%; font-family: var(--mono); font-size: 0.8rem; padding: 6px 9px; border-radius: 7px;
  border: 1px solid var(--line); background: #fff; color: var(--ink);
}
select:hover, input[type=text]:hover { border-color: var(--ink-3); }
.knob-toggle { display: flex; align-items: center; gap: 9px; cursor: pointer; }
.knob-toggle input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }

/* ---- evidence + spec blocks ------------------------------------------------------------------------ */
.spec { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1px; background: var(--line-2); border: 1px solid var(--line-2); border-radius: var(--radius); overflow: hidden; }
.spec > div { background: rgba(255,255,255,.8); padding: 15px 17px; }
.spec > div.wide { grid-column: 1 / -1; }
.spec dt { font-size: 0.68rem; font-weight: 680; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px; }
.spec dd { margin: 0; font-size: 0.87rem; color: var(--ink-2); line-height: 1.5; }
.ev-detail { font-size: 0.84rem; color: var(--ink-2); line-height: 1.55; margin-top: 10px; }
.caveat {
  border-left: 3px solid var(--warm); background: rgba(255,247,242,.75); padding: 13px 16px;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; font-size: 0.87rem; color: #7d3a13; margin-top: 16px;
}
.caveat b { color: #5f2c0e; }
.builder {
  border-left: 3px solid var(--accent); background: rgba(247,248,255,.8); padding: 15px 18px;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; font-size: 0.94rem; color: var(--ink-2); max-width: 68ch;
}

.prevnext { display: flex; gap: 12px; margin-top: 54px; }
.prevnext a {
  flex: 1; text-decoration: none; color: inherit; padding: 14px 17px; border-radius: var(--radius);
  border: 1px solid var(--line-2); background: rgba(255,255,255,.7);
}
.prevnext a:hover { border-color: rgba(22,24,31,.24); background: #fff; }
.prevnext .dir { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3); font-weight: 680; }
.prevnext .nm { font-weight: 620; letter-spacing: -0.02em; margin-top: 3px; }
.prevnext .r { text-align: right; }

/* ---- the knob table on the page (a second, prose-side rendering of the same registry rows) --------- */
.knob-table-wrap { padding: 6px 20px 10px; margin-top: 16px; overflow-x: auto; }
.knob-table { width: 100%; border-collapse: collapse; font-size: 0.87rem; min-width: 520px; }
.knob-table th {
  text-align: left; color: var(--ink-3); font-size: 0.7rem; text-transform: uppercase; letter-spacing: .08em;
  padding: 12px 10px 8px 0;
}
.knob-table th:last-child { padding-right: 0; }
/* No td padding and .9em on the name cell: these two lines are what the inline styles + the `mono` class
   used to produce, restated verbatim so lifting the table out of build.mjs changed nothing on desktop. */
.knob-table .kt-name { font-family: var(--mono); font-size: 0.9em; }
.knob-note { font-size: 0.78rem; color: var(--ink-3); margin: 10px 0 8px; }

@media (max-width: 900px) {
  .editor { grid-template-columns: 1fr; }
  .knobs { border-left: 0; border-top: 1px solid var(--line); }
  /* WAS max-height: 340px. On a phone that was a 340px window onto 579px of knobs: 239px of the panel
     was behind a nested scroll with no affordance, on the one surface this whole tool exists for.
     A phone has vertical room; spend it. The bounded rail comes back in theater + landscape below. */
  .knobs-body { max-height: none; overflow-y: visible; }
  .stage { min-height: 340px; }
  .filters, .feat-nav { top: 54px; }
}
@media (max-width: 620px) {
  .hero { padding: 36px 0 18px; }
  .filters { padding: 8px 0; }
  .filter-row {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    scroll-snap-type: x proximity; padding-bottom: 2px;
  }
  .filter-row::-webkit-scrollbar { display: none; }
  .chip { flex: none; scroll-snap-align: start; }
  .grid { grid-template-columns: 1fr; }
  .prevnext { flex-direction: column; }
  .prevnext .r { text-align: left; }
  .stage { min-height: clamp(300px, 44vh, 400px); }
  .feat-like { font-size: 1.06rem; }
}

/* ============================ MOBILE (2026-07-21) ====================================================
 * Measured with kit/mobile-audit-url.cjs at 320/390/414 against the SERVED playground, then looked at.
 * Desktop rendering is untouched: every rule below is inside a media query.
 *
 * What the harness caught: OVERLAP on every feature page (the knob rail's laid-out rows colliding with
 * the section beneath it), sub-44px targets on every control, sub-12px type across the chrome.
 * What only the EYE and a geometry probe caught, and which matters more:
 *   - The knob rail was silently truncated (340px box, 579px of content). Knobs 4-of-4 were unreachable
 *     unless you discovered an undiscoverable nested scroll.
 *   - "Fullscreen" made the demo SMALLER on a phone. Measured in theater mode: stage 226px at 390x844,
 *     124px at 320x640, and 0px in landscape. The button that exists to make the feature bigger was
 *     deleting it. Nothing in the harness looks at a mode you have to click into.
 *   - The stage's inner scroller used overscroll-behavior: contain, which on a touch screen means a thumb
 *     landing on the stage can drive the demo but can never scroll past it. The stage is 92% of the
 *     screen width, so the escape route was a 16px gutter.
 * ==================================================================================================== */
@media (max-width: 900px) {
  /* --- tap targets: 44px floor (WCAG 2.5.5 / Apple HIG). A range input is the hard case: the visible
     track stays thin, the HIT AREA is what grows, and the thumb grows with it because a 15px dot is
     not something a thumb can catch on a moving slider. --- */
  .feat-nav .wrap { gap: 2px; }
  .feat-nav a { min-height: 44px; display: inline-flex; align-items: center; padding: 0 12px; }
  .chip { min-height: 44px; padding: 0 14px; }
  .btn-reset { min-height: 44px; padding: 0 15px; }
  .stage-expand {
    min-height: 44px; min-width: 44px; padding: 0 14px; right: 8px; top: 8px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  input[type=range] { height: 44px; }
  input[type=range]::-webkit-slider-runnable-track { height: 6px; }
  input[type=range]::-moz-range-track { height: 6px; }
  input[type=range]::-webkit-slider-thumb { width: 26px; height: 26px; border-width: 3px; margin-top: -10px; }
  input[type=range]::-moz-range-thumb { width: 26px; height: 26px; border-width: 3px; }
  select, input[type=text] { min-height: 44px; font-size: 0.88rem; }
  /* The checkbox: a native control ignores padding, so a 44px hit area cannot be bolted onto one. The
     input becomes a transparent 44x44 target and the box you SEE is drawn at 24px inside it. Mobile
     only; the desktop rendering keeps the native checkbox. */
  .knob-toggle { min-height: 44px; }
  .knob-toggle input[type=checkbox] {
    -webkit-appearance: none; appearance: none; position: relative; margin: 0 -10px;
    width: 44px; height: 44px; border: 0; background: none;
  }
  .knob-toggle input[type=checkbox]::before {
    content: ''; position: absolute; left: 10px; top: 10px; width: 24px; height: 24px; box-sizing: border-box;
    border: 2px solid var(--ink-3); border-radius: 6px; background: #fff;
  }
  .knob-toggle input[type=checkbox]:checked::before { background: var(--accent); border-color: var(--accent); }
  .knob-toggle input[type=checkbox]:checked::after {
    content: ''; position: absolute; left: 19px; top: 15px; width: 6px; height: 12px; box-sizing: border-box;
    border: solid #fff; border-width: 0 2.5px 2.5px 0; transform: rotate(45deg);
  }

  /* --- type floor: nothing readable-critical renders below 12px --- */
  .fcard-num { font-size: 0.76rem; }
  .fcard .knobcount { font-size: 0.76rem; }
  .fcard-prev .prev-badge { font-size: 0.78rem; }
  .knobs-head h3 { font-size: 0.78rem; }
  .btn-reset { font-size: 0.82rem; }
  .knob-src { font-size: 0.78rem; padding: 2px 6px; }
  .knob-teach { font-size: 0.82rem; }
  /* "44 px" broke across two lines in the narrow landscape rail. A value readout must never wrap. */
  .knob-val { white-space: nowrap; }
  .stage-note { font-size: 0.76rem; padding: 4px 9px; }
  .stage-expand { font-size: 0.76rem; }
  .stage-hud { font-size: 0.84rem; }
  .prevnext .dir { font-size: 0.76rem; }
  .spec dt { font-size: 0.76rem; }
  .knob-table { font-size: 0.9rem; }
  .knob-table th { font-size: 0.78rem; }
  .knob-note { font-size: 0.82rem; }
  .site-foot a { display: inline-flex; align-items: center; min-height: 44px; }

  /* The stage's OWN instruments. These are set by the stylesheet dom-features.js injects at runtime,
     which lands after this file, so each needs .stage in front of it to win on specificity rather than
     on order. They are readouts, not decoration: a HUD you have to pinch to read is not a HUD. */
  .stage .vel-hud, .stage .fq-hud, .stage .pr-hud { font-size: 0.78rem; }
  .stage .scr-read, .stage .mq-pause, .stage .cl-tab, .stage .sg-cap i { font-size: 0.8rem; }
  .stage .cl-tab caption, .stage .cl-tab th, .stage .sp-card b, .stage .sg-line span,
  .stage .ring-card.gap { font-size: 0.78rem; }
  /* the stage's own BUTTONS are real targets, not chrome: the marquee pause (WCAG 2.2.2 is the reason
     it exists at all), the odometer's trigger, the cursor-feature link tiles. */
  .stage .mq-pause, .stage .odo-btn, .stage .cur-link {
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  }
  .stage .mq-pause { font-size: 0.78rem; padding-left: 14px; padding-right: 14px; }

  /* --- the knob table stops being a 520px-wide table and becomes one block per knob. No row is
     dropped and no cell is dropped; only the column headings go, and the stacked row says the same
     thing without them (bold name, then the sourced/ours chip, then what it teaches). --- */
  .knob-table-wrap { overflow-x: visible; padding: 4px 16px 10px; }
  .knob-table { min-width: 0; display: block; }
  .knob-table thead { display: none; }
  .knob-table tbody, .knob-table tr, .knob-table td { display: block; width: auto; }
  .knob-table tr { padding: 13px 0; border-bottom: 1px solid var(--line-2); }
  .knob-table tr:last-child { border-bottom: 0; }
  .knob-table td { padding: 0 0 5px; vertical-align: baseline; }
  .knob-table td:last-child { padding-bottom: 0; color: var(--ink-2); }
  .knob-table .kt-name { font-size: 1em; }
}

/* --- the horizontal rails (category chips, section nav) get a right-edge fade, because a scroller with
   no visible edge reads as a truncated list rather than as something you can push. --- */
@media (max-width: 620px) {
  .filters, .feat-nav { position: sticky; }
  .filters::after, .feat-nav::after {
    content: ''; position: absolute; right: 0; top: 0; bottom: 0; width: 30px; pointer-events: none;
    background: linear-gradient(90deg, rgba(238,241,251,0), rgba(238,241,251,.92));
  }
  .knob-table-wrap { padding-left: 14px; padding-right: 14px; }
}

/* --- touch: release the stage's scroll trap. overscroll-behavior: contain is right on a desktop
   (a wheel over the stage should drive the demo, not the page); on a thumb it means the page cannot be
   scrolled past a stage that fills the screen. auto still drives the demo first and chains only once
   the demo's own track is exhausted, which is the behaviour a finger expects. --- */
@media (pointer: coarse) {
  .stage-scroll { overscroll-behavior: auto; }
}

/* --- FULLSCREEN / THEATER on a phone. The desktop assumption (a wide stage with a rail beside it) is
   what collapsed the stage to 124px here. Stack it, give the stage every pixel the knobs do not need,
   and give the rail its bounded scroll back because in this mode the page behind it cannot scroll. --- */
@media (max-width: 900px) {
  .editor:fullscreen, .editor.theater { display: flex; flex-direction: column; }
  .editor:fullscreen .stage, .editor.theater .stage { flex: 1 1 auto; min-height: 46vh; }
  .editor:fullscreen .knobs, .editor.theater .knobs { flex: 0 1 auto; min-height: 0; max-height: 46vh; }
  .editor:fullscreen .knobs-body, .editor.theater .knobs-body {
    flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; -webkit-overflow-scrolling: touch;
  }
}

/* --- LANDSCAPE. A 390px-tall window cannot stack a stage on top of a knob rail and leave either of
   them usable, so at that height the editor goes back to two columns and the stage keeps its height by
   sitting BESIDE the rail rather than above it.
   The rail is NOT bounded here. Capping it at 78vh was the first thing tried and it rebuilt, in
   landscape, exactly the nested 340px scroll trap this pass removed from portrait: 500px of knobs in a
   304px box on a touch screen. Unbounded, the editor is simply taller than the viewport and the PAGE
   scrolls, which is what a thumb already knows how to do. The one place a bounded rail is correct is
   theater mode, below, where there is no page behind it to scroll. --- */
@media (orientation: landscape) and (max-height: 520px) {
  .filters, .feat-nav { top: 52px; }
  section.fs { padding-top: 30px; scroll-margin-top: 96px; }
  .feat-hero { padding: 26px 0 4px; }
  .stage { min-height: 78vh; }
}
@media (orientation: landscape) and (max-height: 520px) and (min-width: 620px) {
  .editor { grid-template-columns: 1fr minmax(240px, 36%); }
  .knobs { border-left: 1px solid var(--line); border-top: 0; }
  .knobs-body { max-height: none; overflow-y: visible; }
  .editor:fullscreen, .editor.theater { display: grid; grid-template-columns: 1fr minmax(240px, 36%); }
  .editor:fullscreen .stage, .editor.theater .stage { min-height: 100%; }
  .editor:fullscreen .knobs, .editor.theater .knobs { max-height: 100vh; }
  .editor:fullscreen .knobs-body, .editor.theater .knobs-body { max-height: none; overflow-y: auto; }
}
