/*
  Mode dial — self-contained styles.

  A rotary selector for a mode you stay in. Underneath it is a radio group in
  a <form>: arrow keys, clicks, a screen reader and a server all have it with
  no script. component.js adds the near-way wrap, the drag, the commit and
  the feel.

  One number turns everything. Eight `:has(… :nth-child(k) > :checked)` rules
  write `--mode-dial-sel` on the root and six `:nth-child(k):last-child` rules
  write `--mode-dial-n`; the three turning parts — the knurl, the engraving and
  the options layer — all rotate to `-(sel + laps·n + drag) · 360deg/n` on one
  transition, and each option counter-rotates by the same angle on the same
  transition, so names and icons stay upright in lockstep without @property.

  The face itself does not turn and is not a stacking context. That is what
  lets the options layer sit above the centre key, so a tooltip is never
  drawn under it, and it is why the lighting needs no counter-rotation: the
  light layer simply never moves, and the teeth slide through it.

  Supported: 3 to 8 options, written in clockwise order. 120 teeth divide
  evenly for every count but 7, which is supported and lands between teeth.

  Two finishes on one mechanism: the polished one by default, and
  .mode-dial--plain, which takes the material off — flat fills, hairlines,
  system type, a tick per detent — and composes with every other modifier.

  The feel is tokens too, read by component.js when it needs them: the drag's
  detent curve (--mode-dial-notch), the index's kick per detent passed, the
  key's travel, and an opt-in click (--mode-dial-click, a volume from 0 to 1;
  at 0 no audio is ever started). Set them on the root like any other.
  .mode-dial--ratchet gives the dial a voice of its own under that volume:
  the knurl's scratch, a grain per tooth, and a snap as the face lands.

  Rules (see CLAUDE.md): BEM on `.mode-dial`, no global selectors, no font
  loading, every tunable in the block below. Re-theme it from outside by
  setting these properties on the root itself.
*/

.mode-dial {
  /* --- geometry -------------------------------------------------------- */
  --mode-dial-size: 18rem;                         /* dial diameter */
  --mode-dial-pad: clamp(.75rem, 6cqi, 1.5rem);    /* resolved on the deck, against the root */
  --mode-dial-gap: 1rem;
  --mode-dial-screen-w: 14rem;
  --mode-dial-deck-radius: min(2rem, 9cqi);
  --mode-dial-r: 28cqi;                            /* option ring radius, in the dial's cqi */
  --mode-dial-hit-min: 24px;                       /* an option's own circle, which is its focus ring, not its target */
  --mode-dial-hit-max: 44px;
  --mode-dial-hit-in: 7.5cqi;                      /* the middle of a dial with no key, which is no option's: component.js starts no drag in it either */
  --mode-dial-hit-out: 38.5cqi;                    /* an option's slice reaches the plate's edge and stops, so the knurl stays the grip */
  --mode-dial-tip-reach: 6cqi;                     /* from the option centre to the tooltip's near edge, toward the centre */
  --mode-dial-tip-side: 3cqi;                      /* further, across only, so a wide word clears its own icon */
  --mode-dial-tip-radius: 2px;                     /* tooltip and display tags */
  --mode-dial-pip-reach: 7cqi;                     /* committed pip, outward */
  --mode-dial-teeth: 120;
  --mode-dial-width: calc(var(--mode-dial-size) + 3rem);

  /* --- state: script writes laps/drag/span inline; CSS writes n/sel/peek */
  --mode-dial-n: 6;
  --mode-dial-sel: 0;
  --mode-dial-laps: 0;
  --mode-dial-drag: 0;
  --mode-dial-span: 1;
  --mode-dial-peek: initial;                       /* guaranteed-invalid, so var() falls back */
  --mode-dial-step: calc(360deg / var(--mode-dial-n));
  --mode-dial-angle: calc((var(--mode-dial-sel) + var(--mode-dial-laps) * var(--mode-dial-n) + var(--mode-dial-drag)) * var(--mode-dial-step) * -1);
  --mode-dial-show: var(--mode-dial-peek, var(--mode-dial-sel));

  /* --- light: one azimuth, from vertical toward the upper left ---------- */
  --mode-dial-light: 27deg;
  --mode-dial-sx: sin(var(--mode-dial-light));     /* .454 */
  --mode-dial-sy: cos(var(--mode-dial-light));     /* .891 */

  /* --- colour: the component's own, the same in both themes ------------ */
  --mode-dial-deck: #34363a;
  --mode-dial-well: #1f2023;
  --mode-dial-seam: #141517;
  --mode-dial-face: #2b2d31;
  --mode-dial-tooth-hi: #5d6065;
  --mode-dial-tooth-mid: #2e3034;
  --mode-dial-tooth-lo: #1c1d1f;
  --mode-dial-paint: #b0ada6;
  --mode-dial-paint-hover: #d8d5cd;
  --mode-dial-paint-on: #f1efe8;
  --mode-dial-signal: #f0965f;
  --mode-dial-cap: #3a3d41;
  --mode-dial-cap-lit: #44474c;
  --mode-dial-cap-low: #26282b;
  --mode-dial-glass: #b3b9a2;
  --mode-dial-ink: #1b1f17;
  --mode-dial-gleam: #fff;                         /* every highlight is this, at an alpha */
  --mode-dial-shade: #000;                         /* every shading is this, at an alpha */
  --mode-dial-cast: #12100c;                       /* the warm cast shadow on the ground */
  --mode-dial-shadow-contact: color-mix(in srgb, var(--mode-dial-cast) 40%, transparent);
  --mode-dial-shadow-key: color-mix(in srgb, var(--mode-dial-cast) 42%, transparent);
  --mode-dial-shadow-body: color-mix(in srgb, var(--mode-dial-shade) 60%, transparent);
  --mode-dial-lit: color-mix(in srgb, var(--mode-dial-gleam) 14%, transparent);

  /* --- cloth: in pixels, so a small dial has fewer dots, not smaller ---- */
  --mode-dial-dot-pitch: 4px;
  --mode-dial-dot-r: calc(var(--mode-dial-dot-pitch) * .25);
  --mode-dial-dot-lit: color-mix(in srgb, var(--mode-dial-gleam) 15%, transparent);
  --mode-dial-dot-shade: color-mix(in srgb, var(--mode-dial-shade) 50%, transparent);

  /* --- plain finish, read only by .mode-dial--plain -------------------- */
  --mode-dial-rim: #f4f3f0;                        /* the turning ring */
  --mode-dial-rim-mark: #77746d;                   /* one tick per detent on it */
  --mode-dial-rim-mark-w: max(.5deg, atan2(1px, 42cqi));     /* a tick's width at the rim, never under one CSS pixel */
  --mode-dial-rim-mark-soft: max(.3deg, atan2(.6px, 42cqi)); /* each side of a tick, so a thin wedge does not alias */
  --mode-dial-rim-mark-len: 2.6cqi;                /* in from the rim's outer edge; the rim is 5cqi */
  --mode-dial-line: #cfccc5;                       /* every hairline */
  --mode-dial-hairline: 1px;                       /* their width: a thumbnail drawn at 0.6 restates it, or a 1px line falls under a device pixel and is lost */

  /* --- type ------------------------------------------------------------ */
  --mode-dial-font: "Mona Sans", "Arial Narrow", "Roboto Condensed", sans-serif;
  --mode-dial-mono: "Fragment Mono", ui-monospace, monospace;
  --mode-dial-label-size: max(11px, 4.4cqi);
  --mode-dial-label-stretch: 75%;
  --mode-dial-label-weight: 600;
  --mode-dial-label-weight-on: 700;
  --mode-dial-label-track: .12em;
  --mode-dial-curl-size: 4.6px;                    /* SVG user units = the dial's cqi */
  --mode-dial-curl-track: .14em;
  --mode-dial-curl-ring: 23cqi;                    /* curled focus ring's inner width: the widest word, and air */
  --mode-dial-icon-size: max(16px, 7cqi);
  --mode-dial-icon-stroke: 1.75;
  --mode-dial-lip: .06em;
  --mode-dial-tip-size: 11px;                      /* tooltip, display caption and tags */
  --mode-dial-key-size: max(44px, 26cqi, var(--mode-dial-key-label) * var(--mode-dial-key-fit)); /* centre key diameter: a whole target at any size, and room for its word */
  --mode-dial-key-fit: 4;                          /* ems of its label across: a word of five or six capitals, with air */
  --mode-dial-key-label: max(11px, 3.6cqi);
  --mode-dial-key-hi: 16%;                         /* the key's lit rim, as white's share */
  --mode-dial-key-hi-hover: 22%;
  --mode-dial-screen-name: 1.5rem;
  --mode-dial-screen-line: .8125rem;
  --mode-dial-screen-radius: 6px;
  --mode-dial-screen-pad: .875rem 1rem 1rem;
  --mode-dial-screen-gap: .625rem 1rem;
  --mode-dial-tick-h: 6px;
  --mode-dial-tick-gap: 3px;
  --mode-dial-tick-mark: 4px;
  --mode-dial-label-case: uppercase;               /* names, engraving, key, display */
  --mode-dial-small-track: .08em;                  /* tooltip, display caption and tags */
  --mode-dial-screen-track: .04em;                 /* the display's large name */

  /* --- motion ---------------------------------------------------------- */
  --mode-dial-turn: 420ms;
  --mode-dial-turn-more: 70ms;
  --mode-dial-turn-time: calc(var(--mode-dial-turn) + (var(--mode-dial-span) - 1) * var(--mode-dial-turn-more));
  --mode-dial-detent: cubic-bezier(.34, 1.36, .64, 1);
  --mode-dial-press: 90ms;
  --mode-dial-release: 220ms;
  --mode-dial-release-ease: cubic-bezier(.34, 1.5, .64, 1);
  --mode-dial-fade: 140ms;
  --mode-dial-roll: .5rem;
  --mode-dial-roll-time: 220ms;
  --mode-dial-key-travel: .5cqi;                   /* how far the key goes down, away from the light */
  --mode-dial-key-sink: .97;                       /* and how much smaller it is there */
  --mode-dial-kick: .7cqi;                         /* the index dips this far as a detent is passed */
  --mode-dial-kick-time: 120ms;

  /* --- feel: read by component.js, at the moment it needs them ---------- */
  --mode-dial-notch: .6;                           /* drag detent curve: 0 follows the pointer, 1 is the most notched (read at drag start) */
  --mode-dial-click: 0;                            /* sound volume, 0..1; 0 is silence, and no audio is ever started */
  --mode-dial-click-pitch: 2600;                   /* Hz: the centre of the detent tick's noise band */
  --mode-dial-click-ring: 7;                       /* that band's Q: high rings like metal, low is a dry plastic snap */
  --mode-dial-click-decay: 26;                     /* ms for the tick to die away */
  --mode-dial-key-thump: 120;                      /* Hz: the body of the key's press */
  /* The ratchet's voice, under the same volume: 0 and 0 here, so the two
     finishes keep their ticks; .mode-dial--ratchet turns both up. */
  --mode-dial-scrape: 0;                           /* the knurl's scratch, one grain per tooth past the index, 0..1 of the volume */
  --mode-dial-scrape-pitch: 5200;                  /* Hz: the centre of a grain's noise band, ±12% per grain */
  --mode-dial-scrape-ring: 2.4;                    /* that band's Q */
  --mode-dial-scrape-grain: 1;                     /* ms: a grain's length */
  --mode-dial-snap: 0;                             /* the click of the face landing in a detent, 0..1; 0 keeps the tick */
  --mode-dial-snap-pitch: 3400;                    /* Hz: the centre of the snap's broadband transient */
  --mode-dial-snap-thock: 170;                     /* Hz: the body's sine, falling to 0.53 of it */
  --mode-dial-snap-body: 40;                       /* ms: over which it falls */
  --mode-dial-snap-settle: 15;                     /* ms: until the ball settles into the notch, a second tick */
  --mode-dial-snap-ball: .3;                       /* that tick's level, of the snap's */
  --mode-dial-snap-pass: 1;                        /* a detent passed on the way: the transient alone, at this share */
  /* -------------------------------------------------------------------- */

  display: block;
  box-sizing: border-box;
  margin: 0;
  inline-size: var(--mode-dial-width);
  max-inline-size: 100%;
  container: mode-dial / inline-size;
  font-family: var(--mode-dial-font);
  -webkit-tap-highlight-color: transparent;
}

