/* MODULE: css/base.css — reset, app shell frame, focus rings, and the
   tactile control primitives (.btn / .chip / .field / .panel / .well).
   Depends on css/tokens.css, which MUST be loaded first. Loads nothing
   itself — no @import, no fonts, no network.

   Nothing here styles the dial, the layout grid, or any module. Those
   belong to css/dial.css, css/layout.css and css/mod-*.css.

   ── MATERIALS ─────────────────────────────────────────────────────────
   css/materials.css is this file's other half and loads immediately after
   it. PHASE4 §4's governing rule is:

       Metal for things you touch. Ceramic for surfaces you don't.

   A raised key is a thing you touch, so .btn and .chip are brushed
   aluminium; .panel and .well are moulded ceramic; .field is a recess cut
   into the ceramic and stays matte. START (.btn--lg.btn--primary) is
   neither — it is the one signal-red control in the object.

   The primitives here declare the material as local custom properties
   (--key-face, --key-image, --m-base …) that read from materials.css with
   a flat fallback baked in:

       background-color: var(--m-face, var(--bg-raised));

   so every control still renders correctly — just flat — if materials.css
   is ever absent. Variants re-skin by overriding those variables rather
   than by re-declaring background-color, which is what keeps the metal,
   the press state and the hover state in agreement.

   Contrast on metal is measured — from rendered pixels — in
   css/materials.css's header. The one rule that matters here: --ink-3
   never sits on a metal face (3.88:1 in graphite). .btn carries --ink
   (13.8:1 / 10.7:1), .chip carries --ink-2 (6.9:1 / 5.3:1), and a
   disabled key drops its metal entirely. */

/* ============================================================== *
 * 1. RESET
 * ============================================================== */

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

* {
  margin: 0;
}

html {
  block-size: 100%;
  background-color: var(--bg);
  /* Stop iOS bumping font sizes when the phone is turned sideways. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scrollbar-color: var(--ramp-500) transparent;
  scrollbar-width: thin;
}

img,
svg,
video,
canvas,
picture {
  display: block;
  max-inline-size: 100%;
}

svg {
  fill: currentColor;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
}

p,
h1,
h2,
h3,
h4,
h5,
h6,
li,
dd,
dt {
  overflow-wrap: break-word;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: inherit;
  /* PHASE4 §3: three weights in the entire app — 300, 400, 500 — with 600
     permitted for the single START control and nowhere else. Hierarchy
     comes from size and space, not from piling on weight. */
  font-weight: 500;
  line-height: var(--lh-snug);
  letter-spacing: var(--track-tight);
}

ul,
ol {
  padding-inline-start: 0;
  list-style: none;
}

a {
  color: var(--accent-cool);
  text-decoration-thickness: var(--hairline-w);
  text-underline-offset: 0.18em;
}

fieldset {
  border: 0;
  padding: 0;
  min-inline-size: 0;
}

legend {
  padding: 0;
}

table {
  border-collapse: collapse;
}

hr {
  block-size: 0;
  border: 0;
  border-block-start: var(--hairline-w) solid var(--hairline);
}

/* Agents toggle visibility with the hidden attribute; make it win. */
[hidden] {
  display: none !important;
}

/* ============================================================== *
 * 2. BODY / APP SHELL
 * ============================================================== */

body {
  min-block-size: 100svh;
  color: var(--ink);
  background-color: var(--bg);
  /* Light from above: one wash describing the body curving away from a
     source overhead. Sized to a viewport band rather than the document so
     it does not stretch on long pages. (background-attachment: fixed is
     deliberately avoided — it is unreliable and janky in mobile Safari.)

     css/materials.css restates this stack with plaster.png tiled beneath
     the wash — the ground the object sits on. The wash is declared here on
     its own so the page still lights correctly without that sheet. */
  background-image: var(--wash-top);
  background-repeat: no-repeat;
  background-size: 100% 100svh;

  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--lh-base);
  /* Applied at the root so EVERY digit in the app is tabular, whether or
     not a module remembers to ask. A countdown must never jitter. */
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--font-num-feat);

  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
  accent-color: var(--signal);
  caret-color: var(--signal);

  /* Kill rubber-band scroll chaining so a drag on the dial can't bounce
     the whole page on iOS. */
  overscroll-behavior-y: none;

  /* iPhone notch / home indicator / landscape ears. */
  padding-block-start: var(--safe-top);
  padding-block-end: var(--safe-bottom);
  padding-inline-start: var(--safe-left);
  padding-inline-end: var(--safe-right);
}

