:root {
  /* Flat dark — one surface; structure via hairlines, not nested gray cards */
  --bg: #181818;
  --paper: #181818;
  --ink: #f2f2f2;
  --muted: #a8a8a8;
  --line: #333333;
  --accent: #c45c5c; /* errors / destructive only */
  --chrome: #b8b8b8; /* inactive UI chrome — never competes with score colors */
  --good: #5fbf7a;
  --warn: #c9a34a;
  --bad: #c43c3c;
  --chip: #2a2a2a;
  /* Product: Manrope UI body; active face via --font-mono (debug cycle). */
  --font-sans: "Manrope", "Noto Sans", "Segoe UI", system-ui, sans-serif;
  --font-mono: "Iosevka Aile", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  --font-ui: var(--font-sans);
  --ui-text-scale: 1;
  /* Hairline further down from the input/results boundary (rem). */
  --divider-offset: 0rem;
  /* Extra air below the hairline (rem; does not move the rule). */
  --input-results-gap: 0rem;
  --score-col: 4.35rem;
  /* 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));
  --content-inline: 0.85rem;
  /* Centered content column: phone ≈ full width; larger screens capped. */
  --content-side: max(0.85rem, 2.5vmin);
  --content-col-cap: 32rem;
  --content-col-width: min(100%, var(--content-col-cap));
  /* Input first-line metrics (chrome align + flex underlay pull). */
  --composer-pad-top: 0.55rem;
  --composer-line: calc(1.1rem * 1.2);
  --input-text-band: calc(1.1rem * 1.2);
  --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.35rem;
  /* Hit target ≈ composer line; glyph ≈ input cap height. */
  --chrome-btn: 1.45rem;
  --chrome-icon: 1.05rem;
  --chrome-gap: 0.1rem;
  /* Closed fallback; live width from JS (--chrome-cluster / --chrome-fade-ramp) */
  --chrome-cluster: 2.4rem;
  --chrome-fade-ramp: 1.75rem;
  --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;
}

* { box-sizing: border-box; }

html {
  /*
    Product text scale via root rem (not CSS zoom). Zoom on .page broke
    hit-testing against the input iframe (settings + composer clicks died
    whenever --ui-text-scale ≠ 1).
  */
  font-size: calc(16px * var(--ui-text-scale, 1));
}

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

/* Bottom debug strip — own scroll; page scroll stops above it.
   No CSS zoom here: zoom breaks grid row sizing (controls spill into
   neighboring rows). Keep optical size via a fixed dock font-size. */
.debug-dock {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: var(--debug-dock-height, 33.333vh);
  min-height: 25vh;
  max-height: 50vh;
  margin: 0;
  padding: 0;
  background: #0e0e0e;
  border-top: 0;
  border-bottom: 0;
  color: #c8c8c8;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  /* Fixed px — independent of --ui-text-scale / root rem. */
  font-size: 12.5px;
  line-height: 1.35;
  z-index: 40;
  overflow: hidden;
  box-sizing: border-box;
}

.debug-dock-resize {
  position: relative;
  z-index: 5;
  flex: 0 0 auto;
  height: 10px;
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: 3px solid #5a5a5a;
  background: #0e0e0e;
  cursor: ns-resize;
  touch-action: none;
  user-select: none;
}

.debug-dock-resize:hover,
.debug-dock-resize:focus-visible,
html[data-dock-resize="1"] .debug-dock-resize {
  border-bottom-color: #8a8a8a;
  background: #141414;
  outline: none;
}

html[data-debug="1"] .debug-dock,
html[data-debug="1"] .debug-dock[hidden] {
  display: flex !important;
  flex-direction: column;
}

html[data-debug="1"],
html[data-debug="1"] body {
  height: 100%;
  overflow: hidden;
}