/* `linear()` passes the stop by 6% of the move and settles. Guarded with
   @supports rather than a var() fallback: a var() that resolves to an
   unsupported function is invalid at computed-value time and falls to
   `ease`, not to the declaration before it. */
@supports (transition-timing-function: linear(0, 1)) {
  .mode-dial {
    --mode-dial-detent: linear(0, .42 12%, .8 26%, 1.045 46%, 1.06 52%, .995 72%, 1.003 86%, 1);
  }
}

.mode-dial:has(.mode-dial__display) {
  --mode-dial-width: calc(var(--mode-dial-size) + var(--mode-dial-gap) + var(--mode-dial-screen-w) + 3rem);
}

/* --- the ladders ---------------------------------------------------------
   Position in DOM order is the single source: --mode-dial-i on each option,
   engraved word and readout; the count and the checked index on the root. */

.mode-dial__option:nth-child(1), .mode-dial__engraved:nth-child(1), .mode-dial__readout:nth-child(1) { --mode-dial-i: 0; }
.mode-dial__option:nth-child(2), .mode-dial__engraved:nth-child(2), .mode-dial__readout:nth-child(2) { --mode-dial-i: 1; }
.mode-dial__option:nth-child(3), .mode-dial__engraved:nth-child(3), .mode-dial__readout:nth-child(3) { --mode-dial-i: 2; }
.mode-dial__option:nth-child(4), .mode-dial__engraved:nth-child(4), .mode-dial__readout:nth-child(4) { --mode-dial-i: 3; }
.mode-dial__option:nth-child(5), .mode-dial__engraved:nth-child(5), .mode-dial__readout:nth-child(5) { --mode-dial-i: 4; }
.mode-dial__option:nth-child(6), .mode-dial__engraved:nth-child(6), .mode-dial__readout:nth-child(6) { --mode-dial-i: 5; }
.mode-dial__option:nth-child(7), .mode-dial__engraved:nth-child(7), .mode-dial__readout:nth-child(7) { --mode-dial-i: 6; }
.mode-dial__option:nth-child(8), .mode-dial__engraved:nth-child(8), .mode-dial__readout:nth-child(8) { --mode-dial-i: 7; }

.mode-dial:has(.mode-dial__option:nth-child(3):last-child) { --mode-dial-n: 3; }
.mode-dial:has(.mode-dial__option:nth-child(4):last-child) { --mode-dial-n: 4; }
.mode-dial:has(.mode-dial__option:nth-child(5):last-child) { --mode-dial-n: 5; }
.mode-dial:has(.mode-dial__option:nth-child(6):last-child) { --mode-dial-n: 6; }
.mode-dial:has(.mode-dial__option:nth-child(7):last-child) { --mode-dial-n: 7; }
.mode-dial:has(.mode-dial__option:nth-child(8):last-child) { --mode-dial-n: 8; }

.mode-dial:has(.mode-dial__option:nth-child(1) > .mode-dial__input:checked) { --mode-dial-sel: 0; }
.mode-dial:has(.mode-dial__option:nth-child(2) > .mode-dial__input:checked) { --mode-dial-sel: 1; }
.mode-dial:has(.mode-dial__option:nth-child(3) > .mode-dial__input:checked) { --mode-dial-sel: 2; }
.mode-dial:has(.mode-dial__option:nth-child(4) > .mode-dial__input:checked) { --mode-dial-sel: 3; }
.mode-dial:has(.mode-dial__option:nth-child(5) > .mode-dial__input:checked) { --mode-dial-sel: 4; }
.mode-dial:has(.mode-dial__option:nth-child(6) > .mode-dial__input:checked) { --mode-dial-sel: 5; }
.mode-dial:has(.mode-dial__option:nth-child(7) > .mode-dial__input:checked) { --mode-dial-sel: 6; }
.mode-dial:has(.mode-dial__option:nth-child(8) > .mode-dial__input:checked) { --mode-dial-sel: 7; }

/* Peek: what the display reads while a pointer is on an option. Not while
   the dial is settling under a still cursor, and not while dragging, when
   :active stays on the option the drag began on. */
@media (hover: hover) {
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(1):hover) { --mode-dial-peek: 0; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(2):hover) { --mode-dial-peek: 1; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(3):hover) { --mode-dial-peek: 2; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(4):hover) { --mode-dial-peek: 3; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(5):hover) { --mode-dial-peek: 4; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(6):hover) { --mode-dial-peek: 5; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(7):hover) { --mode-dial-peek: 6; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:nth-child(8):hover) { --mode-dial-peek: 7; }
}

.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(1):active) { --mode-dial-peek: 0; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(2):active) { --mode-dial-peek: 1; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(3):active) { --mode-dial-peek: 2; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(4):active) { --mode-dial-peek: 3; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(5):active) { --mode-dial-peek: 4; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(6):active) { --mode-dial-peek: 5; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(7):active) { --mode-dial-peek: 6; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:nth-child(8):active) { --mode-dial-peek: 7; }

/* --- structure ----------------------------------------------------------- */

.mode-dial__set {
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
}

