/* Header, oscillator, effects and modulators sections. The grid is measured
   in slider units: oscillator, Dist and Output columns are 2, Filter is 3,
   Reverb is 1, 20 in all. Each Modulators row column is 4. */

:root {
  --ink: #000;
  --paper: #fff;
  --track: #ececec;
  --track-hover: #d6d6d6; /* a slider's track under the pointer: strip, Modulators and Route sliders */
  --focus: #888;
  /* Bee yellows, light to deep. Canvas drawing reads these too (CONFIG names them). */
  --yellow-pale: #FFDB8F; /* h2 header bars, the blank bar above Output */
  --yellow: #F7BC69; /* the Wave scope and Orbit trace dots */
  --yellow-pulse: #FACF7A; /* the positive Mod pulse on Destination thumbs */
  /* Mod pulse ramp: black to ember, then ember to the yellow, in OKLCH, so
     partial fills read warm orange rather than olive. --pulse-ember-at is
     where on the ramp (0-1) the ember sits. */
  --pulse-ember: #A8571A; /* positive, on the way to --yellow-pulse */
  --pulse-ember-deep: #8A3414; /* negative, on the way to --yellow-deep */
  --pulse-ember-at: 0.5;
  --yellow-deep: #EDA95C; /* the glow around those dots */
  --max-width: 1512px;
  --slider-height: clamp(280px, 45vh, 400px);
  /* Height of the Modulators row's controls, below its header bars. */
  --mod-height: calc(var(--slider-height) * 0.6);
  /* Header bars and control labels shrink with the window: full size at the
     page's max width (1512px), down to a floor of 8px. */
  --head-size: clamp(0.5rem, 0.79vw, 0.75rem);
  --label-size: clamp(0.5rem, 0.73vw, 0.6875rem);
  /* Wave selector icon: 20px at max width, with the cell sized from it. */
  --radio-icon: clamp(0.8rem, 1.32vw, 1.25rem);
  /* Slider sizes, shared by the strip, the Modulators row and the matrix. */
  --slider-thumb: 20px; /* thumb length along the track; across it for the Route bar */
  --slider-track: 12px;
  /* Slider fills: a strip group's second and third sliders, and the Route
     slider's negative and positive sides. */
  --fill-2: #333;
  --fill-3: #444;
  --route-neg: #362719;
  --route-pos: #362719;
  --label-gap: 0.4rem; /* a control's label (or the Level's speaker icon) below its slider */
  /* Notes column and matrix block: column widths, padding, colors and the
     Route thumb's width. */
  --notes-size: clamp(0.625rem, 0.93vw, 0.875rem); /* Notes text; 14px at max width */
  --notes-copy-scale: 0.9em; /* Notes copy, relative to --notes-size */
  --notes-pad-x: 0.75rem; /* Notes column's side padding */
  --mod-gap: 0.4rem;
  --mod-pad-y: 0.5rem; /* top and bottom of each destination block */
  --mod-section-gap: 8px; /* outside the first and last block of a section, clear of the hover fill */
  --mod-dest-lit: #333; /* a destination's block in the Destination column while its row is hovered */
  /* Dotted rule between destinations of one matrix section. Hidden for now;
     set it to a light gray to show it. */
  --rule-soft: transparent;
  --mod-thumb-width: 10px; /* a bar with curved ends; many circles read too strong */
  --mod-slider-inset: 0; /* Route slider to its Source column's borders */
  --mod-track-height: 16px; /* a Route slider's well; the strip's is --slider-track */
  --mod-track-radius: 0; /* corners of a Route slider's well: square */
  --mod-fill-inset: 3px; /* the fill's gap to the well, top and bottom */
  --mod-dot-size: calc(var(--slider-thumb) - 2px); /* the circle thumb; 2px under the strip's */
  /* The sign drawn in the circle thumb, white at 80%, in its 20-unit space. */
  --mod-sign-pos: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M6.5,10H13.5M10,6.5V13.5' stroke='white' stroke-opacity='0.8' stroke-width='2'/%3E%3C/svg%3E");
  --mod-sign-neg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M6.5,10H13.5' stroke='white' stroke-opacity='0.8' stroke-width='2'/%3E%3C/svg%3E");
  --mod-thumb-rim: rgb(255 255 255 / 0.8); /* the thin-bar thumb's rim */
  --mod-zero-tick: 2px; /* width of the tick at 0 inside the well */
  --mod-zero-tick-color: var(--ink); /* the tick at 0 inside the well */
  --mod-dest-span: 2; /* Destination column, in slider units */
  --mod-source-span: 3; /* each Source column, in slider units */
  --mod-span: calc(var(--mod-dest-span) + 5 * var(--mod-source-span)); /* the whole matrix: Destination column + 5 Source columns */
  --notes-span: calc(20 - var(--mod-span)); /* the Notes column beside the matrix: the rest of the 20 units */
  --head-icon: 1.07em;
  /* The circle thumb's ring on the strip and Modulators sliders: white, or black
     while the thumb pulses. */
  --thumb-ring: white;
  --thumb-ring-ink: black;
--thumb-shadow: 0 1px 3px rgb(0 0 0 / 0.3); /* the circle thumb's soft shadow, as iPad Safari draws it */
  --font-sans: "Scoutie Sans", system-ui, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  background: var(--paper);
  color: var(--ink);
}

