/* ===========================================================================
   BC Class 5&7 — single-page redesign demo (mockup, not the build).

   Palette is taken from two things the product already owns: the app's own
   light-sage screens (assets/app-*.png) and BC road signage — guide-sign
   green, warning-diamond yellow, stop red. No webfont: the real page is
   refused by `external_fetches()` if it asks another host for anything, so
   every face here is a system face.
   =========================================================================== */

:root {
  color-scheme: light dark;

  --paper:      #eef2ec;
  --paper-deep: #e4eae1;
  --card:       #ffffff;
  --ink:        #13201a;
  --muted:      #5d6d63;
  --rule:       #d3dcd2;
  --rule-hard:  #13201a;

  --green:      #046a38;
  --on-green:   #ffffff;
  --logo:       #046a38;
  --logo-mark:  #ffffff;
  --green-soft: #e3f0e7;
  --amber:      #ffc72c;
  --amber-soft: #fdf3d3;
  --red:        #c8102e;
  --red-soft:   #fbe6e8;

  --shadow-lift: 0 1px 2px rgba(19, 32, 26, .06), 0 14px 34px -18px rgba(19, 32, 26, .30);
  --shadow-flat: 0 1px 0 rgba(19, 32, 26, .05);

  --sans: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --serif: Charter, "Iowan Old Style", Georgia, "PingFang SC", "Hiragino Sans GB", serif;
  --cond: "Avenir Next Condensed", "Helvetica Neue", "Arial Narrow", system-ui, sans-serif;

  --page: 72rem;
  --gutter: clamp(1.1rem, 4vw, 3rem);
}

/* Dark is designed, not inverted: a warm green-charcoal ground, the guide
   green lifted until it passes contrast on it, the sign yellow untouched
   because a sign's yellow does not change at night either. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:      #121613;
    --paper-deep: #0d110f;
    --card:       #1a211c;
    --ink:        #e9efe8;
    --muted:      #9aa89e;
    --rule:       #2a332c;
    --rule-hard:  #46534a;

    --green:      #57b67e;
    --on-green:   #09150e;
    --logo:       #57b67e;
    --logo-mark:  #0d1a12;
    --green-soft: #14291d;
    --amber:      #ffc72c;
    --amber-soft: #2c2410;
    --red:        #ff7a86;
    --red-soft:   #2e1517;

    --shadow-lift: 0 1px 2px rgba(0, 0, 0, .5), 0 18px 40px -20px rgba(0, 0, 0, .75);
    --shadow-flat: 0 1px 0 rgba(0, 0, 0, .35);
  }
}
:root[data-theme="dark"] {
  --paper:      #121613;
  --paper-deep: #0d110f;
  --card:       #1a211c;
  --ink:        #e9efe8;
  --muted:      #9aa89e;
  --rule:       #2a332c;
  --rule-hard:  #46534a;

  --green:      #57b67e;
  --on-green:   #09150e;
  --logo:       #57b67e;
  --logo-mark:  #0d1a12;
  --green-soft: #14291d;
  --amber:      #ffc72c;
  --amber-soft: #2c2410;
  --red:        #ff7a86;
  --red-soft:   #2e1517;

  --shadow-lift: 0 1px 2px rgba(0, 0, 0, .5), 0 18px 40px -20px rgba(0, 0, 0, .75);
  --shadow-flat: 0 1px 0 rgba(0, 0, 0, .35);
}

* { box-sizing: border-box; }

/* The nav glides to a section rather than jumping. Anyone who asked for
   reduced motion still lands there instantly. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--sans); }

h1 {
  /* pretty, not balance: balance evened the line lengths and left 「Practice
     the」 alone on line one. pretty only refuses the one-word last line. */
  text-wrap: pretty;
  font-size: clamp(2.5rem, 5.4vw, 4.1rem);
  line-height: 1.01;
  letter-spacing: -0.035em;
  font-weight: 800;
  margin: 0 0 1.1rem;
}
h2 {
  text-wrap: pretty;
  font-size: clamp(1.6rem, 2.6vw, 2.15rem);
  line-height: 1.1;
  letter-spacing: -0.028em;
  font-weight: 800;
  margin: 0 0 .5rem;
}
h3 {
  font-size: 1.02rem;
  letter-spacing: -0.012em;
  font-weight: 700;
  margin: 0 0 .35rem;
}
p { margin: 0 0 1rem; max-width: 40rem; text-wrap: pretty; }
a { color: inherit; }

:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 3px;
  border-radius: 3px;
}

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding-inline: var(--gutter);
}
/* Chinese wraps between any two characters, including inside a word. Phrases
   that must stay whole wrap themselves in this. */
.nb { white-space: nowrap; }

/* --------------------------------------------------------------- top bar */

.bar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(150%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease;
}
.bar .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 4rem;
}

/* The three licence classes, as one plate with three values — never one
   class alone: a reader exchanging a Class 5 who sees only 「7」 leaves. */
.plates {
  display: inline-flex;
  border: 2px solid var(--rule-hard);
  border-radius: 4px;
  overflow: hidden;
  font-family: var(--cond);
  font-weight: 700;
  font-size: .95rem;
  letter-spacing: .01em;
  line-height: 1;
}
.plates span { padding: .3rem .42rem; }
.plates span + span { border-left: 2px solid var(--rule-hard); }

.brand { display: flex; align-items: center; gap: .55rem; text-decoration: none; }
.logo { height: 1.85rem; width: auto; display: block; flex: 0 0 auto; }
.brand b {
  font-family: var(--sans);
  font-size: 1.02rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.bar nav {
  margin-left: auto;
  display: flex;
  /* ⛔ .25rem read as crowded (用户 2026-09-19). The four links are pills, so
     the gap has to clear their own padding before it looks like a gap. */
  gap: .6rem;
  font-family: var(--sans);
  font-size: .875rem;
}
.bar nav a {
  padding: .45rem .8rem;
  border-radius: 99px;
  text-decoration: none;
  color: var(--muted);
}
.bar nav a:hover { color: var(--ink); background: var(--paper-deep); }

.switches { display: flex; gap: .4rem; align-items: center; }
.switch {
  /* ⛔ The language switch is an `<a>` and the theme switch is a `<button>`,
     so only one of them inherits the link underline. They sit side by side and
     have to read as one pair of controls. */
  text-decoration: none;
  font-family: var(--sans);
  font-size: .8125rem;
  font-weight: 600;
  padding: .4rem .7rem;
  border: 1px solid var(--rule);
  border-radius: 99px;
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  line-height: 1.2;
}
.switch:hover { border-color: var(--rule-hard); }
/* With JavaScript off this control is not shown: remembering the choice needs
   storage, so without the script a click would do nothing. */
html.js .js-only[hidden] { display: inline-flex; }

/* The four sections, folded away on narrow screens.
   ⛔ **Added 2026-09-19** (用户: 「发现我在 demo 漏掉了一个在手机界面下的菜单」).
   The demo hides `.bar nav` below 860px and puts nothing in its place, so on
   any phone — and on an iPad in portrait, which is 768 — the four anchors are
   unreachable and the only way through the page is to scroll.

   🔑 **`<details>` rather than a scripted panel.** Open and closed is a state
   the element already has: it works with the script off, the keyboard opens it,
   and a screen reader announces it, none of which anyone has to write. The
   script only closes it after a click, and a menu that stays open is a much
   smaller failure than one that never opens. */
.menu { display: none; position: relative; }
.menu > summary {
  list-style: none;
  display: inline-flex; align-items: center; gap: .45rem;
  font-family: var(--sans); font-size: .8125rem; font-weight: 600;
  padding: .4rem .7rem;
  border: 1px solid var(--rule); border-radius: 99px;
  background: var(--card); color: var(--ink);
  cursor: pointer;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary:hover { border-color: var(--rule-hard); }
/* Three bars, drawn rather than fetched: the page contacts nobody (R3). */
.menu-bars {
  width: .85rem; height: .58rem;
  border-top: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  position: relative;
}
.menu-bars::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  border-top: 2px solid currentColor; transform: translateY(-1px);
}
/* ⛔ Absolute, not in the flow: the bar is sticky, so a panel that took space
   would push the hero down every time it opened. */
.menu-panel {
  position: absolute; right: 0; top: calc(100% + .5rem);
  z-index: 30;
  display: flex; flex-direction: column; gap: .15rem;
  min-width: 12rem; padding: .4rem;
  background: var(--card);
  border: 1px solid var(--rule); border-radius: 12px;
  box-shadow: var(--shadow-lift);
}
.menu-panel a {
  padding: .55rem .7rem; border-radius: 8px;
  text-decoration: none; color: var(--ink);
  font-family: var(--sans); font-size: .9375rem;
}
.menu-panel a:hover { background: var(--paper-deep); }
/* ⚠️ The section you are reading is marked here rather than on the summary.
   The scroll spy already sets `aria-current` on every copy of the link, and a
   closed menu cannot show it — naming the current section on the button would
   need a second mechanism to keep in step with the first. */
.menu-panel a[aria-current="true"] {
  background: var(--green-soft);
  box-shadow: inset 0 0 0 1.5px var(--green);
  font-weight: 600;
}

@media (max-width: 860px) {
  /* ⛔ The direct child only. `.bar nav` also matches `.menu-panel`, which is a
     `<nav>` inside the same bar — the panel rendered with `display: none` and
     the menu opened onto nothing. The `open` attribute was set, so every check
     of 「did it open」 said yes. */
  .bar > .wrap > nav { display: none; }
  /* ⛔ `margin-left: auto` moves here from `.bar nav`. Hiding the nav took the
     only thing pushing the controls to the right edge with it, and all three
     bunched up against the wordmark (用户 2026-09-19). */
  .menu { display: block; margin-left: auto; }
  .switches { margin-left: .6rem; }
}

/* Below 560px the bar carries five things and something has to give.
   ⛔ **The site name is the last thing to go, not the first** (用户 2026-09-19:
   「网站名称我还想尽可能的显示出来」). Room is taken from the controls first:
   the menu keeps its three bars and drops the word, the pills tighten, and the
   name shrinks rather than disappearing. */
@media (max-width: 560px) {
  .menu > summary { font-size: 0; gap: 0; padding: .4rem .55rem; }
  .menu > summary .menu-bars { font-size: 1rem; }
  .switch { padding: .35rem .55rem; font-size: .78rem; }
  .brand { min-width: 0; }
  .brand b {
    font-size: .9rem;
    /* Last resort only: at this width the name still fits. If a longer name
       ever arrives it trims instead of pushing the controls off the edge. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
}

@media (max-width: 400px) {
  .brand b { font-size: .82rem; }
  .switch { padding: .3rem .5rem; font-size: .72rem; }
}

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

.hero { padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(2rem, 4vw, 3.5rem); }
.hero .wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 27rem);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.hero .lede { font-size: 1.1875rem; line-height: 1.6; color: var(--muted); max-width: 32rem; }
.hero .lede strong { color: var(--ink); font-weight: 600; }

.classline {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 1.4rem;
  font-family: var(--sans);
  font-size: .85rem;
  color: var(--muted);
}

.cta { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.6rem 0 0; }
.cta a {
  font-family: var(--sans);
  font-weight: 650;
  font-size: .95rem;
  text-decoration: none;
  padding: .72rem 1.15rem;
  border-radius: 8px;
  border: 1.5px solid var(--rule-hard);
  transition: transform .12s ease, background-color .15s ease, color .15s ease;
}
.cta a.primary { background: var(--green); border-color: var(--green); color: var(--on-green); }
.cta a.primary:hover { transform: translateY(-1px); }
.cta a.secondary { background: transparent; }
.cta a.secondary:hover { background: var(--ink); color: var(--paper); }

.hero-note { margin-top: 1.1rem; font-size: .875rem; color: var(--muted); font-family: var(--sans); }

@media (max-width: 960px) {
  .hero .wrap { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------- counts band */

.counts {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper-deep);
}
.counts .wrap.tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
  padding-block: 1.6rem;
}
.counts b {
  display: block;
  font-variant-numeric: tabular-nums;
  font-family: var(--sans);
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
}
/* ⛔ The label, not every span in the band. The demo could write `.counts span`
   because its only spans were the labels; here the number itself is a
   `<span class="counted" data-count=…>` (that attribute is what lets the
   checker tie it to `content/`), and so is the chapter range inside the note.
   Under the loose selector both of those were served the label's 13px muted
   type — the four numbers rendered at half their size, and 「1–9」 became a
   block in the middle of a sentence. */
.counts .tiles > div > span {
  display: block;
  margin-top: .3rem;
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--muted);
}
.counts-note {
  padding-bottom: 1.6rem;
  margin: 0;
  max-width: 46rem;
  font-size: .9rem;
  color: var(--muted);
}
@media (max-width: 680px) {
  /* The grid is declared on `.counts .wrap.tiles`; one class fewer here loses to
     it, and a media query adds no specificity of its own. Note `.counts-note`
     is also a `.counts .wrap` and is not a grid. */
  .counts .wrap.tiles { grid-template-columns: repeat(2, 1fr); }
}

