/* MOOSSin portal stylesheet.
 *
 * Hand-written, project-original, no framework and no external request: the
 * page must stay inside ADR-0004's `default-src 'none'` with `style-src 'self'`,
 * so there is no @import, no webfont, and no url() reaching off-origin.
 *
 * The colour tokens below were chosen against measured WCAG contrast, not by
 * eye. Ratios on the near-white page background:
 *   ink 15.75:1, muted 7.67:1, moss 6.19:1, moss-strong 8.67:1, line 3.54:1.
 * `--moossin-line` clears 3:1 because a card boundary is a non-text element
 * that has to be perceivable; the softer grey-green that looked nicer measured
 * 1.48:1, which is decoration pretending to be structure.
 *
 * Focus rings use `--moossin-moss`, never the line colour. A focus indicator is
 * the one thing a keyboard user cannot do without.
 */

:root {
  --moossin-page: #fbfcfa;
  --moossin-surface: #f1f4ef;
  --moossin-ink: #1c221d;
  --moossin-muted: #4a544b;
  --moossin-moss: #2f6b3f;
  --moossin-moss-strong: #24532f;
  --moossin-line: #7c8a78;

  --moossin-space-1: 0.25rem;
  --moossin-space-2: 0.5rem;
  --moossin-space-3: 1rem;
  --moossin-space-4: 1.5rem;
  --moossin-space-5: 2.5rem;

  --moossin-measure: 38rem;
  --moossin-radius: 0.5rem;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  /* ADR-0020: no gutter on the body, so the shared one-row bar spans the
   * viewport as on every section; main, the notice and the footer band keep
   * the 1rem gutter themselves. */
  padding: 0;
  background: var(--moossin-page);
  color: var(--moossin-ink);
  /* System stack only: no webfont means no licence obligation and no load cost. */
  font-family: system-ui, -apple-system, "Segoe UI", "Noto Sans TC",
    "Microsoft JhengHei", sans-serif;
  /* Unitless line-height and rem sizing keep 200% text zoom from clipping. */
  line-height: 1.7;
  font-size: 1rem;
}

h1,
h2,
h3 {
  line-height: 1.35;
  text-wrap: balance;
}

a {
  color: var(--moossin-moss);
}

a:hover {
  color: var(--moossin-moss-strong);
}

/* A single focus rule for everything focusable, so no control can lose its
 * indicator by being styled later. */
a:focus-visible,
:focus-visible {
  outline: 3px solid var(--moossin-moss);
  outline-offset: 2px;
}

/* The rounded ring is the portal's own; the shared one-row bar draws its focus
 * ring the same way on every section (ADR-0020), so the rounding stops at it. */
:focus-visible:not(.mooss-bar *) {
  border-radius: var(--moossin-radius);
}

.skip-link {
  position: absolute;
  left: -9999px;
}

.skip-link:focus {
  position: static;
  display: inline-block;
  margin: var(--moossin-space-2) var(--moossin-space-3);
}

main,
.colophon {
  max-width: 60rem;
  margin: 0 auto;
}

/* The gutter the body used to give (ADR-0020): the same column as before.
 * The notice after main takes the same width in its own rule below. */
main {
  width: calc(100% - 2 * var(--moossin-space-3));
}

.hero {
  padding: var(--moossin-space-5) 0 var(--moossin-space-4);
}

.hero h1 {
  max-width: var(--moossin-measure);
  margin: 0 0 var(--moossin-space-3);
  font-size: clamp(1.6rem, 1.2rem + 2vw, 2.4rem);
}

.hero__lead {
  max-width: var(--moossin-measure);
  margin: 0 0 var(--moossin-space-4);
  color: var(--moossin-muted);
}

.hero__action {
  margin: 0;
}

.button {
  display: inline-block;
  padding: var(--moossin-space-2) var(--moossin-space-4);
  border-radius: var(--moossin-radius);
  background: var(--moossin-moss);
  color: var(--moossin-page);
  text-decoration: none;
}

.button:hover {
  background: var(--moossin-moss-strong);
  color: var(--moossin-page);
}

.sites,
.tools,
.principles,
.about {
  padding: var(--moossin-space-4) 0;
}

.sites h2,
.tools h2,
.principles h2,
.about h2 {
  margin: 0 0 var(--moossin-space-3);
  font-size: 1.35rem;
}

.sites__list,
.tools__list {
  display: grid;
  gap: var(--moossin-space-3);
  /* Cards never drop below a readable width, so the grid collapses to one
   * column rather than squeezing at 320px. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
}

.site-card,
.tool-card {
  display: flex;
  flex-direction: column;
  gap: var(--moossin-space-2);
  padding: var(--moossin-space-4);
  border: 1px solid var(--moossin-line);
  border-radius: var(--moossin-radius);
  background: var(--moossin-surface);
}

.site-card__title {
  margin: 0;
  font-size: 1.1rem;
}

.site-card__description {
  margin: 0;
}

.site-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--moossin-space-1) var(--moossin-space-3);
  margin: 0;
  color: var(--moossin-muted);
  font-size: 0.9rem;
}

.site-card__host {
  /* The destination host, shown so a visitor knows where the link goes. */
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  word-break: break-all;
}

