:root {
  color-scheme: light;
  --paper: #f3eee2;
  --paper-deep: #e8decb;
  --paper-card: rgba(252, 249, 242, 0.9);
  --paper-solid: #fbf8f1;
  --ink: #1a1815;
  --ink-soft: #5c574d;
  --ink-faint: #6f695c;
  --cinnabar: #9d3d2f;
  --cinnabar-dark: #762a22;
  --gold: #b8894a;
  --line: rgba(80, 68, 51, 0.2);
  --line-strong: rgba(80, 68, 51, 0.36);
  /* The frame of a block-printed page: one hairline, doubled at the spine edge. */
  --rule: rgba(60, 48, 34, 0.28);
  --shadow: 0 24px 70px rgba(55, 43, 25, 0.12);
  --mark: rgba(184, 137, 74, 0.32);
  /* The two ambient washes behind the page, and the paper tooth over it. */
  --glow-warm: rgba(184, 137, 74, 0.13);
  --glow-cool: rgba(157, 61, 47, 0.08);
  --grain: rgba(65, 50, 31, 0.018);
  --serif: "Poem Serif", "Noto Serif CJK SC", "Source Han Serif SC", "Songti SC", SimSun, STSong, serif;
  --face-serif: "Poem Serif", "Noto Serif CJK SC", "Source Han Serif SC", "Songti SC", SimSun, STSong, serif;
  --face-kai: "Poem Kai", "LXGW WenKai", Kaiti, KaiTi, STKaiti, "Kaiti SC", serif;
  /* Fangsong lacks ~0.6% of this corpus, so it falls through to the shipped Song
     face rather than to whatever the machine has: two printing faces mixed is a
     far quieter seam than one printing face and one UI sans. */
  --face-fangsong: "Poem Fangsong", "Poem Serif", FangSong, STFangsong, "Fangsong SC", serif;
  --face-sans: "Poem Sans", "Noto Sans CJK SC", "Source Han Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
  --sans: Inter, "Noto Sans CJK SC", "Source Han Sans SC", "Microsoft YaHei", sans-serif;
}

/*
 * The reading size. Only the poems scale — the chrome keeps its own sizes, so
 * enlarging the text never rearranges the interface around it.
 */
:root { --reading-scale: 1; }
html[data-size="small"] { --reading-scale: .88; }
html[data-size="large"] { --reading-scale: 1.15; }
html[data-size="xlarge"] { --reading-scale: 1.32; }

/* The reading face. Only the poems and their headings change; the interface keeps
   its own sans so chrome and content never blur into each other. */
html[data-face="kai"] { --serif: var(--face-kai); }
html[data-face="fangsong"] { --serif: var(--face-fangsong); }
html[data-face="sans"] { --serif: var(--face-sans); }

/*
 * Themes are whole palettes, not a light switch: each is a ground the poems could
 * plausibly have been printed or written on. Every one redefines the same tokens,
 * so no component knows they exist.
 */

/* 夜读 — warm charcoal, lamplight on paper. */
html[data-theme="dark"] {
  color-scheme: dark;
  --paper: #171815;
  --paper-deep: #20211d;
  --paper-card: rgba(39, 39, 33, 0.94);
  --paper-solid: #23231e;
  --ink: #eee8dc;
  --ink-soft: #bcb4a6;
  --ink-faint: #918a7f;
  --cinnabar: #cf6a57;
  --cinnabar-dark: #e17a66;
  --gold: #cda363;
  --line: rgba(235, 225, 207, 0.16);
  --line-strong: rgba(235, 225, 207, 0.3);
  --rule: rgba(235, 225, 207, 0.26);
  --shadow: 0 28px 80px rgba(0, 0, 0, 0.28);
  --mark: rgba(205, 163, 99, 0.3);
  --glow-warm: rgba(205, 163, 99, 0.07);
  --glow-cool: rgba(207, 106, 87, 0.06);
  --grain: rgba(235, 225, 207, 0.012);
}

/* 青瓷 — Song celadon glaze, iron-brown crackle. */
html[data-theme="celadon"] {
  color-scheme: light;
  --paper: #e7eee9;
  --paper-deep: #d5e2da;
  --paper-card: rgba(246, 250, 247, 0.9);
  --paper-solid: #f3f8f4;
  --ink: #18231e;
  --ink-soft: #4c5b53;
  --ink-faint: #63736a;
  --cinnabar: #9a4632;
  --cinnabar-dark: #763325;
  --gold: #8b7c46;
  --line: rgba(30, 62, 50, 0.2);
  --line-strong: rgba(30, 62, 50, 0.34);
  --rule: rgba(24, 56, 44, 0.28);
  --shadow: 0 24px 70px rgba(28, 60, 48, 0.12);
  --mark: rgba(139, 124, 70, 0.3);
  --glow-warm: rgba(139, 124, 70, 0.1);
  --glow-cool: rgba(70, 120, 100, 0.12);
  --grain: rgba(28, 56, 46, 0.016);
}

/* 绢素 — aged silk, the warmest of the light grounds. */
html[data-theme="silk"] {
  color-scheme: light;
  --paper: #eddfc6;
  --paper-deep: #dcc9a6;
  --paper-card: rgba(248, 239, 221, 0.92);
  --paper-solid: #f7eedd;
  --ink: #271f13;
  --ink-soft: #5d5034;
  --ink-faint: #6e6248;
  --cinnabar: #9a3a25;
  --cinnabar-dark: #772719;
  --gold: #a07231;
  --line: rgba(92, 72, 36, 0.22);
  --line-strong: rgba(92, 72, 36, 0.38);
  --rule: rgba(82, 62, 30, 0.3);
  --shadow: 0 24px 70px rgba(80, 60, 26, 0.14);
  --mark: rgba(160, 114, 49, 0.3);
  --glow-warm: rgba(160, 114, 49, 0.14);
  --glow-cool: rgba(154, 58, 37, 0.08);
  --grain: rgba(70, 52, 22, 0.02);
}

/* 墨拓 — a stone rubbing: chalk strokes lifted off black. */
html[data-theme="rubbing"] {
  color-scheme: dark;
  --paper: #0e0e0d;
  --paper-deep: #161615;
  --paper-card: rgba(25, 25, 24, 0.94);
  --paper-solid: #1b1b19;
  --ink: #f1eee6;
  --ink-soft: #bfbbb1;
  --ink-faint: #8a8780;
  --cinnabar: #d4614c;
  --cinnabar-dark: #e5745e;
  --gold: #c6ac7e;
  --line: rgba(241, 238, 230, 0.14);
  --line-strong: rgba(241, 238, 230, 0.28);
  --rule: rgba(241, 238, 230, 0.24);
  --shadow: 0 28px 80px rgba(0, 0, 0, 0.5);
  --mark: rgba(198, 172, 126, 0.32);
  --glow-warm: rgba(198, 172, 126, 0.05);
  --glow-cool: rgba(212, 97, 76, 0.05);
  --grain: rgba(241, 238, 230, 0.01);
}

/* 靛夜 — indigo dye, the colour of a night sky over water. */
html[data-theme="indigo"] {
  color-scheme: dark;
  --paper: #121a25;
  --paper-deep: #182333;
  --paper-card: rgba(26, 36, 50, 0.94);
  --paper-solid: #1b2534;
  --ink: #e7eaf0;
  --ink-soft: #aeb8c6;
  --ink-faint: #7f8b9c;
  --cinnabar: #e07460;
  --cinnabar-dark: #ef8874;
  --gold: #c9a86a;
  --line: rgba(198, 214, 236, 0.16);
  --line-strong: rgba(198, 214, 236, 0.3);
  --rule: rgba(198, 214, 236, 0.26);
  --shadow: 0 28px 80px rgba(0, 8, 20, 0.45);
  --mark: rgba(201, 168, 106, 0.3);
  --glow-warm: rgba(201, 168, 106, 0.06);
  --glow-cool: rgba(90, 140, 200, 0.1);
  --grain: rgba(198, 214, 236, 0.012);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  min-width: 320px;
  min-height: 100vh;
  overflow-x: hidden;
  color: var(--ink);
  background:
    radial-gradient(circle at 8% 4%, var(--glow-warm), transparent 26rem),
    radial-gradient(circle at 92% 25%, var(--glow-cool), transparent 24rem),
    linear-gradient(150deg, var(--paper), var(--paper-deep));
  background-attachment: fixed;
  font-family: var(--sans);
  transition: color 180ms ease, background-color 180ms ease;
}

