/* App layout and player (spec 2026-10-20-player-ux §3, §4, §6). The shell rules apply
   only when JavaScript runs (html.js, set by js/settings.js); without it chapter pages
   stay normal documents styled by base.css. */

/* Hidden without JavaScript */
html:not(.js) .player, html:not(.js) .strip, html:not(.js) .tog,
html:not(.js) .app-title, html:not(.js) .sheet-backdrop { display: none; }
html:not(.js) .chapter-list { max-width: var(--measure); margin: var(--space-5) auto; padding: 0 var(--space-3); }

/* Pinyin and translation toggles */
/* Hidden pinyin keeps its space, so turning it on or off never moves the text (spec 2026-10-21-desktop-polish §2). */
.no-pinyin rt { visibility: hidden; }
.no-tr .tr { display: none; }

/* Chinese text: pinyin as ruby, highlight states */
.zh rt { font-family: var(--font-text); font-size: 0.36em; color: var(--text-muted); letter-spacing: 0; }
.zh .pu { color: var(--text-muted); }
.lines.live [data-i] { color: var(--text-dim); transition: color 0.12s, text-shadow 0.12s; }
.lines.live .line.done [data-i], .lines.live [data-i].p { color: var(--text); }
.lines.live [data-i].n { color: var(--text); text-shadow: 0 0 0.45em var(--glow); }
.lines.live [data-i].n rt { color: var(--red-text); }
.line.active { background: linear-gradient(90deg, var(--line-active), transparent 70%); box-shadow: inset 2px 0 0 var(--red); }
html.js .line[data-start] { cursor: pointer; }
/* A line opened from a shared link (#lK): marked for a moment (js/router.js) */
.line.linked { animation: line-linked 3s ease-out; }
@keyframes line-linked { 0%, 50% { background-color: var(--line-active); box-shadow: inset 3px 0 0 var(--red); } }
@media (prefers-reduced-motion: reduce) {
  .line.linked { animation: none; background-color: var(--line-active); box-shadow: inset 3px 0 0 var(--red); }
}

/* Header tools */
.site-header .tools { display: flex; align-items: center; gap: var(--space-2); margin-left: auto; }
.tog, .icon-btn { appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem; min-width: 2.25rem; height: 2.25rem; padding: 0 0.6rem; border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--text-muted); font: 600 var(--step--1) var(--font-ui); text-decoration: none; cursor: pointer; }
.tog:hover, .icon-btn:hover { background: var(--hover); color: var(--text); }
.tog[lang="zh-Hans"] { font-size: var(--step-0); }
.tog[aria-pressed="true"] { background: var(--maroon); border-color: var(--red); color: var(--on-maroon); }
.icon-btn { padding: 0; }
.icon { width: 1.25rem; height: 1.25rem; }
.app-title { appearance: none; border: 0; background: transparent; color: var(--text); font: 600 var(--step-0) var(--font-ui); white-space: nowrap; cursor: pointer; padding: 0 var(--space-1); }
/* One language control (spec 2026-10-21-shell-redesign §6): a details menu, so it works without JS. */
.menu { position: relative; }
.menu > summary { list-style: none; }
.menu > summary::-webkit-details-marker { display: none; }
.lang-btn { padding: 0 0.6rem 0 0.5rem; }
.lang-btn .icon { width: 1.1rem; height: 1.1rem; }
.menu[open] > summary { background: var(--hover); color: var(--text); }
.langs, .site-links { position: absolute; top: calc(100% + 0.4rem); right: 0; z-index: 30; display: grid; min-width: 9.5rem; padding: var(--space-1); border: 1px solid var(--glass-edge); border-radius: var(--radius); background: var(--glass); box-shadow: var(--shadow), var(--glass-shine); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.site-links { min-width: 12rem; }
.langs a, .site-links a { padding: 0.55rem 0.8rem; border-radius: 8px; color: var(--text); font: var(--step-0) var(--font-ui); text-decoration: none; }
.langs a:hover, .site-links a:hover { background: var(--hover); }
.langs a[aria-current] { color: var(--red-text); font-weight: 600; }

/* Player */
.player { position: relative; z-index: 1; box-shadow: var(--shadow-up); padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom)); }
/* The glass is a layer behind the player's content rather than the player's own background: an
   element with a backdrop filter is a backdrop root, and the speed menu inside it would then blur
   only the player instead of the page. */
