/* Liquid Glass — COMPONENT layer.
 *
 * Chrome for everything in `lib/`. Depends on `material.css` for the `.lg`
 * panel styling and every `--lg-*` token; it defines none of its own, which is
 * what keeps a single override on `:root` able to restyle the whole set.
 */

/* --------------------------------------------------------------------- type */
/* Every component root, and only component roots. Until this rule existed the
   library shipped no `font-family` at all: the demos set one on `body` and
   everything inherited it, so it looked right here and rendered an installed
   package in the browser's default serif — a design system in Times New Roman.
 *
 * `:where()` for zero specificity, so a host application overrides it with a
 * plain class and without a fight. Roots only: children mostly say
 * `font: inherit` already, and the ones that do not are choosing a size, not a
 * face. */
:where(.lg-surface, .lgbadge, .lgbar, .lgbtn, .lgcard, .lgchoice, .lgdlg,
  .lgfield, .lglist, .lgmenu, .lgnav, .lgsa, .lgsb, .lgseg, .lgselect, .lgsh,
  .lgsw, .lgtb, .lgtip, .lgtoast, .lgtoasts, .lgtoolbar) {
  font-family: var(--lg-font);
}

/* ------------------------------------------------------------------ switch */
/* Geometry only. Everything that moves is set from switch.js, because the lens
   subregion has to be rebuilt in step with it. */

.lgsw {
  position: relative;
  display: inline-block;
  width: var(--lgsw-w);
  height: var(--lgsw-h);
  padding: 0;
  border: 0;
  background: none;
  border-radius: 999px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* The knob is draggable, so the browser must not claim horizontal gestures
     for scrolling before pointermove ever fires. */
  touch-action: none;
  user-select: none;
}
.lgsw:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: 3px; }

.lgsw-track {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: var(--lg-groove);
  box-shadow: inset 0 1px 2px var(--lg-groove-shadow);
}
.lgsw-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  background: var(--lgsw-on);
}

/* Wins over `.lg` on position and radius — same specificity, later in the file. */
.lgsw-knob {
  position: absolute;
  border-radius: 999px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.28), var(--lg-rim);
}

/* SlideAction — pill track, pill knob in the middle, commits either way. */
.lgsa {
  position: relative;
  display: block;
  max-width: 100%;
  border-radius: 999px;
  /* The knob is dragged horizontally; without this the browser claims the
     gesture for scrolling before pointermove ever fires. */
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* The pointer target does not shrink with the paint. `sm` and `mini` are 40px
   against a 44px minimum, so an invisible strip carries the hit area. It
   only claims the space above and below the track, because ::before paints
   before the element's children and every one of those is positioned — so the
   track, labels and knob all sit on top of it. Events on it target the host.

   No `z-index: -1` here, which is the obvious way to write it and does not
   work: .lgsa is positioned but has no z-index, so it is not a stacking
   context, and a negative index escapes to the ancestor's — landing the strip
   behind the PARENT, which then wins the hit test. */
.lgsa::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: var(--lgsa-hit, 100%);
  transform: translateY(-50%);
}

.lgsa-track {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: var(--lg-groove);
  box-shadow: inset 0 1px 2px var(--lg-groove-shadow);
}
.lgsa-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--lgsa-right, #34c759);
  transition: background 140ms linear;
}
.lgsa-fill[data-side='left'] { background: var(--lgsa-left, #ff453a); }

/* The labels ARE the buttons — they carry the actions and the focus. */
.lgsa-act {
  position: absolute;
  top: 0;
  bottom: 0;
  display: grid;
  place-items: center;
  padding: 0 clamp(14px, 4%, 26px);
  border: 0;
  background: none;
  color: var(--lg-ink);
  font: inherit;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  border-radius: inherit;
}
.lgsa-left { left: 0; }
.lgsa-right { right: 0; }
.lgsa-act:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -4px; }

/* No room for words at these sizes. The text stays in the DOM for assistive tech and
   a chevron carries the direction visually. */
.lgsa[data-compact] .lgsa-act { padding: 0 9px; }
.lgsa[data-compact] .lgsa-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.lgsa[data-compact] .lgsa-act::after {
  content: '';
  width: 6px;
  height: 6px;
  border: solid #fff;
  border-width: 2px 2px 0 0;
  border-radius: 1px;
}
.lgsa[data-compact] .lgsa-left::after { transform: rotate(-135deg) translate(-1px, -1px); }
.lgsa[data-compact] .lgsa-right::after { transform: rotate(45deg) translate(-1px, -1px); }

.lgsa-knob {
  position: absolute;
  border-radius: 999px;
  cursor: grab;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), var(--lg-rim);
}
.lgsa[data-armed='left'] .lgsa-knob,
.lgsa[data-armed='right'] .lgsa-knob { cursor: grabbing; }

/* VERTICAL AXIS — added in titi.re's vendored copy, belongs upstream alongside
   the `axis: 'y'` option in slide-action.js. Only the rules that actually name
   an axis are restated; the rest of the control already works standing up.
   `left` stays the name of the low end and `right` of the high one, so up the
   track is `left` and down it is `right` — the JS never learns a new word. */
