/* cover.css — the theme bar and the cover (the screen before the page). Reuses the page's tokens
   (--bg, --ink, --rule, --chip, --surface, --link, --serif, --sans) so Light and Dark follow
   html[data-theme] like the rest of the page. Loaded after stylesheet.css; bump its ?v= in
   index.html whenever it changes (the cache-busting rule in AGENTS.md). */
.theme-bar {
  position: sticky; top: 0; z-index: 10;
  display: none; justify-content: space-between; align-items: center; gap: 0.75rem;
  height: 48px; padding: 0 1rem;
  background: var(--bg); color: var(--ink);
}
:root[data-theme] .theme-bar { display: flex; }
.theme-toggle, .cover-return {
  box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--chip); color: var(--muted); cursor: pointer;
}
.theme-toggle { margin-left: auto; }
.theme-toggle:hover, .theme-toggle:focus-visible,
.cover-return:hover, .cover-return:focus-visible { color: var(--ink); border-color: var(--muted); }
.theme-toggle:focus-visible, .cover-return:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.theme-toggle svg, .cover-return svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
/* The way back to the cover sits where a site's logo would: leftmost in the header. While the
   cover is open it keeps its place but is not shown (the collapse aims at it). Wide screens
   add its word; phones keep the icon alone. */
.cover-return { gap: 0.4rem; font: 700 0.8rem var(--sans); }
.cover-return span { display: none; }
.cover-return[hidden] { display: none; }
:root.cover-open .cover-return { visibility: hidden; }
@media (min-width: 1200px) {
  .cover-return { width: auto; padding: 0 0.75rem 0 0.6rem; }
  .cover-return span { display: inline; }
}
.theme-icon-sun { display: none; }
:root[data-theme="dark"] .theme-icon-sun { display: block; }
:root[data-theme="dark"] .theme-icon-moon { display: none; }
.header-nav { display: none; }
.palette-picker {
  display: inline-flex; align-items: center; gap: 2px; padding: 3px;
  border: 1px solid var(--rule); border-radius: 999px; background: var(--chip);
}
.palette-picker button {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 28px; padding: 0 0.55rem; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink-soft); font: 700 0.75rem var(--sans); cursor: pointer;
}
.palette-picker button:hover { color: var(--ink); }
.palette-picker button[aria-pressed="true"] {
  background: var(--surface); color: var(--ink); box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
}
.palette-picker button:focus-visible,
.palette-select:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.palette-swatch { display: inline-block; width: 8px; height: 8px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.15); }
.palette-select { display: none; }
/* Ink was chosen on 2026-09-10. The picker stays in the markup and palettes.js keeps working
   (`?palette=sand|jade|iris` still previews the others), but it is hidden until a change is
   wanted: remove the `hidden` attribute in index.html to bring it back. */
.palette-picker[hidden] { display: none; }
:root[data-theme] .theme-bar + .cover {
  height: calc(100vh - var(--header-height, 48px)); height: calc(100svh - var(--header-height, 48px));
  min-height: 512px; padding-top: 0.5rem;
}
/* Keep the complete homepage's sticky nav and anchor targets below the theme bar. */
:root[data-theme] main { scroll-margin-top: var(--header-height, 48px); }
/* The About link targets the hero; its margin equals everything above it, so it lands at the top. */
.hero { scroll-margin-top: 4.5rem; }
:root[data-theme] .hero { scroll-margin-top: calc(var(--header-height, 48px) + 32px); }
:root[data-theme] .toc { top: calc(var(--header-height, 48px) + 24px); }
:root[data-theme] main > section { scroll-margin-top: calc(var(--header-height, 48px) + 24px); }
/* Narrow screens (2026-09-10): Cover, the section links, Search and the theme toggle share the
   header bar; site.js moves the existing nav into .header-nav and adds .unified-header, and the
   links show only while the page is open. Wide screens keep the nav in main's left margin and
   site.js heads it with the Cover button; once the page is open the bar has nothing left to
   show, so it leaves with the cover and the theme toggle joins the sidebar below Search. */