html[data-debug="1"] .page {
  /* Page scrolls in its own pane — never into the dock. */
  height: calc(100dvh - var(--debug-dock-height, 33.333vh));
  max-height: calc(100dvh - var(--debug-dock-height, 33.333vh));
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 1.4rem;
  box-sizing: border-box;
}

.debug-dock-bar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.65rem;
  width: 100%;
  max-width: min(36rem, 100%);
  margin: 0 auto;
  padding: 0.55rem 1rem 0.85rem;
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  box-sizing: border-box;
}

/* Never let flex shrink rows — that crushed controls into each other. */
.debug-dock-bar > * {
  flex: 0 0 auto;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

.debug-dock-head {
  display: block;
  position: static;
  margin: 0;
  padding: 0;
  background: transparent;
}

.debug-dock-tag {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  font: inherit;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  color: #c8c8c8;
  border: 1px solid #4a4a4a;
  background: #161616;
  padding: 0.55rem 0.75rem;
  cursor: pointer;
  z-index: 4;
}

.debug-dock-tag:hover,
.debug-dock-tag:focus-visible {
  color: #fff;
  border-color: #777;
  background: #1e1e1e;
  outline: none;
}

.debug-dock-tag:active {
  color: #fff;
  background: #282828;
}

.debug-font-controls {
  flex-wrap: nowrap;
  align-items: stretch;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* Horizontal pair: ▲ (prev) left, ▼ (next) right — large separate targets */
.debug-font-cycle {
  display: inline-flex;
  flex-direction: row;
  flex: 0 0 auto;
  align-items: stretch;
  gap: 0.35rem;
  border: 0;
  overflow: visible;
}

.debug-font-controls .debug-font-step {
  flex: 0 0 auto;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0.35rem 0.5rem;
  font-size: 1.15rem;
  line-height: 1;
  border: 1px solid #444;
  border-radius: 0;
  background: #161616;
}

.debug-font-controls .debug-font-step + .debug-font-step {
  border-left: 1px solid #444;
}

.debug-font-controls .debug-select {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  max-width: none;
  min-height: 2.75rem;
  align-self: stretch;
}

.debug-font-controls .debug-font-disable {
  flex: 0 0 auto;
  min-width: 3.25rem;
  min-height: 2.75rem;
  padding: 0.35rem 0.7rem;
  align-self: stretch;
  margin-right: 0;
  order: 0;
}

.debug-save-row .debug-row-controls {
  width: 100%;
}

.debug-save-btn {
  flex: 1 1 auto;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.45rem 0.85rem;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #ddd;
  background: #1a1a1a;
  border: 1px solid #555;
  cursor: pointer;
}

.debug-save-btn:hover,
.debug-save-btn:focus-visible {
  color: #fff;
  border-color: #888;
  outline: none;
}

.debug-save-btn.is-saved {
  color: #111;
  background: #b8b8b8;
  border-color: #b8b8b8;
}

.debug-row {
  display: grid;
  grid-template-columns: 6.75rem minmax(0, 1fr);
  align-items: center;
  column-gap: 0.85rem;
  row-gap: 0.35rem;
  min-height: 0;
  height: auto;
  overflow: visible;
}

.debug-row > .debug-field-label {
  grid-column: 1;
  justify-self: start;
  align-self: center;
}

.debug-row-controls {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem 0.65rem;
  min-width: 0;
  max-width: 100%;
}

.debug-field {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  margin: 0;
}

.debug-field-label {
  color: #7a7a7a;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.65rem;
  flex: 0 0 auto;
  line-height: 1.2;
}

.debug-select {
  font: inherit;
  font-size: 0.78rem;
  color: #e8e8e8;
  background: #1a1a1a;
  border: 1px solid #444;
  border-radius: 0;
  padding: 0.28rem 0.45rem;
  min-width: 12rem;
  max-width: min(22rem, 100%);
  flex: 1 1 auto;
}

.debug-select:focus-visible {
  outline: 1px solid #888;
  outline-offset: 1px;
}

.debug-flex-block.is-disabled {
  opacity: 0.38;
  pointer-events: none;
}

.debug-seg {
  display: inline-flex;
  border: 1px solid #444;
  flex: 0 0 auto;
}

.debug-seg-btn {
  font: inherit;
  font-size: 0.72rem;
  color: #9a9a9a;
  background: #161616;
  border: 0;
  padding: 0.28rem 0.55rem;
  cursor: pointer;
  white-space: nowrap;
}

.debug-seg-btn + .debug-seg-btn {
  border-left: 1px solid #444;
}

.debug-seg-btn:hover {
  color: #ddd;
}

.debug-seg-btn.is-on {
  color: #111;
  background: #b8b8b8;
}

.debug-tog-btn {
  border: 1px solid #444;
}

.debug-slider {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  flex: 0 0 auto;
}

.debug-slider input[type="range"] {
  width: 7rem;
  accent-color: #9a9a9a;
}

.debug-slider output,
.debug-slider .debug-val-input {
  font: inherit;
  font-variant-numeric: tabular-nums;
  min-width: 2.4rem;
  max-width: 3.6rem;
  text-align: right;
  color: #e8e8e8;
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  padding: 0.05rem 0.15rem;
  margin: 0;
  border-radius: 0;
  appearance: none;
  -moz-appearance: textfield;
}

.debug-slider .debug-val-input:hover {
  border-bottom-color: #555;
  cursor: text;
}

.debug-slider .debug-val-input:focus {
  outline: none;
  border-bottom-color: #9a9a9a;
  color: #fff;
}

.debug-slider .debug-val-input::-webkit-outer-spin-button,
.debug-slider .debug-val-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.debug-px-unit {
  color: #7a7a7a;
}

.debug-row-toggles {
  gap: 0.75rem 1.4rem;
}

.debug-slider[hidden] {
  display: none !important;
}

.debug-scale-prefix {
  color: #7a7a7a;
}

.page {
  max-width: none;
  width: 100%;
  min-height: 100dvh;
  margin: 0 auto;
  /* Tight top once results are up — idle centering uses .workspace pad */
  padding: 0.65rem var(--content-side) 1.4rem;
  display: flex;
  flex-direction: column;
  /* Text scale = html rem (--ui-text-scale). Do not zoom .page — see html{}. */
  box-sizing: border-box;
}

.page.is-hero {
  padding-top: 1.2rem;
}

.lang-switch {
  display: inline-flex;
  gap: 0.15rem;
  border: 1px solid var(--line);
  background: var(--paper);
  padding: 0.1rem;
}

.lang-btn {
  font: inherit;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--muted);
  background: transparent;
  border: 0;
  padding: 0.15rem 0.45rem;
  cursor: pointer;
}

.lang-btn:hover {
  color: var(--ink);
}

.lang-btn.is-active {
  color: var(--bg);
  background: var(--ink);
}

.home-link {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  border-bottom: 1px dotted transparent;
  white-space: nowrap;
}

.home-link:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.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;
  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:hover,
.legal-link:focus-visible {
  color: color-mix(in srgb, var(--muted) 58%, transparent);
}

.composer-row {
  display: flex;
  align-items: stretch;
  gap: 0;
  width: 100%;
}

.workspace {
  width: var(--content-col-width);
  max-width: 100%;
  margin-inline: auto;
  box-sizing: border-box;
  border: 0;
  border-radius: 0;
  background: transparent;
  overflow: hidden;
  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 — subpixel L/R jitter on random/grow. */
  transition: padding-top 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

.page.is-hero .workspace {
  /* Optical mid of the viewport; exits with first results reveal */
  padding-top: max(3.5rem, calc(50dvh - 4.75rem));
}

/* Input + optional toolbar + input-analysis */
.query-shell {
  position: relative;
}

.composer {
  position: relative;
  z-index: 2;
  isolation: isolate;
  flex: 1 1 auto;
  min-width: 0;
  /* pad-top + 1.1rem/1.2 line — no bottom pad (marks supply --brace-air) */
  --composer-pad-top: 0.55rem;
  --composer-line: calc(1.1rem * 1.2);
  height: calc(var(--composer-pad-top) + var(--composer-line));
  min-height: calc(var(--composer-pad-top) + var(--composer-line));
  border: 0;
  border-radius: 0;
  background: transparent;
  overflow: visible;
  /* Height snaps — avoids clipping the previous line while growing downward. */
  transition: border-color 0.15s ease;
}

.composer.is-loading {
  height: calc(var(--composer-pad-top) + var(--composer-line));
  border-color: transparent;
}

.composer.is-error {
  height: calc(var(--composer-pad-top) + var(--composer-line));
  box-shadow: inset 0 0 0 1px var(--accent);
}

.composer.is-ready {
  /* Never collapse: absolute CE/host do not stretch auto height. */
  height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line))
  );
  min-height: calc(
    var(--composer-pad-top) + var(--input-text-band, var(--composer-line))
  );
  border-color: transparent;
  background: transparent;
  overflow: visible;
}