.lgsa[data-axis='y']::before {
  left: 50%;
  right: auto;
  top: 0;
  bottom: 0;
  width: var(--lgsa-hit, 100%);
  height: auto;
  transform: translateX(-50%);
}
.lgsa[data-axis='y'] .lgsa-fill {
  left: 0;
  right: 0;
  top: auto;
  bottom: auto;
}
.lgsa[data-axis='y'] .lgsa-act {
  left: 0;
  right: 0;
  top: auto;
  bottom: auto;
}
.lgsa[data-axis='y'] .lgsa-left { top: 0; }
.lgsa[data-axis='y'] .lgsa-right { bottom: 0; }
.lgsa[data-axis='y'][data-compact] .lgsa-act { padding: 9px 0; }
/* The chevrons point along the travel. */
.lgsa[data-axis='y'][data-compact] .lgsa-left::after {
  transform: rotate(-45deg) translate(-1px, -1px);
}
.lgsa[data-axis='y'][data-compact] .lgsa-right::after {
  transform: rotate(135deg) translate(-1px, -1px);
}

/* Dark scheme. The engine writes --lg-tint and both rim colours per instance,
   so only the things it does not own live here: the grounding shadow, which has
   to deepen because a soft shadow over a dark page reads as nothing, and the
   solid fallbacks, which invert outright. */
.lg[data-lg-scheme='dark'] {
  --lg-shadow: 0 8px 26px rgba(0, 0, 0, 0.5);
}
.lg[data-lg-scheme='dark'][data-lg-tier='flat'] {
  background: color-mix(in srgb,
    var(--lg-surface) calc((1 - var(--lg-translucency)) * 100%),
    var(--lg-tint, rgba(255, 255, 255, 0.1)));
}
/* GlassBar — separate islands, one of which can take over the row. */
.lgbar { position: relative; display: block; }
.lgbar-island {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border: 0;
  padding: 0;
  color: var(--lg-ink);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: -0.01em;
  display: grid;
  place-items: center;
  overflow: hidden;
  cursor: pointer;
  /* Width and X are written per frame; a transition would fight the curve. */
  will-change: transform, width;
}
.lgbar-island:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: 3px; }
.lgbar-content {
  white-space: nowrap;
  padding: 0 14px;
  /* Fades rather than reflowing: a label re-wrapping mid-collapse is the one
     thing that gives away that these are boxes and not objects. */
  transition: opacity 140ms ease;
}
.lgbar-island.is-collapsed .lgbar-content { opacity: 0; }

/* Toolbar — slots, with trailing actions collapsing into an overflow menu. */
.lgtoolbar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 52px;
  padding: 0 10px;
}
.lgtb-slot { display: flex; align-items: center; gap: 4px; }
.lgtb-lead { flex: 0 0 auto; }
.lgtb-trail { flex: 0 0 auto; margin-left: auto; }
.lgtb-title {
  flex: 1 1 auto;
  /* Not `min-width: 0`. Reserving space for the title in the layout maths does
     not stop flex shrinking the ELEMENT to nothing — the toolbar then packs
     correctly around a title you cannot see. This is the floor below which it
     ellipsises instead of disappearing. */
  min-width: 3.5ch;
  text-align: center;
  font-weight: 640;
  font-size: 15px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lgtb-btn {
  flex: 0 0 auto;
  border: 0;
  background: none;
  color: var(--lg-ink);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  padding: 8px 12px;
  border-radius: 999px;
  cursor: pointer;
}
.lgtb-btn:hover { background: var(--lg-fill); }
.lgtb-btn:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -2px; }
.lgtb-btn[hidden] { display: none; }
.lgtb-more { letter-spacing: 0.06em; }

/* The overflow menu's own styling used to live here. It is `.lgmenu` now — the
   Toolbar composes the real Menu component instead of growing a second one. */

/* NavBar — large title collapsing into an inline one. */
.lgnav {
  position: relative;
  display: block;
  overflow: hidden;
  /* Height is driven from JS per scroll frame; a transition would fight it. */

  /* One inset for the row and the title, so `Edit` and `Library` share a
     margin — two different fixed values is the sloppiness you see before you
     can name it.

     It tracks the corner, because a flat 18px does not clear one. At the large
     title's baseline on a 30px radius the panel's own edge is 15px in, leaving
     3px of daylight; `--lg-radius` is published by `Surface` from the value it
     actually applied. The `max()` keeps the classic full-width bar — radius 0,
     inset 18 — exactly where it was. */
  --lgnav-inset: max(18px, calc(var(--lg-radius, 28px) * 0.72));
}
.lgnav-row {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--lgnav-inset);
  font-size: 15px;
}
.lgnav-compact {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-weight: 640;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.lgnav-lead, .lgnav-trail { opacity: 0.85; font-weight: 500; }
.lgnav-large {
  position: absolute;
  left: var(--lgnav-inset);
  right: var(--lgnav-inset);
  /* Shallower than the horizontal one: the bottom edge is straight for most of
     its length, so only the corner needs clearing, not the whole run. */
  bottom: max(10px, calc(var(--lg-radius, 28px) * 0.46));
  margin: 0;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  transform-origin: left bottom;
  white-space: nowrap;
  /* transform + opacity only: both stay on the compositor. */
  will-change: transform, opacity;
}
.lgnav-sep {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.16);
  pointer-events: none;
}
.lgnav[data-scheme='dark'] .lgnav-sep { background: rgba(255, 255, 255, 0.1); }