/* ------------------------------------------------------------ section */

section.band { padding: clamp(3rem, 6vw, 5rem) 0; }
[id] { scroll-margin-top: 5rem; }   /* The bar is sticky, so anchors have to clear its height. */
.section-head { max-width: 42rem; margin-bottom: 2rem; }
.section-head p { color: var(--muted); margin-bottom: 0; }

/* --------------------------------------------------------- the sign wall */

.signwall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.4rem, 1fr));
  gap: .5rem;
  margin-bottom: 1.2rem;
}

/* ------------------------------------------------------------ the screens */

.screens {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 1rem;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.screens figure {
  margin: 0;
  flex: 0 0 16rem;
  scroll-snap-align: start;
}
.screens img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 14px;
  border: 1px solid var(--rule);
  box-shadow: var(--shadow-lift);
  background: var(--card);
}
.screens figcaption {
  margin-top: .7rem;
  font-family: var(--sans);
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--muted);
}
.screens figcaption b { display: block; color: var(--ink); font-weight: 650; }

/* -------------------------------------------------------------- feature list */

.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 0 2.5rem;
  border-top: 1px solid var(--rule);
}
.features > div {
  padding: 1.3rem 0;
  border-bottom: 1px solid var(--rule);
}
.features p { margin: 0; font-size: .96rem; color: var(--muted); }