button, input { font: inherit; }
/* The reading face ships at one weight; never let the browser fake another. */
body { font-synthesis-weight: none; }
:focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 3px; border-radius: 2px; }
/* Sections are scrolled to under a sticky header. */
.content-section, .authors-section, .hero { scroll-margin-top: 88px; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; }
}
button { color: inherit; }
button, label { -webkit-tap-highlight-color: transparent; }
/*
 * A reader marks a text in vermilion, not with a highlighter: 着重号 — one dot
 * beneath each matched character. `text-emphasis` exists for exactly this, so the
 * marks track the glyphs instead of sitting in a rectangle behind them.
 */
mark {
  color: inherit;
  background: none;
  padding: 0;
  -webkit-text-emphasis-style: filled dot;
  text-emphasis-style: filled dot;
  -webkit-text-emphasis-color: var(--cinnabar);
  text-emphasis-color: var(--cinnabar);
  -webkit-text-emphasis-position: under right;
  text-emphasis-position: under right;
}
/* Latin and digits are too narrow to carry a dot legibly; tint them instead. */
mark:lang(en) { color: var(--cinnabar); }

/* Phrases a neighbour shares with the poem you came from: a side rule, not a dot,
   so the two annotations never read as the same thing. */
.shared {
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 62%, transparent);
  padding-bottom: 1px;
}

.page-grain {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.36;
  background-image:
    repeating-linear-gradient(93deg, transparent 0 5px, var(--grain) 5px 6px),
    repeating-linear-gradient(2deg, transparent 0 7px, var(--grain) 7px 8px);
}

.skip-link {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 100;
  padding: 10px 14px;
  color: white;
  background: var(--cinnabar-dark);
  border-radius: 8px;
  transform: translateY(-160%);
}
.skip-link:focus { transform: translateY(0); }

/* ------------------------------------------------------------------ header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  width: 100%;
  padding: 0 max(20px, calc((100% - 1240px) / 2));
  min-height: 88px;
  display: flex;
  align-items: center;
  gap: 20px;
  justify-content: space-between;
  border-bottom: 1px solid transparent;
  transition: min-height 200ms ease, background-color 200ms ease, border-color 200ms ease, backdrop-filter 200ms ease;
}
.site-header.is-condensed {
  min-height: 66px;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  border-bottom-color: var(--line);
  backdrop-filter: blur(14px);
}

.brand { display: inline-flex; align-items: center; gap: 13px; color: inherit; text-decoration: none; flex: none; }
.brand-seal {
  display: grid;
  width: 42px;
  height: 42px;
  place-items: center;
  color: #fff8ec;
  background: var(--cinnabar);
  border-radius: 3px 9px 4px 7px;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .18);
  font: 400 25px/1 var(--serif);
}
.brand strong { display: block; font: 400 20px/1.1 var(--serif); letter-spacing: .22em; }
.brand small { display: block; margin-top: 5px; color: var(--ink-faint); font-size: 10px; letter-spacing: .18em; }

/* The compact search only exists once the hero search is out of view. */
.mini-search {
  position: relative;
  flex: 1 1 420px;
  max-width: 460px;
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
}
.site-header.is-condensed .mini-search { opacity: 1; transform: none; pointer-events: auto; }
.mini-search input {
  width: 100%;
  height: 42px;
  padding: 0 16px 0 44px;
  color: var(--ink);
  background: var(--paper-card);
  border: 1px solid var(--line-strong);
  border-radius: 6px 16px 6px 16px;
  outline: 0;
}
.mini-search .search-icon { left: 14px; }
.mini-search .search-icon svg { width: 18px; }
.mini-search input:focus { border-color: var(--cinnabar); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cinnabar) 15%, transparent); }

.header-actions { display: flex; align-items: center; gap: 16px; flex: none; }
.feedback-link {
  min-width: 44px;
  min-height: 44px;
  padding: 0 8px;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 13px;
  letter-spacing: .08em;
  transition: color 180ms ease;
}
.feedback-link:hover { color: var(--cinnabar); }
.collection-count { margin: 0; color: var(--ink-soft); font-size: 13px; white-space: nowrap; }

/* One control, two states: the active script is the highlighted half. */
.script-toggle {
  display: inline-flex;
  align-items: center;
  padding: 3px;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 180ms ease;
}
.script-toggle:hover { border-color: var(--cinnabar); }
.script-toggle span {
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  color: var(--ink-faint);
  border-radius: 50%;
  font: 400 14px/1 var(--serif);
  transition: 180ms ease;
}
.script-toggle .script-simplified { color: #fff8ee; background: var(--cinnabar); }
html[lang="zh-Hant"] .script-toggle .script-simplified { color: var(--ink-faint); background: transparent; }
html[lang="zh-Hant"] .script-toggle .script-traditional { color: #fff8ee; background: var(--cinnabar); }

.menu-anchor { position: relative; }
/* The face button is set in the face it chooses; the size button shows the same
   character twice, so both say what they do without a label. */
.face-toggle span { font: 400 18px var(--serif); line-height: 1; }
/* 字 between two hairlines: the character sits on a body the way metal type does,
   and it grows or shrinks with the size that is set. */
.size-gauge { position: relative; font-family: var(--serif); line-height: 1; transition: font-size 160ms ease; }
.size-gauge::before,
.size-gauge::after {
  content: "";
  position: absolute;
  left: -4px;
  right: -4px;
  height: 1px;
  background: currentColor;
  opacity: .4;
}
.size-gauge::before { top: -5px; }
.size-gauge::after { bottom: -5px; }
/* The three header menus are one object in three sizes: a slip of paper under the
   button, each row previewing what it sets. */
.theme-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 40;
  display: grid;
  gap: 1px;
  min-width: 218px;
  padding: 6px;
  background: var(--paper-solid);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 2px 18px 2px 18px;
  box-shadow: 0 22px 50px rgba(30, 24, 14, .24);
  animation: menu-open 150ms ease;
  /* Six papers with their notes is a tall slip on a short window. */
  max-height: min(72vh, 540px);
  overflow-y: auto;
}
@keyframes menu-open {
  from { opacity: 0; transform: translateY(-5px); }
}
/* Names the menu, since the button that opened it is an icon. */
.menu-heading {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 3px 8px 7px;
  color: var(--ink-faint);
  font-size: 11px;
  letter-spacing: .22em;
}
.menu-heading::after { content: ""; flex: 1; height: 1px; background: var(--line); }
/* A face's own sample, set in that face once it has been loaded. */
.face-sample { font: 400 17px var(--face); }
/* The size rows show the same character at the size they set. */
.size-sample { color: var(--ink); font-family: var(--serif); line-height: 1; }
.menu-copy { display: grid; gap: 3px; min-width: 0; }
.menu-label { color: inherit; font: 400 14px var(--serif); letter-spacing: .08em; }
.face-note { color: var(--ink-faint); font-size: 11px; letter-spacing: .02em; }

.theme-option {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) 8px;
  gap: 12px;
  align-items: center;
  padding: 8px 10px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: 3px 10px 3px 10px;
  cursor: pointer;
  text-align: left;
  font-size: 13px;
  transition: background-color 140ms ease, color 140ms ease;
}
/* The sample column is one width across all three menus, so swatches, faces and
   sizes line up whichever menu is open. */
.theme-option > :first-child { justify-self: center; }
.theme-option:hover { color: var(--ink); background: color-mix(in srgb, var(--gold) 14%, transparent); }
.theme-option[aria-checked="true"] { color: var(--ink); background: color-mix(in srgb, var(--cinnabar) 8%, transparent); }
.theme-option[aria-checked="true"] .menu-label { color: var(--cinnabar); }
.theme-option[aria-checked="true"] .theme-tick { opacity: 1; }
/* The mark of a choice made: a seal, like the one on the paper swatches. */
.theme-tick {
  width: 7px;
  height: 7px;
  background: var(--cinnabar);
  border-radius: 1px 3px 1px 3px;
  opacity: 0;
  transition: opacity 140ms ease;
}
/* Each swatch is the theme itself: its paper, its ink stroke, its seal. */
.theme-swatch {
  position: relative;
  width: 22px;
  height: 22px;
  border: 1px solid var(--line-strong);
  border-radius: 3px 8px 3px 8px;
  overflow: hidden;
}
.theme-swatch i { position: absolute; left: 4px; right: 8px; top: 7px; height: 1.5px; border-radius: 1px; }
.theme-swatch b { position: absolute; right: 3px; bottom: 3px; width: 7px; height: 7px; border-radius: 1px; }