.site-card__external::before {
  content: "↗ ";
}

.site-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--moossin-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
}

.site-card__tag {
  padding: 0 var(--moossin-space-2);
  border: 1px solid var(--moossin-line);
  border-radius: var(--moossin-radius);
  color: var(--moossin-muted);
}

.site-card__action {
  margin-top: auto;
  padding-top: var(--moossin-space-2);
  font-weight: 600;
}

.principles__list {
  display: grid;
  gap: var(--moossin-space-3);
  margin: 0;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
}

/* Each term is grouped with its description in a `div`, which HTML allows
 * inside a `dl`. Without the grouping the grid lays out `dt` and `dd` as
 * independent cells, and at any width that fits an odd number of columns the
 * terms stop lining up with their descriptions -- correct at the width you
 * happen to test, wrong at the next one. */
.principle {
  min-width: 0;
}

.principle dt {
  font-weight: 700;
}

.principle dd {
  margin: var(--moossin-space-1) 0 0;
  color: var(--moossin-muted);
}

.about p {
  max-width: var(--moossin-measure);
  margin: 0 0 var(--moossin-space-3);
}

.legal-main {
  max-width: 52rem;
  padding: var(--moossin-space-5) 0;
}

.legal-hero {
  padding-bottom: var(--moossin-space-4);
  border-bottom: 1px solid var(--moossin-line);
}

.legal-hero h1 {
  margin: var(--moossin-space-1) 0 var(--moossin-space-2);
  font-size: clamp(1.7rem, 1.25rem + 2vw, 2.5rem);
}

.legal-hero p {
  margin: 0;
  color: var(--moossin-muted);
}

.legal-eyebrow {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.16em;
}

.legal-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--moossin-space-2) var(--moossin-space-4);
  padding: var(--moossin-space-4) 0;
  border-bottom: 1px solid var(--moossin-line);
}

.legal-content {
  overflow-wrap: anywhere;
}

.legal-content section {
  padding: var(--moossin-space-4) 0;
  border-bottom: 1px solid var(--moossin-line);
  scroll-margin-top: var(--moossin-space-3);
}

.legal-content h2 {
  margin: 0 0 var(--moossin-space-3);
  font-size: 1.3rem;
}

.legal-content p,
.legal-content ul {
  max-width: 46rem;
  margin: 0 0 var(--moossin-space-3);
}

.legal-content li + li {
  margin-top: var(--moossin-space-2);
}

/* Footer: the deep-space band the blog and novel footers use (owner's
 * request, 2026-10-03): full width, the shared star field, a planet horizon at
 * the bottom, white headings and mint links. Measured contrast on space-deep:
 * white 17.80:1, mint 14.32:1, 85% white 13.33:1; the horizon sits in the
 * bottom padding and never carries text. The body has no gutter (ADR-0020),
 * so the band spans the viewport and its padding lines the text up with main. */
.colophon {
  position: relative;
  overflow: hidden;
  max-width: none;
  margin: var(--moossin-space-5) 0 0;
  padding: var(--moossin-space-5) max(var(--moossin-space-3), calc((100% - 60rem) / 2)) 4rem;
  color: rgba(255, 255, 255, 0.85);
  background-image: var(--moossin-stars), linear-gradient(180deg, var(--moossin-space-deep), var(--moossin-space));
}

.colophon::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -30rem;
  width: 90rem;
  height: 34rem;
  border-radius: 50%;
  transform: translateX(-50%);
  opacity: 0.9;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 0%, var(--moossin-teal-light), var(--moossin-teal) 30%, var(--moossin-teal-deep) 60%);
}

.colophon > * {
  position: relative;
  z-index: 1;
}

.colophon__brand {
  margin: 0 0 var(--moossin-space-1);
  color: #fff;
  font-size: 1.25rem;
  font-weight: 700;
}

.colophon h2 {
  color: #fff;
}

.colophon a {
  color: var(--moossin-mint);
  text-decoration: underline;
  text-underline-offset: 0.22em;
}

.colophon a:hover,
.colophon a:focus-visible,
.colophon a[aria-current] {
  color: #fff;
}

.colophon a[aria-current] {
  font-weight: 700;
  text-decoration-thickness: 2px;
}

.colophon :focus-visible {
  outline-color: var(--moossin-star);
}

.colophon .lang-note {
  color: rgba(255, 255, 255, 0.75);
}