::selection {
  background-color: color-mix(in oklab, var(--signal) 24%, transparent);
  color: var(--ink);
}

/* Optional named frame for the shell agent. Purely a max-width column and
   a full-height flex container; it imposes no grid of its own. */
.app-shell {
  display: flex;
  flex-direction: column;
  min-block-size: 100%;
  inline-size: 100%;
  max-inline-size: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-4);
  padding-block: var(--space-4);
}

@media (min-width: 40rem) {
  .app-shell {
    padding-inline: var(--space-5);
    padding-block: var(--space-5);
  }
}

/* ============================================================== *
 * 3. FOCUS
 *    Never removed anywhere in this app. Signal-coloured, offset so the
 *    ring sits clear of the control's own edge highlight.
 * ============================================================== */

:focus-visible {
  outline: var(--ring-w) solid var(--signal);
  outline-offset: var(--ring-offset);
}

/* Elements that scroll or clip can't show an offset outline; give them the
   ring as a box-shadow instead by opting in with .u-ring-inset. */
.u-ring-inset:focus-visible {
  outline: none;
  box-shadow:
    inset 0 0 0 var(--ring-w) var(--signal),
    var(--shadow-raise);
}

/* Skip link: hidden until focused, then a real raised control. */
.skip-link {
  position: absolute;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  z-index: var(--z-toast);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-m);
  background-color: var(--bg-raised);
  color: var(--ink);
  box-shadow: var(--shadow-raise);
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ============================================================== *
 * 4. PRIMITIVE: .btn
 *    A raised key in brushed aluminium. It has a 1px top highlight, a
 *    crisp edge, a shallow drop shadow, one specular band — and on press
 *    it physically travels 1px down, the drop shadow collapses inward,
 *    the metal darkens and the specular band slides down 1px with it.
 * ============================================================== */

.btn {
  /* The material, as variables, so every variant re-skins the key by
     overriding these rather than by fighting background-color in four
     separate states. Fallbacks keep the key flat but correct if
     css/materials.css is absent.

     Pressed metal is a second complete image, not a tweak to the first:
     the light model has to change, and a variable poked at from a state
     rule would not reach inside an already-resolved token. See the rule
     at the top of css/materials.css §3. */
  --key-face: var(--m-face, var(--bg-raised));
  --key-face-press: var(--m-face-press, var(--surface-2));
  --key-image: var(--m-metal-image, none);
  --key-image-press: var(--m-metal-image-press, none);
  --key-pos: var(--m-metal-pos, 0 0);
  --key-pos-press: var(--m-metal-pos-press, 0 0);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  min-block-size: var(--tap);
  min-inline-size: var(--tap);
  padding-block: var(--space-2);
  padding-inline: var(--space-4);

  border: 0;
  border-radius: var(--radius-m);
  background-color: var(--key-face);
  background-image: var(--key-image);
  background-size: var(--m-metal-size, auto);
  background-repeat: var(--m-metal-repeat, no-repeat);
  background-position: var(--key-pos);
  background-blend-mode: var(--m-metal-blend, normal);
  color: var(--ink);
  box-shadow: var(--shadow-raise);

  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;

  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;

  /* Release is the slow half and carries the spring, so the key travels
     back up with a hair of overshoot; the press below is the fast half on
     --ease-out. That asymmetry is what a real switch feels like. */
  transition:
    transform var(--dur-fast) var(--ease-spring),
    box-shadow var(--dur-med) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    background-position var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

/* Sentence-case escape hatch for long labels in settings. */
.btn--plain {
  text-transform: none;
  letter-spacing: var(--track-0);
  font-size: var(--step-0);
  font-weight: 500;
}

.btn--lg {
  min-block-size: 56px;
  padding-inline: var(--space-5);
  font-size: var(--step-1);
  border-radius: var(--radius-l);
}

/* START, and only START. PHASE4 §3 allows one 600 in the whole app and
   this is it. The old 700 existed to buy the WCAG "large text" exemption
   (18.66px bold at 4.5:1 → 3:1), which is no longer needed: white on
   --signal-key measures 4.85:1 in ceramic and --on-signal on --signal
   measures 5.90:1 in graphite, so the label clears normal-text AA
   outright at any weight. .btn--lg on its own — the alarm's Stop key —
   stays at 500. */
.btn--lg.btn--primary {
  font-weight: 600;
}

.btn--icon {
  inline-size: var(--tap);
  block-size: var(--tap);
  padding: 0;
  border-radius: var(--radius-m);
}

.btn--round {
  border-radius: var(--radius-pill);
}

/* Quiet: no key at all, so there is no face to be metal. Just a label.
   Still travels on press. */
.btn--quiet {
  --key-face: transparent;
  --key-face-press: color-mix(in oklab, var(--bg) 88%, var(--ink));
  --key-image: none;
  --key-image-press: none;
  color: var(--ink-2);
  box-shadow: none;
}

.btn--primary {
  /* Signal, not metal. The one loud control in the object does not get a
     material — it gets the colour, and it keeps it in every state.
     --signal-key, not --signal: white on the raw disc red measures 4.31:1
     and fails AA for a button label. The disc keeps --signal (contract). */
  --key-face: var(--signal-key);
  --key-face-press: var(--signal-key);
  --key-image: none;
  --key-image-press: none;
  color: var(--on-signal);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--on-signal) 22%, transparent),
    0 0 0 1px color-mix(in oklab, var(--signal-key) 70%, var(--ink)),
    0 1px 1px rgba(0, 0, 0, 0.14),
    0 2px 3px rgba(0, 0, 0, 0.09);
}