.icon-button {
  display: inline-grid;
  width: 40px;
  height: 40px;
  padding: 0;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--paper-card);
  cursor: pointer;
  transition: border-color 180ms ease, background-color 180ms ease, color 180ms ease, opacity 180ms ease;
}
.icon-button:hover:not(:disabled) { color: var(--cinnabar); border-color: var(--cinnabar); }
.icon-button:disabled { opacity: .35; cursor: default; }
.icon-button svg { width: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.moon-icon { display: none; }
html[data-mode="dark"] .sun-icon { display: none; }
html[data-mode="dark"] .moon-icon { display: block; }

main { width: min(1240px, calc(100% - 40px)); margin: 0 auto; }

/* -------------------------------------------------------------------- hero */

.hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(260px, .78fr) minmax(540px, 1.22fr);
  gap: clamp(36px, 7vw, 96px);
  align-items: center;
  min-height: 500px;
  padding: 56px 0 60px;
}
.hero::before {
  content: "";
  position: absolute;
  top: 54px;
  left: -36px;
  width: 1px;
  height: 235px;
  background: linear-gradient(var(--cinnabar), transparent);
}

.eyebrow, .section-kicker {
  margin: 0 0 16px;
  color: var(--cinnabar);
  font: 600 12px/1.5 var(--sans);
  letter-spacing: .3em;
  text-transform: uppercase;
}
.hero h1 {
  margin: 0;
  font: 400 clamp(38px, 5vw, 64px)/1.28 var(--serif);
  letter-spacing: .04em;
  /* Never break before a comma — 避头尾, and the reason the old headline read as
     "从一字一 / 句，/ 寻见诗心。" */
  line-break: strict;
  text-wrap: balance;
}
.hero h1 em { color: var(--cinnabar); font-style: normal; }
.hero-description { max-width: 31rem; margin: 26px 0 0; color: var(--ink-soft); font: 400 16px/2 var(--serif); }
.hero-hint { margin: 20px 0 0; color: var(--ink-faint); font-size: 12px; letter-spacing: .04em; }
.hero-hint kbd {
  display: inline-block;
  min-width: 20px;
  margin: 0 2px;
  padding: 2px 6px;
  text-align: center;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  font: 600 11px/1.3 var(--sans);
}

.search-card {
  position: relative;
  padding: clamp(24px, 4vw, 38px) clamp(24px, 4vw, 38px) clamp(24px, 3.4vw, 34px);
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 2px 22px 2px 22px;
  box-shadow: 0 18px 48px rgba(55, 43, 25, .07);
  backdrop-filter: blur(14px);
}

.mode-tabs { display: flex; gap: 28px; margin-bottom: 26px; border-bottom: 1px solid var(--line); }
.mode-tab {
  position: relative;
  padding: 0 1px 14px;
  color: var(--ink-faint);
  background: none;
  border: 0;
  cursor: pointer;
  font-weight: 600;
}
.mode-tab::after { content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--cinnabar); transform: scaleX(0); transition: transform 180ms ease; }
.mode-tab:hover, .mode-tab.is-active { color: var(--ink); }
.mode-tab.is-active::after { transform: scaleX(1); }

.search-form { position: relative; display: flex; align-items: center; }
.search-icon { position: absolute; left: 17px; display: grid; color: var(--ink-faint); pointer-events: none; }
.search-icon svg { width: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.search-form input {
  width: 100%;
  height: 58px;
  padding: 0 132px 0 51px;
  color: var(--ink);
  background: color-mix(in srgb, var(--paper-card) 86%, white 14%);
  border: 1px solid var(--line-strong);
  border-radius: 8px 18px 8px 18px;
  outline: 0;
}
.search-form input:focus, .endpoint-field input:focus { border-color: var(--cinnabar); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cinnabar) 15%, transparent); }
.search-form input::placeholder, .endpoint-field input::placeholder { color: var(--ink-faint); }

.primary-button, .secondary-button, .text-button {
  border: 0;
  cursor: pointer;
  transition: background-color 180ms ease, border-color 180ms ease, color 180ms ease, box-shadow 180ms ease;
}
.primary-button {
  height: 44px;
  padding: 0 21px;
  color: #fff8ee;
  background: var(--cinnabar);
  border-radius: 5px 13px 5px 13px;
  box-shadow: 0 7px 18px rgba(118, 42, 34, .18);
  white-space: nowrap;
}
.search-form .primary-button { position: absolute; right: 7px; }
.primary-button:hover { background: var(--cinnabar-dark); }
.clear-button { position: absolute; right: 91px; display: grid; width: 32px; height: 32px; padding: 0; place-items: center; color: var(--ink-faint); background: transparent; border: 0; border-radius: 50%; cursor: pointer; }
.clear-button:hover { color: var(--ink); background: var(--line); }
.clear-button svg { width: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.search-hint { margin: 12px 3px 0; color: var(--ink-faint); font-size: 12px; line-height: 1.7; }

.quick-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 2px 0; }
.quick-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.quick-label { color: var(--ink-faint); font-size: 12px; }
.chip {
  padding: 5px 12px;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font: 400 12px var(--serif);
  transition: 160ms ease;
}
.chip:hover { color: var(--cinnabar); border-color: color-mix(in srgb, var(--cinnabar) 50%, transparent); background: color-mix(in srgb, var(--cinnabar) 7%, transparent); }

.suggestions {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 22px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 22px), transparent);
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  left: 0;
  z-index: 20;
  max-height: 340px;
  overflow-y: auto;
  padding: 8px;
  background: var(--paper-solid);
  border: 1px solid var(--line-strong);
  border-radius: 8px 18px 8px 18px;
  box-shadow: 0 18px 48px rgba(34, 28, 19, .2);
}
.suggestion-item { display: grid; grid-template-columns: 33px 1fr auto; width: 100%; gap: 11px; align-items: center; padding: 10px 12px; text-align: left; color: var(--ink); background: transparent; border: 0; border-radius: 8px; cursor: pointer; }
.suggestion-item:hover, .suggestion-item.is-active { background: color-mix(in srgb, var(--cinnabar) 9%, transparent); }
.suggestion-mark { display: grid; width: 30px; height: 30px; place-items: center; color: var(--cinnabar); border: 1px solid color-mix(in srgb, var(--cinnabar) 38%, transparent); border-radius: 50%; font: 400 13px var(--serif); }
.suggestion-main { min-width: 0; }
.suggestion-main strong, .suggestion-main small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggestion-main strong { color: var(--ink); font: 400 15.5px var(--serif); }
.suggestion-main small, .suggestion-meta { margin-top: 3px; color: var(--ink-faint); font-size: 11px; }
.suggestion-meta { white-space: nowrap; }
.suggestion-meta.is-famous { color: var(--cinnabar); }
/* The typed prefix is context; the completion is the answer, so it carries the
   weight. No 着重号 here — the marks belong on poems being read, not on a list. */
.suggestion-main strong mark {
  background: none;
  color: var(--ink-faint);
  font-weight: 400;
  -webkit-text-emphasis-style: none;
  text-emphasis-style: none;
}
.suggestion-item.is-line .suggestion-mark { color: var(--gold, var(--cinnabar)); }

.suggestion-group { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 8px 12px 2px; color: var(--ink-faint); font-size: 11px; letter-spacing: .2em; }
.suggestion-group:first-child { margin-top: 2px; }
.suggestion-group small { letter-spacing: 0; }
.suggestion-waiting, .suggestion-footer { margin: 6px 12px; color: var(--ink-faint); font-size: 11px; }
.suggestion-footer { padding-top: 8px; border-top: 1px solid var(--line); }

/* Inline completion preview: the typed characters are painted invisible so the
   remainder sits exactly where the caret leaves off. */
.typeahead-ghost {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 51px;
  right: 132px;
  display: flex;
  align-items: center;
  overflow: hidden;
  color: var(--ink-faint);
  white-space: pre;
  pointer-events: none;
  user-select: none;
}
.typeahead-ghost i { color: transparent; font-style: normal; }
.typeahead-ghost b { font-weight: 400; opacity: .72; }

/* ---------------------------------------------------------------- views */

.site-nav { display: flex; gap: 4px; margin-left: 18px; }
.nav-link {
  padding: 7px 12px;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  font-size: 13px;
  white-space: nowrap;
}
.nav-link:hover { color: var(--ink); background: var(--line); }
.nav-link.is-active { color: var(--cinnabar); background: color-mix(in srgb, var(--cinnabar) 10%, transparent); }

