/* ==========================================================================
   Demo chrome — shared by index.html (the workbench) and reference.html.
   Not part of the shipped engine; nothing here is required to use cs.

   Direction: an instrument, not a landing page. The audience is a designer on
   a desktop with the CMS open in the next window, and the page's one job is
   configure -> copy. Two rules carry the personality:

   1. Mono means MEASURED. Every number the browser reported - a card width, a
      breakpoint, a byte count - is set in the mono face. Prose is set in the
      sans. The typeface tells you which kind of thing you are reading.
   2. One accent. Amber, the colour of an instrument lamp. It marks the
      current thing and the out-of-tolerance thing, and nothing else.
   ========================================================================== */

:root {
  color-scheme: light dark;

  --paper: #f5f7f9;
  --surface: #fff;
  --sunken: #eceff3;
  --ink: #10151c;
  --ink-soft: #55606e;
  --rule: #dde2e9;
  --signal: #b8770a;
  --signal-bg: #fdf3dd;
  --on-signal: #1a1204; /* dark ON amber: white was 3.70:1, this is 5.02:1 */
  --signal-text: #8a5300; /* amber-family TEXT on --signal-bg; --signal itself is 3.35:1 there */
  --focus: #1a5fb4;
  --glyph-bg: #eef2f6;
  --glyph-edge: #c4ced8;
  --glyph-mid: #a9bccd;
  --glyph-ink: #16324f;
  --glyph-soft: #cdd9e3;

  /* An OEM mark is drawn black-on-transparent and sits straight on --surface,
     which is fine in light mode and invisible once --surface inverts in dark
     mode (Chrysler vanished outright; Lincoln, Jaguar, Kia, Land Rover, Lexus,
     Porsche, RAM, Genesis and Infiniti barely showed). filter: invert() was
     tried and reverted - it also inverts Toyota's red and BMW's blue, which
     are not black. A plate under the mark is the fix that touches only the
     background, not the mark's own colours. */
  --mark-plate: transparent;

  /* The code panel keeps Monokai in both themes: it is a quotation of an
     editor, and an editor does not change colour with the page around it. */
  --code-bg: #272822;
  --sans: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Code", "Consolas", monospace;

  /* 11rem, not more: the ceiling is whatever still leaves the work column
     1170px at a 1440 window, and 12rem missed it by 11px. The rail earns its
     legibility from short labels instead of width. */
  --rail: 11rem;
  --shell: 1760px;
}

[data-theme="dark"] {
  --paper: #0e1116;
  --surface: #161b22;
  --sunken: #1c222b;
  --ink: #e6eaf0;
  --ink-soft: #97a3b3;
  --rule: #262d38;
  --signal: #e8a317;
  --signal-bg: #2a2113;
  --on-signal: #1a1204;
  --signal-text: #e8a317; /* 7.31:1 on the dark signal background */
  --focus: #7cb0ff;
  --glyph-bg: #1b222c;
  --glyph-edge: #333d4b;
  --glyph-mid: #5b6d84;
  --glyph-ink: #9db6d4;
  --glyph-soft: #2b3542;
  --mark-plate: #fff;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #0e1116;
    --surface: #161b22;
    --sunken: #1c222b;
    --ink: #e6eaf0;
    --ink-soft: #97a3b3;
    --rule: #262d38;
    --signal: #e8a317;
    --signal-bg: #2a2113;
    --on-signal: #1a1204;
    --signal-text: #e8a317; /* was missing here: system-dark users got the LIGHT amber on the dark code panel, 2.73:1 */
    --focus: #7cb0ff;
    --glyph-bg: #1b222c;
    --glyph-edge: #333d4b;
    --glyph-mid: #5b6d84;
    --glyph-ink: #9db6d4;
    --glyph-soft: #2b3542;
    --mark-plate: #fff;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

:where(a) {
  color: inherit;
}

/* pre[tabindex], for the tags block: it is focusable because it scrolls
   sideways, and a focusable element with no focus ring is WCAG 2.4.7. It was
   the one thing on the page you could Tab to and not see. */
:where(button, input, select, a, pre[tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ---- header ------------------------------------------------------------ */

/* NOT sticky, as of 2026-09-08, and the two halves of that decision belong
   together: the preview was unpinned so it would stop sliding over the settings
   you are reading, and a sticky masthead does the same thing to the preview -
   measured at 1700x900 scrolled 260px, it painted over the top 87px of the
   frame, which reads as a card with its top border missing.

   Raising the preview above it was tried and reverted: it fixes the card and
   breaks the header, putting a slider over the site nav. Nothing on this page
   overlaps anything now; the whole thing scrolls as one. The rail below stays
   sticky because it sits BESIDE the content rather than over it. */
.ui-head {
  background: var(--surface);
  border-block-end: 1px solid var(--rule);
}

.ui-head-in {
  display: flex;
  gap: 0.75rem 1.5rem;
  align-items: center;
  max-inline-size: var(--shell);
  block-size: 3.5rem;
  padding: 0 1.5rem;
  margin: 0 auto;
}

/* The masthead is the one row that cannot wrap, so it has to give instead:
   the byte figure is the first thing to go, then the padding. Without this
   the page scrolled sideways at 375px. */
@media (width <= 30rem) {
  .ui-head-in {
    gap: 0.35rem;
    padding: 0 0.5rem;
  }

  #ui-size {
    display: none;
  }

  .ui-tabs a {
    padding: 0.4rem 0.45rem;
    font-size: 0.82rem;
  }
}

.ui-tabs::-webkit-scrollbar {
  display: none;
}

.ui-brand {
  display: flex;
  gap: 0.55rem;
  align-items: baseline;
  margin: 0;
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 700;
  font-stretch: 125%;
  letter-spacing: -0.01em;
}

/* The wordmark is the way back to the builder from anywhere. It looked like a
   link on no page before, because it was a heading and nothing else. */
.ui-brand a {
  color: inherit;
  text-decoration: none;
}

.ui-brand a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The byte count only. This was `.ui-brand span`, which then caught the
   wordmark the moment it was wrapped in one and rendered it at 11px mono
   grey - the wordmark did not disappear, it was demoted to look like this. */
#ui-size {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--ink-soft);
}

/* min-inline-size + overflow: the masthead is the one row that cannot wrap, so
   the tab strip scrolls inside itself rather than pushing the page sideways.
   Adding a third tab put it 4px over at 375px. */
.ui-tabs {
  display: flex;
  gap: 0.25rem;
  min-inline-size: 0;
  margin-inline-start: auto;
  overflow-x: auto;
  scrollbar-width: none;
}

.ui-tabs a {
  padding: 0.4rem 0.85rem;
  font-size: 0.87rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  border-radius: 7px;
}

.ui-tabs a:hover {
  color: var(--ink);
  background: var(--sunken);
}

.ui-tabs a[aria-current="page"] {
  color: var(--ink);
  background: var(--sunken);
}

.ui-theme {
  display: grid;
  place-items: center;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  font-size: 1rem;
  color: inherit;
  cursor: pointer;
  background: none;
  border: 1px solid var(--rule);
  border-radius: 7px;
}

.ui-theme:hover {
  background: var(--sunken);
}

/* ---- shell ------------------------------------------------------------- */

/* Two columns, not three: one rail holding every control, and one work column
   wide enough to preview at a real 1170px .container. Three columns looked
   tidier and capped the preview at 983px on a 1680px screen, which is the one
   thing this layout must not do. */
.ui-shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: 0;
  max-inline-size: var(--shell);
  margin: 0 auto;
}

/* Sticky at 0 and full height, not 3.5rem and short of it: those numbers were
   clearing a masthead that stayed put, and it no longer does. Left as they were
   the rail would hold a 3.5rem gap above itself against nothing, and stop 3.5rem
   short of the bottom of the window. The rail may stick because it sits BESIDE
   the content, never over it. */
.ui-rail {
  position: sticky;
  inset-block-start: 0;
  max-block-size: 100vh;
  padding: 1.25rem 0.75rem 2.5rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-inline-end: 1px solid var(--rule);
}

/* Keep and Reset sit above the settings, not among them: they act on the
   whole panel, and a button that means "all of this" reads wrong in a column
   of things that each mean one knob. */
.ui-panel-bar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: flex-end;
  padding-block-end: 0.6rem;
  margin-block-end: 0.6rem;
  border-block-end: 1px solid var(--rule);
}