#instrument {
  display: grid;
  grid-template-columns: repeat(20, minmax(0, 1fr));
  max-width: var(--max-width);
  margin: 0 auto;
  border-bottom: 1px solid var(--ink);
  font-family: var(--font-mono);
}
/* Side borders only once the viewport is wider than --max-width (a media query
   can't read the variable, so 1512px is repeated here). */
@media (min-width: 1513px) {
  #instrument { border-inline: 1px solid var(--ink); }
}

/* Wrappers whose cells sit directly in the page grid: the header (row 1),
   the strip and the Modulators row. */
#instrument > header,
#strip,
#strip > div,
#modulators,
#modulators > div {
  display: contents;
}

h1 {
  grid-column: span 8;
  margin: 0;
  padding: 1.5rem 5%;
  display: flex;
  align-items: center;
  border-right: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
}
h1 img { display: block; width: 100%; height: auto; }

/* The Output scope's cell: 10 units, the canvas filling it. A light dot grid
   sits behind the trace and is always visible, since the canvas (not the
   cell) fades in and out. */
.header-scope {
  --dot-gap: 16px;
  --dot-size: 1px;
  --dot-alpha: 15%;
  position: relative;
  grid-column: span 10;
  background-image: radial-gradient(
    circle at center,
    color-mix(in srgb, var(--ink) var(--dot-alpha), transparent) var(--dot-size),
    transparent calc(var(--dot-size) + 0.5px)
  );
  background-size: var(--dot-gap) var(--dot-gap);
  background-position: center;
  border-right: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
}

.header-scope .output-scope,
.header-scope .canvas-box { position: absolute; inset: 0; }

.header-actions {
  grid-column: span 2;
  display: flex;
  flex-direction: column;
  border-bottom: 1px solid var(--ink);
}
.header-actions button {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  cursor: pointer;
  background: var(--paper);
}
.header-actions img { width: 64px; height: 64px; }
#transport { background: var(--ink); }
#transport:disabled { cursor: default; }
.header-actions button:focus-visible { outline: 3px solid var(--focus); outline-offset: -6px; }

/* Row 2: section labels, and Under the hood's. The Preset drawer's title
   sits deeper, so it is not one. */