.mode-dial__legend,
.mode-dial__status {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The deck: the top plate, and the only textile surface. A quincunx of
   raised bumps, two to a tile, each a shade disc with a highlight nudged a
   fraction of a pixel toward the light. */
.mode-dial__deck {
  box-sizing: border-box;
  --mode-dial-bump: calc(var(--mode-dial-dot-pitch) * .07);
  display: grid;
  grid-template-columns: minmax(0, var(--mode-dial-size));
  justify-content: center;
  align-items: center;
  gap: var(--mode-dial-gap);
  padding: var(--mode-dial-pad);
  border-radius: var(--mode-dial-deck-radius);
  background-color: var(--mode-dial-deck);
  background-image:
    linear-gradient(calc(180deg - var(--mode-dial-light)),
      color-mix(in srgb, var(--mode-dial-gleam) 7%, transparent),
      transparent 45%,
      color-mix(in srgb, var(--mode-dial-shade) 20%, transparent)),
    radial-gradient(circle var(--mode-dial-dot-r)
      at calc(25% - var(--mode-dial-bump) * var(--mode-dial-sx)) calc(25% - var(--mode-dial-bump) * var(--mode-dial-sy)),
      var(--mode-dial-dot-lit) 30%, transparent),
    radial-gradient(circle calc(var(--mode-dial-dot-r) * 1.25)
      at calc(25% + var(--mode-dial-bump) * var(--mode-dial-sx)) calc(25% + var(--mode-dial-bump) * var(--mode-dial-sy)),
      var(--mode-dial-dot-shade) 35%, transparent),
    radial-gradient(circle var(--mode-dial-dot-r)
      at calc(75% - var(--mode-dial-bump) * var(--mode-dial-sx)) calc(75% - var(--mode-dial-bump) * var(--mode-dial-sy)),
      var(--mode-dial-dot-lit) 30%, transparent),
    radial-gradient(circle calc(var(--mode-dial-dot-r) * 1.25)
      at calc(75% + var(--mode-dial-bump) * var(--mode-dial-sx)) calc(75% + var(--mode-dial-bump) * var(--mode-dial-sy)),
      var(--mode-dial-dot-shade) 35%, transparent);
  background-size:
    100% 100%,
    var(--mode-dial-dot-pitch) var(--mode-dial-dot-pitch),
    var(--mode-dial-dot-pitch) var(--mode-dial-dot-pitch),
    var(--mode-dial-dot-pitch) var(--mode-dial-dot-pitch),
    var(--mode-dial-dot-pitch) var(--mode-dial-dot-pitch);
  box-shadow:
    inset calc(1.5px * var(--mode-dial-sx)) calc(1.5px * var(--mode-dial-sy)) 0 var(--mode-dial-lit),
    inset calc(5px * var(--mode-dial-sx)) calc(5px * var(--mode-dial-sy)) 8px -4px color-mix(in srgb, var(--mode-dial-gleam) 9%, transparent),
    inset calc(-2px * var(--mode-dial-sx)) calc(-2px * var(--mode-dial-sy)) 0 color-mix(in srgb, var(--mode-dial-shade) 38%, transparent),
    inset calc(-6px * var(--mode-dial-sx)) calc(-6px * var(--mode-dial-sy)) 10px -5px color-mix(in srgb, var(--mode-dial-shade) 45%, transparent),
    0 0 0 1px color-mix(in srgb, var(--mode-dial-cast) 55%, transparent),
    calc(.5cqi * var(--mode-dial-sx)) calc(.5cqi * var(--mode-dial-sy)) 1cqi var(--mode-dial-shadow-contact),
    calc(2.5cqi * var(--mode-dial-sx)) calc(2.5cqi * var(--mode-dial-sy)) 4cqi color-mix(in srgb, var(--mode-dial-cast) 26%, transparent),
    calc(7cqi * var(--mode-dial-sx)) calc(7cqi * var(--mode-dial-sy)) 12cqi -1cqi var(--mode-dial-shadow-key);
}

/* Without a display the deck is square, so the margin round the dial is
   the same on every side whatever the root's width left over. */
.mode-dial__deck:not(:has(> .mode-dial__display)) {
  aspect-ratio: 1;
}

.mode-dial__deck:has(> .mode-dial__display) {
  grid-template-columns: minmax(0, var(--mode-dial-size)) minmax(0, var(--mode-dial-screen-w));
}

/* 33rem is a 15rem dial, the 14rem display, the deck's padding and the gap:
   any narrower and the two tracks share the loss, and the curled words fall
   under 11px, so the display goes under the dial instead. */
@container mode-dial (inline-size < 33rem) {
  .mode-dial__deck:has(> .mode-dial__display) {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- the dial --------------------------------------------------------------
   Every length inside is in the dial's own cqi; the dial is square, so cqi
   is cqmin. Radial plan, cqi from the centre: index 46.5–50, well 43.5–46,
   knurl 38.5–43.5, chamfer 37.4–38.5, face 0–37.4, options at 28, curled
   baseline 27, centre key 0–13. */
.mode-dial__dial {
  box-sizing: border-box;
  position: relative;
  isolation: isolate;
  inline-size: min(var(--mode-dial-size), 100%);
  max-inline-size: 100%;
  aspect-ratio: 1;
  justify-self: center;
  container: mode-dial-dial / inline-size;
  /* The turning parts are squares; turned, their boxes reach past the
     dial's corners and would push a phone's page sideways. Everything
     painted stays inside 47cqi, so the box can clip. */
  overflow: hidden;
  overflow: clip;
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}

.mode-dial[data-mode-dial-dragging] .mode-dial__dial,
.mode-dial[data-mode-dial-dragging] .mode-dial__input {
  cursor: grabbing;
}

/* The well the dial sits in: a recess, so its upper-left wall is in shadow
   and its lower-right lip catches the light. */
.mode-dial__dial::before {
  content: "";
  position: absolute;
  inset: 4cqi;
  z-index: 0;
  border-radius: 50%;
  background: var(--mode-dial-well);
  box-shadow:
    inset calc(1.2cqi * var(--mode-dial-sx)) calc(1.2cqi * var(--mode-dial-sy)) 2cqi color-mix(in srgb, var(--mode-dial-shade) 70%, transparent),
    calc(.35cqi * var(--mode-dial-sx)) calc(.35cqi * var(--mode-dial-sy)) .2cqi var(--mode-dial-lit),
    calc(-.3cqi * var(--mode-dial-sx)) calc(-.3cqi * var(--mode-dial-sy)) .3cqi color-mix(in srgb, var(--mode-dial-shade) 45%, transparent);
}

/* The dial's own cast shadow, on a disc that never turns, so the shadow
   never swings. No shadow with an offset sits on a turning element. */
.mode-dial__dial::after {
  content: "";
  position: absolute;
  inset: 6.5cqi;
  z-index: 0;
  border-radius: 50%;
  box-shadow:
    0 0 0 .35cqi var(--mode-dial-seam),
    calc(1.4cqi * var(--mode-dial-sx)) calc(1.4cqi * var(--mode-dial-sy)) 1.6cqi var(--mode-dial-shadow-body);
}

.mode-dial__index {
  position: absolute;
  top: .6cqi;
  left: 50%;
  z-index: 6;
  translate: -50% 0;
  inline-size: max(4px, 1.6cqi);
  max-inline-size: 100%;
  block-size: 3.6cqi;
  border-radius: 999px;
  background: var(--mode-dial-signal);
  box-shadow: calc(.3cqi * var(--mode-dial-sx)) calc(.3cqi * var(--mode-dial-sy)) .4cqi color-mix(in srgb, var(--mode-dial-shade) 55%, transparent);
}

/* Browsing: with a centre key, the mark goes hollow while the choice under
   it is not yet the committed one. */
.mode-dial:has(.mode-dial__commit):has(.mode-dial__input:checked:not(:default)) .mode-dial__index {
  background: transparent;
  box-shadow: inset 0 0 0 max(1.5px, .5cqi) var(--mode-dial-signal);
}

/* The face groups the parts; it does not turn. */
.mode-dial__face {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
}

/* The three turning parts. One angle, one transition. */
.mode-dial__knurl,
.mode-dial__engraving,
.mode-dial__options {
  rotate: var(--mode-dial-angle);
  transition: rotate var(--mode-dial-turn-time) var(--mode-dial-detent);
}

/* 120 teeth on a triangle profile with no hard stops, so the conic does not
   alias; a radial shade over them rounds the band into a cylinder. */
.mode-dial__knurl {
  position: absolute;
  inset: 6.5cqi;
  z-index: 1;
  border-radius: 50%;
  background:
    radial-gradient(closest-side,
      transparent 88%,
      color-mix(in srgb, var(--mode-dial-shade) 22%, transparent) 94%,
      color-mix(in srgb, var(--mode-dial-shade) 45%, transparent) 100%),
    repeating-conic-gradient(
      var(--mode-dial-tooth-hi) 0deg,
      var(--mode-dial-tooth-mid) calc(360deg / var(--mode-dial-teeth) * .4),
      var(--mode-dial-tooth-lo) calc(360deg / var(--mode-dial-teeth) * .6),
      var(--mode-dial-tooth-mid) calc(360deg / var(--mode-dial-teeth) * .8),
      var(--mode-dial-tooth-hi) calc(360deg / var(--mode-dial-teeth)));
  pointer-events: auto;
  touch-action: none;
}

/* The face plate: flat satin. All of its light comes from the layer above,
   which does not turn. */
.mode-dial__plate {
  position: absolute;
  inset: 11.5cqi;
  z-index: 1;
  border-radius: 50%;
  background: var(--mode-dial-face);
  box-shadow: 0 0 0 .25cqi var(--mode-dial-seam);
  pointer-events: auto;
}

/* The chamfer: a polished bevel round the plate's edge, with a sharp
   specular toward the light and a weaker one opposite. On the plate, which
   does not turn, so it needs no counter-rotation. */
.mode-dial__plate::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from calc(-1 * var(--mode-dial-light)),
    color-mix(in srgb, var(--mode-dial-gleam) 62%, transparent) 0deg,
    transparent 28deg 152deg,
    color-mix(in srgb, var(--mode-dial-gleam) 30%, transparent) 180deg,
    transparent 208deg 332deg,
    color-mix(in srgb, var(--mode-dial-gleam) 62%, transparent) 360deg);
  -webkit-mask: radial-gradient(closest-side, transparent 96.6%, #000 97.4%, #000 99.2%, transparent 100%);
  mask: radial-gradient(closest-side, transparent 96.6%, #000 97.4%, #000 99.2%, transparent 100%);
}

/* The light: a layer that never moves, so the teeth slide through a
   highlight that stays put. Its two pseudos are masked to their bands —
   the knurl's rim lighting, and the face's two-lobed satin streak. A mask
   on the layer itself would clip both, so it carries none. White over the
   face never passes 7%. */
.mode-dial__light {
  position: absolute;
  inset: 6.5cqi;
  z-index: 2;
  border-radius: 50%;
  pointer-events: none;
}

.mode-dial__light::before,
.mode-dial__light::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
}

.mode-dial__light::before {
  background: conic-gradient(from calc(-1 * var(--mode-dial-light)),
    color-mix(in srgb, var(--mode-dial-gleam) 34%, transparent),
    transparent 70deg,
    color-mix(in srgb, var(--mode-dial-shade) 50%, transparent) 180deg,
    transparent 290deg,
    color-mix(in srgb, var(--mode-dial-gleam) 34%, transparent));
  -webkit-mask: radial-gradient(closest-side, transparent 88.2%, #000 88.8%, #000 99.3%, transparent 100%);
  mask: radial-gradient(closest-side, transparent 88.2%, #000 88.8%, #000 99.3%, transparent 100%);
}

.mode-dial__light::after {
  background:
    conic-gradient(from calc(-1 * var(--mode-dial-light)),
      color-mix(in srgb, var(--mode-dial-gleam) 4.5%, transparent) 0deg,
      color-mix(in srgb, var(--mode-dial-gleam) 2%, transparent) 22deg,
      transparent 50deg 130deg,
      color-mix(in srgb, var(--mode-dial-gleam) 1.5%, transparent) 160deg,
      color-mix(in srgb, var(--mode-dial-gleam) 3%, transparent) 180deg,
      color-mix(in srgb, var(--mode-dial-gleam) 1.5%, transparent) 200deg,
      transparent 230deg 310deg,
      color-mix(in srgb, var(--mode-dial-gleam) 2%, transparent) 338deg,
      color-mix(in srgb, var(--mode-dial-gleam) 4.5%, transparent) 360deg),
    radial-gradient(circle at calc(50% - 22% * var(--mode-dial-sx)) calc(50% - 22% * var(--mode-dial-sy)),
      color-mix(in srgb, var(--mode-dial-gleam) 2.5%, transparent),
      transparent 65%);
  -webkit-mask: radial-gradient(closest-side, transparent 6%, #000 40%, #000 85.6%, transparent 86.1%);
  mask: radial-gradient(closest-side, transparent 6%, #000 40%, #000 85.6%, transparent 86.1%);
}

/* --- curled words ----------------------------------------------------------
   One lap of path starting at 6 o'clock, so startOffset 50% is 12 o'clock
   for every word, with half a lap of slack each side. Each word is turned
   to its own slot about the centre of the view box, which is the dial's. */
.mode-dial__engraving {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: none;
  inline-size: 100%;
  max-inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  pointer-events: none;
}

.mode-dial__arc {
  fill: none;
}

.mode-dial__engraved {
  --mode-dial-on: calc(1 - min(1, max(var(--mode-dial-i) - var(--mode-dial-sel), var(--mode-dial-sel) - var(--mode-dial-i))));
  transform: rotate(calc(var(--mode-dial-i) * var(--mode-dial-step)));
  transform-box: view-box;
  transform-origin: 50% 50%;
  font-family: var(--mode-dial-font);
  font-size: var(--mode-dial-curl-size);
  font-stretch: var(--mode-dial-label-stretch);
  font-weight: calc(var(--mode-dial-label-weight) + (var(--mode-dial-label-weight-on) - var(--mode-dial-label-weight)) * var(--mode-dial-on));
  letter-spacing: var(--mode-dial-curl-track);
  text-transform: var(--mode-dial-label-case);
  /* The word under the pointer lifts to the hover paint, as a name does. */
  --mode-dial-near: calc(1 - min(1, max(var(--mode-dial-i) - var(--mode-dial-show), var(--mode-dial-show) - var(--mode-dial-i))));
  fill: color-mix(in srgb, var(--mode-dial-paint-on) calc(var(--mode-dial-on) * 100%),
    color-mix(in srgb, var(--mode-dial-paint-hover) calc(var(--mode-dial-near) * 100%), var(--mode-dial-paint)));
}

/* --- options ---------------------------------------------------------------
   The layer turns; each option sits on it at sin/cos of its own slot and
   turns back by the same angle on the same transition, so it is upright on
   every frame. --mode-dial-at is the option's angle on screen; laps drop
   out of sin() and cos(). */
.mode-dial__options {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}

.mode-dial__option {
  box-sizing: border-box;
  --mode-dial-hit: clamp(var(--mode-dial-hit-min), calc(2 * pi * var(--mode-dial-r) / var(--mode-dial-n) - 6px), var(--mode-dial-hit-max));
  --mode-dial-at: calc((var(--mode-dial-i) - var(--mode-dial-sel) - var(--mode-dial-drag)) * var(--mode-dial-step));
  position: absolute;
  left: 50%;
  top: 50%;
  display: grid;
  place-items: center;
  min-inline-size: var(--mode-dial-hit);
  min-block-size: var(--mode-dial-hit);
  max-inline-size: 100%;
  margin: 0;
  translate:
    calc(-50% + sin(calc(var(--mode-dial-i) * var(--mode-dial-step))) * var(--mode-dial-r))
    calc(-50% - cos(calc(var(--mode-dial-i) * var(--mode-dial-step))) * var(--mode-dial-r));
  rotate: calc(var(--mode-dial-angle) * -1);
  transition: rotate var(--mode-dial-turn-time) var(--mode-dial-detent);
  /* Its own box, and the radio's, are the focus ring's, not the target:
     the target is the radio's slice, below. */
  pointer-events: none;
}

/* --- the target: the option's slice of the face ---------------------------
   Everything between the half-way lines to its neighbours, from the key's
   edge (or the middle, without one) out to the plate's, so a tap or a click
   anywhere in it picks the option drawn there. The knurl outside it stays the
   grip. A pseudo-element of the radio, so a press on it is a press on the
   radio — focused by the press, checked by the click, as before — and the
   hover, the peek and the tooltip come for nothing, since the radio is in
   the label they hang off.

   It turns with the face. The label, and the radio in it, turn back by the
   face's angle to stay upright; the slice turns forward by the same angle on
   the same transition, then by its own slot's, i · step, so it sits in the
   face's own frame with its slot straight up. There it is a disc the plate's
   size centred on the dial, so its outer edge is the plate's own circle, by
   border-radius, and clip-path cuts the wedge and the hole. Both bound
   hit-testing, where a mask would not. The two straight edges lie on the
   half-way lines, shared with the neighbours, so the slices meet with no gap
   and no overlap.

   The polygon is drawn on the content box, which the padding shrinks to the
   hole's square, so the inner arc is a plain percentage of it and the far
   points a plain distance past the plate, 100cqi out. That is what keeps it
   cheap. Written with var() it was substituted and parsed again on every
   restyle the turn causes, which every drag frame is, and it doubled the
   dial's style cost; as literals it is parsed once. They depend only on the
   count, so there is one per count, below: thirteen points round the hole
   from one half-way line to the other, each 50%/cos(15/n deg) from the
   middle so the twelve chords lie round the circle, not in it, and never
   cover the key — at most 0.38% of its radius past it at three stops, 0.10%
   at six — and three far out, on the half-way lines and between them. */
.mode-dial__input::before {
  --mode-dial-hole: var(--mode-dial-hit-in);
  content: "";
  box-sizing: border-box;
  position: absolute;
  inset: calc(50% - var(--mode-dial-hit-out));
  padding: max(0px, calc(var(--mode-dial-hit-out) - max(var(--mode-dial-hole), 1px)));
  border-radius: 50%;
  transform:
    rotate(var(--mode-dial-angle))
    translate(
      calc(sin(var(--mode-dial-i) * var(--mode-dial-step)) * var(--mode-dial-r) * -1),
      calc(cos(var(--mode-dial-i) * var(--mode-dial-step)) * var(--mode-dial-r)))
    rotate(calc(var(--mode-dial-i) * var(--mode-dial-step)));
  transition: transform var(--mode-dial-turn-time) var(--mode-dial-detent);
  pointer-events: auto;
}

.mode-dial:has(.mode-dial__option:nth-child(3):last-child) .mode-dial__input::before {
  clip-path: polygon(
    6.533% 24.905%, 11.551% 17.738%, 17.738% 11.551%, 24.905% 6.533%,
    32.834% 2.836%, 41.284% 0.572%, 50% -0.191%, 58.716% 0.572%,
    67.166% 2.836%, 75.095% 6.533%, 82.262% 11.551%, 88.449% 17.738%,
    93.467% 24.905%,
    calc(50% + 86.603cqi) calc(50% - 50cqi),
    50% calc(50% - 100cqi),
    calc(50% - 86.603cqi) calc(50% - 50cqi)) content-box;
}

.mode-dial:has(.mode-dial__option:nth-child(4):last-child) .mode-dial__input::before {
  clip-path: polygon(
    14.569% 14.569%, 19.497% 10.247%, 24.946% 6.606%, 30.825% 3.707%,
    37.031% 1.6%, 43.46% 0.321%, 50% -0.107%, 56.54% 0.321%,
    62.969% 1.6%, 69.175% 3.707%, 75.054% 6.606%, 80.503% 10.247%,
    85.431% 14.569%,
    calc(50% + 70.711cqi) calc(50% - 70.711cqi),
    50% calc(50% - 100cqi),
    calc(50% - 70.711cqi) calc(50% - 70.711cqi)) content-box;
}

.mode-dial:has(.mode-dial__option:nth-child(5):last-child) .mode-dial__input::before {
  clip-path: polygon(
    20.57% 9.494%, 24.966% 6.639%, 29.635% 4.26%, 34.528% 2.382%,
    39.59% 1.026%, 44.766% 0.206%, 50% -0.069%, 55.234% 0.206%,
    60.41% 1.026%, 65.472% 2.382%, 70.365% 4.26%, 75.034% 6.639%,
    79.43% 9.494%,
    calc(50% + 58.779cqi) calc(50% - 80.902cqi),
    50% calc(50% - 100cqi),
    calc(50% - 58.779cqi) calc(50% - 80.902cqi)) content-box;
}

.mode-dial:has(.mode-dial__option:nth-child(6):last-child) .mode-dial__input::before {
  clip-path: polygon(
    24.976% 6.657%, 28.849% 4.641%, 32.883% 2.971%, 37.047% 1.658%,
    41.309% 0.713%, 45.638% 0.143%, 50% -0.048%, 54.362% 0.143%,
    58.691% 0.713%, 62.953% 1.658%, 67.117% 2.971%, 71.151% 4.641%,
    75.024% 6.657%,
    calc(50% + 50cqi) calc(50% - 86.603cqi),
    50% calc(50% - 100cqi),
    calc(50% - 50cqi) calc(50% - 86.603cqi)) content-box;
}

.mode-dial:has(.mode-dial__option:nth-child(7):last-child) .mode-dial__input::before {
  clip-path: polygon(
    28.291% 4.92%, 31.72% 3.424%, 35.252% 2.188%, 38.866% 1.219%,
    42.543% 0.524%, 46.261% 0.105%, 50% -0.035%, 53.739% 0.105%,
    57.457% 0.524%, 61.134% 1.219%, 64.748% 2.188%, 68.28% 3.424%,
    71.709% 4.92%,
    calc(50% + 43.388cqi) calc(50% - 90.097cqi),
    50% calc(50% - 100cqi),
    calc(50% - 43.388cqi) calc(50% - 90.097cqi)) content-box;
}

.mode-dial:has(.mode-dial__option:nth-child(8):last-child) .mode-dial__input::before {
  clip-path: polygon(
    30.856% 3.781%, 33.919% 2.628%, 37.052% 1.678%, 40.24% 0.934%,
    43.47% 0.401%, 46.728% 0.08%, 50% -0.027%, 53.272% 0.08%,
    56.53% 0.401%, 59.76% 0.934%, 62.948% 1.678%, 66.081% 2.628%,
    69.144% 3.781%,
    calc(50% + 38.268cqi) calc(50% - 92.388cqi),
    50% calc(50% - 100cqi),
    calc(50% - 38.268cqi) calc(50% - 92.388cqi)) content-box;
}

/* With a key, the hole is the key and its seam, the disc the names' stand-in
   cuts out too. */
.mode-dial:has(.mode-dial__commit) .mode-dial__input::before {
  --mode-dial-hole: calc(var(--mode-dial-key-size) / 2 + .4cqi);
}

/* A disabled stop is passed over, so its slice is plain face: grabbed, not
   picked. Its own circle still takes the pointer, as every option's did
   before the slices, so its name rises and the display peeks at it: on an
   icons-only dial that is the one way left to learn what it is, since it
   cannot take focus. A click there does nothing; a press drags. */
.mode-dial__input:disabled::before {
  pointer-events: none;
}

.mode-dial__option:has(> .mode-dial__input:disabled) {
  pointer-events: auto;
}

/* Each option turns, so each is a stacking context and a later one paints
   over an earlier one's name. The one whose name is up is lifted: the
   choice, and above it the press, the focus and — only where hover is real,
   since a touch screen leaves it on the last thing tapped — the pointer.
   The slices never overlap, so the lift decides nothing about them: only
   whose raised name is on top, and so, where hover is real, which name
   takes the pointer over a neighbour's slice. */
.mode-dial__option:has(> .mode-dial__input:checked) {
  z-index: 1;
}

.mode-dial__option:is(:active, :has(> .mode-dial__input:focus-visible)) {
  z-index: 2;
}

@media (hover: hover) {
  .mode-dial__option:hover {
    z-index: 2;
  }
}

.mode-dial__input {
  box-sizing: border-box;
  position: absolute;
  inset: 0;
  z-index: 2;
  margin: 0;
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}

.mode-dial__input:focus {
  outline: none;
}

.mode-dial__input:focus-visible {
  outline: 2px solid var(--mode-dial-signal);
  outline-offset: 2px;
}

/* An engraved word is wider than its hit circle; the ring opens into a
   stadium round it rather than striking through its ends — sized to the
   word in the dial's cqi, so it is as wide at 240px as at 288 and still
   clears the next word round at eight stops. */
.mode-dial--curled .mode-dial__input:focus-visible {
  inset: 0 calc((var(--mode-dial-hit) - max(var(--mode-dial-hit), var(--mode-dial-curl-ring) - 4px)) / 2);
}

/* Names: engraved and paint-filled. The lip is fixed because the names are
   upright in screen space: dark on the side toward the light, lit opposite. */
.mode-dial__name {
  display: block;
  max-inline-size: 100%;
  font-family: var(--mode-dial-font);
  font-size: var(--mode-dial-label-size);
  font-weight: var(--mode-dial-label-weight);
  font-stretch: var(--mode-dial-label-stretch);
  line-height: 1;
  text-transform: var(--mode-dial-label-case);
  letter-spacing: var(--mode-dial-label-track);
  /* the tracking trails the last letter; this gives it back so the word
     centres on its slot */
  margin-inline-end: calc(var(--mode-dial-label-track) * -1);
  color: var(--mode-dial-paint);
  white-space: nowrap;
  pointer-events: none;
  text-shadow:
    calc(-1 * var(--mode-dial-lip) * var(--mode-dial-sx)) calc(-1 * var(--mode-dial-lip) * var(--mode-dial-sy)) 0 color-mix(in srgb, var(--mode-dial-shade) 70%, transparent),
    calc(var(--mode-dial-lip) * var(--mode-dial-sx)) calc(var(--mode-dial-lip) * var(--mode-dial-sy)) 0 color-mix(in srgb, var(--mode-dial-gleam) 12%, transparent);
  transition: color var(--mode-dial-fade);
}

.mode-dial__icon {
  display: none;
}

.mode-dial:not(.mode-dial--icons, .mode-dial--curled) .mode-dial__option:has(> .mode-dial__input:checked) .mode-dial__name {
  color: var(--mode-dial-paint-on);
  font-weight: var(--mode-dial-label-weight-on);
}

@media (hover: hover) {
  .mode-dial:not(.mode-dial--icons, .mode-dial--curled, [data-mode-dial-settling], [data-mode-dial-dragging]) .mode-dial__option:hover:not(:has(> :checked)) .mode-dial__name {
    color: var(--mode-dial-paint-hover);
  }
}

/* --- icons, whose names rise as tooltips ------------------------------------
   The name is the radio's accessible name, so it is never display:none or
   visibility:hidden — it is clipped to nothing, and shown on hover, on
   keyboard focus, and under (hover: none) on the press and the choice. It
   moves toward the centre, so a tooltip never leaves the dial. */
.mode-dial--icons:not(.mode-dial--curled) .mode-dial__icon {
  display: block;
  inline-size: var(--mode-dial-icon-size);
  max-inline-size: 100%;
  aspect-ratio: 1;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--mode-dial-icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--mode-dial-paint);
  pointer-events: none;
  filter:
    drop-shadow(calc(-.06 * var(--mode-dial-icon-size) * var(--mode-dial-sx)) calc(-.06 * var(--mode-dial-icon-size) * var(--mode-dial-sy)) 0 color-mix(in srgb, var(--mode-dial-shade) 70%, transparent))
    drop-shadow(calc(.05 * var(--mode-dial-icon-size) * var(--mode-dial-sx)) calc(.05 * var(--mode-dial-icon-size) * var(--mode-dial-sy)) 0 color-mix(in srgb, var(--mode-dial-gleam) 10%, transparent));
  transition: color var(--mode-dial-fade);
}

.mode-dial--icons:not(.mode-dial--curled) .mode-dial__option:has(> .mode-dial__input:checked) .mode-dial__icon {
  color: var(--mode-dial-paint-on);
}

.mode-dial--icons:not(.mode-dial--curled) .mode-dial__name {
  box-sizing: border-box;
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  max-inline-size: none;
  margin: 0;
  padding: .4em .6em .35em;
  border-radius: var(--mode-dial-tip-radius);
  background: var(--mode-dial-glass);
  color: var(--mode-dial-ink);
  font-family: var(--mode-dial-mono);
  font-size: var(--mode-dial-tip-size);
  font-weight: 400;
  font-stretch: normal;
  letter-spacing: var(--mode-dial-small-track);
  text-shadow: none;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--mode-dial-ink) 50%, transparent),
    calc(.3rem * var(--mode-dial-sx)) calc(.3rem * var(--mode-dial-sy)) .6rem color-mix(in srgb, var(--mode-dial-cast) 45%, transparent);
  /* Toward the centre, and pushed a further half of its own extent along
     that direction, so its near edge — not its middle — is the reach,
     measured from the option's centre. Across, it reaches tip-side further,
     so a wide word beside a side option clears its own icon. */
  --mode-dial-tx: sin(var(--mode-dial-at));
  --mode-dial-ty: cos(var(--mode-dial-at));
  translate:
    calc(-50% - var(--mode-dial-tx) * (var(--mode-dial-tip-reach) + var(--mode-dial-tip-side)) - var(--mode-dial-tx) * max(var(--mode-dial-tx), -1 * var(--mode-dial-tx)) * 50%)
    calc(-50% + var(--mode-dial-ty) * var(--mode-dial-tip-reach) + var(--mode-dial-ty) * max(var(--mode-dial-ty), -1 * var(--mode-dial-ty)) * 50%);
  opacity: 0;
  clip-path: inset(50%);
  transition:
    opacity var(--mode-dial-fade),
    clip-path 0s var(--mode-dial-fade),
    translate var(--mode-dial-turn-time) var(--mode-dial-detent);
}

.mode-dial--icons:not(.mode-dial--curled, [data-mode-dial-hush]) .mode-dial__option:has(> .mode-dial__input:focus-visible) .mode-dial__name {
  opacity: 1;
  clip-path: none;
  transition:
    opacity var(--mode-dial-fade),
    translate var(--mode-dial-turn-time) var(--mode-dial-detent);
}

@media (hover: hover) {
  .mode-dial--icons:not(.mode-dial--curled) .mode-dial__option:hover:not(:has(> :checked)) .mode-dial__icon {
    color: var(--mode-dial-paint-hover);
  }

  /* Hoverable: the raised name takes the pointer, so moving onto it keeps
     the option hovered and the name up. */
  .mode-dial--icons:not(.mode-dial--curled, [data-mode-dial-settling], [data-mode-dial-dragging], [data-mode-dial-hush]) .mode-dial__option:hover .mode-dial__name {
    opacity: 1;
    clip-path: none;
    pointer-events: auto;
    transition:
      opacity var(--mode-dial-fade),
      translate var(--mode-dial-turn-time) var(--mode-dial-detent);
  }

  /* With a centre key, the key outranks a name drawn over it. The name is
     drawn as before, over the key, but what takes the pointer is a stand-in
     the size of the name with the key's disc cut out of it, so a click on
     the key is the key's wherever a name lies across it. The disc is found
     from the name's own placement: the dial's centre, in the name's box. */
  .mode-dial--icons:not(.mode-dial--curled, [data-mode-dial-settling], [data-mode-dial-dragging], [data-mode-dial-hush]):has(.mode-dial__commit) .mode-dial__option:hover .mode-dial__name {
    pointer-events: none;
  }

  .mode-dial--icons:not(.mode-dial--curled, [data-mode-dial-settling], [data-mode-dial-dragging], [data-mode-dial-hush]):has(.mode-dial__commit) .mode-dial__option:hover .mode-dial__name::before {
    --mode-dial-kx: calc(50% + var(--mode-dial-tx) * max(var(--mode-dial-tx), -1 * var(--mode-dial-tx)) * 50% - var(--mode-dial-tx) * (var(--mode-dial-r) - var(--mode-dial-tip-reach) - var(--mode-dial-tip-side)));
    --mode-dial-ky: calc(50% - var(--mode-dial-ty) * max(var(--mode-dial-ty), -1 * var(--mode-dial-ty)) * 50% + var(--mode-dial-ty) * (var(--mode-dial-r) - var(--mode-dial-tip-reach)));
    /* the key and its seam, and the 16-gon drawn round the circle, not in it */
    --mode-dial-kr: calc((var(--mode-dial-key-size) / 2 + .4cqi) * 1.0196);
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: auto;
    /* a frame well outside the box round the hole, so no part of the hole
       falls outside it, where even-odd would fill it in */
    clip-path: polygon(evenodd,
      -100cqi -100cqi,
      calc(100% + 100cqi) -100cqi,
      calc(100% + 100cqi) calc(100% + 100cqi),
      -100cqi calc(100% + 100cqi),
      -100cqi -100cqi,
      calc(var(--mode-dial-kx) + var(--mode-dial-kr)) var(--mode-dial-ky),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.9239) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.3827),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.7071) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.7071),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.3827) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.9239),
      var(--mode-dial-kx) calc(var(--mode-dial-ky) + var(--mode-dial-kr)),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.3827) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.9239),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.7071) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.7071),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.9239) calc(var(--mode-dial-ky) + var(--mode-dial-kr) * 0.3827),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr)) var(--mode-dial-ky),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.9239) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.3827),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.7071) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.7071),
      calc(var(--mode-dial-kx) - var(--mode-dial-kr) * 0.3827) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.9239),
      var(--mode-dial-kx) calc(var(--mode-dial-ky) - var(--mode-dial-kr)),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.3827) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.9239),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.7071) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.7071),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr) * 0.9239) calc(var(--mode-dial-ky) - var(--mode-dial-kr) * 0.3827),
      calc(var(--mode-dial-kx) + var(--mode-dial-kr)) var(--mode-dial-ky));
  }
}

