/* Product face. Debug families stay off this sheet (loaded with the dock). */
@font-face {
  font-family: "Iosevka Aile";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("../fonts/iosevka-aile/iosevka-aile-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Iosevka Aile";
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url("../fonts/iosevka-aile/iosevka-aile-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Source Code Pro";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("../fonts/source-code-pro/source-code-pro-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Source Code Pro";
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url("../fonts/source-code-pro/source-code-pro-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("../fonts/manrope/manrope-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url("../fonts/manrope/manrope-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 600;
  font-display: block;
  src: url("../fonts/manrope/manrope-latin-600-normal.woff2") format("woff2");
}

:root {
  /* Ultra-dark is the product default. Gray only when html loses data-ultra.
     Keeping :root black avoids the gray FOUC while app.css is still parsing
     (critical html vars lose to :root specificity until the ultra rule). */
  --bg: #000000;
  --paper: #000000;
  --ink: #f2f2f2;
  --muted: #a8a8a8;
  --line: #333333;
  --accent: #c45c5c; /* errors / destructive only */
  --chrome: #b8b8b8; /* inactive UI chrome — never competes with score colors */
  /* One OKLCH ramp: 0% red, 25% orange, 50% yellow, 100% green.
     Score % interpolates these in js/main.js (SCORE_RAMP). Trust marks
     use the red / yellow / green stops. */
  --ramp-red: oklch(58% 0.170 27);
  --ramp-orange: oklch(70% 0.160 64);
  --ramp-yellow: oklch(84% 0.150 102);
  --ramp-green: oklch(72% 0.150 150);
  --good: var(--ramp-green);
  --warn: var(--ramp-yellow);
  --bad: var(--ramp-red);
  --chip: #0a0a0a;
  --surface-1: #000000;
  --surface-2: #000000;
  /* Product face is Manrope. Iosevka stays in the stack for glyphs Manrope lacks. */
  --font-sans: "Manrope", "Noto Sans", "Segoe UI", system-ui, sans-serif;
  --font-mono: "Manrope", "Iosevka Aile", "Noto Sans", "Segoe UI", system-ui, sans-serif;
  --font-ui: var(--font-sans);
  /* Lautschrift. Defaults to the product face; the debug dock can swap it. */
  --font-ipa: var(--font-mono);
  /* 1 = the old 100% face. 1.1 is that face at the old 110% slider — first visit. */
  --ui-text-scale: 1.1;
  /* Landing wordmark only. Independent of --ui-text-scale / other chrome.
     Defaults baked from Layout-Dock (0.85 scale, 0rem y). */
  --logo-scale: 0.85;
  --logo-nudge-y: 0rem;
  /* Gap below the gear before the floating panel; floor keeps iOS home-indicator. */
  --settings-panel-gap: 0.85rem;
  --settings-panel-floor: calc(env(safe-area-inset-bottom, 0px) + 0.7rem);
  /* Hairline further down from the input/results boundary (rem). */
  --divider-offset: 0.5rem;
  /* Extra air below the hairline (rem; does not move the rule). */
  --input-results-gap: 0.5rem;
  /* The field is one thin rule (the only Eingabestil, docs/ui-layout.md §4d).
     --rule-gap = the air on BOTH sides of it: IPA baseline of the input info
     block → rule, and rule → cap height of the first result word. */
  --rule-gap: 0.625rem;
  /* .results top → cap height of the first result word: panel + row air
     plus Manrope's line-box-top → cap distance at line-height 1.2
     (measured, ui-geometry-states checks the result ±1 px). */
  --row-cap-inset: calc(
    2 * var(--rhyme-air-eff) + 0.14rem + var(--row-cap-k) * 1.1rem * var(--ui-text-scale, 1.1)
  );
  --row-cap-k: 0.112;
  --field-rule: color-mix(in srgb, var(--ink) 28%, transparent);
  /* Focus/typing: the same neutral rule, a little brighter — never the
     accent green (green means "clean match" everywhere else). */
  --field-rule-hover: color-mix(in srgb, var(--ink) 38%, transparent);
  --field-rule-focus: color-mix(in srgb, var(--ink) 55%, transparent);
  --score-col: max(4.35rem, 44px);
  /* Minimum touch target of the % hit; also the floor of a result row, so
     loading placeholders (.rhyme.is-ghost) never come out shorter. */
  --hit-min-h: 44px;
  /* Trust frame: hairline around syl group + IPA */
  --trust-frame: 38%;
  --trust-outline: 1px;
  --trust-outline-offset: 2px;
  /* How far the colored frame sits outside the analysis content box */
  --trust-outset: calc(var(--trust-outline) + var(--trust-outline-offset));
  /* Text inset of the shared column (rows, ghosts, states, input). */
  --content-inline: 0.85rem;
  /* Stroke of the input frame (.composer). The input pads that much less, so
     typed text and result words start on the same pixel. */
  --field-border: 1px;
  /* Input text pad inside the frame; JS mirrors it to px (syncInputFrameScale). */
  --pad-left: calc(var(--content-inline) - var(--field-border));
  /* Gap between E-014 Auftakt and match span (hairline sits in the middle). */
  --auftakt-gap: 0px;
}

html[data-auftakt-gap="1"] {
  --auftakt-gap: 0.32rem;
}

:root {
  /* Centered content column: phone ≈ full width; larger screens capped. */
  --content-side: max(0.85rem, 2.5vmin);
  --content-col-cap: 24rem;
  --content-col-width: min(100%, var(--content-col-cap));
  /* Settings hangs outside the field. First paint must already reserve it —
     `.page.is-hero .workspace { max-width: 100% }` used to beat this and the
     box painted wide, then JS shrank it (Impressum back). */
  /* Input first-line metrics (chrome align + flex underlay pull).
     Equal air above the word and below the IPA — the lang-tag lane is gone. */
  --composer-pad-y: 0.42rem;
  --composer-pad-top: var(--composer-pad-y);
  /* Below the IPA: the rule gap (see --rule-gap), not the top pad. The IPA
     line ends ~0.27 em (of its font) under the baseline; on the hero that
     tail grows with --input-scale, so the pad gives the extra back and the
     baseline → rule air stays --rule-gap (ui-geometry-states, ±1 px). */
  --composer-pad-bot: calc(
    var(--rule-gap) - 0.178rem * var(--ui-text-scale, 1.1) * (var(--input-scale, 1) - 1)
  );
  /* Eingabestil "Strich" draws the idle hero field larger (--input-scale,
     see "Eingabestil" below). Everything inside .composer reads its type
     scale from --input-ui-scale; rows and the rest of the page do not. */
  --input-scale: 1;
  --input-ui-scale: calc(var(--ui-text-scale, 1.1) * var(--input-scale, 1));
  --composer-line: calc(1.1rem * var(--input-ui-scale) * 1.2);
  --input-text-band: calc(1.1rem * var(--input-ui-scale) * 1.2);
  /* Shelf under the orthography: brace + IPA. Match js/input-ce analysisBandPx
     so first paint and the post-boot pin share one height (no field shrink). */
  --input-analysis-band: calc(
    var(--brace-air-eff, 0.06rem) + var(--brace-ipa-air-eff, 0.06rem) +
      var(--brace-slot, 1.6px) +
      (0.66rem * var(--input-ui-scale) * 1.35)
  );
  --input-text-block: var(--composer-line);
  /* Multi-reading stacked mark rows */
  --reading-stack-gap: 0.18rem;
  /* Debug dock open height (vh); default ≈ ⅓ viewport. */
  --debug-dock-height: 33.333vh;
  --chrome-right: 0.5rem;
  /* Larger press target; glyph size shared by search (inside) and settings (outside). */
  --chrome-btn: 2.45rem;
  --chrome-icon: 1.85rem;
  --chrome-gap: 0.1rem;
  /* Search icon only inside the box; live width from JS (--chrome-cluster). */
  --chrome-cluster: 2.85rem;
  --chrome-settings-gap: 0.28rem;
  /* Air past the outer settings icon so it never kisses the screen edge. */
  --chrome-settings-safe: 0.4rem;
  --workspace-settings-max: min(
    100%,
    calc(
      100% - 2 * max(
        0px,
        var(--chrome-btn) + var(--chrome-settings-gap) + var(--chrome-settings-safe) -
          var(--content-side) - env(safe-area-inset-right, 0px)
      )
    )
  );
  --analysis-pair-gap: 0.72rem;
  /* Clear air between stacked trust outlines */
  --analysis-stack-gap: calc(var(--trust-outset) * 2 + 0.45rem);
  /* Horizontal air between trust outlines (outsets + a sliver of clear) */
  --analysis-word-gap: calc(var(--trust-outset) * 2 + 0.4rem);
  /* One vertical step between rhyme/result blocks */
  --rhyme-air: 0.28rem;
  /* Within a block: word→bars a bit tighter; bars→IPA closer still */
  /* Target visual air (overridden in px by font-ink.js per face). */
  --brace-air: 0.06rem;
  --brace-ipa-air: 0.06rem;
  /* Debug ±rem nudges (0 = leave product defaults). */
  --brace-air-nudge: 0rem;
  --brace-ipa-air-nudge: 0rem;
  --rhyme-air-nudge: 0rem;
  --brace-air-eff: calc(var(--brace-air) + var(--brace-air-nudge));
  --brace-ipa-air-eff: calc(var(--brace-ipa-air) + var(--brace-ipa-air-nudge));
  --rhyme-air-eff: calc(var(--rhyme-air) + var(--rhyme-air-nudge));
  --ortho-ink-below: 0px;
  --ipa-ink-above: 0px;
}

/* Ultra Dark early — same vars as the opus block below. */
html[data-ultra="1"],
html[data-ultra="1"][data-ui="opus"] {
  --bg: #000000;
  --paper: #000000;
  --surface-1: #000000;
  --surface-2: #000000;
  --chip: #0a0a0a;
}

/* Explicit off: restore the flat gray paper face. */
html:not([data-ultra]) {
  --bg: #181818;
  --paper: #181818;
  --surface-1: #1e1e1e;
  --surface-2: #242424;
  --chip: #2a2a2a;
}

* { box-sizing: border-box; }

html {
  /*
    Root rem stays 16px. --ui-text-scale multiplies rhyme and search type
    only. Scaling the root also grew the layout dock and the settings icons.
    Zoom on .page stays off: it broke composer hit-testing.
  */
  font-size: 16px;
}

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  /* Manrope = UI Latin; Noto Sans = IPA / rare glyphs. */
  font-family: var(--font-ui);
  line-height: 1.45;
  direction: ltr;
  color-scheme: dark;
  font-feature-settings: "kern" 1, "liga" 1;
}

body {
  font-size: 1rem;
}

.page {
  max-width: none;
  width: 100%;
  min-height: 100dvh;
  margin: 0 auto;
  /* Tight top once results are up — idle centering uses .workspace pad */
  /* No shared max-prefix gutter (0.10.187). Safe pad is only the *deficit*
     when per-row hang would clip past the viewport (iPhone). JS remeasures
     after the pad, because a centered column only shifts partway. */
  --auftakt-safe-pad: 0px;
  padding-top: 0.65rem;
  padding-bottom: 1.4rem;
  padding-left: calc(
    var(--content-side) + env(safe-area-inset-left, 0px) + var(--auftakt-safe-pad)
  );
  padding-right: calc(var(--content-side) + env(safe-area-inset-right, 0px));
  display: flex;
  flex-direction: column;
  /* Text scale is --ui-text-scale on search/rhyme type. Root rem stays 16px. */
  box-sizing: border-box;
}

.page.is-hero {
  --hero-page-pad: 1.2rem;
  padding-top: var(--hero-page-pad);
}

/* Results: field sits at the top. iOS blends web content into the status-bar
   black; a 1px #333 stroke in that zone always looks like a fade (0.334–0.338).
   Sit clearly below the blend — extra air, not a mask/glow/clip. */
.page:not(.is-hero) {
  padding-top: calc(0.65rem + env(safe-area-inset-top, 0px));
}

@media (max-width: 900px) {
  .page:not(.is-hero) {
    padding-top: calc(env(safe-area-inset-top, 0px) + 2.25rem);
  }
}

/* Exact final wordmark PNG above the idle field. Rides with the search
   shell (absolute above it) so hero padding ease slides it both ways —
   no display:none snap. Soft fade keeps the exit/entry smooth. */
.site-wordmark-wrap {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translate(-50%, var(--logo-nudge-y, 0rem));
  width: calc(min(72vw, 22rem) * var(--logo-scale, 1));
  max-width: 100%;
  margin: 0;
  pointer-events: none;
  user-select: none;
  /* PNG ink sits mid-canvas; pull wrap down so bars sit closer to the
     field with a bit more air above, not a half-viewport empty square. */
  margin-bottom: calc(min(72vw, 22rem) * var(--logo-scale, 1) * -0.26);
  opacity: 1;
  transition: opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

.site-wordmark {
  display: block;
  width: 100%;
  height: auto;
}

/* Start sequence (index.html head script, docs/ui-layout.md): logo and
   field wait invisible in their final place (no layout change), then rise
   in together - logo first, field 100 ms later - opacity + 8px, 0.34 s.
   .is-shown = logo decoded; a logo that is late (slow network) fades in
   on its own once it is. Only opacity / translate animate. */
html[data-landing="pending"] .site-wordmark-wrap,
html[data-landing] .site-wordmark-wrap:not(.is-shown) {
  opacity: 0;
  translate: 0 8px;
  visibility: hidden;
  pointer-events: none;
  transition: none;
}

html[data-landing="intro"] .page.is-hero .site-wordmark-wrap.is-shown,
html[data-landing="done"] .page.is-hero .site-wordmark-wrap.is-shown {
  opacity: 1;
  translate: 0 0;
  visibility: visible;
  transition:
    opacity 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    translate 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}

html[data-landing="pending"] .composer-row {
  opacity: 0;
  translate: 0 8px;
}

html[data-landing="intro"] .composer-row {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity 0.34s cubic-bezier(0.22, 1, 0.36, 1) 0.1s,
    translate 0.34s cubic-bezier(0.22, 1, 0.36, 1) 0.1s;
}

html[data-landing] .composer::after {
  content: none;
  display: none;
  opacity: 0;
  animation: none;
}

/* Stay in the tree while the field rises — fade as it leaves the viewport.
   visibility:hidden after the hero so an opacity-0 PNG cannot composite
   a soft band over the field / status-bar. */
.page:not(.is-hero) .site-wordmark-wrap {
  opacity: 0;
  visibility: hidden;
}

.ver {
  flex-shrink: 0;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  letter-spacing: 0.04em;
}

.site-foot {
  width: var(--content-col-width);
  max-width: 100%;
  margin-top: auto;
  margin-inline: auto;
  padding-top: 1.1rem;
  padding-bottom: max(0.55rem, env(safe-area-inset-bottom, 0px));
  border-top: 0;
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 1.1rem;
  color: color-mix(in srgb, var(--muted) 26%, transparent);
  min-height: 0;
  flex-shrink: 0;
  box-sizing: border-box;
}

/* Idle: a bit more air above the whisper link */
.page.is-hero .site-foot {
  padding-top: 2.2rem;
}

.legal-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 0;
}

.legal-link:focus-visible {
  color: color-mix(in srgb, var(--muted) 58%, transparent);
}
@media (hover: hover) {
  .legal-link:hover {
    color: color-mix(in srgb, var(--muted) 58%, transparent);
  }
}

/* In-app impressum. Same document, so the lexicon stays in memory. */
.legal-view {
  display: none;
  width: min(36rem, calc(100% - 2.4rem));
  margin: 0 auto;
  padding: 2.4rem 0 3.2rem;
  box-sizing: border-box;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 1rem;
  line-height: 1.5;
}

html[data-view="impressum"] .page,
html[data-view="impressum"] #debug-dock {
  display: none !important;
}

html[data-view="impressum"] .legal-view {
  display: block;
}

.legal-view a { color: inherit; }

.legal-view .back {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.82rem;
}

.legal-view .back:focus-visible {
  color: var(--ink);
}
@media (hover: hover) {
  .legal-view .back:hover {
    color: var(--ink);
  }
}

.legal-view h1 {
  margin: 1.6rem 0 0.2rem;
  font-size: 1.45rem;
  font-weight: 500;
  letter-spacing: -0.02em;
}

.legal-view .lead {
  margin: 0 0 1.6rem;
  color: var(--muted);
  font-size: 0.92rem;
}

.legal-view h2 {
  margin: 1.8rem 0 0.55rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.legal-view address { font-style: normal; }
.legal-view p { margin: 0.4rem 0; }

.legal-view .contact {
  display: grid;
  gap: 0.15rem;
}

.legal-view ul {
  margin: 0.2rem 0 0;
  padding: 0;
  list-style: none;
}

.legal-view li {
  margin: 0;
  padding: 0.7rem 0;
  border-top: 1px solid var(--line);
}

.legal-view li strong { font-weight: 500; }

.legal-view .lic {
  display: block;
  margin-top: 0.15rem;
  color: var(--muted);
  font-size: 0.92rem;
}

.composer-row {
  position: relative;
  display: block;
  width: 100%;
}

/* Settings hangs outside the field to the right. The composer stays
   viewport-centered; this cluster overhangs into the gutter.
   JS caps --content-col-width so icon + gap + --chrome-settings-safe
   never leave the viewport (centered column → 2× deficit past padR). */
.settings-chrome {
  position: absolute;
  z-index: 30;
  left: 100%;
  top: var(
    --chrome-top,
    calc(
      var(--pad-top, var(--composer-pad-top, 0.55rem)) +
        (
          var(--input-text-band, var(--composer-line, 1.1rem * 1.2)) +
            var(--input-analysis-band, 1.6rem) - var(--chrome-btn)
        ) / 2
    )
  );
  height: var(--chrome-btn);
  margin-left: var(--chrome-settings-gap, 0.28rem);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: var(--chrome-gap);
  pointer-events: auto;
}

.settings-chrome > * {
  pointer-events: auto;
}

/*
  Phones (≤ 480px): the settings icon moves INTO the field, left of the
  search icon. Hanging it outside cost the column ~2 × 50 px (69–73 % of the
  screen; words broke mid-word at 320 px). Now the column spans the screen
  minus a 12 px gutter per side. JS reads --settings-inside (no overhang
  reserve in contentColMetrics); the search icon shifts left by one button
  so the measured --chrome-cluster pads the text for both icons.
*/
@media (max-width: 480px) {
  :root {
    --settings-inside: 1;
    --content-side: 0.75rem;
    --workspace-settings-max: 100%;
    --score-col: 3.2rem;
  }
  .settings-chrome {
    left: auto;
    right: var(--chrome-right);
    margin-left: 0;
  }
  /* .composer prefix: the base .input-chrome rule comes later in this file. */
  .composer .input-chrome {
    right: calc(var(--chrome-right) + var(--chrome-btn) + var(--chrome-gap));
  }
}

.workspace {
  width: var(--content-col-width);
  /* Outer settings icon is the right bound — not the box alone.
     Same formula as --workspace-settings-max (critical CSS + hero). */
  max-width: var(--workspace-settings-max);
  margin-inline: auto;
  box-sizing: border-box;
  border: 0;
  border-radius: 0;
  background: transparent;
  /* visible: E-014 Auftakt hang uses negative margin into .page gutter */
  overflow: visible;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-height: 0;
  /* Idle: float input toward mid-viewport (thumb / mouse reach) */
  padding-top: 0.15rem;
  /* Width never transitions by default — subpixel L/R jitter on typing.
     Random-word calm path enables transition via html.is-col-animating. */
  transition: padding-top 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

html.is-col-animating .workspace,
html.is-col-animating .site-foot {
  transition:
    width 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    padding-top 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

/* While the column eases, keep old ink from wrapping/jittering horizontally. */
html.is-col-animating .composer,
html.is-col-animating .input-host,
html.is-col-animating #q {
  overflow-x: hidden;
}

/* Random-word swap: hide input + results until one width ease finishes. */
html.is-random-swap #q,
html.is-random-swap #q-ghost,
html.is-random-swap #q-placeholder,
html.is-random-swap #units,
html.is-random-swap #results {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: none !important;
}

.page.is-hero .workspace {
  /* Must win over critical CSS max-width: 100% (higher specificity). */
  max-width: var(--workspace-settings-max);
  /* Field centre sits at --hero-field-mid of the locked layout height
     (--hero-vvh, measured without keyboard) — not 50dvh, which shrinks with
     the soft keyboard and slides logo+field on every focus/blur. */
  padding-top: max(
    0px,
    calc(
      var(--hero-field-mid, calc(var(--hero-vvh, 100svh) * 0.5))
      - var(--hero-page-pad, 1.2rem)
      - var(--composer-live-h,
          calc(
            var(--pad-top, var(--composer-pad-top, 0.42rem))
            + var(--input-text-band, var(--composer-line, calc(1.1rem * 1.2)))
            + var(--input-analysis-band, 1.6rem)
            + var(--composer-pad-bot, 0.42rem)
          )
        ) / 2
    )
  );
}

/* Touch (soft keyboard): iOS WebKit centres the focused field in the area
   above the keyboard and pans the page to get it there. Sit the idle field
   where that centre already is — (layout height − keyboard) / 2 — so the
   keyboard opens without any page movement on a typical iPhone. Never above
   the logo's room, never below the old mid. Mirror in index.html critical CSS. */
@media (hover: none) and (pointer: coarse) {
  .page.is-hero {
    /* Soft keyboard incl. accessory bar, in layout px (iPhone ≈ 300–310). */
    --hero-kb-reserve: 19rem;
    /* Logo ink above the field top (wrap is mostly transparent) + air. */
    --hero-logo-room: calc(
      env(safe-area-inset-top, 0px) + 1.25rem +
        min(72vw, 22rem) * var(--logo-scale, 1) * 0.38
    );
    --hero-field-mid: min(
      var(--hero-vvh, 100svh) * 0.5,
      max(
        var(--hero-logo-room) + var(--composer-box-h, 3.6rem) / 2,
        (var(--hero-vvh, 100svh) - var(--hero-kb-reserve)) / 2
      )
    );
  }
}

/* Soft-keyboard focus in the idle hero: the field already sits above the
   keyboard (see above). Lock document scroll/overscroll so a drag cannot move
   the hero while the keyboard is up. No JS transform or scroll counter: if
   WebKit still has to pan (small phone, landscape), that is its own single
   smooth move with the keyboard — fighting it caused the down/up jump. */
html.is-hero-keyboard,
html.is-hero-keyboard body {
  overflow: hidden !important;
  overscroll-behavior: none;
  touch-action: none;
}

html.is-hero-keyboard .page {
  min-height: var(--hero-vvh, 100svh);
  transition: none !important;
}

html.is-hero-keyboard .page.is-hero .workspace {
  transition: none !important;
}

/* Input + optional toolbar + input-analysis.
   Above the rhyme list so the Lesarten menu (it hangs out of the field)
   stays the hit target. Opaque plate — never a mask/gradient into the
   status-bar black (0.334). */
.query-shell {
  position: relative;
  z-index: 2;
  background: #000;
  overflow: visible;
  /* No compositing layer — iOS fades sticky/filtered plates into the status bar. */
  transform: none;
  -webkit-transform: none;
  will-change: auto;
  isolation: auto;
  mix-blend-mode: normal;
  filter: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
  -webkit-box-shadow: none;
}

.composer {
  position: relative;
  z-index: 2;
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  /* One Informationsblock, centered: the same air above the word and below IPA. */
  --ui-text-scale: var(--input-ui-scale);
  /* Re-resolve with the field scale, so the hero IPA grows with the word
     (--fs-ipa on :root only knows the list scale). */
  --fs-ipa: calc(0.66rem * var(--ui-text-scale, 1.1));
  --composer-line: calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2);
  height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
  min-height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
  /* One thin rule under the text. The frame keeps its 1px stroke
     (transparent on three sides), so every metric — text edge, row frame,
     hero centre, keyboard position — stays on the same pixel. */
  border: var(--field-border) solid transparent;
  border-bottom-color: var(--field-rule);
  border-radius: 0;
  outline: none;
  background: #000;
  /* border-box: padding-box left a 1px hairline that iOS AA'd into a fade */
  background-clip: border-box;
  -webkit-background-clip: border-box;
  box-shadow: none;
  -webkit-box-shadow: none;
  filter: none;
  /* 0.337 clipped the 1px top stroke into an iOS fade — keep this visible */
  overflow: visible;
  -webkit-appearance: none;
  appearance: none;
  transform: none;
  -webkit-transform: none;
  will-change: auto;
  /* Height snaps — avoids clipping the previous line while growing downward.
     Only the rule colour eases. */
  transition: border-bottom-color 0.16s ease-out;
}

/* No overlay stroke. Search progress is the SVG, not a fade layer. */
.composer::after,
.composer.is-focused::after {
  content: none !important;
  display: none !important;
  box-shadow: none !important;
  background: none !important;
  opacity: 0 !important;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}

/* Search progress: a light sweeps along the rule. */
.composer-progress {
  position: absolute;
  top: auto;
  left: 0;
  right: 0;
  width: auto;
  bottom: calc(-1 * var(--field-border, 1px));
  z-index: 5;
  height: var(--field-border, 1px);
  pointer-events: none;
}

html.is-searching .composer-progress,
html.is-phrase-searching .composer-progress {
  background: linear-gradient(#e0e0e0, #e0e0e0) no-repeat;
  background-size: 22% 100%;
  animation: composer-line-sweep 1.25s linear infinite;
}

@keyframes composer-line-sweep {
  from { background-position: -30% 0; }
  to { background-position: 130% 0; }
}

@media (hover: hover) {
  .composer:not(.is-focused):hover {
    border-bottom-color: var(--field-rule-hover);
  }
}

.composer.is-focused {
  border-bottom-color: var(--field-rule-focus);
}

/* The idle hero draws the field larger (--input-scale; index.html mirrors
   it for the first paint). After the first search the rule is the divider
   between field and list. */
html:has(.page.is-hero) {
  --input-scale: 1.24;
}

.composer.is-loading {
  /* Same box as the ready field, so the bar and the ghost share one block. */
  height: calc(
    var(--pad-top, var(--composer-pad-top, 0.55rem)) +
      var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
  min-height: calc(
    var(--pad-top, var(--composer-pad-top, 0.55rem)) +
      var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
}

.composer.is-loading .lex-load {
  position: absolute;
  inset: 0;
  overflow: visible;
  justify-content: flex-start;
}

.composer.is-error {
  height: auto;
  min-height: 9.25rem;
  box-shadow: none;
}

.composer.is-ready {
  /* Never collapse: absolute CE/host do not stretch auto height.
     One info block even when the ghost is the only thing in the field. */
  height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
  min-height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line)) +
      var(--input-analysis-band) + var(--composer-pad-bot, 0.28rem)
  );
  background: #000;
  overflow: visible;
  background-clip: border-box;
  -webkit-background-clip: border-box;
  transform: none;
  will-change: auto;
}

.input-host {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-height: none;
  overflow: visible;
  opacity: 1;
  pointer-events: none;
  background: #000;
  box-shadow: none;
  -webkit-box-shadow: none;
  -webkit-appearance: none;
  appearance: none;
  transform: none;
  will-change: auto;
  filter: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.input-host[hidden] {
  display: none;
}

.input-host.is-entering {
  animation: none;
}

.q-form {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  pointer-events: none;
}

/* Single-ink: contenteditable is the only glyph layer. */
.q-ce {
  position: absolute;
  inset: 0;
  z-index: 1;
  box-sizing: border-box;
  margin: 0;
  border: 0;
  outline: none;
  background: #000;
  box-shadow: none;
  -webkit-box-shadow: none;
  -webkit-appearance: none;
  appearance: none;
  transform: none;
  will-change: auto;
  filter: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: var(--ink);
  caret-color: transparent;
  /* Avoid font-shorthand unitless line-height — it can collapse composer math. */
  font-family: var(--font-mono);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  line-height: var(--input-text-band, calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2));
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
  padding: var(--pad-top, var(--composer-pad-top, 0.55rem))
    var(--chrome-cluster, 2.85rem) 0 var(--pad-left);
  white-space: pre-wrap;
  overflow-wrap: normal;
  word-break: keep-all;
  overflow: hidden;
  pointer-events: auto;
  cursor: text;
  -webkit-user-modify: read-write-plaintext-only;
}

.q-ce:focus,
.q-ce:focus-visible {
  outline: none;
}

.q-ce::selection {
  background: transparent;
  color: var(--ink);
}

/* Decorations sit above CE only for mark hit-targets (pe none elsewhere). */
.q-decor.units {
  position: absolute;
  inset: 0;
  z-index: 2;
  margin: 0;
  padding: 0;
  pointer-events: none;
  overflow: visible;
}

.decor-word {
  position: absolute;
  left: 0;
  top: 0;
  box-sizing: border-box;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.decor-word .units-readings,
.decor-word .unit-choice.is-solo,
.decor-word .analysis-stack {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

/* Syllable cells: flex row sized to CE advances in JS (same brace→IPA as results). */
.decor-word .syl-track,
.decor-word .syl-word {
  position: relative;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-start;
  width: 100%;
  min-height: 0;
}

.decor-word .syl-cell {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-width: 0;
}

/* IPA hangs under the CE-sized dash. Never grow the cell to the IPA;
   ink is condensed in JS (scale/size), not wrapped to a second line. */
.decor-word .syl-ipa {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  white-space: nowrap;
}

.q-sel-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}

.q-sel-layer > i {
  position: absolute;
  display: block;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 1px;
  background: color-mix(in srgb, #6ea8fe 45%, transparent);
  pointer-events: none;
}

.q-ghost {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: auto;
  z-index: 3;
  box-sizing: border-box;
  padding: var(--pad-top, var(--composer-pad-top, 0.55rem))
    var(--chrome-cluster, 2.85rem) 0 var(--pad-left);
  pointer-events: none;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  line-height: var(--input-text-band, calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2));
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
  white-space: normal;
  overflow: visible;
  text-align: left;
  /* Class-driven visibility — NOT [hidden], which used display:none!important
     and silently killed the finished typewriter. */
  display: none;
}

.q-ghost.is-live {
  display: block;
  visibility: visible;
}

/* Opener ghost: one info block per word, wrapping inside the field. */
.q-ghost-line {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  width: max-content;
  max-width: 100%;
  gap: 0;
}
.q-ghost-line > .analysis-stack {
  flex: 0 0 auto;
  width: max-content;
  max-width: 100%;
}
.q-ghost-gap,
.q-ghost-tail {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  line-height: 1.2;
  white-space: pre;
  color: var(--ink);
}

/* Brand ghost uses the logo's ink, not trust green. Stress is brightness:
   first syllable white, the other two grey — braces and IPA together. */
.q-ghost .q-ghost-brand {
  width: max-content;
  max-width: 100%;
}
.q-ghost .q-ghost-brand .syl-ortho,
.q-ghost .q-ghost-brand .syl-cell.is-stress .syl-ipa {
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
}
.q-ghost .q-ghost-brand .syl-cell:not(.is-stress) .syl-ipa {
  color: color-mix(in srgb, var(--ink) 46%, transparent);
  -webkit-text-fill-color: color-mix(in srgb, var(--ink) 46%, transparent);
}
.q-ghost .q-ghost-brand .syl-brace,
html[data-ui="opus"] .q-ghost .q-ghost-brand .syl-brace,
html[data-trust-wash="0"][data-ui="opus"] .q-ghost .q-ghost-brand .syl-brace {
  --brace-color: var(--ink);
  opacity: 0.42;
}
.q-ghost .q-ghost-brand .syl-cell.is-stress .syl-brace,
html[data-ui="opus"] .q-ghost .q-ghost-brand .syl-cell.is-stress .syl-brace {
  opacity: 1;
}

.q-ghost-caret {
  display: inline-block;
  width: 1.5px;
  height: 1.05em;
  margin-left: 1px;
  background: color-mix(in srgb, var(--ink) 55%, transparent);
  vertical-align: -0.1em;
  animation: ghost-blink 1.05s step-end infinite;
}

/* Idle search hint: left-aligned like the old opener, icon gray. */
.q-placeholder {
  position: absolute;
  inset: 0;
  z-index: 3;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  margin: 0;
  padding: 0 var(--chrome-cluster, 2.85rem) 0 var(--pad-left);
  pointer-events: none;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
  font-family: var(--font-ui);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  line-height: var(--input-text-band, calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2));
  letter-spacing: 0;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.q-placeholder[hidden] {
  display: none !important;
}

/* Custom caret spans the info-block, not only the orthography band. */
.q-info-caret {
  position: absolute;
  z-index: 4;
  width: 1.5px;
  margin: 0;
  padding: 0;
  background: var(--ink);
  pointer-events: none;
  transform: translateX(-0.75px);
  animation: ghost-blink 1.05s step-end infinite;
}

.q-info-caret[hidden] {
  display: none !important;
}

@keyframes ghost-blink {
  50% { opacity: 0; }
}

.input-fade {
  display: none !important;
}

.input-chrome {
  position: absolute;
  z-index: 30;
  /* Center gear on first-line glyphs — --chrome-top from syncChromeToFirstGlyph. */
  top: var(
    --chrome-top,
    calc(
      var(--pad-top, var(--composer-pad-top, 0.55rem)) +
        (
          var(--input-text-band, var(--composer-line, 1.1rem * 1.2)) +
            var(--input-analysis-band, 1.6rem) - var(--chrome-btn)
        ) / 2
    )
  );
  bottom: auto;
  height: var(--chrome-btn);
  right: var(--chrome-right);
  left: auto;
  width: max-content;
  max-width: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  gap: var(--chrome-gap);
  pointer-events: auto;
}

/* Opaque plate under icons (= invisible divider from the text lane) */
.input-chrome::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  bottom: auto;
  height: 100%;
  left: 0;
  /* Stop short of the inset stroke so the plate cannot eat the right edge. */
  right: calc(-1 * var(--chrome-right) + 2px);
  background: var(--paper);
  pointer-events: none;
}

.input-chrome > * {
  pointer-events: auto;
}

.input-settings {
  position: relative;
  flex: 0 0 auto;
  width: var(--chrome-btn);
  height: var(--chrome-btn);
  margin: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 2px;
  background: transparent;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.input-settings:focus-visible {
  color: color-mix(in srgb, var(--muted) 88%, var(--ink));
  outline: none;
}
@media (hover: hover) {
  .input-settings:hover {
    color: color-mix(in srgb, var(--muted) 88%, var(--ink));
    outline: none;
  }
}

.input-settings:active {
  color: var(--ink);
}

.input-settings[aria-expanded="true"] {
  color: var(--ink);
}

.input-settings-icon,
.tool-icon {
  width: var(--chrome-icon);
  height: var(--chrome-icon);
  display: block;
  overflow: visible;
  flex: 0 0 auto;
  opacity: 1;
  shape-rendering: geometricPrecision;
}

.tool-icon .icon-fill,
.input-settings-icon .icon-fill {
  fill: currentColor;
  stroke: none;
}

.input-settings-icon .icon-knob {
  fill: var(--paper);
  stroke: currentColor;
}

/* Multi-reading hint while analysis is off: muted ↔ ink */
@keyframes chrome-attention {
  0%,
  100% {
    color: color-mix(in srgb, var(--muted) 62%, transparent);
  }
  50% {
    color: var(--ink);
  }
}

.input-settings.is-attention,
.tool-btn.is-attention {
  animation: chrome-attention 1.55s ease-in-out infinite;
}

.input-settings.is-attention:focus-visible,
.tool-btn.is-attention:focus-visible {
  animation: none;
  color: var(--ink);
}
@media (hover: hover) {
  .input-settings.is-attention:hover,
  .tool-btn.is-attention:hover {
    animation: none;
    color: var(--ink);
  }
}

/*
  Tools expand left from the settings cluster (no second row / no divider).
  Closed: width 0. Open: slide out beside the gear.
*/
/* Gear menu. Width stays out of the icon cluster — the panel is absolute.
   Opens downward below the settings button. No outer glow. */
/* ==========================================================================
   Einstellungen (Blatt). Handy (≤ 600 px): Bottom-Sheet mit Abdunklung.
   Desktop: Popover unter dem Zahnrad (JS setzt --sheet-top/--sheet-right).
   8-px-Raster, Zeilen ≥ 48 px, Tap-Flächen ≥ 44 px, grüner Akzent.
   Kein „aktiv“-Klebezustand: Hover nur bei echter Maus, sonst :focus-visible;
   offen/an steht in aria-expanded / aria-checked / :checked.
   Längen in px/rem auf Root 16 px – unabhängig von --ui-text-scale.
   ========================================================================== */
/* Sheets, cards, popovers: one radius and one bottom inset for all of them.
   Phones have round display corners (iPhone ~47–55 pt, Android similar) and
   without viewport-fit=cover Safari reports env(safe-area-inset-bottom) = 0.
   So content of a sheet that reaches the screen bottom keeps at least
   --display-corner-pad above it: at 16 px side inset a 55 pt corner arc rises
   ~16 px, plus air. Floating menus keep --pop-edge-bottom to the screen bottom. */
:root {
  --sheet-radius: 16px;
  /* Cards inside a sheet (detail box, settings groups). */
  --card-radius: 12px;
  /* Menus and cards in the result list keep the list's square look. */
  --pop-radius: 2px;
  --display-corner-pad: 2rem;
  --sheet-pad-bottom: max(var(--display-corner-pad), calc(env(safe-area-inset-bottom, 0px) + 0.75rem));
  --pop-edge-bottom: 8px;
}

@media (pointer: coarse) {
  :root {
    --pop-edge-bottom: 28px;
  }
}

:root {
  --set-accent: var(--ramp-green);
  --set-surface: color-mix(in srgb, var(--ink) 6%, var(--paper));
  --set-card: color-mix(in srgb, var(--ink) 10%, var(--paper));
  --set-line: color-mix(in srgb, var(--ink) 12%, transparent);
  --set-row-h: 48px;
  --set-radius: var(--card-radius);
}

.settings-scrim,
.settings-scrim[hidden],
.settings-grab {
  display: none;
}

.settings-sheet {
  position: fixed;
  z-index: 60;
  top: var(--sheet-top, 72px);
  right: var(--sheet-right, 16px);
  left: auto;
  bottom: auto;
  width: min(400px, calc(100vw - 32px));
  max-height: var(--sheet-max, calc(100dvh - 32px));
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  margin: 0;
  background: var(--set-surface);
  color: var(--ink);
  border: 1px solid var(--set-line);
  border-radius: var(--sheet-radius);
  box-shadow: 0 16px 48px rgb(0 0 0 / 0.6);
  overflow: hidden;
  font-family: var(--font-ui);
  font-size: 0.9375rem;
  line-height: 1.4;
  text-align: left;
  animation: set-pop 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.settings-sheet[hidden] {
  display: none !important;
}

.settings-sheet *,
.settings-scrim {
  -webkit-tap-highlight-color: transparent;
}

@keyframes set-pop {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

@keyframes set-sheet-up {
  from { transform: translateY(24px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

@keyframes set-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (max-width: 600px) {
  .settings-sheet {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-height: min(88dvh, calc(100dvh - 48px));
    border-width: 1px 0 0;
    border-radius: var(--sheet-radius) var(--sheet-radius) 0 0;
    /* Rows scroll above this strip, never into the round display corners. */
    padding-bottom: var(--sheet-pad-bottom);
    box-shadow: 0 -8px 32px rgb(0 0 0 / 0.5);
    animation: set-sheet-up 220ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .settings-scrim:not([hidden]) {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 59;
    background: rgb(0 0 0 / 0.6);
    touch-action: none;
    animation: set-fade 180ms ease;
  }
  .settings-grab {
    display: block;
    flex: 0 0 auto;
    width: 40px;
    height: 4px;
    margin: 8px auto 0;
    border-radius: 2px;
    background: color-mix(in srgb, var(--ink) 28%, transparent);
  }
  /* Griff + Kopf: a drag there is the sheet's (swipe to close, JS), never a
     page scroll. The body keeps pan-y; JS takes the downward drag at the top. */
  .settings-grab,
  .settings-head {
    touch-action: none;
  }
  .settings-sheet.is-dragging {
    animation: none;
    transition: none;
    will-change: transform;
  }
  .settings-sheet.is-snapping,
  .settings-sheet.is-swiped-out {
    animation: none;
    transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .settings-scrim:not([hidden]) {
    transition: opacity 180ms ease;
  }
}


.settings-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  padding: 16px 8px 12px 16px;
  border-bottom: 1px solid var(--set-line);
}

.settings-title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.01em;
}

.settings-title:focus {
  outline: none;
}

.settings-applies {
  margin: 4px 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
}

.settings-close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: -8px 0 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

.settings-close svg {
  width: 20px;
  height: 20px;
}

.settings-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0 16px 24px;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ink) 30%, transparent) transparent;
}

.set-group {
  margin: 24px 0 0;
}

.set-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  padding: 0 4px;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.set-group-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}

.set-card {
  border-radius: var(--set-radius);
  background: var(--set-card);
  overflow: hidden;
}

/* One row = name · (?) · control. Whole name area toggles (label for). */
.set-row {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--set-row-h);
  box-sizing: border-box;
  margin: 0;
  padding: 0 4px 0 16px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  text-decoration: none;
}

.set-card > .set-row:not(:first-child),
.set-card > .set-reset {
  border-top: 1px solid var(--set-line);
}

.set-name {
  flex: 1 1 auto;
  min-width: 0;
  padding: 12px 0;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

label.set-name {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.set-row.is-nested {
  padding-left: 24px;
}

.branch-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  color: var(--muted);
}

.set-row.is-disabled .set-name {
  color: var(--muted);
  cursor: default;
}

/* (?) — optically small, 44 px to tap. */
.set-help {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
}

.set-help > span {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--muted) 55%, transparent);
  border-radius: 50%;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
}

.set-help[aria-expanded="true"] > span {
  border-color: var(--set-accent);
  background: var(--set-accent);
  color: #000;
}

.set-help-text {
  margin: 0 16px 12px;
  padding: 4px 0 4px 12px;
  border-left: 2px solid var(--set-accent);
  font-size: 0.8125rem;
  line-height: 1.45;
  color: color-mix(in srgb, var(--ink) 82%, transparent);
}

.set-help-text[hidden],
.set-note[hidden],
.set-sub[hidden],
.set-confirm[hidden],
.set-row[hidden] {
  display: none !important;
}

.set-note {
  margin: 0;
  padding: 0 16px 12px;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--muted);
}

/* Switch: 52 × 44 hit box, drawn track 44 × 26. */
.set-switch {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 52px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.set-switch::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 50%;
  width: 44px;
  height: 26px;
  margin-top: -13px;
  border-radius: 13px;
  background: color-mix(in srgb, var(--ink) 22%, transparent);
  transition: background-color 180ms ease, box-shadow 180ms ease;
}

.set-switch::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 50%;
  width: 22px;
  height: 22px;
  margin-top: -11px;
  border-radius: 50%;
  background: var(--ink);
  transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
}

.set-switch:checked::before {
  background: var(--set-accent);
}

.set-switch:checked::after {
  transform: translateX(18px);
  background: #fff;
}

.set-switch:disabled {
  cursor: default;
  opacity: 0.4;
}

.set-switch:focus {
  outline: none;
}

.set-switch:focus-visible::before {
  box-shadow: 0 0 0 2px var(--set-card), 0 0 0 4px var(--set-accent);
}

/* Stacked row: name + (?) on top, segmented choice below. */
.set-row-stack {
  flex-direction: column;
  align-items: stretch;
  padding: 0 4px 12px 16px;
}

.set-row-head {
  display: flex;
  align-items: center;
  min-height: 44px;
}

.set-seg {
  display: flex;
  gap: 4px;
  margin: 0 12px 0 0;
  padding: 4px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.set-seg-btn {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0 8px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.875rem;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
}

.set-seg-btn > input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.set-seg-btn.is-on,
.set-seg-btn:has(> input:checked) {
  background: color-mix(in srgb, var(--set-accent) 20%, transparent);
  box-shadow: inset 0 0 0 1px var(--set-accent);
  color: var(--ink);
  font-weight: 600;
}

/* Rows that are buttons or links. */
.set-action,
.set-disclosure,
.set-link,
.set-version {
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}

.set-action:disabled {
  cursor: default;
  opacity: 0.4;
}

.set-row-icon,
.set-chevron {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 0 12px 0 8px;
  color: var(--muted);
}

.set-chevron {
  width: 16px;
  height: 16px;
  transition: transform 180ms ease;
}

.set-disclosure[aria-expanded="true"] .set-chevron {
  transform: rotate(90deg);
}

.set-value {
  flex: 0 0 auto;
  margin: 0 12px 0 8px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.set-version {
  cursor: default;
}

#set-reset-btn .set-name {
  color: color-mix(in srgb, var(--accent) 70%, var(--ink));
}

.set-sub {
  padding: 4px 0 8px;
  border-top: 1px solid var(--set-line);
  background: color-mix(in srgb, var(--paper) 35%, transparent);
}

.set-sub > .set-note:first-child {
  padding-top: 12px;
}

.set-sub-title {
  margin: 12px 16px 4px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.lex-source-row + .lex-source-row {
  border-top: 1px solid var(--set-line);
}

.lex-source-line {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 0;
  cursor: pointer;
}

.lex-source-license,
.lex-source-count {
  color: var(--muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.set-sub .help-legend {
  padding: 12px 16px 0;
}

.set-sub .help-demo-btn {
  width: calc(100% - 32px);
  min-height: 44px;
  margin: 16px 16px 4px;
  padding: 8px 0 0;
}

.set-confirm {
  padding: 12px 16px 16px;
}

.set-confirm-q {
  margin: 0 0 12px;
  font-size: 0.875rem;
}

.set-confirm-btns {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.set-btn {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--set-line);
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.set-btn.is-danger {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}

.set-toast {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin: 16px 0 0;
  padding: 0;
  border-radius: 10px;
  font-size: 0.8125rem;
  text-align: center;
  color: var(--ink);
}

.set-toast.is-shown {
  padding: 12px 16px;
  background: color-mix(in srgb, var(--ink) 16%, var(--set-surface));
}

/* Focus: one visible ring in the accent color. */
.settings-close:focus-visible,
.set-help:focus-visible,
.set-row:focus-visible,
.set-btn:focus-visible,
.set-seg-btn:focus-visible,
.set-seg-btn:has(> input:focus-visible),
.set-sub .help-demo-btn:focus-visible {
  outline: 2px solid var(--set-accent);
  outline-offset: -2px;
}

.settings-close:focus,
.set-help:focus,
.set-row:focus,
.set-btn:focus,
.set-seg-btn:focus,
.set-seg-btn > input:focus {
  outline: none;
}

.settings-close:focus-visible,
.set-help:focus-visible {
  outline-offset: -4px;
}

@media (hover: hover) {
  .settings-close:hover,
  .set-help:hover {
    color: var(--ink);
  }
  .set-action:not(:disabled):hover,
  .set-disclosure:hover,
  .set-link:hover,
  .set-btn:hover {
    background: color-mix(in srgb, var(--ink) 5%, transparent);
  }
  .set-seg-btn:hover {
    color: var(--ink);
  }
}

@media (prefers-reduced-motion: reduce) {
  .settings-sheet,
  .settings-scrim:not([hidden]),
  .settings-sheet.is-snapping {
    animation: none;
    transition: none;
  }
  .set-switch::before,
  .set-switch::after,
  .set-chevron {
    transition: none;
  }
}

.tool-btn {
  flex: 0 0 auto;
  width: var(--chrome-btn);
  height: var(--chrome-btn);
  margin: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 2px;
  background: transparent;
  /* Same muted ink for every tool — no fill-heavy icon looking darker. */
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}

.tool-btn:focus-visible {
  color: color-mix(in srgb, var(--muted) 88%, var(--ink));
  outline: none;
}
@media (hover: hover) {
  .tool-btn:hover {
    color: color-mix(in srgb, var(--muted) 88%, var(--ink));
    outline: none;
  }
}

.tool-btn.is-on {
  color: var(--ink);
}

.tool-btn.is-on:focus-visible {
  color: var(--ink);
}
@media (hover: hover) {
  .tool-btn.is-on:hover {
    color: var(--ink);
  }
}

.tool-btn:disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}

/* Apple HIG ~44×44 hit, glyph stays the chrome size. */
.tool-btn,
.input-settings,
.lex-reload {
  position: relative;
}

.tool-btn::before,
.input-settings::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.tool-btn:focus-visible,
.input-settings:focus-visible,
.lex-reload:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--ink) 70%, transparent);
  outline-offset: 2px;
}

.help-legend {
  margin: 0.1rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.72rem;
}

.help-legend-item {
  margin: 0;
  padding: 0;
}

.help-legend-item dt {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.18rem;
  font-size: 0.78rem;
  font-weight: 650;
  color: var(--ink);
  letter-spacing: 0.01em;
}

.help-legend-item dd {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--muted);
}

.patch-notes-body {
  margin: 0.1rem 0 0;
  padding: 0 0.15rem 0.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.patch-notes-ver {
  margin: 0;
}

.patch-notes-ver dt {
  margin: 0 0 0.22rem;
  font-size: 0.78rem;
  font-weight: 650;
  letter-spacing: 0.04em;
}

.patch-notes-ver dd {
  margin: 0;
}

.patch-notes-ver ul {
  margin: 0;
  padding: 0 0 0 1.05rem;
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.4;
}

.help-demo-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  margin: 0.85rem 0 0.15rem;
  padding: 0.55rem 0.15rem 0.15rem;
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 650;
  text-align: left;
  cursor: pointer;
}

.help-demo-btn:focus-visible {
  color: var(--ink);
}
@media (hover: hover) {
  .help-demo-btn:hover {
    color: var(--ink);
  }
}

.help-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.18rem;
  flex: 0 0 auto;
  width: 1.35rem;
  height: 0.7rem;
  color: var(--ink);
}