.btn--cool {
  --key-face: var(--accent-cool);
  --key-face-press: var(--accent-cool);
  --key-image: none;
  --key-image-press: none;
  color: var(--on-accent);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--on-accent) 20%, transparent),
    0 0 0 1px color-mix(in oklab, var(--accent-cool) 70%, var(--ink)),
    0 1px 1px rgba(0, 0, 0, 0.14),
    0 2px 3px rgba(0, 0, 0, 0.09);
}

/* One hover rule for every variant, because each one publishes its own
   --key-face. Quiet keeps its own: it tints the body, not a key face. */
@media (hover: hover) {
  .btn:hover:not(:disabled):not([aria-disabled="true"]) {
    background-color: color-mix(in oklab, var(--key-face) 94%, var(--ink));
  }

  .btn--quiet:hover:not(:disabled):not([aria-disabled="true"]) {
    background-color: color-mix(in oklab, var(--bg) 92%, var(--ink));
    color: var(--ink);
  }
}

/* THE PRESS. Down fast on --ease-out, back up on the spring — that
   asymmetry is what makes a key feel mechanical rather than animated.
   Pressed metal also darkens, and its specular band slides down 1px:
   the highlight is a reflection of a fixed light source, so when the key
   travels the reflection travels with it. */
.btn:active:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(1px);
  box-shadow: var(--shadow-press);
  background-color: var(--key-face-press);
  background-image: var(--key-image-press);
  background-position: var(--key-pos-press);
  transition-duration: var(--dur-press);
  transition-timing-function: var(--ease-out);
}

.btn--quiet:active:not(:disabled):not([aria-disabled="true"]) {
  box-shadow: none;
}

.btn--primary:active:not(:disabled):not([aria-disabled="true"]),
.btn--cool:active:not(:disabled):not([aria-disabled="true"]) {
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.32),
    inset 0 -1px 0 rgba(255, 255, 255, 0.1),
    0 0 0 1px rgba(0, 0, 0, 0.18);
}

/* Latched (a toggle that is on) reads as a key held down: still metal,
   but the pressed face, with the specular band already dropped 1px. */
.btn[aria-pressed="true"],
.btn.is-latched {
  --key-face: var(--key-face-press);
  --key-image: var(--key-image-press);
  --key-pos: var(--key-pos-press);
  color: var(--ink);
  box-shadow: var(--shadow-press);
  transform: translateY(1px);
}

.btn[aria-pressed="true"]:active,
.btn.is-latched:active {
  transform: translateY(1px);
}

/* A dead key is not a surface you touch: the metal comes off entirely.
   That also keeps --ink-3 off a metal face, where it measures 3.8:1 in
   graphite. */
.btn:disabled,
.btn[aria-disabled="true"] {
  --key-face: var(--surface-2);
  --key-image: none;
  --key-image-press: none;
  cursor: default;
  color: var(--ink-3);
  box-shadow: 0 0 0 var(--hairline-w) var(--hairline);
  transform: none;
}

/* ============================================================== *
 * 5. PRIMITIVE: .chip
 *    A small raised pill. Presets, scale toggles, day-of-week keys.
 *    Touch target stays 44px even though the pill reads smaller.
 * ============================================================== */