.ui-panel-acts {
  display: flex;
  gap: 0.4rem;
}

/* Says the panel holds something the next reload will not. Amber is the one
   accent and it marks the out-of-tolerance thing; unsaved work is exactly
   that. */
.ui-dirty {
  margin-inline-end: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--signal-text);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* The settings column. One column of folders, not a full-width band: the pane
   puts a value at the right-hand edge of whatever it is given, so across the
   whole page a label and its field sat 1200px apart. */
.ui-panel {
  padding: 0.9rem 1rem 1rem;
  margin-block-end: 1.25rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

#wb-settings {
  /* Tweakpane is themed through its own custom properties, mapped to the
     demo's tokens here so the pane follows the light/dark switch with no
     script and never shows the library's dark dev-tool default. The full
     list is in node_modules/tweakpane/dist/tweakpane.js - this build ships
     no separate .css, the styles are injected from JS. */
  --tp-base-background-color: var(--surface);
  --tp-base-shadow-color: transparent;
  --tp-base-font-family: var(--sans);
  --tp-base-border-radius: 6px;
  --tp-container-background-color: var(--sunken);
  --tp-container-background-color-hover: var(--glyph-bg);
  --tp-container-background-color-focus: var(--glyph-bg);
  --tp-container-background-color-active: var(--glyph-bg);
  --tp-container-foreground-color: var(--ink);
  --tp-groove-foreground-color: var(--rule);
  --tp-input-background-color: var(--paper);
  --tp-input-background-color-hover: var(--paper);
  --tp-input-background-color-focus: var(--surface);
  --tp-input-background-color-active: var(--surface);
  --tp-input-foreground-color: var(--ink);
  --tp-label-foreground-color: var(--ink-soft);
  --tp-button-background-color: var(--glyph-bg);
  --tp-button-background-color-hover: var(--glyph-soft);
  --tp-button-background-color-focus: var(--glyph-soft);
  --tp-button-background-color-active: var(--glyph-mid);
  --tp-button-foreground-color: var(--ink);
  --tp-monitor-background-color: var(--paper);
  --tp-monitor-foreground-color: var(--ink-soft);
  --tp-blade-value-width: 11rem;
  --tp-blade-horizontal-padding: 0.5rem;

  /* 24, not the library's 20: WCAG 2.2's minimum target. Every Tweakpane
     control and both plugin rows are sized from this one property. At 20 a
     row passed only on the spacing exception, and an open colour picker took
     that spacing away - axe failed the rows under and beside it (2026-10-27). */
  --tp-container-unit-size: 24px;
}

/* ---- work area ----------------------------------------------------------
   One column at every width: the preview on top, the settings under it. It was
   two columns from 1200px up, the settings in a 22-26rem rail and the preview
   in what was left - which on a 1440 laptop is about 790px, so the 1200px
   Desktop frame was shown at 63%. Judging a card style, a gap or a photo crop
   at 63% is judging a picture of the thing rather than the thing. The preview
   gets the WHOLE width now and that same frame renders at 99%, life size, with
   nothing left to scale away.

   Nothing is ordered by CSS any more. The DOM says preview, then settings,
   which is both the reading order and the visual one, so the `order` swaps
   this block used to carry are gone with the second column.

   A plain block box, not a grid: a sticky element is constrained to its
   containing block, and a grid item's containing block is its own row - as
   tall as the item, with nothing to slide within. In flow, the preview's
   containing block is .ui-work, which is preview PLUS settings, and that is
   the height it has to stay pinned over. */
.ui-work {
  display: block;
}

/* The preview scrolls with the page. It used to be pinned under the masthead
   with a height cap, so the settings passed beneath it - and that is exactly
   what was wrong with it: a panel you are reading slides under a white slab
   that is covering the thing you came to read. Removed 2026-09-08 at Steven's
   call.

   What went with the pinning, because each existed only to serve it: the
   `inset-block-start` that held it clear of the sticky masthead; the
   `max-block-size` cap; `overflow-y: auto`, which only mattered for content
   taller than that cap; the `z-index`, which stopped the pane's positioned
   rows interleaving with the frame as they passed under; and the
   `@media (height < 640px)` block that turned the whole arrangement off again
   on a short window. Nothing passes under anything now, so none of it has
   anything to do.

   The background stays. It is what keeps the 1.5rem under the readout opaque,
   and --paper so the strip reads as the page it sits on.

   fitFrameScale() in workbench.js reads this element's computed
   max-block-size and scales the frame into whatever it finds; with no cap it
   finds `none` and scales to width alone, which is the same path the short
   window already took. */
.ui-preview {
  background: var(--paper);
}

.ui-main {
  min-inline-size: 0;
  padding: 1.5rem 1.75rem 4rem;
}

/* Below this the side margins give way before the preview does: a 1440 laptop
   needs the pixels more than the page needs a comfortable gutter, and 1170 is
   the container most dealer desktop traffic actually gets. */
@media (width <= 1500px) {
  .ui-main {
    padding-inline: 1rem;
  }
}

/* ---- the pattern index ------------------------------------------------- */

.ui-rail h2 {
  padding-block-start: 0.55rem;
  margin: 0 0 0.6rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border-block-start: 1px solid var(--rule);
}

#wb-nav {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

#wb-nav button {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  padding: 0.35rem 0.4rem;
  font: inherit;
  font-size: 0.87rem;
  color: var(--ink-soft);
  text-align: start;
  cursor: pointer;
  background: none;
  border: 0;
  border-radius: 6px;
}

#wb-nav button:hover {
  color: var(--ink);
  background: var(--sunken);
}

/* The current pattern gets the amber marker. It is the only place in the rail
   any colour appears, so there is never a question which one you are on. */
#wb-nav button[aria-current="true"] {
  position: relative;
  font-weight: 600;
  color: var(--ink);
  background: var(--sunken);
}

#wb-nav button[aria-current="true"]::before {
  position: absolute;
  inset-block: 0.35rem;
  inset-inline-start: 0;
  inline-size: 2px;
  content: "";
  background: var(--signal);
  border-radius: 2px;
}

/* Visually hidden but announced — used for headings that exist to keep the
   document outline continuous rather than to be read on screen. */
.ui-sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}

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

.ui-title {
  margin: 0 0 0.3rem;
  font-size: 1.9rem;
  font-weight: 700;
  font-stretch: 118%;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.ui-blurb {
  max-inline-size: 68ch;
  margin: 0 0 1.25rem;
  color: var(--ink-soft);
}

/* An artboard, not a themed panel. The stage stays a light page in both
   themes because the slider is going onto a dealer site, which is light: a
   preview that went dark would show colours nobody is going to ship. Dark
   chrome around a light canvas is the same arrangement as every design tool.

   The pattern CSS uses literal colours rather than tokens from this file, so
   nothing demo-only ever lands in the snippet you paste. */
.wb-stage {
  display: flex;
  justify-content: center;
  padding: 1.25rem;
  color: #10151c;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 12px 12px 0 0;
}

.wb-variants {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-block-end: 0.6rem;
}

.wb-variants button {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
  padding: 0.3rem 0.7rem 0.3rem 0.4rem;
  font: inherit;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
}

.wb-variants button img {
  inline-size: 1.6rem;
  block-size: auto;
}

.wb-variants button[aria-pressed="true"] {
  color: var(--paper);
  background: var(--ink);
  border-color: var(--ink);
}

.wb-variants button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* The rest of the brand control: every brand a card can take that is not
   already a chip, so a roster-only brand (or every brand on a card with
   nothing measured) is still one control away, and the note under it. */
#wb-brand {
  padding: 0.3rem 1.6rem 0.3rem 0.7rem;
  font: inherit;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
}

#wb-brand:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.wb-brand-note {
  flex-basis: 100%;
  margin: 0.25rem 0 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* The host for a pattern whose point is covering the page - today only the
   lightbox. workbench.js clones the snippet's wrapper into it and opens the
   dialog HERE rather than inside the preview frame, because a modal is only
   ever fullscreen relative to its own document and the frame's document is a
   simulated device. The host itself must take no space and paint nothing: the
   only thing it holds that renders is the dialog, and a modal dialog is in the
   top layer, positioned against the viewport whatever its parent does. No
   transform, filter or contain here, ever - any of the three would give the
   dialog a containing block and pull it back into a box. */