.player::before { background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.player::before { content: ""; position: absolute; inset: 0; z-index: -1; border-top: 1px solid var(--glass-edge); border-radius: inherit; }
.seekrow { display: flex; align-items: center; gap: var(--space-2); }
.time { min-width: 2.75rem; color: var(--text-muted); font: var(--step--1) var(--font-ui); font-variant-numeric: tabular-nums; }
.time.dur { text-align: right; }
.seek { flex: 1; --p: 0%; height: 1.5rem; margin: 0; background: transparent; cursor: pointer; appearance: none; -webkit-appearance: none; }
/* A thin track filled in red up to the playhead (--p, set by js/player.js), and a small round thumb. */
.seek::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: linear-gradient(var(--red), var(--red)) 0 / var(--p) 100% no-repeat, var(--line); }
.seek::-moz-range-track { height: 4px; border-radius: 2px; background: linear-gradient(var(--red), var(--red)) 0 / var(--p) 100% no-repeat, var(--line); }
.seek::-webkit-slider-thumb { width: 0.8rem; height: 0.8rem; margin-top: calc(2px - 0.4rem); border: 0; border-radius: 50%; background: var(--text); -webkit-appearance: none; }
.seek::-moz-range-thumb { width: 0.8rem; height: 0.8rem; border: 0; border-radius: 50%; background: var(--text); }
.seek:hover::-webkit-slider-thumb { background: var(--red-text); }
.seek:hover::-moz-range-thumb { background: var(--red-text); }
.controls { display: flex; align-items: center; justify-content: space-between; max-width: 26rem; margin: var(--space-1) auto 0; }
.ctl { appearance: none; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border: 0; border-radius: 50%; background: var(--hover); color: var(--text); font: 600 var(--step--1) var(--font-ui); cursor: pointer; }
.ctl.play { width: 3.5rem; height: 3.5rem; border-color: var(--red); background: var(--red); color: var(--on-red); }
.ctl .icon { width: 1.4rem; height: 1.4rem; }
/* Speed: a menu of steps; the button has one width whatever the speed, so the bar never moves. */
.ctl.speed { width: 3.4rem; border-radius: 999px; font-variant-numeric: tabular-nums; }
.speeds { position: absolute; bottom: calc(100% + 0.6rem); left: 0; z-index: 30; display: grid; min-width: 5.5rem; padding: var(--space-1); border: 1px solid var(--glass-edge); border-radius: var(--radius); background: var(--glass); box-shadow: var(--shadow), var(--glass-shine); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.speeds button { appearance: none; padding: 0.5rem 0.8rem; border: 0; border-radius: 8px; background: none; color: var(--text); font: var(--step-0) var(--font-ui); font-variant-numeric: tabular-nums; cursor: pointer; }
.speeds button:hover { background: var(--hover); }
.speeds button[aria-pressed="true"] { color: var(--red-text); font-weight: 600; }
.player .i-pause, .player.playing .i-play, .mode .icon { display: none; }
.player.playing .i-pause { display: block; }
.player[data-mode="next"] .i-mode-next, .player[data-mode="repeat"] .i-mode-repeat, .player[data-mode="stop"] .i-mode-stop { display: block; }

/* Chapter strip (mobile) */
.strip { position: relative; display: flex; gap: var(--space-1); overflow-x: auto; padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom)); background: var(--surface); scrollbar-width: none; scroll-snap-type: x proximity; }
.strip::-webkit-scrollbar { display: none; }
.strip a, .strip span { flex: 0 0 auto; display: grid; place-items: center; min-width: 2.5rem; height: 2.5rem; border-radius: 10px; background: var(--hover); color: var(--text-muted); font: 600 var(--step--1) var(--font-ui); text-decoration: none; scroll-snap-align: center; }
.strip a[aria-current] { background: var(--red); color: var(--on-red); }
.strip .soon { background: transparent; border: 1px dashed var(--line); }

/* Chapter list: desktop sidebar, mobile sheet */
.chapter-list .cl-title { margin: 0; padding: var(--space-3) var(--space-3) var(--space-2); font: 600 var(--step-1) var(--font-zh); letter-spacing: 0.1em; }
.chapter-list ol { margin: 0; padding: 0 var(--space-2) var(--space-3); list-style: none; }
.chapter-list ol a, .chapter-list .soon { display: flex; align-items: baseline; gap: var(--space-3); padding: 0.5rem var(--space-3) 0.5rem var(--space-2); border-radius: 8px; color: var(--text); font-family: var(--font-zh); font-size: 1.3rem; letter-spacing: 0.05em; text-decoration: none; }
.chapter-list b { min-width: 1.8rem; color: var(--text-muted); font: 500 var(--step-0) var(--font-ui); font-variant-numeric: tabular-nums; text-align: right; }
.chapter-list a:hover { background: var(--hover); color: var(--text); }
/* data-was: the chapter last opened, still marked while another page is shown (js/ui.js). */
.chapter-list a[aria-current], .chapter-list a[aria-current] b, .chapter-list a[data-was], .chapter-list a[data-was] b { background: var(--maroon); color: var(--on-maroon); }
.sheet-backdrop { display: none; position: fixed; inset: 0; z-index: 19; background: var(--scrim); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }

/* Settings dialog and page */
.sheet-dialog, .settings-page { font-family: var(--font-ui); }
.sheet-dialog { width: min(32rem, 92vw); max-height: 85dvh; padding: var(--space-4); border: 1px solid var(--glass-edge); box-shadow: var(--shadow), var(--glass-shine); border-radius: var(--sheet-radius); background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); color: var(--text); }
.sheet-dialog h2 { margin-top: 0; }
.sheet-dialog::backdrop { background: var(--scrim); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.settings-form fieldset { display: grid; gap: var(--space-2); margin: 0; padding: var(--space-3) 0; border: 0; }
.settings-form legend { padding: 0; font-weight: 600; }
.settings-form label { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.settings-form select, .settings-form button, .dialog-actions button { padding: 0.35rem 0.6rem; border: 1px solid transparent; border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; }
.settings-form input[type="checkbox"] { width: 1.2rem; height: 1.2rem; accent-color: var(--red); }
.dialog-actions { margin: var(--space-3) 0 0; text-align: right; }

/* The shell: mobile first */
html.js body.app { display: grid; grid-template-rows: auto minmax(0, 1fr); height: 100dvh; overflow: hidden; }
html.js body.app .site-header { padding: var(--space-2) var(--space-3); }
html.js body.app .pager, html.js body.app .chapter-audio { display: none; }
/* The footer ends the scrolled content on app pages (spec 2026-10-21-home-redesign §10). */
html.js body.app main .site-footer { margin-top: var(--space-5); padding: var(--space-4) 0 0; background: none; }
/* The strip is the chapter navigation of chapter pages only. */
html.js body.app:not(.page-chapter) .strip { display: none; }
html.js body.app .shell { display: grid; min-height: 0; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto auto; grid-template-areas: "main" "player" "strip"; }
html.js body.app main { grid-area: main; max-width: none; margin: 0; padding: var(--space-3) var(--space-3) var(--space-4); overflow-y: auto; overscroll-behavior: contain; }
/* The page scrolls under the glass player (main spans its row and leaves room at the end). */
html.js body.app main { grid-row: 1 / 3; padding-bottom: 8rem; }
html.js body.app .player { grid-area: player; align-self: end; }
html.js body.app .strip { grid-area: strip; }
html.js body.app .chapter-list { position: fixed; right: 0; bottom: 0; left: 0; z-index: 20; height: 70dvh; overflow-y: auto; border-top: 1px solid var(--glass-edge); border-radius: var(--sheet-radius) var(--sheet-radius) 0 0; background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); transform: translateY(100%); visibility: hidden; transition: transform 0.25s, visibility 0.25s; }
html.js body.app.sheet-open .chapter-list { transform: none; visibility: visible; transition: transform 0.25s, visibility 0s; }   /* visible at once on open, so focus can move in */
html.js body.app.sheet-open .sheet-backdrop { display: block; }
html.js body.app .chapter-head h1 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
html.js body.app .chapter-head .lead { margin: 0 0 var(--space-3); font-size: var(--step-0); }
/* One text size per breakpoint, never per chapter or language (spec 2026-10-21-shell-redesign §4);
   only the reader's text-size setting scales it. */
html.js body.app { --zh-size: 1.6rem; --tr-size: 1.125rem; }
html.js body.app .zh { font-size: calc(var(--zh-size) * var(--user-scale, 1)); line-height: 1.9; }
html.js body.app .tr { font-size: calc(var(--tr-size) * var(--user-scale, 1)); }
html.js body.app .line { padding: var(--space-2); border-bottom: 0; border-radius: 6px; }
@media (max-width: 63.99rem) {
  html.js body.app .brand span { display: none; }
  html.js body.app .site-header { gap: var(--space-2); }
  html.js body.app .site-header .tools { gap: var(--space-1); }
}
/* Small phones: the tools close up and the language button shows its code only, so the header fits 360px. */
@media (max-width: 26rem) {
  html.js body.app .site-header .tools { gap: 0; }
  html.js body.app .site-header .tog, html.js body.app .site-header .icon-btn { min-width: 2.1rem; padding: 0; }
  html.js body.app .lang-btn .icon { display: none; }
}
/* The smallest phones (320px): the logo gives way; Home is in the ☰ menu. */
@media (max-width: 21rem) {
  html.js body.app .brand { display: none; }
}

/* Desktop: no top bar. The header and the shell step aside (display: contents) so the brand, the
   chapter label, the tools, main, the player and the list sit on one body grid; the player row is
   the toolbar (spec 2026-10-21-shell-redesign §1). Nothing is duplicated in the DOM. */
@media (min-width: 64rem) {
  html.js body.app { --zh-size: 2rem; --tr-size: 1.2rem; --bar-h: 4.25rem; --tools-w: 12rem; grid-template-columns: 18rem minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; grid-template-areas: "brand main" "list main" "list main"; }
  html.js body.app .site-header, html.js body.app .shell { display: contents; }
  html.js body.app .brand { grid-area: brand; gap: var(--space-3); padding: var(--space-4) var(--space-4) var(--space-3); background: var(--surface); }
  html.js body.app .brand img { width: 48px; height: 48px; }
  html.js body.app .brand-zh { font-size: 1.6rem; }
  html.js body.app .brand-py { font-size: 0.9rem; }
  html.js body.app .chapter-list { position: static; grid-area: list; min-height: 0; height: auto; overflow-y: auto; border: 0; border-radius: 0; background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; transform: none; visibility: visible; transition: none; }
  html.js body.app .chapter-list .cl-title { display: none; }
  html.js body.app .strip, html.js body.app .sheet-backdrop, html.js body.app.sheet-open .sheet-backdrop { display: none; }
  html.js body.app main { grid-row: 1 / -1; min-height: 0; padding: var(--space-5) var(--space-5) calc(var(--bar-h) + var(--space-3) + var(--space-5)); }
  /* One row (spec 2026-10-21-desktop-polish §1): the controls, the seek bar, the tools. The tools
     are header elements laid over the bar's end (same grid area), so the bar leaves room for them.
     No chapter label: the sidebar marks the chapter. */
  html.js body.app:has(.tools .tog) { --tools-w: 18rem; }
  html.js body.app .player { display: flex; align-items: center; gap: var(--space-4); height: var(--bar-h); margin: 0 var(--space-4) var(--space-3); padding: 0 var(--tools-w) 0 var(--space-4); border-radius: var(--radius); box-shadow: var(--shadow), var(--glass-shine); }
  html.js body.app .player::before { border: 1px solid var(--glass-edge); }
  html.js body.app .controls { order: -1; flex: none; gap: var(--space-1); max-width: none; margin: 0; }
  html.js body.app .seekrow { flex: 1; min-width: 0; }
  html.js body.app .ctl { width: 2.75rem; height: 2.75rem; background: transparent; color: var(--text); }
  html.js body.app .ctl:hover { background: var(--hover); color: var(--text); }
  html.js body.app .ctl.play { width: 3rem; height: 3rem; margin: 0 var(--space-1); background: var(--red); color: var(--on-red); }
  html.js body.app .ctl.play:hover { filter: brightness(1.1); }
  html.js body.app .ctl .icon { width: 1.45rem; height: 1.45rem; }
  html.js body.app .ctl.speed { width: 3.6rem; font-size: var(--step-0); }
  html.js body.app .time { min-width: 3rem; color: var(--text); font-size: var(--step-0); }
  html.js body.app .tools .tog, html.js body.app .tools .icon-btn { min-width: 2.75rem; height: 2.75rem; color: var(--text); font-size: var(--step-0); }
  html.js body.app .tools .tog[lang="zh-Hans"] { font-size: var(--step-1); }
  html.js body.app .tools .icon-btn .icon { width: 1.35rem; height: 1.35rem; }
  html.js body.app .tools .tog[aria-pressed="true"] { color: var(--on-maroon); }
  html.js body.app .app-title { display: none; }
  html.js body.app .player, html.js body.app .tools { grid-area: 3 / 2; }
  html.js body.app .tools { z-index: 2; align-self: end; height: var(--bar-h); margin-bottom: var(--space-3); }
  html.js body.app .tools { justify-self: end; gap: var(--space-1); margin-right: calc(var(--space-4) + var(--space-3)); }
  html.js body.app .chapter-head .h1-zh { font-size: 2.6rem; }
  html.js body.app .tools .langs, html.js body.app .site-links { top: auto; bottom: calc(100% + 0.6rem); }
}
@media (min-width: 64rem) {
  html.js body.app .chapter-head { margin-bottom: var(--space-4); }
  html.js body.app .chapter-head h1 { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; margin: 0 0 var(--space-3); }
  /* Both columns in every language and with the translation hidden: the Chinese never moves. */
  html.js body.app .line { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); column-gap: var(--space-5); align-items: center; }
  html.js body.app .tr { margin: 0; }
}