.chip {
  /* Same alloy as .btn — a chip is a small key, and it is touched. Its
     label is --ink-2, which measures 6.7:1 / 5.1:1 on the composited
     metal face. Never drop a chip's label to --ink-3. */
  --key-face: var(--m-face, var(--bg-raised));
  --key-face-press: var(--m-face-press, var(--surface-2));
  --key-image: var(--m-metal-image, none);
  --key-pos: var(--m-metal-pos, 0 0);
  --key-pos-press: var(--m-metal-pos-press, 0 0);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  min-block-size: var(--tap);
  min-inline-size: var(--tap);
  padding-block: var(--space-2);
  padding-inline: var(--space-4);

  border: 0;
  border-radius: var(--radius-pill);
  background-color: var(--key-face);
  background-image: var(--key-image);
  background-size: var(--m-metal-size, auto);
  background-repeat: var(--m-metal-repeat, no-repeat);
  background-position: var(--key-pos);
  background-blend-mode: var(--m-metal-blend, normal);
  color: var(--ink-2);
  box-shadow: var(--shadow-raise);

  font-size: var(--step--1);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  white-space: nowrap;

  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;

  transition:
    transform var(--dur-fast) var(--ease-spring),
    box-shadow var(--dur-med) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    background-position var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

@media (hover: hover) {
  .chip:hover:not(:disabled):not([aria-disabled="true"]) {
    background-color: color-mix(in oklab, var(--key-face) 94%, var(--ink));
    color: var(--ink);
  }
}

.chip:active:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(1px);
  box-shadow: var(--shadow-press);
  background-color: var(--key-face-press);
  background-image: var(--key-image-press);
  background-position: var(--key-pos-press);
  transition-duration: var(--dur-press);
  transition-timing-function: var(--ease-out);
}

/* Selected: pushed all the way into the chassis, so you are looking at
   the ceramic floor of the recess rather than at the key's metal face.
   Label in signal. Engaged, not shouting. Keeping the sunk ceramic here
   is also what preserves --signal-text's measured 4.5:1 on --bg-sunk. */
.chip[aria-pressed="true"],
.chip[aria-selected="true"],
.chip.is-selected {
  --key-face: var(--bg-sunk);
  --key-face-press: var(--bg-sunk);
  --key-image: none;
  --key-image-press: none;
  color: var(--signal-text);
  box-shadow: var(--shadow-press);
  transform: translateY(1px);
}

/* Loud variant, for the one chip that must be unmissable. Signal, not
   metal — the same rule START follows. */
.chip--signal {
  --key-face: var(--signal-key);
  --key-face-press: var(--signal-key);
  --key-image: none;
  --key-image-press: none;
  color: var(--on-signal);
}

.chip:disabled,
.chip[aria-disabled="true"] {
  --key-face: var(--surface-2);
  --key-image: none;
  --key-image-press: none;
  cursor: default;
  color: var(--ink-3);
  box-shadow: 0 0 0 var(--hairline-w) var(--hairline);
  transform: none;
}

/* ============================================================== *
 * 6. PRIMITIVE: .field
 *    Inputs are cut into the surface, the inverse of a key. A recess is
 *    not a thing you touch, it is a thing you type into, so it stays
 *    matte ceramic — and it stays untextured: a field is rarely more
 *    than 44px tall, which is a third of one ceramic tile, so the grain
 *    would arrive as an uneven tint rather than as a surface.
 * ============================================================== */

.field {
  display: block;
  inline-size: 100%;
  min-block-size: var(--tap);
  padding-block: var(--space-2);
  padding-inline: var(--space-3);

  border: var(--hairline-w) solid var(--hairline);
  border-radius: var(--radius-m);
  background-color: var(--bg-sunk);
  color: var(--ink);
  box-shadow: var(--shadow-field);

  font: inherit;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}

.field::placeholder {
  color: var(--ink-3);
  opacity: 1;
}

.field:focus-visible {
  border-color: color-mix(in oklab, var(--signal) 55%, var(--hairline));
}

.field:disabled {
  color: var(--ink-3);
  box-shadow: none;
}

select.field {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: var(--space-6);
  cursor: pointer;
}

textarea.field {
  min-block-size: calc(var(--tap) * 2);
  resize: vertical;
  line-height: var(--lh-base);
}

input[type="range"].field {
  min-block-size: var(--tap);
  padding-inline: var(--space-2);
  box-shadow: none;
  background-color: transparent;
  border-color: transparent;
  cursor: pointer;
}

.field--num {
  font-family: var(--font-num);
  font-size: var(--step-1);
  text-align: end;
}

/* Field furniture */
.field-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field-label {
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}

.field-hint {
  font-size: var(--step--1);
  line-height: var(--lh-snug);
  color: var(--ink-3);
}

.field-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-block-size: var(--tap);
}