.wb-overlay {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: 0;
  block-size: 0;
}

/* border: 0 is load-bearing, not tidying. An iframe's default 2px inset border
   comes out of its VIEWPORT, so a frame set to 390px gave the slider a 386px
   window and every measurement landed 4px short of a real phone - which is the
   one thing this preview exists to get right. display: block kills the inline
   baseline gap under it. */
#wb-stage {
  display: block;

  /* The frame is exactly the width on the button, never the width the column
     happens to have: it used to be `inline-size: 100%` capped at --frame, so a
     column narrower than the frame silently shrank it and the Desktop button
     previewed a 999px screen while still calling itself Desktop. With the
     settings beside it that would have been every window under ~1800px. It
     keeps its width - flex: none so the flex container cannot shrink it
     either - and fitFrameScale() in workbench.js scales the PICTURE down to
     the column when it does not fit. transform-origin lives here because the
     transform is set from script and the origin never varies.
     1200, not 1170: the frame is the SCREEN the slider is being previewed on,
     and Bootstrap hands out its 1170px container at 1200 and up. The fallback
     has to agree with the button that starts out pressed, or a first visit
     renders one tier down from what the panel says. "Fill" sets --frame to
     100%, which is how it still fills the column. */
  flex: none;
  inline-size: var(--frame, 1200px);
  border: 0;
  transform-origin: top left;
}

/* No overflow:hidden here - it clipped the focus ring off the buttons inside,
   leaving them with no visible focus at all. The ends are rounded instead.
   Wraps rather than shrinks: the seven buttons are 339px in one row, which
   ran 13px past the card at 375 and scrolled the page 52px sideways at 320.
   Nothing wraps at 768 and up, where the row is 365px in a far wider card. */
.ui-widths {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1px;
  border: 1px solid var(--rule);
  border-radius: 6px;
}

.ui-widths button:first-child {
  border-start-start-radius: 5px;
  border-end-start-radius: 5px;
}

.ui-widths button:last-child {
  border-start-end-radius: 5px;
  border-end-end-radius: 5px;
}

/* Icon over a word. A row of bare numbers (750 / 970 / 1170) asked the reader
   to know which number was a phone; a phone icon does not. */
.ui-widths button {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  align-items: center;
  padding: 0.3rem 0.55rem;
  font-family: var(--sans);
  font-size: 0.68rem;
  color: var(--ink-soft);
  cursor: pointer;
  background: var(--surface);
  border: 0;
}