/* TabBar — the bar is the glass; the indicator is a filled capsule on it. */
.lgtb {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 0;
  padding: 6px;
  isolation: isolate;
}
/* `.lgtb > ` is load-bearing, not tidiness. The engine adds `.lg` to whatever it
   mounts on, and `.lg > *:not(.lg-proxy)` forces `position: relative` on every
   child at specificity (0,2,0) — which beats a lone `.lgtb-ind` and drops the
   indicator back INTO the flex flow, where its per-frame inline width shoves the
   tabs sideways while it animates. Matching that specificity puts it back out. */
.lgtb > .lgtb-ind {
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  border-radius: 999px;
  background: var(--lg-fill-strong);
  box-shadow: inset 0 0 0 1px var(--lg-hairline);
  pointer-events: none;
  /* No CSS transition: the position and width are driven per frame by the same
     lead/trail pair the Toggle uses, and a transition on top would fight it. */
}

.lgtb-tab {
  position: relative;
  z-index: 1;
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 40px;
  padding: 0 16px;
  border: 0;
  background: none;
  color: var(--lg-ink);
  font: inherit;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  border-radius: 999px;
  opacity: 0.62;
  transition: opacity 160ms ease;
}
.lgtb-tab[aria-selected='true'] { opacity: 1; }
.lgtb-tab:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -3px; }
.lgtb-icon { display: block; font-style: normal; line-height: 1; }

/* Sidebar — a glass rail that collapses to icons and can be dragged wider. */
.lgsb {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 8px;
  isolation: isolate;
  /* The width is written inline per frame while dragging; no transition here or
     it would fight the drag. `applyWidth` adds one only for the snap. */
  overflow: hidden;
}
.lgsb-body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
}
.lgsb-scroll {
  position: relative;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  /* Room for the scroll-edge ramps, so the first and last row can sit fully
     clear of the fade rather than permanently half-blurred. */
  padding: 10px 0;
}
.lgsb-scroll::-webkit-scrollbar { width: 0; }
/* Content fades under the panel's own edges. A mask rather than a layer: it
   adds no paint, so it cannot accumulate the glass tint the way stacked
   backdrop-filters do. Both ends are driven from JS and are 0 when there is
   nothing hidden past them. */
.lgsb-scroll {
  --fade-top: 0px;
  --fade-bot: 0px;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bot)), transparent 100%);
}

/* Same specificity fight as the TabBar indicator — see the note above. */
.lgsb-scroll > .lgsb-ind {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  border-radius: 12px;
  background: var(--lg-fill-strong);
  box-shadow: inset 0 0 0 1px var(--lg-hairline);
  pointer-events: none;
}

.lgsb-section {
  padding: 14px 12px 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lg-ink-dim);
  white-space: nowrap;
}
.lgsb-item {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 38px;
  padding: 0 12px;
  border: 0;
  background: none;
  color: var(--lg-ink);
  font: inherit;
  font-weight: 600;
  font-size: 13.5px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  text-align: left;
  cursor: pointer;
  border-radius: 12px;
  opacity: 0.66;
  transition: opacity 160ms ease;
}
.lgsb-item[aria-current='page'] { opacity: 1; }
.lgsb-item:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -3px; }
.lgsb-icon {
  flex: none;
  width: 20px;
  font-style: normal;
  line-height: 1;
  text-align: center;
}
.lgsb-label { overflow: hidden; text-overflow: ellipsis; }

/* Collapsed: the labels go, the glyphs centre. Visually hidden rather than
   `display: none` so the button still announces its name. */
.lgsb[data-collapsed='true'] .lgsb-item { justify-content: center; padding: 0 8px; }
/* A section name does not fit a 64px rail — truncating it to "PLAYLIS" is worse
   than not showing it. The heading becomes a hairline, which is what the
   grouping actually needs to convey at this width, and the text is moved out of
   sight rather than out of the accessibility tree. */
.lgsb[data-collapsed='true'] .lgsb-section {
  padding: 0;
  margin: 12px 14px 8px;
  height: 1px;
  overflow: hidden;
  text-indent: -200px;
  background: var(--lg-hairline);
  border-radius: 1px;
}
.lgsb[data-collapsed='true'] .lgsb-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* `.lgsb > ` for the same reason as `.lgtb > .lgtb-ind` above: the grip is a
   direct child of the host the engine mounted on, so `.lg > *:not(.lg-proxy)`
   at (0,2,0) beats a lone `.lgsb-grip` and forces `position: relative` — which
   drops the handle out of the right edge and into the column flow, where it is
   invisible and undraggable. Measured: a drag on it moved the width by 0px. */