.theme-bar.has-section-nav { box-shadow: inset 0 -1px var(--rule); }
@media (prefers-reduced-motion: no-preference) {
  :root.unified-header .header-nav:not([hidden]) { animation: header-nav-in 0.3s ease; }
  @keyframes header-nav-in { from { opacity: 0; } }
}
.theme-label { display: none; }
@media (min-width: 1200px) {
  :root[data-theme].cover-closed .theme-bar { display: none; }
  :root[data-theme] main { padding-top: 32px; }
  /* The sidebar is one list of icon-and-word rows in one weight. Cover and the four sections are
     the page's chapters; a hairline sets Search and the theme toggle apart as tools. Every row
     shares the links' structure: a 2px marker slot (inked for the active section), a 16px icon,
     then the word, so all icons and all words align. Boxes and fills would outweigh the words. */
  .toc ul a, .toc .cover-return, .toc .toc-search, .toc .theme-toggle {
    box-sizing: border-box; display: flex; align-items: center; justify-content: flex-start;
    width: 100%; height: auto; min-height: 0; margin: 0; padding: 0.15rem 0 0.15rem 10px; gap: 0.5rem;
    border: 0; border-left: 2px solid transparent; border-radius: 0; background: transparent;
    font: 400 0.92rem/1.4 var(--sans); color: var(--muted); cursor: pointer;
  }
  .toc ul a[aria-current="true"] { border-left-color: var(--ink); color: var(--ink); font-weight: 700; }
  .toc ul a:hover, .toc .cover-return:hover, .toc .toc-search:hover, .toc .theme-toggle:hover,
  .toc ul a:focus-visible, .toc .cover-return:focus-visible, .toc .toc-search:focus-visible, .toc .theme-toggle:focus-visible { color: var(--ink); }
  .toc .cover-return:focus-visible, .toc .toc-search:focus-visible, .toc .theme-toggle:focus-visible { outline-offset: 3px; }
  .toc a svg, .toc .cover-return svg, .toc .toc-search svg, .toc .theme-toggle svg {
    width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor;
    stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; /* no display here: the sun/moon rules choose one */
  }
  .toc .cover-return { margin: 0 0 0.4rem; } /* the same rhythm as the section rows (li margin) */
  .toc .toc-tools {
    display: flex; flex-direction: column; align-items: stretch; gap: 0.25rem;
    margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--rule);
  }
  .toc .toc-search kbd { margin: 0; padding: 0; border: 0; background: transparent; font-size: 0.72rem; color: var(--muted); }
  .toc .theme-label { display: inline; }
}
@media (max-width: 1199.98px) {
  .toc a svg { display: none; } /* the narrow row has no room for section icons */
}
@media (max-width: 1199.98px) {
  :root[data-theme] main { padding-top: 0; scroll-margin-top: 48px; }
  :root[data-theme] .toc { top: 48px; border-bottom-color: var(--rule); }
  :root[data-theme] main > section { scroll-margin-top: 7.25rem; }
  .theme-bar { box-sizing: border-box; height: auto; min-height: 48px; padding: 4px 0.5rem; gap: 0.5rem; }
  :root.unified-header .header-nav:not([hidden]) { display: block; flex: 1; min-width: 0; }
  :root.unified-header .header-nav .toc { position: static; top: auto; margin: 0; padding: 0; border: 0; }
  .header-nav .toc-panel { gap: 0.5rem; }
  .header-nav .toc ul { gap: 1rem; }
  .header-nav .toc-search { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0; }
  .header-nav .toc-search span, .header-nav .toc-search kbd { display: none; }
  :root[data-theme].unified-header main { padding-top: 32px; scroll-margin-top: var(--header-height, 48px); }
  :root[data-theme].unified-header main > section { scroll-margin-top: calc(var(--header-height, 48px) + 16px); }
}
/* The cover as a view (2026-09-10). site.js sets one of two root classes: .cover-open pins the
   cover over main and the document waits unscrolled behind it (its own content can still
   scroll on a short screen); .cover-closed hides it and the page is an ordinary document.
   .page-locked keeps the document still a little longer after entering, while a wheel's
   momentum tail runs out. theme.js adds .cover-closed before first paint for anchor links
   and restored pages. With neither class (no JS, or site.js unavailable) the cover stays in
   flow before main. */