/* ----------------------------------------------------------------- price */

/* The buy block: what it opens on the left, the two lengths on the right.
   The day count keeps the regulatory-plate shape the rest of the page speaks
   in; everything under it is an ordinary product card, because it is one. */
.offer {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 21rem);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
  margin-bottom: 2rem;
}
@media (max-width: 860px) { .offer { grid-template-columns: minmax(0, 1fr); } }

.offer-what { display: flex; flex-direction: column; height: 100%; }
.offer-what h3 { font-size: 1.05rem; margin-bottom: .7rem; }
ul.opens { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: .55rem; }
ul.opens li {
  display: grid;
  grid-template-columns: 1.2rem minmax(0, 1fr);
  gap: .6rem;
  align-items: start;
  font-size: .98rem;
}
ul.opens li::before {
  content: "";
  margin-top: .45rem;
  width: .8rem; height: .45rem;
  border-left: 2.5px solid var(--green);
  border-bottom: 2.5px solid var(--green);
  transform: rotate(-45deg);
}
.offer-free {
  font-size: .92rem;
  color: var(--muted);
  border-top: 1px solid var(--rule);
  padding-top: .9rem;
  margin-bottom: 0;
}

.plans { display: grid; gap: .9rem; }
.plan {
  display: grid;
  grid-template-columns: 3.6rem minmax(0, 1fr);
  grid-template-areas: "plate price" "plate rate" "go go";
  column-gap: 1rem;
  align-items: center;
  background: var(--card);
  /* 2px on both cards, not 1px + 2px: the highlighted one would otherwise sit
     a pixel over and its price would not start on the same line as the other's. */
  border: 2px solid var(--rule);
  border-radius: 14px;
  padding: 1.1rem 1.2rem 1.2rem;
  box-shadow: var(--shadow-flat);
}
.plan.pick { border-color: var(--green); box-shadow: var(--shadow-lift); }