.lgsb > .lgsb-grip {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 10px;
  cursor: ew-resize;
  /* No paint of its own — a visible handle on a glass rail reads as a seam.
     It becomes a hairline only while it is being used or focused. */
  background: none;
  border: 0;
  padding: 0;
}
.lgsb-grip::after {
  content: '';
  position: absolute;
  top: 12px;
  bottom: 12px;
  right: 3px;
  width: 2px;
  border-radius: 2px;
  background: var(--lg-ink-faint);
  opacity: 0;
  transition: opacity 140ms ease;
}
.lgsb-grip:hover::after,
.lgsb-grip:focus-visible::after,
.lgsb[data-dragging='true'] .lgsb-grip::after { opacity: 1; }
.lgsb-grip:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -2px; border-radius: 6px; }

/* Component chrome follows the RESOLVED scheme, which the component writes onto
   its own host. Not a `prefers-color-scheme` query: that only knows about the
   OS, and `scheme` is per instance — an instance forced to dark on a light OS
   would otherwise be dark glass sitting in a light groove. */
/* (the groove follows --lg-groove, set per scheme with the other ink tokens) */

/* Apple: reduce transparency wins over everything. */
@media (prefers-reduced-transparency: reduce) {
  .lg {
    background: rgba(28, 30, 36, 0.92);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .lg-proxy { display: none !important; }
}

/* Sheet — rises from the bottom edge, rests at a detent, dragged between them. */
.lgsh-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: #000;
  opacity: 0;
  transition: opacity 120ms linear;
}
.lgsh {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index: 61;
  display: flex;
  flex-direction: column;
  /* Only the top corners: the bottom edge is off-screen, and rounding it makes
     the sheet look like a floating card that failed to reach the edge. */
  border-radius: 22px 22px 0 0;
  overflow: hidden;
  /* The drag is vertical; without this the browser claims it for scrolling
     before pointermove ever fires. */
  touch-action: none;
  will-change: transform;
}
@media (min-width: 620px) {
  /* Inset card on anything wider than a phone — a full-bleed sheet on a desktop
     reads as a broken modal, not as a sheet. */
  .lgsh { left: 50%; right: auto; width: 560px; transform-origin: bottom center; margin-left: -280px; }
}
.lgsh > .lgsh-grab {
  position: relative;
  flex: none;
  height: 26px;
  border: 0;
  background: none;
  cursor: grab;
  padding: 0;
}
.lgsh > .lgsh-grab::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 9px;
  width: 38px;
  height: 5px;
  margin-left: -19px;
  border-radius: 3px;
  background: var(--lg-ink-faint);
}
.lgsh-grab:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: -4px; border-radius: 10px; }
.lgsh-body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
}
.lgsh-scroll {
  height: 100%;
  overflow-y: auto;
  padding: 4px 22px 28px;
  scrollbar-width: none;
}
.lgsh-scroll::-webkit-scrollbar { width: 0; }
.lgsh-scroll {
  --fade-top: 0px;
  --fade-bot: 0px;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top),
    #000 calc(100% - var(--fade-bot)), transparent 100%);
}
.lgsh-title {
  margin: 0 0 12px;
  font-size: 21px;
  font-weight: 650;
  letter-spacing: -0.02em;
}

/* ------------------------------------------------------------------ button */
/* Sizes come from JS as custom properties so one rule serves every size, and a
   new size in BUTTON_SIZES needs no CSS at all. */
.lgbtn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--lgbtn-gap);
  height: var(--lgbtn-h);
  padding: 0 var(--lgbtn-px);
  border: 0;
  border-radius: 999px;
  color: var(--lg-ink);
  /* `font: inherit` first to defeat the UA's own button font, then the family
     back on top — the shorthand resets `font-family` to the inherited value,
     which on an unstyled host page is the browser's default serif. The type
     rule at the top of this file cannot do it: it is `:where()`, so it is
     specificity zero and this declaration outranks it. Every OTHER
     `font: inherit` here is on a child of a root that already carries the
     family, so inheriting is exactly right; `.lgbtn` is the root. */
  font: inherit;
  font-family: var(--lg-font);
  font-size: var(--lgbtn-font);
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* Only the transform is animated. Animating a filtered element's box would
     rebuild its lens every frame. */
  transition: transform var(--lg-dur-press) var(--lg-ease-smooth), opacity 120ms linear;
}
.lgbtn[data-size='icon'] { width: var(--lgbtn-h); padding: 0; }
.lgbtn[data-block='true'] { display: flex; width: 100%; }

/* The pointer target does not shrink with the paint — same rule as SlideAction.
   ::before paints under the positioned children, so the label stays clickable. */
.lgbtn::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: var(--lgbtn-hit);
  transform: translateY(-50%);
}

.lgbtn:active:not([disabled]) { transform: scale(0.97); }
.lgbtn:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: 2px; }
/* `disabled` is out of the tab order, so nobody reads it closely — 0.5 is the
   familiar "off" weight. `inactive` IS reachable and is meant to be read (that
   is its whole purpose), so it stays legible and only loses its affordance. */
.lgbtn[disabled] { opacity: 0.5; cursor: not-allowed; }
.lgbtn[data-inactive='true'] { opacity: 0.72; cursor: not-allowed; }

/* Tones. `ghost` has no surface at all until it is hovered — that is what makes
   a row of them read as text rather than as a wall of chips. */