.input-host {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-height: none;
  overflow: visible;
  opacity: 1;
  pointer-events: none;
}

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

.input-host.is-entering {
  animation: input-enter 0.42s ease both;
}

@keyframes input-enter {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* 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: transparent;
  color: var(--ink);
  caret-color: var(--ink);
  /* Avoid font-shorthand unitless line-height — it can collapse composer math. */
  font-family: var(--font-mono);
  font-size: calc(1.1rem * var(--input-text-scale, 1));
  font-weight: 400;
  line-height: var(--input-text-band, calc(1.1rem * 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, 0.85rem);
  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;
}

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

.q-sel-layer > i {
  position: fixed;
  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;
  height: calc(
    var(--pad-top, var(--composer-pad-top, 0.55rem)) +
      var(--input-text-band, calc(1.1rem * 1.2))
  );
  z-index: 4;
  box-sizing: border-box;
  padding: var(--pad-top, var(--composer-pad-top, 0.55rem))
    var(--chrome-cluster, 2.85rem) 0 var(--pad-left, 0.85rem);
  pointer-events: none;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
  font-family: var(--font-mono);
  font-size: calc(1.1rem * var(--input-text-scale, 1));
  font-weight: 400;
  line-height: var(--input-text-band, calc(1.1rem * 1.2));
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
  white-space: nowrap;
  overflow: hidden;
  /* Class-driven visibility — NOT [hidden], which used display:none!important
     and silently killed the finished typewriter. */
  display: none;
}

.q-ghost.is-live {
  display: block;
  /* Soft fade just before the settings cluster (padding already reserves it). */
  -webkit-mask-image: linear-gradient(
    to right,
    #000 0%,
    #000
      calc(
        100% - var(--chrome-cluster, 2.4rem) - var(--chrome-fade-ramp, 1.75rem)
      ),
    transparent calc(100% - var(--chrome-cluster, 2.4rem))
  );
  mask-image: linear-gradient(
    to right,
    #000 0%,
    #000
      calc(
        100% - var(--chrome-cluster, 2.4rem) - var(--chrome-fade-ramp, 1.75rem)
      ),
    transparent calc(100% - var(--chrome-cluster, 2.4rem))
  );
}

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

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

.qframe {
  display: none;
}

/*
  Fade ramp + chrome sit on the first input line (optical center with glyphs),
  not a taller 2.75rem strip that drifts above/below the text.
*/
.input-fade {
  position: absolute;
  z-index: 2;
  /* Optical center of the first text band (JS sets --chrome-top from glyphs). */
  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(--chrome-btn)) /
          2
    )
  );
  bottom: auto;
  height: var(--chrome-btn);
  right: var(--chrome-cluster);
  width: var(--chrome-fade-ramp);
  pointer-events: none;
  background: linear-gradient(
    to right,
    transparent 0%,
    color-mix(in srgb, var(--paper) 40%, transparent) 40%,
    var(--paper) 92%
  );
  opacity: 1;
  transition:
    right 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    width 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.18s ease;
}