:root[data-theme].cover-open, :root[data-theme].page-locked {
  overflow: hidden; scrollbar-gutter: stable;
  overflow-anchor: none; /* the bar's arrival/departure must move the page by exactly its height, not be absorbed by scroll anchoring */
}
:root[data-theme].cover-open .theme-bar + .cover {
  position: fixed; top: var(--header-height, 48px); right: 0; bottom: 0; left: 0; z-index: 8;
  height: auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
:root.cover-closed .cover, .cover[hidden] { display: none; }
/* The transition reads as one scroll: the page rises from below by the cover's height while the
   cover, shrinking about its top-left corner, collapses into the Cover button (site.js sets
   --cover-dx/--cover-dy/--cover-scale from the button's place and --cover-h from the cover's
   height, so the seam between them moves as one). The cover fades only near the end, once it
   is small, and the button pops to catch it. Returning plays the same motion backwards. */
.cover.is-leaving, .cover.is-entering { transform-origin: 0 0; pointer-events: none; }
.theme-bar.is-leaving, .theme-bar.is-entering { pointer-events: none; }
/* Above the backdrop and the header while it moves; the selector must outrank the pinned rule's z-index. */
:root[data-theme].cover-open .theme-bar + .cover:is(.is-leaving, .is-entering) { z-index: 11; }
main.is-arriving, main.is-parting { pointer-events: none; }
/* While the cover moves, this full-width sheet rides on the seam and hides whatever part of the
   page lies above it (the reader's earlier position, on returning), as the cover itself did in
   the old continuous scroll. It follows the cover element in the markup and exists only then. */
.cover-backdrop {
  display: none; position: fixed; top: var(--header-height, 48px); left: 0; right: 0;
  height: calc(var(--cover-h, 100vh) + 2px); background: var(--bg); z-index: 9; pointer-events: none; /* 2px past the seam hides sub-pixel gaps */
}
.cover.is-leaving + .cover-backdrop, .cover.is-entering + .cover-backdrop { display: block; }
@media (prefers-reduced-motion: no-preference) {
  /* One duration and curve for the cover and the page keeps their seam exact. The curve eases in
     briefly, then settles; site.js's fallback timer (700ms) must stay longer than the duration. */
  :root { --cover-motion: 0.6s; --cover-ease: cubic-bezier(0.35, 0.05, 0.2, 1); }
  .cover.is-leaving { animation: cover-out var(--cover-motion) var(--cover-ease) both; }
  .cover.is-entering { animation: cover-in var(--cover-motion) var(--cover-ease) both; }
  main.is-arriving { animation: page-up var(--cover-motion) var(--cover-ease) both; }
  main.is-parting { animation: page-down var(--cover-motion) var(--cover-ease) both; }
  .theme-bar.is-leaving { animation: bar-out var(--cover-motion) var(--cover-ease) both; }
  .theme-bar.is-entering { animation: bar-in var(--cover-motion) var(--cover-ease) both; }
  .cover.is-leaving + .cover-backdrop { animation: backdrop-up var(--cover-motion) var(--cover-ease) both; }
  .cover.is-entering + .cover-backdrop { animation: backdrop-down var(--cover-motion) var(--cover-ease) both; }
  .cover-return.is-catching { animation: cover-catch 0.45s cubic-bezier(0.34, 1.4, 0.64, 1); }
  @keyframes cover-out {
    from { transform: none; opacity: 1; }
    65% { opacity: 1; }
    92% { opacity: 0; }
    to { transform: translate(var(--cover-dx, 0px), var(--cover-dy, -100%)) scale(var(--cover-scale, 0.06)); opacity: 0; }
  }
  @keyframes cover-in {
    from { transform: translate(var(--cover-dx, 0px), var(--cover-dy, -100%)) scale(var(--cover-scale, 0.06)); opacity: 0.6; }
    25% { opacity: 1; }
    to { transform: none; opacity: 1; }
  }
  @keyframes page-up { from { transform: translateY(var(--cover-h, 100vh)); } to { transform: translateY(calc(-1 * var(--bar-h, 0px))); } }
  @keyframes page-down { from { transform: translateY(calc(-1 * var(--bar-h, 0px))); } to { transform: translateY(var(--cover-h, 100vh)); } }
  @keyframes backdrop-up { from { transform: none; } to { transform: translateY(calc(-1 * var(--backdrop-travel, 100vh))); } }
  @keyframes backdrop-down { from { transform: translateY(calc(-1 * var(--backdrop-travel, 100vh))); } to { transform: none; } }
  /* Only wide screens set --bar-h; narrow screens keep the bar, and a 0px travel is no motion. */
  @keyframes bar-out { from { transform: none; opacity: 1; } to { transform: translateY(calc(-1 * var(--bar-h, 0px))); opacity: 0; } }
  @keyframes bar-in { from { transform: translateY(calc(-1 * var(--bar-h, 0px))); opacity: 0; } to { transform: none; opacity: 1; } }
  @keyframes cover-catch { from { transform: scale(0.5); opacity: 0.3; } to { transform: none; opacity: 1; } }
}

/* Three layout rows keep text, the complete rotating landscape, and controls separate. */
.cover {
  box-sizing: border-box;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: 0;
  padding: clamp(1.5rem, 4svh, 3rem) 1rem 1rem;
  height: 100vh;
  height: 100svh;
  min-height: 560px;
  background: var(--bg);
  color: var(--ink);
}
.cover-text { flex: none; text-align: center; }
.cover-name {
  margin: 0;
  font-family: var(--serif);
  font-weight: 700;
  font-size: clamp(2.4rem, 5.2vw, 3.6rem);
  line-height: 1.1;
  letter-spacing: 0.005em;
  color: var(--ink);
}
.cover-tagline { margin: 0.7rem 0 1rem; font-size: 1.05rem; line-height: 1.5; color: var(--ink-soft); }
.cover-links { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.5rem 1.6rem; font-weight: 700; }
.cover-links a { color: var(--link); }
.cover-links a:hover,
.cover-links a:focus-visible { color: var(--link-hover); }

.cover-stage { position: relative; flex: 1; min-height: 180px; overflow: hidden; }
.cover canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  cursor: grab;
  touch-action: pan-y pinch-zoom; /* Vertical swipes and page pinch stay native; only horizontal swipes rotate the model. */
}
.cover canvas:active { cursor: grabbing; }