.lgbtn[data-tone='ghost'] { background: none; box-shadow: none; }
.lgbtn[data-tone='ghost']:hover:not([disabled]) { background: var(--lg-fill); }
/* The accent fill CANNOT use ::after — the engine already owns both pseudos on
   every `.lg` (::before is the dim layer, ::after the grain), and ::after
   carries `opacity: var(--lg-grain, 0)` and `mix-blend-mode: overlay`. Reusing
   it inherited opacity 0, so the tone silently did nothing and Primary rendered
   identical to Neutral. It is a real child element instead, which is also the
   only version that cannot collide with a future engine layer — and it carries
   `lg-abs`, without which `.lg > *` drags it back into the flex flow. */
.lgbtn[data-tone='primary'],
.lgbtn[data-tone='danger'] { color: var(--lg-on-accent); }
.lgbtn-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
}
.lgbtn[data-tone='primary'] .lgbtn-fill { background: var(--lg-accent); }
.lgbtn[data-tone='danger'] .lgbtn-fill { background: var(--lg-danger); }
/* The engine's `.lg > *:not(.lg-proxy)` already lifts real children to z-index 1,
   so the tint above sits under the label without another rule. */
.lgbtn-label, .lgbtn-icon { position: relative; z-index: 1; }
.lgbtn-icon { display: grid; place-items: center; font-style: normal; line-height: 1; }

/* Loading. The label stays in place and keeps its width — swapping it for a
   spinner makes the button change size mid-click, which moves everything after
   it in the row. */
.lgbtn-spin {
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  opacity: 0;
  z-index: 2;
}
.lgbtn[data-loading='true'] .lgbtn-spin { opacity: 0.9; animation: lgbtn-spin 700ms linear infinite; }
.lgbtn[data-loading='true'] .lgbtn-label,
.lgbtn[data-loading='true'] .lgbtn-icon { opacity: 0; }
.lgbtn[data-loading='true'] { cursor: progress; }
@keyframes lgbtn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .lgbtn[data-loading='true'] .lgbtn-spin { animation-duration: 2.4s; }
  .lgbtn { transition: none; }
}

/* ------------------------------------------------------------------- field */
/* Input / Textarea. The box is the Surface; the field wrapper is plain. */
.lgfield { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.lgfield-label {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--lg-ink-dim);
}
.lgfield-label[data-required='true']::after { content: ' *'; color: var(--lg-danger); }

.lgfield-box {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--lgin-h);
  padding: 0 var(--lgin-px);
  /* The focus ring lives on the BOX, not the control — the control has no
     border of its own, so a ring on it would sit inside the padding and read as
     a second nested field. */
  transition: box-shadow 120ms ease;
}
.lgfield[data-multiline='true'] .lgfield-box { align-items: stretch; padding-top: 9px; padding-bottom: 9px; }
.lgfield[data-focus='true'] .lgfield-box { box-shadow: 0 0 0 2px var(--lg-accent); }
.lgfield[data-invalid='true'] .lgfield-box { box-shadow: 0 0 0 1.5px var(--lg-danger); }
.lgfield[data-invalid='true'][data-focus='true'] .lgfield-box { box-shadow: 0 0 0 2px var(--lg-danger); }
.lgfield[data-disabled='true'] { opacity: 0.5; }
.lgfield[data-disabled='true'] .lgfield-box { cursor: not-allowed; }

.lgfield-control {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  border: 0;
  background: none;
  padding: 0;
  color: var(--lg-ink);
  font: inherit;
  font-size: var(--lgin-font);
  line-height: 1.45;
  /* The box already draws focus. Two rings is worse than none. */
  outline: none;
  resize: vertical;
}
.lgfield-control::placeholder { color: var(--lg-ink-faint); }
/* Chromium paints its own opaque background on autofill, which covers the glass
   entirely. There is no way to remove it — only to push it out with a long
   inset shadow, which is the documented workaround. */
.lgfield-control:-webkit-autofill,
.lgfield-control:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--lg-ink);
  box-shadow: 0 0 0 1000px transparent inset;
  transition: background-color 9999s;
}

.lgfield-affix { display: grid; place-items: center; color: var(--lg-ink-faint); font-size: 14px; }
.lgfield-desc { margin: 0; font-size: 12px; color: var(--lg-ink-faint); }
.lgfield-err { margin: 0; font-size: 12px; font-weight: 550; color: var(--lg-danger); }
@media (prefers-reduced-motion: reduce) { .lgfield-box { transition: none; } }

/* ------------------------------------------------------ checkbox and radio */
/* The native input is kept and made invisible, NOT removed — see choice.js.
   It stays focusable and stays in the form; only its rendering is replaced. */
.lgchoice {
  position: relative;
  display: inline-grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 9px;
  min-height: 26px;
  cursor: pointer;
}
.lgchoice-input {
  position: absolute;
  width: 20px;
  height: 20px;
  margin: 0;
  opacity: 0;
  cursor: inherit;
  z-index: 1;
}
.lgchoice-box {
  position: relative;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  transition: box-shadow 120ms ease;
}
.lgchoice[data-type='radio'] .lgchoice-box { border-radius: 999px; }
/* Focus follows the real input, which is what :focus-visible is watching. */
.lgchoice-input:focus-visible ~ .lgchoice-box { box-shadow: 0 0 0 2px var(--lg-accent); }
.lgchoice[data-disabled='true'] { opacity: 0.45; cursor: not-allowed; }