/* While editing, show the caret end — hide the idle fade */
.composer.is-focused .input-fade,
.input-host.is-focused ~ .input-fade {
  opacity: 0;
}

.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(--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;
  right: calc(-1 * var(--chrome-right));
  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(--muted) 70%, var(--ink));
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.input-settings:hover,
.input-settings:focus-visible {
  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:hover,
.input-settings.is-attention:focus-visible,
.tool-btn.is-attention:hover,
.tool-btn.is-attention:focus-visible {
  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.
*/
/*
  Tools expand left from the settings cluster (no second row / no divider).
  Width must stay auto — a fixed tray width poisons abspos shrink-to-fit
  and made the input inset far too large.
*/
.toolbar {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  gap: 0.12rem;
  flex: 0 0 auto;
  width: auto;
  max-width: 0;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  opacity: 0;
  transform: translateX(0.45rem);
  pointer-events: none;
  transition:
    max-width 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s ease,
    transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}

.toolbar.is-open {
  max-width: 14.5rem;
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
  /* keep overflow hidden — voice panel lives outside composer */
}

.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:hover,
.tool-btn:focus-visible {
  color: color-mix(in srgb, var(--muted) 88%, var(--ink));
  outline: none;
}

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

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

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

.prefs-toggle {
  flex: 0 0 auto;
  width: 3.35rem;
  align-self: stretch;
  min-height: 3.35rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--paper);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.composer-row.is-loading .prefs-toggle,
.composer-row.is-error .prefs-toggle {
  border-color: var(--line);
}

.prefs-toggle:hover {
  color: var(--ink);
  border-color: var(--chrome);
}

.prefs-toggle[aria-expanded="true"] {
  color: var(--bg);
  background: var(--ink);
  border-color: var(--ink);
}

.prefs-toggle-icon {
  display: block;
}

.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 {
  visibility: hidden;
  pointer-events: none;
}

/* Progress lives inside the composer shell (same box as the future input). */
.lex-load {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  /* Align with the text/icon band (pad-top + line), not the full box mid. */
  justify-content: flex-start;
  padding-top: calc(
    var(--composer-pad-top, 0.55rem) +
      (var(--composer-line, calc(1.1rem * 1.2)) - 4px) / 2
  );
  gap: 0.35rem;
  margin: 0;
  /* Centered on the full line — same inset both sides (no chrome reserve). */
  padding-left: var(--content-inline);
  padding-right: var(--content-inline);
  border: 0;
  background: transparent;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Fade ramp would soft-clip the bar’s right edge — keep it off while the
   loader is on screen (including the exit dissolve into the text field). */
.composer.is-loading .input-fade,
.composer.is-error .input-fade,
.composer:has(.lex-load:not([hidden])) .input-fade,
/* Overlay words already clear the chrome cluster — fade would ghost them. */
html[data-flex-input="1"] .input-fade {
  opacity: 0;
}

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

.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 */
  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);
  /* scaleX from rAF — avoids subpixel width rounding vs track */
  will-change: transform;
}

.lex-load-track.is-indeterminate .lex-load-fill {
  /* Sweep via left (not translate) so height stays crisp */
  width: 32%;
  transform: 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;
}

.lex-load.is-error .lex-load-track {
  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;
}

.prefs-drawer {
  margin: 0.75rem 0 0.15rem;
}

.prefs-drawer[hidden] {
  display: none;
}

.prefs {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  margin: 0;
}

.prefs-panel {
  border: 1px solid var(--line);
  background: var(--paper);
}

.prefs-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.4rem 0.65rem;
  border-bottom: 1px solid var(--line);
}