.ui-widths svg {
  inline-size: 1.05rem;
  block-size: 1.05rem;
  fill: none;
  stroke: currentcolor;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.ui-widths button:hover {
  color: var(--ink);
}

.ui-widths button[aria-pressed="true"] {
  color: var(--on-signal);
  background: var(--signal);
}

/* The grid select beside the width buttons: the same type and rule as the
   buttons, so the two page settings read as one control group. */
.ui-select {
  padding: 0.3rem 0.4rem;
  font-family: var(--sans);
  font-size: 0.74rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
}

/* ---- the signature: a spec readout under the stage ---------------------- */

.ui-spec {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.75rem;
  align-items: center;
  padding: 0.65rem 1rem;
  margin-block-end: 1.5rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  background: var(--sunken);
  border: 1px solid var(--rule);
  border-block-start: 0;
  border-radius: 0 0 12px 12px;
}

.ui-spec b {
  font-weight: 500;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* The label and its control sit side by side, and wrap onto two lines when
   they cannot: "Preview at" plus the five screen-size buttons needs 310px, so
   on a 320px phone the row hung 23px off the right edge and the last button
   was unreachable. Wrapping keeps every button on screen and reads the same at
   every other width, where it never wraps. */
.ui-spec-item {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: baseline;
}

/* The class sets a display, so the UA's [hidden] rule loses to it and an item
   hidden from script would go on showing. "Shown at" is the only item that
   comes and goes today; the rule is written for the attribute rather than for
   that one id so the next one is covered too. */
.ui-spec-item[hidden] {
  display: none;
}

/* Card width against the width this look's content needs. The bar is the
   fit check made visible: amber once the card is inside the last 15% before
   the minimum, which is where text starts colliding. */
.ui-gauge {
  position: relative;
  flex: 1;
  min-inline-size: 6rem;
  max-inline-size: 11rem;
  block-size: 4px;
  overflow: hidden;
  background: var(--rule);
  border-radius: 2px;
}

.ui-gauge i {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  display: block;
  background: var(--ink-soft);
  border-radius: 2px;
  transition: inline-size 0.2s ease;
}

.ui-spec[data-fit="tight"] {
  color: var(--signal-text);
  background: var(--signal-bg);
  border-color: var(--signal);
}

.ui-spec[data-fit="tight"] b {
  color: inherit;
}

.ui-spec[data-fit="tight"] .ui-gauge i {
  background: var(--signal);
}

.ui-spec-note {
  flex-basis: 100%;
  padding-block-start: 0.35rem;
  font-family: var(--sans);
  font-size: 0.8rem;
}

/* ---- settings panel ---------------------------------------------------- */

/* The slide editor's label/field pair. The settings panel is a Tweakpane now
   and draws its own rows; this is what those rows are laid out against inside
   a slide card, where the field wants the whole width (the .wb-slide block
   below restates it). Two columns, not a flex row pushing the ends apart:
   space-between let every control find its own left edge, and a long label
   shoved its field out of line. */
.wb-row {
  display: grid;
  grid-template-columns: 1fr 9.5rem;
  gap: 0.6rem;
  align-items: center;

  /* Was 0.35rem, which read as one undifferentiated stack of 14 knobs. The
     rows are label/value pairs, so they need enough space to read as pairs
     and not enough to read as separate things. */
  margin-block-end: 0.5rem;
  font-size: 0.85rem;
}

.wb-row > span {
  color: var(--ink-soft);

  /* Wrap rather than push the control column out of alignment. */
  overflow-wrap: anywhere;
}

.wb-row input[type="text"],
.wb-row input[type="url"],
.wb-row input[type="number"],
.wb-row select,
.wb-row textarea {
  inline-size: 100%;
  min-inline-size: 0; /* a form control will not shrink below its size attribute without it */
  padding: 0.28rem 0.45rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
}

/* justify-self, because a 1rem checkbox in a 9.5rem control column would
   otherwise stretch across it and sit nowhere near the other controls' edge. */
.wb-row input[type="checkbox"] {
  justify-self: start;
  inline-size: 1rem;
  block-size: 1rem;
  margin: 0;
  accent-color: var(--signal);
}

.wb-note {
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* A note is prose sitting in a panel of controls, so it takes the panel's type,
   not the page's. It was 0.8rem — 12.8px against the 11px every label in the
   pane is drawn at, which read as page copy dropped into the settings rather
   than part of them. `inherit` rather than a second hardcoded number, so it
   tracks the pane if the pane ever moves.

   The padding is the other half. Measured in Advanced at 1440: the note's text
   started at x=1018 and the labels beside it at x=1026, so every note hung 8px
   into the left margin of its own folder — 4px of the row's inset plus the 4px
   the label carries itself. Two numbers from Tweakpane's own layout, restated
   here because there is no custom property exposing either. */
.tp-notev {
  padding-inline-start: 8px;
  margin: 0.35rem 0 0.5rem;
  font-size: inherit;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* The .tp-lookv rules for the card-style picker lived here. They went on
   2026-09-08 with the picker itself: every card is a rail entry now, so the
   grid of thumbnails has nothing to draw. The card ICONS survive in looks.js -
   the rail's own glyphs are CSS, but check-looks still requires each look to
   carry one, and the Reference draws them. */

/* A settings field holding something its property cannot use — a gap of `10`,
   which is a number and not a length. The declaration is dropped rather than
   shipped, so the slider keeps working; this says why the typed value is not
   in the code. Spans the whole row, which is a flex line: the pane's own two
   children are the label and the field, so this has to wrap under both. On
   --signal-bg rather than the panel, because that is the pair whose contrast
   this file has already measured, in both themes. */
#wb-settings .tp-lblv {
  flex-wrap: wrap;
}

/* The pane dims a disabled row's LABEL to 0.5, which measured 2.18:1 on white
   (axe, builder #hero). The one disabled row there is "At the ends (autoplay
   always wraps)" — and the words in that label are the whole reason the
   control is off. A greyed-out control that will not explain itself is the
   mistake the previous panel made; the <select> is still disabled and reads
   that way, so only the sentence comes back. */
#wb-settings .tp-lblv.tp-v-disabled .tp-lblv_l {
  opacity: 1;
}

/* A length is one number and one unit, so the row is a number box and a short
   unit list rather than one free-text field a designer has to spell a unit
   into. The unit column is fixed and narrow — the widest option is "px" — and
   the number takes the rest, which keeps the two halves in the same place down
   the whole panel. The pane's own inputs are styled from the --bld- and --in-
   shorthand variables that tweakpane.js declares for itself; these use the
   documented --tp- ones the panel already maps to the demo's tokens above, so
   the pair follows the light/dark switch with everything else. The pane sets
   `outline: none` on its own fields; these two do not, because the browser's
   focus ring is the only thing saying which half of the row has the caret. */
.tp-lenv {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 3.4rem;
  gap: 0.25rem;
  align-items: center;
}

.tp-lenv_n,
.tp-lenv_u {
  box-sizing: border-box;
  min-inline-size: 0;
  block-size: var(--tp-container-unit-size, 20px);
  padding-inline: var(--tp-blade-horizontal-padding, 0.5rem);
  font: inherit;
  color: var(--tp-input-foreground-color);
  background: var(--tp-input-background-color);
  border: 0;
  border-radius: var(--tp-base-border-radius, 6px);
}

/* Right, like the pane's own number fields directly above it in the panel -
   and it puts the number against its unit, so the pair reads as the one value
   it is rather than as a field and a menu. */
.tp-lenv_n {
  text-align: right;
}

.tp-lenv_u {
  padding-inline-end: 0;
  cursor: pointer;
}

.tp-lenv_n:hover,
.tp-lenv_u:hover {
  background: var(--tp-input-background-color-hover);
}

.tp-lenv_n:focus,
.tp-lenv_u:focus {
  background: var(--tp-input-background-color-focus);
}

/* A colour is a text field that stays authoritative, a swatch beside it that
   can show ANY value the field holds, and a × that puts the default back. The
   swatch opens a popover with the spectrum, an opacity slider and the colours
   already on this slider.

   The checkerboard under every swatch is the whole reason the row exists: a
   native colour input has no alpha and no `transparent`, so a half-opaque
   arrow background and a fully transparent one drew the same solid square. Two
   backgrounds - the colour over a conic checkerboard - and the difference is
   visible at a glance.

   `--sw` is set on the element by the plugin, so an unreadable value (a
   currentcolor, a var()) falls back to `transparent` and shows the
   checkerboard rather than a colour it is not. */
.tp-colv {
  position: relative;
  display: grid;
  grid-template-columns: 1.4rem minmax(0, 1fr) 1.2rem;
  gap: 0.25rem;
  align-items: center;
}

.tp-colv_sw,
.tp-colv_list button {
  inline-size: 1.4rem;
  block-size: 1.4rem;
  padding: 0;
  cursor: pointer;
  background:
    linear-gradient(var(--sw, transparent), var(--sw, transparent)),
    repeating-conic-gradient(#ccc 0 25%, #fff 0 50%) 0 0 / 8px 8px;
  border: 1px solid var(--rule);
  border-radius: 4px;
}

/* Monospace, like the code panel: a hex is read character by character, and
   the panel's own proportional font makes an 8 and a B the same shape. */
.tp-colv_txt {
  min-inline-size: 0;
  block-size: var(--tp-container-unit-size, 20px);
  padding-inline: var(--tp-blade-horizontal-padding, 0.5rem);
  font: inherit;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--tp-input-foreground-color);
  background: var(--tp-input-background-color);
  border: 0;
  border-radius: var(--tp-base-border-radius, 6px);
}

.tp-colv_txt:hover {
  background: var(--tp-input-background-color-hover);
}

.tp-colv_txt:focus {
  background: var(--tp-input-background-color-focus);
}

.tp-colv_clear {
  padding: 0;
  font: inherit;
  font-size: 0.9rem;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
  background: none;
  border: 0;
}

.tp-colv_clear:hover {
  color: var(--ink);
}

/* In the top layer (popover="manual", opened by tp-plugins.js), under its row.
   It was absolute in the panel, which flows its folders through CSS columns,
   and Chromium fragments even an absolutely positioned box across them: a
   picker opened at the foot of a column split in two, its swatches landing on
   the next column's first folder, and the rebalance moved whole folders
   between columns (measured 2026-10-27). In the flow instead, it pushed the
   clicked swatch 414-511px away. The top layer is outside the panel's layout,
   so it cannot split or move anything; ColourView.place() sets its width and
   position from the row.

   It still opens over the rows below it, exactly where it always did: the top
   layer moves nothing apart. What keeps the rows under and beside it clear of
   its controls is the 24px unit size on #wb-settings. axe cannot see a fixed
   box - it passed these rows only because the top layer hid the picker from
   it - so scripts/a11y.mjs takes the picker out of the top layer to check.

   inset and margin undo the user-agent popover sheet, which centres a popover
   in the window; color undoes its CanvasText. */
.tp-colv_pop {
  position: fixed;
  inset: auto;
  display: grid;
  gap: 0.35rem;
  min-inline-size: 11rem;
  padding: 0.6rem;
  margin: 0;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}

/* `display: grid` above outranks the UA sheet's `[hidden] { display: none }`,
   so a closed popover has to be shut explicitly. Without this it was still
   laid out - invisible only because it had nothing painted over the row below
   - and it swallowed the clicks meant for the next colour down. It outranks
   the UA's `[popover]:not(:popover-open)` the same way, which is why the
   control keeps `hidden` in step with the top layer. */
.tp-colv_pop[hidden] {
  display: none;
}

.tp-colv_sp {
  inline-size: 100%;
  block-size: 2rem;
  padding: 2px;
  cursor: pointer;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
}

.tp-colv_a {
  inline-size: 100%;
  margin: 0;
  accent-color: var(--signal);
}

.tp-colv_cap {
  margin: 0;
  font-size: 0.68rem;
  line-height: 1.2;
  color: var(--ink-soft);
}

.tp-colv_list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.tp-flagv {
  flex-basis: 100%;
  padding: 0.3rem 0.5rem;
  margin: 0.25rem 0 0.35rem;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--signal-text);
  background: var(--signal-bg);
  border-radius: 4px;
}

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

.ui-content {
  margin-block-end: 1.4rem;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

.ui-content-acts {
  display: flex;
  gap: 0.4rem;
}

/* The bar is shared with the code panel, whose colours assume the dark Monokai
   ground underneath. On this light panel that grey-green measured 3.0:1 — the
   bar keeps its shape here but takes the page's own ink. */
.ui-content > .ui-code-bar {
  color: var(--ink-soft);
  background: var(--paper);
  border-block-end-color: var(--rule);
}

#wb-content {
  padding: 0.8rem;
}

#wb-content .wb-note {
  margin-block: 0 0.8rem;
}

/* Auto-fit rather than a fixed column count: the same editor sits under a
   1170px stage and in a narrow window, and a slide card stops being readable
   below about 15rem. */
.wb-slides {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.7rem;
}

/* A fieldset, so the legend names the group for a screen reader. Its UA
   `min-inline-size: min-content` is the catch: inside the grid above it stops
   the card shrinking and the whole row overflows, so it has to be reset. */
.wb-slide {
  min-inline-size: 0;
  padding: 0.7rem;
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 8px;
}

.wb-slide > legend {
  padding-inline: 0.35rem;
  font-size: 0.8rem;
  font-weight: 700;
}

/* Remove sits on its own line under the legend rather than beside it: a legend
   is laid out on the border box by the UA, and anything flexed next to it
   fights that. */
.wb-slide-head {
  display: flex;
  justify-content: flex-end;
  margin-block-end: 0.5rem;
}

/* The panel's rows put label and field side by side in a narrow rail. In a
   slide card the field needs the whole width — a URL or a paragraph in a
   7.5rem box is unreadable — so the row stacks here instead. */
.wb-slide .wb-row {
  display: block;
  margin-block-end: 0.5rem;
}

.wb-slide .wb-row > span {
  display: block;
  margin-block-end: 0.15rem;
  font-size: 0.75rem;
}