.lgchoice-mark {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  display: grid;
  place-items: center;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 110ms ease, transform var(--lg-dur-press) var(--lg-ease-snappy);
}
.lgchoice[data-checked='true'] .lgchoice-mark,
.lgchoice[data-indeterminate='true'] .lgchoice-mark { opacity: 1; transform: scale(1); }
.lgchoice[data-checked='true'] .lgchoice-mark { background: var(--lg-accent); }
.lgchoice[data-type='radio'] .lgchoice-mark { background: none; }
.lgchoice[data-type='radio'][data-checked='true'] .lgchoice-mark::after {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--lg-accent);
}
/* The tick is drawn, not a glyph — a glyph inherits the page font and lands in
   a different place in every one. */
.lgchoice[data-type='checkbox'][data-checked='true'] .lgchoice-mark::after {
  content: '';
  width: 5px;
  height: 9px;
  margin-top: -2px;
  border: solid var(--lg-on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(43deg);
}
.lgchoice[data-indeterminate='true'] .lgchoice-mark::after {
  content: '';
  width: 10px;
  height: 2px;
  border-radius: 1px;
  background: var(--lg-accent);
}
.lgchoice-label {
  font-size: 14px;
  color: var(--lg-ink);
  cursor: inherit;
  user-select: none;
}
.lgchoice-group { display: flex; flex-direction: column; gap: 10px; }
@media (prefers-reduced-motion: reduce) {
  .lgchoice-mark, .lgchoice-box { transition: none; }
}

/* ----------------------------------------------------------- badge and card */
.lgbadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.01em;
  /* A badge is usually a count, and proportional digits make it change width as
     it ticks 9 -> 10 -> 11, which shoves whatever is beside it. */
  font-variant-numeric: var(--lg-numeric);
  color: var(--lg-ink);
  white-space: nowrap;
}
.lgbadge[data-tone='accent'] { background: var(--lg-accent); color: var(--lg-on-accent); }
.lgbadge[data-tone='danger'] { background: var(--lg-danger); color: var(--lg-on-accent); }
.lgbadge[data-tone='neutral'] { background: var(--lg-fill); }
.lgbadge-dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; }

.lgcard { display: flex; flex-direction: column; gap: 10px; padding: 20px 22px; }
.lgcard-head { display: flex; align-items: flex-start; gap: 12px; }
.lgcard-title { margin: 0; font-size: 16.5px; font-weight: 640; letter-spacing: -0.012em; color: var(--lg-ink); }
.lgcard-sub { margin: 2px 0 0; font-size: 12.5px; color: var(--lg-ink-dim); }
.lgcard-body { font-size: 13.5px; line-height: 1.5; color: var(--lg-ink-dim); }
.lgcard-foot { display: flex; gap: 10px; align-items: center; margin-top: 4px; }
.lgcard-spacer { flex: 1 1 auto; }

/* ------------------------------------------------------ select and combobox */
/* Both draw their own list — see the note at the top of select.js. The real
   <select> stays in the DOM for the form and is hidden HERE rather than with
   `display: none`, which would take it out of form submission along with the
   rendering. */