.days-plate {
  grid-area: plate;
  width: 3.6rem;
  background: #fff;
  color: #000;
  border: 3px solid #000;
  border-radius: 5px;
  padding: .3rem .2rem .35rem;
  text-align: center;
  font-family: var(--cond);
  line-height: 1;
}
.days-plate b { display: block; font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
.days-plate i { font-style: normal; font-size: .68rem; font-weight: 700; letter-spacing: .1em; }

.plan .price {
  grid-area: price;
  align-self: end;
  font-family: var(--sans);
  font-size: 1.85rem;
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.1;
}
.plan .price em { font-style: normal; font-size: .95rem; font-weight: 650; color: var(--muted); }
.plan .rate {
  grid-area: rate;
  align-self: start;
  font-family: var(--sans);
  font-size: .82rem;
  color: var(--muted);
}
.plan .go {
  grid-area: go;
  margin-top: 1rem;
  display: block;
  text-align: center;
  text-decoration: none;
  font-family: var(--sans);
  font-weight: 700;
  font-size: .98rem;
  padding: .78rem 1rem;
  border-radius: 10px;
  border: 1.5px solid var(--rule-hard);
  transition: transform .12s ease, background-color .15s ease, color .15s ease;
}
.plan.pick .go { background: var(--green); border-color: var(--green); color: var(--on-green); }
/* ⛔ The seven-day button is filled too, and still plainly the second one:
   solid brand green with reversed text is the recommendation, the tinted green
   surface is the alternative. Two buttons of equal weight would mean no
   recommendation at all.
   ⚠️ **The same fill the chosen correct answer gets** (`--green-soft`, 用户
   2026-09-19). One tinted-green surface across the page rather than a second
   colour invented for this one button. */
.plan:not(.pick) .go { background: var(--green-soft); border-color: var(--rule-hard); }
.plan .go:hover { transform: translateY(-1px); }
.plan:not(.pick) .go:hover { background: var(--ink); color: var(--paper); }
.plans-note { margin: .2rem 0 0; font-family: var(--sans); font-size: .82rem; color: var(--muted); }

.terms { max-width: 42rem; }
.terms li { margin-bottom: .5rem; }
.paynote {
  max-width: 42rem;
  margin-top: 1.1rem;
  margin-bottom: 0;
  font-size: .92rem;
  color: var(--muted);
  border-left: 3px solid var(--amber);
  padding-left: 1rem;
}

/* ------------------------------------------------------------------- faq */

.faq { max-width: 46rem; border-top: 1px solid var(--rule); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  list-style: none;
  cursor: pointer;
  padding: 1rem 2rem 1rem 0;
  font-family: var(--sans);
  font-weight: 650;
  font-size: 1rem;
  position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
/* The arrow is centred on the line box, not pinned to a fixed `top`: Chinese
   and English line heights differ, and a hard-coded value sits off-centre in
   one of them. */
.faq summary::after {
  content: "";
  position: absolute;
  right: .45rem; top: 50%;
  width: .5rem; height: .5rem;
  margin-top: -.35rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform .2s ease;
}
.faq details[open] summary::after { margin-top: -.1rem; transform: rotate(-135deg); }
.faq .answer { padding: 0 0 1.1rem; color: var(--muted); font-size: .96rem; }
.faq .answer p { margin: 0; }

/* --------------------------------------------------------------- refusals */

.refusals { max-width: 46rem; display: grid; gap: .9rem; }
.refusals > div {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  gap: .9rem;
  align-items: start;
}
.refusals .no {
  width: 2.2rem; height: 2.2rem;
  border-radius: 50%;
  border: 3px solid var(--red);
  position: relative;
}
.refusals .no::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 1.9rem; height: 3px;
  background: var(--red);
  transform: translate(-50%, -50%) rotate(-45deg);
}
.refusals p { margin: .15rem 0 0; font-size: .96rem; color: var(--muted); }

/* ------------------------------------------------------------------ store */

/* Neither store has a listing. Nothing here may look pressable. */
.waiting {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1rem;
  align-items: center;
  padding: .9rem 1.1rem;
  border: 1px dashed var(--rule-hard);
  border-radius: 12px;
  font-family: var(--sans);
  font-size: .875rem;
  color: var(--muted);
  max-width: 46rem;
}
.waiting b { color: var(--ink); font-weight: 650; }

/* ----------------------------------------------------------------- footer */

footer.page {
  border-top: 1px solid var(--rule);
  background: var(--paper-deep);
  padding: 2.5rem 0 3.5rem;
  font-size: .75rem;
  color: var(--muted);
}
footer.page a { color: var(--ink); }
footer.page p { max-width: 44rem; }
/* The two legal links run on at the end of the disclaimer, so they take the
   face, size and colour of the sentence they sit in; the underline is the only
   thing marking them as links.

   ⛔ **`footer.page` has to be in the selector, and that is the whole point of
   this rule.** `footer.page a` above is 0-1-2 and a bare `.legal-links a` is
   0-1-1, so --ink won and the links stayed a shade darker than the sentence.
   The first draft of this comment already named `footer.page a` as the thing
   that would pull them back out of the paragraph — and then the rule under it
   could not beat it. Both rules applied; one simply won.

   ⚠️ `:visited` is listed because it is a separate state that would otherwise
   take the UA's purple the moment either page has been opened. */
footer.page .legal-links a,
footer.page .legal-links a:visited { color: inherit; }

/* --------------------------------------------------------------- entrance */

/* Only two things move as you scroll, and each plays once: the numbers count
   up, and the wall of signs lights up in turn.
   ⛔ No 「fade-and-rise every section into view」 — that is the page where
   everything drifts. */
@media (prefers-reduced-motion: no-preference) {

  /* The wall lights up as it comes into view. ⛔ Two implementations, and only
     one runs: the script adds `.in` where scroll-driven animation is missing,
     and skips it where the browser has it. Both at once fight each other. */
  html.js .signwall svg { opacity: 0; transform: scale(.9);
                          transition: opacity .5s ease, transform .5s ease; }
  html.js .signwall.in svg { opacity: 1; transform: none; }

  /* Where the browser supports scroll-driven animation, the wall follows the
     scroll position instead: it lights up as far as you have come and unwinds
     as you scroll back. It cannot finish before you see it. */
  @supports (animation-timeline: view()) {
    html.js .signwall svg {
      opacity: 1; transform: none; transition: none;
      animation: signIn linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}
@keyframes signIn {
  from { opacity: 0; transform: scale(.9); }
  to   { opacity: 1; transform: none; }
}

.quiz {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 18px;
  box-shadow: var(--shadow-lift);
  overflow: hidden;
  font-family: var(--sans);
  position: relative;
}
.quiz-head {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--rule);
  font-size: .8125rem;
  color: var(--muted);
}
.quiz-progress { display: flex; gap: .3rem; }
.quiz-progress i {
  width: 1.5rem; height: .28rem; border-radius: 99px;
  background: var(--rule); display: block;
  transition: background-color .25s ease;
}
.quiz-progress i.on { background: var(--green); }
/* Question language, set apart from the page language — the app's own
   distinction, and the reason this product exists. */
/* 看得出是个开关：有轨道、有滑块、鼠标移上去另一半会亮。
   只有描边和一段灰字的话，它读起来像个状态标签。 */
.qlang {
  margin-left: auto;
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 99px;
}
.qlang button {
  font: inherit;
  font-size: .75rem;
  font-weight: 650;
  padding: .24rem .6rem;
  border: 0;
  border-radius: 99px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  line-height: 1.35;
  transition: background-color .15s ease, color .15s ease;
}
.qlang button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
}
.qlang button:not([aria-pressed="true"]):hover {
  background: var(--rule);
  color: var(--ink);
}

.quiz-body { padding: 1.1rem 1.15rem 1.25rem; }
.quiz-sign {
  display: block;
  height: 5.5rem;
  margin: .2rem auto 1rem;
  padding: .35rem .5rem;
  background: #fff;
  border-radius: 6px;
  box-shadow: var(--shadow-flat);
}

.quiz-q {
  font-size: 1.0625rem;
  font-weight: 650;
  line-height: 1.4;
  letter-spacing: -0.012em;
  margin: 0 0 .9rem;
  max-width: none;
}
.opts { display: grid; gap: .45rem; }
.opt label {
  display: flex;
  gap: .6rem;
  /* ⛔ Centred, not top-aligned (用户 2026-09-19). Two of the four options wrap
     to a second line and the circle sat beside the first line only, which
     reads as the mark belonging to that line rather than to the option. */
  align-items: center;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: .9375rem;
  line-height: 1.4;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid transparent;
  border-radius: 10px;
  padding: .65rem .75rem;
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
.opt label:hover { border-color: var(--rule-hard); }

.opt label .mark {
  flex: 0 0 auto;
  width: 1.15rem; height: 1.15rem;
  border: 1.5px solid var(--rule-hard);
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: .7rem; font-weight: 800; line-height: 1;
  color: transparent;
}
.opt.is-answer input:checked ~ label { background: var(--green-soft); border-color: var(--green); }
.opt.is-answer input:checked ~ label .mark { background: var(--green); border-color: var(--green); color: var(--on-green); }
.opt.is-answer input:checked ~ label .mark::after { content: "\2713"; }
.opt:not(.is-answer) input:checked ~ label { background: var(--red-soft); border-color: var(--red); }
.opt:not(.is-answer) input:checked ~ label .mark { background: var(--red); border-color: var(--red); color: #fff; }
.opt:not(.is-answer) input:checked ~ label .mark::after { content: "\2715"; }

.reveal {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .32s cubic-bezier(.2, .7, .3, 1);
}
.q:has(input:checked) .reveal { grid-template-rows: 1fr; }
.reveal > div { overflow: hidden; }
.reveal .inner {
  margin-top: .85rem;
  padding-top: .85rem;
  border-top: 1px solid var(--rule);
  font-family: var(--serif);
  font-size: .9375rem;
  line-height: 1.6;
}
.cite {
  margin-top: .6rem;
  font-family: var(--sans);
  font-size: .78rem;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: .35rem .5rem;
  align-items: baseline;
}
.cite b { color: var(--ink); font-weight: 650; }
/* The verdict above the explanation. ⛔ Both sentences are in the markup and
   the stylesheet picks one, so it says the right thing with no script — the
   demo wrote it from JavaScript and the wording here is the demo's. */
.verdict { display: block; font-family: var(--sans); font-weight: 650; margin-bottom: .35rem; }
.verdict.right { color: var(--green); }
.verdict.wrong { color: var(--red); }
.q .verdict { display: none; }
.q:has(.opt.is-answer input:checked) .verdict.right,
.q:has(.opt:not(.is-answer) input:checked) .verdict.wrong { display: block; }
.quiz-foot {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .75rem 1.15rem 1rem;
  font-size: .8125rem;
  color: var(--muted);
}
.next {
  margin-left: auto;
  font: inherit;
  font-weight: 650;
  color: var(--ink);
  background: transparent;
  border: 1.5px solid var(--rule-hard);
  border-radius: 8px;
  padding: .45rem .8rem;
  cursor: pointer;
}
.next:hover { background: var(--ink); color: var(--paper); }

/* ---------------------------------------------------------- counts band */


/* Radios instead of the demo's buttons: a radio group is operable by keyboard
   and readable by a screen reader without any of that being written, and it
   answers with the script switched off. ⛔ Everything above is the demo's
   styling unchanged; only the element the rules hang on moved from the button
   to its label. */
.opts { list-style: none; margin: 0; padding: 0; }
.opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt label { display: flex; }
.opt input:focus-visible ~ label { outline: 2px solid var(--green); outline-offset: 2px; }
/* One card at a time once the script can page between them; all three without
   it, each answerable. */
.q[hidden], .quiz-progress[hidden], .qlang[hidden], .next[hidden] { display: none; }
.quiz[data-qlang="en"] [data-q="zh-Hans"],
.quiz[data-qlang="zh-Hans"] [data-q="en"] { display: none; }

/* The wall of drawings: inline SVG here, so the rule targets the element
   rather than an `<img>`. ⛔ White plate underneath — the artwork carries its
   own white field and a dark page would otherwise show a white rectangle. */
.signwall svg { width: 100%; height: auto; background: #fff; border-radius: 6px; padding: 3px; }

/* ------------------------------------------------------------------ */
/* Restored 2026-09-19. These eight behaviours were in the demo and were
   dropped when the pages were written, on a reading of R2 that R2 does not
   support: R2 is about the server ("the site is one page, static, requests do
   not go through the Go service") and says nothing about the page depending on
   JavaScript. ⛔ Every control below still hides itself when the script does
   not run — that rule is unrelated to R2 and stands on its own. */

/* The bar sits on the page rather than floating over it, once you scroll. */
.bar.is-stuck { border-bottom-color: var(--rule); }

/* The nav marks the section you are reading. ⛔ The links are real anchors, so
   with no script they still jump; what is missing is the highlight. */
.bar nav a[aria-current="true"] {
  color: var(--ink); background: var(--green-soft);
  box-shadow: inset 0 0 0 1.5px var(--green); font-weight: 600;
}

@media (prefers-reduced-motion: no-preference) {
  /* The hero's one entrance, on load. Nothing else animates on scroll. */
  html.js .rise { opacity: 0; transform: translateY(12px); }
  html.js .rise.in {
    opacity: 1; transform: none;
    transition: opacity .55s ease, transform .55s cubic-bezier(.2, .7, .3, 1);
  }
}


/* ⛔ A class rule that sets `display` beats the user agent's `[hidden]`, so a
   control written `<span class="qlang" hidden>` is shown anyway. Both of these
   were visible with no script running — the state where they do nothing.
   ⚠️ The demo hit the same thing on `.quiz-sign[hidden]`; the note there is
   what named the cause. */
.quiz-progress[hidden], .qlang[hidden], .next[hidden], .q[hidden] { display: none; }

/* The question's language, chosen by the card rather than by the page.
   ⛔ Both languages are in the markup and CSS picks one, so the card opens in
   the page's own language with no script running; the button only changes it.
   🔑 This is the app's own distinction — interface language and question
   language are two settings — and the page lets a reader press it once. */
.quiz[data-qlang="en"] [data-q="zh-Hans"],
.quiz[data-qlang="zh-Hans"] [data-q="en"] { display: none; }

/* One card at a time once the script can page between them. ⛔ Without it
   every card is shown, which is what a reader with no script gets: three
   questions, all answerable. */
.q[hidden] { display: none; }