@media (hover: none) {
  .mode-dial--icons:not(.mode-dial--curled, [data-mode-dial-hush]) .mode-dial__option:is(:active, :has(> .mode-dial__input:checked)) .mode-dial__name {
    opacity: 1;
    clip-path: none;
    transition:
      opacity var(--mode-dial-fade),
      translate var(--mode-dial-turn-time) var(--mode-dial-detent);
  }
}

/* On a small dial there is no room beside an icon for a word, so the name
   is shown in the middle of the face, over nothing. Not with a centre key,
   which is what the middle is for there: a keyed small dial keeps its names
   at the edge. */
@container mode-dial-dial (inline-size < 11rem) {
  .mode-dial--icons:not(.mode-dial--curled, :has(.mode-dial__commit)) .mode-dial__name {
    translate:
      calc(-50% - var(--mode-dial-tx) * var(--mode-dial-r))
      calc(-50% + var(--mode-dial-ty) * var(--mode-dial-r));
  }
  /* One place, so one name: the option being pressed, or pointed at, has
     it, and a focused or chosen one gives way for as long as it does. */
  .mode-dial--icons:not(.mode-dial--curled, :has(.mode-dial__commit)):has(.mode-dial__option:active) .mode-dial__option:not(:active) .mode-dial__name {
    opacity: 0;
    clip-path: inset(50%);
  }
}