.lgfield-native {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.lgfield[data-select='true'] .lgfield-control {
  display: flex;
  align-items: center;
  text-align: left;
  cursor: pointer;
}
.lgselect-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lgselect-text[data-placeholder='true'] { color: var(--lg-ink-faint); }
.lgfield[data-select='true'] .lgfield-affix:last-child,
.lgfield[data-combobox='true'] .lgfield-affix:last-child { font-size: 17px; margin-top: -3px; }

.lglist {
  position: fixed;
  z-index: 60;
  max-height: 264px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 5px;
}
.lglist-row {
  display: flex;
  align-items: center;
  min-height: 32px;
  padding: 0 10px;
  border-radius: 8px;
  font-size: 13.5px;
  color: var(--lg-ink);
  cursor: pointer;
  white-space: nowrap;
}
/* Hover and keyboard highlight are the SAME state deliberately. Two different
   highlights on one list means two rows look chosen at once, and Enter takes
   the one the mouse is not on. */
.lglist-row:hover,
.lglist-row[data-active='true'] { background: var(--lg-fill); }
.lglist-row[aria-selected='true'] { font-weight: 640; }
.lglist-row[aria-selected='true']::after { content: '✓'; margin-left: auto; padding-left: 14px; }
.lglist-row[aria-disabled='true'] { opacity: 0.42; cursor: not-allowed; }
.lglist-empty { padding: 9px 10px; font-size: 13px; color: var(--lg-ink-faint); }

/* ------------------------------------------------------------------ dialog */
/* The <dialog> element brings UA defaults that all have to go: a border, a
   white background, `width: fit-content` and auto margins. */
.lgdlg {
  /* NO padding here. A click on the backdrop reports the dialog itself as the
     event target, so any padding on this element becomes a ring that dismisses
     the dialog when clicked just inside its own edge. The children carry it. */
  padding: 0;
  border: 0;
  width: min(var(--lgdlg-w), calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  margin: auto;
  /* MUST clip. `<dialog>`'s UA style is `overflow: auto`, which beats `.lg`'s
     own `hidden` — and the engine's grain layer (`.lg::after`) is a square
     inset:0 rectangle, so unclipped it paints its corners outside the rounded
     rect and the dialog grows four visible square shoulders. The scrolling
     lives on `.lgdlg-body`, which has its own overflow. */
  overflow: hidden;
  color: var(--lg-ink);
  --lgdlg-w: 460px;
}
.lgdlg[data-size='sm'] { --lgdlg-w: 360px; }
.lgdlg[data-size='lg'] { --lgdlg-w: 620px; }
.lgdlg-title { margin: 0; padding: 22px 24px 0; font-size: 18px; font-weight: 650; letter-spacing: -0.015em; }
.lgdlg-body {
  padding: 10px 24px 0;
  max-height: min(56vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  font-size: 14px;
  line-height: 1.55;
  color: var(--lg-ink-dim);
}
.lgdlg-title + .lgdlg-body { padding-top: 8px; }
/* The footer needs MORE side padding than the text blocks, and it is not a
   rounding error. Measured: the buttons sit at exactly the same 24px inset as
   the title — and read tighter anyway, because a pill in the bottom corner is
   inside the radius's arc, where the clearance to the actual edge is less than
   the inset says. Text starts well clear of the corner and never notices. This
   is the concentric-radii rule (`concentric()` in surface.js) showing up as a
   padding: the tighter the corner, the less this matters. */
.lgdlg-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 18px 26px 24px; }

/* The backdrop is a pseudo-element on the top layer, so it cannot be styled
   through the page's own stacking — and it is the only dimming that works over
   a `filter`ed ancestor. Kept light: the dialog is already glass, and a heavy
   scrim behind translucent glass turns it grey. */
.lgdlg::backdrop {
  background: rgba(0, 0, 0, 0.32);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.lgdlg[open] { animation: lgdlg-in var(--lg-dur-fast) var(--lg-ease-snappy); }
.lgdlg[open]::backdrop { animation: lgdlg-fade var(--lg-dur-fast) linear; }
@keyframes lgdlg-in { from { opacity: 0; transform: translateY(8px) scale(0.97); } }
@keyframes lgdlg-fade { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .lgdlg[open], .lgdlg[open]::backdrop { animation: none; }
}

/* -------------------------------------------------------------------- menu */
/* Fixed and portalled to <body> by Popover — a `filter` on any ancestor makes
   it the containing block for fixed descendants, and in a glass system that
   ancestor is usually the thing you anchored to. */
.lgmenu {
  position: fixed;
  z-index: 200;
  min-width: 176px;
  max-width: min(320px, calc(100vw - 24px));
  max-height: min(70vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  padding: 5px;
}
.lgmenu[hidden] { display: none; }
.lgmenu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--lg-ink);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.lgmenu-item:hover:not([aria-disabled='true']) { background: var(--lg-fill); }
/* `:focus-visible` is wrong here. Arrow keys move REAL focus between rows, and
   a row you arrowed onto with no visible highlight is a menu you are driving
   blind — the highlight IS the cursor. */
.lgmenu-item:focus { outline: none; background: var(--lg-fill); }
.lgmenu-item[aria-disabled='true'] { opacity: 0.4; cursor: not-allowed; }
.lgmenu-item[data-danger='true'] { color: var(--lg-danger); }
.lgmenu-item[aria-checked='true']::before { content: '✓'; margin-left: -4px; width: 14px; }
.lgmenu-item[aria-checked='false']::before { content: ''; width: 14px; }
.lgmenu-icon { display: grid; place-items: center; width: 16px; font-style: normal; }
.lgmenu-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }
.lgmenu-kbd {
  font: inherit;
  font-size: 11.5px;
  color: var(--lg-ink-faint);
  letter-spacing: 0.04em;
}
.lgmenu-sep { height: 1px; margin: 5px 6px; background: var(--lg-hairline); }
.lgmenu-header {
  padding: 7px 10px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lg-ink-faint);
}

/* ----------------------------------------------------------------- tooltip */
.lgtip {
  position: fixed;
  z-index: 300;
  max-width: 260px;
  padding: 6px 10px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--lg-ink);
  /* Never a hit target. A tooltip that swallows the click aimed at the control
     underneath it is worse than no tooltip — but `pointer-events: none` cannot
     go here, because WCAG 1.4.13 requires the pointer to be able to REACH it.
     So it is hoverable and simply has nothing clickable inside. */
  user-select: none;
}
.lgtip[hidden] { display: none; }

/* ------------------------------------------------------------------ toasts */
.lgtoasts {
  position: fixed;
  z-index: 400;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* The container spans the edge but must not eat clicks across it; the toasts
     themselves opt back in. */
  pointer-events: none;
  padding: 18px;
  width: min(400px, calc(100vw - 24px));
}
.lgtoasts[data-position^='top'] { top: 0; }
/* The stack grows AWAY from its edge, so `prepend` puts the newest nearest the
   edge in both directions and the JS never has to know which way it is pinned. */