.wb-slide .wb-row input,
.wb-slide .wb-row textarea {
  inline-size: 100%;
  padding: 0.3rem 0.45rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
}

.wb-slide .wb-row textarea {
  resize: vertical;
}

.wb-slide .wb-row input[type="checkbox"] {
  inline-size: auto;
}

.wb-slide .ui-btn:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

/* Step numbers. The page is a sequence — write the slides, copy the code, add
   the files — and the numbers are what make that readable at a glance instead
   of three unrelated panels stacked up. */
.ui-step {
  display: inline-grid;
  place-items: center;
  inline-size: 1.15rem;
  block-size: 1.15rem;
  margin-inline-end: 0.4rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--on-signal);
  background: var(--signal);
  border-radius: 50%;
}

/* The one sentence that says what the two class prefixes in the code below
   mean. On its own line so it reads as a legend rather than as more of the
   step-2 instruction. */
.ui-code-legend {
  display: block;
  margin-block-start: 0.2rem;
  font-size: 0.95em;
  opacity: 0.85;
}

/* The slider's name, directly above the parts list and the code. The only
   setting that is about the PAGE rather than about this slider, so it sits
   where the copying happens instead of in the settings panel. */
.ui-name {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.7rem;
  align-items: baseline;
  padding: 0.75rem 0.9rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
  background: var(--surface);
  border-block-end: 1px solid var(--rule);
}

.ui-name label {
  font-weight: 600;
  color: var(--ink);
}

.ui-name input {
  inline-size: 12rem;
  padding: 0.25rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 5px;
}

/* The warning takes the rest of the row on a wide panel and its own line on a
   narrow one, rather than squeezing the field. */
.ui-name span {
  flex: 1 1 16rem;
  min-inline-size: 0;
}

.ui-name b {
  color: var(--ink);
}

/* What is in the code box, and where each piece goes. Sits between the bar and
   the code so it is read before the copy button is pressed, not after. */
.ui-parts {
  display: grid;
  gap: 0.3rem;
  padding: 0.7rem 0.9rem;
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
  list-style: none;
  background: var(--surface);
  border-block-end: 1px solid var(--rule);
}

.ui-parts li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: baseline;
}

.ui-parts b {
  min-inline-size: 5.5rem;
  color: var(--ink);
}

.ui-parts span {
  min-inline-size: 4.5rem;
  font-family: var(--mono);
  font-size: 0.72rem;
}

.ui-parts strong {
  color: var(--ink);
}

/* The photos warning is the one line here that is a caution, not a fact. */
.ui-parts-warn {
  padding-block-start: 0.4rem;
  border-block-start: 1px dashed var(--rule);
}

.ui-parts-warn b {
  color: var(--signal-text);
}

/* ---- code -------------------------------------------------------------- */

.ui-code {
  overflow: hidden;
  background: var(--code-bg);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

.ui-code-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.7rem;
  font-size: 0.78rem;
  color: #b9c0aa;
  border-block-end: 1px solid #3e3d32;
}

.ui-code pre {
  max-block-size: 32rem;
  padding: 1rem 1.1rem;
  margin: 0;
  overflow: auto;
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.6;
  color: #f8f8f2;
  tab-size: 2;
}

.ui-btn {
  padding: 0.32rem 0.8rem;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
}

.ui-btn:hover {
  color: var(--on-signal);
  background: var(--signal);
  border-color: var(--signal);
}

/* Steps 1 and 2 are real headings now, so the numbered sequence is in the
   outline and not only on screen. Nothing about them may LOOK different: a
   browser's own h3 size and margins would break the bar, so the bar's type
   wins. */
.ui-code-bar h3 {
  margin: 0;
  font: inherit;
}

.ui-code-bar .ui-btn {
  color: #f8f8f2;
  background: #3e3d32;
  border-color: #4c4b3f;
}

.ui-code-bar .ui-btn:hover {
  color: var(--on-signal);
  background: var(--signal);
  border-color: var(--signal);
}

/* Monokai. Tokens come from assets/highlight.js — no CDN, because this page
   has to work opened straight off disk. */

/* Two tokens lifted off Monokai's originals to clear 4.5:1 on #272822:
   the comment was 3.03:1 and the keyword 3.93:1. #ff6188 is Monokai Pro's
   own pink, so the panel still reads as Monokai. */
.t-c {
  color: #99957f;
}

.t-s {
  color: #e6db74;
}

.t-n {
  color: #ae81ff;
}

.t-k,
.t-tag {
  color: #ff6188;
}

.t-attr,
.t-sel {
  color: #a6e22e;
}

.t-p {
  color: #66d9ef;
}

/* ---- step 1: the engine files ------------------------------------------ */

.ui-h3 {
  margin: 2.5rem 0 0.3rem;
  font-size: 1.15rem;
  font-weight: 700;
  font-stretch: 112%;
}

/* align-items, because a grid item defaults to `stretch`: the two cards were
   drawn the same height whatever was in them, and once the upload column grew
   the four file rows the paste column was padded from its own 370px out to
   911px - half a card of empty white with a border round it. `start` lets each
   card stop where its content does. */
.ui-get {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
  gap: 1rem;
  align-items: start;
  margin-block-start: 1.1rem;
}