.colophon__note {
  margin: var(--moossin-space-3) 0 0;
  padding-top: var(--moossin-space-3);
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  font-size: 0.85rem;
}

/* Only hover and focus feedback exists, and even that is dropped on request. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Creator pages share the portal's original contrast and focus tokens. */
.hero { display: block; max-width: 48rem; }
.creator-introduction { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 2rem; align-items: center; }
.hero__copy { min-width: 0; }
.hero__action { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.eyebrow { color: var(--moossin-moss-strong); font-size: .9rem; font-weight: 600; }
.creator-portrait { margin: 0; min-width: 0; }
.creator-portrait img { display: block; width: 100%; height: auto; border-radius: 1.5rem; }
.creator-portrait figcaption { font-size: .8rem; color: var(--moossin-muted); margin-top: .5rem; text-align: center; }
.creator-note, .recent-updates { padding: 2.5rem 0; border-top: 1px solid var(--moossin-line); }
.creator-note p, .creator-page section > p { max-width: 42rem; }
.creator-page, .updates-page { padding-block: 2.5rem; }
.creator-page section + section { margin-top: 3rem; }
.creator-page h1 { font-size: clamp(1.8rem, 3vw, 2.4rem); }
.character-note { padding: 1rem; border-left: 3px solid var(--moossin-moss); background: var(--moossin-surface); }
.character-details { max-width: 48rem; }
.character-details > div { display: grid; grid-template-columns: 8rem minmax(0, 1fr); padding: 1rem 0; border-bottom: 1px solid var(--moossin-line); gap: 1rem; }
.character-details dt { font-weight: 700; }
.character-details dd { margin: 0; }
.updates-list { list-style: none; padding: 0; max-width: 48rem; }
.updates-list > li { padding: 1.5rem 0; border-bottom: 1px solid var(--moossin-line); }
.updates-list h3 { margin: .5rem 0; }
.update-meta { color: var(--moossin-muted); font-size: .9rem; margin: 0; }
@media (max-width: 620px) {
  .hero, .creator-introduction { grid-template-columns: 1fr; gap: 1.5rem; }
  .creator-portrait { width: min(100%, 19rem); justify-self: center; }
  .character-details > div { grid-template-columns: 1fr; gap: .35rem; }
}

/* ADR-0012: the notes that say a link leads to a page in the other language
 * (the language switch itself is the one-row bar's since ADR-0020). English
 * headings get overflow-wrap so a long word cannot push a card wider than a
 * 320px screen. */
.lang-note, .site-card__language, .update-language { color: var(--moossin-muted); font-size: 0.85rem; font-weight: 400; }
.about__language { color: var(--moossin-muted); }
html[lang="en"] h1, html[lang="en"] h2, html[lang="en"] h3, html[lang="en"] .site-card__action, html[lang="en"] .button { overflow-wrap: anywhere; }

.creator-sheet{margin:2rem 0}.creator-sheet img{display:block;width:100%;height:auto}.creator-sheet figcaption{font-size:.9rem;color:var(--muted)}

/* ADR-0014: the shared MOOSSin navigation every public page carries, the
 * homepage digest of the four sections, and the longer card paragraph. */
.mooss-nav { margin: 0 0 var(--moossin-space-3); font-size: 0.9rem; }
.mooss-nav ul { display: flex; flex-wrap: wrap; gap: var(--moossin-space-1) var(--moossin-space-3); margin: 0; padding: 0; list-style: none; }
.mooss-nav a[aria-current="page"] { font-weight: 700; text-decoration-thickness: 2px; }
.latest { padding: var(--moossin-space-4) 0; border-top: 1px solid var(--moossin-line); }
.latest h2 { margin: 0 0 var(--moossin-space-2); font-size: 1.35rem; }
.latest__lead, .latest__note { margin: 0 0 var(--moossin-space-3); max-width: var(--moossin-measure); color: var(--moossin-muted); }
.latest__note { margin: var(--moossin-space-3) 0 0; font-size: 0.9rem; }
.latest__grid { display: grid; gap: var(--moossin-space-3); grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); }
.latest__group { min-width: 0; padding: var(--moossin-space-3); border: 1px solid var(--moossin-line); border-radius: var(--moossin-radius); background: var(--moossin-surface); }
.latest__group h3 { margin: 0 0 var(--moossin-space-2); font-size: 1.1rem; }
.latest__list { margin: 0; padding: 0; list-style: none; }
.latest__list li { padding: var(--moossin-space-2) 0; border-top: 1px solid var(--moossin-line); overflow-wrap: anywhere; }
.latest__list time { margin-left: var(--moossin-space-2); color: var(--moossin-muted); font-size: 0.85rem; }
.latest__list p { margin: var(--moossin-space-1) 0 0; color: var(--moossin-muted); font-size: 0.9rem; }
.latest__more { margin: var(--moossin-space-2) 0 0; font-weight: 600; }
.site-card__summary { margin: 0; color: var(--moossin-muted); font-size: 0.95rem; }