/* ============================================================== *
 * 7. PRIMITIVE: .panel and .well
 *    Moulded ceramic. These are surfaces you look at and put controls
 *    on, not surfaces you touch, so they never take metal.
 *
 *    --m-base tells css/materials.css which colour the ceramic tile is
 *    being laid over; it must be declared on the same element that uses
 *    the tile, because custom properties inherit and an ancestor's
 *    --m-base would otherwise tint the veil wrongly. Variants override
 *    --m-base and inherit .panel's background-* declarations.
 * ============================================================== */

.panel {
  --m-base: var(--surface);
  position: relative;
  background-color: var(--m-base);
  background-image: var(--m-tile-image, none);
  background-size: var(--m-tile-size, auto);
  background-repeat: var(--m-tile-repeat, repeat);
  background-blend-mode: var(--m-tile-blend, normal);
  color: var(--ink);
  border: var(--hairline-w) solid var(--hairline);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-panel);
  padding: var(--space-5);
}

.panel--tight {
  padding: var(--space-3);
}

.panel--flush {
  padding: 0;
  overflow: hidden;
}

.panel--raised {
  --m-base: var(--bg-raised);
  border-color: transparent;
  box-shadow: var(--shadow-raise);
}

.panel--sunk {
  --m-base: var(--bg-sunk);
  border-color: transparent;
  box-shadow: var(--shadow-well);
}

.panel__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-block-end: var(--space-3);
}

.panel__title {
  font-size: var(--step--1);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}

.panel__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* A bare recessed well — the dial housing, meters, inset readouts. The
   floor of a recess is ceramic too; it is simply in shadow. */
.well {
  --m-base: var(--bg-sunk);
  background-color: var(--m-base);
  background-image: var(--m-tile-image, none);
  background-size: var(--m-tile-size, auto);
  background-repeat: var(--m-tile-repeat, repeat);
  background-blend-mode: var(--m-tile-blend, normal);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-well);
}

.well--round {
  border-radius: var(--radius-pill);
}

.rule {
  block-size: var(--hairline-w);
  background-color: var(--hairline);
  border: 0;
}

/* ============================================================== *
 * 8. UTILITIES
 * ============================================================== */

.u-sr-only {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Visible once focused — for skip links and hidden shortcuts. */
.u-sr-only-focusable:not(:focus):not(:focus-within) {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.u-safe-pad {
  padding-block-start: var(--safe-top);
  padding-block-end: var(--safe-bottom);
  padding-inline-start: var(--safe-left);
  padding-inline-end: var(--safe-right);
}

.u-safe-bottom {
  padding-block-end: max(var(--space-3), var(--safe-bottom));
}

.u-num {
  font-family: var(--font-num);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--font-num-feat);
  letter-spacing: var(--track-tight);
  line-height: var(--lh-tight);
}

.u-caps {
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}

.u-muted {
  color: var(--ink-2);
}

.u-quiet {
  color: var(--ink-3);
}

/* Signal AS TYPE. Uses --signal-text, not --signal: the raw disc red is a
   fill colour and does not clear 4.5:1 as small text on ceramic. */
.u-signal {
  color: var(--signal-text);
}

.u-tap {
  min-inline-size: var(--tap);
  min-block-size: var(--tap);
}

.u-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.u-no-select {
  user-select: none;
  -webkit-user-select: none;
}

/* ============================================================== *
 * 9. PREFERENCE OVERRIDES
 * ============================================================== */

/* Motion becomes an instant state change. 0.01ms rather than 0 so that
   transitionend / animationend still fire and no state machine stalls
   waiting for an event that never arrives. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-press: 0.01ms;
    --dur-fast: 0.01ms;
    --dur-med: 0.01ms;
    --dur-slow: 0.01ms;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media (prefers-contrast: more) {
  :root,
  [data-theme] {
    --hairline: color-mix(in oklab, var(--ink) 48%, transparent);
    --hairline-strong: color-mix(in oklab, var(--ink) 72%, transparent);
    --ink-2: var(--ramp-800);
    --ink-3: var(--ramp-700);
  }

  :focus-visible {
    outline-width: 3px;
  }
}

@media (forced-colors: active) {
  .btn,
  .chip,
  .field,
  .panel,
  .well {
    border: 1px solid ButtonBorder;
    box-shadow: none;
    /* The system owns every surface here: no metal, no ceramic, no
       texture. css/materials.css strips its own layers to match. */
    background-image: none;
  }

  :focus-visible {
    outline: 2px solid Highlight;
  }
}