.cover-footer {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-areas: "controls cue legend";
  gap: 1rem;
  align-items: center;
  flex: none;
}
.cover-controls { grid-area: controls; justify-self: start; }
.cover-hint { margin: 0 0 0.3rem; font-size: 0.72rem; color: var(--muted); }
.cover-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1rem; }
.cover-actions button {
  padding: 0.3rem 0;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font: 0.78rem var(--sans);
  cursor: pointer;
}
.cover-actions button:hover { color: var(--link); }
.cover-actions button:disabled { color: var(--muted); cursor: default; }
.cover-actions button[hidden] { display: none; }
.cover-camera { display: inline-flex; align-items: center; gap: 0.15rem; }
.cover-camera[hidden] { display: none; }
.cover-camera button { min-width: 28px; min-height: 28px; }
.cover-camera .cover-zoom-in,
.cover-camera .cover-zoom-out { font-size: 1rem; line-height: 1; }
.cover-camera .cover-reset { margin-left: 0.35rem; }
.cover-zoom-level { min-width: 2.5rem; text-align: center; font: 0.72rem var(--sans); font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.cover-legend {
  grid-area: legend; justify-self: end;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem 1rem;
  margin: 0; padding: 0; list-style: none;
  font-size: 0.78rem; color: var(--ink-soft);
}
.cover-legend li { white-space: nowrap; }
.cover-legend i { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 0.4rem; }
.cover-cue {
  grid-area: cue;
  display: inline-flex; align-items: center; justify-content: center; gap: 0.8rem;
  min-height: 54px; padding: 0 1.5rem; border-radius: 30px;
  border: 1px solid var(--rule); background: var(--chip);
  color: var(--ink); font: 700 1rem var(--sans); text-decoration: none;
}
.cover-cue:hover,
.cover-cue:focus-visible { color: var(--link); border-color: var(--link); text-decoration: none; }
.cover button:focus-visible,
.cover a:focus-visible { outline: 2px solid var(--link); outline-offset: 5px; }
@media (prefers-reduced-motion: no-preference) {
  .cover-cue span { animation: cover-cue-bob 2.4s ease-in-out infinite; }
  @keyframes cover-cue-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(3px); } }
}
.cover.is-paused .cover-cue span { animation-play-state: paused; }
@media (max-width: 900px) {
  .cover-hint { display: none; }
  .cover-footer { grid-template-columns: 1fr auto; grid-template-areas: "legend legend" "controls cue"; gap: 0.75rem; }
  .cover-legend { justify-self: center; }
}
@media (max-width: 480px) {
  .theme-bar { padding: 0 0.5rem; }
  .palette-picker { padding: 0; border: 0; background: transparent; }
  .palette-picker button { display: none; }
  .palette-select {
    display: block; min-height: 34px; width: 100px; padding: 0 0.5rem;
    border: 1px solid var(--rule); border-radius: 999px;
    background: var(--chip); color: var(--ink); font: 700 0.75rem var(--sans);
  }
  .cover { padding: 1.5rem 0.5rem 1rem; gap: 0.75rem; }
  .cover-tagline { font-size: 0.92rem; margin: 0.6rem 0 0.8rem; }
  .cover-links { font-size: 0.875rem; gap: 0.4rem 0.75rem; }
  .cover-legend { gap: 0.75rem; }
  .cover-cue { min-height: 48px; padding: 0 0.875rem; font-size: 0.9rem; gap: 0.5rem; }
  .cover-footer { grid-template-columns: 1fr; grid-template-areas: "legend" "controls" "cue"; gap: 0.5rem; }
  .cover-controls, .cover-cue { justify-self: center; }
  .cover-actions { gap: 0.65rem; }
  .cover-actions button { font-size: 0.72rem; min-height: 40px; }
}