.latest__note { margin-block: .5rem 1.5rem; }
.latest__list .chapter-number { color: var(--moossin-muted); font-size: 0.9rem; white-space: nowrap; }
.hero__action a { display: inline-flex; align-items: center; min-height: 44px; }

/* Homepage content layout (2026-10-03): the content itself where a headline
 * used to be, then compact updates, on the existing moss palette. The hero
 * keeps ADR-0015's deep-space band at the owner's request and carries a
 * carousel of the recommended novels and the latest blog articles: still no
 * url(), image, font or script; the stars are radial-gradient dots and the
 * planet is a gradient circle. Measured contrast on the band: white on
 * space-deep 17.80:1, on teal 6.69:1; teal-light (3.38:1) never carries text,
 * so the planet stays clear of the slide text at every width (it shrinks into
 * the bottom padding below 64rem). */
:root {
  --moossin-space-deep: #0b1730;
  --moossin-space: #12264a;
  --moossin-teal-deep: #1b4d4a;
  --moossin-teal: #23665f;
  --moossin-teal-light: #3f9a8b;
  --moossin-mint: #cfe9e2;
  --moossin-star: #ffe9a8;
  --moossin-stars:
    radial-gradient(1.6px 1.6px at 8% 22%, rgba(255, 255, 255, 0.95) 50%, transparent 52%),
    radial-gradient(1.2px 1.2px at 19% 71%, rgba(255, 255, 255, 0.75) 50%, transparent 52%),
    radial-gradient(2px 2px at 27% 38%, rgba(255, 233, 168, 0.95) 50%, transparent 52%),
    radial-gradient(1px 1px at 36% 12%, rgba(255, 255, 255, 0.8) 50%, transparent 52%),
    radial-gradient(1.4px 1.4px at 44% 64%, rgba(255, 255, 255, 0.7) 50%, transparent 52%),
    radial-gradient(1px 1px at 52% 28%, rgba(255, 255, 255, 0.85) 50%, transparent 52%),
    radial-gradient(2px 2px at 61% 80%, rgba(207, 233, 226, 0.9) 50%, transparent 52%),
    radial-gradient(1.2px 1.2px at 68% 18%, rgba(255, 255, 255, 0.8) 50%, transparent 52%),
    radial-gradient(1.6px 1.6px at 77% 52%, rgba(255, 233, 168, 0.9) 50%, transparent 52%),
    radial-gradient(1px 1px at 84% 30%, rgba(255, 255, 255, 0.75) 50%, transparent 52%),
    radial-gradient(1.4px 1.4px at 91% 70%, rgba(255, 255, 255, 0.85) 50%, transparent 52%),
    radial-gradient(1px 1px at 96% 10%, rgba(255, 255, 255, 0.7) 50%, transparent 52%),
    radial-gradient(1px 1px at 13% 90%, rgba(255, 255, 255, 0.6) 50%, transparent 52%),
    radial-gradient(1.2px 1.2px at 58% 6%, rgba(255, 255, 255, 0.7) 50%, transparent 52%);
}
/* The band is the one full-bleed element besides the bar: main spans the
 * viewport so the band reaches its edges, while the sections and the footer
 * text stay inside 64rem. */