.prefs-panel-title {
  font-size: 0.82rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.pref {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  min-width: 0;
}

.pref.check {
  display: grid;
  grid-template-columns: 1.1rem 1fr;
  align-items: start;
  gap: 0.45rem;
  margin: 0;
  line-height: 1.35;
}

.pref.check input[type="checkbox"] {
  margin: 0.15rem 0 0;
  accent-color: var(--ink);
}

.prefs-sliders > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  font-size: 0.82rem;
  color: var(--muted);
  padding: 0.4rem 0.65rem;
  letter-spacing: 0.02em;
}

.prefs-sliders > summary::-webkit-details-marker {
  display: none;
}

.prefs-sliders > summary::before {
  content: "▸";
  display: inline-block;
  width: 1em;
  color: var(--muted);
}

.prefs-sliders[open] > summary::before {
  content: "▾";
}

.prefs-sliders > summary:hover {
  color: var(--ink);
}

.prefs-matrix {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.55rem 1.4rem;
  padding: 0.65rem;
}

.prefs-sliders .prefs-matrix {
  padding: 0.65rem;
  border-top: 1px solid var(--line);
}

@media (max-width: 620px) {
  .prefs-matrix {
    grid-template-columns: 1fr;
  }
}

.pref.range {
  display: grid;
  grid-template-columns: minmax(7.5rem, 9.5rem) 1fr 2.4rem;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
}