:is(#instrument, .under-the-hood) > h2 {
  margin: 0;
  padding: 0.75rem 0.4rem 0.5rem;
  font-size: 1.5rem;
  font-weight: 700;
  background: var(--yellow-pale);
  border-bottom: 1px solid var(--ink);
}
.label-oscillators { grid-column: span 12; border-right: 1px solid var(--ink); }
.label-effects { grid-column: span 6; border-right: 1px solid var(--ink); }
.label-output { grid-column: span 2; background: var(--yellow-pale); border-bottom: 1px solid var(--ink); }

/* A column of controls under a header bar, in any row. The last in each row
   drops its border. */
.group {
  min-width: 0;
  border-right: 1px solid var(--ink);
}

/* Row 3: one group per column. */
#strip .group { grid-column: span 2; }
#strip .group[data-group="master"] { border-right: 0; }
#strip .group[data-group="filter"] { grid-column: span 3; }
#strip .group[data-group="reverb"] { grid-column: span 1; }

/* The heading is a black bar across the top of its column. The Notes bar is
   blank, like the Destination column's, so the bars run unbroken across the
   20 units. The matrix's Destination column takes the bars' type and colors. */
:is(#strip, #modulators, #matrix, #under-the-hood) h3,
#notes .notes-bar,
.mod-dest {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: var(--head-size);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
:is(#strip, #modulators, #matrix, #under-the-hood) h3,
#notes .notes-bar {
  margin: 0;
  padding: 0.2rem 0.4rem;
  font-weight: 700;
}

/* Icons in header bars and Destination headers, one --head-icon square. */
#oscillators .group h3::before,
#effects .group:not([data-group="reverb"]) h3::after,
.mod-head[data-icon]::before {
  content: "";
  display: inline-block;
  width: var(--head-icon);
  height: var(--head-icon);
  vertical-align: middle;
}
#oscillators .group h3::before,
#effects .group:not([data-group="reverb"]) h3::after {
  margin-block: -0.4rem; /* keeps the icon from growing the line box */
  position: relative;
  top: -0.1rem; /* middle sits on the x-height; the labels are all caps */
}

/* Oscillator headers read "[sine icon]_01", the icon flush against the underscore. */
#oscillators .group h3::before {
  background: url("../assets/icn-wave-sine.svg") no-repeat center / contain;
}

/* Effect headers carry an arrow to show the signal flowing on to the next
   stage. Reverb is the last effect, so it has none. */
#effects .group:not([data-group="reverb"]) h3::after {
  margin-left: 0.3rem;
  opacity: 0.8;
  background: url("../assets/icn-arrow-right.svg") no-repeat center / contain;
}

#strip .group-controls { display: flex; }
#strip .control {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  padding: 0.6rem 0 0.4rem;
  align-items: center;
}
#strip .group[data-group="master"] .control { flex: 0 0 50%; }

/* The speaker icon stands in for the meter's label, at the label's size. */
#strip .control.level > img {
  order: 2;
  width: 1rem;
  height: 1rem;
  margin-top: var(--label-gap);
}

/* Level meter: LED segments filling the Output column's second slider width.
   Each segment's lit layer fades in and out; the timings come from config. */
.segments {
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  width: var(--track-width);
  height: 100%;
}
.seg {
  flex: 1 1 0;
  position: relative;
  background: var(--track);
}
.seg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  opacity: 0;
  transition: opacity var(--fade-out) ease-out;
}
.seg.on::after {
  opacity: 1;
  transition-duration: var(--fade-in);
}