.ui-get > div {
  padding: 1.1rem 1.2rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

.ui-get h4 {
  margin: 0 0 0.2rem;
  font-size: 0.95rem;
}

.ui-get p {
  margin: 0 0 0.8rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* A button on its own line. This was an inline style on two <p>s, and it had to
   be: `.ui-get p` is (0,1,1) and beats a bare `.ui-act`, so the class is
   qualified to outrank it rather than fight it with !important. */
.ui-get .ui-act {
  margin: 0.8rem 0 0;
}

/* A hint belongs to the button above it, not to the paragraph after it. */
.ui-get .ui-act + p {
  margin-block-start: 0.45rem;
}

/* A list of files is a list. `.ui-parts` above is the same shape and the same
   decision; the row keeps `.ui-file` so nothing that reaches for one has to
   know it moved into a <ul>. */
.ui-files {
  padding: 0;
  margin: 0 0 0.8rem;
  list-style: none;
}

.ui-tags {
  padding: 0.7rem 0.8rem;
  margin: 0;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.65;
  color: #f8f8f2;
  background: var(--code-bg);
  border-radius: 8px;
}

.ui-where {
  padding: 0.6rem 0.75rem;
  margin-block-start: 0.8rem;
  font-size: 0.81rem;
  line-height: 1.5;
  background: var(--sunken);
  border-radius: 8px;
}

.ui-where b {
  display: block;
  margin-block-end: 0.2rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 500;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.ui-file {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
  margin-block-end: 0.45rem;
}

.ui-file code {
  flex: 1;
  min-inline-size: 8.5rem;
  font-family: var(--mono);
  font-size: 0.78rem;
}

/* An inline <code> is usually a class name or a property, but some of them are
   a full shared path — 62 characters with no space in it, which pushed the
   install panel 139px past a 320px phone. `anywhere` rather than `break-word`
   so the break can happen mid-token; a <pre> keeps its own scrolling and is
   excluded. */
code {
  font-family: var(--mono);
  font-size: 0.88em;
}

:not(pre) > code {
  overflow-wrap: anywhere;
}

#wb-file-view {
  margin-block-start: 1rem;
  overflow: hidden;
  background: var(--code-bg);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

#wb-file-view pre {
  max-block-size: 24rem;
  padding: 1rem;
  margin: 0;
  overflow: auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1.55;
  color: #f8f8f2;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* ---- reference page ---------------------------------------------------- */

.ui-doc {
  max-inline-size: 62rem;
  padding: 2rem 1.75rem 5rem;
  margin: 0 auto;
}

.ui-doc section {
  padding-block-start: 2.25rem;

  /* Same reason as .gx-card: the masthead is sticky, so a TOC jump would
     otherwise land with the section heading hidden underneath it. */
  scroll-margin-block-start: 4.5rem;
}

.ui-doc h3 {
  margin: 0 0 0.5rem;
  font-size: 1.3rem;
  font-weight: 700;
  font-stretch: 112%;
}

/* Doc prose, but never inside a live example. A card's name is a <p>, and this
   rule reached it: the tall tile's names came out --ink-soft on its own dark
   strip at 2.83:1, because `.ui-doc p` is (0,1,1) and the card sheet's
   `:where(.cargo-portrait) .cargo-name` is (0,1,0) - :where() contributes
   nothing, which is exactly what makes a designer's own rule win on a dealer
   page and what makes the demo's chrome win here. The examples are not this
   page's prose and must take none of its styling. */
.ui-doc p:not(.gx-stage p) {
  max-inline-size: 72ch;
  color: var(--ink-soft);
}

.g-list {
  max-inline-size: 78ch;
  padding-inline-start: 1.1rem;
  margin: 0.8rem 0 0;
}

.g-list li {
  margin-block-end: 0.55rem;
  color: var(--ink-soft);
}

.g-list li strong,
.ui-doc p strong {
  color: var(--ink);
}

.g-scroll {
  margin-block-start: 1rem;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.g-scroll table {
  inline-size: 100%;
  font-size: 0.85rem;
  border-collapse: collapse;
}

.g-scroll th {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  color: var(--ink-soft);
  text-align: start;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.g-scroll th,
.g-scroll td {
  padding: 0.6rem 0.85rem;
  border-block-end: 1px solid var(--rule);
}

.g-scroll tr:last-child td {
  border-block-end: 0;
}

.g-scroll td:first-child {
  white-space: nowrap;
}

.g-scroll td code {
  color: var(--ink);
}

.g-code {
  padding: 1rem 1.1rem;
  margin-block-start: 1rem;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: #f8f8f2;
  background: var(--code-bg);
  border-radius: 10px;
}

.g-sub {
  margin-block-start: 1.5rem;
}

/* The name over each arrow recipe. Sits tight to the block it labels, so a
   list of them reads as pairs rather than as one wall of code. */
.g-recipe {
  margin-block: 1.5rem -0.5rem;
  font-size: 0.9rem;
  font-weight: 600;
}

.ui-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-block-start: 1.25rem;
}

.ui-toc a {
  padding: 0.32rem 0.7rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--ink-soft);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
}

.ui-toc a:hover {
  color: var(--on-signal);
  background: var(--signal);
  border-color: var(--signal);
}

/* ---- pattern glyphs ---------------------------------------------------- */

/* A small drawing of each pattern's LAYOUT, so the rail is scannable by shape
   rather than by reading seventeen labels. Ported from the original demo's
   picker, where they did the same job; the hardcoded blues are tokens now so
   they hold up in both themes. Pure CSS on ::before/::after — no image
   requests, and they scale with the text. */
.wb-glyph {
  position: relative;
  flex: none;
  inline-size: 38px;
  block-size: 27px;
  overflow: hidden;
  background: var(--glyph-bg);
  border: 1px solid var(--glyph-edge);
  border-radius: 3px;
}

.wb-glyph::before,
.wb-glyph::after {
  position: absolute;
  content: "";
}

/* A full-width photo with a dot row under it. */
.wb-glyph--hero::before {
  inset: 4px 5px 9px;
  background: var(--glyph-mid);
}

.wb-glyph--hero::after {
  inset-block-end: 3px;
  inset-inline: 11px;
  block-size: 4px;
  background: radial-gradient(circle 1.7px, var(--glyph-ink) 98%, transparent) 0 0 / 7px 4px repeat-x;
}

/* Evenly spaced cutouts across a single row. */
.wb-glyph--modelbar::before {
  inset: 8px 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 5px, transparent 5px 8px);
}

/* The same row under a strip of tabs. */
.wb-glyph--tabs::before {
  inset: 3px 7px auto;
  block-size: 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 8px, transparent 8px 11px);
}

.wb-glyph--tabs::after {
  inset: 11px 4px 5px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 5px, transparent 5px 8px);
}

/* A wordmark bar over a row of cutouts. */
.wb-glyph--wordmark::before {
  inset: 4px 9px auto;
  block-size: 3px;
  background: var(--glyph-ink);
  border-radius: 2px;
}

.wb-glyph--wordmark::after {
  inset: 11px 4px 5px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 5px, transparent 5px 8px);
}

/* Tall cards, each with a button bar across its foot. */
.wb-glyph--portrait::before {
  inset: 2px 5px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 6px, transparent 6px 9px);
}

.wb-glyph--portrait::after {
  inset: auto 5px 4px;
  block-size: 3px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 6px, transparent 6px 9px);
}

/* One wide card: a photo half beside a copy half. */
.wb-glyph--split::before {
  inset: 5px 4px;
  background: var(--glyph-mid);
  border-radius: 2px;
}

.wb-glyph--split::after {
  inset: 5px 4px 5px 50%;
  background: var(--glyph-bg);
  border-radius: 0 2px 2px 0;
}

/* One card centred, slivers of its neighbours at both edges. */
.wb-glyph--peek::before {
  inset-block: 4px;
  inset-inline: 0;
  background: linear-gradient(90deg, var(--glyph-soft) 0 5px, transparent 5px calc(100% - 5px), var(--glyph-soft) calc(100% - 5px));
}

.wb-glyph--peek::after {
  inset: 4px 9px;
  background: var(--glyph-mid);
}

/* A photo with its own little pager inside the card. */
.wb-glyph--card-gallery::before {
  inset: 3px 9px;
  background: var(--glyph-mid);
  border-radius: 2px;
}

.wb-glyph--card-gallery::after {
  inset-block-end: 6px;
  inset-inline-end: 11px;
  inline-size: 8px;
  block-size: 4px;
  background: var(--glyph-ink);
  border-radius: 3px;
}

/* A photo above its thumbnail strip. */
.wb-glyph--gallery::before {
  inset: 3px 7px 10px;
  background: var(--glyph-mid);
}

.wb-glyph--gallery::after {
  inset-block-end: 3px;
  inset-inline: 7px;
  block-size: 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 5px, transparent 5px 8px);
}

/* Filter pills above the photo. */
.wb-glyph--gallery-filter::before {
  inset-block-start: 3px;
  inset-inline: 8px;
  block-size: 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 7px, transparent 7px 10px);
  border-radius: 3px;
}

.wb-glyph--gallery-filter::after {
  inset: 10px 7px 4px;
  background: var(--glyph-mid);
}

/* A photo with a play triangle over it. */
.wb-glyph--media-gallery::before {
  inset: 3px 7px;
  background: var(--glyph-mid);
}

.wb-glyph--media-gallery::after {
  inset-block-start: 50%;
  inset-inline-start: 50%;
  border: 5px solid transparent;
  border-inline-start: 8px solid var(--glyph-ink);
  transform: translate(-30%, -50%);
}

/* A panel floating over a dimmed page. */
.wb-glyph--lightbox::before {
  inset: 0;
  background: var(--glyph-soft);
}

.wb-glyph--lightbox::after {
  inset: 6px 9px;
  background: var(--glyph-bg);
  border: 1px solid var(--glyph-ink);
}

/* A row of cards, each with a line of text under it. */
.wb-glyph--cards::before {
  inset: 5px 4px 10px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 8px, transparent 8px 11px);
}

.wb-glyph--cards::after {
  inset-block-end: 6px;
  inset-inline: 4px;
  block-size: 2px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 8px, transparent 8px 11px);
}

/* Tall portraits, no text under them. */
.wb-glyph--models::before {
  inset: 3px 7px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 7px, transparent 7px 11px);
}

/* Three source files at three different heights — the whole point. */
.wb-glyph--mixed::before {
  inset: 4px;
  background:
    linear-gradient(var(--glyph-mid), var(--glyph-mid)) 0 100% / 8px 12px no-repeat,
    linear-gradient(var(--glyph-mid), var(--glyph-mid)) 11px 100% / 8px 18px no-repeat,
    linear-gradient(var(--glyph-mid), var(--glyph-mid)) 22px 100% / 8px 9px no-repeat;
}

/* One card: photo on top, a paragraph under it. */
.wb-glyph--service::before {
  inset: 4px 10px 12px;
  background: var(--glyph-mid);
}

.wb-glyph--service::after {
  inset-block-end: 4px;
  inset-inline: 10px;
  block-size: 6px;
  background: repeating-linear-gradient(var(--ink-soft) 0 2px, transparent 2px 4px);
}

/* Poster on the left, its caption on the right. */
.wb-glyph--video::before {
  inset: 4px 19px 4px 5px;
  background: var(--glyph-mid);
}