@media (hover: hover) {
  @container mode-dial-dial (inline-size < 11rem) {
    /* The name is in the middle, so the slice of the option pointed at
       reaches it, and the pointer stays on the option all the way there. */
    .mode-dial--icons:not(.mode-dial--curled, :has(.mode-dial__commit), [data-mode-dial-settling], [data-mode-dial-dragging], [data-mode-dial-hush]) .mode-dial__option:hover .mode-dial__input::before {
      --mode-dial-hole: 0px;
    }

    .mode-dial--icons:not(.mode-dial--curled, :has(.mode-dial__commit), [data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:hover) .mode-dial__option:not(:hover) .mode-dial__name {
      opacity: 0;
      clip-path: inset(50%);
    }
  }
}

/* --- curled: the words are engraved round the arc and turn with it ---------
   Names and icons are clipped to nothing — still the radios' names — and the
   options stay as transparent hit areas over the words. */
.mode-dial--curled .mode-dial__engraving {
  display: block;
}

.mode-dial--curled .mode-dial__icon {
  display: none;
}

.mode-dial--curled .mode-dial__name {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- the committed pip -------------------------------------------------------
   With a centre key, a small mark on the face stays with what was committed
   while the index moves on to what is being looked at. */
.mode-dial:has(.mode-dial__commit) .mode-dial__option:has(> .mode-dial__input:default)::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  inline-size: max(5px, 1.8cqi);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--mode-dial-signal);
  box-shadow: calc(.2cqi * var(--mode-dial-sx)) calc(.2cqi * var(--mode-dial-sy)) .3cqi color-mix(in srgb, var(--mode-dial-shade) 60%, transparent);
  pointer-events: none;
  translate:
    calc(-50% + sin(var(--mode-dial-at)) * var(--mode-dial-pip-reach))
    calc(-50% - cos(var(--mode-dial-at)) * var(--mode-dial-pip-reach));
  transition: translate var(--mode-dial-turn-time) var(--mode-dial-detent);
}