/* The label sits under the slider. */
:is(#strip, #modulators) .control > label {
  order: 2;
  margin-top: var(--label-gap);
  font-size: var(--label-size);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* No visible readouts in the strip or the Modulators row; the value is in
   aria-valuetext. */
:is(#strip, #modulators) output { display: none; }

/* Ticks are drawn beside the slider; --thumb is the thumb's length along the
   track, so they line up with the thumb's centre at each value. --at is that
   centre for the element's own --pos, used by ticks and the track fill.
   The Route slider's thumb is narrower along its track, so it sets --tw. */
.ticked {
  position: relative; display: flex;
  --thumb: var(--slider-thumb);
  --track-width: var(--slider-track);
}
.ticked > *,
.mod-slider input {
  --at: calc(var(--tw, var(--thumb)) / 2 + (100% - var(--tw, var(--thumb))) * var(--pos, 0.5));
}
#strip .ticked { height: var(--slider-height); }
.tick {
  position: absolute; left: calc(100% + 2px); width: 5px; height: 1px;
  background: currentColor;
  bottom: calc(var(--at) - 0.5px);
}
.tick.major { width: 10px; }

/* Vertical sliders, minimum at the bottom. The track is filled up to the
   thumb's centre (--pos is set from script). */
:is(#strip, #modulators) input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  writing-mode: vertical-lr;
  direction: rtl;
  width: var(--track-width);
  height: 100%;
  margin: 0;
  background: transparent;
  cursor: ns-resize;
}
/* Under the pointer the unfilled track darkens a step, as on the Route sliders. */
:is(#strip, #modulators) input[type="range"]:hover:not(:disabled) { --track: var(--track-hover); }
:is(#strip, #modulators) input[type="range"]::-webkit-slider-runnable-track {
  width: var(--track-width);
  height: 100%;
  border-radius: calc(var(--track-width) / 2);
  background: linear-gradient(to top, var(--fill, var(--ink)) var(--at), var(--track) var(--at));
}
/* Sliders sharing a strip group step lighter: first --ink, then --fill-2, --fill-3. */
#strip .group-controls > .control:nth-child(2) { --fill: var(--fill-2); }
#strip .group-controls > .control:nth-child(3) { --fill: var(--fill-3); }
/* A circle with a 2px white ring (a border; the fill is clipped to the padding box).
   Mod pulse: --pulse (0-1, default 0, set from script) moves the fill from
   black through --pulse-ember to --yellow-pulse, or through
   --pulse-ember-deep to --yellow-deep with data-side="neg". At 0 it is solid
   black, as before. While it pulses (data-side is set, level above 0) the
   rim is black. */
:is(#strip, #modulators) input[type="range"] { --thumb-rim: var(--thumb-ring); --pulse-ember-to: var(--pulse-ember); --pulse-to: var(--yellow-pulse); }
:is(#strip, #modulators) input[type="range"][data-side] { --thumb-rim: var(--thumb-ring-ink); }
:is(#strip, #modulators) input[type="range"][data-side="neg"] { --pulse-ember-to: var(--pulse-ember-deep); --pulse-to: var(--yellow-deep); }
:is(#strip, #modulators) input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: var(--thumb);
  height: var(--thumb);
  margin-left: calc((var(--track-width) - var(--thumb)) / 2);
  /* Black to ember over 0..--pulse-ember-at, ember to yellow above it. */
  --pulse-t1: clamp(0, var(--pulse, 0) / var(--pulse-ember-at), 1);
  --pulse-t2: clamp(0, (var(--pulse, 0) - var(--pulse-ember-at)) / (1 - var(--pulse-ember-at)), 1);
  --thumb-fill: color-mix(in oklch, var(--pulse-to) calc(var(--pulse-t2) * 100%),
    color-mix(in oklch, var(--pulse-ember-to) calc(var(--pulse-t1) * 100%), var(--ink)));
}
/* The circle itself, shared with the Route slider's circle thumb, which
   sets its own --thumb-fill and --thumb-rim and draws a --mod-sign on top. */
:is(#strip, #modulators) input[type="range"]::-webkit-slider-thumb,
.mod-slider input[type="range"]:is(:hover, [data-side="neg"], [data-side="pos"])::-webkit-slider-thumb {
  box-sizing: border-box;
  border: 2px solid var(--thumb-rim);
  border-radius: 50%;
  background: var(--mod-sign, none) center / 100% 100% no-repeat padding-box, var(--thumb-fill) padding-box;
  box-shadow: var(--thumb-shadow);
}
:is(#strip, #modulators, #matrix) input[type="range"]:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
:is(#strip, #modulators) input[type="range"]:disabled { opacity: 0.35; cursor: default; }

/* Row 4: the Modulators row. Five 4-unit columns, the two LFOs then
   the three Chaos orbits, with the strip's header bars. */
.label-modulators { grid-column: span 20; border-top: 1px solid var(--ink); }
#modulators .group { grid-column: span 4; }
#sloths .group:last-child { border-right: 0; }
#modulators .group-controls {
  height: var(--mod-height);
  padding: 0.6rem 0 0.4rem;
}
#sloths .group-controls { display: flex; padding-inline: 0.6rem; }
#sloths .control.trace { flex: 1 1 0; min-width: 0; }

/* Row 5: the modulation matrix, one table on 17 of the 20 units, then the
   Notes column. The Destination column holds the labels, then one Source
   column of Route sliders per source. Its rows (--mod-rows, set from script)
   are shared through row subgrids; see docs/features/modulation-matrix.md. */
.label-modulation { grid-column: span var(--mod-span); border-top: 1px solid var(--ink); }
.label-notes { grid-column: span var(--notes-span); border-top: 1px solid var(--ink); border-left: 1px solid var(--ink); }
.label-notes img { display: block; width: 1.5rem; height: 1.5rem; }
#matrix {
  grid-column: span var(--mod-span);
  display: grid;
  grid-template-columns: subgrid;
  grid-template-rows: repeat(var(--mod-rows), auto);
}
.mod-col {
  grid-row: 1 / -1;
  display: grid;
  grid-template-rows: subgrid;
  min-width: 0;
  border-right: 1px solid var(--ink);
}
.mod-col.mod-dest { grid-column: span var(--mod-dest-span); }
.mod-col.group { grid-column: span var(--mod-source-span); }
/* Source names centred over their sliders. */
.mod-col.group h3 { text-align: center; }
.mod-col:last-child { border-right: 0; }
/* The Destination column's header bar: blank, so the bars run unbroken. */
.mod-dest-bar { grid-column: 1 / -1; }
/* Each section's blocks sit --mod-section-gap clear of the header bar, the
   table's foot and the next section. No rule between sections. */
.mod-section,
.mod-block {
  display: grid;
  grid-template-rows: subgrid;
}
.mod-section { padding: var(--mod-section-gap) 0; }
.mod-block {
  align-items: center;
  padding: var(--mod-pad-y) var(--mod-gap);
}
.mod-block + .mod-block { border-top: 1px dotted var(--rule-soft); }
/* Route sliders run --mod-slider-inset from their Source column's borders. */
.mod-col.group .mod-block { padding-inline: var(--mod-slider-inset); }
/* The Destination column is white on black, like the Source header bars
   (colors and type from the header bar rule). Its header and parameter
   sub-columns are one page unit each (column subgrids down to the block),
   labels right-aligned in each. An oscillator's header spans its Freq and
   Amp rows. */
.mod-dest {
  white-space: nowrap;
  text-align: right;
}
.mod-dest,
.mod-dest .mod-section,
.mod-dest .mod-block { grid-template-columns: subgrid; }
.mod-dest .mod-section,
.mod-dest .mod-block { grid-column: 1 / -1; }
.mod-block.lit { background: var(--mod-dest-lit); }
/* Destination headers and parameters set like the Source header bars' names
   (type from .mod-dest; parameters at regular weight). */
.mod-head { grid-column: 1; align-self: start; padding: calc((var(--slider-thumb) - 1lh) / 2) var(--mod-gap) 0 0; font-weight: 700; }
.mod-param { grid-column: 2; }
.mod-slider {
  min-width: 0;
  --thumb: var(--slider-thumb);
  --tw: var(--mod-thumb-width);
  --track-width: var(--mod-track-height);
}
/* Horizontal Route slider in the strip's look. 0 is the centre; the fill (--route-neg
   below 0, --route-pos above) runs from the centre to the thumb's centre (--pos is set
   from script), --mod-fill-inset shorter than the well top and bottom. A gray
   tick marks 0 across the well, over the fill; at 0 the thumb covers it. */
.mod-slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: var(--thumb);
  margin: 0;
  display: block;
  background: transparent;
  cursor: ew-resize;
}
.mod-slider input[type="range"]::-webkit-slider-runnable-track {
  height: var(--track-width);
  border-radius: var(--mod-track-radius);
  background:
    linear-gradient(var(--mod-zero-tick-color), var(--mod-zero-tick-color)) center / var(--mod-zero-tick) 100% no-repeat,
    linear-gradient(to right,
      transparent min(50%, var(--at)),
      var(--route-neg) min(50%, var(--at)) 50%,
      var(--route-pos) 50% max(50%, var(--at)),
      transparent max(50%, var(--at)))
      center / 100% calc(100% - 2 * var(--mod-fill-inset)) no-repeat,
    var(--track);
}
.mod-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: var(--tw);
  height: var(--thumb);
  margin-top: calc((var(--track-width) - var(--thumb)) / 2);
  border: 2px solid var(--mod-thumb-rim);
  border-radius: var(--tw);
  background: var(--ink) padding-box;
  box-sizing: border-box;
  box-shadow: none;
}
/* The thumb stays black on both sides of zero; its rim parts it from the
   fill, so it reads as a handle. Under the pointer the track darkens a step.
   Away from 0, and under the pointer at 0, the thumb is the strip's circle
   (black, white ring; --tw widens to match so the fill still ends at its centre).
   See docs/mockups/prototype-route-sliders.html (C). */
.mod-slider input[type="range"]:hover { --track: var(--track-hover); }
.mod-slider input[type="range"]:is(:hover, [data-side="neg"], [data-side="pos"]) { --tw: var(--mod-dot-size); }
/* A + or - in the circle for the route's polarity; none at 0. */
.mod-slider input[type="range"][data-side="pos"] { --mod-sign: var(--mod-sign-pos); }
.mod-slider input[type="range"][data-side="neg"] { --mod-sign: var(--mod-sign-neg); }
.mod-slider input[type="range"]:is(:hover, [data-side="neg"], [data-side="pos"])::-webkit-slider-thumb {
  height: var(--mod-dot-size);
  margin-top: calc((var(--track-width) - var(--mod-dot-size)) / 2);
  --thumb-rim: var(--thumb-ring);
  --thumb-fill: var(--ink);
}
/* The self-route placeholder: the slider's look with a faded thumb at the
   centre and no fill. Inert; it is not a control. */
.mod-slider.inert {
  display: flex;
  align-items: center;
  height: var(--thumb);
  position: relative;
  pointer-events: none;
}
.mod-slider.inert::before {
  content: "";
  flex: 1;
  height: var(--track-width);
  border-radius: var(--mod-track-radius);
  background: var(--track);
  opacity: 0.6;
}
.mod-slider.inert::after {
  content: "";
  position: absolute;
  left: calc(50% - var(--tw) / 2);
  width: var(--tw);
  height: var(--thumb);
  border: 2px solid var(--paper);
  border-radius: var(--tw);
  background: var(--ink) padding-box;
  box-sizing: border-box;
  opacity: 0.2;
}
/* Section marker icons (data-icon, set from MATRIX_SECTIONS in ui.js): the
   sine on every oscillator header, the lowpass curve on LP, a space
   before the label. */
.mod-head[data-icon="wave-sine"] { --head-img: url("../assets/icn-wave-sine.svg"); } /* drawn for a black bar */
.mod-head[data-icon="filter-lp"] {
  --head-img: url("../assets/icn-filter-lp.svg");
  --head-img-filter: invert(1); /* the curve is drawn black */
}
.mod-head[data-icon]::before {
  margin-right: 1ch;
  filter: var(--head-img-filter, none);
  background: var(--head-img) no-repeat center / contain;
}

/* The Notes column beside the matrix: short notes on the controls, in Scoutie Sans.
   Each note is its key, icon or term as a header, the copy under it. */
#notes {
  grid-column: span var(--notes-span);
  min-width: 0;
  border-left: 1px solid var(--ink);
}
#notes ul {
  display: grid;
  row-gap: 0.75em;
  margin: 0;
  padding: var(--mod-section-gap) 0;
  list-style: none;
  font-family: var(--font-sans);
  font-size: var(--notes-size);
  font-weight: 400;
  line-height: 1.4;
}
#notes li {
  display: grid;
  row-gap: 0.15em;
  padding-inline: var(--notes-pad-x);
}
#notes li > span:last-child {
  font-size: var(--notes-copy-scale); /* the copy a touch smaller than its header */
}
#notes a {
  color: inherit;
  text-underline-offset: 0.15em;
}
#notes kbd {
  padding: 0 0.3em;
  border: 1px solid var(--ink);
  border-radius: 2px;
  font: inherit;
}
#notes img {
  width: 1.4em;
  height: 1.4em;
  vertical-align: -0.35em;
}