.wb-glyph--video::after {
  inset: 6px 5px 6px 21px;
  background: repeating-linear-gradient(var(--glyph-mid) 0 2px, transparent 2px 4px);
}

/* Stars over a quote. */
.wb-glyph--reviews::before {
  inset-block-start: 5px;
  inset-inline: 7px;
  block-size: 4px;
  background: radial-gradient(circle 2px, var(--signal) 98%, transparent) 0 0 / 7px 4px repeat-x;
}

.wb-glyph--reviews::after {
  inset: 13px 7px 5px;
  background: repeating-linear-gradient(var(--glyph-mid) 0 2px, transparent 2px 4px);
}

/* The engine untouched: a card with the default arrows either side. */
.wb-glyph--stock::before {
  inset: 5px 10px;
  background: var(--glyph-mid);
}

/* Logo strip: squat panels, wider apart than the model bar's cards and each
   holding one centred mark rather than a vehicle and its name. Added with the
   pattern on 2026-09-08 - the rail draws .wb-glyph--<id>, so a pattern without
   a rule of its own gets an empty box beside its name. */
.wb-glyph--logostrip::before {
  inset: 7px 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 9px, transparent 9px 12px);
}

.wb-glyph--logostrip::after {
  inset: 11px 4px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 4px, transparent 4px 12px);
}

/* Locations: a photo band with a line of address under it, two across. */
.wb-glyph--locations::before {
  inset: 5px 4px 12px;
  background: repeating-linear-gradient(90deg, var(--glyph-mid) 0 15px, transparent 15px 19px);
}

.wb-glyph--locations::after {
  inset-block-end: 7px;
  inset-inline: 4px;
  block-size: 3px;
  background: repeating-linear-gradient(90deg, var(--glyph-ink) 0 15px, transparent 15px 19px);
}

.wb-glyph--stock::after {
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 2px;
  font-size: 11px;
  font-weight: 700;
  color: var(--glyph-ink);
  content: "‹ ›";
}

/* ---- the index (patterns.html) ----------------------------------------- */

.gx-doc {
  max-inline-size: 78rem;
}

/* The catalogue on one screen. Twenty-four live sliders is fifteen screens of
   scrolling, and before this the only way to find one was to scroll past the
   rest of them. Tiles rather than a list of links because the glyph is how the
   builder's rail names these, and a designer recognises the shape first. */
.gx-index {
  display: grid;

  /* Wide enough for the longest name in either index ("Tall tile with CTA"),
     because a tile that ellipsises the thing it names is a tile you cannot
     read. */
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: 0.5rem;
  margin-block-start: 1.25rem;
}

.gx-tile {
  display: flex;
  gap: 0.55rem;
  align-items: center;
  padding: 0.5rem 0.7rem;
  font-size: 0.85rem;
  color: inherit;
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 9px;
}

.gx-tile:hover {
  border-color: var(--accent);
}

.gx-tile .wb-glyph {
  flex: none;
}

.gx-tile-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Brands tiles only, not patterns.html: a brand's name is what identifies
   the tile, so it wraps instead of ellipsising - Chevro…, Alfa Ro…, Land R…,
   Mitsubi… and Volksw… were unreadable. */
.bb-tile {
  /* And the badge drops under the name rather than out of the tile. At 320 the
     rail is two 144px columns, and logo + gap + badge + padding is 134 of that
     before the name has any - so the name held its min-content width and pushed
     the badge 30px past the page's right edge, which is the whole of the
     sideways scroll brands.html had there (2026-09-15). Wrapping is not
     shrinking: the badge still never squeezes, it just takes its own line. */
  flex-wrap: wrap;
  align-items: center;
}

.bb-tile .gx-tile-name {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
}

/* A brands.html tile swaps the glyph for a logo and adds a badge, so both new
   children need the same "never shrink" treatment .wb-glyph already gets
   above (.gx-tile is a flex row, and .gx-tile-name is the only child that
   overflow:hidden lets flex-shrink actually squeeze - see the ellipsis rule
   above). Without it the badge's own text would wrap onto a second line
   inside its pill before the name gave up any width. */
.bb-tile img {
  flex: none;
  inline-size: 2.4rem;
  block-size: auto;
}

/* --paper on --ink, inverted from the page's own text-on-background pair, so
   it is always at that pair's own contrast: 17.06:1 in light, 15.67:1 in
   dark. Measured in the rendered page, not assumed. */
.bb-badge {
  flex: none;
  padding: 0.1em 0.5em;
  font-size: 0.7em;
  font-weight: 600;
  color: var(--paper);
  white-space: nowrap;
  background: var(--ink);
  border-radius: 999px;
}

.gx-card {
  padding: 1.25rem 1.4rem 1.5rem;
  margin-block-start: 1.5rem;

  /* The site header is sticky, so a jump would otherwise land with the card's
     own heading hidden underneath it. */
  scroll-margin-block-start: 4.5rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
}

.gx-head {
  display: flex;
  gap: 1rem;
  align-items: start;
  margin-block-end: 1.1rem;
}

.gx-head h2 {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  font-stretch: 112%;
}

.gx-head p {
  max-inline-size: 66ch;
  margin: 0.2rem 0 0;
  font-size: 0.87rem;
  color: var(--ink-soft);
}

.gx-also {
  margin: 0.3rem 0 0;
  font-size: 0.85em;
  color: var(--ink-soft);
}

.gx-head .ui-btn {
  flex: none;
  margin-inline-start: auto;
  white-space: nowrap;
  text-decoration: none;
}

/* brands.html's section head carries the OEM's mark instead of a glyph. */
.bb-logo {
  flex: none;
  inline-size: 3.5rem;
  block-size: auto;
}

/* Every mark on the page (the index tile, the section head, the variant
   strip's chips) gets its own plate under it - see --mark-plate at the top
   of this file for why: the marks are black-on-transparent, --surface
   inverts in dark mode, and most of them nearly vanished on it. */
.bb-tile img,
.bb-logo,
.wb-variants button img {
  padding: 0.15em;
  background: var(--mark-plate);
  border-radius: 4px;
}

/* One of these per pattern a brand section shows — a heading and the same
   "open in the builder" link the pattern gets on patterns.html, above the
   live stage. */

/* Wider than the patterns page (78rem): a section here holds two or more
   live stages in a row of chrome, and 1440px gives the stage room either side
   of a desktop-width bar. */
.bb-doc {
  max-inline-size: 90rem;
}

.bb-block {
  margin-block-start: 1rem;
}

/* The code under each stage: closed by default (the page is for looking), the
   builder's own code bar and Monokai box when opened. */
.bb-code {
  margin-block-start: 0.5rem;
}

.bb-code > summary {
  inline-size: fit-content;
  padding: 0.32rem 0.8rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  border: 1px solid var(--rule);
  border-radius: 6px;
}

.bb-code > summary:hover {
  color: var(--on-signal);
  background: var(--signal);
  border-color: var(--signal);
}

.bb-code[open] > summary {
  margin-block-end: 0.5rem;
}

/* A five-pane bar is ~380 lines; the box scrolls rather than swallowing the
   page, and the copy buttons stay in reach at its top. */
.bb-code .g-code {
  max-block-size: 60vh;
  margin-block-start: 0;
  overflow: auto;
}

.bb-block-head {
  display: flex;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  margin-block-end: 0.5rem;
}

.bb-block-head h3 {
  margin: 0;
  font-size: 1rem;
}

/* Same light artboard the builder previews on, for the same reason. */

/* The same base type the builder's preview frame sets, and for the same reason:
   a card sizes everything in em off the carousel root, so the page's own font
   decides how big every one of them comes out. Rendered in the demo's Archivo
   15px this catalogue drew cards a typeface and a size away from what the
   builder showed for the identical pattern - same generator, two answers.
   Arial 14 is what a Bootstrap 3 storefront hands them. Keep in step with
   FRAME_DOC in workbench.js; html{font-size} is deliberately NOT copied,
   because the engine and card CSS carry no rem at all (enforced by
   unit-disallowed-list), so it changes nothing here. */

/* The lightbox card, shown OPEN. A <dialog open> is not a modal - no backdrop,
   no top layer - but it keeps the UA's centring and its auto width, which on
   this page put a 1100px panel half outside its card. Unset both so it lays out
   like the block it is standing in for, and let the card's own dark background
   read as the overlay it is. gallery.js is what opens it; see the note there
   for why the closed trigger was not an example of anything. */