.results-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 0 0 20px; }
.filter-anchor { position: relative; display: flex; align-items: center; gap: 8px; }

/* The box is easy to miss as a field alone, so the roster gets its own handle
   beside it: the caret says there is a list behind this, and it turns over while
   the list is open. */
.author-picker {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 36px;
  padding: 0 11px;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 6px 14px 6px 14px;
  cursor: pointer;
  white-space: nowrap;
  font: 400 13px var(--serif);
  transition: 160ms ease;
}
.author-picker svg { width: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: transform 160ms ease; }
.author-picker:hover { color: var(--cinnabar); border-color: color-mix(in srgb, var(--cinnabar) 50%, transparent); background: color-mix(in srgb, var(--cinnabar) 7%, transparent); }
.author-picker[aria-expanded="true"] { color: var(--cinnabar); border-color: color-mix(in srgb, var(--cinnabar) 55%, transparent); }
.author-picker[aria-expanded="true"] svg { transform: rotate(180deg); }

/* The field opens onto the poets themselves — an anthology's, ranked by how much
   of that book is theirs, or the corpora's, ranked by output — instead of asking
   the reader to guess a name. Typing narrows the same grid. */
.author-overlay {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  width: min(720px, calc(100vw - 48px));
  max-height: 340px;
  overflow-y: auto;
  padding: 14px 16px 16px;
  background: var(--paper-solid);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 2px 18px 2px 18px;
  box-shadow: 0 20px 50px rgba(30, 24, 14, .2);
}
.author-overlay-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.author-overlay-head p { margin: 0; color: var(--ink-faint); font-size: 12px; }
.author-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 2px 6px; }
.author-cell {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 10px;
  color: var(--ink);
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  text-align: left;
  font: 400 14px var(--serif);
}
.author-cell:hover { background: color-mix(in srgb, var(--gold) 14%, transparent); }
.author-cell[aria-pressed="true"] { color: var(--cinnabar); background: color-mix(in srgb, var(--cinnabar) 10%, transparent); }
.author-cell b { color: var(--ink-faint); font: 400 11px var(--sans); font-variant-numeric: tabular-nums; }
.author-cell[aria-pressed="true"] b { color: inherit; }
/* The "everyone" row is a switch back, not another poet: it spans the grid and is
   separated from the names below it. */
.author-cell.is-all {
  grid-column: 1 / -1;
  justify-content: flex-start;
  margin-bottom: 4px;
  padding-bottom: 9px;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  font-size: 13px;
}
.author-cell.is-all[aria-pressed="true"] { background: none; color: var(--cinnabar); }
.author-grid-empty { grid-column: 1 / -1; margin: 6px 2px 2px; color: var(--ink-faint); font-size: 12px; }
.filter-field { display: flex; align-items: center; gap: 8px; color: var(--ink-faint); font-size: 12px; }
.filter-field input {
  width: 190px;
  height: 36px;
  padding: 0 12px;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 6px 14px 6px 14px;
  outline: 0;
  font-size: 13px;
}
.filter-field input:focus { border-color: var(--cinnabar); }
.sort-toggle { display: flex; gap: 6px; }
.sort-toggle:first-of-type { margin-left: auto; }
.results-toolbar .sort-toggle + .sort-toggle { margin-left: 4px; padding-left: 12px; border-left: 1px solid var(--line); }
.chip.is-active { color: var(--cinnabar); border-color: color-mix(in srgb, var(--cinnabar) 45%, transparent); }

.browse-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 22px;
  padding: 18px 22px;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: 10px 22px 10px 22px;
}
.browse-facts { margin: 0; color: var(--ink-soft); font-size: 12.5px; line-height: 1.8; }
.content-section[data-mode="author"] .card-author { display: none; }
.content-section[data-mode="author"] .poem-card h3 { margin-bottom: 10px; }

/* The end of a browse view: how far in you are, and a real control for getting
   further, so scrolling is an accelerator rather than the only way through. */
.grid-more { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 14px; margin: 28px 0 4px; }
.grid-progress { margin: 0; color: var(--ink-faint); font-size: 12px; font-variant-numeric: tabular-nums; }
.scroll-sentinel { height: 1px; }

/* The picker that chooses what the 诗云 shows: 200 poets or 200 tunes is too many
   for a menu, so it is a proper window with a search box. */
.picker-dialog {
  width: min(720px, calc(100vw - 32px));
  max-height: min(78vh, 720px);
  padding: 22px 24px 24px;
  color: var(--ink);
  background: var(--paper-solid);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 4px 26px 4px 26px;
  box-shadow: 0 30px 70px rgba(30, 24, 14, .3);
}
.picker-dialog::backdrop { background: rgba(24, 19, 12, .42); backdrop-filter: blur(2px); }
.picker-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
.picker-head h2 { margin: 0; font: 400 26px var(--serif); letter-spacing: .1em; }
.picker-search { margin-bottom: 10px; }
.picker-search input { width: min(320px, 60vw); }
.picker-state { margin: 0 0 12px; color: var(--ink-faint); font-size: 12px; }
.picker-grid { max-height: min(52vh, 460px); overflow-y: auto; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
.picker-grid .author-cell { font-size: 14.5px; }

/* 诗云 — the cloud, its tooltip, and the cluster chips under it. */
.map-hint { margin: -14px 0 18px; color: var(--ink-faint); font-size: 12.5px; }
.map-stage {
  position: relative;
  height: min(68vh, 640px);
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--gold) 9%, transparent), transparent 60%),
    var(--paper-card);
  border: 1px solid var(--line);
  border-radius: 4px 26px 4px 26px;
  cursor: grab;
  touch-action: none;
}
.map-stage:active { cursor: grabbing; }
.map-stage canvas { display: block; width: 100%; height: 100%; }
.map-stage-title {
  position: absolute;
  top: 14px;
  left: 16px;
  z-index: 4;
  max-width: calc(100% - 32px);
  margin: 0;
  padding: 7px 12px 8px;
  overflow: hidden;
  color: var(--ink);
  background: color-mix(in srgb, var(--paper-solid) 88%, transparent);
  border: 1px solid var(--line);
  border-left: 3px solid var(--cinnabar);
  border-radius: 2px 12px 2px 12px;
  box-shadow: 0 8px 22px rgba(30, 24, 14, .1);
  font: 400 clamp(16px, 2vw, 20px)/1.3 var(--serif);
  letter-spacing: .08em;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
  backdrop-filter: blur(5px);
}
/* The poem under the pointer, on its own slip of paper, the cluster's ink on its edge. */
.map-tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  width: min(320px, 62vw);
  max-height: 60%;
  overflow: hidden;
  padding: 12px 15px 14px;
  background: var(--paper-solid);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--cluster-ink, var(--cinnabar));
  border-radius: 2px 14px 2px 14px;
  box-shadow: 0 18px 40px rgba(30, 24, 14, .26);
  pointer-events: none;
}
.map-tip-head { margin: 0 0 8px; }
.map-tip-head strong { display: block; color: var(--ink); font: 400 17px var(--serif); letter-spacing: .06em; }
.map-tip-head small { display: block; margin-top: 3px; color: var(--ink-faint); font-size: 11.5px; }
.map-tip-body { color: var(--ink-soft); font: calc(14px * var(--reading-scale))/1.95 var(--serif); }
.map-tip-body p { margin: 0; }
.map-legend { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.map-legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font: 400 12.5px var(--serif);
  letter-spacing: .06em;
  transition: 160ms ease;
}
.map-legend-chip i { width: 9px; height: 9px; border-radius: 2px; }
.map-legend-chip b { color: var(--ink-faint); font: 400 11px var(--sans); font-variant-numeric: tabular-nums; }
.map-legend-chip:hover { color: var(--ink); border-color: var(--line-strong); }
.map-legend-chip[aria-pressed="true"] { color: var(--ink); border-color: color-mix(in srgb, var(--cinnabar) 50%, transparent); background: color-mix(in srgb, var(--cinnabar) 7%, transparent); }

/* Choosing a cluster names its poems under the cloud: the picture says how they
   sit together, the list says which they are. */
.map-list { margin-top: 26px; }
.map-list-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.map-list-head h3 { margin: 0; color: var(--ink); font: 400 20px var(--serif); letter-spacing: .1em; }
.map-list-head h3 b { color: var(--ink-faint); font: 400 12px var(--sans); letter-spacing: .04em; }