.help-swatch svg {
  width: 0.85rem;
  height: 0.85rem;
  display: block;
}

.help-dash {
  display: block;
  width: 0.55rem;
  height: 1.6px;
  background: color-mix(in srgb, var(--ink) 38%, transparent);
  border-radius: 1px;
}

.help-dash.is-stress {
  background: var(--ink);
  opacity: 1;
}

.help-dash.is-good {
  background: color-mix(in srgb, var(--good) 88%, transparent);
}

.help-dash.is-warn {
  background: color-mix(in srgb, var(--warn) 88%, transparent);
}

.help-ipa-swatch {
  font-family: var(--font-ipa);
  font-size: 0.62rem;
  line-height: 1;
  color: color-mix(in srgb, var(--muted) 70%, var(--warn));
}

/*
  Touch: small controls get an invisible ≥ 44 px hit area (WCAG 2.5.5)
  without changing their drawn size or the layout. Search and settings
  already have one (.input-settings::before). The Lesart band is left
  alone on purpose: growing it up would cover the typed letters again.
  Mode chips (38 px) sit in an overflow:hidden group, so a pseudo hit area
  cannot reach out; left as is (the group is also where the parallel
  "Normale/Flexible Reime" work lands).
*/
@media (pointer: coarse) {
  .site-foot .legal-link {
    position: relative;
  }
  .rhyme-feat-chip::before,
  .site-foot .legal-link::before {
    content: "";
    position: absolute;
    top: min(0px, calc((100% - 44px) / 2));
    bottom: min(0px, calc((100% - 44px) / 2));
    left: min(0px, calc((100% - 44px) / 2));
    right: min(0px, calc((100% - 44px) / 2));
  }
  /* The chip sits left of the score: grow only leftward, never over it. */
  .rhyme-feat-chip::before {
    right: 0;
    left: min(0px, calc(100% - 44px));
  }
}