/* A canvas that fills its box, which CSS sizes; script follows the box. */
.canvas-box { position: relative; width: 100%; height: 100%; }
.canvas-box canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* LFO column: the Wave scope over the Wave selector, taking three units; the
   Rate slider and RATE label in the fourth. The Slow toggle sits beside the
   foot of the slider, offset to its left. */
#lfos .group-controls {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 25%;
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas:
    "scope slider"
    "wave  label";
}
#lfos .control.scope { grid-area: scope; min-height: 0; padding: 0 0.6rem 0.6rem; }
#lfos .control.radio { grid-area: wave; align-self: end; justify-self: center; }
#lfos .control.icon-toggle {
  grid-row: 1 / 3;
  grid-column: 2;
  align-self: end;
  margin-bottom: calc(var(--label-size) + var(--label-gap));  /* level with the slider's foot */
  justify-self: center;
  transform: translateX(-1.5rem);
  z-index: 1;
}
/* The RATE label lines up with the bottom of the Wave selector. The slider
   runs down both rows and stops a label's height and gap above the bottom. */
#lfos .control.slider { display: contents; }
#lfos .control.slider > .ticked {
  grid-row: 1 / 3;
  grid-column: 2;
  justify-self: center;
  align-self: start;
  height: calc(100% - var(--label-size) - var(--label-gap));
  min-height: 0;
}
#lfos .control.slider > label { grid-area: label; justify-self: center; align-self: end; line-height: 1; }

/* Wave selector: a row of icon cells, the selected one inverted. The radio
   inputs are invisible but cover their cells, so they still take clicks,
   focus and arrow keys. */
.radios { display: flex; border: 1px solid var(--ink); border-radius: 3px; }
.radios label:first-child { border-radius: 2px 0 0 2px; }
.radios label:last-child { border-radius: 0 2px 2px 0; }
.radios label {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--radio-icon) * 1.8);
  height: calc(var(--radio-icon) * 1.6);
  cursor: pointer;
}
.radios label + label { border-left: 1px solid var(--ink); }
.radios input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
/* brightness(0) makes every icon black whatever color it was drawn in. */
.radios img { width: var(--radio-icon); height: var(--radio-icon); pointer-events: none; filter: brightness(0); }
.radios label:has(input:checked) { background: var(--ink); }
.radios input:checked + img { filter: brightness(0) invert(1); }
.radios label:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; z-index: 1; }

/* Slow toggle: the timer icon alone, at the strip labels' icon size. */
.icon-toggle {
  display: flex;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.icon-toggle img { width: 1rem; height: 1rem; }
.icon-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Visually hidden, still announced. */
#status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