.lgtoasts[data-position^='bottom'] { bottom: 0; flex-direction: column-reverse; }
.lgtoasts[data-position$='left'] { left: 0; align-items: flex-start; }
.lgtoasts[data-position$='right'] { right: 0; align-items: flex-end; }
.lgtoasts[data-position$='center'] { left: 50%; transform: translateX(-50%); align-items: center; }

.lgtoast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 14px;
  color: var(--lg-ink);
  touch-action: pan-y;
  opacity: 0;
  transform: translateY(10px) scale(0.97);
  transition: opacity var(--lg-dur-fast) var(--lg-ease-snappy),
              transform var(--lg-dur-fast) var(--lg-ease-snappy);
}
.lgtoast[data-in='true'] { opacity: 1; transform: none; }
/* No transition while a finger is on it, or the toast lags behind the drag. */
.lgtoast[data-dragging='true'] { transition: none; cursor: grabbing; }
.lgtoast-body { flex: 1 1 auto; min-width: 0; }
.lgtoast-title { font-size: 13.5px; font-weight: 620; letter-spacing: -0.005em; }
.lgtoast-desc { margin-top: 2px; font-size: 12.5px; line-height: 1.45; color: var(--lg-ink-dim); }
.lgtoast[data-tone='success'] .lgtoast-title::before { content: '✓ '; }
.lgtoast[data-tone='danger'] .lgtoast-title { color: var(--lg-danger); }
.lgtoast-action {
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  padding: 7px 13px;
  background: var(--lg-fill-strong);
  color: var(--lg-ink);
  font: inherit;
  font-size: 12.5px;
  font-weight: 640;
  cursor: pointer;
}
.lgtoast-close {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--lg-ink-faint);
  font-size: 11px;
  cursor: pointer;
}
.lgtoast-close:hover { background: var(--lg-fill); color: var(--lg-ink); }
.lgtoast-action:focus-visible,
.lgtoast-close:focus-visible { outline: 2px solid var(--lg-ink); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .lgtoast { transition: opacity 120ms linear; transform: none; }
  .lgtoast[data-in='true'] { transform: none; }
}

/* ------------------------------------------------------------------- morph */
/* The flying lens. Portalled to <body> and fixed, for the same reason Popover
   is: any ancestor with a `filter` becomes the containing block for fixed
   descendants, and in a glass system the thing you are morphing FROM is
   usually made of glass.

   No transition on anything here — every value is written per frame by the rAF
   loop, and a CSS transition would fight the curve. */
.lgmorph {
  position: fixed;
  z-index: 500;
  pointer-events: none;
}
.lgmorph[hidden] { display: none; }
/* Top-layer flights (Dialog). The UA stylesheet gives every `[popover]` a
   border, padding, a background and `inset: 0; margin: auto; width: fit-content`
   — all of which would fight the left/top/width/height JS writes per frame, and
   the background would sit in front of the lens it is supposed to be. */
.lgmorph[popover] {
  inset: auto;
  margin: 0;
  border: 0;
  padding: 0;
  background: none;
  width: auto;
  height: auto;
  overflow: visible;
  color: inherit;
}

/* Segmented — a recessed track with a raised glass thumb.
   The inverse of the TabBar: there, the bar is the surface and the indicator is
   a tinted fill; here the track is a groove and the THUMB is the lens. Both
   match what Apple ships, and the rule behind both is the same — the raised
   thing catches the light. */
.lgseg {
  position: relative;
  display: flex;
  align-items: stretch;
  padding: 3px;
  border-radius: 12px;
  background: var(--lg-groove);
  /* No `isolation: isolate` here, unlike `.lgtb`. The thumb is a real lens and
     an isolated group would become its backdrop root — it would refract the
     track's own tint and nothing else, so a control sitting on a photograph
     would show a flat grey pill instead of the photograph bent. */
}
.lgseg > .lgseg-ind {
  position: absolute;
  left: 3px;
  top: 3px;
  bottom: 3px;
  z-index: 0;
  pointer-events: none;
  /* `width` is animated per frame from JS, so the lens is rebuilt per frame —
     which is normally forbidden. It is affordable only because the thumb is
     small: ~100x28 is under 3k map pixels, against the 16k a Morph runs at
     reduced quality. See COMPONENT-LIBRARY §5 Q4. */
  will-change: transform, width;
}
.lgseg-opt {
  position: relative;
  z-index: 1;
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 7px 12px;
  border-radius: 10px;
  cursor: pointer;
  color: var(--lg-ink-dim);
  font: inherit;
  font-size: 13px;
  font-weight: 590;
  letter-spacing: -0.01em;
  white-space: nowrap;
  user-select: none;
}
.lgseg-input {
  /* Visually gone, still focusable, still a real radio. `display: none` and
     `visibility: hidden` each take it out of the tab order AND the
     accessibility tree — which is the entire reason for using real radios. */
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
.lgseg-text { display: inline-flex; align-items: center; gap: 6px; overflow: hidden; text-overflow: ellipsis; }
.lgseg-opt:where(:has(:checked)) { color: var(--lg-ink); }
.lgseg-opt:where(:has(:focus-visible)) { outline: 2px solid var(--lg-ink); outline-offset: -2px; }
.lgseg-opt:where(:has(:disabled)) { opacity: 0.5; cursor: default; }