.gx-stage .gx-lb-inline {
  position: static;
  inline-size: 100%;
  max-inline-size: none;
  margin: 0;
}

/* [hidden] alone does not hide it: .cargo-lb-open is display: inline-flex in the
   pattern's own CSS, and a display beats the UA's [hidden] rule - the same trap
   the engine documents for its own controls. gallery.js hides rather than
   removes it, because the pattern's script binds a click to it. */
.gx-stage .cargo-lb-open[hidden] {
  display: none;
}

/* The real dialog is a 1100px panel on a dark backdrop, and the photo inside it
   is only ever as tall as the window allows. Standing in a card it has no window
   to be bounded by, so it grew to a full-width 16:10 and made one example taller
   than the four around it. Cap the picture, not the panel: the point of the card
   is the arrangement - dark surround, header, close - not the photograph's size. */
.gx-stage .gx-lb-inline .cargo-photo img {
  max-block-size: 42vh;
}

/* The stage also carries the platform's theme layer, as page scaffolding for
   the Patterns and Brands pages: the four theme tokens every DealerOn :root
   defines, and below, Bootstrap 3's .h1 size class and .btn/.btn-lg and the
   platform's .btn-cta built from the tokens. The tabbed bar's heading and
   button name these and ship no values of their own. Same rules as THEME_CSS
   in workbench.js (the builder's frame); a brand's `theme` in brands.js
   overrides the tokens on its own stages. Nothing here is in any snippet. */
.gx-stage {
  --cta-background-color: #16324f;
  --cta-font-color: #fff;
  --cta-hover-color: #0e2438;
  --main-color: #262626;

  padding: 1.25rem;
  overflow: hidden;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 14px;
  color: #10151c;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.gx-stage .h1 {
  margin: 20px 0 10px;
  font-size: 36px;
  font-weight: 500;
  line-height: 1.1;
}

.gx-stage .btn {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4286;
  vertical-align: middle;
  text-align: center;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 4px;
}

.gx-stage .btn-lg {
  padding: 10px 16px;
  font-size: 18px;
  line-height: 1.3333;
  border-radius: 6px;
}

.gx-stage .btn-cta {
  color: var(--cta-font-color);
  background-color: var(--cta-background-color);
  border-color: var(--cta-background-color);
}

.gx-stage .btn-cta:hover,
.gx-stage .btn-cta:focus {
  color: var(--cta-font-color);
  background-color: var(--cta-hover-color);
  border-color: var(--cta-hover-color);
}

/* The lead paragraph's classes and the phone-only span a tab name can carry,
   Bootstrap 3's own again. */
.gx-stage .lead {
  margin: 0 0 20px;
  font-size: 16px;
  font-weight: 300;
  line-height: 1.4;
}

@media (width >= 768px) {
  .gx-stage .lead {
    font-size: 21px;
  }
}

.gx-stage .text-muted {
  color: #777;
}

/* The platform's dark band and its large-heading class, as bg-main and
   heading-lg draw on a storefront. */
.gx-stage .bg-main {
  color: #fff;
  background-color: var(--main-color);
}

.gx-stage .bg-main .btn-cta {
  color: #fff;
  background-color: transparent;
  border-color: #fff;
}

.gx-stage .bg-main .btn-cta:hover,
.gx-stage .bg-main .btn-cta:focus {
  color: var(--main-color);
  background-color: #fff;
  border-color: #fff;
}

.gx-stage .heading-lg {
  font-size: 42px;
  line-height: 1.1;
}

@media (width <= 767px) {
  .gx-stage .hidden-xs {
    display: none !important;
  }
}

/* The white card is scaffolding for looks that assume a light page. A look that
   paints its own strip supplies that itself, so the frame just doubles up. */
.gx-stage--bare {
  padding: 0;
  color: inherit;
  background: none;
  border: 0;
}

@media (width <= 700px) {
  .gx-head {
    flex-wrap: wrap;
  }

  .gx-head .ui-btn {
    margin-inline-start: 0;
  }

  /* A phone is too narrow for two 11.5rem tiles, so auto-fill drops to one and
     the index becomes seventeen rows - a screenful of jump list before the
     first example. Two fixed columns instead, with names wrapping rather than
     ellipsising, since a wrapped name still reads and a clipped one does not. */
  .gx-index {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .gx-tile-name {
    overflow: visible;
    white-space: normal;
  }

  /* Three nested paddings - page, card, stage - took 78px off each side of a
     390px phone, so every example rendered in 273px and drew its own
     conclusions from it: the tabbed bar stacked all three tabs on separate
     rows and its cards came out 70px wide. None of that happens on a real
     page, where the same slider gets the full width and puts the tabs on one
     row. The examples were being judged in a box no dealer page has, so the
     chrome gets out of the way here instead. */
  .gx-doc {
    padding-inline: 0.75rem;
  }

  .gx-card {
    padding-inline: 0.6rem;
  }

  .gx-stage {
    padding: 0.7rem;
  }
}

/* ---- narrower screens -------------------------------------------------- */

@media (width <= 900px) {
  .ui-shell {
    grid-template-columns: 1fr;
  }

  .ui-rail {
    position: static;
    max-block-size: none;
    padding: 1rem;
    border-block-end: 1px solid var(--rule);
    border-inline-end: 0;
  }

  #wb-nav {
    flex-flow: row wrap;
  }

  .ui-main {
    padding: 1.25rem 1rem 3rem;
  }
}

@media (width <= 600px) {
  .wb-stage {
    padding: 0.5rem 0.25rem;
  }

  .ui-title {
    font-size: 1.5rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ui-gauge i {
    transition: none;
  }
}

/* ---- pane ----------------------------------------------------------------
   The --tp-* theme variables live on the #wb-settings rule above - one rule,
   since stylelint's no-duplicate-selectors won't allow a second #wb-settings
   block. This is just the pane root's own sizing: full width of the panel, the
   .tp-dfwv class confirmed against node_modules/tweakpane/dist/tweakpane.js. */
#wb-settings .tp-dfwv {
  inline-size: 100%;
  min-inline-size: 0;
}

/* The panel is the full width of the page now, and a pane row spends every
   pixel it is given on its LABEL: Tweakpane hands the value
   --tp-blade-value-width (11rem, set above) and lets the label take the rest.
   Measured at 1440 with the settings under the preview: "Phone · under 768"
   ran 217 to 1227 with its slider starting at 1227 - a metre of dotted nothing
   between a label and the thing it names, on every row of every folder. The
   old 26rem column hid it rather than fixed it.

   So the FOLDERS flow into columns instead of each row stretching. 23rem is
   picked off the arithmetic rather than by eye: it is the widest column that
   still gives a 1024 window two of them (784px of panel, two 380px columns),
   and it gives 1440 three at about the 24rem the two-column panel used to be -
   which is the width these rows were drawn for.

   .tp-rotv_c is the pane root's container and the folders' actual parent -
   read off the live DOM, not assumed: pane.js passes its own container, so
   .tp-dfwv (the wrapper Tweakpane makes when it is not given one) is not in
   this tree at all. */
#wb-settings .tp-rotv_c {
  columns: 23rem;
  column-gap: 1.5rem;
}

/* Never split a folder across a column boundary. This is the failure mode the
   demo's previous columns layout had - a folder heading stranded at the foot
   of one column with its controls starting the next - and it is the whole
   reason the rule above is safe to have. */
#wb-settings .tp-fldv {
  -webkit-column-break-inside: avoid;
  break-inside: avoid;
}

/* Every section stays open, so the fold has to go - see the note on folder() in
   pane.js for why this is CSS and not an option. Tweakpane 4.0.5 offers no
   non-collapsible folder, and its click listener is registered on this button
   in the controller's constructor, so the click is starved rather than unbound.
   pointer-events takes the hover styling with it, which is right: nothing here
   responds any more. pane.js puts tabindex="-1" on the same button, because it
   is a real <button> and would otherwise still answer a Tab and an Enter. */
#wb-settings .tp-fldv_b {
  pointer-events: none;
  cursor: default;
}

/* The chevron. It only ever meant "this opens and closes". */
#wb-settings .tp-fldv_m {
  display: none;
}