.pref.range input[type="range"] {
  width: 100%;
  min-width: 0;
  accent-color: var(--ink);
}

.pref-reset {
  border-bottom: 1px dotted transparent;
}

.pref-reset:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.pref-val {
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ink);
  font-size: 0.8rem;
}

.prefs-reset-all {
  font: inherit;
  font-size: 0.78rem;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  padding: 0.15rem 0.5rem;
  cursor: pointer;
  border-radius: 2px;
  flex-shrink: 0;
}

.prefs-reset-all:hover {
  color: var(--ink);
  border-color: var(--chrome);
}

.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 analysis → rhyme results (full content width).
   top tracks --divider-offset only — gap below does not move the hairline. */
.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;
}

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

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

.units-literal {
  display: none;
}

html[data-flex-input="1"] .unit-surface {
  display: block;
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
  font-size: 1.1rem;
  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;
}

/* Punctuation / symbols: visible ink, no marks/IPA (algorithm ignores). */
html[data-flex-input="1"] .units-literal {
  display: block;
  flex: 0 0 auto;
  align-self: flex-start;
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
  font-size: 1.1rem;
  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;
}

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: 1.1rem;
  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;
}

/* Multi-reading mark rows: no second surface word. */
html[data-flex-input="1"]
  .units-readings
  .reading-marks
  .unit-choice
  .syl-ortho {
  height: 0 !important;
  line-height: 0 !important;
  overflow: hidden !important;
  opacity: 0 !important;
  color: transparent !important;
  -webkit-text-fill-color: transparent !important;
}

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

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

/*
  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-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: 1.1rem;
}

.syl-ortho {
  font-size: 0.78rem;
  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: 1.1rem;
  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: 1.1rem;
  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));
}

.units-word-gap {
  flex: 0 0 auto;
  white-space: pre;
  font-size: 1.1rem;
  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: 1.1rem;
  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;
}

.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-ui);
  font-feature-settings: "kern" 1;
  font-size: 0.58rem;
  /* Stable box so IPA glyphs share one baseline */
  line-height: 1.35;
  color: var(--muted);
  direction: ltr;
  unicode-bidi: isolate;
  margin-top: 0;
  /* Always centered under the brace / syllable */
  display: flex;
  justify-content: center;
  text-align: center;
  white-space: pre;
  overflow: visible;
}