/* E-045: Normale Reime | Flexible Reime — one switch (.set-seg in the settings sheet). */
/* Tap target ≥ 44 px on phones. */
.stress-mode-chip {
  min-height: 44px;
}

/* Note above the list while the flexible mode is shown. */
.stress-flex-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  margin: 0 var(--content-inline) 0.35rem;
  font-size: 0.78rem;
  line-height: 1.3;
  color: var(--ink);
}

.stress-flex-note[hidden] {
  display: none;
}

.stress-flex-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.18rem 0.6rem;
  border: 1px solid color-mix(in srgb, var(--ink) 35%, transparent);
  border-radius: 999px;
  font-size: 0.72rem;
  letter-spacing: 0.01em;
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.stress-flex-empty {
  flex: 1 1 100%;
  margin: 0.2rem 0 0;
  color: var(--ink);
}

.stress-flex-back {
  min-height: 44px;
  margin: 0;
  padding: 0 0.2rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  cursor: pointer;
}

.syl-cell.is-dash-hide .syl-brace {
  opacity: 0;
}

.syl-cell.is-dash-long .syl-brace {
  position: relative;
  overflow: visible;
  z-index: 1;
}

/* Split mark sits on the dash stroke (slot is 1.6px). */
.syl-cell.is-dash-long .syl-brace::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0.18rem;
  height: 100%;
  transform: translate(-50%, -50%);
  background: var(--paper);
  pointer-events: none;
}

.combo-combine {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.75rem;
  margin: 0 0 0.35rem;
  color: var(--muted);
  font-size: 0.78rem;
}

.combo-combine[hidden] {
  display: none !important;
}

.combo-combine-count {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  color: var(--ink);
  font-size: 0.82rem;
}

.combo-combine-count[hidden] {
  display: none !important;
}

