/* 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;
  --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);
  --composer-pad-bot: var(--composer-pad-y);
  --composer-line: calc(1.1rem * var(--ui-text-scale, 1.1) * 1.2);
  --input-text-band: calc(1.1rem * var(--ui-text-scale, 1.1) * 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.58rem * var(--ui-text-scale, 1.1) * 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,
html[data-landing="intro"] .composer-progress.is-landing-draw rect {
  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. */
  --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)
  );
  border: var(--field-border) solid #333;
  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. */
  transition: none;
}

/* 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 rides the field border. Idle stays the quiet inset line. */
.composer-progress {
  position: absolute;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

.composer-progress rect {
  fill: none;
  stroke: #e0e0e0;
  stroke-width: 1px;
  stroke-linecap: square;
  opacity: 0;
}

html.is-searching .composer-progress rect,
html.is-phrase-searching .composer-progress rect {
  opacity: 1;
  stroke-dasharray: 18 82;
  animation: composer-stroke 1.25s linear infinite;
}

@keyframes composer-stroke {
  to { stroke-dashoffset: -100; }
}

.composer.is-focused {
  border-color: #555;
}

.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.
   ========================================================================== */
: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: 12px;
}

.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: 16px;
  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: 16px 16px 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    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);
  }
}

.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]) {
    animation: 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;
  margin-top: 0;
  border-top: 0;
  overflow: visible;
  /* Offset = space above the rule; gap = space below the rule (before content). */
  padding-top: calc(
    var(--divider-offset, 0rem) + var(--input-results-gap, 0rem)
  );
}

/* Soft section cut: input → rhyme results (full content width).
   top tracks --divider-offset only — gap below does not move the hairline.
   After search the field sits at the top — the hairline is noise; hide it. */
.results::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: var(--divider-offset, 0rem);
  left: var(--content-inline);
  right: var(--content-inline);
  height: 1px;
  background: color-mix(in srgb, var(--line) 55%, transparent);
  pointer-events: none;
}

.page:not(.is-hero) .results::before {
  content: none;
  display: none;
}

.page:not(.is-hero) .results {
  padding-top: var(--input-results-gap, 0.5rem);
}

/* Before the list exists, the flow bar stands in for the hairline.
   Once results are up, that bar is the full column and pushes the first
   word down. The hairline itself sweeps instead — same inset, same gap. */
html.is-searching:not([data-results="ready"]) .results::before,
html.is-phrase-searching:not([data-results="ready"]) .results::before {
  opacity: 0;
}

/* Searching no longer sweeps the Trennstich. The field outline is the progress. */

.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 {
  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: same shelf, one grey bar per letter run, dim IPA weight. */
.analysis-stack.is-placeholder .syl-ipa.is-ph {
  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;
  color: transparent;
}

.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 {
  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: 2px;
  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));
}

/* 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))
  );
}

/* 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) { --ghost-w: 2.4em; }
#rhymes > .rhyme:nth-child(6n + 2) { --ghost-w: 3.3em; }
#rhymes > .rhyme:nth-child(6n + 3) { --ghost-w: 1.9em; }
#rhymes > .rhyme:nth-child(6n + 4) { --ghost-w: 2.8em; }
#rhymes > .rhyme:nth-child(6n + 5) { --ghost-w: 3.7em; }
#rhymes > .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: 2px;
  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));
}

.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))
  );
}

.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: 2px;
}

.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;
  }

  html[data-landing="intro"] .composer-progress.is-landing-draw rect {
    animation: none;
    opacity: 0;
    stroke-dashoffset: 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;
}

.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: opacity 200ms ease;
}

.why-panel {
  --why-pad: 1rem;
  position: relative;
  box-sizing: border-box;
  width: 100%;
  max-height: 88svh;
  max-height: 88dvh;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0 var(--why-pad) calc(1rem + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -12px 40px color-mix(in srgb, #000 55%, transparent);
  transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
  font-family: var(--font-sans);
}

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

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

.why-backdrop.is-entering {
  opacity: 0;
}

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

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

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

.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);
  }
}

.why-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 2.4rem 0.85rem;
  border-bottom: 1px solid var(--line);
  touch-action: pan-x;
}

.why-score {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.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.8rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.why-compare {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 0.35rem 0.8rem;
  max-width: 100%;
}

.why-block {
  min-width: 0;
  max-width: 100%;
}

.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-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. */
.why-grid-scroll {
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0.25rem 0 0.15rem;
  display: flex;
  justify-content: center;
}

.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;
}

.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: 10px;
}

.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-grid .why-skel-bar {
  height: 9rem;
  border-radius: 10px;
}

.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: 10px;
}

.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-grid.is-animated .why-cell {
  animation: why-fade 260ms ease both;
}

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

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

.why-grid.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;
    width: min(36rem, calc(100vw - 3rem));
    max-height: min(52rem, calc(100vh - 3rem));
    max-height: min(52rem, calc(100dvh - 3rem));
    border-bottom: 1px solid var(--line);
    border-radius: 14px;
    padding-bottom: 1.5rem;
    box-shadow: 0 24px 60px color-mix(in srgb, #000 55%, transparent);
  }

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

  .why-backdrop.is-entering .why-panel {
    transform: translateY(12px) scale(0.98);
  }

  .why-backdrop.is-leaving .why-panel {
    transform: 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-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,
  html.is-searching .composer-progress rect,
  html.is-phrase-searching .composer-progress rect {
    animation: none;
  }

  html.is-searching .composer-progress rect,
  html.is-phrase-searching .composer-progress rect {
    stroke-dasharray: none;
    opacity: 0.9;
  }
}

.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: 0;
  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;
  }
}