.author-list { display: grid; gap: 0; margin: 0; padding: 0; max-width: 1020px; list-style: none; }
/* A ranked table, so it is set like one: fixed columns, tabular figures, and the
   quantities aligned on their own edge instead of floating after the names. */
.author-row {
  display: grid;
  grid-template-columns: 46px minmax(132px, 210px) 92px minmax(140px, 1fr) 132px;
  gap: 16px;
  align-items: center;
  padding: 9px 14px 9px 10px;
  border-bottom: 1px solid var(--line);
  transition: background-color 160ms ease, box-shadow 160ms ease;
}
.author-row:first-child { border-top: 1px solid var(--rule); }
.author-row:hover { background: color-mix(in srgb, var(--gold) 7%, transparent); box-shadow: inset 3px 0 0 var(--cinnabar); }
.author-rank { color: var(--ink-faint); font-size: 12px; font-variant-numeric: tabular-nums; text-align: right; }
.author-name-link {
  padding: 0;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font: 400 17px var(--serif);
  letter-spacing: .06em;
}
.author-name-link:hover { color: var(--cinnabar); }
.author-count { color: var(--ink-soft); font-size: 12.5px; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* One track, three inks: 唐诗 vermilion, 宋诗 gold, 宋词 ink — the split is the
   information, so the segments sit flush rather than as separate bars. */
.author-bar { display: flex; height: 6px; background: color-mix(in srgb, var(--ink) 6%, transparent); border-radius: 1px; overflow: hidden; }
.author-bar i { display: block; height: 100%; }
.author-bar i[data-corpus="tang"] { background: color-mix(in srgb, var(--cinnabar) 62%, transparent); }
.author-bar i[data-corpus="song"] { background: color-mix(in srgb, var(--gold, #b8894a) 68%, transparent); }
.author-bar i[data-corpus="ci"] { background: color-mix(in srgb, var(--ink-soft) 42%, transparent); }
.author-marks { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: flex-end; }
/* A tune's other names — 念奴娇 was also filled as 酹江月, 百字令, 大江东去 — kept
   quiet beside the count, but present: they are how a reader finds the tune when
   the anthology they remember used a different name for it. */
.tune-row { grid-template-columns: 46px minmax(120px, 200px) 78px minmax(110px, 1fr) 64px minmax(150px, 300px); }
.tune-alias {
  color: var(--ink-faint);
  font-size: 11.5px;
  letter-spacing: .04em;
  /* One line, however many names a tune went by; the tooltip holds them all. */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.tune-authors { color: var(--ink-soft); font-size: 11.5px; font-variant-numeric: tabular-nums; text-align: right; }
/* The tune of a 词, in the reading dialog: the poem's title *is* the tune, so this
   is the one place it can be a way through to everything else in that form. */
.tune-link {
  padding: 0 0 0 10px;
  margin-left: 2px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-left: 1px solid var(--line);
  cursor: pointer;
  font: inherit;
}
.tune-link:hover { color: var(--cinnabar); }

/* A line with a vector of its own is a way into every other line like it, so it
   answers the pointer — but quietly: a poem should not read as a row of buttons.
   The cue is the same vermilion rule the shared-phrase marks use. */
.poem-line { cursor: pointer; border-radius: 2px; transition: background-color 140ms ease, box-shadow 140ms ease; }
.poem-line:hover { background: color-mix(in srgb, var(--gold) 13%, transparent); box-shadow: 0 1px 0 var(--cinnabar); }
.poem-line.is-open { background: color-mix(in srgb, var(--cinnabar) 9%, transparent); box-shadow: 0 1px 0 var(--cinnabar); }
.poem-line:focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
/* The line whose neighbours are showing, quoted above them. */
.line-source { margin: 0 0 6px; color: var(--ink); font: 400 calc(17px * var(--reading-scale)) var(--serif); letter-spacing: .04em; }
/* A line card is one line long, so it sits tighter than a poem card. */
/* The same inset the poem cards give their text: a line, and the poem it sits in,
   should start where a card's text starts rather than against its edge. */
.similar-card .similar-body { margin: 0; padding: 14px 18px 17px; }
.similar-card.is-line .similar-body p { margin: 0; font-size: calc(16px * var(--reading-scale)); }
/* Expanded, the card carries the whole poem, and the line that matched is the one
   thing that must not be lost in it: vermilion ink on a tinted ground, with the
   rest of the poem in the softer colour a neighbour's text always uses. */
.similar-card.is-line.is-full .similar-body { color: var(--ink-soft); font: calc(15.5px * var(--reading-scale))/2.05 var(--serif); }
.similar-card.is-line.is-full .similar-body p { font-size: inherit; }
.matched-line {
  display: block;
  margin: 0 -6px;
  padding: 0 6px;
  color: var(--cinnabar-dark);
  background: color-mix(in srgb, var(--cinnabar) 8%, transparent);
  border-radius: 2px;
  box-shadow: inset 2px 0 0 var(--cinnabar);
}
.tier-badge { color: var(--gold); font-size: 11px; letter-spacing: .18em; }

@media (max-width: 760px) {
  .site-header { flex-wrap: wrap; min-height: 0; padding-top: 8px; padding-bottom: 8px; gap: 10px; row-gap: 6px; }
  /* Two rows, always the same two: brand with the toggles, then the views. The
     condensed header used to swap the nav for a search box, which meant that
     after one scroll there was no way to reach 作者 or the anthologies at all.
     The hero's own search box is one tap away up top. */
  .mini-search { display: none; }
  .header-actions { gap: 10px; margin-left: auto; }
  /* Wrapping, not scrolling: a horizontally scrolled nav hid 唐诗三百首 and
     宋词三百首 off the right edge with nothing to say they were there. */
  .site-nav { order: 3; flex-wrap: wrap; width: 100%; margin: 2px 0 0; row-gap: 4px; }
  .nav-link { padding: 6px 10px; font-size: 12px; }
  .author-row { grid-template-columns: 38px 1fr auto; grid-template-areas: "rank name count" ". bar bar" ". marks marks"; row-gap: 7px; }
  .tune-row { grid-template-columns: 38px 1fr auto; grid-template-areas: "rank name count" ". bar bar" ". hands alias"; }
  .tune-authors { grid-area: hands; text-align: left; }
  .tune-alias { grid-area: alias; text-align: right; }
  .author-rank { grid-area: rank; }
  .author-name-link { grid-area: name; }
  .author-count { grid-area: count; }
  .author-bar { grid-area: bar; }
  .author-marks { grid-area: marks; justify-content: flex-start; }
  .sort-toggle { margin-left: 0; }
}

.anthology-badge {
  display: inline-block;
  padding: 1px 6px;
  color: var(--cinnabar);
  background: none;
  border: 1px solid color-mix(in srgb, var(--cinnabar) 32%, transparent);
  border-radius: 2px;
  font-size: 10px;
  letter-spacing: .08em;
  vertical-align: middle;
}

.endpoint-form { display: grid; grid-template-columns: 1fr 28px 1fr auto; gap: 12px; align-items: end; }
.endpoint-field label { display: block; margin: 0 0 8px 3px; color: var(--ink-soft); font-size: 12px; }
.endpoint-field input { width: 100%; height: 48px; padding: 0 14px; color: var(--ink); background: transparent; border: 1px solid var(--line-strong); border-radius: 7px 14px 7px 14px; outline: none; }
.endpoint-line { height: 1px; margin-bottom: 24px; background: var(--line-strong); }
.endpoint-form .primary-button { height: 48px; }

.corpus-filters { display: flex; flex-wrap: wrap; gap: 9px; margin: 24px 0 0; padding: 20px 0 0; border: 0; border-top: 1px solid var(--line); }
/* Sits with the corpus chips and reads the same, but chooses one of three. */
.scope-filters { margin-top: 22px; padding-top: 18px; }
.corpus-filters legend { float: left; margin-right: 8px; padding: 8px 0; color: var(--ink-faint); font-size: 12px; }
.corpus-filters label { cursor: pointer; }
.corpus-filters input { position: absolute; opacity: 0; pointer-events: none; }
.corpus-filters span { display: inline-flex; gap: 6px; padding: 7px 11px; color: var(--ink-soft); border: 1px solid var(--line); border-radius: 999px; font-size: 12px; transition: 180ms ease; }
.corpus-filters b { font-weight: 500; color: var(--ink-faint); }
.corpus-filters input:checked + span { color: var(--cinnabar-dark); background: color-mix(in srgb, var(--cinnabar) 9%, transparent); border-color: color-mix(in srgb, var(--cinnabar) 45%, transparent); }
.corpus-filters label.is-empty span { opacity: .45; }
.corpus-filters input:focus-visible + span { outline: 2px solid var(--cinnabar); outline-offset: 2px; }

/* ----------------------------------------------------------------- results */

.content-section { padding: 52px 0 80px; border-top: 1px solid var(--line); }
.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 24px; margin-bottom: 26px; }
.section-heading .section-kicker { margin-bottom: 8px; }
.section-heading h2 { margin: 0; font: 400 clamp(26px, 4vw, 40px)/1.25 var(--serif); letter-spacing: .08em; overflow-wrap: anywhere; }
.heading-actions { display: flex; align-items: center; justify-content: flex-end; gap: 14px; }
.search-state { max-width: 25rem; color: var(--ink-faint); font-size: 13px; text-align: right; }

.progress-track { position: relative; height: 2px; margin: -10px 0 24px; overflow: hidden; background: var(--line); }
.progress-track span { position: absolute; inset: 0 auto 0 0; width: 32%; background: var(--cinnabar); animation: sweep 1.05s ease-in-out infinite; }
@keyframes sweep { 0% { transform: translateX(-110%); } 100% { transform: translateX(420%); } }

/*
 * Cards take the height their poem needs, and the grid packs them.
 *
 * Rows of equal-height cards are what forced every poem down to three lines; the
 * obvious fix — letting each card size itself — leaves a ragged gap under every
 * short poem, because a grid row is as tall as its tallest card. So the tracks are
 * a fine 4px ruler instead: each card spans as many of those as it needs (set from
 * script, see `packGrid`), and dense flow drops the next card into the first hole
 * that fits. `masonry` would do this in CSS alone, but no shipping browser has it.
 */
.poem-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 4px;
  grid-auto-flow: row dense;
  column-gap: 18px;
  align-items: start;
}
.poem-card { grid-row-end: span var(--span, 60); }
/*
 * A poem sits in a 版框 — the frame a block-printed page draws around its text —
 * rather than in a floating card: hairline rule all round, doubled at the spine
 * edge, and no drop shadow. Paper does not hover above paper.
 */
.poem-card {
  position: relative;
  min-height: 172px;
  padding: 22px 24px 22px 26px;
  overflow: hidden;
  color: var(--ink);
  text-align: left;
  display: block;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 2px 14px 2px 14px;
  cursor: pointer;
  transition: border-color 200ms ease, background-color 200ms ease;
  animation: card-in 320ms ease both;
}

.poem-card::after { content: ""; position: absolute; top: -1px; bottom: -1px; left: -3px; width: 3px; background: var(--cinnabar); transform: scaleY(0); transform-origin: bottom; transition: transform 220ms ease; }
.poem-card:hover { border-color: color-mix(in srgb, var(--ink) 34%, transparent); background: color-mix(in srgb, var(--paper-card) 94%, var(--gold) 6%); }
.poem-card:hover::after { transform: scaleY(1); }
.poem-card:focus-within { border-color: color-mix(in srgb, var(--cinnabar) 55%, transparent); }
.card-corpus { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; color: var(--ink-faint); font-size: 10px; letter-spacing: .2em; }
/* Some Song records carry a whole preface as the title; clamp so one card cannot
   stretch its whole grid row. The dialog still shows it in full. */
.poem-card h3 {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  font: 400 calc(21px * var(--reading-scale))/1.5 var(--serif);
  letter-spacing: .06em;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
.card-open { padding: 0; color: inherit; text-align: left; background: none; border: 0; cursor: pointer; font: inherit; }
.card-open::after { content: ""; position: absolute; inset: 0; }
.card-author { margin: 7px 0 18px; color: var(--ink-faint); font: 13px var(--serif); }

/* Sits above the card-wide hit area so an author name is its own action. */
.author-link {
  position: relative;
  z-index: 1;
  padding: 0;
  color: inherit;
  background: none;
  border: 0;
  text-decoration: none;
  cursor: pointer;
  font: inherit;
  transition: color 160ms ease;
}
.author-link:hover { color: var(--cinnabar); }
/* The line cap above counts the poem's own lines; this one is the backstop for a
   card whose lines wrap, so a single card can never run away with the page. */
.card-content { display: -webkit-box; margin: 0; overflow: hidden; color: var(--ink-soft); font: calc(15.5px * var(--reading-scale))/2 var(--serif); -webkit-box-orient: vertical; -webkit-line-clamp: 18; white-space: pre-line; }
/* Only the ~4% of poems too long for a card fade out; the rest simply end. */
.card-content.is-clipped {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 30px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 30px), transparent);
}
.matched-sentence { margin-top: 13px; padding-top: 12px; color: var(--cinnabar-dark); border-top: 1px solid var(--line); font: 13px/1.7 var(--serif); }

.poem-card.is-skeleton { min-height: 236px; cursor: default; animation: none; }
.poem-card.is-skeleton::before, .poem-card.is-skeleton::after { display: none; }
.poem-card.is-skeleton span {
  display: block;
  height: 13px;
  margin-bottom: 14px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--line) 25%, color-mix(in srgb, var(--line) 40%, transparent) 50%, var(--line) 75%);
  background-size: 280% 100%;
  animation: shimmer 1.3s linear infinite;
}
.poem-card.is-skeleton span:nth-child(1) { width: 22%; }
.poem-card.is-skeleton span:nth-child(2) { width: 62%; height: 20px; }
.poem-card.is-skeleton span:nth-child(3) { width: 34%; }
.poem-card.is-skeleton span:nth-child(4) { width: 88%; height: 52px; }
@keyframes shimmer { from { background-position: 140% 0; } to { background-position: -140% 0; } }

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 14px;
  padding: 36px 0 4px;
}
.pager-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 38px;
  padding: 0 14px;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 5px 12px 5px 12px;
  cursor: pointer;
  font-size: 13px;
  transition: 160ms ease;
}
.pager-step:hover:not(:disabled) { color: var(--cinnabar); border-color: var(--cinnabar); }
.pager-step:disabled { opacity: .35; cursor: default; }
.pager-step svg { width: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.pager-numbers { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.pager-number {
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  transition: 160ms ease;
}
.pager-number:hover { color: var(--ink); background: color-mix(in srgb, var(--gold) 12%, transparent); }
.pager-number.is-current {
  color: #fff8ee;
  background: var(--cinnabar);
  border-color: var(--cinnabar);
  font-weight: 600;
}
.pager-gap { padding: 0 2px; color: var(--ink-faint); }

.pager-jump, .pager-size { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-faint); font-size: 12px; }
.pager-jump input {
  width: 76px;
  height: 32px;
  padding: 0 8px;
  color: var(--ink);
  text-align: center;
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  outline: 0;
  font-variant-numeric: tabular-nums;
}
.pager-jump input:focus { border-color: var(--cinnabar); }
.pager-size select {
  height: 32px;
  padding: 0 6px;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  cursor: pointer;
  font: inherit;
}

.empty-state { padding: 70px 20px; color: var(--ink-faint); text-align: center; border: 1px dashed var(--line-strong); border-radius: 3px 24px; }
.empty-state svg { width: 58px; fill: none; stroke: currentColor; stroke-width: 1.2; }
.empty-state h3 { margin: 18px 0 8px; color: var(--ink); font: 400 21px var(--serif); }
.empty-state p { margin: 0; font-size: 13px; }

footer { width: min(1240px, calc(100% - 40px)); margin: 0 auto; padding: 28px 0 38px; display: flex; justify-content: space-between; gap: 24px; color: var(--ink-faint); border-top: 1px solid var(--line); font-size: 11px; letter-spacing: .08em; }
/* ------------------------------------------------------------------ dialog */

.poem-dialog {
  display: none;
  width: min(1140px, calc(100% - 28px));
  max-height: min(920px, calc(100vh - 28px));
  padding: 0;
  overflow: hidden;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 4px 30px 4px 30px;
  box-shadow: 0 35px 100px rgba(20, 16, 10, .35);
}
.poem-dialog[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.poem-dialog::backdrop { background: rgba(22, 20, 17, .62); backdrop-filter: blur(5px); }

.dialog-topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  background: color-mix(in srgb, var(--paper) 88%, var(--paper-deep) 12%);
  border-bottom: 1px solid var(--line);
}
.dialog-topbar-actions { display: flex; gap: 8px; margin-left: auto; }
.dialog-trail { display: flex; align-items: center; gap: 7px; min-width: 0; overflow: hidden; }
.trail-item {
  max-width: 15ch;
  overflow: hidden;
  padding: 0;
  color: var(--ink-faint);
  text-overflow: ellipsis;
  white-space: nowrap;
  background: none;
  border: 0;
  cursor: pointer;
  font: 400 13px var(--serif);
}
.trail-item:hover { color: var(--cinnabar); text-decoration: underline; }
.trail-item.is-current { color: var(--ink); cursor: default; }
.trail-separator { color: var(--ink-faint); font-size: 12px; }

.dialog-tabs { display: none; }
.dialog-body { display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr); min-height: 0; overflow: hidden; }
.poem-full { padding: clamp(38px, 5vw, 64px) clamp(24px, 4vw, 60px); overflow-y: auto; text-align: center; border-right: 1px solid var(--line); }
.poem-dynasty { margin: 0 0 16px; color: var(--cinnabar); font-size: 11px; letter-spacing: .35em; }
.poem-full h2 { margin: 0; font: 400 calc(clamp(26px, 3.4vw, 38px) * var(--reading-scale))/1.35 var(--serif); letter-spacing: .12em; }
.poem-author { margin: 10px 0 32px; color: var(--ink-faint); font: 14px var(--serif); }
.poem-content { display: inline-block; text-align: left; font: calc(18px * var(--reading-scale))/2.25 var(--serif); letter-spacing: .06em; }
.poem-content p { margin: 0; }
.poem-actions { display: flex; justify-content: center; gap: 10px; margin-top: 34px; }