.combo-combine button,
.combo-combine-primary {
  margin: 0;
  padding: 0.2rem 0;
  min-height: 2rem;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.combo-combine-primary {
  color: var(--ink);
  font-weight: 600;
}

.combo-combine-primary.is-on {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.combo-combine button:focus-visible,
.combo-combine-primary:focus-visible {
  color: var(--ink);
  outline: none;
}
@media (hover: hover) {
  .combo-combine button:hover,
  .combo-combine-primary:hover {
    color: var(--ink);
    outline: none;
  }
}

.combo-combine-out {
  margin: 0;
  flex: 1 1 12rem;
  min-width: 0;
  font-size: 0.72rem;
  opacity: 0.8;
}

.combo-combine-mode {
  margin: 0;
  flex: 1 1 100%;
  font-size: 0.72rem;
  opacity: 0.7;
}

.combo-combine-mode[hidden] {
  display: none !important;
}

.combo-combine-semantics {
  margin: 0;
  flex: 0 0 auto;
  font-size: 0.72rem;
  opacity: 0.85;
}

.combo-combine-semantics[hidden] {
  display: none !important;
}

.combo-combine-llm-help {
  margin: 0.25rem 0 0;
  padding: 0 0 0 1.1rem;
  flex: 1 1 100%;
  font-size: 0.72rem;
  opacity: 0.75;
  line-height: 1.35;
}

.combo-combine-llm-help[hidden] {
  display: none !important;
}

.combo-combine-paste {
  flex: 1 1 100%;
  box-sizing: border-box;
  min-height: 2.4rem;
  margin: 0.35rem 0 0;
  padding: 0.35rem 0.45rem;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  border-radius: 2px;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  color: var(--ink);
  font: inherit;
  font-size: 0.75rem;
  resize: vertical;
}

.status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.composer.is-loading .input-chrome,
.composer.is-error .input-chrome,
.composer:has(.lex-load:not([hidden])) .input-chrome,
.composer-row:has(.composer.is-loading) .settings-chrome,
.composer-row:has(.composer.is-error) .settings-chrome,
.composer-row:has(.composer .lex-load:not([hidden])) .settings-chrome {
  visibility: hidden;
  pointer-events: none;
}

/* Packed/offline first paint: only the lexicon bar. Remote never sets
   data-packed-boot (ship stamps data-engine=remote; ?engine=remote also
   skips). Cleared in setComposerState("ready"). 0.328 hid .input-chrome
   (search icon) and missed the field: #input-host + .composer::after. */
html[data-packed-boot="1"] .query-shell > :not(.composer-row),
html[data-packed-boot="1"] .composer-row > :not(#composer),
html[data-packed-boot="1"] #composer > :not(#lex-load),
html[data-packed-boot="1"]:not([data-boot="error"]) #lex-load > :not(#lex-load-track),
html[data-packed-boot="1"] .site-foot,
html[data-packed-boot="1"] #debug-dock,
html[data-packed-boot="1"] #word-menu,
html[data-packed-boot="1"] #legal-view,
html[data-packed-boot="1"] .results,
html[data-packed-boot="1"] .shard-load-status,
html[data-packed-boot="1"] .origin-status {
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
html[data-packed-boot="1"] .composer::after {
  content: none !important;
  display: none !important;
}
html[data-packed-boot="1"] .lex-load-track {
  visibility: visible !important;
  opacity: 1 !important;
  display: block !important;
}
html[data-boot="loading"]:not([data-engine="remote"]) .site-wordmark-wrap {
  opacity: 0 !important;
  visibility: hidden;
}

/* Progress covers the composer. The bar is pinned to the opener line. */
.lex-load {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: 1.2rem;
  margin: 0;
  /* Same lane as the opener: left pad, right gear cluster. */
  padding: 0.45rem var(--chrome-cluster, 2.85rem) 0.55rem var(--pad-left);
  border: 0;
  background: transparent;
  pointer-events: none;
  opacity: 1;
  text-align: center;
  transition: none;
}

.lex-load.is-exiting {
  opacity: 0;
  transition: none;
}

/* Finish: fill flashes to ink, then the bar collapses to a caret at the left. */
.lex-load.is-morph .lex-load-brand,
.lex-load.is-morph .lex-load-label {
  opacity: 0;
  transition: opacity 140ms linear;
}

.lex-load.is-morph .lex-load-fill {
  background: var(--ink);
}

.lex-load.is-morph .lex-load-track {
  transform-origin: left center;
  transform: translateY(-50%) scaleX(0.01);
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.lex-load[hidden] {
  display: none;
}

.composer.is-ready .lex-load:not(.is-exiting) {
  display: none;
}

.lex-load-label {
  margin: 0;
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  color: var(--muted);
  text-align: center;
}

.lex-load-label[hidden] {
  display: none;
}

.lex-load-track {
  position: relative;
  /* Integer px — rem heights blur to uneven thickness on DPR screens */
  width: 100%;
  height: 4px;
  border: 0;
  background: var(--chip);
  overflow: hidden;
  flex-shrink: 0;
  border-radius: 0;
}

.lex-load-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 4px;
  width: 100%;
  background: color-mix(in srgb, var(--ink) 55%, var(--muted));
  transform-origin: left center;
  transform: scaleX(0);
  /* CSS transition keeps moving if the main thread stalls briefly. */
  transition: transform var(--dur-3, 340ms) cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

.lex-load-track.is-indeterminate .lex-load-fill {
  /* Sweep via left (not translate) so height stays crisp */
  width: 32%;
  transform: none;
  transition: none;
  /* Ease in-out sweep — accel at entry, soft exit past the track end */
  animation: lex-indeterminate 1.7s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
}

/* Hidden until fonts and the field width have settled (data-boot=loading). */
html:not([data-boot="loading"]):not([data-boot="error"]) .lex-load-track,
html:not([data-boot="loading"]):not([data-boot="error"]) .lex-load-brand,
html:not([data-boot="loading"]):not([data-boot="error"]) .lex-load-label {
  visibility: hidden;
}

/* Remote engine: never the lexicon ingest overlay — including origin errors.
   Packed/offline is the only path that paints .lex-load-track.
   0.344: also kill hit-testing. 0.343 display:none lost when data-engine
   was missing on the live HTML, so the inset layer ate taps. */
html[data-engine="remote"] .lex-load,
html:not([data-packed-boot="1"]) .lex-load-track {
  display: none !important;
  pointer-events: none !important;
}
html[data-engine="remote"] .lex-load {
  visibility: hidden !important;
}
html[data-engine="remote"] .composer.is-loading .input-chrome,
html[data-engine="remote"] .composer:has(.lex-load:not([hidden])) .input-chrome,
html[data-engine="remote"] .composer-row:has(.composer.is-loading) .settings-chrome,
html[data-engine="remote"] .composer-row:has(.composer .lex-load:not([hidden])) .settings-chrome {
  visibility: visible;
  pointer-events: auto;
}

.origin-status {
  margin: 0.7rem 0 0;
  padding: 0 0.2rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  line-height: 1.35;
  pointer-events: none;
}

.origin-status[hidden] {
  display: none;
  pointer-events: none;
}

/* Brand stays blank until the head script confirms the product face. */
html:not([data-font-ready="1"]) .lex-load-brand {
  visibility: hidden;
}

.lex-load-brand,
.lex-load-label {
  /* Critical CSS hides these until app.css lands — restore here. */
  visibility: visible;
}

.lex-load-brand {
  display: none;
}

/* Bar sits on the optical center of the search field, not the text line. */
.composer.is-loading .lex-load-track,
.composer.is-error .lex-load-track {
  position: absolute;
  left: var(--content-inline, 0.85rem);
  right: var(--content-inline, 0.85rem);
  width: auto;
  max-width: none;
  margin: 0;
  top: 50%;
  transform: translateY(-50%);
}

.lex-reload {
  pointer-events: auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0.15rem 0 0;
  padding: 0 1.15rem;
  min-width: 44px;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--surface-1, #1e1e1e);
  color: var(--ink);
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
}

.lex-reload[hidden] {
  display: none;
}

.lex-load.is-error {
  gap: 0.7rem;
}

.lex-load.is-error .lex-load-track,
.lex-load-track.is-failed {
  display: none;
}

.lex-load.is-error .lex-load-fill,
.lex-load-track.is-failed .lex-load-fill {
  display: none;
}

.lex-load.is-error .lex-load-label {
  display: block;
  color: var(--accent);
}

@keyframes lex-indeterminate {
  0% { left: -34%; }
  100% { left: 104%; }
}

.panels[hidden],
.results[hidden] {
  display: none;
}

.results {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 0;
  overflow: visible;
  /* The field rule is the divider (no hairline of its own). The first
     result word's cap height sits --rule-gap under the rule, the same air
     as IPA baseline → rule above it (docs/ui-layout.md §4d). Margin, not
     padding: the row's own top air can be larger than the gap. */
  padding-top: 0;
  margin-top: calc(var(--rule-gap) - var(--row-cap-inset));
}


.panel-rhymes {
  position: relative;
  z-index: 1;
  min-height: 0;
  border-top: 0;
  /* Same air below the rule as panel-units leaves above it */
  padding: var(--rhyme-air-eff) 0 0;
  overflow: visible;
}

.panel {
  background: transparent;
  border: 0;
  /* No horizontal pad — borders stay full-bleed; content pads itself */
  padding: 0;
  min-height: 0;
}

/*
  Input analysis decorations live inside .input-host (#units.q-decor).
  panel-units is a legacy aria shell — keep out of flow.
*/
.panel-units {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  margin: 0;
  padding: 0;
  border: 0;
  pointer-events: none;
}

.panel-units[hidden] {
  display: none;
}

.panel-rhymes .rhymes {
  padding-top: 0;
  overflow: visible;
}

.panel h2 {
  margin: 0 0 0.7rem;
  font-size: 0.82rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}

.meta { letter-spacing: 0; text-transform: none; font-weight: 400; }

/*
  Analysis stack: continuous ortho, horizontal syllable marks (inset under letters),
  IPA under a shared baseline. No side boxes, no /slashes/.
*/
.analysis-stack {
  /* Same stroke weight for all; stress = opacity only */
  --brace-stroke: 1.6px;
  --brace-slot: 1.6px;
  --brace-inset: 0.07em;
  /* --brace-air / --brace-ipa-air come from :root */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  direction: ltr;
  unicode-bidi: isolate;
  min-width: 0;
}

.syl-track {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
  gap: 0;
  max-width: 100%;
}

/* Word = flex group of syllable cells.
   Results may wrap between syllables; underlay never — wrap is per info-block
   on .units-row so marks/IPA stay glued to their word. */
.syl-word {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  max-width: 100%;
}

/*
  Results: grow the content column first. Wrap syllables only when the column
  already sits at the screen max (data-col-at-max from syncContentColumn).
*/
#rhymes .syl-track,
#rhymes .syl-word,
#start-demo-rows .syl-track,
#start-demo-rows .syl-word {
  flex-wrap: nowrap;
  max-width: none;
}

html[data-col-at-max="1"] #rhymes .syl-track,
html[data-col-at-max="1"] #rhymes .syl-word {
  flex-wrap: wrap;
  max-width: 100%;
}

.analysis-stack.is-underlay .syl-track,
.analysis-stack.is-underlay .syl-word {
  flex-wrap: nowrap;
  max-width: none;
}

.syl-cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  flex: 0 0 auto;
  width: fit-content;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  position: relative;
}

/* Flex mode: fixed syllable fields; glyphs fill via axes + scaleX. */
html[data-font-mode="flex"] .analysis-stack,
html[data-font-mode="flex"] .syl-track,
html[data-font-mode="flex"] .syl-word {
  justify-content: flex-start;
  align-items: flex-start;
}

/*
  FOFT / flex flash: hide result-list syllable ink until axes applied.
  Never gate #units underlay — that flash was visible as letter flicker
  next to the stable .unit-surface while marks rebuilt.
*/
html[data-font-mode="flex"]:not([data-flex-eq="1"]) #rhymes .syl-ortho,
html[data-font-mode="flex"]:not([data-flex-eq="1"]) #rhymes .syl-brace,
html[data-font-mode="flex"]:not([data-flex-eq="1"]) #rhymes .syl-ipa {
  opacity: 0;
}

/*
  Ortho must read as one flowing word (no artificial letter gaps).
  Join air lives only on the braces — see --flex-brace-gap below.
*/
html[data-font-mode="flex"] .syl-word {
  --flex-brace-gap: 0.22em;
  column-gap: 0;
  row-gap: 0;
  align-items: flex-start;
}

html[data-font-mode="flex"] .syl-cell[data-syl-col] {
  flex: 0 0 auto;
  align-items: stretch;
  overflow: visible;
}

html[data-font-mode="flex"] .syl-ortho {
  display: block;
  box-sizing: border-box;
  text-align: left;
  letter-spacing: 0 !important;
  line-height: 1.2;
  height: 1.2em;
  overflow: visible;
  white-space: pre;
  /* Default axis before JS equalize — avoids static→flex glyph jump. */
  font-variation-settings: "wdth" 100;
  font-stretch: 100%;
}

/* Brace-only joins (override product --brace-inset when cells abut). */
html[data-font-mode="flex"] .syl-cell .syl-brace {
  margin-left: 0;
}
html[data-font-mode="flex"] .syl-cell:not(.is-syl-last) .syl-brace {
  width: calc(100% - var(--flex-brace-gap, 0.22em));
  margin-left: 0;
}
html[data-font-mode="flex"] .syl-cell.is-syl-last .syl-brace {
  width: 100%;
  margin-left: 0;
}
html[data-font-mode="flex"] .syl-cell:not(.is-syl-first) .syl-brace {
  margin-left: 0;
  width: 100%;
}
html[data-font-mode="flex"] .syl-cell:not(.is-syl-first):not(.is-syl-last) .syl-brace {
  margin-left: 0;
  width: calc(100% - var(--flex-brace-gap, 0.22em));
}

html[data-font-mode="flex"] .syl-ipa {
  display: block;
  text-align: center;
  letter-spacing: normal;
  line-height: 1.25;
  min-height: 1.25em;
}

/*
  Overlay input: typed surface is a stable full-word layer (no chip reflow
  blink). Syllable ortho stays height-0 for brace width only.
*/
.unit-surface {
  display: none;
}

html[data-flex-input="1"] .unit-surface {
  display: block;
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  font-family: var(--font-mono);
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
  line-height: 1.2;
  white-space: pre;
  letter-spacing: 0;
  pointer-events: none;
  /* Own compositor layer — analysis DOM swaps must not invalidate glyphs. */
  position: relative;
  z-index: 1;
  contain: paint;
  isolation: isolate;
  transform: translateZ(0);
  backface-visibility: hidden;
}

html[data-flex-input="1"] .unit-choice.is-solo,
html[data-flex-input="1"] .units-readings,
html[data-flex-input="1"] .unit-cluster {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* Underlay ortho: keep letter runs for brace width, but never paint them
   (opacity/color only — visibility:hidden collapsed marks/IPA). Flex-eq
   still skips #units so these runs are not mutated into view. */
html[data-flex-input="1"] .analysis-stack.is-underlay .syl-ortho,
html[data-flex-input="1"] #units .syl-ortho {
  display: block;
  height: 0 !important;
  line-height: 0 !important;
  overflow: hidden !important;
  margin: 0 !important;
  padding: 0 !important;
  color: transparent !important;
  -webkit-text-fill-color: transparent !important;
  opacity: 0 !important;
  transform: none !important;
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  white-space: pre;
  pointer-events: none;
}

html[data-flex-input="1"] .panel-units {
  /* Shell only — decorations are in .input-host #units */
  margin: 0;
  padding: 0;
}

/* IPA-only stacks never show ortho. */
.analysis-stack.is-ipa-only .syl-ortho,
.units-readings .reading-ipa .syl-ortho {
  display: block;
  height: 0 !important;
  line-height: 0 !important;
  overflow: hidden !important;
  margin: 0 !important;
  padding: 0 !important;
  opacity: 0 !important;
  color: transparent !important;
}


/*
  Input underlay brace air = same global rule as #rhymes / .is-underlay:
  margin-top: calc(var(--brace-air-eff) - var(--ortho-ink-below)).
  Never override to full --brace-air-eff here — that doubled the ink→mark gap.
*/

/* Mid marks only: ortho → brace → IPA */
.syl-ortho { order: 2; }
.syl-brace { order: 3; }
.syl-ipa { order: 4; }
.syl-ipa-break {
  order: 4;
  position: absolute;
  right: 0;
  bottom: 0;
  transform: translateX(42%);
  font-family: var(--font-ipa);
  font-size: calc(0.58rem * var(--ui-text-scale, 1.1));
  line-height: 1.35;
  color: var(--muted);
  pointer-events: none;
}

.syl-gap {
  /* Real space glyph — matches inter-word gap; no brace spans this. */
  flex: 0 0 auto;
  width: auto;
  white-space: pre;
  align-self: stretch;
  font-size: inherit;
  line-height: 0;
  pointer-events: none;
}

.analysis-stack.is-primary .syl-gap,
.analysis-stack.is-underlay .syl-gap {
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
}

.syl-ortho {
  font-size: calc(0.78rem * var(--ui-text-scale, 1.1));
  font-weight: 400;
  line-height: 1.15;
  color: var(--muted);
  /* pre: never wrap mid-syllable */
  white-space: pre;
  letter-spacing: 0;
}

/* Result row: ortho IS the name — braces hang directly under it */
.analysis-stack.is-primary .syl-ortho {
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  line-height: 1.2;
  color: var(--ink);
  font-weight: 400;
}

/*
  Underlay: ortho only sizes brace width — zero height (word lives in
  .unit-surface). Never visibility:hidden — that collapses brace width.
*/
.analysis-stack.is-underlay .syl-ortho {
  display: block;
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  line-height: 0;
  white-space: pre;
  height: 0;
  overflow: hidden;
  margin: 0;
  padding: 0;
  opacity: 0;
  color: transparent;
  -webkit-text-fill-color: transparent;
  pointer-events: none;
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

.analysis-stack.is-underlay .syl-brace {
  margin-top: calc(var(--brace-air-eff) - var(--ortho-ink-below, 0px));
  margin-bottom: calc(var(--brace-ipa-air-eff) - var(--ipa-ink-above, 0px));
}

/* Typing, before analyze (docs/ui-layout.md §4e): the real info-block shape
   in neutral grey — one Silbenstrich per estimated syllable with the real
   stroke and joins, and under each a short IPA bar of the SAME stroke,
   centred where the IPA's x-height sits. Same shelf, so nothing moves. */
:root {
  --ph-brace: color-mix(in srgb, var(--ink) 30%, transparent);
  --ph-ipa: color-mix(in srgb, var(--ink) 20%, transparent);
}

.analysis-stack.is-placeholder .syl-brace {
  --brace-color: var(--ph-brace);
  opacity: 1;
}

.analysis-stack.is-placeholder .syl-ipa.is-ph {
  height: 1.35em;
  background: linear-gradient(var(--ph-ipa), var(--ph-ipa)) center 58% / 56% var(--brace-stroke, 1.6px)
    no-repeat;
  color: transparent;
}

/* Placeholder → real block: grey marks fade out in place, the real marks
   rise in (opacity/transform only, no layout). */
.decor-word.is-leaving {
  z-index: 0;
  animation: ph-leave 0.24s ease-out both;
}

.decor-word.is-arriving .syl-brace,
.decor-word.is-arriving .syl-ipa {
  animation: ph-arrive 0.24s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes ph-leave {
  to { opacity: 0; }
}

@keyframes ph-arrive {
  from {
    opacity: 0;
    transform: translateY(2px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .decor-word.is-leaving {
    display: none;
  }
  .decor-word.is-arriving .syl-brace,
  .decor-word.is-arriving .syl-ipa {
    animation: none;
  }
}

.units-word-gap {
  flex: 0 0 auto;
  white-space: pre;
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  line-height: 0;
  pointer-events: none;
  font-family: var(--font-mono);
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

html[data-flex-input="1"] .units-word-gap {
  /* Match mono space advance (textarea). */
  align-self: flex-start;
  box-sizing: content-box;
  width: 1ch;
  min-width: 1ch;
  max-width: 1ch;
  flex: 0 0 1ch;
  height: 1.2em;
  line-height: 1.2;
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  font-family: var(--font-mono);
  color: transparent;
  overflow: hidden;
}

/*
  Horizontal syllable marks: full width on outer edges and mono syllables.
  Only shorten toward inter-syllable joins (gap = --brace-inset each side).
*/
.syl-brace {
  --brace-color: color-mix(in srgb, var(--chrome) 55%, transparent);
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: var(--brace-slot);
  /* Subtract line-box slack so air is ink→mark / mark→ink, not box→box. */
  margin-top: calc(var(--brace-air-eff) - var(--ortho-ink-below, 0px));
  margin-bottom: calc(var(--brace-ipa-air-eff) - var(--ipa-ink-above, 0px));
  margin-left: 0;
  padding: 0;
  border: none;
  border-radius: 1px;
  background: linear-gradient(var(--brace-color), var(--brace-color))
    center / 100% var(--brace-stroke) no-repeat;
  opacity: 0.42;
  pointer-events: none;
}

/* Join: shorten the right edge of every non-final syllable */
.syl-cell:not(.is-syl-last) .syl-brace {
  width: calc(100% - var(--brace-inset));
}

/* Join: shorten the left edge of every non-first syllable */
.syl-cell:not(.is-syl-first) .syl-brace {
  margin-left: var(--brace-inset);
  width: calc(100% - var(--brace-inset));
}

/* Middle: shortened on both sides of the joins */
.syl-cell:not(.is-syl-first):not(.is-syl-last) .syl-brace {
  margin-left: var(--brace-inset);
  width: calc(100% - 2 * var(--brace-inset));
}

.syl-cell.is-stress .syl-brace {
  opacity: 1;
}

.syl-cell.is-bent .syl-brace {
  opacity: 1;
}

.analysis-stack.q-syl-good .syl-brace {
  --brace-color: color-mix(in srgb, var(--good) 88%, transparent);
}
.analysis-stack.q-syl-warn .syl-brace {
  --brace-color: color-mix(in srgb, var(--warn) 88%, transparent);
}
.analysis-stack.q-syl-bad .syl-brace {
  --brace-color: color-mix(in srgb, var(--bad) 88%, transparent);
}

.syl-ipa {
  /* Don't expand the cell: hang under the ortho width */
  width: 0;
  min-width: 100%;
  box-sizing: border-box;
  font-family: var(--font-ipa);
  font-feature-settings: "kern" 1;
  font-size: calc(0.58rem * var(--ui-text-scale, 1.1));
  line-height: 1.35;
  color: var(--muted);
  direction: ltr;
  unicode-bidi: isolate;
  margin-top: 0;
  display: flex;
  justify-content: center;
  text-align: center;
  white-space: nowrap;
  overflow: visible;
}

.syl-ipa.is-ipa-condensed {
  transform-origin: center top;
}

/*
  Result rows: the IPA must not size its syllable cell. `min-width:100%` is a
  cyclic percentage inside the shrink-to-fit cell, so Chrome fell back to the
  IPA's content width and every IPA wider than its letters pushed the next
  syllable away — visible gaps inside words ("Life  line", "hin  über").
  contain:inline-size makes the IPA's own width contribution 0; it still
  centres under the cell and may hang past it (js/ui/ipa-fit.js decides
  stretch / shrink / spill; only a word's last syllable may widen the cell).
*/
#rhymes .syl-cell > .syl-ipa,
#start-demo-rows .syl-cell > .syl-ipa {
  contain: inline-size;
}

/* IPA a bit wider than its dash: hang centered into the free space under
   the neighbours (js/ui/ipa-fit.js checks they have room). Flex-grid cells
   set display:block inline, which would start-align the overflow. */
.syl-cell .syl-ipa.is-ipa-spill {
  display: flex !important;
  justify-content: center;
}

/*
  Center under the brace without changing cell width.
  Keep width:0;min-width:100% (max-content was expanding syllables when
  IPA was longer than ortho — visible especially on iOS).
  flex + justify-content works on WebKit where text-align alone failed.
*/

.ipa-fallback {
  font-family: var(--font-ipa);
  font-feature-settings: "kern" 1;
  font-size: calc(0.62rem * var(--ui-text-scale, 1.1));
  line-height: 1.2;
  color: var(--muted);
  direction: ltr;
  unicode-bidi: isolate;
  word-break: break-word;
  margin-top: 0;
  text-align: left;
  max-width: 100%;
}

.analysis-stack.q-ipa-good .syl-ipa,
.analysis-stack.q-ipa-good .ipa-fallback {
  color: color-mix(in srgb, var(--muted) 70%, var(--good));
}
.analysis-stack.q-ipa-warn .syl-ipa,
.analysis-stack.q-ipa-warn .ipa-fallback {
  color: color-mix(in srgb, var(--muted) 70%, var(--warn));
}
.analysis-stack.q-ipa-bad .syl-ipa,
.analysis-stack.q-ipa-bad .ipa-fallback {
  color: color-mix(in srgb, var(--muted) 70%, var(--bad));
}

/*
  Default face is Manrope (UI, brand, input, analysis).
  Iosevka Aile remains available from the debug font cycle.
*/
html {
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

.analysis-stack,
.analysis-stack .syl-ortho,
.score-pct {
  font-family: var(--font-mono);
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

.analysis-stack .syl-ipa,
.ipa-fallback {
  font-family: var(--font-ipa);
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

.site-foot,
.site-foot .legal-link {
  font-family: var(--font-sans);
  font-variant-ligatures: normal;
  font-feature-settings: "kern" 1, "liga" 1;
}

.ipa-tok.is-diff {
  background: color-mix(in srgb, var(--muted) 28%, transparent);
  border-radius: 0.15em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.units {
  display: flex;
  flex-direction: column;
  gap: calc(var(--rhyme-air-eff) + 0.35rem);
}

.name-line {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  min-width: 0;
  max-width: 100%;
}

.lang-mark {
  font-size: 0.55em;
  line-height: 0;
  color: var(--faint);
  margin-left: 0.15em;
  vertical-align: super;
  font-weight: 550;
  letter-spacing: 0;
}
.estimate-mark {
  font-size: 0.62em;
  line-height: 0;
  color: var(--faint);
  margin-right: 0.12em;
  vertical-align: super;
  font-weight: 500;
}
.reading-lesart-badge {
  display: block;
  font-size: 0.62rem;
  line-height: 1.2;
  color: var(--faint);
  margin-bottom: 0.15rem;
  text-align: center;
  letter-spacing: 0.02em;
}
button.lang-mark {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: 0.55em;
  color: var(--faint);
}

/* Input line carries no language codes. */
.lang-mark-input {
  display: none !important;
}

/*
  Result kennzeichen: top-right diagonal beside the word (clear of the
  last letter). 0.291's right:-0.05em sat on the glyph.
*/
#rhymes .analysis-stack.is-primary > .syl-track,
#rhymes .name-line {
  position: relative;
  overflow: visible;
}
#rhymes .analysis-stack.is-primary > .syl-track > .lang-mark,
#rhymes .name-line > .lang-mark {
  position: absolute;
  top: 0;
  left: 100%;
  right: auto;
  margin: 0;
  /* -0.58em (was -0.72em): at 8.8 px the mark sat ~1.2 px too high. */
  transform: translate(0.08em, -0.58em);
  line-height: 1;
  vertical-align: baseline;
  pointer-events: none;
  z-index: 1;
  white-space: nowrap;
}
#rhymes .analysis-stack.is-primary > .syl-track > .estimate-mark,
#rhymes .name-line > .estimate-mark {
  position: absolute;
  top: 0;
  left: 100%;
  right: auto;
  margin: 0;
  transform: translate(0.08em, -0.72em);
  line-height: 1;
  pointer-events: none;
  z-index: 1;
  white-space: nowrap;
}
/* ≈ then lang code — shift the code past the estimate glyph. */
#rhymes .analysis-stack.is-primary > .syl-track:has(> .estimate-mark) > .lang-mark,
#rhymes .name-line:has(> .estimate-mark) > .lang-mark {
  transform: translate(0.85em, -0.72em);
}
.lang-hint {
  color: var(--faint);
  font-size: 0.85em;
}
.why-origin {
  margin: 0.4rem 0 0;
  color: var(--faint);
  font-size: var(--fs-note, 0.82rem);
}
.name-line .name {
  min-width: 0;
}

/* Reading picker: opacity only — no fill/border fighting trust outlines */
.unit-choice {
  appearance: none;
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.unit-choice.is-solo {
  cursor: default;
}

/* Multi-word row: gap = one space glyph (must match textarea, not 0.85rem). */
.units-row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 0;
  max-width: 100%;
}

/* Never let word clusters expand to row width. */
.units-row > .unit-cluster {
  flex: 0 0 auto;
  width: max-content;
  max-width: 100%;
}

.unit-cluster {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 100%;
}

.unit-cluster > .unit-analysis {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  overflow: visible;
  pointer-events: none;
  contain: paint;
}

/* Selection highlight on underlay glyphs (textarea ::selection is transparent). */
#units {
  position: relative;
}

html.is-lesart-hover,
html.is-lesart-hover * {
  cursor: pointer !important;
}

/* Multi-reading: pulse the syl+IPA band; the menu hangs under that block. */
.units-readings {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  max-width: 100%;
  -webkit-tap-highlight-color: transparent;
  pointer-events: none;
}

.units-readings.is-multi {
  pointer-events: none;
  position: relative;
}

@keyframes lesart-band-pulse {
  0%,
  100% { opacity: 0.55; }
  50% { opacity: 1; }
}

.units-readings.is-multi > .analysis-stack {
  animation: lesart-band-pulse 2.1s ease-in-out infinite;
  animation-delay: var(--lesart-pulse-delay, 0ms);
}

.units-readings.is-multi.is-open > .analysis-stack {
  animation: none;
  opacity: 1;
}
@media (hover: hover) {
  .units-readings.is-multi:hover > .analysis-stack {
    animation: none;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .units-readings.is-multi > .analysis-stack {
    animation: none;
  }
}

.units-readings.is-multi .analysis-stack {
  pointer-events: none;
}

.units-readings.is-multi .syl-cell,
.units-readings.is-multi .syl-brace,
.units-readings.is-multi .syl-ipa {
  pointer-events: none;
  cursor: inherit;
}

.reading-picker {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
}

/* Invisible hit on the syl+IPA band. The orthography stays the text caret. */
.reading-band-hit {
  position: absolute;
  /* Start just below the text line: a tap on the letters is a caret tap. */
  inset: 2px 0 0 0;
  z-index: 4;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}

.reading-band-hit:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--muted) 55%, transparent);
  outline-offset: 2px;
}

.reading-picker-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: auto;
  z-index: 5;
  width: 100%;
  min-width: 0;
  max-width: none;
  margin: 0.16rem 0 0;
  padding: 0.2rem 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.34rem;
  background: var(--paper);
  border-radius: var(--pop-radius);
  box-shadow: 0 0.35rem 0.9rem color-mix(in srgb, var(--ink) 10%, transparent);
  pointer-events: auto;
}

.reading-picker-menu[hidden] {
  display: none !important;
}

.reading-picker-option {
  display: flex;
  align-items: flex-start;
  gap: 0;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.82rem;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}

.reading-picker-option:focus-visible {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  outline: none;
}
@media (hover: hover) {
  .reading-picker-option:hover {
    background: color-mix(in srgb, var(--ink) 6%, transparent);
    outline: none;
  }
}

.reading-picker-ipa {
  font-family: var(--font-ipa, var(--font-mono));
  font-size: 0.9em;
}

/* Lesart row: Silbenstrich + Lautschrift, no repeated orthography. */
.reading-option-stack {
  pointer-events: none;
}
.reading-option-stack .syl-track,
.reading-option-stack .syl-word {
  flex-wrap: nowrap;
}
.reading-option-stack .syl-cell {
  flex: 0 0 auto;
  width: auto;
  min-width: 0.85em;
  max-width: none;
}
.reading-option-stack .syl-brace {
  margin-top: 0;
  margin-bottom: calc(var(--brace-ipa-air-eff) - var(--ipa-ink-above, 0px));
  --brace-color: var(--ink);
}
.reading-option-stack .syl-ipa {
  width: auto;
  min-width: max-content;
  display: block;
  text-align: center;
}
.reading-option-stack .syl-cell.is-stress .syl-ipa {
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
}
.reading-option-stack .syl-cell:not(.is-stress) .syl-ipa {
  color: color-mix(in srgb, var(--ink) 46%, transparent);
  -webkit-text-fill-color: color-mix(in srgb, var(--ink) 46%, transparent);
}
.reading-option-stack .syl-ipa.is-ph {
  width: 1.4em;
  min-width: 1.4em;
  height: 1.35em;
  background: linear-gradient(
      color-mix(in srgb, var(--ink) 22%, transparent),
      color-mix(in srgb, var(--ink) 22%, transparent)
    )
    center / 100% 0.22em no-repeat;
}

.units-readings .reading-ipa {
  margin-top: calc(var(--brace-ipa-air-eff) - var(--ipa-ink-above, 0px));
  padding-top: 0;
  pointer-events: auto;
  max-width: 100%;
}

.units-readings .reading-ipa .analysis-stack.is-ipa-only.is-underlay .syl-ipa,
.units-readings .reading-ipa .ipa-fallback {
  margin-top: 0;
}

.units-readings .unit-choice:not(.is-active) {
  opacity: 0.55;
}

.units-readings .unit-choice.is-active {
  opacity: 1;
}

.unit-choice:not(.is-solo):not(.is-active) {
  opacity: 0.55;
}

.unit-choice:not(.is-solo):not(.is-active):focus-visible {
  opacity: 0.88;
  outline: none;
}
@media (hover: hover) {
  .unit-choice:not(.is-solo):not(.is-active):hover {
    opacity: 0.88;
    outline: none;
  }
}

.unit-choice.is-active {
  opacity: 1;
  background: transparent;
}

.rhyme {
  /*
    Between blocks: --rhyme-air (consistent).
    Within a block: equal --brace-air above and below the syllable marks.
    Score sits in a fixed right column (under chrome) so % stays assigned.
    Vertically center % against the whole analysis stack (wrap-aware).
  */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--score-col);
  grid-template-areas: "main meta";
  column-gap: 0.35rem;
  align-items: center;
  padding-top: calc(var(--rhyme-air-eff) + 0.14rem);
  padding-bottom: calc(var(--rhyme-air-eff) + 0.14rem);
  padding-left: var(--content-inline);
  padding-right: var(--content-inline);
  border-bottom: 0;
  overflow: visible;
}