.home main { width: auto; max-width: none; }
.home main > section, .home .colophon { width: min(64rem, 100% - 2 * var(--moossin-space-3)); max-width: none; margin-inline: auto; }
.home main > section { scroll-margin-top: var(--moossin-space-3); }
.home .hero-band { position: relative; overflow: hidden; width: auto; margin-inline: 0; padding: 1.75rem max(var(--moossin-space-3), calc((100% - 64rem) / 2)) 1.5rem; border-radius: 0 0 2rem 2rem; color: #fff; text-align: center; background-image: var(--moossin-stars), linear-gradient(160deg, var(--moossin-space-deep) 0%, var(--moossin-space) 40%, var(--moossin-teal-deep) 78%, var(--moossin-teal) 100%); }
.home .hero-band::before { content: ""; position: absolute; right: -13rem; bottom: -7rem; width: 36rem; height: 14rem; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.28); transform: rotate(-10deg); pointer-events: none; }
.home .hero-band::after { content: ""; position: absolute; right: -7rem; bottom: -14rem; width: 22rem; height: 22rem; border-radius: 50%; background: radial-gradient(circle at 32% 30%, var(--moossin-teal-light), var(--moossin-teal) 42%, var(--moossin-teal-deep) 72%, var(--moossin-space) 100%); box-shadow: 0 0 60px rgba(63, 154, 139, 0.35); pointer-events: none; }
.home .hero-band > * { position: relative; z-index: 1; }
.home .hero-band > .visually-hidden { position: absolute; }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* The carousel is CSS only (the page carries no script of its own). All
 * slides share one grid cell, so the band is as tall as the tallest slide and
 * nothing jumps when they change; the one on show sits on top and takes the
 * clicks. Every slide stays in the document for assistive technology and
 * keyboard users: a slide whose link has focus is the one shown.
 *
 * A hidden radio group is the control. "Auto" (checked on load) runs the
 * rotation, seven seconds a slide; choosing a dot shows that slide and stops
 * it, which is the stop mechanism WCAG 2.2.2 asks for. "Auto" has no visible
 * label since 2026-10-03 (owner's request): after a dot, loading the page
 * again usually starts the rotation again (a browser may keep the choice
 * on reload or on going back), and keyboard users can still arrow back to
 * it (its name is an aria-label, and the dot row shows its focus). Two layers keep a pause
 * from ever freezing something half shown: the list item switches which slide
 * is on show in one step, and its body fades in and out inside that window.
 * A mouse resting on the carousel or keyboard focus on a dot pauses every
 * layer and forces the body fully opaque (`!important` outranks a running
 * animation), so exactly one whole slide is on show. Focus on a link inside a
 * slide shows that slide instead: there the item, its body and the dots all
 * drop their animation together, so they start again in step, from the first
 * slide, when focus leaves. Dropping it from the item alone left the three
 * layers out of step afterwards and the band blank (2026-10-03 review). A
 * touch screen has no hover to get stuck in, and a dot pressed with a mouse
 * does not count as keyboard focus, so it does not pause anything. Nothing
 * moves, only opacity changes. The owner wants the rotation
 * for every visitor (2026-10-03), so the carousel is exempt from the
 * reduced-motion rule near the top of this file (see the end of this block):
 * it only fades, and the dots, a resting mouse and keyboard focus still stop
 * or pause it. The skeleton carries one radio and one dot per slide: five.
 *
 * The dot of the slide on show is a filled disc and the others are rings, so
 * the state is a shape and not only a colour, and both keep an outline when a
 * contrast theme replaces the colours. */
.home .hero-carousel { position: relative; }
/* The radios sit inside the dot row: a browser scrolls a focused control into
 * view, and one parked at the top of the carousel made the page jump there
 * whenever a dot was pressed with the top of the band scrolled away. */
.home .hero-pick { position: absolute; left: 50%; bottom: 22px; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.home .hero-slides { display: grid; margin: 0; padding: 0; list-style: none; }
.home .hero-slide { grid-area: 1 / 1; display: grid; min-width: 0; opacity: 0; z-index: 0; }
.home .hero-slide:first-child { opacity: 1; z-index: 1; }
.home .hero-slide__body { display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 0; }
.home .hero-slide__kind { margin: 0 0 var(--moossin-space-1); color: var(--moossin-star); font-weight: 600; font-size: 0.85rem; letter-spacing: 0.04em; }
.home .hero-slide h2 { max-width: 34em; margin: 0 0 var(--moossin-space-2); font-size: clamp(1.2rem, 1rem + 1vw, 1.65rem); font-weight: 800; overflow-wrap: anywhere; }
.home .hero-slide h2 a { color: #fff; text-decoration: none; }
.home .hero-slide h2 a:hover { color: #fff; text-decoration: underline; }
.home .hero-slide__summary { max-width: var(--moossin-measure); margin: 0 0 var(--moossin-space-3); color: rgba(255, 255, 255, 0.92); line-height: 1.6; text-wrap: balance; }
/* Each slide and its dot start one seven-second window after the last. */
.home .hero-slide:nth-child(2), .home .hero-dots label:nth-child(2) { --hero-delay: -28s; }
.home .hero-slide:nth-child(3), .home .hero-dots label:nth-child(3) { --hero-delay: -21s; }
.home .hero-slide:nth-child(4), .home .hero-dots label:nth-child(4) { --hero-delay: -14s; }
.home .hero-slide:nth-child(5), .home .hero-dots label:nth-child(5) { --hero-delay: -7s; }
.hero-pick--auto:checked ~ .hero-slides .hero-slide { animation: hero-rotate 35s step-end var(--hero-delay, 0s) infinite; }
.hero-pick--auto:checked ~ .hero-slides .hero-slide__body { animation: hero-fade 35s var(--hero-delay, 0s) infinite; }
@keyframes hero-rotate {
  0% { opacity: 1; z-index: 2; }
  20%, 100% { opacity: 0; z-index: 0; }
}
@keyframes hero-fade {
  0% { opacity: 0; }
  1.5%, 18.5% { opacity: 1; }
  20%, 100% { opacity: 0; }
}
.hero-pick:not(.hero-pick--auto):checked ~ .hero-slides .hero-slide { opacity: 0; z-index: 0; }
.hero-pick--1:checked ~ .hero-slides .hero-slide:nth-child(1),
.hero-pick--2:checked ~ .hero-slides .hero-slide:nth-child(2),
.hero-pick--3:checked ~ .hero-slides .hero-slide:nth-child(3),
.hero-pick--4:checked ~ .hero-slides .hero-slide:nth-child(4),
.hero-pick--5:checked ~ .hero-slides .hero-slide:nth-child(5) { opacity: 1; z-index: 2; }
@media (hover: hover) {
  .home .hero-carousel:hover .hero-slides .hero-slide,
  .home .hero-carousel:hover .hero-slides .hero-slide__body,
  .home .hero-carousel:hover .hero-dots .hero-dot { animation-play-state: paused; }
  .home .hero-carousel:hover .hero-slide__body { opacity: 1 !important; }
}
.home .hero-pick:focus-visible ~ .hero-slides .hero-slide,
.home .hero-pick:focus-visible ~ .hero-slides .hero-slide__body,
.home .hero-pick:focus-visible ~ .hero-dots .hero-dot { animation-play-state: paused; }
.home .hero-pick:focus-visible ~ .hero-slides .hero-slide__body { opacity: 1 !important; }
/* Focus on a link inside a slide: every layer drops its animation in the same
 * rule set, so none of them can restart on its own clock afterwards. */
.home .hero-carousel .hero-slides:focus-within .hero-slide:nth-child(n) { animation: none; opacity: 0; z-index: 0; }
.home .hero-carousel .hero-slides .hero-slide:nth-child(n):focus-within { opacity: 1; z-index: 3; }
.home .hero-carousel .hero-slides:focus-within .hero-slide__body { animation: none; opacity: 1; }
.home .hero-dots { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; width: fit-content; margin: var(--moossin-space-1) auto 0; }
.home .hero-dots label { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; min-height: 44px; border-radius: 999px; cursor: pointer; }
.home .hero-dot { width: 0.7rem; height: 0.7rem; border: 2px solid rgba(255, 255, 255, 0.85); border-radius: 50%; }
.hero-pick--auto:checked ~ .hero-dots label:nth-child(1) .hero-dot { border-color: var(--moossin-star); border-width: 0.35rem; background: var(--moossin-star); }
.hero-pick--auto:checked ~ .hero-dots .hero-dot { animation: hero-dot 35s step-end var(--hero-delay, 0s) infinite; }
@keyframes hero-dot {
  0% { border-color: var(--moossin-star); border-width: 0.35rem; background: var(--moossin-star); }
  20%, 100% { border-color: rgba(255, 255, 255, 0.85); border-width: 2px; background: transparent; }
}
.hero-pick--1:checked ~ .hero-dots label:nth-child(1) .hero-dot,
.hero-pick--2:checked ~ .hero-dots label:nth-child(2) .hero-dot,
.hero-pick--3:checked ~ .hero-dots label:nth-child(3) .hero-dot,
.hero-pick--4:checked ~ .hero-dots label:nth-child(4) .hero-dot,
.hero-pick--5:checked ~ .hero-dots label:nth-child(5) .hero-dot { border-color: var(--moossin-star); border-width: 0.35rem; background: var(--moossin-star); }
/* While a link inside a slide has focus, no dot runs on a timer: they all go
 * back to rings, and the dot of the slide that holds the focus is the filled
 * one. That last rule needs `:has()`; without it no dot is filled, which is
 * still truer than a dot that points at another slide. */
.home .hero-carousel .hero-slides:focus-within ~ .hero-dots label .hero-dot { animation: none; border-color: rgba(255, 255, 255, 0.85); border-width: 2px; background: transparent; }
.home .hero-carousel .hero-slides:has(.hero-slide:nth-child(1):focus-within) ~ .hero-dots label:nth-child(1) .hero-dot,
.home .hero-carousel .hero-slides:has(.hero-slide:nth-child(2):focus-within) ~ .hero-dots label:nth-child(2) .hero-dot,
.home .hero-carousel .hero-slides:has(.hero-slide:nth-child(3):focus-within) ~ .hero-dots label:nth-child(3) .hero-dot,
.home .hero-carousel .hero-slides:has(.hero-slide:nth-child(4):focus-within) ~ .hero-dots label:nth-child(4) .hero-dot,
.home .hero-carousel .hero-slides:has(.hero-slide:nth-child(5):focus-within) ~ .hero-dots label:nth-child(5) .hero-dot { border-color: var(--moossin-star); border-width: 0.35rem; background: var(--moossin-star); }
/* The radios are invisible, so the dot they belong to shows their focus. */
.hero-pick--1:focus-visible ~ .hero-dots label:nth-child(1),
.hero-pick--2:focus-visible ~ .hero-dots label:nth-child(2),
.hero-pick--3:focus-visible ~ .hero-dots label:nth-child(3),
.hero-pick--4:focus-visible ~ .hero-dots label:nth-child(4),
.hero-pick--5:focus-visible ~ .hero-dots label:nth-child(5) { outline: 3px solid #fff; outline-offset: -4px; }
/* The auto radio has no visible label (owner, 2026-10-03); its name is an
 * aria-label, and while it has keyboard focus the whole dot row shows it. */
.hero-pick--auto:focus-visible ~ .hero-dots { outline: 3px solid #fff; outline-offset: 2px; border-radius: 999px; }
.home .hero__action { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--moossin-space-2) var(--moossin-space-3); margin: 0; }
.home .hero__action a { padding: 0 var(--moossin-space-4); border-radius: 999px; text-decoration: none; font-weight: 700; }
.home .button--light { background: #fff; color: var(--moossin-space-deep); }
.home .button--light:hover { background: var(--moossin-mint); color: var(--moossin-space-deep); }
.home .button--ghost { color: #fff; border: 2px solid rgba(255, 255, 255, 0.85); }
.home .button--ghost:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
/* The moss focus ring measures 2.79:1 on space-deep; white keeps it visible. */
.home .hero-band a:focus-visible { outline-color: #fff; }
.home .tools, .home .sites, .home .latest { padding-block: 2rem; }
.home .tools, .home .sites, .home .latest { border-top: 1px solid var(--moossin-line); }
/* The section right under the band needs no rule above it. */
.home #start + section { border-top: 0; }
.home section > h2 { margin: 0 0 var(--moossin-space-2); font-size: 1.5rem; }
.home .section__lead { max-width: 48rem; margin: 0 0 var(--moossin-space-4); color: var(--moossin-muted); }
.home .tools__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.home .tool-card__icon { display: none; }
.home .sites__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.home .sites__actions { display: flex; flex-wrap: wrap; gap: var(--moossin-space-4); margin: 0 0 var(--moossin-space-3); font-weight: 600; }
.home .sites__actions a { display: inline-flex; align-items: center; min-height: 44px; }
.home .latest__grid { grid-template-columns: minmax(0, 1fr); }
.home .latest__grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.home .latest__group { background: transparent; border: 0; border-radius: 0; padding: 0; }
.home .latest__list .latest__entry { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--moossin-space-3); padding-block: var(--moossin-space-3); line-height: 1.65; }
.home .latest__body { min-width: 0; }
.home .latest__work { display: block; margin-bottom: var(--moossin-space-1); font-size: .9rem; color: var(--moossin-muted); }
.home .latest__list a { display: inline-block; padding-block: .35rem; font-weight: 600; }
.home .latest__list time { margin: .35rem 0 0; font-size: .8rem; white-space: nowrap; }
.home .latest__summary { margin: var(--moossin-space-1) 0 0; font-size: .9rem; }
.home .latest__more { margin-top: var(--moossin-space-2); }
/* The home body has no gutter, so the band only needs its own padding. */
.home .colophon { width: auto; margin: var(--moossin-space-5) 0 0; padding: var(--moossin-space-5) max(var(--moossin-space-3), calc((100% - 64rem) / 2)) 4rem; }
.home .colophon__grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr .8fr; gap: var(--moossin-space-4); }
.home .colophon__brand { margin: 0 0 var(--moossin-space-2); font-weight: 700; font-size: 1.25rem; }
.home .colophon__col h2 { margin: 0 0 var(--moossin-space-2); font-size: 1rem; }
.home .colophon__col p { margin: 0; }
.home .colophon__col ul, .home .mooss-nav ul { display: block; margin: 0; padding: 0; list-style: none; }
.home .mooss-nav { margin: 0; }
.home .colophon__note { margin-top: var(--moossin-space-3); padding-top: var(--moossin-space-3); border-top: 1px solid rgba(255, 255, 255, 0.25); font-size: .85rem; }
/* A title with natural pauses breaks at its punctuation and spaces instead of
 * splitting a Chinese word at the line end; the builder adds the class only
 * when every phrase fits a phone line (app/section_digest.py). */
.wrap-phrases { word-break: keep-all; overflow-wrap: anywhere; }
.site-card__action a, .latest__more a, .colophon__grid a { display: inline-flex; align-items: center; min-height: 44px; }
.latest__group h3 a, .site-card h3 a { display: inline-block; padding-block: .35rem; }
/* The carousel keeps rotating when a visitor asks for reduced motion (owner's
 * decision, 2026-10-03). The site-wide rule above shortens every animation to
 * nothing with `!important`; these three selectors outrank its `*` and give
 * the carousel its own timing back. Only opacity changes, and the rotation can
 * still be stopped with a dot or paused by hover and keyboard focus. */
@media (prefers-reduced-motion: reduce) {
  .home .hero-slide,
  .home .hero-slide__body,
  .home .hero-dot { animation-duration: 35s !important; animation-iteration-count: infinite !important; }
}
@media (max-width: 64rem) {
  .home .hero-band { padding-block: 1.5rem 2.75rem; border-radius: 0 0 1.5rem 1.5rem; }
  .home .hero-band::before { right: -12rem; bottom: -7rem; width: 28rem; height: 10.5rem; }
  .home .hero-band::after { right: -6rem; bottom: -15.5rem; width: 18rem; height: 18rem; }
}
@media (max-width: 52rem) {
  .home .colophon__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 42rem) {
  .home .tools__list, .home .sites__list, .home .latest__grid--2 { grid-template-columns: minmax(0, 1fr); }
  .home .latest__list .latest__entry { gap: var(--moossin-space-2); }
}
@media (max-width: 30rem) { .home .colophon__grid { grid-template-columns: minmax(0, 1fr); } }

/* ADR-0013, 2026-10-03 revision: the machine-translation notice on the four
 * English inner pages. The translation worker inserts it right after </main>
 * (`notice_after` in tools/translation.json), so it ends the content and sits
 * between main and the footer band; the owner moved it there from the top of
 * the content. Google's attribution rules ask for the badge next to the
 * translated text, unmodified, fully visible and linking to Google Translate;
 * they set no position. So this is a quiet end of the content, not a box: no
 * fill and no frame, one rule in the line colour, main's column (52rem on the
 * legal pages), and the light page background under the full-colour badge.
 * The pause above the rule is main's own 2.5rem bottom padding plus the 1rem
 * bottom margin of the page's last paragraph (3.5rem on all four pages); the
 * footer band's 2.5rem top margin is the space below. Measured on the page
 * background: muted text 7.67:1, moss links 6.19:1. The PNG is Google's @2x
 * file (352 x 32), drawn at its 1x size of 176 x 16 and never smaller; both
 * sides are set, so the box keeps its height before the image arrives and
 * the footer does not jump. 4px above and below make the link 24px tall. */
.machine-translation {
  width: calc(100% - 2 * var(--moossin-space-3));
  max-width: 60rem;
  margin: 0 auto;
  padding-top: var(--moossin-space-3);
  border-top: 1px solid var(--moossin-line);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--moossin-muted);
}

.legal-main + .machine-translation {
  max-width: 52rem;
}

/* Every legal section ends with a rule, so the last one would stack with the
 * notice's rule as an empty stripe; on a translated page the notice's rule
 * takes its place. Without :has() both rules stay, which is only untidy. */
.legal-main:has(+ .machine-translation) .legal-content > section:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.machine-translation p {
  max-width: var(--moossin-measure);
  margin: 0 0 var(--moossin-space-2);
}

.machine-translation__badge {
  display: inline-block;
  padding: 4px 0;
  line-height: 0;
  vertical-align: middle;
}

.machine-translation__badge img {
  display: block;
  width: 176px;
  max-width: none;
  height: 16px;
}

.machine-translation .machine-translation__disclaimer {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.5;
}

/* ADR-0019: the inner pages' footer row -- home, About, the two legal pages,
 * contact and the update history, the same on every page -- with links as
 * tall as the homepage footer's, and the copyright line under it. */
.colophon__links {
  margin: 0;
}

.colophon__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* ADR-0019: the privacy policy's table of what each section keeps. A narrow
 * screen stacks every row into a small labelled block instead of scrolling
 * the table sideways. The labels are words inside each cell, hidden while the
 * column headers show, so the translated English page translates them too
 * (a data attribute shown with CSS would stay Chinese there). */
.legal-content table {
  width: 100%;
  margin: 0 0 var(--moossin-space-3);
  border-collapse: collapse;
  font-size: 0.95rem;
  line-height: 1.6;
}

.legal-content caption {
  margin-bottom: var(--moossin-space-2);
  color: var(--moossin-muted);
  font-weight: 700;
  text-align: left;
}

.legal-content th,
.legal-content td {
  padding: var(--moossin-space-2);
  border: 1px solid var(--moossin-line);
  text-align: left;
  vertical-align: top;
}

.legal-content thead th {
  background: var(--moossin-surface);
}

.legal-content tbody th {
  font-weight: 700;
}

.legal-table__label {
  display: none;
}

@media (max-width: 42rem) {
  .legal-content table,
  .legal-content caption,
  .legal-content tbody,
  .legal-content tr,
  .legal-content th,
  .legal-content td {
    display: block;
  }

  .legal-content thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .legal-content tbody tr {
    margin-bottom: var(--moossin-space-3);
    border: 1px solid var(--moossin-line);
  }

  .legal-content tbody th,
  .legal-content td {
    border: 0;
  }

  .legal-content tbody th {
    background: var(--moossin-surface);
  }

  .legal-table__label {
    display: block;
    color: var(--moossin-muted);
    font-size: 0.85rem;
    font-weight: 700;
  }
}