/* --- the centre key ----------------------------------------------------------
   Its presence turns on browse and commit: the checked radio is pending, the
   radio carrying the `checked` attribute (:default) is committed. It does
   not turn. */
.mode-dial__commit {
  box-sizing: border-box;
  --mode-dial-key-ring: 0 0 0 0 transparent;
  position: absolute;
  inset: calc(50cqi - var(--mode-dial-key-size) / 2);
  z-index: 4;
  display: grid;
  place-items: center;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  touch-action: manipulation;
  background: radial-gradient(circle at calc(50% - 18% * var(--mode-dial-sx)) calc(50% - 18% * var(--mode-dial-sy)),
    var(--mode-dial-cap-lit), var(--mode-dial-cap) 60%, var(--mode-dial-cap-low));
  color: var(--mode-dial-paint-on);
  font-family: var(--mode-dial-font);
  font-size: var(--mode-dial-key-label);
  font-weight: var(--mode-dial-label-weight);
  font-stretch: var(--mode-dial-label-stretch);
  line-height: 1;
  text-transform: var(--mode-dial-label-case);
  letter-spacing: var(--mode-dial-label-track);
  box-shadow:
    0 0 0 .4cqi var(--mode-dial-seam),
    var(--mode-dial-key-ring),
    calc(1.2cqi * var(--mode-dial-sx)) calc(1.2cqi * var(--mode-dial-sy)) 2.4cqi color-mix(in srgb, var(--mode-dial-shade) 55%, transparent),
    inset calc(.5cqi * var(--mode-dial-sx)) calc(.5cqi * var(--mode-dial-sy)) .6cqi color-mix(in srgb, var(--mode-dial-gleam) var(--mode-dial-key-hi), transparent),
    inset calc(-.6cqi * var(--mode-dial-sx)) calc(-.6cqi * var(--mode-dial-sy)) 1cqi color-mix(in srgb, var(--mode-dial-shade) 40%, transparent);
  transition:
    box-shadow var(--mode-dial-release) var(--mode-dial-release-ease),
    translate var(--mode-dial-release) var(--mode-dial-release-ease),
    scale var(--mode-dial-release) var(--mode-dial-release-ease);
}

.mode-dial__commit-label {
  margin-inline-end: calc(var(--mode-dial-label-track) * -1);
  pointer-events: none;
}