/* Rows stay painted. content-visibility:auto left blank bands on
   iPhone and Mac while the next slice was still offscreen. */
html[data-col-frozen="1"] #rhymes .rhyme {
  content-visibility: visible;
  contain: none;
}

.rhyme-sentinel {
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}

.rhyme-end {
  position: relative;
  margin: 0;
  padding: 0;
  height: calc(var(--divider-offset, 0rem) + 1px);
  border: 0;
  pointer-events: none;
}

.rhyme-end::before {
  content: "";
  position: absolute;
  top: var(--divider-offset, 0rem);
  left: var(--content-inline);
  right: var(--content-inline);
  height: 1px;
  background: color-mix(in srgb, var(--line) 55%, transparent);
}

#rhymes .rhyme-main {
  cursor: pointer;
}

.rhyme.is-ghost {
  width: 100%;
  box-sizing: border-box;
  pointer-events: none;
  user-select: none;
}

.rhyme.is-ghost .rhyme-main {
  cursor: default;
  justify-content: stretch;
}

/* Same grid as a real hit: word+IPA column | % column, full row width. */
.rhyme.is-ghost .rhyme-main,
.rhyme.is-ghost .rhyme-meta {
  align-self: stretch;
  justify-self: stretch;
  min-width: 0;
  width: auto;
  min-height: calc((var(--input-text-band) + var(--input-analysis-band)) / var(--input-scale, 1));
}

/* A real row is never shorter than its % hit target (.score-hit). */
.rhyme.is-ghost .rhyme-meta {
  min-height: max(
    var(--hit-min-h),
    calc((var(--input-text-band) + var(--input-analysis-band)) / var(--input-scale, 1))
  );
}

/* Ghost = a real hit drawn in placeholder ink: word bar on the text line,
   Silbenstrich, shorter IPA bar (all from the shared text edge), and a %
   bar whose right edge is the score's right edge. Row box = field box. */
.rhyme.is-ghost .rhyme-info {
  width: auto;
  max-width: 100%;
  align-self: flex-start;
}

.rhyme.is-ghost .rhyme-meta-end {
  display: flex;
  width: 100%;
  justify-content: flex-end;
}

/* Typical word lengths (in the result font's em), by list position. Set
   on every row so a fading ghost veil inside a real row keeps its width. */
#rhymes > .rhyme:nth-child(6n + 1),
#start-demo-rows > .rhyme:nth-child(6n + 1) { --ghost-w: 2.4em; }
#rhymes > .rhyme:nth-child(6n + 2),
#start-demo-rows > .rhyme:nth-child(6n + 2) { --ghost-w: 3.3em; }
#rhymes > .rhyme:nth-child(6n + 3),
#start-demo-rows > .rhyme:nth-child(6n + 3) { --ghost-w: 1.9em; }
#rhymes > .rhyme:nth-child(6n + 4),
#start-demo-rows > .rhyme:nth-child(6n + 4) { --ghost-w: 2.8em; }
#rhymes > .rhyme:nth-child(6n + 5),
#start-demo-rows > .rhyme:nth-child(6n + 5) { --ghost-w: 3.7em; }
#rhymes > .rhyme:nth-child(6n),
#start-demo-rows > .rhyme:nth-child(6n) { --ghost-w: 2.2em; }

.ghost-slot {
  --ghost-ink: #2a2a2a;
  --ghost-ink-soft: #222;
  display: flex;
  min-width: 0;
  box-sizing: border-box;
  border-radius: 0;
  /* Quiet "loading" breath while a page is in flight (off for reduced motion). */
  animation: ghost-breathe 1.4s ease-in-out infinite;
}

/* Word block = a real .analysis-stack.is-primary (ortho line, brace, IPA
   line); only the ink is replaced by bars. */
.ghost-word {
  flex-direction: column;
  width: max-content;
}

.ghost-cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: fit-content;
}

.ghost-ortho {
  display: block;
  width: var(--ghost-w, 2.6em);
  /* = the real ortho line box (line-height 1.2 of the result font). */
  height: 1.2em;
  /* Bar over the glyph body (x-height to cap), not the whole line box. */
  background: linear-gradient(var(--ghost-ink), var(--ghost-ink)) 0 62% / 100% 0.56em no-repeat;
}

.ghost-word .syl-brace {
  --brace-color: var(--ghost-ink);
  opacity: 1;
}

.ghost-ipa {
  display: block;
  width: 62%;
  /* = the real IPA line box (line-height 1.35 of the IPA font). */
  height: 1.35em;
  background: linear-gradient(var(--ghost-ink-soft), var(--ghost-ink-soft)) 0 55% / 100% 0.5em no-repeat;
}

/* Same box as .score-hit, so the bar ends where "98%" ends. */
.ghost-score {
  align-items: center;
  justify-content: flex-end;
  min-width: 44px;
  min-height: var(--hit-min-h);
  padding: 0.16rem 0.28rem;
  font-size: calc(0.78rem * var(--ui-text-scale, 1.1));
}

.ghost-pct {
  display: block;
  width: 2.15em;
  height: 0.72em;
  background: var(--ghost-ink);
}

@keyframes ghost-breathe {
  0%,
  100% { opacity: 1; }
  50% { opacity: 0.55; }
}

html[data-results-anim] #rhymes .rhyme.is-ghost {
  animation: none !important;
}

/* Reveal choreography (docs/ui-layout.md): a hit takes its ghost's slot, so
   the row box never moves. Its content rises in (opacity + 6px) while the
   ghost's bars, kept as a veil on top, fade out. --enter-delay comes from JS
   (chronological, top to bottom). Only opacity/transform animate. */
html[data-results] #rhymes > .rhyme.is-entering {
  animation: none !important;
}

html[data-results] #rhymes > .rhyme.is-entering > :not(.rhyme-ghost-veil) {
  animation: rhyme-rise 0.22s cubic-bezier(0.22, 1, 0.36, 1) both !important;
  animation-delay: var(--enter-delay, 0s) !important;
}

html[data-results] #rhymes > .rhyme > .rhyme.rhyme-ghost-veil {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  margin: 0;
  pointer-events: none;
  content-visibility: visible;
  animation: ghost-veil-out 0.18s ease-out both !important;
  animation-delay: var(--enter-delay, 0s) !important;
}

@keyframes rhyme-rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes ghost-veil-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

.word-menu {
  position: fixed;
  z-index: 70;
  width: max-content;
  min-width: 11rem;
  max-width: calc(100vw - 1rem);
  box-sizing: border-box;
  margin: 0;
  padding: 0.28rem;
  display: flex;
  flex-direction: column;
  gap: 0.08rem;
  background: color-mix(in srgb, var(--ink) 10%, var(--paper));
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--ink) 42%, transparent);
  border-radius: var(--pop-radius);
  box-shadow: 0 0.65rem 1.6rem color-mix(in srgb, #000 55%, transparent);
}

.word-menu[hidden] {
  display: none !important;
}

.word-menu-item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 0.35rem 0.7rem;
  border: 0;
  border-radius: 2px;
  background: transparent;
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
  font: inherit;
  font-size: 0.92rem;
  font-weight: 560;
  line-height: 1.2;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.word-menu-icon {
  display: inline-flex;
  flex: 0 0 1rem;
  width: 1rem;
  height: 1rem;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}

.word-menu-icon svg {
  width: 1rem;
  height: 1rem;
  display: block;
}

.word-menu-label {
  flex: 1 1 auto;
  min-width: 0;
}