/*
  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-ui);
  font-feature-settings: "kern" 1;
  font-size: 0.62rem;
  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));
}

/*
  UI body = Manrope (--font-ui). Input brand + analysis stacks = Iosevka Aile.
  Impressum stays Manrope.
*/
html {
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

.analysis-stack,
.analysis-stack .syl-ortho,
.analysis-stack .syl-ipa,
.ipa-fallback,
.score-pct {
  font-family: var(--font-mono);
  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;
}

.sentence-bits {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem 0.65rem;
  direction: ltr;
  unicode-bidi: isolate;
}

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

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

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

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

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

html[data-flex-input="1"] .unit-cluster {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* Multi-reading: single stack; click cycles (no stacked brace rows). */
.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 {
  /* Stack itself does not steal CE hits — cells/braces/IPA do (Lesart). */
  pointer-events: none;
  cursor: pointer;
  position: relative;
}

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

/* Full syl-cell (brace + IPA) is the Lesart hit target. */
.units-readings.is-multi .syl-cell {
  pointer-events: auto;
  cursor: pointer;
}

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

/*
  Lesart affordance: soft opacity pulse on IPA only (keeps quality color).
  No extra underline — brace/IPA air stays the global shared rhythm.
*/
@keyframes lesart-ipa-pulse {
  0%,
  100% {
    opacity: 0.55;
  }
  50% {
    opacity: 1;
  }
}

.units-readings.is-multi .syl-ipa,
.units-readings.is-multi .ipa-fallback {
  animation: lesart-ipa-pulse 2.1s ease-in-out infinite;
  animation-delay: var(--lesart-pulse-delay, 0ms);
}

.units-readings.is-multi:hover .syl-ipa,
.units-readings.is-multi:hover .ipa-fallback,
.units-readings.is-multi:focus-visible .syl-ipa,
.units-readings.is-multi:focus-visible .ipa-fallback {
  animation: none;
  opacity: 1;
}

.units-readings:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--muted) 55%, transparent);
  outline-offset: 3px;
}

.units-readings .reading-marks {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--reading-stack-gap, 0.18rem);
  max-width: 100%;
  pointer-events: auto;
  /* Expand hit target below thin braces */
  padding: 0.15rem 0 0.35rem;
  margin: -0.15rem 0 -0.1rem;
  box-sizing: content-box;
  cursor: pointer;
}

.units-readings .reading-marks .unit-choice {
  width: auto;
  max-width: 100%;
  padding: 0;
  margin: 0;
  box-sizing: content-box;
  cursor: inherit;
}

/*
  Multi-reading: stacked mark rows only; IPA always under the last mark row
  (never under the active row mid-stack). Air = same debug vars as output.
*/
.units-readings .reading-marks .unit-choice .analysis-stack.is-underlay .syl-brace {
  margin-bottom: 0;
}

.units-readings .reading-marks .unit-choice:not(:first-child) .analysis-stack.is-underlay .syl-brace {
  margin-top: 0;
}

.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:hover .unit-choice:not(.is-active),
.units-readings:focus-visible .unit-choice:not(.is-active) {
  opacity: 0.72;
}

.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):hover,
.unit-choice:not(.is-solo):not(.is-active):focus-visible {
  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);
  column-gap: 0.65rem;
  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;
}

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

/*
  E-014 Auftakt hang: shift the whole result ink left by the measured width of
  the unstressed prefix so the first correlated syllable lines up under the
  query window. No flex-font column grid — just margin from --auftakt-shift.
*/
.rhyme-main.has-auftakt-hang {
  margin-left: calc(-1 * var(--auftakt-shift, 0px));
}

.rhyme-main.has-auftakt-hang .analysis-stack,
.rhyme-main.has-auftakt-hang .name-line {
  overflow: visible;
}

.rhyme-meta {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.35rem;
  flex: 0 0 auto;
  width: 100%;
  padding-top: 0;
}

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

.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: 1.1rem;
  /* 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="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;
}

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

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