.mode-dial:has(.mode-dial__input:checked:not(:default)) .mode-dial__commit {
  --mode-dial-key-ring: 0 0 0 .9cqi var(--mode-dial-signal);
}

@media (hover: hover) {
  .mode-dial__commit:hover {
    --mode-dial-key-hi: var(--mode-dial-key-hi-hover);
  }
}

/* The key travels: down on the press — data-mode-dial-press, which script
   sets from a pointer's down or a held Space or Enter until it comes up, and
   :active for a page without script, which a touch does not reliably get —
   and for a beat after a commit, then springs back on the release curve,
   which overshoots. */
.mode-dial__commit:active,
.mode-dial[data-mode-dial-press] .mode-dial__commit,
.mode-dial[data-mode-dial-set] .mode-dial__commit {
  translate: calc(var(--mode-dial-key-travel) * var(--mode-dial-sx)) calc(var(--mode-dial-key-travel) * var(--mode-dial-sy));
  scale: var(--mode-dial-key-sink);
  background: radial-gradient(circle at calc(50% + 18% * var(--mode-dial-sx)) calc(50% + 18% * var(--mode-dial-sy)),
    var(--mode-dial-cap-lit), var(--mode-dial-cap) 60%, var(--mode-dial-cap-low));
  box-shadow:
    0 0 0 .4cqi var(--mode-dial-seam),
    var(--mode-dial-key-ring),
    calc(.3cqi * var(--mode-dial-sx)) calc(.3cqi * var(--mode-dial-sy)) .6cqi color-mix(in srgb, var(--mode-dial-shade) 50%, transparent),
    inset calc(1.2cqi * var(--mode-dial-sx)) calc(1.2cqi * var(--mode-dial-sy)) 1.6cqi color-mix(in srgb, var(--mode-dial-shade) 50%, transparent);
  transition-duration: var(--mode-dial-press);
  transition-timing-function: ease-out;
}

.mode-dial__commit:focus {
  outline: none;
}

.mode-dial__commit:focus-visible {
  outline: 2px solid var(--mode-dial-signal);
  outline-offset: 1.6cqi;
}

/* --- the display -------------------------------------------------------------
   A matte LCD cut into the deck: one ink on one glass, because a second,
   dimmer ink was the first thing to fall under the floor. State is carried
   by shape — an inverse tag, an outlined one, a dashed one — never by tone. */
.mode-dial__display {
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--mode-dial-screen-gap);
  align-content: start;
  align-self: center;
  min-inline-size: 0;
  max-inline-size: 100%;
  margin: 0;
  padding: var(--mode-dial-screen-pad);
  border-radius: var(--mode-dial-screen-radius);
  color: var(--mode-dial-ink);
  font-family: var(--mode-dial-mono);
  background-color: var(--mode-dial-glass);
  background-image:
    linear-gradient(calc(180deg - var(--mode-dial-light)),
      color-mix(in srgb, var(--mode-dial-gleam) 18%, transparent),
      transparent 40%),
    radial-gradient(circle 1px, color-mix(in srgb, var(--mode-dial-ink) 7%, transparent) 50%, transparent);
  background-size: 100% 100%, var(--mode-dial-dot-pitch) var(--mode-dial-dot-pitch);
  box-shadow:
    0 0 0 1px var(--mode-dial-seam),
    calc(-1px * var(--mode-dial-sx)) calc(-1px * var(--mode-dial-sy)) 0 1px color-mix(in srgb, var(--mode-dial-shade) 45%, transparent),
    calc(1px * var(--mode-dial-sx)) calc(1px * var(--mode-dial-sy)) 0 1.5px var(--mode-dial-lit),
    inset calc(.6rem * var(--mode-dial-sx)) calc(.6rem * var(--mode-dial-sy)) 1.2rem color-mix(in srgb, var(--mode-dial-shade) 15%, transparent);
}

.mode-dial__caption,
.mode-dial__tag {
  margin: 0;
  font-size: var(--mode-dial-tip-size);
  line-height: 1.2;
  letter-spacing: var(--mode-dial-small-track);
  text-transform: var(--mode-dial-label-case);
}

.mode-dial__caption {
  grid-area: 1 / 1;
  align-self: center;
}

.mode-dial__tags {
  grid-area: 1 / 2;
  display: grid;
  justify-items: end;
  margin: 0;
}

.mode-dial__tag {
  box-sizing: border-box;
  grid-area: 1 / 1;
  padding: .2em .45em .15em;
  border-radius: var(--mode-dial-tip-radius);
  opacity: 0;
}

.mode-dial__tag--current {
  background: var(--mode-dial-ink);
  color: var(--mode-dial-glass);
  opacity: 1;
}

.mode-dial__tag--preview {
  outline: 1px dashed var(--mode-dial-ink);
  outline-offset: -1px;
}

.mode-dial__tag--pending {
  box-shadow: inset 0 0 0 1px var(--mode-dial-ink);
}

/* Pending outranks Current; Preview outranks both. */
.mode-dial:has(.mode-dial__commit):has(.mode-dial__input:checked:not(:default)) .mode-dial__tag--current { opacity: 0; }
.mode-dial:has(.mode-dial__commit):has(.mode-dial__input:checked:not(:default)) .mode-dial__tag--pending { opacity: 1; }

.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:active > .mode-dial__input:not(:checked)) .mode-dial__tag { opacity: 0; }
.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:active > .mode-dial__input:not(:checked)) .mode-dial__tag--preview { opacity: 1; }

@media (hover: hover) {
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:hover > .mode-dial__input:not(:checked)) .mode-dial__tag { opacity: 0; }
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:hover > .mode-dial__input:not(:checked)) .mode-dial__tag--preview { opacity: 1; }
}

/* The readouts stack in one grid cell, so the window is the tallest of them
   in whatever language it is in; the one shown rolls in from the side it is
   on. */
.mode-dial__readouts {
  grid-column: 1 / -1;
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow: hidden;
}

.mode-dial__readout {
  --mode-dial-seen: calc(1 - min(1, max(var(--mode-dial-i) - var(--mode-dial-show), var(--mode-dial-show) - var(--mode-dial-i))));
  grid-area: 1 / 1;
  min-inline-size: 0;
  opacity: var(--mode-dial-seen);
  translate: 0 calc(clamp(-1, var(--mode-dial-i) - var(--mode-dial-show), 1) * var(--mode-dial-roll));
  transition:
    opacity var(--mode-dial-roll-time),
    translate var(--mode-dial-roll-time) var(--mode-dial-detent);
}

.mode-dial__readout-name {
  display: block;
  margin-block-end: .3rem;
  font-family: var(--mode-dial-font);
  font-size: var(--mode-dial-screen-name);
  font-weight: var(--mode-dial-label-weight-on);
  font-stretch: var(--mode-dial-label-stretch);
  line-height: 1.05;
  letter-spacing: var(--mode-dial-screen-track);
  text-transform: var(--mode-dial-label-case);
  text-decoration-thickness: 1px;
  text-underline-offset: .14em;
}

.mode-dial__readout-line {
  display: block;
  font-size: var(--mode-dial-screen-line);
  line-height: 1.35;
}

.mode-dial:not([data-mode-dial-dragging]):has(.mode-dial__option:active > .mode-dial__input:not(:checked)) .mode-dial__readout-name {
  text-decoration-line: underline;
  text-decoration-style: dashed;
}

@media (hover: hover) {
  .mode-dial:not([data-mode-dial-settling], [data-mode-dial-dragging]):has(.mode-dial__option:hover > .mode-dial__input:not(:checked)) .mode-dial__readout-name {
    text-decoration-line: underline;
    text-decoration-style: dashed;
  }
}

/* One tick per option; a block marks the one the display is reading. */
.mode-dial__ticks {
  position: relative;
  grid-column: 1 / -1;
  display: block;
  block-size: var(--mode-dial-tick-h);
  background:
    repeating-linear-gradient(90deg,
      var(--mode-dial-ink) 0 calc(100% / var(--mode-dial-n) - var(--mode-dial-tick-gap)),
      transparent 0 calc(100% / var(--mode-dial-n)))
    0 100% / calc(100% + var(--mode-dial-tick-gap)) 1px no-repeat;
}

.mode-dial__ticks::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  inline-size: calc((100% + var(--mode-dial-tick-gap)) / var(--mode-dial-n) - var(--mode-dial-tick-gap));
  block-size: var(--mode-dial-tick-mark);
  background: var(--mode-dial-ink);
  translate: calc((100% + var(--mode-dial-tick-gap)) * var(--mode-dial-show)) 0;
  transition: translate var(--mode-dial-roll-time) var(--mode-dial-detent);
}

/* --- the plain finish ----------------------------------------------------------
   The same dial with the material taken off: one modifier that re-declares
   the palette and the type, and switches the surface recipes off. What goes
   is the cloth, the 120 teeth and their rim light, the chamfer and the
   streak, every cast shadow and bevel, the engraving lip and the glass.
   What stays is everything that says something: the index, the turning,
   the chosen weight, the hollow index and the key's ring while a choice is
   pending, the pip, the focus rings and the tooltips. The rim still shows it
   turns, with one tick per detent rather than a tooth per three degrees.
   Geometry, state and script are untouched, so it composes with every other
   modifier and part.

   Its own light palette, because the component owns its colours. The signal
   is darker than the polished one, which is about 2:1 on white: #cf5a22 is
   4.09:1 on the face and the deck. The hairline is the plate's edge on a
   light ground, and the lit plate's on a dark one. */