.word-menu-item:focus-visible .word-menu-icon {
  color: var(--ink);
}
@media (hover: hover) {
  .word-menu-item:hover .word-menu-icon {
    color: var(--ink);
  }
}

.word-menu-item:focus-visible {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  outline: none;
}
@media (hover: hover) {
  .word-menu-item:hover {
    color: var(--ink);
    background: color-mix(in srgb, var(--ink) 6%, transparent);
    outline: none;
  }
}

.rhyme-info {
  position: relative;
  align-self: flex-start;
  width: max-content;
  max-width: 100%;
}

.form-chip {
  position: absolute;
  top: 50%;
  left: 100%;
  display: inline-flex;
  align-items: center;
  gap: 0.08rem;
  margin: 0 0 0 0.1rem;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.72rem;
  line-height: 1;
  cursor: pointer;
  transform: translateY(-50%);
  z-index: 2;
}

.form-chip-arrow {
  width: 1.28rem;
  height: 1.28rem;
  display: block;
}

.form-chip.is-open .form-chip-arrow {
  transform: rotate(90deg);
}

.form-chip-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.78em;
}

.form-chip:focus-visible {
  color: var(--ink);
}
@media (hover: hover) {
  .form-chip:hover {
    color: var(--ink);
  }
}

.form-siblings {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.form-siblings[hidden] {
  display: none;
}

.form-siblings > .rhyme {
  padding-left: 0;
  padding-right: 0;
}

.algo-variant-banner {
  display: none;
}

.algo-variant-banner[data-variant="0.300"] {
  color: var(--faint);
}

.speak-pip {
  position: absolute;
  left: calc(100% + 0.38rem);
  top: 50%;
  width: 0.9rem;
  height: 0.9rem;
  margin-top: -0.45rem;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  opacity: 0;
  pointer-events: none;
  transition: opacity 280ms ease;
}

.speak-pip svg {
  width: 0.85rem;
  height: 0.85rem;
  display: block;
}

#rhymes .rhyme.is-speaking .speak-pip,
#rhymes .rhyme-slot.is-speaking .speak-pip {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .speak-pip {
    transition: none;
  }
}

.rhyme.is-combo {
  display: flex;
  align-items: stretch;
  gap: 0;
}

/* Product lattice: one readable line + score, like a rhyme row. */
.rhyme.is-combo.is-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--score-col);
  grid-template-areas: "main meta";
  column-gap: 0.35rem;
  align-items: center;
  outline: none;
}

.rhyme.is-combo.is-line .name {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* The result-word menu is the affordance. A row outline is not a menu. */

.rhyme.is-lattice.is-cross-word:not(.is-line) {
  outline: 1px solid color-mix(in srgb, var(--accent, #8f8) 35%, transparent);
  outline-offset: 2px;
  border-radius: 2px;
}

.rhyme.is-lattice:not(.is-line) {
  position: relative;
  flex-wrap: wrap;
  row-gap: 0.2rem;
}

html[data-debug="0"] .lattice-badges {
  display: none !important;
}

.lattice-badges {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.45rem;
  align-items: center;
  min-width: 0;
  font-size: 0.68rem;
  line-height: 1.2;
  color: var(--muted);
  opacity: 0.9;
}

.lattice-badge {
  display: inline-block;
  padding: 0.05rem 0.28rem;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  border-radius: 2px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.lattice-badge.is-fingerprint {
  font-family: ui-monospace, "Iosevka Aile", monospace;
  color: var(--ink);
  opacity: 0.85;
}

.lattice-badge.is-variants {
  opacity: 0.7;
}

.lattice-badge.is-semantic {
  border-color: color-mix(in srgb, var(--accent, #8f8) 45%, transparent);
  color: var(--ink);
}

.lattice-badge.is-top {
  opacity: 0.75;
}

.rhyme.is-lattice.is-semantic:not(.is-line) {
  outline: 1px solid color-mix(in srgb, var(--accent, #8f8) 28%, transparent);
  outline-offset: 2px;
}

.rhyme-slot.is-span-merge {
  flex: 1.35 1 0;
}

.rhyme-slot.is-span-merge::before {
  content: "⟷";
  position: absolute;
  left: 0.15rem;
  top: 0.1rem;
  font-size: 0.65rem;
  opacity: 0.55;
  pointer-events: none;
}

/* Shrink the ink inside the slot. The slot itself keeps its share of the row. */
.rhyme-slot .rhyme-main,
.rhyme-slot .rhyme-meta {
  zoom: var(--combo-fit);
}

html[data-word-combo="1"] #rhymes .rhyme.is-combo .syl-track,
html[data-word-combo="1"] #rhymes .rhyme.is-combo .syl-word {
  flex-wrap: nowrap;
  max-width: none;
}

.rhyme-slot {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "main meta";
  column-gap: 0.55rem;
  align-items: center;
  overflow: visible;
}

.rhyme-slot + .rhyme-slot {
  margin-left: 0.75rem;
  padding-left: 0.75rem;
  border-left: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
}

/*
  Word lists while the search runs (js/ui/combo-progress.js, docs/ui-layout.md §4b).
  Head: one cell per query word, same share and gutter as the slots below.
  Rows: a ghost slot per word until that word's column is in.
*/
.combo-head {
  display: flex;
  align-items: flex-end;
  box-sizing: border-box;
  min-height: 1.6rem;
  padding: 0 var(--content-inline);
  pointer-events: none;
  user-select: none;
}

.combo-head-cell {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: calc(0.7rem * var(--ui-text-scale, 1.1));
  line-height: 1.6rem;
  letter-spacing: 0.02em;
  color: var(--muted);
}

.combo-head-cell + .combo-head-cell {
  margin-left: 0.75rem;
  padding-left: 0.75rem;
  border-left: 1px solid transparent;
}

.combo-head-word {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.combo-head-cell.is-done .combo-head-word {
  color: color-mix(in srgb, var(--ink) 72%, var(--muted));
}

/* The word being ranked right now: its head breathes, a dot shows activity. */
.combo-head-cell.is-active .combo-head-word {
  color: var(--ink);
}

.combo-head-cell.is-active::after {
  content: "";
  flex: none;
  width: 0.3rem;
  height: 0.3rem;
  border-radius: 50%;
  background: var(--ink);
  animation: combo-head-pulse 1.1s ease-in-out infinite;
}

@keyframes combo-head-pulse {
  0%,
  100% { opacity: 0.25; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1); }
}

.rhyme-slot[hidden],
.combo-head-cell[hidden] {
  display: none !important;
}

/* Waiting columns: quiet skeleton; only the active column's ghosts breathe. */
.rhyme-slot.is-ghost-slot,
.rhyme-slot.is-empty-slot {
  pointer-events: none;
  user-select: none;
}

.rhyme-slot.is-ghost-slot .ghost-slot {
  animation: none;
}

.rhyme-slot.is-ghost-slot.is-active-col .ghost-slot {
  animation: ghost-breathe 1.4s ease-in-out infinite;
}

/* Same height as a real cell: ortho + brace + IPA bands, or the % tap target. */
.rhyme-slot.is-ghost-slot > .rhyme-main,
.rhyme-slot.is-ghost-slot > .rhyme-meta,
.rhyme-slot-veil > .rhyme-main,
.rhyme-slot-veil > .rhyme-meta {
  min-height: calc((var(--input-text-band) + var(--input-analysis-band)) / var(--input-scale, 1));
}

.rhyme-slot.is-ghost-slot > .rhyme-meta,
.rhyme-slot-veil > .rhyme-meta {
  min-height: max(
    var(--hit-min-h),
    calc((var(--input-text-band) + var(--input-analysis-band)) / var(--input-scale, 1))
  );
}

.rhyme-slot.is-ghost-slot .rhyme-info,
.rhyme-slot-veil .rhyme-info {
  width: auto;
  max-width: 100%;
  align-self: flex-start;
}

.rhyme-slot.is-ghost-slot .rhyme-main,
.rhyme-slot-veil .rhyme-main {
  cursor: default;
  justify-content: center;
}

/* A cell rolls in like a row (§4 Reveal): content rises, the ghost veil fades. */
html[data-results] #rhymes .rhyme-slot.is-entering > :not(.rhyme-slot-veil) {
  animation: rhyme-rise 0.22s cubic-bezier(0.22, 1, 0.36, 1) both !important;
  animation-delay: var(--enter-delay, 0s) !important;
}

.rhyme-slot-veil {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "main meta";
  column-gap: 0.55rem;
  align-items: center;
  padding-left: inherit;
  pointer-events: none;
}

html[data-results] #rhymes .rhyme-slot > .rhyme-slot-veil {
  animation: ghost-veil-out 0.18s ease-out both !important;
  animation-delay: var(--enter-delay, 0s) !important;
}

/* Error column: one small card in the first row, the rest of the column blank. */
.rhyme-slot.is-error-slot {
  display: flex;
  align-items: center;
}

.combo-slot-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 0.5rem;
  min-width: 0;
  font-size: calc(0.72rem * var(--ui-text-scale, 1.1));
  line-height: 1.25;
  color: var(--muted);
}

.combo-retry {
  min-height: var(--hit-min-h);
  margin: 0;
  padding: 0 0.15rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  cursor: pointer;
}

.combo-retry:disabled {
  opacity: 0.45;
  cursor: default;
}

.combo-retry:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .combo-head-cell.is-active::after,
  .rhyme-slot.is-ghost-slot.is-active-col .ghost-slot {
    animation: none;
  }

  html[data-results] #rhymes .rhyme-slot.is-entering > * {
    animation: none !important;
  }

  html[data-results] #rhymes .rhyme-slot > .rhyme-slot-veil {
    display: none;
  }
}

.combo-gap {
  position: absolute;
  z-index: 3;
  width: 1px;
  margin-left: -0.5px;
  background: color-mix(in srgb, var(--line) 55%, transparent);
  pointer-events: none;
}

.rhyme-main {
  grid-area: main;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.rhyme-slot > .rhyme-main {
  /* Slot grid is name | %, not the one-line --score-col. */
  min-width: 0;
}

/*
  E-014 Auftakt hang: --auftakt-shift is that row's prefix + gap.
  margin-left comes from the rule below so the match stays under the query.
  No shared max-prefix column, no boundary hairline.
*/
.rhyme-main.has-auftakt-hang .analysis-stack,
.rhyme-main.has-auftakt-hang .name-line {
  overflow: visible;
}

/* Shift lives on the custom prop so a later clip pass cannot drop the
   margin while leaving .is-auftakt gray in the main column. */
.rhyme-main.has-auftakt-hang {
  margin-left: calc(-1 * var(--auftakt-shift, 0px));
}

/* E-014 Auftakt cue: quieter ink. The match boundary is the gap only. */
.syl-cell.is-auftakt .syl-ortho {
  color: color-mix(in srgb, var(--muted) 78%, transparent);
}

.analysis-stack.is-primary .syl-cell.is-auftakt .syl-ortho {
  color: color-mix(in srgb, var(--muted) 92%, var(--ink));
}

.syl-cell.is-auftakt .syl-ipa {
  color: color-mix(in srgb, var(--muted) 62%, transparent);
}

.syl-cell.is-auftakt .syl-brace {
  opacity: 0.28;
  --brace-color: color-mix(in srgb, var(--line) 55%, transparent);
}

.syl-cell.is-auftakt.is-stress .syl-brace {
  opacity: 0.4;
}

.syl-cell.is-auftakt-end {
  /* Visual Zwischenraum — hang shift includes this so match stays under query. */
  margin-right: var(--auftakt-gap);
}

.name-line.has-auftakt-hang .auftakt-hang {
  color: color-mix(in srgb, var(--muted) 92%, var(--ink));
  margin-right: var(--auftakt-gap);
}

.rhyme-features {
  display: none;
}

.rhyme-meta {
  grid-area: meta;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0;
  flex: 0 0 auto;
  width: 100%;
  padding-top: 0;
}

.rhyme-slot > .rhyme-meta {
  width: auto;
  justify-self: end;
  align-self: center;
}

.rhyme-meta-end {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
}

.rhyme-feat-chip {
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  box-sizing: border-box;
  margin: 0 0.18rem 0 0;
  padding: 0.1rem 0.22rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.06rem;
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 2px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  color: color-mix(in srgb, var(--ink) 55%, transparent);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.rhyme-feat-chip:focus {
  outline: none;
}

.rhyme-feat-chip:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--ink) 55%, transparent);
  outline-offset: 2px;
}

.hit-features {
  display: inline-flex;
  align-items: center;
  gap: 0.08rem;
  margin: 0;
  color: inherit;
}

.feat-chip-pop {
  position: fixed;
  z-index: 72;
  width: max-content;
  min-width: 14.5rem;
  max-width: min(22rem, calc(100vw - 1rem));
  box-sizing: border-box;
  margin: 0;
  padding: 0.45rem 0.5rem 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  background: color-mix(in srgb, var(--ink) 10%, var(--paper));
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--ink) 42%, transparent);
  border-radius: var(--pop-radius);
}

.feat-chip-pop[hidden] {
  display: none;
}

.feat-chip-pop-title {
  margin: 0 0 0.12rem;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.feat-chip-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 0.55rem;
  min-height: 2.2rem;
  margin: 0;
  padding: 0.1rem 0;
  font-size: 0.78rem;
  cursor: pointer;
}

.feat-chip-copy {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  line-height: 1.25;
}

.feat-chip-copy strong {
  font-weight: 600;
}

.feat-chip-copy span {
  color: var(--muted);
  font-size: 0.72rem;
}

.feat-chip-row .hit-feature {
  color: color-mix(in srgb, var(--ink) 60%, transparent);
}

.feat-chip-row input {
  flex: 0 0 auto;
}

.hit-feature {
  display: inline-flex;
  width: 0.78rem;
  height: 0.78rem;
  flex: 0 0 auto;
}

.hit-feature svg {
  width: 100%;
  height: 100%;
  display: block;
}

.score-bar {
  --p: 0;
  --score-fill: var(--bad);
  width: 2.35rem;
  height: 0.3rem;
  border-radius: 1px;
  display: inline-block;
  flex: 0 0 auto;
  vertical-align: middle;
  background: linear-gradient(
    to right,
    var(--score-fill) calc(var(--p) * 1%),
    color-mix(in srgb, var(--line) 60%, transparent) 0
  );
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent);
}

.score-pct {
  font-family: var(--font-mono);
  font-size: calc(0.78rem * var(--ui-text-scale, 1.1));
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  line-height: 1;
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--score-fill, var(--muted));
}

/* Quiet hit target on the percent. Same ink as the old span. */
.score-hit {
  box-sizing: border-box;
  margin: 0;
  padding: 0.16rem 0.28rem;
  border: 0;
  border-radius: 2px;
  background: transparent;
  cursor: pointer;
  min-width: 44px;
  min-height: var(--hit-min-h);
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  flex: 0 0 auto;
  appearance: none;
  -webkit-appearance: none;
}

.score-hit:focus {
  outline: none;
}

.score-hit:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--ink) 55%, transparent);
  outline-offset: 2px;
}

.rhyme-combo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05rem;
  height: 1.05rem;
  color: var(--muted);
  flex: 0 0 auto;
}

.rhyme-combo svg {
  width: 100%;
  height: 100%;
  display: block;
}

.rhyme .name {
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  /* Tight line box → padding-top measures to letter tops, not half-leading */
  line-height: 1;
}

.rhyme .sub {
  direction: ltr;
  unicode-bidi: isolate;
  margin-top: 0;
}

/*
  Results reveal: never flash mid-render. pending = invisible buffer;
  ready = opacity + staggered row roll-out.
  Input analysis (.panel-units) stays visible — it is the live underlay while
  typing (hero / empty / pending). Only the rhyme list is gated.
*/
html[data-results="pending"] #results {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.16s ease;
}

html[data-results="ready"] #results {
  opacity: 1;
  transition: opacity 0.22s ease;
}

html[data-results="empty"] #results {
  opacity: 0;
  transition: opacity 0.16s ease;
}

html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme {
  animation: result-row-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}