.secondary-button, .text-button {
  display: inline-flex;
  height: 43px;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 16px;
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 5px 12px 5px 12px;
  cursor: pointer;
}
.secondary-button:hover { color: #fff8ee; background: var(--cinnabar); border-color: var(--cinnabar); }
.text-button:hover { color: var(--cinnabar); border-color: var(--cinnabar); }
.text-button.compact { height: 32px; padding: 0 12px; font-size: 13px; }
.secondary-button svg, .text-button svg { width: 18px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.link-button { padding: 0; color: var(--cinnabar); background: none; border: 0; cursor: pointer; font-size: 13px; }
.link-button:hover { text-decoration: underline; }

/* Similar poems: full text on the card, because judging a match should not cost a click. */
.similar-panel { position: relative; padding: 30px 26px 34px; overflow-y: auto; overscroll-behavior: contain; background: color-mix(in srgb, var(--paper-deep) 62%, transparent); }
.similar-heading-row { display: flex; align-items: end; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.similar-heading-row .section-kicker { margin-bottom: 5px; }
.similar-heading-row h3 { margin: 0; font: 400 24px var(--serif); }
.similar-heading-row h3 b { margin-left: 8px; color: var(--ink-faint); font: 400 12px var(--sans); }
.similar-legend { margin: 0 0 16px; color: var(--ink-faint); font-size: 11px; line-height: 1.7; }
.similar-legend em { font-style: normal; text-decoration: underline; text-decoration-color: var(--gold); text-decoration-thickness: 2px; text-underline-offset: 2px; }
.similar-list { display: grid; gap: 12px; }

/* Full text is the default; compact is an escape hatch when scanning 100 poems.
   Whole lines are dropped rather than the block being clipped, so nothing is
   ever cut through the middle of a character. */
.similar-list.is-compact .similar-text p + p + p { display: none; }
.similar-list.is-compact .similar-text p:nth-child(2):not(:last-child)::after { content: "…"; color: var(--ink-faint); }

.segmented { display: inline-flex; padding: 2px; background: color-mix(in srgb, var(--line) 55%, transparent); border-radius: 999px; }
.segmented button { padding: 5px 12px; color: var(--ink-faint); background: transparent; border: 0; border-radius: 999px; cursor: pointer; font-size: 12px; transition: 160ms ease; }
.segmented button.is-active { color: var(--ink); background: var(--paper-solid); box-shadow: 0 1px 4px rgba(40, 32, 20, .12); }

.similar-more { display: flex; justify-content: center; padding: 18px 0 4px; }
.similar-more .secondary-button { height: 38px; font-size: 13px; }

.panel-top {
  position: sticky;
  bottom: 10px;
  left: 100%;
  display: grid;
  width: 38px;
  height: 38px;
  place-items: center;
  color: var(--ink-soft);
  background: var(--paper-solid);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  box-shadow: 0 8px 20px rgba(40, 32, 20, .18);
  cursor: pointer;
}
.panel-top:hover { color: var(--cinnabar); border-color: var(--cinnabar); }
.panel-top svg { width: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* Phrases the neighbour shares with the poem being read. */
.similar-text u.shared {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--gold) 70%, transparent);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

.similar-card {
  overflow: hidden;
  background: var(--paper-solid);
  border: 1px solid var(--rule);
  border-left: 3px double var(--rule);
  border-radius: 2px 12px 2px 12px;
  transition: border-color 180ms ease;
}
.similar-card:hover { border-color: color-mix(in srgb, var(--cinnabar) 45%, transparent); }

/* Sticky so the poem you are reading is always labelled, however long it runs. */
.similar-card header {
  position: sticky;
  top: -1px;
  z-index: 1;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  padding: 11px 16px;
  background: color-mix(in srgb, var(--paper-solid) 96%, var(--gold) 4%);
  border-bottom: 1px solid var(--line);
}
/* Neighbours are ranked by similarity, so the position genuinely is information. */
.similar-rank { color: var(--ink-faint); font: 400 13px var(--sans); font-variant-numeric: tabular-nums; }
.similar-identity { min-width: 0; }
.similar-identity strong { display: block; overflow: hidden; color: var(--ink); font: 400 16px var(--serif); letter-spacing: .04em; text-overflow: ellipsis; white-space: nowrap; }
.similar-identity small { display: block; margin-top: 3px; color: var(--ink-faint); font-size: 11px; }
.similar-score { display: grid; gap: 4px; justify-items: end; color: var(--ink-soft); font-size: 11px; font-variant-numeric: tabular-nums; }
.similar-gauge { display: block; width: 52px; height: 2px; overflow: hidden; background: var(--line); }
.similar-gauge i { display: block; height: 100%; background: var(--gold); }

/* Never clamped: the whole point of the panel is reading the neighbour in full. */
.similar-text { margin: 0; padding: 14px 18px 17px; color: var(--ink-soft); font: calc(15.5px * var(--reading-scale))/2.05 var(--serif); }
.similar-text p { margin: 0; }
.similar-loading { padding: 36px 0; color: var(--ink-faint); text-align: center; font-size: 13px; }

.scroll-top {
  position: fixed;
  right: 26px;
  bottom: 26px;
  z-index: 25;
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  color: var(--ink-soft);
  background: var(--paper-solid);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  box-shadow: 0 10px 26px rgba(40, 32, 20, .18);
  cursor: pointer;
}
.scroll-top:hover { color: var(--cinnabar); border-color: var(--cinnabar); }
.scroll-top svg { width: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.help-dialog {
  width: min(430px, calc(100% - 32px));
  padding: 30px 32px 26px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 4px 24px 4px 24px;
  box-shadow: 0 30px 80px rgba(20, 16, 10, .34);
}
.help-dialog::backdrop { background: rgba(22, 20, 17, .55); backdrop-filter: blur(4px); }
.help-dialog h2 { margin: 0 0 20px; font: 400 22px var(--serif); letter-spacing: .08em; }
.help-dialog dl { display: grid; gap: 12px; margin: 0 0 24px; }
.help-subhead { margin: 0 0 12px; color: var(--ink-soft); font: 400 14px var(--serif); letter-spacing: .12em; }
.help-notes { margin: 0 0 24px; padding-left: 18px; color: var(--ink-soft); font-size: 13px; line-height: 1.9; }
.help-dialog dl > div { display: grid; grid-template-columns: 108px 1fr; gap: 14px; align-items: baseline; }
.help-dialog dt { display: flex; gap: 4px; margin: 0; }
.help-dialog dd { margin: 0; color: var(--ink-soft); font-size: 13px; }
.help-dialog kbd {
  display: inline-block;
  min-width: 22px;
  padding: 3px 7px;
  text-align: center;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  font: 600 11px/1.3 var(--sans);
}
.help-dialog .secondary-button { width: 100%; }

.feedback-dialog {
  width: min(560px, calc(100% - 28px));
  max-height: min(760px, calc(100dvh - 28px));
  padding: 0;
  overflow: auto;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 4px 28px 4px 28px;
  box-shadow: 0 30px 90px rgba(20, 16, 10, .36);
}
.feedback-dialog::backdrop { background: rgba(22, 20, 17, .62); backdrop-filter: blur(5px); }
.feedback-head { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 24px 28px 18px; border-bottom: 1px solid var(--line); }
.feedback-head .section-kicker { margin: 0 0 4px; }
.feedback-head h2 { margin: 0; font: 400 24px var(--serif); letter-spacing: .08em; }
.feedback-subject { margin: 22px 28px 0; color: var(--ink-soft); font: 400 15px var(--serif); }
.feedback-selection { margin: 16px 28px 0; padding: 14px 16px; background: color-mix(in srgb, var(--paper-deep) 70%, transparent); border: 0; border-left: 3px solid var(--gold); border-radius: 2px 10px 2px 10px; }
.feedback-selection span { display: block; margin-bottom: 6px; color: var(--ink-faint); font-size: 10px; letter-spacing: .18em; }
.feedback-selection p { margin: 0; color: var(--ink-soft); font: 14px/1.8 var(--serif); white-space: pre-wrap; }
.feedback-dialog form { position: relative; padding: 20px 28px 28px; }
.feedback-field { display: grid; gap: 8px; }
.feedback-field > span { font-size: 13px; font-weight: 600; }
.feedback-field textarea { width: 100%; min-height: 150px; resize: vertical; padding: 13px 14px; color: var(--ink); background: var(--paper-solid); border: 1px solid var(--line-strong); border-radius: 4px 14px 4px 14px; font: 15px/1.75 var(--sans); }
.feedback-field textarea::placeholder { color: var(--ink-faint); }
.feedback-field small { color: var(--ink-faint); text-align: right; font-size: 10px; }
.feedback-privacy { margin: 15px 0 0; color: var(--ink-faint); font-size: 11px; line-height: 1.75; }
.feedback-status { min-height: 22px; margin: 12px 0 0; color: var(--ink-soft); font-size: 12px; line-height: 1.7; }
.feedback-status.is-error { color: var(--cinnabar-dark); }
.feedback-status.is-success { color: var(--ink); font-weight: 600; }
.feedback-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }
.feedback-actions .primary-button { position: static; min-width: 118px; }
.feedback-actions button { min-height: 44px; }
.feedback-dialog[data-state="sending"] { cursor: progress; }
.report-honeypot { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }

.toast {
  position: fixed;
  bottom: 28px;
  left: 50%;
  z-index: 60;
  padding: 11px 20px;
  color: #fff8ee;
  background: var(--cinnabar-dark);
  border-radius: 6px 16px 6px 16px;
  box-shadow: 0 16px 40px rgba(20, 16, 10, .3);
  font-size: 13px;
  opacity: 0;
  transform: translate(-50%, 12px);
  transition: opacity 200ms ease, transform 200ms ease;
}
.toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.noscript { margin: 20px; padding: 14px; color: white; text-align: center; background: var(--cinnabar-dark); }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 3px; }

@keyframes card-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

@media (max-width: 1080px) {
  .mini-search { flex-basis: 260px; }
  .collection-count { display: none; }
}

@media (max-width: 920px) {
  .hero { grid-template-columns: 1fr; min-height: 0; gap: 40px; padding: 44px 0 52px; }
  .hero-copy { max-width: 650px; }
  .hero::before { display: none; }
  .poem-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dialog-tabs { display: flex; gap: 6px; padding: 0 14px 12px; border-bottom: 1px solid var(--line); }
  .dialog-tabs button {
    flex: 1;
    padding: 9px 10px;
    color: var(--ink-faint);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 999px;
    cursor: pointer;
    font-size: 13px;
  }
  .dialog-tabs button.is-active { color: var(--cinnabar-dark); background: color-mix(in srgb, var(--cinnabar) 9%, transparent); border-color: color-mix(in srgb, var(--cinnabar) 40%, transparent); }
  .dialog-tabs b { font-weight: 500; }
  .poem-dialog[open] { grid-template-rows: auto auto minmax(0, 1fr); }
  .dialog-body { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr); overflow: hidden; }
  .poem-full, .similar-panel { overflow-y: auto; border-right: 0; }
  .poem-dialog[data-tab="poem"] .similar-panel { display: none; }
  .poem-dialog[data-tab="similar"] .poem-full { display: none; }
}

@media (max-width: 620px) {
  .site-header { padding: 0 14px; gap: 10px; }
  main, footer { width: min(100% - 28px, 1240px); }
  .brand small { display: none; }
  .script-toggle span { width: 26px; height: 26px; font-size: 13px; }
  .mini-search { flex-basis: 100%; max-width: none; }
  .hero { padding: 34px 0 44px; }
  .hero h1 { font-size: clamp(36px, 12vw, 54px); }
  .hero-description { margin-top: 18px; font-size: 14px; }
  .hero-hint { display: none; }
  .search-card { padding: 20px 16px 34px; border-radius: 3px 21px 3px 21px; }
  .quick-row .chip { min-height: 44px; }
  .mode-tabs { margin-bottom: 20px; }
  .search-form { align-items: stretch; flex-wrap: wrap; gap: 10px; }
  .search-form input { height: 52px; padding-right: 49px; }
  .search-form .primary-button { position: static; width: 100%; }
  /* The submit button drops below the field here, so the ghost only clears the × button. */
  .typeahead-ghost { right: 49px; bottom: auto; height: 52px; }
  .clear-button { top: 10px; right: 10px; }
  .endpoint-form { grid-template-columns: 1fr 14px 1fr; }
  .endpoint-form .primary-button { grid-column: 1 / -1; }
  .corpus-filters legend { width: 100%; padding: 0; }
  .content-section { padding: 40px 0 56px; }
  .section-heading { align-items: start; flex-direction: column; }
  .search-state { text-align: left; }
  .poem-grid { grid-template-columns: 1fr; }
  .poem-card { min-height: 200px; }
  .scroll-top { right: 14px; bottom: 84px; }
  .pager { gap: 8px; }
  .pager-step span { display: none; }
  .pager-step { padding: 0 12px; }
  .pager-jump { display: none; }
  footer { flex-direction: column; gap: 7px; }
  footer p { margin: 0; }
  .poem-dialog { width: calc(100% - 12px); max-height: calc(100dvh - 12px); }
  .dialog-topbar { padding: 10px 12px; }
  .poem-full { padding: 34px 20px 30px; }
  .poem-content { font-size: calc(16px * var(--reading-scale)); }
  .similar-panel { padding: 24px 14px 28px; }
  .feedback-link { padding: 0 4px; }
  .feedback-dialog { width: calc(100% - 12px); max-height: calc(100dvh - 12px); }
  .feedback-head { padding: 18px 18px 14px; }
  .feedback-subject { margin: 18px 18px 0; }
  .feedback-selection { margin: 14px 18px 0; }
  .feedback-dialog form { padding: 18px; }
  .feedback-actions { display: grid; grid-template-columns: 1fr 1fr; }
  .feedback-actions .text-button, .feedback-actions .primary-button { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* 另见 — the copy of this poem the corpus also holds, under another title.
   Set quieter than the poem and tighter than the 相似 cards: it is a footnote to
   the text above, not a second poem competing with it. */
.poem-variants {
  margin-top: 22px;
  padding-top: 14px;
  border-top: 1px dashed var(--rule, #d8d2c4);
  font-size: 0.92em;
}

.poem-variants-label {
  display: block;
  margin-bottom: 8px;
  font-size: 0.82em;
  letter-spacing: 0.08em;
  color: var(--ink-soft, #7a7263);
}

.poem-variant + .poem-variant { margin-top: 12px; }

.poem-variant-title {
  font-weight: 600;
  color: var(--ink-soft, #7a7263);
}

.poem-variant-text {
  margin: 4px 0 0;
  color: var(--ink-soft, #7a7263);
  line-height: 1.75;
}

/* The characters this copy differs in — the reason the note is worth reading. */
.poem-variant-diff {
  color: var(--accent, #9d3d2f);
  font-weight: 600;
}

/* The colophon is a third line in a footer laid out with space-between, so it
   needs its own basis or it squeezes the two lines beside it. */
footer .colophon { flex: 1 1 100%; order: 3; text-align: left; opacity: .85; }
footer .colophon a { color: inherit; }
footer .colophon a:hover { color: var(--accent, #9d3d2f); }
@media (min-width: 900px) { footer { flex-wrap: wrap; } }