.mode-dial--plain {
  --mode-dial-deck: #fff;
  --mode-dial-face: #fff;
  --mode-dial-paint: #5c5a55;
  --mode-dial-paint-hover: #2b2a27;
  --mode-dial-paint-on: #1b1a18;
  --mode-dial-signal: #cf5a22;
  --mode-dial-cap: #fff;                           /* the key at rest */
  --mode-dial-cap-lit: #f5f4f1;                    /* under a pointer */
  --mode-dial-cap-low: #ecebe6;                    /* pressed */
  --mode-dial-glass: #f7f6f3;                      /* the display and the tooltip */
  --mode-dial-ink: #1d1c1a;
  --mode-dial-deck-radius: min(1rem, 6cqi);
  --mode-dial-tip-radius: 4px;
  --mode-dial-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mode-dial-mono: var(--mode-dial-font);
  --mode-dial-label-stretch: 100%;
  --mode-dial-label-weight: 400;
  --mode-dial-label-weight-on: 600;
  --mode-dial-label-track: 0em;
  --mode-dial-label-case: none;
  --mode-dial-curl-track: .02em;
  --mode-dial-small-track: 0em;
  --mode-dial-screen-track: 0em;
  --mode-dial-tip-size: 12px;
  --mode-dial-key-label: var(--mode-dial-label-size); /* the names' size, at the chosen name's weight below */
  --mode-dial-light: 0deg;                         /* no light to move away from: the key travels straight down */
  /* A lighter, drier tick: plastic rather than the polished dial's metal. */
  --mode-dial-click-pitch: 4200;
  --mode-dial-click-ring: 1.4;
  --mode-dial-click-decay: 9;
  --mode-dial-key-thump: 210;
}

.mode-dial--plain .mode-dial__deck,
.mode-dial--plain .mode-dial__display {
  background-image: none;
  box-shadow: inset 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line);
}

.mode-dial--plain .mode-dial__dial::before,
.mode-dial--plain .mode-dial__plate::before,
.mode-dial--plain .mode-dial__light {
  display: none;
}

/* The rim's fill and its outer edge are on the disc that does not turn. A
   turned box is snapped to the pixel grid before it is turned, which moves
   its circle by up to a pixel as the dial goes round: hidden in the
   polished dial's dark seam, plain as day against a hairline. So only the
   ticks turn. */
.mode-dial--plain .mode-dial__dial::after {
  background: var(--mode-dial-rim);
  box-shadow: 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line);
}

.mode-dial--plain .mode-dial__index {
  box-shadow: none;
}

/* A tick at each detent's angle, so the one under the index is the chosen
   one at rest, masked to the rim's outer band. */
.mode-dial--plain .mode-dial__knurl {
  background: repeating-conic-gradient(
    from calc(var(--mode-dial-rim-mark-w) / -2 - var(--mode-dial-rim-mark-soft)),
    transparent 0deg,
    var(--mode-dial-rim-mark) var(--mode-dial-rim-mark-soft),
    var(--mode-dial-rim-mark) calc(var(--mode-dial-rim-mark-soft) + var(--mode-dial-rim-mark-w)),
    transparent calc(var(--mode-dial-rim-mark-soft) * 2 + var(--mode-dial-rim-mark-w)),
    transparent var(--mode-dial-step));
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--mode-dial-rim-mark-len)), #000 calc(100% - var(--mode-dial-rim-mark-len)));
  mask: radial-gradient(closest-side, transparent calc(100% - var(--mode-dial-rim-mark-len)), #000 calc(100% - var(--mode-dial-rim-mark-len)));
}

.mode-dial--plain .mode-dial__plate {
  box-shadow: 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line);
}

.mode-dial--plain .mode-dial__name {
  text-shadow: none;
}

.mode-dial--plain.mode-dial--icons:not(.mode-dial--curled) .mode-dial__icon {
  filter: none;
}

.mode-dial--plain.mode-dial--icons:not(.mode-dial--curled) .mode-dial__name {
  box-shadow: 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line);
}

.mode-dial--plain:has(.mode-dial__commit) .mode-dial__option:has(> .mode-dial__input:default)::after {
  box-shadow: none;
}

.mode-dial--plain .mode-dial__commit {
  background: var(--mode-dial-cap);
  font-weight: var(--mode-dial-label-weight-on);
  box-shadow: 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line), var(--mode-dial-key-ring);
}

@media (hover: hover) {
  .mode-dial--plain .mode-dial__commit:hover {
    background: var(--mode-dial-cap-lit);
  }
}

.mode-dial--plain .mode-dial__commit:active,
.mode-dial--plain[data-mode-dial-press] .mode-dial__commit,
.mode-dial--plain[data-mode-dial-set] .mode-dial__commit {
  background: var(--mode-dial-cap-low);
  box-shadow: 0 0 0 var(--mode-dial-hairline) var(--mode-dial-line), var(--mode-dial-key-ring);
}

/* --- the ratchet ---------------------------------------------------------------
   A voice, not a finish: it composes with both and changes nothing that is
   seen. Under the same volume, --mode-dial-click, the knurl scratches as it
   turns, one grain of bright noise per tooth that passes the index, so a
   quick turn buzzes, a slow drag crackles and a still dial is silent; and
   the face snaps as it lands in a detent, a transient, a low thock and the
   ball settling a beat after. A detent passed on the way gets the transient
   alone. The snap replaces the finish's detent tick; the key keeps the
   finish's own press and release. Every tunable is in the property block;
   this sets the two levels. */
.mode-dial--ratchet {
  --mode-dial-scrape: .5;
  --mode-dial-snap: 1;
}

/* --- the kick ------------------------------------------------------------------
   Each detent passed dips the index and lets it back. Script restarts it by
   swapping between two keyframes that are the same, since an animation only
   starts over when its name changes. It starts and ends at rest, so a page
   that pauses animations leaves the index where it belongs. */
@keyframes mode-dial-kick-a {
  0%, 100% { translate: -50% 0; }
  30% { translate: -50% var(--mode-dial-kick); }
}

@keyframes mode-dial-kick-b {
  0%, 100% { translate: -50% 0; }
  30% { translate: -50% var(--mode-dial-kick); }
}

.mode-dial[data-mode-dial-kick="a"] .mode-dial__index {
  animation: mode-dial-kick-a var(--mode-dial-kick-time) ease-out;
}

.mode-dial[data-mode-dial-kick="b"] .mode-dial__index {
  animation: mode-dial-kick-b var(--mode-dial-kick-time) ease-out;
}

/* --- states ------------------------------------------------------------------ */

/* While a drag holds the dial, nothing eases: the angle is the pointer's. */
.mode-dial[data-mode-dial-dragging] :is(.mode-dial__knurl, .mode-dial__engraving, .mode-dial__options, .mode-dial__option, .mode-dial__name) {
  transition: none;
}

.mode-dial[data-mode-dial-dragging] .mode-dial__input::before,
.mode-dial[data-mode-dial-dragging] .mode-dial__option::after {
  transition: none;
}

/* A rule, not a token, so an inline override cannot defeat it. The turn
   time is also zeroed on each option, so the name and the pip inherit 0s
   whatever the specificity of the rules that move them. */
@media (prefers-reduced-motion: reduce) {
  .mode-dial .mode-dial__option {
    --mode-dial-turn-time: 0s;
  }

  .mode-dial :is(.mode-dial__knurl, .mode-dial__engraving, .mode-dial__options, .mode-dial__option, .mode-dial__name, .mode-dial__ticks),
  .mode-dial .mode-dial__input::before,
  .mode-dial .mode-dial__option::after,
  .mode-dial .mode-dial__ticks::before {
    transition-duration: 0s;
  }

  .mode-dial .mode-dial__readout {
    transition: opacity 120ms;
  }

  .mode-dial[data-mode-dial-kick] .mode-dial__index {
    animation: none;
  }

  /* The key still goes down and comes back, without the spring past rest. */
  .mode-dial .mode-dial__commit {
    transition-timing-function: ease-out;
  }
}

/* Forced colours: every mark here is a background or a shadow, and both are
   dropped. The index, the pip and the key's edge come back in system colours;
   pending is a hollow index and a dashed key, as it is a hollow index and a
   ring otherwise. The pending rule repeats the hollow rule's selector so it
   outranks it. */
@media (forced-colors: active) {
  .mode-dial .mode-dial__index,
  .mode-dial:has(.mode-dial__commit) .mode-dial__option:has(> .mode-dial__input:default)::after {
    forced-color-adjust: none;
    background: Highlight;
    box-shadow: none;
  }

  .mode-dial:has(.mode-dial__commit):has(.mode-dial__input:checked:not(:default)) .mode-dial__index {
    background: Canvas;
    box-shadow: inset 0 0 0 2px Highlight;
  }

  .mode-dial__commit {
    border: 1px solid ButtonText;
  }

  .mode-dial:has(.mode-dial__input:checked:not(:default)) .mode-dial__commit {
    border: 2px dashed Highlight;
  }

  /* SVG keeps its author colours here (preserve-parent-color), so the
     paints would stay as they are on a ground that is now Canvas: the
     chosen icon and word, in the lightest paint, would all but vanish. They
     are handed the system text colour, the chosen one included — the index
     marks which it is — and the icons lose the drop-shadow lip, which
     forced colours also leave alone and which reads as a doubled stroke.
     The second selector matches the checked rule's weight, to outrank it. */
  .mode-dial .mode-dial__engraved {
    fill: CanvasText;
  }

  .mode-dial--icons:not(.mode-dial--curled) .mode-dial__icon,
  .mode-dial--icons:not(.mode-dial--curled) .mode-dial__option:has(> .mode-dial__input:checked) .mode-dial__icon {
    color: CanvasText;
    filter: none;
  }
}