html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(1) { animation-delay: 0.02s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(2) { animation-delay: 0.05s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(3) { animation-delay: 0.08s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(4) { animation-delay: 0.11s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(5) { animation-delay: 0.14s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(6) { animation-delay: 0.17s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(7) { animation-delay: 0.2s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(8) { animation-delay: 0.23s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(9) { animation-delay: 0.26s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(10) { animation-delay: 0.29s; }
html[data-results-anim="cascade-down"][data-results="ready"] #rhymes .rhyme:nth-child(n + 11) { animation-delay: 0.32s; }

/* Staggered opacity only — no horizontal motion. */
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme {
  animation: result-fade 0.32s ease both;
}
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(1) { animation-delay: 0.02s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(2) { animation-delay: 0.05s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(3) { animation-delay: 0.08s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(4) { animation-delay: 0.11s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(5) { animation-delay: 0.14s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(6) { animation-delay: 0.17s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(7) { animation-delay: 0.2s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(8) { animation-delay: 0.23s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(9) { animation-delay: 0.26s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(10) { animation-delay: 0.29s; }
html[data-results-anim="cascade-fade"][data-results="ready"] #rhymes .rhyme:nth-child(n + 11) { animation-delay: 0.32s; }

/* Left→right uncover (clip), glyphs stay put. */
html[data-results-anim="wipe-right"][data-results="ready"] #rhymes .rhyme {
  animation: result-wipe-right 0.38s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: 0s !important;
}

html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme {
  animation: result-wipe-right 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(1) { animation-delay: 0.02s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(2) { animation-delay: 0.05s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(3) { animation-delay: 0.08s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(4) { animation-delay: 0.11s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(5) { animation-delay: 0.14s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(6) { animation-delay: 0.17s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(7) { animation-delay: 0.2s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(8) { animation-delay: 0.23s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(9) { animation-delay: 0.26s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(10) { animation-delay: 0.29s; }
html[data-results-anim="wipe-right-stagger"][data-results="ready"] #rhymes .rhyme:nth-child(n + 11) { animation-delay: 0.32s; }

html[data-results-anim="fade"][data-results="ready"] #rhymes .rhyme {
  animation: result-fade 0.28s ease both;
  animation-delay: 0s !important;
}

html[data-results-anim="fade"][data-results="ready"] #rhymes .rhyme:not(.is-entering) {
  animation: none !important;
}

html[data-results-anim="blur-in"][data-results="ready"] #rhymes .rhyme {
  animation: result-blur-in 0.36s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: 0s !important;
}

html[data-results-anim="none"][data-results="ready"] #rhymes .rhyme {
  animation: none !important;
}

@keyframes result-row-in {
  from {
    opacity: 0;
    transform: translateY(-0.35rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes result-wipe-right {
  from {
    clip-path: inset(0 100% 0 0);
    opacity: 1;
  }
  to {
    clip-path: inset(0 0 0 0);
    opacity: 1;
  }
}

@keyframes result-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes result-blur-in {
  from {
    opacity: 0;
    filter: blur(4px);
  }
  to {
    opacity: 1;
    filter: blur(0);
  }
}

/* Follow-up crossfade: only the rhyme list, underlay stays put. */
#rhymes {
  transition: opacity 0.16s ease;
}

#rhymes.is-crossfading {
  opacity: 0;
}

/* Entrance plays once per row. A later page must not replay it on rows
   that are already on screen (that read as a full list reload). */
#rhymes > .rhyme:not(.is-entering) {
  animation: none !important;
}

#rhymes > .rhyme.is-page.is-entering {
  animation-delay: var(--enter-delay, 0s) !important;
}

.empty { color: var(--muted); }

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

/* Opus skin — revert with html[data-ui="classic"] / pref lyrika-uiDesign. */
html[data-ui="opus"] {
  --line-soft: color-mix(in srgb, var(--line) 55%, transparent);
  --faint: color-mix(in srgb, var(--muted) 72%, transparent);
  --focus: color-mix(in srgb, var(--ink) 70%, transparent);
  --score-hi: var(--ramp-green);
  --score-lo: var(--ramp-red);
  /* Trust (braces + IPA) sits on the score ramp: clean green, hybrid yellow, algo red. */
  --trust-clean: var(--ramp-green);
  --trust-hybrid: var(--ramp-yellow);
  --trust-algo: var(--ramp-red);
  --trust-good: var(--trust-clean);
  --trust-warn: var(--trust-hybrid);
  --trust-bad: var(--trust-algo);
  --fs-ipa: calc(0.66rem * var(--ui-text-scale, 1.1));
  --dur-1: 140ms;
  --dur-2: 220ms;
  --dur-3: 340ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --hit: 44px;
}

/* Gray paper surfaces only when Ultra Dark is off (default stays black). */
html[data-ui="opus"]:not([data-ultra]) {
  --surface-1: #1e1e1e;
  --surface-2: #242424;
}

/* Ultra Dark: gray surfaces become black. Ink, score, and trust stay. */
html[data-ultra="1"],
html[data-ultra="1"][data-ui="opus"] {
  --bg: #000000;
  --paper: #000000;
  --surface-1: #000000;
  --surface-2: #000000;
  --chip: #0a0a0a;
}

html[data-ui="opus"] .lex-load-brand {
  display: none;
}

/* Trust buckets. Solid strokes — stress is brightness, not a second color. */
html[data-ui="opus"] .syl-brace {
  background: linear-gradient(var(--brace-color), var(--brace-color))
    center / 100% var(--brace-stroke) no-repeat;
  height: var(--brace-slot);
  border: 0;
  opacity: 0.42;
}

html[data-ui="opus"] .syl-cell.is-stress .syl-brace {
  opacity: 1;
}

html[data-ui="opus"] .syl-cell.is-auftakt .syl-brace {
  opacity: 0.28;
}

html[data-ui="opus"] .syl-cell.is-auftakt.is-stress .syl-brace {
  opacity: 0.4;
}

html[data-ui="opus"] .analysis-stack[data-trust-syl="clean"] .syl-brace,
html[data-ui="opus"] .analysis-stack.q-syl-good .syl-brace {
  --brace-color: var(--trust-clean);
}
html[data-ui="opus"] .analysis-stack[data-trust-syl="hybrid"] .syl-brace,
html[data-ui="opus"] .analysis-stack.q-syl-warn .syl-brace {
  --brace-color: var(--trust-hybrid);
}
html[data-ui="opus"] .analysis-stack[data-trust-syl="algo"] .syl-brace,
html[data-ui="opus"] .analysis-stack.q-syl-bad .syl-brace {
  --brace-color: var(--trust-algo);
}

html[data-ui="opus"] .syl-ipa,
html[data-ui="opus"] .ipa-fallback {
  font-size: var(--fs-ipa);
  letter-spacing: 0.02em;
}

html[data-ui="opus"] .analysis-stack[data-trust-ipa="clean"] .syl-ipa,
html[data-ui="opus"] .analysis-stack[data-trust-ipa="clean"] .ipa-fallback,
html[data-ui="opus"] .analysis-stack.q-ipa-good .syl-ipa,
html[data-ui="opus"] .analysis-stack.q-ipa-good .ipa-fallback {
  color: var(--trust-clean);
}
html[data-ui="opus"] .analysis-stack[data-trust-ipa="hybrid"] .syl-ipa,
html[data-ui="opus"] .analysis-stack[data-trust-ipa="hybrid"] .ipa-fallback,
html[data-ui="opus"] .analysis-stack.q-ipa-warn .syl-ipa,
html[data-ui="opus"] .analysis-stack.q-ipa-warn .ipa-fallback {
  color: var(--trust-hybrid);
}
html[data-ui="opus"] .analysis-stack[data-trust-ipa="algo"] .syl-ipa,
html[data-ui="opus"] .analysis-stack[data-trust-ipa="algo"] .ipa-fallback,
html[data-ui="opus"] .analysis-stack.q-ipa-bad .syl-ipa,
html[data-ui="opus"] .analysis-stack.q-ipa-bad .ipa-fallback {
  color: var(--trust-algo);
}

/* White IPA (default): trust colour lives on dashes. Combined trust:
   green only when syl+IPA would both be green; yellow if either is yellow;
   red/algo still wins over the yellow-union. */
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack .syl-ipa,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack .ipa-fallback,
html[data-ipa-color="0"][data-ui="opus"] .syl-ipa,
html[data-ipa-color="0"][data-ui="opus"] .ipa-fallback {
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
}
html[data-ipa-color="0"][data-ui="opus"] .reading-option-stack .syl-cell:not(.is-stress) .syl-ipa {
  color: color-mix(in srgb, var(--ink) 46%, transparent);
  -webkit-text-fill-color: color-mix(in srgb, var(--ink) 46%, transparent);
}
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust="clean"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack.q-syl-good.q-ipa-good .syl-brace {
  --brace-color: var(--trust-clean);
}
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust="hybrid"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust-syl="hybrid"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust-ipa="hybrid"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack.q-syl-warn .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack.q-ipa-warn .syl-brace {
  --brace-color: var(--trust-hybrid);
}
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust="algo"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust-syl="algo"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack[data-trust-ipa="algo"] .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack.q-syl-bad .syl-brace,
html[data-ipa-color="0"][data-ui="opus"] .analysis-stack.q-ipa-bad .syl-brace {
  --brace-color: var(--trust-algo);
}

html[data-trust-wash="0"][data-ui="opus"] .analysis-stack .syl-brace,
html[data-trust-wash="0"][data-ui="opus"] .syl-brace,
html[data-trust-wash="0"][data-ipa-color="0"][data-ui="opus"] .analysis-stack .syl-brace {
  --brace-color: var(--ink);
}
html[data-trust-wash="0"][data-ui="opus"] .analysis-stack .syl-ipa,
html[data-trust-wash="0"][data-ui="opus"] .analysis-stack .ipa-fallback,
html[data-trust-wash="0"][data-ui="opus"] .syl-ipa,
html[data-trust-wash="0"][data-ui="opus"] .ipa-fallback,
html[data-trust-wash="0"][data-ipa-color="0"][data-ui="opus"] .analysis-stack .syl-ipa,
html[data-trust-wash="0"][data-ipa-color="0"][data-ui="opus"] .analysis-stack .ipa-fallback {
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
}
html[data-trust-wash="0"][data-ui="opus"] .reading-option-stack .syl-cell:not(.is-stress) .syl-ipa {
  color: color-mix(in srgb, var(--ink) 46%, transparent);
  -webkit-text-fill-color: color-mix(in srgb, var(--ink) 46%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-1: 0ms;
    --dur-2: 120ms;
    --dur-3: 120ms;
  }

  .site-wordmark-wrap {
    transition: none;
  }

  html[data-landing] .page.is-hero .site-wordmark-wrap.is-shown {
    opacity: 1;
    translate: none;
    transition: none;
  }

  html[data-landing] .site-wordmark-wrap,
  html[data-landing] .composer-row {
    translate: none;
    transition: none;
  }

  html[data-landing] .composer::after {
    content: none;
    display: none;
    opacity: 0;
  }

  .lex-load-track.is-indeterminate .lex-load-fill,
  .q-ghost-caret,
  .tool-btn.is-attention,
  .input-settings.is-attention,
  .rhyme.is-ghost .ghost-slot {
    animation: none;
  }

  /* New rows appear at once, no fade or stagger (JS sets no delay/veil). */
  html[data-results] #rhymes > .rhyme.is-entering,
  html[data-results] #rhymes > .rhyme.is-entering > * {
    animation: none !important;
  }

  html[data-results] #rhymes > .rhyme > .rhyme.rhyme-ghost-veil {
    display: none;
  }

  .lex-load.is-morph .lex-load-track,
  .moon-icon .moon-layer {
    transform: none;
    transition: none;
  }

  .lex-load,
  .lex-load-fill,
  #rhymes {
    transition-duration: 120ms;
  }

  .why-backdrop,
  .why-panel {
    transition: none;
  }

  .settings-menu,
  .algo-panel {
    animation: none;
  }
}

/* Why sheet — bottom sheet on phones, fixed-width panel on desktop.
   Head: score + word pair. Body: one sentence, the phoneme grid with its
   path, the cost bars that add up to the percent (E-036). docs/ui-layout.md */
html.why-sheet-open {
  overflow: hidden;
}

/* The scrim fades (background only); the sheet slides in fully opaque. */
.why-backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: color-mix(in srgb, #000 62%, transparent);
  transition: background-color 240ms ease;
}

.why-panel {
  --why-pad: 1rem;
  /* Final height; a loading sheet takes it at once (is-reserved) and keeps it. */
  --why-max-h: 88vh;
  position: relative;
  box-sizing: border-box;
  width: 100%;
  max-height: var(--why-max-h);
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0 var(--why-pad) var(--sheet-pad-bottom);
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--sheet-radius) var(--sheet-radius) 0 0;
  box-shadow: 0 -12px 40px color-mix(in srgb, #000 55%, transparent);
  transition: transform 280ms cubic-bezier(0.32, 0.72, 0, 1), opacity 200ms ease;
  font-family: var(--font-sans);
}

@supports (height: 1dvh) {
  .why-panel {
    --why-max-h: 88dvh;
  }
}

.why-panel.is-reserved {
  height: var(--why-max-h);
}

.why-panel:focus {
  outline: none;
}

.why-panel.is-dragging {
  transition: none;
}

.why-backdrop.is-entering,
.why-backdrop.is-leaving {
  background-color: transparent;
}

.why-backdrop.is-leaving {
  pointer-events: none;
}

.why-backdrop.is-entering .why-panel,
.why-backdrop.is-leaving .why-panel {
  transform: translateY(100%);
}

.why-backdrop.is-leaving .why-panel {
  transition-duration: 220ms;
  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
}

.why-grip {
  width: 2.5rem;
  height: 4px;
  margin: 0.5rem auto 0.25rem;
  border-radius: 2px;
  background: var(--line);
  touch-action: none;
}

.why-close {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  z-index: 3;
  min-width: 44px;
  min-height: 44px;
  margin: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.why-close:focus-visible {
  color: var(--ink);
  outline: 2px solid var(--score-fill, var(--ink));
  outline-offset: -4px;
}

@media (hover: hover) {
  .why-close:hover {
    color: var(--ink);
  }
}

/* The × sits beside the score only; the word pair below may use the full width. */
.why-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0 0.85rem;
  border-bottom: 1px solid var(--line);
  touch-action: pan-x;
}

.why-score {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0 2.75rem;
  text-align: center;
}

.why-pct {
  font-size: 2.4rem;
  font-weight: 650;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--score-fill, var(--ink));
}

.why-pct-label {
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Word pair: one row when it fits, else stacked with ↓ (why-viz fitCompare). */
.why-compare {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  justify-content: center;
  gap: 0.35rem 0.8rem;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

.why-block {
  flex: 0 0 auto;
  min-width: 0;
}

.why-compare.is-stacked {
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.why-compare.is-stacked .why-block {
  flex: 0 1 auto;
  max-width: 100%;
}

.why-compare.is-stacked .why-arrow {
  transform: rotate(90deg);
}

/* Last resort for a single word wider than the sheet: break between syllables. */
.why-compare.is-wrapping .why-block .syl-track,
.why-compare.is-wrapping .why-block .syl-word {
  flex-wrap: wrap;
  justify-content: center;
}

.why-block .syl-track,
.why-block .syl-word {
  flex-wrap: nowrap;
  max-width: none;
}

.why-arrow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  height: calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2);
  font-size: calc(1.1rem * var(--ui-text-scale, 1.1));
  line-height: 1;
  color: var(--muted);
}

.why-block .analysis-stack .syl-brace,
html[data-ui="opus"] .why-block .analysis-stack .syl-brace {
  --brace-color: var(--chrome);
  opacity: 0.4;
}

.why-block .syl-cell.is-stress .syl-brace,
html[data-ui="opus"] .why-block .syl-cell.is-stress .syl-brace {
  --brace-color: var(--chrome);
  opacity: 1;
}

.why-block .analysis-stack .syl-ipa,
.why-block .analysis-stack .ipa-fallback,
html[data-ui="opus"] .why-block .analysis-stack .syl-ipa,
html[data-ui="opus"] .why-block .analysis-stack .ipa-fallback {
  color: var(--ink);
}

/* The list's analysis blocks run small; in the sheet every glyph stays >= 12 px. */
.why-block .syl-ipa,
.why-block .ipa-fallback {
  font-size: max(0.85rem, 13px) !important;
}

.why-block .lang-mark {
  font-size: max(0.75rem, 12px) !important;
}

.why-phone {
  color: var(--phone-fill, var(--score-fill, var(--ink)));
}

.why-body {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding-top: 0.9rem;
}

.why-body.is-arriving {
  animation: why-fade 220ms ease both;
}

.why-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.why-origin {
  margin: 0;
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
}

.why-say {
  margin: 0;
  font-size: 0.98rem;
  line-height: 1.45;
  color: var(--ink);
  text-align: center;
  text-wrap: pretty;
}

.why-sec {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  min-width: 0;
}

.why-h {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.why-hint,
.why-note,
.why-detail-hint {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--muted);
}

.why-note.is-mismatch {
  color: var(--warn);
}

/* Phoneme grid: CSS grid of equal cells, header column and row of phones.
   Cells scale to the width (30–56 px); long words stack in syllable sections.
   Never an inner scroll area. */
.why-grid-scroll {
  max-width: 100%;
  padding: 0.25rem 0 0.15rem;
}

.why-grids {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}

.why-grid {
  --cell: 44px;
  --head: 2.25rem;
  position: relative;
  display: grid;
  grid-template-columns: var(--head) repeat(var(--cols), var(--cell));
  grid-template-rows: var(--head) repeat(var(--rows), var(--cell));
  flex: 0 0 auto;
}

.why-gc {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

.why-colh,
.why-rowh {
  position: relative;
  font-family: var(--font-ipa), var(--font-mono), sans-serif;
  font-size: 1.05rem;
  color: var(--muted);
}

.why-colh.is-stress,
.why-rowh.is-stress {
  color: var(--ink);
}

.why-stressmark {
  margin-right: 1px;
  color: var(--score-fill, var(--ink));
}

.why-cell {
  border-right: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
}

.why-cell.sb-l,
.why-colh.sb-l {
  border-left: 2px solid var(--line);
}

.why-cell.sb-t,
.why-rowh.sb-t {
  border-top: 2px solid var(--line);
}

.why-cell.is-free {
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--chrome) 22%, transparent) 0 2px,
    transparent 2px 7px
  ) !important;
}

.why-path-layer {
  position: absolute;
  left: var(--head);
  top: var(--head);
  width: calc(var(--cell) * var(--cols));
  height: calc(var(--cell) * var(--rows));
  pointer-events: none;
}

.why-path-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.why-seg {
  stroke: var(--seg, var(--score-fill));
  stroke-width: 3;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.why-seg.is-gap {
  stroke-dasharray: 0.08 0.1;
  stroke-width: 2;
}

/* Section 2+: the line comes in from the previous section (top-left). */
.why-seg.is-lead {
  opacity: 0.55;
}

.why-dot {
  position: absolute;
  width: var(--cell);
  height: var(--cell);
  min-width: 30px;
  min-height: 30px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}

.why-dot-mark {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: 0 0 0 3px var(--paper);
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.why-dot.is-sub .why-dot-mark {
  width: 16px;
  height: 16px;
  background: var(--paper);
  box-shadow: inset 0 0 0 4px var(--dot), 0 0 0 3px var(--paper);
}

.why-dot.is-gap .why-dot-mark {
  width: 10px;
  height: 10px;
  background: var(--paper);
  box-shadow: inset 0 0 0 2px var(--dot), 0 0 0 2px var(--paper);
}

.why-dot.is-free .why-dot-mark {
  background: var(--paper);
  box-shadow: inset 0 0 0 2px var(--chrome), 0 0 0 3px var(--paper);
}

.why-dot.is-on .why-dot-mark {
  transform: scale(1.35);
  box-shadow: inset 0 0 0 4px var(--dot), 0 0 0 3px var(--paper), 0 0 0 5px var(--dot);
}

.why-dot:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -6px;
  border-radius: 50%;
}

.why-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem 0.9rem;
  font-size: 0.78rem;
  color: var(--muted);
}

.why-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.why-swatch {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--sw);
}

.why-detail {
  min-height: 3.6rem;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--card-radius);
}

.why-detail-line {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3rem 0.7rem;
}

.why-detail-pair {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  font-size: 1.25rem;
}

.why-ipa {
  font-family: var(--font-ipa), var(--font-mono), sans-serif;
  color: var(--ink);
}

.why-detail-arrow {
  color: var(--dot, var(--muted));
}

.why-detail-slot {
  font-size: 0.85rem;
  color: var(--muted);
}

.why-detail-cost {
  margin-left: auto;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  color: var(--dot, var(--ink));
}

.why-detail-note {
  margin: 0.3rem 0 0;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--muted);
}

.why-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.5rem;
}

.why-chip {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.8rem;
  color: var(--muted);
}

.why-chip.is-diff {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--ink) 45%, transparent);
}

.why-chip-k {
  color: var(--muted);
}

/* Cost bars: one row per rhymeCost post; the total is the shown percent. */
.why-bars {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.why-bar {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 6px;
  align-items: baseline;
  gap: 0.3rem 0.75rem;
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 0.45rem 0.5rem;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.why-bar.is-on {
  background: color-mix(in srgb, var(--bar) 12%, transparent);
}

.why-bar:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -2px;
}

.why-bar-name {
  font-size: 0.92rem;
  line-height: 1.3;
}

.why-bar-ipa {
  margin-left: 0.45rem;
  font-family: var(--font-ipa), var(--font-mono), sans-serif;
  color: var(--bar);
}

.why-bar-num {
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  color: var(--bar);
}

.why-bar-track {
  grid-column: 1 / -1;
  height: 6px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--line) 60%, transparent);
  overflow: hidden;
}

.why-bar-fill {
  display: block;
  height: 100%;
  width: var(--w);
  border-radius: 3px;
  background: var(--bar);
  transform-origin: left center;
  animation: why-grow 380ms cubic-bezier(0.2, 0.8, 0.2, 1) var(--delay, 0ms) both;
}

.why-total {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.7rem 0.5rem 0;
  border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}

.why-total-name {
  font-size: 0.92rem;
  color: var(--muted);
}

.why-total-num {
  font-size: 0.9rem;
  color: var(--ink);
}

.why-total-pct {
  font-size: 1.25rem;
  font-weight: 650;
  color: var(--score-fill, var(--ink));
}

.why-retry {
  align-self: center;
  min-height: 44px;
  padding: 0 1.2rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.why-skel {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.why-skel-bar {
  display: block;
  height: 0.9rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--line) 70%, transparent);
  animation: why-pulse 1.2s ease-in-out infinite;
}

.why-skel.is-say {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.why-skel.is-say .why-skel-bar {
  width: 82%;
  height: 0.95rem;
}

.why-skel.is-say .why-skel-bar:nth-child(2) {
  width: 58%;
}

.why-skel.is-grid {
  padding-top: 1.9rem;
}

.why-skel.is-grid .why-skel-bar {
  width: min(100%, 15rem);
  aspect-ratio: 1;
  height: auto;
  margin: 0 auto;
  border-radius: 10px;
}

.why-skel.is-bars {
  padding-top: 1.6rem;
}

.why-skel-bar:nth-child(2) {
  width: 75%;
}

.why-skel-bar:nth-child(3) {
  width: 55%;
}

.why-phrase-meta {
  padding: 0.6rem 0.75rem;
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--card-radius);
}

.why-phrase-k {
  display: inline-block;
  min-width: 4.6rem;
  color: var(--ink);
  font-weight: 600;
}

.why-phrase-slots {
  margin: 0.35rem 0 0;
  padding: 0;
  list-style: none;
}

.why-phrase-slots li {
  display: flex;
  gap: 0.45rem;
  align-items: baseline;
}

.why-phrase-pb {
  min-width: 2.4rem;
  color: var(--ink);
}

.why-phrase-pct {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* Entrance: cells fade, path draws, dots pop. ≤ 400 ms per element. */
.why-grids.is-animated .why-cell {
  animation: why-fade 260ms ease both;
}

.why-grids.is-animated .why-seg {
  stroke-dashoffset: 0;
  animation: why-draw 320ms ease-out both;
}

.why-grids.is-animated .why-seg:not(.is-gap) {
  stroke-dasharray: 1;
}

.why-grids.is-animated .why-dot {
  animation: why-pop 240ms ease-out both;
}

@keyframes why-fade {
  from { opacity: 0; }
}

@keyframes why-draw {
  from { stroke-dashoffset: 1; opacity: 0; }
  to { stroke-dashoffset: 0; opacity: 1; }
}

@keyframes why-pop {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
}

@keyframes why-grow {
  from { transform: scaleX(0); }
}

@keyframes why-pulse {
  50% { opacity: 0.45; }
}

/* Desktop: centered panel, fixed width so it does not jump between hits. */
@media (min-width: 720px) {
  .why-backdrop {
    align-items: center;
    padding: 1.5rem;
  }

  .why-panel {
    --why-pad: 1.5rem;
    --why-max-h: min(52rem, calc(100vh - 3rem));
    width: min(36rem, calc(100vw - 3rem));
    border-bottom: 1px solid var(--line);
    border-radius: var(--sheet-radius);
    padding-bottom: 1.5rem;
    box-shadow: 0 24px 60px color-mix(in srgb, #000 55%, transparent);
    transition-duration: 200ms;
    transition-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  @supports (height: 1dvh) {
    .why-panel {
      --why-max-h: min(52rem, calc(100dvh - 3rem));
    }
  }

  .why-grip {
    visibility: hidden;
    height: 0.5rem;
    margin: 0;
  }

  .why-backdrop.is-entering .why-panel,
  .why-backdrop.is-leaving .why-panel {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }

  .why-pct {
    font-size: 2.8rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .why-backdrop,
  .why-panel {
    transition: none;
    animation: none;
  }

  .why-grid .why-cell,
  .why-grid .why-seg,
  .why-grid .why-dot,
  .why-body.is-arriving,
  .why-bar-fill,
  .why-skel-bar {
    animation: none !important;
  }
}

html[data-ipa-font] .syl-ipa,
html[data-ipa-font] .ipa-fallback,
html[data-ipa-font] .why-ph,
html[data-ipa-font] .why-ipa {
  font-weight: var(--font-ipa-weight, 400);
  font-stretch: var(--font-ipa-stretch, 100%);
  letter-spacing: 0;
}

/* Phrasenreim: load indicator = animated sweep on the input/results hairline.
   Stays visible while searching even if the text field blurs.
   Same horizontal inset as .results::before (Trennstich) — never wider. */
.phrase-search-status {
  display: none;
  position: relative;
  width: auto;
  max-width: none;
  margin: 0.55rem var(--content-inline) 0;
  height: 1px;
  min-height: 1px;
  padding: 0;
  box-sizing: border-box;
  overflow: hidden;
  background: color-mix(in srgb, var(--line) 55%, transparent);
  justify-content: unset;
  align-items: unset;
}

html.is-searching .phrase-search-status,
html.is-phrase-searching .phrase-search-status,
#root.is-phrase-searching .phrase-search-status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  border: 0;
  pointer-events: none;
}

/* Results already on screen: the hairline sweeps. This node stays for
   aria-live and must not widen the rule or open the gap under it. */
html.is-searching[data-results="ready"] .phrase-search-status,
html.is-phrase-searching[data-results="ready"] .phrase-search-status,
#root.is-phrase-searching[data-results="ready"] .phrase-search-status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  border: 0;
  pointer-events: none;
}

.phrase-search-status[hidden] {
  display: none !important;
}

/* Shard reload: same hairline as Phrasenreim. Input stays typable. */
.shard-load-status {
  display: none;
}

html.is-shard-loading .shard-load-status:not([hidden]) {
  display: block;
  position: relative;
  /* Match .results::before / .phrase-search-status width. */
  width: auto;
  max-width: none;
  margin: 0.55rem var(--content-inline) 0;
}

.shard-load-status[hidden] {
  display: none !important;
}

.shard-load-line {
  position: relative;
  height: 1px;
  min-height: 1px;
  overflow: hidden;
  background: color-mix(in srgb, var(--line) 55%, transparent);
}

html.is-shard-loading .results::before {
  opacity: 0;
}

.shard-load-slow {
  margin: 0.35rem 0 0;
  font-size: 0.75rem;
  line-height: 1.3;
  text-align: center;
  color: var(--faint);
}

.shard-load-slow[hidden] {
  display: none;
}

.phrase-search-spinner {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 28%;
  border-radius: 0;
  background: color-mix(in srgb, var(--ink) 82%, transparent);
  opacity: 1;
  transform: translateX(-120%);
  animation: phrase-load-sweep 1.15s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

@keyframes phrase-load-sweep {
  0% {
    transform: translateX(-120%);
    opacity: 0.35;
  }
  35% {
    opacity: 1;
  }
  100% {
    transform: translateX(420%);
    opacity: 0.35;
  }
}

@media (prefers-reduced-motion: reduce) {
  .phrase-search-spinner {
    animation: none;
    left: 0;
    width: 100%;
    transform: none;
    opacity: 0.7;
  }

  .q-info-caret {
    animation: none;
  }
}

.rhyme.is-phrase.is-line .name {
  font-weight: 500;
}

/* Phrase rows: mono grid; column grows to fit — never horizontal scroll. */
.rhyme.is-phrase.is-line .rhyme-info {
  min-width: 0;
  width: 100%;
  max-width: 100%;
  overflow-x: visible;
}

.rhyme.is-phrase.is-line .analysis-stack.is-primary {
  max-width: none;
}

.phrase-empty {
  margin: 0.5rem 0 0;
  color: var(--muted);
  font-size: 0.95rem;
}

/* Empty result / failed search: a visible card in the list column. */
.rhyme-state {
  margin: 0.9rem var(--content-inline) 0;
  padding: 0.85rem 0.95rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: var(--pop-radius);
  color: var(--ink);
  font-family: var(--font-ui);
  text-align: left;
}

.rhyme-state.is-error {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
}

.rhyme-state-title {
  margin: 0;
  font-size: calc(0.95rem * var(--ui-text-scale, 1.1));
  font-weight: 600;
  line-height: 1.35;
}

.rhyme-state-hint {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: calc(0.82rem * var(--ui-text-scale, 1.1));
  line-height: 1.45;
}

.rhyme-state-retry {
  margin-top: 0.75rem;
  min-height: 44px;
  padding: 0 1.1rem;
  border: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: calc(0.85rem * var(--ui-text-scale, 1.1));
  cursor: pointer;
}

.rhyme-state-retry:focus-visible {
  border-color: var(--chrome);
}
@media (hover: hover) {
  .rhyme-state-retry:hover {
    border-color: var(--chrome);
  }
}

/* Phrasenreim: true monospace (Source Code Pro) — not Iosevka Aile (proportional). */
.phrase-source-strip,
.phrase-grid,
.phrase-grid .analysis-stack,
.phrase-grid .syl-ortho,
.phrase-grid .syl-ipa,
.phrase-grid .syl-brace,
.phrase-source-strip .analysis-stack,
.phrase-source-strip .syl-ortho,
.phrase-source-strip .syl-ipa,
.phrase-source-strip .syl-brace,
.phrase-col-gap {
  font-family: "Source Code Pro", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-variant-ligatures: none;
  font-feature-settings: "calt" 0, "liga" 0;
  letter-spacing: 0;
}

.phrase-source-strip {
  display: block;
  margin: 0 0 0.65rem;
  padding-left: var(--content-inline);
  padding-right: calc(var(--content-inline) + var(--score-col, 3.2rem));
  color: var(--muted);
  font-size: 1em;
  line-height: 1.35;
  overflow-x: visible;
}

.phrase-grid {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  white-space: pre;
  max-width: none;
}

.phrase-source-strip .phrase-col.is-lead {
  color: var(--muted);
  opacity: 0.78;
}

.phrase-source-strip .phrase-col.is-chunk {
  color: var(--ink);
  opacity: 1;
}

.phrase-col {
  display: inline-block;
  box-sizing: border-box;
  flex: 0 0 auto;
  overflow: visible;
  white-space: nowrap;
  vertical-align: top;
}

.phrase-col.is-spacer {
  color: transparent;
  user-select: none;
  pointer-events: none;
  min-height: 1.2em;
}

.phrase-col-gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-start;
  width: 1ch;
  flex: 0 0 1ch;
  /* Match ortho line only — never the full syl+IPA stack (that pushed "-" high). */
  height: 1.2em;
  line-height: 1.2;
  color: inherit;
  user-select: none;
  position: relative;
}

.phrase-col-gap.is-hyphen {
  color: var(--ink);
  opacity: 1;
  font-weight: 600;
}

/* Word group wrapper — hyphens mark belonging. */
.phrase-span {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  flex: 0 0 auto;
  color: inherit;
}

.phrase-syl {
  display: block;
  min-width: 0;
  max-width: 100%;
}

.phrase-syl.analysis-stack {
  gap: 0;
  outline: none;
  box-shadow: none;
}

.phrase-syl .syl-cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  min-width: 0;
}

.phrase-syl .syl-ortho {
  display: block;
  font-family: inherit;
  white-space: pre;
  color: inherit;
  line-height: 1.2;
  height: 1.2em;
}

.phrase-syl .syl-ipa {
  /* Trust colors via .analysis-stack[data-trust-ipa] / .q-ipa-* (not --accent). */
  color: var(--muted);
  font-family: inherit;
  font-size: 0.78em;
  width: auto;
  min-width: 0;
  justify-content: center;
}

.phrase-syl.is-plain .syl-brace,
.phrase-syl.is-plain .syl-ipa {
  display: none;
}

.phrase-teilsatz-label {
  display: block;
  font-size: 0.72em;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.2em;
}

.rhyme.is-phrase.is-teilsatz.is-line {
  opacity: 0.92;
}

/* Phrasenreim: no analysis underlay on the input line */
html[data-phrase-rhyme="1"] #units,
html[data-phrase-rhyme="1"] .panel-units {
  display: none !important;
}

/* —— Ranking-Feedback-Modus (debug) —— */
html[data-ranking-feedback="1"] #rhymes.is-feedback,
#rhymes.is-feedback {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding-bottom: 1.5rem;
}

.feedback-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 0.35rem var(--content-inline) 0.15rem;
}

.feedback-hint {
  margin: 0;
  flex: 1 1 12rem;
  font-size: 0.78rem;
  opacity: 0.72;
  line-height: 1.35;
}

.feedback-copy {
  appearance: none;
  border: 1px solid color-mix(in oklab, currentColor 28%, transparent);
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.78rem;
  padding: 0.28rem 0.7rem;
  border-radius: 0.35rem;
  cursor: pointer;
}

.feedback-copy:focus-visible {
  border-color: color-mix(in oklab, currentColor 55%, transparent);
}
@media (hover: hover) {
  .feedback-copy:hover {
    border-color: color-mix(in oklab, currentColor 55%, transparent);
  }
}

.feedback-band {
  margin: 0;
  padding: 0;
}

.feedback-band-title {
  margin: 0;
  padding: 0.45rem var(--content-inline) 0.25rem;
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.78;
  border-top: 1px solid color-mix(in oklab, currentColor 18%, transparent);
}

.feedback-band.is-top .feedback-band-title {
  border-top: 0;
}

.feedback-band-list {
  min-height: 0.5rem;
}

.feedback-band-list:not(:has(.feedback-row)) {
  min-height: 1.8rem;
}

.feedback-drop-gap {
  height: 0.42rem;
  margin: 0.18rem var(--content-inline);
  border-radius: 1px;
  background: color-mix(in oklab, currentColor 52%, transparent);
  pointer-events: none;
}

.feedback-row {
  display: grid;
  grid-template-columns: 1.6rem 1.5rem minmax(0, 1fr);
  column-gap: 0.35rem;
  align-items: center;
  cursor: grab;
}

.feedback-row.is-dragging {
  opacity: 0.55;
}

.feedback-row .rhyme {
  padding-left: 0;
  padding-right: var(--content-inline);
}

.feedback-rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.45rem;
  height: 1.45rem;
  margin-left: var(--content-inline);
  border-radius: 0.3rem;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  background: color-mix(in oklab, currentColor 10%, transparent);
  opacity: 0.9;
}

.feedback-rank.is-pending {
  position: absolute;
  left: 0.15rem;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 0;
  z-index: 1;
}

html[data-ranking-feedback="pending"] #rhymes > .rhyme {
  padding-left: calc(var(--content-inline) + 1.7rem);
}

.feedback-controls {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.feedback-move {
  appearance: none;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.7rem;
  line-height: 1;
  padding: 0.1rem 0.15rem;
  opacity: 0.55;
  cursor: pointer;
}

.feedback-move:focus-visible {
  opacity: 1;
}
@media (hover: hover) {
  .feedback-move:hover {
    opacity: 1;
  }
}

/* ── Start example (js/ui/start-demo.js, docs/ui-layout.md §4c) ──────────
   Decoration only: an extra .q-ghost layer in #input-host (same padding and
   type as the real field) and a row list hanging under the composer
   (absolute, inside .query-shell). Neither touches layout or the real field. */
html.is-start-demo .q-placeholder {
  visibility: hidden;
}

.start-demo-field {
  z-index: 4;
  transition: opacity 0.36s ease;
}

.start-demo-word .sd-ch {
  visibility: hidden;
}

.start-demo-word .sd-ch.is-on {
  visibility: visible;
}

/* The field itself never shows a real info block before the user writes
   (docs/ui-layout.md §4e): under the typed example only the grey
   placeholders — one mark per (real) syllable, short IPA bars. The real
   Silbenstriche/IPA are in the example rows below. */
html[data-ui="opus"] .start-demo-word .syl-brace,
.start-demo-word .syl-brace {
  --brace-color: var(--ph-brace) !important;
  background: linear-gradient(var(--ph-brace), var(--ph-brace)) center / 100% var(--brace-stroke) no-repeat;
}

.start-demo-word .syl-ipa {
  color: transparent !important;
  -webkit-text-fill-color: transparent;
  background: linear-gradient(var(--ph-ipa), var(--ph-ipa)) center 58% / 56% var(--brace-stroke, 1.6px)
    no-repeat;
}

/* The placeholders arrive after the word, like the live typing ones.
   html[data-ui]: must beat the opus brace opacity (it showed the marks
   before the first letter). */
html[data-ui] .start-demo-word .syl-cell .syl-brace,
html[data-ui] .start-demo-word .syl-cell .syl-ipa {
  opacity: 0;
  transform: translateY(3px);
  transition:
    opacity 0.24s ease-out,
    transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}

html[data-ui] .start-demo-field.is-analysed .start-demo-word .syl-cell .syl-brace,
html[data-ui] .start-demo-field.is-analysed .start-demo-word .syl-cell .syl-ipa {
  opacity: 1;
  transform: none;
}

/* Same caret as the real field (.q-info-caret): info-block tall. */
.start-demo-caret {
  position: absolute;
  z-index: 5;
  top: var(--pad-top, var(--composer-pad-top, 0.42rem));
  left: 0;
  width: 1.5px;
  height: calc(var(--input-text-band) + var(--input-analysis-band));
  background: var(--ink);
  transform: translateX(-0.75px);
  pointer-events: none;
}

.start-demo-caret.is-blink {
  animation: ghost-blink 1.05s step-end infinite;
}

.start-demo-field.is-ready-caret .start-demo-caret {
  /* In front of the hint, not on its first letter. */
  transform: translateX(-3px);
}

.start-demo-rows {
  position: absolute;
  z-index: 1;
  box-sizing: border-box;
  margin: 0;
  /* Same place as the real list under the rule (.results margin + the
     .panel-rhymes air): cap height --rule-gap below the rule. */
  padding: 0;
  margin-top: calc(var(--rule-gap) - var(--row-cap-inset) + var(--rhyme-air-eff, 0.28rem));
  pointer-events: none;
  user-select: none;
  opacity: 0;
  transition: opacity 0.18s ease-out;
}

.start-demo-rows.is-shown {
  opacity: 1;
}

.start-demo-rows .rhyme {
  pointer-events: none;
}

/* Ghost → hit, as in the real list (see "Reveal choreography"). */
#start-demo-rows > .rhyme.is-entering > :not(.rhyme-ghost-veil) {
  animation: rhyme-rise 0.22s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: var(--enter-delay, 0s);
}

#start-demo-rows > .rhyme > .rhyme.rhyme-ghost-veil {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  margin: 0;
  pointer-events: none;
  animation: ghost-veil-out 0.18s ease-out both;
  animation-delay: var(--enter-delay, 0s);
}

html[data-start-demo="fade"] .start-demo-field,
html[data-start-demo="fade"] .start-demo-rows {
  opacity: 0;
  transition: opacity 0.36s ease;
}

@media (prefers-reduced-motion: reduce) {
  .start-demo-field,
  .start-demo-rows,
  html[data-ui] .start-demo-word .syl-cell .syl-brace,
  html[data-ui] .start-demo-word .syl-cell .syl-ipa {
    transition: none;
    transform: none;
  }

  .start-demo-caret.is-blink,
  #start-demo-rows .ghost-slot {
    animation: none;
  }

  /* Reduced motion: no sweep — the whole rule glows while searching. */
  html.is-searching .composer-progress,
  html.is-phrase-searching .composer-progress {
    animation: none;
    background-size: 100% 100%;
    opacity: 0.55;
  }
}
