/* Chinese Flow. Every color comes from a palette: the admin picks one for
   the whole site (data-palette on <html>, see palette.go). Chinese is the
   only large type; controls around it stay quiet. */
@import url("/static/fonts/wenkai.css");
@import url("/static/fonts/nunito.css");
@import url("/static/fonts/noto-sans-sc.css");


:root {
  --radius-sm: 12px;   /* fields, small chips */
  --radius: 18px;      /* cards, choices */
  --radius-lg: 26px;   /* the big playing cards */
  /* Spacing: one scale for the whole site. Tight inside a block, wide
     between blocks: what sits close together reads as one thing. */
  --sp-1: .25rem;
  --sp-2: .5rem;
  --sp-3: .75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --gap: var(--sp-4);
  --gap-lg: var(--sp-6);
  /* Type: the interface is 17 px; Chinese and titles are the only large type. */
  --fs-xs: .75rem;
  --fs-sm: .875rem;
  /* Under a character (pinyin, French in a tile): the smallest running text. */
  --fs-note: .8125rem;
  --fs-md: 1.0625rem;
  --fs-lg: 1.25rem;
  --fs-xl: 1.6rem;
  /* Buttons: one size for every button, one for the small ones (a line's
     tools, the admin). A big call to action is taller, not larger. */
  --fs-btn: var(--fs-md);
  --fs-btn-sm: .9375rem;
  /* The page's top padding, eaten by a page head band. */
  --page-top: clamp(1.25rem, 3vw, 2rem);
  /* Chinese is written in Kai (楷体) everywhere. In French text Nunito
     comes first: it has no Chinese character, so those fall to LXGW
     WenKai, while the punctuation both fonts have (… ’ “ ” — ·) stays
     Latin; WenKai would draw « Moi, c'est… » with the dots centered, as in
     Chinese. Chinese text (.hanzi) takes WenKai first, its punctuation too. */
  --serif: "Nunito", "LXGW WenKai", system-ui, sans-serif;
  --hanzi: "LXGW WenKai", "Noto Sans SC", "Kaiti SC", "KaiTi", "PingFang SC", "Microsoft YaHei", sans-serif;
  --sans: "Nunito", "LXGW WenKai", system-ui, sans-serif;
  --ease: cubic-bezier(.2,.7,.2,1);
  --dur: 240ms;
  /* The brand's brush stroke, as a mask: a
     short stroke of ink, its end dry, painted in vermillon under the labels
     over titles. */
  --brush: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -1 84 14'%3E%3Cdefs%3E%3Cfilter id='b' x='-4%25' y='-60%25' width='108%25' height='220%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.05 0.5' numOctaves='2' seed='4' result='n'/%3E%3CfeDisplacementMap in='SourceGraphic' in2='n' scale='2.2' xChannelSelector='R' yChannelSelector='G' result='d'/%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.01 0.9' numOctaves='1' seed='17' result='s'/%3E%3CfeColorMatrix in='s' type='matrix' values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -.9 1.5' result='m'/%3E%3CfeComposite in='d' in2='m' operator='in'/%3E%3C/filter%3E%3ClinearGradient id='g' x1='0' x2='1'%3E%3Cstop offset='0'/%3E%3Cstop offset='.8' stop-opacity='.9'/%3E%3Cstop offset='1' stop-opacity='.45'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M2 6.4C1 3.6 4 2.2 8 2.4c14 .6 34 .8 52 .6 8-.1 14-.6 18-1.2 2-.3 3 .6 2 1.6-2 1.8-6 3.4-12 4.4-12 2-34 3-52 3-6 0-11-.4-12-1.8C2.8 8.4 2.2 7.6 2 6.4z' fill='url(%23g)' filter='url(%23b)'/%3E%3C/svg%3E");

  /* Shared by the light palettes; Nuit redefines them. */
  --jade: #22765a;       /* right, done */
  --jade-2: #e1f1e9;
  --danger: #c4372a;     /* wrong, errors */
  --danger-2: #9c2a1f;
  --danger-bg: #fbe8e4;
  --warm-bg: #f8ecd3;    /* badges, streaks */
  --warm-ink: #74500f;
  --star: #d49a1c;
  --block-blue: #dfe7f1;
  --block-blue-ink: #2d4b70;
  --block-orange: #f5e3d0;
  --block-orange-ink: #87501c;
  --block-purple: #e9e1f1;
  --block-purple-ink: #5b3b7d;
  --block-teal: #d9ecec;
  --block-teal-ink: #1f5f61;
  /* Tone colors: one hue per tone, the same everywhere tones are shown. */
  --tone-1: #c23b30;
  --tone-2: #b86e00;
  --tone-3: #2f7d3e;
  --tone-4: #2d62a8;
  --tone-5: #857f76;
  --shadow: 0 1px 3px rgb(30 25 20 / 12%), 0 8px 24px rgb(30 25 20 / 12%);
  /* A card rests on the paper: a border you see and a shadow under it
     (Jonathan, 2026-10-10: the faded hairline was invisible). */
  --shadow-soft: 0 1px 3px rgb(30 25 20 / 10%), 0 6px 18px rgb(30 25 20 / 9%);
  --edge: var(--line-strong);
  --scrim: color-mix(in srgb, var(--paper) 60%, transparent);
  --tint: color-mix(in srgb, var(--cinnabar) 6%, var(--card));
  --accent-bg: color-mix(in srgb, var(--cinnabar) 10%, var(--card));
  --playing-bg: color-mix(in srgb, var(--cinnabar) 12%, var(--card));
  --on-accent: #fff;
  --panel-head: color-mix(in srgb, var(--paper-2) 55%, var(--card));
}

/* Each palette names the same roles:
   paper, paper-2  the page and a quiet fill
   card            raised surfaces: cards, fields
   ink, ink-2, stone  text, strong to faint
   line, line-strong  hairlines, and the edges of fields and buttons
   cinnabar(-2)    the accent: links, focus, what is chosen or playing
   btn(-hover/-edge)  the main button
   heading         page titles
   violet(-bg)     a selected card or row, pinyin of the words
   gold            the edge of the cards to play with
   carbon, on-carbon  a solid band (the page head, a section of the home
                   page) and its text: a warm charcoal, lighter than the ink */
:root, [data-palette="encre"] {
  --paper: #f7f5f1;
  --paper-2: #efebe4;
  --card: #fff;
  --ink: #231d1a;
  --ink-2: #4f4640;
  --stone: #766c63;
  --line: #d3d0cd;
  --line-strong: #b0aaa5;
  --cinnabar: #c23b28;
  --cinnabar-2: #9e2d1d;
  --btn: #c23b28;
  --btn-hover: #ad3321;
  --heading: #231d1a;
  --violet: #2f5d8f;
  --violet-bg: #e8eef6;
  --gold: #d9b36c;
  --carbon: #3a3532;
  --on-carbon: #f7f5f1;
}
[data-palette="jade"] {
  --paper: #f1f5f2;
  --paper-2: #e5ede7;
  --card: #fff;
  --ink: #15302a;
  --ink-2: #3c534c;
  --stone: #66786f;
  --line: #ced4d1;
  --line-strong: #a6b1ab;
  --cinnabar: #16755f;
  --cinnabar-2: #0f5645;
  --btn: #16755f;
  --btn-hover: #12664f;
  --heading: #15302a;
  --violet: #6a4bb8;
  --violet-bg: #efebf9;
  --gold: #d6a94f;
  --jade: #2f7d3e;
  --jade-2: #e2f1e4;
  --carbon: #34403c;
  --on-carbon: #f1f5f2;
}
[data-palette="porcelaine"] {
  --paper: #f3f5f9;
  --paper-2: #e8edf5;
  --card: #fff;
  --ink: #172240;
  --ink-2: #3d4867;
  --stone: #67708a;
  --line: #ced1da;
  --line-strong: #a7acbb;
  --cinnabar: #2753ad;
  --cinnabar-2: #1c3f86;
  --btn: #2753ad;
  --btn-hover: #214a9c;
  --heading: #172240;
  --violet: #8a3fb0;
  --violet-bg: #f3eaf8;
  --gold: #e0b04f;
  --carbon: #363c4b;
  --on-carbon: #f3f5f9;
}
/* The first look of the site: bright, for children. */
[data-palette="joyeux"] {
  --paper: #fff8ec;
  --paper-2: #fff0d9;
  --card: #fff;
  --ink: #2b2140;
  --ink-2: #4a3f63;
  --stone: #6e6484;
  --line: #d1cdd8;
  --line-strong: #aba5b8;
  --cinnabar: #e0503a;
  --cinnabar-2: #c23c27;
  --btn: #ff7a45;
  --btn-hover: #ff8c5c;
  --heading: #6a3fd1;
  --violet: #6a3fd1;
  --violet-bg: #f4efff;
  --gold: #ffd27a;
  --warm-bg: #fff0c2;
  --warm-ink: #8a5a00;
  --star: #f0a500;
  --carbon: #3e3650;
  --on-carbon: #fff8ec;
}
[data-palette="nuit"] {
  color-scheme: dark;
  --paper: #16181d;
  --paper-2: #1d2027;
  --card: #22252d;
  --ink: #ece8e1;
  --ink-2: #c4bfb6;
  --stone: #9a948b;
  --line: #48494b;
  --line-strong: #686564;
  --cinnabar: #f08a6c;
  --cinnabar-2: #f6a58c;
  --btn: #f08a6c;
  --btn-hover: #f5a086;
  --on-accent: #1d1410;
  --heading: #f3efe8;
  --violet: #a597ff;
  --violet-bg: #2b2745;
  --gold: #b8944a;
  --jade: #5cc491;
  --jade-2: #1c3a2d;
  --danger: #ff7b6b;
  --danger-2: #ff9d90;
  --danger-bg: #3d2220;
  --warm-bg: #3a301c;
  --warm-ink: #f0cf87;
  --star: #e8b84a;
  --block-blue: #22314a;
  --block-blue-ink: #a9c3ea;
  --block-orange: #3f2e1f;
  --block-orange-ink: #efbf8e;
  --block-purple: #33294a;
  --block-purple-ink: #cdb6f0;
  --block-teal: #1d3a3b;
  --block-teal-ink: #93d4d2;
  --tone-1: #ff8277;
  --tone-2: #f0a93c;
  --tone-3: #6cc47d;
  --tone-4: #7fa8ec;
  --tone-5: #a8a29a;
  --shadow: 0 1px 2px rgb(0 0 0 / 30%), 0 8px 24px rgb(0 0 0 / 35%);
  --shadow-soft: 0 1px 2px rgb(0 0 0 / 25%), 0 6px 20px rgb(0 0 0 / 22%);
  --edge: var(--line-strong);
  --carbon: #30333b;
  --on-carbon: #ece8e1;
}


*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-md);
  line-height: 1.55;
  overflow-x: clip; /* a full-bleed band is 100vw wide, scrollbar included */
}
/* Text is aligned left and right everywhere (Jonathan, 2026-10-04): the
   body justifies, words are cut with a hyphen so a short phone line does
   not open wide gaps. Titles, fields and lines of Chinese stay aligned
   left; what centers itself (a tile, a button) keeps its center. */
body { text-align: justify; }
:where(p, li, dd, blockquote, figcaption) { hyphens: auto; -webkit-hyphens: auto; }
/* A link or a button is never cut with a hyphen (« Cré-dits »). */
:where(a, button, .btn) { hyphens: manual; -webkit-hyphens: manual; }
:where(h1, h2, h3, input, textarea, select, .line .text, .line .fr, .prompt-words, .site-nav, .brand-eyebrow, .eyebrow, .pillar-kicker, .hero-card-title, .class-card-next) { text-align: left; }
/* Chinese inside a justified French line is one block: the justification
   never spreads its characters apart. */
:where(p, li, dd) :where(.hanzi-inline, .hanzi, span[lang="zh"]) { display: inline-block; text-indent: 0; }
/* The steps of a lesson, for a student (2026-11-14): ragged right, never
   hyphenated, as « Je construis » already was: a phone's justified line
   opened wide gaps and cut « recon-naître ». The rest of the site stays
   justified (Jonathan). A word of the exam and of the role play
   (ruby-words) never breaks inside. */
.learn { text-align: left; }
.learn :where(p, li, dd, blockquote, figcaption, td, th) { hyphens: manual; -webkit-hyphens: manual; }
.rw { display: inline-block; text-indent: 0; }
/* The exam's pinyin, over each character: the words apart (Wáng Míng,
   not WángMíng); never spread inside (Chrome spreads a centred pinyin
   narrower than its character letter by letter: « A¯n » for Ān). */
.exam .rw + .rw { margin-left: .15em; }
.exam :is(ruby, rt) { ruby-align: start; }
.exam rt { padding: 0 .1em; }
h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 .6em;
  text-wrap: balance;
}
/* A page's title in the home page's look (docs/design.md): light type,
   its key word in bold (<strong>). */
h1 { font-size: clamp(2rem, 5.5vw, 2.75rem); font-weight: 300; line-height: 1.12; color: var(--heading); letter-spacing: -.01em; }
h1 strong { font-weight: 800; }
h2 { font-size: 1.3rem; margin-top: 2.25rem; }
p { margin: 0 0 1rem; }
a { color: var(--cinnabar); text-decoration: none; background-image: linear-gradient(currentColor, currentColor); background-size: 0% 1px; background-repeat: no-repeat; background-position: 0 100%; transition: background-size var(--dur) var(--ease); }
a:hover, a:focus-visible { background-size: 100% 1px; }
:where(a, button, summary, [tabindex]):focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
code { font-size: .9em; }
.hanzi { font-family: var(--hanzi); font-size: 1.75rem; line-height: 1.4; }

/* Header: the brand, then on the right what a student needs on every page,
   and nothing more: the audio speed and a menu behind the avatar. */
.site-header {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem 1rem;
  padding: .7rem clamp(1rem, 4vw, 3rem);
  border-bottom: 1px solid var(--edge);
  background: var(--card);
}
.brand { display: inline-flex; align-items: center; gap: .6rem; color: var(--ink); background: none; flex: none; }
.brand-name { font-family: var(--serif); font-size: 1.1rem; font-weight: 700; letter-spacing: .01em; }
.seal {
  display: inline-grid; place-items: center;
  width: 2rem; height: 2rem;
  font-family: var(--serif); font-size: 1.1rem; font-weight: 600;
  color: #fff; background: var(--cinnabar);
  border-radius: 15%;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 25%);
  transition: transform var(--dur) var(--ease);
}
.brand:hover .seal { transform: rotate(-4deg); }
.site-nav { display: flex; align-items: center; justify-content: flex-end; gap: .35rem; font-size: .95rem; min-width: 0; }
.site-nav > a, .site-nav > form button.link {
  display: inline-flex; align-items: center; min-height: 44px; padding: .3rem .8rem; border-radius: 999px;
  color: var(--ink-2); background: none; white-space: nowrap;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.site-nav > a:hover { background: var(--paper-2); color: var(--ink); }

/* The menu behind the avatar: my lessons, my account, log out. */
.user-menu { position: relative; flex: none; }
.user-menu > summary {
  display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; padding: .2rem .7rem .2rem .2rem;
  border-radius: 999px; cursor: pointer; list-style: none; color: var(--ink-2);
  transition: background-color var(--dur) var(--ease);
}
.user-menu > summary::-webkit-details-marker { display: none; }
.user-menu > summary:hover, .user-menu[open] > summary { background: var(--paper-2); color: var(--ink); }
.avatar {
  display: inline-grid; place-items: center; flex: none;
  width: 2.25rem; height: 2.25rem; border-radius: 50%;
  background: var(--accent-bg); color: var(--cinnabar-2);
  font-weight: 700; font-size: 1rem; line-height: 1;
}
.user-menu-name { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-menu-panel {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 30;
  display: grid; min-width: 13rem; padding: .4rem;
  border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow);
  animation: rise var(--dur) var(--ease) both;
}
.user-menu-panel a, .user-menu-panel button.link {
  display: flex; align-items: center; width: 100%; min-height: 44px; padding: .4rem .9rem; border-radius: var(--radius-sm);
  color: var(--ink); background: none; font-size: 1rem; text-align: left;
}
.user-menu-panel a:hover, .user-menu-panel button.link:hover { background: var(--paper-2); color: var(--ink); }
/* « Mes leçons » is always in the bar; only the teacher's phone, where the
   bar has no room for it, finds it in the menu. */
.user-menu-panel .menu-home { display: none; }
/* « Déconnexion » answers at once, even when the network is slow
   (app.js marks it .is-sending). */
.user-menu-panel button.is-sending { opacity: .6; cursor: progress; }
.user-menu-panel button.is-sending::after { content: "…"; }
.user-menu-panel form { margin: 0; padding-top: .3rem; margin-top: .3rem; border-top: 1px solid var(--line); }
.user-menu-panel button.link { color: var(--ink-2); }

/* Phones: one row. A signed-in viewer gets the seal alone, so « Mes
   leçons » keeps its place; the teacher's « Mes leçons » goes into the menu,
   next to « Admin ». */
@media (max-width: 48rem) {
  .user-menu-panel .menu-home { display: flex; }
  .site-header { padding-inline: 1rem; }
  .site-header:has(.user-menu) .brand-name, .site-nav > .nav-home-admin, .speed-label, .user-menu-name { display: none; }
  .site-nav { gap: .2rem; }
  .site-nav > a { padding-inline: .6rem; }
  .user-menu > summary { padding: .2rem; }
  .speed-switch label span { min-width: 2.3rem; padding: 0 .35rem; }
}
/* A visitor's three links (Cours, Tarifs, Connexion) and the name fit
   beside each other on a phone. */
@media (max-width: 26rem) {
  .site-header:not(:has(.user-menu)) .brand { gap: .45rem; }
  .site-header:not(:has(.user-menu)) .brand-name { font-size: 1rem; }
  .site-header:not(:has(.user-menu)) .site-nav > a { padding-inline: .45rem; font-size: .9rem; }
}
@media (max-width: 23rem) {
  .brand-name { display: none; }
}

/* Global playback speed, a three-way switch in the header. */
.speed-switch { display: inline-flex; flex: none; align-items: center; gap: .1rem; padding: .2rem; border-radius: 999px; background: var(--paper-2); font-size: .85rem; line-height: 1; }
.speed-label { padding: 0 .35rem 0 .5rem; color: var(--stone); }
.speed-switch label { position: relative; cursor: pointer; }
.speed-switch input { position: absolute; opacity: 0; pointer-events: none; }
.speed-switch label span { display: inline-grid; place-items: center; min-width: 2.5rem; min-height: 2.25rem; padding: 0 .45rem; border-radius: 999px; text-align: center; color: var(--ink-2); transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.speed-switch label:hover span { color: var(--ink); }
.speed-switch input:checked + span { background: var(--card); color: var(--cinnabar-2); font-weight: 700; box-shadow: 0 1px 3px rgb(30 25 20 / 12%); }
.speed-switch input:focus-visible + span { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
.inline { display: inline; margin: 0; }
button.link {
  background: none; border: 0; padding: 0; font: inherit; color: var(--ink-2); cursor: pointer;
}
button.link:hover { color: var(--cinnabar); }

/* Layout: one centered column, its width set by the page. */
.page {
  flex: 1;
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: var(--page-top) clamp(1rem, 4vw, 3rem) clamp(2.5rem, 6vw, 4.5rem);
  animation: rise var(--dur) var(--ease) both;
}

/* The page head: a white band right under the site header, edge to edge,
   holding the way back, the title and where you are. The content below
   sits on the paper, in panels. */
.page-head {
  display: grid; gap: var(--sp-2);
  margin: calc(-1 * var(--page-top)) calc(50% - 50vw) var(--sp-5);
  padding: var(--sp-4) calc(50vw - 50%) var(--sp-4);
  background: var(--carbon); color: var(--on-carbon);
}
.page-head > * { margin: 0; }
.page-head h1 { font-size: var(--fs-xl); line-height: 1.2; }
.page-head .page-sub { font-size: var(--fs-sm); }
.page-head .child-progress { margin: 0; }
.page-head .back { font-size: var(--fs-sm); font-weight: 600; }
@media (min-width: 48rem) {
  .page-head { padding-block: var(--sp-5); }
  .page-head h1 { font-size: 2rem; }
}

/* A panel: one unit of content (a grammar point, a conversation, a
   lesson's steps). A tinted head with its number and title, a body, and a
   foot for its action. Panels stack with a clear gap. */
.panel {
  margin: 0 0 var(--sp-5);
  border: 1px solid var(--edge); border-radius: var(--radius);
  background: var(--card); box-shadow: var(--shadow-soft);
  overflow: clip;
}
.panel-head {
  display: grid; gap: var(--sp-1);
  padding: var(--sp-3) var(--sp-4);
  background: var(--panel-head);
  border-bottom: 1px solid var(--edge);
}
.panel-head > *, .panel-head h2, .panel-head p { margin: 0; }
.panel-kicker { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--cinnabar-2); }
.panel-title { font-size: var(--fs-lg); font-weight: 700; line-height: 1.3; }
.panel-sub { font-size: var(--fs-sm); color: var(--ink-2); }
/* A head with an action on its right (« Tout écouter »), under it on a phone. */
.panel-head-split { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2) var(--sp-4); }
.panel-head-split > * { margin: 0; }
.panel-body { padding: var(--sp-4); }
.panel-body > :first-child { margin-top: 0; }
.panel-body > :last-child { margin-bottom: 0; }
.panel-foot { padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--edge); background: var(--panel-head); }
.panel-tools {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--edge); font-size: var(--fs-sm); font-weight: 600;
}
.panel-tools > *, .panel-tools p { margin: 0; }
.panel-hint { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); color: var(--stone); }
/* Rows inside a panel: the first one starts at the panel's padding, the
   last one has no rule under it. Lines that are cards keep theirs. */
.panel-body .line:not(.dialogue-lines > .line):first-child { padding-top: var(--sp-2); }
.panel-body .line:not(.dialogue-lines > .line):last-child { border-bottom: 0; padding-bottom: 0; }
/* A panel of one piece (a form, a short card): padded, no head. */
.panel-pad { padding: var(--sp-4); }
@media (min-width: 48rem) { .panel-pad { padding: var(--sp-4) var(--sp-5); } }
.label { margin: var(--sp-4) 0 var(--sp-1); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
@media (min-width: 48rem) {
  .panel-head { padding: var(--sp-4) var(--sp-5); }
  .panel-body { padding: var(--sp-5); }
  .panel-foot { padding: var(--sp-3) var(--sp-5); }
  .panel-tools { padding-inline: var(--sp-5); }
}
.page > section { margin-inline: auto; }
.narrow { max-width: 28rem; }
.wide { max-width: 60rem; }
/* The teacher's pages hold wide tables: they take the whole screen. */
.page:has(> .admin.wide) { max-width: 80rem; }
.admin.wide { max-width: none; }
.site-footer { text-align: center; padding: 2rem 1rem; border-top: 1px solid var(--line); color: var(--stone); font-size: .95rem; }
.site-footer p { margin: 0; }
.credits { max-width: 40rem; }
.credit-list { padding-left: 1.2rem; }
.credit-list li { margin-bottom: .5rem; }

/* Hero */
.hero { max-width: 40rem; padding-top: clamp(1rem, 6vw, 4rem); }
.hero-small { padding-top: 0; }
.page > .hero-landing { margin-inline: 0; }
.eyebrow a { color: inherit; }
.eyebrow :where(.hanzi, [lang="zh"]) { font-size: 1.3em; line-height: 1; letter-spacing: .1em; }
.lede { color: var(--ink-2); font-size: 1.15rem; max-width: 34rem; margin-bottom: 1.75rem; }

/* Landing: what the site offers, to the visitors. Mobile first; the
   grids open up into columns as the screen widens. */

/* The home page's hero in the brand's look (docs/marque.md): the label in
   capitals with its vermillon line, the slogan in light type with one
   bold word, the one-liner, one call to action; Fei at once, a chip on a
   phone, her portrait beside the slogan on a wide screen. */
.page > .hero-landing { display: grid; gap: 1.5rem; max-width: none; }
.hero-main { max-width: 40rem; }
.hero-main .lede { margin-bottom: 0; }
/* The one-liner reads left, no hyphen: big type justified opens gaps. */
.hero-landing .lede { text-align: left; hyphens: manual; -webkit-hyphens: manual; }
/* The label says what the site is: on one line, readable. */
.hero-landing .brand-eyebrow { font-size: .9rem; letter-spacing: .08em; }
/* The title is a sentence (who teaches), not the slogan: smaller. */
.hero-landing .brand-title { font-size: clamp(2.1rem, 8vw, 3.4rem); line-height: 1.1; }
.hero-act { display: grid; justify-items: start; gap: .6rem; }
.hero-act p { margin: 0; }
.hero-hello { font-weight: 700; }
.hero-act-note { font-size: .95rem; text-align: left; }
.hero-act-note a { color: inherit; font-weight: 700; }
.hero-act-note [lang="zh"] { font-family: var(--hanzi); font-weight: 400; }
.hero-cta { flex-wrap: wrap; justify-content: flex-start; text-align: left; }
.fei-chip { display: flex; align-items: center; gap: .75rem; justify-self: start; padding: .5rem 1.1rem .5rem .5rem; border-radius: 999px; background: var(--card); color: var(--ink-2); text-decoration: none; text-align: left; box-shadow: 0 10px 24px -12px rgb(0 0 0 / 45%); }
.fei-chip:hover { color: var(--ink); }
.fei-chip img { flex: none; width: 3rem; height: 3rem; border-radius: 50%; object-fit: cover; background: var(--accent-bg); }
.fei-chip span { font-size: .95rem; line-height: 1.3; }
.fei-chip strong { display: block; font-size: 1.05rem; color: var(--heading); }
.fei-chip span { text-wrap: balance; }
@media (min-width: 60rem) {
  .page > .hero-landing { grid-template-columns: minmax(0, 1fr) 16rem; grid-template-areas: "main fei" "act fei"; align-items: center; gap: 0 4rem; }
  .hero-landing .hero-main { grid-area: main; }
  .hero-landing .fei-chip { grid-area: fei; }
  .hero-landing .hero-act { grid-area: act; align-self: start; margin-top: 1.75rem; }
  .hero-landing .brand-title { font-size: clamp(3rem, 4.6vw, 3.9rem); }
  /* The chip turns into her portrait. */
  .fei-chip { flex-direction: column; justify-self: center; gap: 1rem; padding: 1.25rem 1.25rem 1.5rem; border-radius: var(--radius-lg); text-align: center; }
  .fei-chip img { width: 11rem; height: 11rem; border: 4px solid var(--on-accent); }
  .fei-chip span { font-size: 1rem; }
  .fei-chip strong { font-size: 1.3rem; }
}
/* The trial button fixed at the bottom of a phone's screen (Fei,
   2026-11-09); app.js adds .is-on once the hero is passed, while no other
   trial button shows. None on a wide screen. */
.sticky-cta { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex; padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom)); background: var(--card); box-shadow: 0 -6px 18px -8px rgb(0 0 0 / 35%); transform: translateY(110%); visibility: hidden; transition: transform 200ms ease-out, visibility 200ms; }
.sticky-cta .btn { flex: 1; justify-content: center; }
.sticky-cta.is-on { transform: none; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .sticky-cta { transition: none; } }
@media (min-width: 60rem) { .sticky-cta { display: none; } }
@media (max-width: 59.99rem) { body:has(.sticky-cta) { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); } }
/* The label over a title, on every page: capitals, its vermillon line. */
.brand-eyebrow, .eyebrow { margin: 0 0 1.1rem; font-family: "Nunito", system-ui, sans-serif; font-size: .78rem; font-weight: 700; letter-spacing: .32em; text-transform: uppercase; color: var(--cinnabar-2); }
/* The touch of calligraphy, for titles only: a short vermillon brush
   stroke that paints itself in, under the label over a title (Jonathan,
   2026-10-10: never under a character). */
.brand-eyebrow::after, .eyebrow::after {
  content: ""; display: block; width: 64px; height: 11px; margin-top: .55rem;
  background: var(--cinnabar);
  -webkit-mask: var(--brush) no-repeat center / 100% 100%; mask: var(--brush) no-repeat center / 100% 100%;
  clip-path: inset(0 100% 0 0); animation: brush-paint 650ms cubic-bezier(.45,.05,.25,1) 120ms forwards;
}
@keyframes brush-paint { to { clip-path: inset(0 0 0 0); } }
@media (prefers-reduced-motion: reduce) {
  .brand-eyebrow::after, .eyebrow::after { animation: none; clip-path: none; }
}
.brand-title { font-size: clamp(2.6rem, 8vw, 4.6rem); font-weight: 300; letter-spacing: -.01em; line-height: 1.05; margin-bottom: 1.1rem; }
.brand-title strong, .brand-h2 strong { font-weight: 800; }
.brand-h2 { font-weight: 300; }
.hero-landing .lede { font-size: clamp(1.15rem, 2.4vw, 1.35rem); }
.btn-cta { min-height: 56px; padding-inline: 1.6rem; }
.cta-note { margin: 0; font-size: .95rem; color: var(--ink-2); }

/* The trial: hear 你好, say it, trace 好. One column on a phone, two
   cards side by side on a wide screen. */
/* The home page in full-width bands, white and paper in turn: each
   section is one band, its title in the same light type. */
.page > .landing-band { --band-pad: var(--sp-6); max-width: none; margin-inline: calc(50% - 50vw); padding: var(--band-pad) calc(50vw - 50%); }
@media (min-width: 48rem) { .page > .landing-band { --band-pad: var(--sp-7); } }
/* Bands in solid color, no shades: the red of the brand, then carbon.
   The text turns white; on the red, the main button turns white. */
.band-red { background: var(--cinnabar); color: var(--on-accent); }
.band-carbon { background: var(--carbon); color: var(--on-carbon); }
.band-red :is(.brand-eyebrow, .brand-title, .lede, .cta-note),
.band-carbon .brand-h2 { color: inherit; }
.band-red .brand-eyebrow::after { background: currentColor; }
.band-red .btn-primary { background: var(--on-accent); color: var(--cinnabar-2); box-shadow: 0 8px 20px -8px rgb(0 0 0 / 40%); }
.band-red .btn-primary:hover { background: var(--paper); color: var(--cinnabar-2); }
.band-red .btn-ghost { background: transparent; border-color: color-mix(in srgb, var(--on-accent) 70%, transparent); color: var(--on-accent); }
.band-red .btn-ghost:hover { background: color-mix(in srgb, var(--on-accent) 12%, transparent); color: var(--on-accent); }
.page:has(> .landing-band) { padding-bottom: 0; }
.page > .landing-band:first-child { margin-top: calc(-1 * var(--page-top)); border-top: 0; }
.landing-band .brand-h2 { margin: 0 0 var(--sp-3); font-size: clamp(1.6rem, 4vw, 2.1rem); }

.trial-lede { color: var(--ink-2); }
/* The trial, one step at a time: its count and dots over one card, the
   way on at the card's foot. The same width on a phone and a computer. */
.trial-steps { display: grid; gap: var(--sp-3); max-width: 40rem; }
.trial-steps [data-trial-step][hidden] { display: none; }
.trial-progress { display: flex; align-items: center; gap: var(--sp-3); }
.trial-dots { display: inline-flex; gap: .4rem; }
.trial-dots span { width: .6rem; height: .6rem; border-radius: 50%; background: var(--line-strong); }
.trial-dots span.is-on { background: var(--cinnabar); }
.trial-nav { display: flex; justify-content: space-between; gap: var(--sp-3); width: 100%; margin-top: var(--sp-2); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.trial-nav [data-trial-go="1"] { margin-left: auto; }
.trial-nav .btn[hidden] { display: none; }
.trial-card { display: grid; justify-items: start; gap: .85rem; padding: 1.25rem 1.25rem 1.5rem; border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); }
.trial-card > p { margin: 0; }
.trial-step { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; font-weight: 700; color: var(--heading); }
.trial-num { display: inline-grid; place-items: center; width: 1.7rem; height: 1.7rem; border-radius: 50%; background: var(--cinnabar); color: var(--on-accent); font-size: .9rem; line-height: 1; }
.trial-step .hanzi-inline { font-size: 1.35em; line-height: 1; }
.trial-word { display: grid; justify-items: start; gap: .1rem; }
.trial-hanzi { font-family: var(--hanzi); font-size: clamp(4rem, 14vw, 5.5rem); line-height: 1.15; color: var(--ink); }
.trial-py { font-size: 1.35rem; }
.trial-step .trial-py, .trial-step .trial-fr { font-size: 1rem; font-weight: 600; }
.trial-fr { color: var(--ink-2); }
.trial-buttons { display: flex; flex-wrap: wrap; gap: .5rem; }
.play.trial-slow { padding: .5rem 1.25rem; border-radius: 999px; font-size: var(--fs-btn); font-weight: 700; }
.trial-traced[hidden] { display: none; }
.trial-text { max-width: 26rem; }
.trial-stroke { width: min(16rem, 100%); }
/* Before Hanzi Writer draws, the character's shape waits in the box. */
/* Until Hanzi Writer has the strokes, the character waits in grey in the
   box, which is never empty; its drawing then takes the place. */
.trial-stroke { position: relative; }
.trial-stroke svg { display: block; }
.trial-stroke:not(.has-strokes)::before { content: attr(data-char); position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--hanzi); font-size: min(11rem, 60vw); line-height: 1; color: var(--line); pointer-events: none; }
.trial-traced { display: grid; gap: .3rem; max-width: 26rem; padding: .9rem 1rem; border-radius: var(--radius); background: var(--jade-2); color: var(--ink); animation: rise var(--dur) var(--ease) both; }
.trial-traced p { margin: 0; }
.trial-formula { font-family: var(--hanzi); font-size: 1.9rem; line-height: 1.2; color: var(--ink-2); }
.trial-formula span { color: var(--ink); }
.trial-next { display: grid; justify-items: start; gap: .75rem; margin-top: 1.5rem; }
.trial-next p { margin: 0; color: var(--ink-2); }
.landing-section { margin: 0; }
/* The home page reads left, no word cut (2026-11-05): big type
   justified opens gaps on a phone. The rest of the site keeps justified
   text (2026-10-04). */
.hero-landing :where(p, li, dd), .landing-section :where(p, li, dd) { text-align: left; hyphens: manual; -webkit-hyphens: manual; }
.price-via { margin: var(--sp-3) 0 var(--sp-4); color: var(--ink-2); }
.prices > .price-site { margin: 0 0 var(--sp-4); }
.landing-section > h2 { margin: 0 0 var(--sp-4); }
.teacher-photo { flex: none; width: 5rem; height: 5rem; border-radius: 50%; object-fit: cover; background: var(--accent-bg); }
.price-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: .75rem; margin-bottom: .75rem; }
.price-card { display: flex; flex-direction: column; align-items: flex-start; gap: .6rem; padding: var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.price-card h3 { margin: 0; font-size: 1.15rem; }
.price-card p { margin: 0; }
.price-premium { border-color: var(--gold); }
/* The two formulas: what they are in small capitals, their name big. */
.price-kind { font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--cinnabar-2); }
.price-card h3 { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 0; font-size: 1.6rem; font-weight: 300; color: var(--heading); }
.price-card .btn-cta { margin-top: auto; }
.price-mine { margin-top: auto; font-weight: 700; color: var(--jade); }
/* The site, beside a formula of classes: in sight, not in small print. */
.price-site { padding: .5rem .75rem; border-left: 3px solid var(--gold); border-radius: var(--radius-sm, 6px); background: color-mix(in srgb, var(--gold) 10%, transparent); font-size: .95rem; text-align: left; }
/* The free account, under the formulas: a band of its own, never a fifth formula. */
.price-free { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 1.5rem; margin-top: .25rem; padding: var(--sp-5); border: 2px dashed var(--line-strong); border-radius: var(--radius); background: var(--card); }
.price-free p { margin: 0; max-width: 34rem; font-size: 1.05rem; text-align: left; }

/* The four pillars of docs/positionnement.md: Fei, then a screen of the
   site for each. One column on a phone, the text beside its screen on a
   wide one. */
.pillars { display: grid; gap: var(--sp-4); }
.pillar { display: grid; gap: var(--sp-4); align-items: start; padding: var(--sp-5); border: 1px solid color-mix(in srgb, var(--on-carbon) 18%, transparent); border-radius: var(--radius-lg); background: color-mix(in srgb, var(--on-carbon) 7%, transparent); }
.pillar-text { display: grid; gap: .5rem; text-align: left; }
.pillar-text p, .pillar-text h3 { margin: 0; }
.pillar-text h3 { font-size: 1.35rem; line-height: 1.25; color: inherit; }
.pillar-text p:not(.pillar-kicker) { hyphens: manual; -webkit-hyphens: manual; text-align: left; }
.pillar-text a { color: var(--gold); }
.pillar-kicker { font-size: .78rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--gold); }
/* Each screen in a small phone: a dark bezel, rounded, the top of the
   page only, so it reads as the site in a hand, not a pasted capture. */
.phone-frame { display: block; justify-self: center; width: 11.5rem; padding: .4rem; border-radius: 1.75rem; background: #161311; box-shadow: 0 0 0 1px color-mix(in srgb, var(--on-carbon) 22%, transparent), 0 18px 40px -18px rgb(0 0 0 / 70%); }
.pillar-shot { display: block; width: 100%; height: auto; aspect-ratio: 3 / 4; border-radius: 1.4rem; background: var(--paper); }
.pillar-figure { display: grid; justify-items: center; gap: .5rem; margin: 0; }
.pillar-figure figcaption { font-size: .85rem; opacity: .75; }
.pillar-fei { align-items: start; }
@media (min-width: 30rem) { .pillar-fei { grid-template-columns: auto 1fr; } }
.pillar-fei .teacher-photo { width: 5rem; height: 5rem; }
@media (min-width: 48rem) {
  .pillar:not(.pillar-fei) { grid-template-columns: 1fr 11.5rem; gap: var(--sp-6); }
  .pillar-fei .teacher-photo { width: 7rem; height: 7rem; }
}
@media (min-width: 60rem) {
  .pillars { grid-template-columns: 1fr 1fr; }
}
/* On a phone, the four pillars side by side, swiped (Fei, 2026-11-09):
   each card a little narrower than the screen, the next one peeking. */
@media (max-width: 47.99rem) {
  .pillars { display: flex; gap: var(--sp-3); overflow-x: auto; scroll-snap-type: x mandatory; margin-inline: calc(-1 * var(--sp-4)); padding: 0 var(--sp-4) var(--sp-2); scroll-padding-inline: var(--sp-4); scrollbar-width: none; overscroll-behavior-x: contain; }
  .pillars::-webkit-scrollbar { display: none; }
  .pillar { flex: 0 0 85%; scroll-snap-align: start; align-content: start; }
  .pillar-fei { grid-template-columns: 1fr; }
}
/* One button for both offers, under them. */
.price strong { font-family: var(--serif); font-size: 1.6rem; color: var(--heading); }
.landing-final { margin: 0 0 1rem; padding: 2rem 1.5rem; border-radius: var(--radius); background: var(--tint); }
.landing-final-title { margin: 0 0 1rem; font-family: var(--serif); font-size: 1.5rem; font-weight: 700; color: var(--heading); }
.landing-final-title .hanzi { font-size: 1.1em; line-height: 1; }

/* Blog: the list of articles and an article, a column easy to read. */
.blog, .blog-article { max-width: 42rem; margin-inline: auto; }
.blog-article .eyebrow a { color: inherit; background: none; }
.article-list { display: grid; gap: .75rem; list-style: none; margin: 0; padding: 0; }
.article-card a { display: block; padding: 1.1rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); color: var(--ink); transition: border-color var(--dur) var(--ease); box-shadow: var(--shadow-soft); }
.article-card a:hover { border-color: var(--stone); }
.article-card h2 { margin: 0 0 .4rem; font-size: 1.25rem; color: var(--heading); }
.article-card p { margin: 0 0 .4rem; color: var(--ink-2); }
.article-card-chars { display: block; font-size: 1.5rem; color: var(--cinnabar); letter-spacing: .1em; }
.prose { max-width: 38rem; line-height: 1.65; }
.prose p { margin: 0 0 var(--sp-3); }
.prose h2 { margin: 2rem 0 .75rem; }
.prose li { margin-bottom: .35rem; }
.prose img { display: block; max-width: 100%; height: auto; margin: 1.25rem 0; border: 1px solid var(--edge); border-radius: var(--radius); }
.hanzi-inline { font-family: var(--hanzi); font-size: 1.15em; }
.article-cta { margin: 2.5rem 0 1.5rem; }
.article-cta p:not(.landing-final-title) { max-width: 30rem; margin: 0 auto 1rem; color: var(--ink-2); }
.article-follow { font-weight: 600; }
.article-share { color: var(--ink-2); }
.article-more ul { padding-left: 1.2rem; }

/* Buttons: big and round for students; smaller on the admin pages. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 52px; padding: .7rem 1.4rem; border-radius: 999px;
  font: inherit; font-size: var(--fs-btn); font-weight: 700; line-height: 1.2; text-align: center; cursor: pointer;
  border: 1.5px solid transparent; background: none; color: var(--ink);
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--btn); color: var(--on-accent); box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--btn) 70%, transparent); }
.btn-primary:hover { background: var(--btn-hover); color: var(--on-accent); }
.btn-primary:active { box-shadow: 0 2px 6px -2px color-mix(in srgb, var(--btn) 60%, transparent); }
.btn-ghost { background: var(--card); border-color: var(--line-strong); color: var(--ink-2); }
.btn-ghost:hover { border-color: var(--stone); color: var(--ink); }
/* « J'écris les caractères »: « Teste-moi » stays light until the last
   character shows (Fei, 2026-11-12: one red button at a time). */
.chars-test:not(.is-ready) .test-start .btn-primary { background: var(--card); border-color: var(--line-strong); color: var(--ink-2); box-shadow: none; }
.btn-danger { color: var(--danger); }
.btn-danger:hover { background: var(--danger-bg); }
.btn-sm { min-height: 44px; padding: .45rem 1rem; font-size: var(--fs-btn-sm); }
/* The way on to the next step, at the foot of a step: always there, never
   louder than the block's own action. */
.btn-next { width: 100%; border-radius: var(--radius); background: var(--card); border-color: var(--edge); color: var(--cinnabar-2); box-shadow: var(--shadow-soft); }
.btn-next:hover { border-color: var(--cinnabar); color: var(--cinnabar-2); }
.btn-block { width: 100%; }
a.btn { background-image: none; }
.admin .btn { min-height: 44px; padding: .5rem 1.1rem; font-size: var(--fs-btn-sm); }
.admin .btn-sm { min-height: 36px; padding: .3rem .8rem; font-size: var(--fs-btn-sm); }
.admin .btn-primary { box-shadow: none; }

/* Admin home: one card per section, with what waits in it. */
.admin-hub { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: .75rem; list-style: none; margin: 1.25rem 0 0; padding: 0; }
.admin-tile { display: flex; flex-direction: column; gap: .4rem; height: 100%; padding: 1.1rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); color: var(--ink); transition: border-color var(--dur) var(--ease); box-shadow: var(--shadow-soft); }
.admin-tile:hover { border-color: var(--stone); }
.admin-tile h2 { margin: 0; font-size: 1.25rem; color: var(--heading); }
.admin-tile p { margin: 0; color: var(--ink-2); }
.admin-tile .admin-tile-state { margin-top: auto; padding-top: .3rem; font-size: .92rem; font-weight: 600; color: var(--stone); }
.admin-tile .admin-tile-todo { color: var(--cinnabar); }

/* The revision page (/admin/revision): a few sentences to read and
   characters to write, drawn at each visit, then every lesson's
   « Je retiens » and characters. An answer (.rev-answer), or a part
   unticked in « Afficher », is masked and shows while it has the focus
   (a tap): the student answers first. */
.rev-show { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); margin: 0 0 var(--sp-5); padding: 0; border: 0; }
.rev-show legend { float: left; margin-right: var(--sp-2); font-weight: 700; }
.rev-show label { display: inline-flex; align-items: center; gap: var(--sp-2); }
.rev-show .hint { flex-basis: 100%; margin: 0; }
.rev-memo { margin: 0; padding-left: 1.6rem; }
.rev-memo li { padding: var(--sp-2) 0; border-bottom: 1px solid var(--line); }
.rev-memo li:last-child { border-bottom: 0; }
.rev-memo p { margin: 0; }
.rev-memo .hanzi { font-size: 1.45rem; line-height: 1.35; }
.rev-memo .reading-text { line-height: 2.1; }
.rev-chars { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.rev-chars li { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); padding: var(--sp-2); border: 1px solid var(--line); border-radius: var(--radius-sm); text-align: center; }
.rev-chars .hanzi { font-size: 2.2rem; line-height: 1.2; }
.rev-chars .fr { font-size: var(--fs-sm); color: var(--ink-2); }
.rev-answer:not(:focus),
.revision:has(#rev-show-hanzi:not(:checked)) .rev-hanzi:not(:focus),
.revision:has(#rev-show-pinyin:not(:checked)) .rev-pinyin:not(:focus),
.revision:has(#rev-show-fr:not(:checked)) .rev-fr:not(:focus) {
  color: transparent; background: var(--panel-head); border-radius: var(--radius-sm); cursor: pointer; user-select: none;
}
.rev-chars .rev-answer { min-width: 3.2rem; }
/* « Je revois mes leçons »: one tap shows a word's pinyin and meaning together. */
.rev-answer:not(:focus) * { color: transparent; }
.rev-chars .rev-answer .fr { display: block; }

/* « Je lis » at the start of a class: one sentence at a time, big; a tap
   shows its pinyin and meaning, ✓ or ✗, a score at the end (Fei, 2026-11-12). */
.rev-quiz-list { margin: 0; padding: 0; list-style: none; }
.rev-quiz-again { margin: 0 0 var(--sp-2); text-align: center; }
.rev-quiz-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-3); width: 100%; min-height: 15rem; padding: var(--sp-5) var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink); font: inherit; text-align: center; cursor: pointer; }
.rev-quiz-card:hover { border-color: var(--stone); }
.rev-quiz-card .reading-text { font-family: var(--font-zh, inherit); font-size: clamp(2rem, 9vw, 3.4rem); line-height: 1.9; }
.rev-quiz-card .reading-text rt { font-size: .38em; }
.rev-quiz-answer { display: none; flex-direction: column; align-items: center; gap: var(--sp-1); animation: rev-quiz-in .35s var(--ease, ease) both; }
.rev-quiz-card[aria-expanded="true"] .rev-quiz-answer { display: flex; }
.rev-quiz-card[aria-expanded="true"] .rev-quiz-tap { display: none; }
.rev-quiz-answer .pinyin { font-size: 1.5rem; }
.rev-quiz-answer .fr { font-size: 1.2rem; color: var(--ink-2); }
.rev-quiz-tap { margin: 0; }
@keyframes rev-quiz-in { from { opacity: 0; transform: translateY(.4rem); } to { opacity: 1; transform: none; } }
.rev-quiz-marks { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); margin: var(--sp-4) 0 0; }
.rev-quiz-marks .btn { width: 100%; font-size: 1.1rem; padding-block: .8rem; }
.rev-quiz-count { margin: var(--sp-3) 0 0; text-align: center; color: var(--stone); }
.rev-quiz-end { padding: var(--sp-5) var(--sp-3); text-align: center; animation: rev-quiz-in .4s var(--ease, ease) both; }
.rev-quiz-score { margin: 0; font-size: 2.6rem; font-weight: 800; }
.rev-quiz-cheer { margin: var(--sp-2) 0 var(--sp-4); font-size: 1.2rem; }
/* « Je lis »: a character tapped shows its tip: drawn big with its key
   in red, its pinyin in its tone's colour, its meaning, ancient form and
   how it was formed (Fei, 2026-11-12). */
.rev-ch { cursor: pointer; border-radius: var(--radius-sm); transition: background-color .2s; }
.rev-ch:hover, .rev-ch.is-on { background: color-mix(in srgb, var(--cinnabar) 12%, transparent); }
.rev-ch-tip:not([hidden]) { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); width: 100%; animation: rev-quiz-in .35s var(--ease) both; }
.rev-ch-head { display: flex; align-items: center; justify-content: center; gap: var(--sp-4); }
.rev-ch-glyph { font-size: 5rem; line-height: 1; }
.rev-ch-glyph svg { display: block; width: 1em; height: 1em; fill: currentColor; }
.rev-ch-glyph .is-key { fill: var(--cinnabar); }
.rev-ch-say { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); text-align: left; }
.rev-ch-say .pinyin { font-size: 1.8rem; font-weight: 700; }
.rev-ch-say .pinyin.tone-1 { color: var(--tone-1); }
.rev-ch-say .pinyin.tone-2 { color: var(--tone-2); }
.rev-ch-say .pinyin.tone-3 { color: var(--tone-3); }
.rev-ch-say .pinyin.tone-4 { color: var(--tone-4); }
.rev-ch-say .pinyin.tone-5 { color: var(--tone-5); }
.rev-ch-say .fr { font-size: 1.1rem; color: var(--ink-2); }
.rev-ch-tip .rev-card-tip { margin-top: 0; }
.rev-ch-tip .hanzi.rev-ch-glyph { font-size: 5rem; line-height: 1; }
.rev-chars > .rev-tip-row { grid-column: 1 / -1; display: block; text-align: left; }
.rev-tip-slot:not(:empty) { margin-top: var(--sp-3); padding: var(--sp-4) var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }

/* « J'écris »: the pinyin and meaning asked, the character shown at the
   1st tap, its key and tip at the 2nd. */
.rev-quiz-ask { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); }
.rev-quiz-ask .pinyin { font-size: 2rem; }
.rev-quiz-ask .fr { font-size: 1.25rem; color: var(--ink-2); }
.rev-quiz-char .rev-quiz-answer .hanzi { font-size: clamp(5rem, 22vw, 9rem); line-height: 1; }
.rev-quiz-char .rev-card-tip { display: none; animation: rev-quiz-in .35s var(--ease) both; }
.rev-quiz-char[data-tip] .rev-card-tip { display: flex; }
.rev-quiz-more { display: none; margin: 0; }
.rev-quiz-char[aria-expanded="true"][data-steps="2"]:not([data-tip]) .rev-quiz-more { display: block; }
@media (prefers-reduced-motion: reduce) { .rev-quiz-answer, .rev-quiz-end, .rev-quiz-char .rev-card-tip { animation: none; } }

/* « Révisions »: the characters one at a time, big and alone; a click
   shows the pinyin and meaning under it, without moving it (Fei, 2026-11-11). */
.rev-cards-list { margin: 0; padding: 0; list-style: none; }
.rev-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-3); width: 100%; min-height: 20rem; padding: var(--sp-5) var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink); font: inherit; cursor: pointer; }
.rev-card:hover { border-color: var(--stone); }
.rev-card .hanzi { font-size: clamp(6rem, 24vw, 12rem); line-height: 1; }
/* Drawn from its strokes, the character lights its key up as the answer
   shows: the 女 of 好 turns red. */
.rev-card > .hanzi svg { display: block; width: 1em; height: 1em; fill: currentColor; }
.rev-card > .hanzi path { transition: fill .6s var(--ease) .25s; }
.rev-card[aria-expanded="true"] > .hanzi .is-key { fill: var(--cinnabar); }
.rev-card-answer { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); visibility: hidden; }
.rev-card[aria-expanded="true"] .rev-card-answer { visibility: visible; }
.rev-card .pinyin { font-size: 2rem; }
.rev-card .fr { font-size: 1.25rem; color: var(--ink-2); }
/* The memory tip: the ancient drawing, how the character was formed, its key. */
.rev-card-tip { display: flex; align-items: center; gap: var(--sp-4); max-width: 40rem; margin-top: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-radius: var(--radius); background: var(--paper-2); text-align: left; }
.rev-card-old { flex: none; display: grid; justify-items: center; gap: var(--sp-1); }
.rev-card-old img { width: 96px; height: 96px; object-fit: contain; padding: 6px; border-radius: var(--radius-sm); background: #fbf8f2; }
.rev-card-script { font-size: var(--fs-xs); color: var(--stone); }
.rev-card-why { display: flex; flex-direction: column; gap: var(--sp-2); }
.rev-card-etym { font-size: 1.05rem; line-height: 1.45; color: var(--ink); }
.rev-card-key { font-size: 1.05rem; color: var(--ink-2); }
.rev-card-key .hanzi { font-size: 1.6rem; line-height: 1; color: var(--cinnabar); vertical-align: -.15em; }
/* The answer comes in step by step: the sound, the pinyin and the meaning,
   then the ancient drawing grows into the tip. */
@media (prefers-reduced-motion: no-preference) {
  .rev-card[aria-expanded="true"] .rev-card-answer > * { animation: rev-in .35s var(--ease) both; }
  .rev-card[aria-expanded="true"] .rev-card-answer > .fr { animation-delay: .12s; }
  .rev-card[aria-expanded="true"] .rev-card-answer > .rev-card-tip { animation-delay: .45s; }
  .rev-card[aria-expanded="true"] .rev-card-old img { animation: rev-old .6s .6s var(--ease) both; }
  .rev-card[aria-expanded="true"] .rev-card-key .hanzi { display: inline-block; animation: rev-key .5s 1s var(--ease) both; }
}
@keyframes rev-in { from { opacity: 0; transform: translateY(.6rem); } }
@keyframes rev-old { from { opacity: 0; transform: scale(.5) rotate(-8deg); } }
@keyframes rev-key { 50% { transform: scale(1.5); } }
.rev-cards-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin: var(--sp-3) 0 0; }
.rev-cards-count { font-weight: 700; color: var(--ink-2); }

/* Fields: one look for every text field, list and text area. */
:where(input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="hidden"], [type="submit"], [type="button"]), select, textarea) {
  font: inherit; font-size: 1rem; color: var(--ink);
  min-width: 0; min-height: 46px; padding: .55rem .8rem;
  background-color: var(--card); border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
:where(input, select, textarea)::placeholder { color: var(--stone); opacity: .8; }
:where(input, select, textarea):hover:not(:focus, [readonly], :disabled) { border-color: var(--stone); }
:where(input, select, textarea):focus { outline: none; border-color: var(--cinnabar); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cinnabar) 20%, transparent); }
:where(textarea) { min-height: 6rem; line-height: 1.55; resize: vertical; }
:where(select) {
  appearance: none; padding-right: 2.4rem; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--stone) 50%), linear-gradient(135deg, var(--stone) 50%, transparent 50%);
  background-position: calc(100% - 1.15rem) 55%, calc(100% - .8rem) 55%;
  background-size: .35rem .35rem; background-repeat: no-repeat;
}
:where(input[type="checkbox"], input[type="radio"]) { width: 1.15rem; height: 1.15rem; margin: 0; accent-color: var(--cinnabar); }
:where(input[type="file"]) { max-width: 100%; font: inherit; font-size: .88rem; color: var(--stone); }
input[type="file"]::file-selector-button {
  margin-right: .7rem; padding: .4rem .9rem; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--ink-2);
  background: var(--card); border: 1px solid var(--line-strong); border-radius: 999px;
}
input[type="file"]::file-selector-button:hover { border-color: var(--stone); color: var(--ink); }
.form-page h1 { margin-bottom: 1.25rem; }
.form-page .form { margin-top: .5rem; }
.form-page .form .btn { margin-top: .4rem; }
/* A card on the paper, the home page's (docs/design.md): a fine edge, a
   soft shadow, wide corners. */
.card { padding: 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); }
.card > :last-child, .card .form-row:last-child { margin-bottom: 0; }
.card > h2:first-child { margin-top: 0; }
/* A card that says what happened and its one way on (a link gone stale). */
.card-action { display: grid; justify-items: start; gap: .9rem; }
.card-action p { margin: 0; }
/* Signing in, signing up, a new password: the form is a card; the account
   is a stack of them. */
.form-page > .form { padding: 1.5rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); }
.form-page > .card + .card { margin-top: 1rem; }
.form-page > :is(.form, .card) + p { margin-top: 1rem; }
.form { display: grid; gap: 1.25rem; }
.form-row { grid-template-columns: 1fr 1fr auto; align-items: end; margin-bottom: 2rem; }
.admin .form-row .btn { min-height: 46px; }
@media (max-width: 40rem) { .form-row { grid-template-columns: 1fr; } }
.field { display: grid; gap: .4rem; }
.field label { font-size: .92rem; font-weight: 600; color: var(--ink-2); }
.hint, .small { font-size: var(--fs-sm); color: var(--stone); margin: 0; }
.muted { color: var(--stone); }
.field-error { font-size: var(--fs-sm); color: var(--danger); margin: 0; }
.form-error, .form-success {
  padding: .75rem 1rem; border-radius: var(--radius-sm); font-size: .95rem; margin-bottom: 1.2rem;
  animation: rise var(--dur) var(--ease) both;
}
.form-error { background: var(--danger-bg); color: var(--danger-2); }
.form-success { background: var(--jade-2); color: var(--jade); }
.form-links { display: grid; gap: .25rem; margin-top: 1.25rem; }
.meta { display: grid; grid-template-columns: auto 1fr; gap: .3rem 1.2rem; margin: 0 0 1rem; }
.meta dt { color: var(--stone); font-size: .9rem; }
.meta dd { margin: 0; }

/* Site colors, for the admin. */
.palettes { display: grid; gap: .6rem; margin-top: 1rem; }
.palette {
  display: flex; align-items: center; gap: 1rem; min-height: 60px; padding: .6rem 1rem; cursor: pointer; text-align: left;
  font: inherit; color: var(--ink); background: var(--card); border: 1px solid var(--line-strong); border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease);
}
.palette:hover { border-color: var(--stone); }
.palette.current { border-color: var(--cinnabar); box-shadow: 0 0 0 1px var(--cinnabar); }
.swatches { display: inline-flex; flex: none; }
.swatches span { width: 1.6rem; height: 1.6rem; border-radius: 50%; border: 2px solid var(--card); box-shadow: 0 0 0 1px var(--line-strong); }
.swatches span + span { margin-left: -.45rem; }
.palette-name { flex: 1; font-weight: 600; }
.palette-state { font-size: .85rem; color: var(--cinnabar); font-weight: 700; }

/* Table */
.table-wrap { overflow-x: auto; }
/* A teacher's table is a panel; inside a theme's panel it is not framed again. */
.admin .table-wrap { margin: 0 0 var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.admin .theme-group .table-wrap { margin: 0; border: 0; border-radius: 0; background: none; box-shadow: none; }
.admin .table-wrap :is(th, td):first-child { padding-left: var(--sp-4); }
.admin .table-wrap :is(th, td):last-child { padding-right: var(--sp-4); }
.admin .table-wrap tbody tr:last-child td { border-bottom: 0; }
.admin .table-wrap th { background: var(--panel-head); }
.admin .theme-group .table-wrap th { background: none; }
.admin .theme-group .table-wrap :is(th, td):first-child { padding-left: .75rem; }
.lessons { width: 100%; border-collapse: collapse; font-size: .95rem; }
.lessons th, .lessons td { text-align: left; padding: .7rem .75rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.lessons th { font-weight: 600; color: var(--stone); font-size: var(--fs-sm); }
.lessons tbody tr { animation: rise var(--dur) var(--ease) both; }
.lessons tbody tr:hover { background: color-mix(in srgb, var(--paper-2) 50%, transparent); }
.actions { white-space: nowrap; text-align: right; }
.actions > * { vertical-align: middle; }
.tag {
  display: inline-block; padding: .1rem .6rem; border-radius: 999px;
  font-size: var(--fs-note); background: var(--paper-2); color: var(--ink-2); border: 1px solid var(--line);
}
.tag-ok { background: var(--jade-2); color: var(--jade); border-color: transparent; }
.tag-warn { background: var(--danger-bg); color: var(--danger-2); border-color: transparent; }
.access-grant { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; margin-top: .4rem; }
.access-grant input { width: 11rem; padding: .25rem .5rem; font-size: .85rem; }
.access-grant input.prepaid-n { width: 4.5rem; }
.ledger { margin-top: .8rem; font-size: .85rem; }
.ledger > summary { cursor: pointer; color: var(--stone); }
.ledger table { border-collapse: collapse; margin-top: .3rem; }
.ledger th, .ledger td { padding: .15rem .5rem .15rem 0; border: 0; font-size: var(--fs-sm); }
.invite-link { display: inline-block; margin-top: .3rem; word-break: break-all; color: var(--ink-2); font-size: .8rem; }

/* Students: the list, then a student's fiche (classes, payments, account). */
.students-title { margin: 2.5rem 0 1rem; font-size: 1.25rem; }
.students-add { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 .75rem; }
.students-add .theme-new { margin: 0 0 1rem; }
.students-add .theme-new[open] { flex-basis: 100%; }
.students-add .hint { margin: 0 0 .8rem; }
.student-row[hidden] { display: none; }
.student-row .item-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .4rem; margin: 0; }
.student-row .item-title a { margin-right: .2rem; font-weight: 600; }
.student-row .hint { margin: .25rem 0 0; }
.student-row.student-child { padding-left: 1.5rem; }
.student-block { margin: 2.2rem 0 0; }
.student-block h2 { font-size: 1.15rem; margin: 0 0 .7rem; }
.student-class { margin: 0 0 .75rem; padding: 1rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.student-class p { margin: .3rem 0; }
.student-class .student-class-go { margin-top: .9rem; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); }
.pay-row { padding: .9rem 0; border-top: 1px solid var(--line); }
.pay-row h3 { margin: 0 0 .3rem; font-size: 1rem; }
.pay-row p { margin: .3rem 0; }
.access-pause { margin: .5rem 0 0; }
.rename { margin: -.4rem 0 1rem; font-size: .9rem; }
.rename > summary { cursor: pointer; color: var(--stone); }
.rename[open] > summary { margin-bottom: .5rem; }
.class-edit-top .class-form { max-width: 44rem; }
.dupe-list { display: grid; gap: .5rem; margin: 0; padding: 0; list-style: none; }
.dupe { padding: .6rem .8rem; border: 1px solid var(--edge); border-radius: var(--radius-sm); background: var(--card); box-shadow: var(--shadow-soft); }
.dupe p { margin: .15rem 0; }
.dupe-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .5rem; }
.student-account select { max-width: 100%; }
.pay-more { margin-top: .5rem; font-size: .9rem; }
.pay-more > summary { cursor: pointer; color: var(--stone); }
.student-account { margin-top: 2.5rem; }
.student-account h3 { margin: 1.2rem 0 .3rem; font-size: 1rem; }
.student-family { margin: 0 0 .5rem; padding-left: 1.2rem; }

/* ---- Lesson editor ----
   The editor follows the student's path: four numbered steps, each a seal
   that fills in when opened. Chinese text is the only large type; every
   control around it stays small and quiet. */

.lessons .hanzi { font-size: 1.1rem; margin-left: .35rem; }
.theme-group { margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
@media (min-width: 48rem) { .theme-group { padding: var(--sp-5); } }
.theme-group > h2:first-child { margin-top: 0; }
.theme-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .5rem 1rem; }
.theme-head h2 { margin: 0; }
/* A chapter folds to its title: the whole head is the button, a chevron at its end. */
.theme-fold > summary { list-style: none; cursor: pointer; flex-wrap: nowrap; align-items: center; }
.theme-fold > summary::-webkit-details-marker { display: none; }
.theme-fold > summary h2 { flex: 1; }
.theme-fold > summary:hover h2 { color: var(--cinnabar); }
.theme-fold > summary::after { content: ""; flex: none; width: .55rem; height: .55rem; border-right: 2px solid var(--stone); border-bottom: 2px solid var(--stone); transform: rotate(45deg); margin: 0 .3rem .25rem .5rem; transition: transform var(--dur) var(--ease); }
.theme-fold[open] > summary::after { transform: rotate(-135deg); margin-bottom: -.2rem; }
.theme-fold[open] > summary { margin-bottom: var(--sp-3); }
.theme-count { flex: none; font-size: var(--fs-sm); white-space: nowrap; }
.theme-group:has(> .theme-fold:not([open])) { margin-bottom: var(--sp-3); }
/* « Accès gratuit », « Bonus », ↑ ↓, Supprimer: one row that wraps. */
.theme-head .actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: .4rem .9rem; white-space: normal; }
.theme-head .hanzi { color: var(--ink-2); white-space: nowrap; }
.theme-group .titles { margin: .3rem 0 .8rem; }
.theme-group .titles form { max-width: 44rem; }
.theme-group .titles .form-row { display: grid; gap: 1.1rem; grid-template-columns: 1fr 1fr; margin-bottom: 0; }
@media (max-width: 40rem) { .theme-group .titles .form-row { grid-template-columns: 1fr; } }
.theme-new { margin: 0 0 2.5rem; }
.theme-new > summary { list-style: none; }
.theme-new > summary::-webkit-details-marker { display: none; }
.theme-new[open] > summary { margin-bottom: 1rem; }
.theme-new form { max-width: 44rem; padding: 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.theme-new .form-row { display: grid; gap: 1.1rem; grid-template-columns: 1fr 1fr; margin-bottom: 0; }
.theme-new .btn, .lessons-add .btn { justify-self: start; }
.lessons select { font-size: .88rem; min-height: 36px; padding-top: .25rem; padding-bottom: .25rem; max-width: 14rem; }
@media (max-width: 40rem) { .theme-new .form-row { grid-template-columns: 1fr; } }
.lessons-add { margin-top: 1rem; }
.lessons-add > summary { display: inline-block; color: var(--cinnabar); font-weight: 600; cursor: pointer; }
.lessons-add form { max-width: 36rem; margin-top: .75rem; }

.editor textarea { width: 100%; }
.editor input[readonly] { color: var(--stone); background-color: transparent; }
.editor fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.editor legend, .field-label { display: block; padding: 0; margin-bottom: .4rem; font-size: .9rem; color: var(--ink-2); }
.editor .item-main p, .editor .step-body p { margin: 0; }
.editor .hint + *, .editor .sub-title + .hint { margin-top: .35rem; }
.hanzi-input { font-family: var(--hanzi); font-size: 1.15rem; }
.hanzi-inline { font-family: var(--hanzi); font-size: 1.05em; }
.btn.btn-link { border: 0; padding-left: 0; padding-right: 0; color: var(--cinnabar); }
.btn-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.btn:disabled { opacity: .35; cursor: default; }
.chip input:focus-visible + span {
  outline: 2px solid var(--cinnabar); outline-offset: 2px;
}

.editor { max-width: 56rem; }
.lesson-head { margin-bottom: 0; }
.lesson-title { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .75rem 1.5rem; }
.lesson-title h1 { font-size: clamp(1.75rem, 3.5vw, 2.3rem); margin: 0; }
.lesson-title h1 .hanzi { font-size: .85em; color: var(--ink-2); }
.title-pinyin { font-family: var(--sans); font-size: .9rem; font-weight: 400; color: var(--ink-2); white-space: nowrap; }
.lesson-state { display: flex; align-items: center; gap: .6rem; }
.lesson-head .form-error, .lesson-head .form-success { margin: 1rem 0 0; }
.lesson-head .form-error ul { margin: .35rem 0 0; padding-left: 1.2rem; }
.audio-status { display: flex; flex-wrap: wrap; align-items: center; gap: .1rem 1rem; margin-top: .5rem; font-size: .9rem; color: var(--stone); }
.audio-status p { margin: 0; }

.editor details > summary { list-style: none; cursor: pointer; }
.editor details > summary::-webkit-details-marker { display: none; }
.titles { margin-top: .5rem; }
.titles > summary, .upload > summary, .preview > summary { display: inline-block; color: var(--cinnabar); font-size: .9rem; }
.titles > summary:hover, .upload > summary:hover, .preview > summary:hover { text-decoration: underline; text-underline-offset: 3px; }
.titles[open] > summary { margin-bottom: 1rem; }

/* Steps */
/* The lesson editor: a card per step, folded; open, its head is tinted. */
.step, .step-note { margin: 0 0 var(--sp-3); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.step { overflow: clip; }
.step > summary {
  display: grid; grid-template-columns: auto auto 1fr auto; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}
.step > summary:hover { background: var(--panel-head); }
.step[open] > summary { background: var(--panel-head); border-bottom: 1px solid var(--edge); }
.step > summary::after {
  content: ""; width: .5rem; height: .5rem; margin: 0 .4rem .2rem;
  border-right: 1.5px solid var(--stone); border-bottom: 1.5px solid var(--stone);
  transform: rotate(45deg); transition: transform var(--dur) var(--ease);
}
.step[open] > summary::after { transform: rotate(-135deg); margin-bottom: -.2rem; }
.step > summary:hover .step-title { color: var(--cinnabar); }
.step-n {
  display: inline-grid; place-items: center; width: 2rem; height: 2rem; flex: none;
  font-family: var(--serif); font-size: 1rem; font-weight: 600;
  color: var(--cinnabar); border: 1.5px solid var(--cinnabar); border-radius: 8px;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.step[open] .step-n { background: var(--cinnabar); color: var(--on-accent); }
.step-title { font-family: var(--serif); font-size: var(--fs-lg); font-weight: 700; transition: color var(--dur) var(--ease); }
.sub-step { margin: var(--sp-4) 0 0; border: 1px solid var(--edge); border-radius: var(--radius-sm); padding: 0 var(--sp-4); background: var(--card); }
.sub-step > summary { cursor: pointer; padding: .8rem 0; font-family: var(--serif); font-weight: 600; font-size: 1.1rem; display: flex; gap: .8rem; align-items: baseline; }
.sub-step > summary .step-sum { margin-left: auto; font-family: var(--sans); font-weight: 400; }
.sub-step[open] > summary { border-bottom: 1px solid var(--line); margin-bottom: .6rem; }
.input-with-action { display: flex; gap: .5rem; align-items: center; }
.input-with-action input { flex: 1; min-width: 0; }
.step-sum { justify-self: end; text-align: right; color: var(--stone); font-size: .9rem; }
.step-body { padding: var(--sp-4); }
@media (min-width: 48rem) {
  .step > summary { padding-inline: var(--sp-5); }
  .step-body { padding: var(--sp-5); }
}
.step-note { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); color: var(--stone); font-size: .95rem; }
.step-note .step-n { color: var(--stone); border-color: var(--line); }
.sub-title { font-size: 1.1rem; margin: 2.5rem 0 0; }

/* Item lists */
.item-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.item {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: .5rem 1.25rem;
  padding: .9rem 0; border-bottom: 1px solid var(--line);
}
.item:first-child { border-top: 1px solid var(--line); }
.item .hanzi { font-size: 1.45rem; line-height: 1.35; }
.item-main { min-width: 0; }
.item-title { font-weight: 500; }
.item-side { display: flex; flex-direction: column; align-items: flex-end; gap: .3rem; }
.item-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .2rem; }
.item-actions .btn { min-height: 34px; padding: .25rem .6rem; }
.pinyin { color: var(--ink-2); }
.item.editing {
  display: block; margin: .8rem 0; padding: 1.25rem 1.4rem;
  background: var(--tint); border: 1px solid var(--line); border-radius: var(--radius);
}
.form-title { font-size: 1.05rem; margin: 0; }
.vocab-item { grid-template-columns: minmax(4.5rem, auto) 1fr auto; }
.vocab-item > .hanzi { font-size: 1.8rem; }
.line-item { grid-template-columns: auto 1fr auto; align-items: start; }
.line-item .pinyin, .line-item .fr { font-size: .95rem; }
.line-item .fr { color: var(--ink-2); }
.speaker {
  display: inline-grid; place-items: center; width: 1.8rem; height: 1.8rem; flex: none;
  border: 1.5px solid currentColor; border-radius: 50%;
  font-size: .8rem; font-weight: 600; line-height: 1;
}
.line-side { display: flex; flex-direction: column; align-items: center; gap: .3rem; margin-top: .3rem; min-width: 1.8rem; }
.line-n { font-size: var(--fs-sm); color: var(--stone); font-variant-numeric: tabular-nums; }
.needs-review { font-family: var(--sans); font-size: .75rem; vertical-align: middle; }
.speaker-A { color: var(--cinnabar); }
.speaker-B { color: var(--jade); }

.upload { text-align: right; font-size: .85rem; }
.upload-form { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: .4rem; margin-top: .4rem; }
.upload-form input[type="file"] { max-width: 17rem; font-size: .8rem; }
.upload-inline { justify-content: flex-start; margin: 0; }
.upload-inline .field-label { width: 100%; margin: 0; }

/* Add forms */
.add { margin-top: 1rem; }
.add > summary {
  display: inline-flex; align-items: center; gap: .5rem; min-height: 40px;
  padding: .45rem 1rem; border: 1px dashed var(--line-strong); border-radius: 999px; background: var(--card);
  color: var(--ink-2); font-size: .95rem;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.add > summary::before { content: "+"; color: var(--cinnabar); font-size: 1.15rem; line-height: 1; }
.add > summary:hover { border-color: var(--cinnabar); color: var(--ink); }
.add[open] > summary {
  padding: 0; min-height: 0; margin-bottom: .9rem; border: 0; background: none;
  font-family: var(--serif); font-size: 1.05rem; font-weight: 600; color: var(--ink);
}
.add[open] > summary::before { content: "−"; }

.item-form { gap: 1.1rem; }
.fields { display: grid; gap: 1rem; }
.fields-3 { grid-template-columns: 1.2fr 1fr 1.3fr; }
.input-with-action { display: flex; gap: .4rem; }
.input-with-action input { flex: 1; }
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }
.speaker-choice { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem 1.5rem; }
.speaker-choice legend { width: 100%; }
.speaker-choice label, .check { display: inline-flex; align-items: center; gap: .45rem; min-height: 36px; cursor: pointer; }
.dialogue-kind { gap: .4rem; margin-bottom: 1.25rem; }
.check-list { display: grid; }

/* Sentence editor */
.sentence-editor { display: grid; gap: .5rem; }
.sentence-editor .field-label { margin-bottom: 0; }
.sentence-editor p { margin: 0; }
.sentence-input { display: flex; gap: .4rem; }
.sentence-input input { flex: 1; }
.token-rows { display: grid; gap: .3rem; }
.token-head, .token-row { display: grid; grid-template-columns: 6.5rem 9.5rem 1fr 14.5rem; gap: .4rem; align-items: start; }
.token-head { padding: 0 .4rem; font-size: .8rem; color: var(--stone); }
.token-row { padding: .3rem .3rem .3rem .4rem; border-left: 3px solid transparent; border-radius: var(--radius); }
.token-row.unknown { background: var(--tint); border-left-color: var(--danger); }
.token-row input { width: 100%; }
.token-fr { display: grid; gap: .3rem; }
.editor .readings { padding: .2rem .4rem; font-size: .8rem; color: var(--ink-2); }
.token-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .15rem; }
/* A word's punctuation shows beside it, read-only: it is never a row. */
.token-word { display: flex; align-items: center; gap: .15rem; min-width: 0; }
.token-word input[type="text"] { flex: 1; min-width: 0; }
.mark { font-family: var(--hanzi); font-size: 1.1rem; color: var(--stone); }
.token-actions .btn { min-height: 34px; padding: .2rem .5rem; font-size: var(--fs-sm); }

/* Step files */
.lesson-files { margin: 0; }
.lesson-files > summary { color: var(--cinnabar); font-size: .95rem; cursor: pointer; }
.lesson-files[open] > summary { margin-bottom: .6rem; }
.file-list { list-style: none; margin: .4rem 0; padding: 0; display: grid; gap: .3rem; }
.file-list li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .4rem; }
.file-list a { overflow-wrap: anywhere; }

/* Questions */
.chip-list { display: flex; flex-wrap: wrap; gap: .35rem; }
.chip { position: relative; display: inline-flex; cursor: pointer; }
.chip input { position: absolute; opacity: 0; pointer-events: none; }
.chip span {
  display: inline-flex; align-items: center; gap: .3rem; min-height: 44px;
  padding: .15rem .6rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); font-family: var(--hanzi); font-size: 1.2rem;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.chip small { font-family: var(--sans); font-size: var(--fs-note); }
.chip:hover span { border-color: var(--stone); }
.chip input:checked + span { background: var(--cinnabar); border-color: var(--cinnabar); color: var(--on-accent); }
.chip input:focus-visible + span { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
.chip input:disabled + span { opacity: .5; cursor: default; }
button.chip { padding: 0; border: 0; background: none; color: inherit; font: inherit; }
button.chip:focus-visible span { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
.gaps-fields { display: grid; gap: .8rem; }
.gap-preview { font-size: 1.3rem; overflow-wrap: anywhere; }
.gap-preview .gap-hole { padding: 0 .15rem; border-radius: 4px; background: var(--violet-bg); color: inherit; box-shadow: inset 0 -2px 0 var(--violet); }
.check-list { display: flex; flex-wrap: wrap; gap: .3rem 1rem; }
.check { display: inline-flex; align-items: center; gap: .4rem; min-height: 44px; }
.line-checks { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.line-checks .check { align-items: baseline; flex-wrap: wrap; }
.line-checks .line-n { color: var(--stone); font-size: .85rem; }
.wrong-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: .4rem; }
.wrong-radicals { display: grid; gap: .4rem; max-width: 26rem; }
.wrong-radical { display: grid; grid-template-columns: 4.5rem 1fr; gap: .4rem; }
.test-editor { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.test-editor .switch { display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; font-weight: 500; }
.test-editor .switch input { accent-color: var(--jade); }
.test-tools { display: flex; flex-wrap: wrap; gap: .4rem; margin: .4rem 0; }
.test-group { font-size: 1rem; margin: 1rem 0 .3rem; }
.badge-unchecked { font-family: var(--sans); font-size: .75rem; }
.match-preview { border-collapse: collapse; margin: .3rem 0; font-size: .95rem; }
.match-preview th { font-weight: 500; font-size: var(--fs-sm); color: var(--stone); text-align: left; padding: 0 1rem .2rem 0; }
.match-preview td { padding: .1rem 1rem .1rem 0; border-top: 1px solid var(--line); }
.lines-preview { margin: .2rem 0; padding-left: 1.2rem; }
.hanzi-inline { font-family: var(--hanzi); }
.question-form fieldset { border: 0; padding: 0; margin: 0; display: grid; gap: .4rem; }
.question-form legend { font-size: .9rem; color: var(--ink-2); margin-bottom: .3rem; }
.item audio { max-width: 100%; }
.options { list-style: none; margin: .2rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .2rem 1rem; font-size: .95rem; color: var(--ink-2); }
.options .right { color: var(--jade); font-weight: 500; }
.option-rows { display: grid; gap: .4rem; }
.option-row { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: .6rem; }

/* Grammar */
.breakdown { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .4rem; }
.block { display: inline-flex; align-items: baseline; gap: .4rem; padding: .1rem .55rem; border-radius: var(--radius); font-size: var(--fs-sm); }
/* The pinyin under a block: the note size, never smaller. */
.block small { font-size: var(--fs-note); }
.item .block .hanzi { font-size: 1.05rem; }
.block-blue { background: var(--block-blue); color: var(--block-blue-ink); }
.block-orange { background: var(--block-orange); color: var(--block-orange-ink); }
.block-green { background: var(--jade-2); color: var(--jade); }
.block-purple { background: var(--block-purple); color: var(--block-purple-ink); }
.block-teal { background: var(--block-teal); color: var(--block-teal-ink); }
.block-grey { background: var(--paper-2); color: var(--ink-2); }
.chunk-editor { display: grid; gap: .3rem; }
.chunk-editor .field-label { margin: 0; }
.chunk-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .3rem; }
.chunk-row { display: grid; grid-template-columns: minmax(6rem, auto) 10rem 1fr; gap: .4rem; align-items: center; }
.chunk-row .block .hanzi { font-size: 1.1rem; }
.chunk-actions { display: flex; flex-wrap: wrap; gap: .15rem; }
.chunk-actions .btn, .block-actions .btn { min-height: 34px; padding: .2rem .5rem; font-size: var(--fs-sm); }
.block-actions { display: flex; justify-content: flex-end; gap: .15rem; }
.drill-add { display: flex; flex-wrap: wrap; gap: .4rem; }
.char-input { max-width: 6rem; font-size: 1.6rem; text-align: center; }
.stroke.stroke-preview { width: 9rem; height: 9rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.example { display: grid; gap: .6rem; margin-bottom: .8rem; padding: 1rem 1.1rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.example legend { padding: 0 .3rem; margin: 0; }

/* Culture */
.culture-image { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; margin: 1.25rem 0; }
.culture-preview { display: block; max-width: 16rem; border-radius: var(--radius); }
.preview { margin-top: .5rem; }
.editor .prose p { margin: 0 0 .9rem; }

@media (max-width: 40rem) {
  .step > summary { grid-template-columns: auto 1fr auto; row-gap: .1rem; }
  .step-sum { grid-column: 2; grid-row: 2; justify-self: start; text-align: left; }
  .step-body { padding-left: .25rem; }
  .fields-3 { grid-template-columns: 1fr; }
  .item { grid-template-columns: 1fr; }
  .vocab-item, .line-item { grid-template-columns: auto 1fr; }
  .item-side { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; }
  .upload { text-align: left; }
  .token-head { display: none; }
  .token-row { grid-template-columns: 1fr 1fr; background: var(--paper-2); }
  .token-fr, .token-actions { grid-column: 1 / -1; }
  .token-actions { justify-content: flex-start; }
  .chunk-row { grid-template-columns: auto 1fr; }
  .chunk-actions { grid-column: 1 / -1; }
}

/* ---- Learner pages ---- */
.learn { max-width: 44rem; }
.learn h1 .hanzi { margin-left: .5rem; }
/* The lesson's Chinese title under the French one, lighter. */
.learn h1 .title-zh { display: block; margin-top: .25rem; font-size: .72em; font-weight: 400; }
.learn h1 .title-zh .hanzi { margin-left: 0; font-size: 1.15em; }
/* The lesson page: one line of cheer, one big button, then the steps. A
   step done wears its sticker; the next one to do has the accent edge. */
.hub-cheer { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .7rem; color: var(--ink-2); font-size: var(--fs-sm); text-wrap: pretty; }
.hub-cheer .lesson-stars { margin: 0; }
.hub-win { font-weight: 700; color: var(--on-carbon); white-space: nowrap; }
.page-head .eyebrow { font-size: var(--fs-sm); }
/* The lesson page uses the width: the steps, and beside them the sheets
   and the extras. */
.learn.hub { max-width: 60rem; }
.hub-grid { display: grid; gap: 0 var(--sp-5); align-items: start; }
.hub-side:not(:has(*)) { display: none; }
@media (min-width: 60rem) {
  .hub-grid { grid-template-columns: minmax(0, 1fr) 20rem; }
  .hub-grid:not(:has(.hub-side > *)) { grid-template-columns: minmax(0, 44rem); }
}
/* The steps on a board, as « Mon parcours » (step_board.go). */
.step-board { margin: 0 0 var(--sp-5); }
/* On the left edge of the page, as the band's title. */
section.journey.step-board > * { margin-inline: 0; }
.step-board .board-title { font-size: var(--fs-lg); }
.disc .step-score { position: absolute; top: -8px; right: -18px; margin: 0; padding: 0 .4rem; border: 1px solid var(--edge); border-radius: 99px; background: var(--card); font-size: var(--fs-xs); font-weight: 800; line-height: 1.5; color: var(--jade); }
/* Mes leçons: two zones. « Aujourd'hui »: the next step alone in its big
   card, then the reviews and the class, each its own card, one under the
   other on every screen (side by side left a hole under the reviews), the
   reminder and the offer as quiet lines (Jonathan, 2026-10-10). « Mon parcours »: a card
   per theme, the lesson under way tinted with the accent edge, the ones
   done ticked, the stars once won. */
.learn.home { max-width: 52rem; }
.today { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-6); }
.today .hero-card { margin: 0; }
a.hero-card.continue-card:hover { transform: none; }
.today .placement-card .hero-card-title { font-size: clamp(1.4rem, 3.5vw, 1.75rem); }
.today-cards { display: grid; gap: var(--sp-4); align-items: start; }
.today .review-card, .today .class-card { display: grid; justify-items: start; gap: var(--sp-2); margin: 0; padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.today .today-card-title { margin: 0; font-size: var(--fs-lg); }
/* The reviews: what is due on the left, « Réviser » at the far right; under
   it when the screen is too narrow. */
.today .review-card { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3) var(--sp-4); }
.review-text { display: grid; gap: var(--sp-1); }
.today-meta { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.today .class-card h3 { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.today .class-card p { margin: 0; }
.today .class-card .class-message { margin: 0; }
.today-note { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.today-note[hidden] { display: none; }
.today-offer .book-card { margin: 0; }
.today-offer .btn-primary { background: var(--card); border-color: var(--cinnabar); color: var(--cinnabar-2); box-shadow: none; }
.zone-title { margin: 0 0 var(--sp-3); font-size: var(--fs-lg); }
.journey-note { margin: 0 0 var(--sp-3); color: var(--ink-2); font-size: var(--fs-sm); }
.theme-zh { color: var(--ink-2); font-size: var(--fs-sm); }
.theme-zh .hanzi { font-size: 1.25rem; line-height: 1.3; }
/* « Tu sauras… »: what the chapter or the lesson teaches, one quiet line
   under its title, aligned left like it (a short line justified gapes). */
.goal { text-align: left; hyphens: manual; -webkit-hyphens: manual; text-wrap: pretty; color: var(--ink-2); }
.learn .goal { margin: -.2rem 0 1rem; }
.page-head .goal { margin: 0; }
.theme-group > .goal { margin: .3rem 0 .6rem; }
.next-lesson { margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }

/* « Mon parcours », a board game (plans/2026-11-11-parcours-plateau.md,
   marketing/maquettes/parcours.html): a round per lesson on a dotted trail,
   in a calm low poly landscape. Red is the lesson to do now, blue the class
   with Fei, pale green and ticked what is done, white what comes, pale gold
   the diploma until it is won. The colours come from the palette. The end
   of a lesson (.lesson-end) shows a strip of the same board. */
.journey, .lesson-end {
  --b-sky: color-mix(in srgb, var(--jade) 5%, var(--paper));
  --b-g1: color-mix(in srgb, var(--jade) 6.5%, var(--paper));
  --b-g2: color-mix(in srgb, var(--jade) 8%, var(--paper));
  --b-g3: color-mix(in srgb, var(--jade) 9.5%, var(--paper));
  --b-tree-1: color-mix(in srgb, var(--jade) 24%, var(--paper));
  --b-tree-2: color-mix(in srgb, var(--jade) 34%, var(--paper));
  --b-trunk: color-mix(in srgb, var(--stone) 40%, var(--paper));
  /* The trail is a light grey, never a rival to the rounds (Fei,
     2026-11-11); the part walked paler still. */
  --b-trail: color-mix(in srgb, var(--line-strong) 85%, var(--paper));
  --b-walked: color-mix(in srgb, var(--line) 75%, var(--paper));
  --b-fei: var(--block-blue-ink);
  --b-fei-2: color-mix(in srgb, var(--block-blue-ink) 72%, var(--ink));
  --b-later: color-mix(in srgb, var(--line-strong) 70%, var(--card));
}
/* On the dark paper, the bright jade shows more: softer facets. */
[data-palette="nuit"] :is(.journey, .lesson-end) {
  --b-sky: color-mix(in srgb, var(--jade) 4%, var(--paper));
  --b-g1: color-mix(in srgb, var(--jade) 4%, var(--paper));
  --b-g2: color-mix(in srgb, var(--jade) 4.6%, var(--paper));
  --b-g3: color-mix(in srgb, var(--jade) 5.2%, var(--paper));
  --b-tree-1: color-mix(in srgb, var(--jade) 18%, var(--paper));
  --b-tree-2: color-mix(in srgb, var(--jade) 26%, var(--paper));
}
.board-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.board-defs .g1 { fill: var(--b-g1); }
.board-defs .g2 { fill: var(--b-g2); }
.board-defs .g3 { fill: var(--b-g3); }
/* The chapters: a tab each, on a line that slides. */
.board-tabs { display: flex; gap: var(--sp-2); overflow-x: auto; scrollbar-width: none; margin: 0 calc(-1 * var(--sp-4)) var(--sp-3); padding: 2px var(--sp-4); }
.board-tabs::-webkit-scrollbar { display: none; }
.board-tab {
  flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--sp-4);
  border: 1px solid var(--edge); border-radius: 999px; background: var(--card); color: var(--ink-2);
  font-size: var(--fs-sm); font-weight: 800; white-space: nowrap; background-image: none;
}
.board-tab:hover { color: var(--ink); }
.board-tab[aria-current] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
@media (scripting: enabled) { .journey .board-panel:not(.is-open) { display: none; } }
.board-panel + .board-panel { margin-top: var(--sp-5); }
.board-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.board-title { margin: 0; font-size: var(--fs-lg); font-weight: 800; }
.board-count { flex: none; font-weight: 800; color: var(--jade); white-space: nowrap; }
.board, .lesson-end .strip { position: relative; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--b-sky); }
/* The heading, the tabs, the title line and the board share one column,
   on the left edge of the page's cards. */
/* One unit, centred in the page's column: the tabs, the title line and
   the board; the heading on the cards' edge, as the other zone titles. */
.journey > :not(.zone-title, .board-go) { max-width: 34rem; margin-inline: auto; }
@media (max-width: 47.99rem) { .journey > .board-tabs { max-width: none; margin-inline: calc(-1 * var(--sp-4)); } }
@media (min-width: 48rem) { .board-tabs { padding-inline: 2px; } }
/* On « Leçons », the board's top on the phone's first screen. */
.page.has-go .board-tabs { margin-top: calc(-1 * var(--sp-2)); margin-bottom: var(--sp-2); }
.page.has-go .board-head { margin-bottom: var(--sp-2); }
.board-bg, .board-trail { position: absolute; inset: 0; width: 100%; height: 100%; }
.trail { fill: none; stroke: var(--b-trail); stroke-width: 4px; stroke-dasharray: 8 16; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.trail-walked { stroke: var(--b-walked); }
.board-toks { list-style: none; margin: 0; padding: 0; }
/* At most 29 % wide: the titles of two rounds side by side never meet,
   even on a narrow phone. */
.tok { position: absolute; width: min(108px, 29%); transform: translate(-50%, -32px); text-align: center; }
.tok-in { position: relative; display: grid; justify-items: center; gap: 6px; min-height: 44px; color: var(--ink); text-decoration: none; background-image: none; border-radius: var(--radius-sm); }
a.tok-in:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.disc {
  position: relative; display: grid; place-items: center; width: 64px; height: 64px; border-radius: 50%;
  font-family: var(--sans); font-weight: 900; font-size: 1.7rem; line-height: 1;
  transition: transform var(--dur) var(--ease);
}
a.tok-in:hover .disc { transform: translateY(-2px); }
.tok-name {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  font-size: var(--fs-sm); font-weight: 800; line-height: 1.2; color: var(--ink-2); text-align: center;
  hyphens: manual; -webkit-hyphens: manual; overflow-wrap: anywhere;
  text-shadow: 0 0 3px var(--b-g1), 0 0 3px var(--b-g1);
}
.tok .tag { font-size: var(--fs-xs); padding: 0 .45rem; }
.tok-done .disc { width: 46px; height: 46px; margin-top: 9px; background: var(--jade-2); color: var(--jade); border: 2px solid color-mix(in srgb, var(--jade) 35%, transparent); font-size: 1.2rem; }
.tok-done .tok-name { font-weight: 700; }
.tok-later .disc { background: var(--card); color: var(--stone); border: 3px solid var(--b-later); box-shadow: 0 5px 0 var(--b-later); }
.tok-now .disc { width: 74px; height: 74px; margin-top: -5px; background: var(--card); color: var(--cinnabar); border: 5px solid var(--cinnabar); box-shadow: 0 5px 0 var(--cinnabar-2), 0 10px 22px color-mix(in srgb, var(--cinnabar) 35%, transparent); font-size: 2rem; }
.tok-now .tok-name { color: var(--cinnabar-2); font-weight: 900; }
.tok-paid .disc { width: 52px; height: 52px; margin-top: 6px; background: var(--paper-2); border: 2px solid var(--line); font-size: 1.1rem; }
.tok-paid .tok-name { color: var(--stone); font-weight: 700; }
.tok-paid .pawn ~ .disc { width: 64px; height: 64px; margin-top: 0; border: 3px solid var(--cinnabar); }
.tok-fei .disc { width: 56px; height: 56px; margin-top: 4px; padding: 5px; background: var(--b-fei); box-shadow: 0 5px 0 var(--b-fei-2), 0 8px 14px rgb(0 0 0 / .12); }
.tok-fei .disc img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 2px solid var(--card); }
.tok-fei .tok-name { color: var(--b-fei); font-weight: 900; }
/* Not won yet: calm, pale gold; it shines once the chapter is done. */
.tok-end .disc { width: 60px; height: 60px; background: var(--card); border: 3px dashed color-mix(in srgb, var(--star) 55%, var(--card)); font-size: 1.6rem; filter: grayscale(.6); opacity: .8; }
.tok-end .tok-name { color: var(--stone); }
.tok-end.is-won .disc { background: var(--warm-bg); border: 3px solid var(--star); box-shadow: 0 5px 0 color-mix(in srgb, var(--star) 70%, var(--ink)); filter: none; opacity: 1; }
.tok-end.is-won .tok-name { color: var(--warm-ink); font-weight: 900; }
/* The pawn hops three times, then rests. */
.pawn { position: absolute; z-index: 2; top: -44px; left: 50%; width: 34px; height: 48px; margin-left: -17px; fill: var(--cinnabar); filter: drop-shadow(0 4px 3px rgb(0 0 0 / .25)); animation: pawn-hop 1.4s ease-in-out 3; }
.pawn-foot { fill: var(--cinnabar-2); }
.pawn-shine { fill: #fff; opacity: .55; }
@keyframes pawn-hop { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
/* The first visit of « Leçons » only: a tip right over the pawn, in the
   room journey.go keeps for it (tipW × tipH), its arrow down onto the
   pawn; gone after the first click (app.js). */
.board-tip {
  position: absolute; z-index: 3; margin: 0; width: 230px; max-width: calc(100% - 16px); min-height: 44px; padding: 5px 10px;
  display: flex; align-items: center; transform: translateY(-100%); border-radius: 12px; background: var(--ink); color: var(--paper);
  font-size: var(--fs-xs); font-weight: 800; line-height: 1.3; text-align: left; hyphens: manual; -webkit-hyphens: manual; box-shadow: 0 8px 20px rgb(0 0 0 / .2);
}
.board-tip-arrow { position: absolute; z-index: 3; margin-left: -8px; border: 8px solid transparent; border-bottom: 0; border-top-color: var(--ink); }
.board-tip[hidden], .board-tip-arrow[hidden] { display: none; }
/* « Continuer », a bar pinned at the bottom of « Leçons », above the tab
   bar on a phone, its button in the column of the board. The page keeps
   room for it under its footer: app.js measures the bar, the padding
   here is the fallback. */
.board-go { position: fixed; z-index: 30; left: 0; right: 0; bottom: 0; padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--edge); background: var(--paper); box-shadow: 0 -4px 16px rgb(0 0 0 / .06); }
/* The page's rise leaves a transform, which would pin the bar to the page
   instead of the screen. */
.page.has-go { animation: none; }
.page.has-go + .site-footer { padding-bottom: 6rem; }
.page.has-go.has-offer + .site-footer { padding-bottom: 10rem; }
.board-go-in { max-width: 34rem; margin: 0 auto; }
.board-go .btn-go { display: block; width: 100%; font-size: 1.1rem; text-align: center; white-space: normal; }
.board-go .book-card { margin: 0; }
@media (max-width: 47.99rem) {
  .tabbar ~ .page .board-go { bottom: calc(57px + env(safe-area-inset-bottom)); }
}
/* Fei's photo: before the class, revise the last one. A sheet from the
   bottom of the screen. */
.fei-sheet {
  position: fixed; inset: auto 0 0; width: 100%; max-width: 34rem; max-height: 80vh; margin: 0 auto; padding: var(--sp-5) var(--sp-4) calc(var(--sp-5) + env(safe-area-inset-bottom));
  border: 0; border-radius: 24px 24px 0 0; background: var(--card); color: var(--ink); box-shadow: 0 -10px 30px rgb(0 0 0 / .15);
}
.fei-sheet[open] { display: grid; gap: var(--sp-3); animation: sheet-up .25s ease-out; }
.fei-sheet::backdrop { background: rgb(20 16 14 / .35); }
@keyframes sheet-up { from { transform: translateY(40px); opacity: 0; } }
.fei-sheet-head { display: flex; gap: var(--sp-3); align-items: center; }
.fei-sheet-head img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; border: 3px solid var(--b-fei); }
.fei-sheet-head p, .fei-sheet-msg, .fei-sheet form { margin: 0; }
.fei-sheet-k { font-size: var(--fs-xs); font-weight: 800; color: var(--stone); }
.fei-sheet-t { font-size: var(--fs-lg); font-weight: 900; color: var(--b-fei); }
.fei-sheet-msg { font-weight: 700; color: var(--ink-2); line-height: 1.4; text-align: left; }
.fei-sheet-msg b { color: var(--ink); }
.btn-fei { background: var(--b-fei); color: var(--paper); box-shadow: 0 4px 0 var(--b-fei-2); }
.btn-fei:hover { background: var(--b-fei-2); color: var(--paper); }
.fei-sheet form { display: grid; justify-items: center; }
.fei-sheet-close { min-height: 44px; padding: 0 var(--sp-4); border: 0; background: none; font: inherit; font-weight: 800; color: var(--stone); cursor: pointer; }
.fei-sheet-close:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .pawn, .fei-sheet[open] { animation: none; }
  a.tok-in:hover .disc { transform: none; }
}
/* The end of a finished lesson (marketing/maquettes/fin-lecon.html):
   Bravo and the stars, a strip of the board where the pawn hops to the
   next lesson, one button. Played once on arrival; still with
   prefers-reduced-motion. */
.lesson-end { position: relative; display: grid; gap: var(--sp-4); max-width: 34rem; margin: var(--sp-6) auto 0; padding: var(--sp-5) var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); overflow: hidden; }
.end-top { display: grid; justify-items: center; gap: var(--sp-2); text-align: center; }
.end-seal { display: grid; place-items: center; width: 64px; height: 64px; border-radius: 50%; background: var(--jade); color: var(--card); font-weight: 900; font-size: 2rem; box-shadow: 0 8px 20px color-mix(in srgb, var(--jade) 30%, transparent); }
.lesson-end .end-bravo { margin: var(--sp-1) 0 0; font-size: 1.9rem; font-weight: 900; text-align: center; }
.end-what { margin: 0; color: var(--ink-2); font-weight: 800; text-align: center; }
.end-stars { display: flex; gap: 10px; margin-top: var(--sp-1); }
.end-star { width: 46px; height: 46px; fill: var(--line); }
.end-star.is-won { fill: var(--star); animation: star-pop .5s cubic-bezier(.3, 1.6, .5, 1) both; }
.end-star.is-won:nth-child(2) { animation-delay: .25s; }
.end-star.is-won:nth-child(3) { animation-delay: .5s; }
@keyframes star-pop { from { transform: scale(0) rotate(-30deg); } to { transform: scale(1) rotate(0); } }
.end-stars-t { margin: 0; color: var(--stone); font-size: var(--fs-sm); font-weight: 800; text-align: center; }
.lesson-end .strip { height: 172px; }
.lesson-end .tok { width: min(132px, 29%); }
.lesson-end .tok-done .disc { width: 42px; height: 42px; margin-top: 7px; font-size: 1.1rem; }
.lesson-end .tok-now .disc { width: 56px; height: 56px; margin-top: 0; border-width: 4px; font-size: 1.5rem; box-shadow: 0 4px 0 var(--cinnabar-2); }
.lesson-end .tok-fei .disc { width: 52px; height: 52px; padding: 4px; }
.lesson-end .tok-end .disc { width: 52px; height: 52px; margin-top: 2px; font-size: 1.4rem; }
.lesson-end .tok.is-just .disc { animation: tok-tick .4s ease-out .9s both; }
@keyframes tok-tick { from { background: var(--card); color: var(--cinnabar); border-color: var(--cinnabar); transform: scale(1.2); } to { transform: scale(1); } }
.lesson-end .tok-now .disc { animation: tok-light .3s ease-out 1.5s both; }
@keyframes tok-light { from { color: var(--stone); border-color: var(--b-later); box-shadow: 0 4px 0 var(--b-later); } }
/* The pawn hops from the lesson done to the next one, once. */
.strip-pawn { position: absolute; inset: 0; pointer-events: none; }
.strip-pawn .pawn { top: 8px; left: var(--to); width: 30px; height: 42px; margin-left: -15px; animation: pawn-move 1s ease-in-out .6s both; }
@keyframes pawn-move { 0% { left: var(--from); top: 8px; } 50% { top: -8px; } 100% { left: var(--to); top: 8px; } }
.end-go { display: grid; gap: var(--sp-3); }
.end-next { margin: 0; text-align: center; color: var(--ink-2); font-weight: 800; }
.end-next b { color: var(--ink); }
.end-go .btn-go { display: block; text-align: center; font-size: 1.1rem; }
.end-go .btn-back { display: flex; align-items: center; justify-content: center; min-height: 44px; }
/* The offer under the end's buttons: one red button only, the end's own;
   the offer a quiet card. */
.end-go .book-card { margin: 0; padding: var(--sp-3) 0 0; border: 0; border-top: 1px solid var(--line); border-radius: 0; background: none; text-align: center; }
.end-go .book-card .btn-primary { background: transparent; color: var(--cinnabar-2); border: 2px solid var(--line-strong); box-shadow: none; }
.end-go .book-card .btn-primary:hover { background: var(--panel-head); color: var(--cinnabar-2); }
/* A few paper bits, once. */
.end-bits { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.end-bits i { position: absolute; top: -12px; width: 8px; height: 12px; border-radius: 2px; opacity: 0; animation: bit-fall 2.2s ease-in both; }
.end-bits i:nth-child(1) { left: 12%; background: var(--cinnabar); animation-delay: .1s; }
.end-bits i:nth-child(2) { left: 26%; background: var(--star); animation-delay: .3s; }
.end-bits i:nth-child(3) { left: 41%; background: var(--jade); }
.end-bits i:nth-child(4) { left: 57%; background: var(--cinnabar); animation-delay: .4s; }
.end-bits i:nth-child(5) { left: 70%; background: var(--b-fei); animation-delay: .2s; }
.end-bits i:nth-child(6) { left: 84%; background: var(--star); animation-delay: .5s; }
@keyframes bit-fall { 0% { opacity: 1; transform: translateY(0) rotate(0); } 100% { opacity: 0; transform: translateY(420px) rotate(540deg); } }
@media (prefers-reduced-motion: reduce) {
  .end-star.is-won, .lesson-end .tok .disc, .strip-pawn .pawn { animation: none; }
  .end-bits { display: none; }
}

.word { font: inherit; font-family: var(--hanzi); background: none; border: 0; border-bottom: 1px dotted var(--stone); padding: 0 .05em; margin: 0; color: inherit; cursor: pointer; min-height: 44px; }
.word:hover, .word:focus-visible { border-bottom-color: var(--cinnabar); color: var(--cinnabar); outline: none; }
/* A word and its punctuation never break apart, so a mark never starts a line. */
.w { white-space: nowrap; }
/* A clause of a dialogue line (up to its ， 。 ？ or ！) breaks as a whole,
   never leaving 吗？ alone on a line; one wider than the line wraps
   inside. */
.clause { display: inline-block; max-width: 100%; }
.popover { position: absolute; z-index: 10; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: .8rem 1rem; min-width: 9rem; text-align: center; }
.popover .hanzi { font-size: 1.75rem; }

.play, .loop, .play-all, .pinyin-toggle {
  font: inherit; font-size: var(--fs-lg); display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-width: 52px; min-height: 52px; padding: 0; cursor: pointer;
  background: var(--card); border: 1px solid var(--line-strong); border-radius: 50%; color: var(--ink-2);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.play-all, .pinyin-toggle { padding: .5rem 1.2rem; border-radius: 999px; font-size: var(--fs-btn-sm); font-weight: 700; }
.head-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
/* « Cacher le pinyin »: the pinyin keeps its room, so nothing moves. */
.no-pinyin rt { visibility: hidden; }
.play:hover, .loop:hover, .play-all:hover, .pinyin-toggle:hover { border-color: var(--stone); color: var(--ink); }
/* One action, one icon, one color, everywhere (Fei, 2026-11-11): every
   « Écouter » is this round white button, its 🔊 red, no text; larger
   only when the sound is a test's question (.play-big). */
.play .icon { color: var(--cinnabar); }
.loop.active { border-color: var(--cinnabar); color: var(--cinnabar); }
.line-actions .loop.active { background: var(--accent-bg); }
/* Whatever is playing gets the same tint and red edge: rows, lines, words,
   popovers. An outline, not a border, so table rows show it too and nothing
   shifts. The popover already has a border, so it recolors that instead. */
.playing { background: var(--playing-bg); outline: 1px solid var(--cinnabar); outline-offset: -1px; border-radius: var(--radius); }
.popover.playing { outline: none; border-color: var(--cinnabar); }
.word.playing { border-radius: 3px; }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* A sentence: each word wears its pinyin above it, the French under the
   line, the buttons beside it (under it on a phone). Everything starts at
   the page's left edge: the speaker is a badge inside the line, not a
   column. */
.dialogue-lines { list-style: none; padding: 0; margin: 0; }
.line { display: grid; grid-template-columns: 1fr auto; gap: .35rem .8rem; align-items: start; padding: .9rem 0; border-bottom: 1px solid var(--line); }
.line .text { grid-column: 1; font-family: var(--hanzi); font-size: 1.5rem; line-height: 2.2; margin-top: -.3rem; }
.line .pinyin, .line .fr { grid-column: 1; }
.line .speaker { margin-right: .2rem; color: var(--stone); font-family: var(--sans); font-weight: 600; vertical-align: .2em; }
ruby { ruby-position: over; ruby-align: center; }
.word rt { font-family: var(--sans); font-size: .56em; font-weight: 400; line-height: 1.2; letter-spacing: 0; color: var(--ink-2); }
.word:hover rt, .word:focus-visible rt, .word.playing rt { color: inherit; }
/* The word the audio is saying lights up (audio.WordTimes): vermillon,
   its pinyin too. */
.word.saying { color: var(--cinnabar-2); border-bottom-color: var(--cinnabar); }
.word.saying rt { color: var(--cinnabar-2); }


/* The buttons of a line, one under the other, each with its word. */
.line-actions { grid-column: 2; grid-row: 1 / span 2; display: flex; flex-direction: column; align-items: flex-start; gap: .35rem 1.4rem; }
/* The tools of a line, the parts of a long one and « Tout écouter »: real
   buttons, outlined, the icon in vermillon (Jonathan, 2026-10-10: « un
   bouton est un bouton »). « En boucle » turns red while it loops, « À
   toi » while it listens. */
:is(.line-actions .loop, .line-actions .repeat-line, .line .repeat-part, .learn .play-all) {
  min-width: 0; min-height: 44px; padding: .45rem 1rem; border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--card); box-shadow: none; color: var(--ink-2);
  justify-content: center; gap: .45rem; font-size: var(--fs-btn-sm); font-weight: 700; white-space: nowrap;
}
:is(.line-actions .loop, .line-actions .repeat-line, .line .repeat-part, .learn .play-all) .icon { color: var(--cinnabar); }
:is(.line-actions .loop, .line-actions .repeat-line, .line .repeat-part, .learn .play-all):is(:hover, :focus-visible) { border-color: var(--stone); color: var(--ink); }
:is(.line-actions .loop.active, .line-actions .repeat-line:is(.is-on, .is-waiting), .line .repeat-part:is(.is-on, .is-waiting)) { border-color: var(--cinnabar); background: var(--accent-bg); color: var(--cinnabar-2); }
/* A part shows its characters over their pinyin: a rounded box, not a pill. */
.line .repeat-part { border-radius: var(--radius-sm); text-align: left; }
/* Each line is a card, its sentence, translation and tools together
   (Jonathan, 2026-10-05: « organiser, pas un paquet de texte »). */
.dialogue-lines { display: grid; gap: .8rem; }
.dialogue-lines > .line { grid-template-columns: 1fr; padding: .85rem 1.1rem .6rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
/* In its card, a line's tools sit in a row under its translation, on
   every screen. */
.dialogue-lines .line-actions { grid-column: 1; grid-row: auto; flex-direction: row; flex-wrap: wrap; }
/* The conversation, like its role play: the pinyin in front, large, bold,
   in violet; the characters quiet, small and light grey (Fei,
   2026-11-12): the oral first. */
.oral-lines .line .text { font-size: 1.15rem; line-height: 2.8; color: color-mix(in srgb, var(--stone) 70%, var(--card)); }
.oral-lines .word rt { padding: 0 .18em; font-size: 1.05em; font-weight: 800; color: var(--violet); }
.oral-lines .word:is(:hover, :focus-visible, .playing, .saying) rt { color: var(--cinnabar-2); }
/* « Je construis des phrases » (Fei, 2026-11-12: « super chiante »): the
   teacher's instruction as the challenge, a dark band; the rule, the first
   line of each point, in bold; the word the point is about in red in its
   examples; their whole translation in bold; no justified text. */
.block-intro.grammar-defi { background: var(--ink); color: var(--card); border-color: var(--ink); text-align: left; }
.block-intro.grammar-defi .defi-label { margin: 0 0 var(--sp-1); font-size: var(--fs-xs); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: color-mix(in srgb, var(--cinnabar) 45%, var(--card)); }
.block-intro.grammar-defi .word { color: inherit; }
.grammar-point, .grammar-point .prose { text-align: left; }
.grammar-point .explanation > p:first-child { font-weight: 700; }
.word.is-key { border-radius: 10px; background: color-mix(in srgb, var(--cinnabar) 12%, transparent); }
.oral-lines .word.is-key rt, .word.is-key rt { color: var(--cinnabar); }
.grammar-point .examples .fr { font-weight: 700; color: var(--ink); }
/* One grammar point at a time (Fei, 2026-11-12). */
.gp-steps { scroll-margin-top: var(--sp-4); }
.gp-tabs { margin: 0 0 var(--sp-4); }
/* The colored blocks of an example (Fei, 2026-11-12: « harmonise »), the
   shape of « Je change les mots »: each block in its role's colour, its
   role over it, its pinyin in front in bold, its characters smaller and
   lighter, the « + » kept with the block after it, the end mark out. */
.structure { align-items: flex-end; row-gap: var(--sp-3); }
.structure-pair { display: inline-flex; align-items: flex-end; gap: .3rem; }
/* The « + » at the height of the pinyin, its box no taller than a block:
   a structure without roles (an « À toi » answer) gets no gap over its
   second line. In em, it follows the structure's size. */
.structure-plus { align-self: flex-end; padding: 0 0 1.6em; font-size: 1em; line-height: 1.4; }
.structure .slot { min-width: 3rem; justify-content: center; border-radius: var(--radius-sm); font-weight: 400; }
/* The structure first (Fei, 2026-11-12: « pourquoi la structure est en
   petit ? »): larger than the text around it, its roles as large. */
.oral-lines .line .text.structure { font-size: 1.5rem; }
.structure .structure-role { font-size: 1.25rem; font-weight: 800; }
.structure .word rt, .oral-lines .structure .word rt { font-size: 1.05em; font-weight: 800; color: inherit; }
.oral-lines .line .text.structure { color: inherit; }
.structure .block-blue { color: color-mix(in srgb, var(--block-blue-ink) 60%, var(--card)); } .structure .block-blue rt { color: var(--block-blue-ink) !important; }
.structure .block-orange { color: color-mix(in srgb, var(--block-orange-ink) 60%, var(--card)); } .structure .block-orange rt { color: var(--block-orange-ink) !important; }
.structure .block-green { color: color-mix(in srgb, var(--jade) 60%, var(--card)); } .structure .block-green rt { color: var(--jade) !important; }
.structure .block-purple { color: color-mix(in srgb, var(--block-purple-ink) 60%, var(--card)); } .structure .block-purple rt { color: var(--block-purple-ink) !important; }
.structure .block-teal { color: color-mix(in srgb, var(--block-teal-ink) 60%, var(--card)); } .structure .block-teal rt { color: var(--block-teal-ink) !important; }
.structure .block-grey { color: color-mix(in srgb, var(--ink-2) 60%, var(--card)); } .structure .block-grey rt { color: var(--ink-2) !important; }
/* In a block, the word the point is about is red by its pinyin alone: no
   tint of its own, a box in the box (2026-11-13). */
.structure .word.is-key { background: none; }
.structure .word.is-key rt { color: var(--cinnabar) !important; }
/* The mark after a block (， 。 ？) against the block, out of its colour,
   even when the role over it is wider (« complément » over 八岁): inside
   the block in the page, drawn just past its right edge; the block's
   column keeps the room for it. */
.structure .slot { position: relative; }
.structure-end { position: absolute; left: 100%; bottom: 0; padding: 0 0 .5rem .1em; font-family: var(--hanzi); font-weight: 400; color: var(--stone); white-space: nowrap; }
.structure-col:has(.structure-end) { margin-right: .9em; }
/* A group of blocks keeps together while it fits (chunkClauses): the
   last two, a block ending with 的 and the next. */
.structure-group { display: flex; flex-wrap: wrap; align-items: flex-end; row-gap: inherit; column-gap: inherit; max-width: 100%; min-width: 0; }
/* A clause of a structure (up to its ， or 。) keeps to one line while it
   fits: a phone breaks after « gēge ， », not inside a clause. One wider
   than the screen wraps inside. */
.structure-clause { display: flex; flex-wrap: wrap; align-items: flex-end; align-self: flex-end; row-gap: inherit; column-gap: inherit; max-width: 100%; min-width: 0; }
/* A word's mark in a line where the pinyin leads (oral-words): right
   after its characters, its hidden copy after the pinyin centring it. */
.rt-mark { visibility: hidden; }
@media (max-width: 479px) {
  /* « wǒ + méiyǒu + jiějie » side by side on a 360 px phone, the pinyin
     still 1.5 rem: less room around the words, the words of a block kept
     apart by a margin instead of their pinyin's padding. */
  .structure, .structure-clause, .structure-group { column-gap: .2rem; }
  .structure-pair { gap: .2rem; }
  .structure .slot { min-width: 2em; padding-inline: 6px; }
  .structure .word rt, .oral-lines .structure .word rt { padding: 0; }
  .structure .slot .w + .w { margin-left: .12em; }
  /* Under 445 px the structure follows the screen's width, 1.5 rem
     above: « nǐ jiā + yǒu + jǐ kǒu rén ？ » on one line of a 360 px
     phone (2026-11-13). An « À toi » answer, without roles, a little
     smaller. */
  .oral-lines .line .text.structure { font-size: min(1.5rem, 5.2vw); }
  /* The roles smaller, so that a role (« complément » over 安娜) hardly
     widens its block and never runs into the next one (2026-11-14). */
  .structure .structure-role { font-size: min(1.25rem, 3.6vw); }
  .say-it .oral-lines .line .text.structure { font-size: min(1.25rem, 4.4vw); }
}
.gp-tabs .practice-tab { padding: 0 1rem; font-family: var(--sans); font-size: var(--fs-btn-sm); font-weight: 700; }
.gp-step[hidden] { display: none; }
.gp-nav { display: flex; justify-content: space-between; gap: var(--sp-3); margin: 0 0 var(--sp-5); }
.gp-nav .btn-primary { margin-left: auto; }
.learn .breakdown { gap: .4rem; margin: .6rem 0; }
.learn .block { flex-direction: column; align-items: center; gap: 0; padding: .2rem .6rem; font-size: 1.25rem; }
.learn .block .word { min-height: 0; }
.learn .block-h { font-family: var(--hanzi); }
.learn .block .pinyin, .learn .block .fr, .learn .block small { font-family: var(--sans); font-size: var(--fs-note); }
.learn .block small { margin-top: .15rem; text-transform: uppercase; letter-spacing: .04em; opacity: .8; }
.explanation { padding-top: 0; }
.explanation .word { min-height: 0; }
/* A table in an explanation lines up forms: 我 → 我的 → mon. */
.explanation table { border-collapse: collapse; margin: .5rem 0 1rem; }
.explanation th { text-align: left; font-weight: 500; font-size: var(--fs-sm); color: var(--stone); padding: 0 1.25rem .4rem 0; border-bottom: 1px solid var(--line); }
.explanation td { padding: .4rem 1.25rem .4rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
/* A cell is too narrow to justify: its words would drift apart. */
.explanation :is(th, td) { text-align: left; hyphens: manual; -webkit-hyphens: manual; }
/* A table whose rows are labeled (an empty top-left cell: « Demander »,
   « Dire »…) is a list of cards on a phone: the label, then its cells
   under it, at full width. */
@media (max-width: 40rem) {
  .explanation table:has(thead th:first-child:empty) :is(tbody, tr, td) { display: block; }
  .explanation table:has(thead th:first-child:empty) thead { display: none; }
  .explanation table:has(thead th:first-child:empty) tr { padding: .75rem 0; border-bottom: 1px solid var(--line); }
  .explanation table:has(thead th:first-child:empty) td { padding: .1rem 0; border: 0; }
  .explanation table:has(thead th:first-child:empty) td:first-child { color: var(--cinnabar-2); }
  .explanation table:has(thead th:first-child:empty) td + td + td { color: var(--ink-2); }
}
.explain-pinyin { color: var(--stone); font-size: .9em; }
/* A sentence structure in bold, **Sujet + 住在 + 哪儿 ?**: a grey framed
   box on its own line, a tag per part, the slots to fill in blue, the
   fixed words in the accent color. */
.formula { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .4rem; width: fit-content; max-width: 100%; margin: .5rem 0; padding: .55rem .8rem; border: 1px solid var(--line); border-left: 3px solid var(--cinnabar); border-radius: var(--radius-sm); background: var(--paper-2); font-weight: 600; font-size: 1.05em; }
.explanation ul:has(.formula) { padding-left: 0; list-style: none; }
.explanation li:has(.formula) { margin-bottom: var(--sp-3); }
.explanation li > .formula { margin-bottom: var(--sp-1); }
.formula-slot, .formula-word { padding: .15rem .55rem; border-radius: 6px; }
.formula-slot { background: var(--block-blue); color: var(--block-blue-ink); font-style: italic; }
.formula-word { background: var(--card); color: var(--cinnabar); border: 1px solid color-mix(in srgb, var(--cinnabar) 35%, transparent); font-family: var(--hanzi); font-size: 1.2em; }
/* A fixed word's pinyin, when the page has typed it, and its literal
   meaning: smaller, after the characters. */
.formula-pinyin { font-family: var(--font, inherit); font-size: .72em; font-weight: 500; color: var(--ink-2); }
.formula-gloss { margin-left: .35em; font-family: var(--font, inherit); font-size: .68em; font-weight: 500; font-style: italic; color: var(--stone); }
.formula-plus { color: var(--stone); font-weight: 700; }
/* A point to practise in class (« Découvre le chinois », 2026-11-11): the
   structure in colored blocks, the word box, « À toi » with one button per
   answer, and the culture's characters to guess. */
.slot { display: inline-flex; align-items: baseline; padding: .1rem .55rem; border-radius: var(--radius-sm); font-weight: 700; }
.slots { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
.slots .word { border-bottom: 0; min-height: 0; }
.structure { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sp-2) .25rem; grid-column: 1 / -1; }
.line .text.structure { grid-column: 1 / -1; }
.line .text.structure ~ .play { justify-self: start; }
.structure .slot { padding: .1rem .4rem; }
.structure-col { display: grid; justify-items: center; gap: var(--sp-1); }
.structure-role { font-size: var(--fs-xs); font-weight: 800; white-space: nowrap; }
.structure-plus { padding-top: .1rem; font-weight: 700; color: var(--stone); }
.structure .word { border-bottom: 0; min-height: 0; }
.role-blue { color: var(--block-blue-ink); } .role-orange { color: var(--block-orange-ink); } .role-purple { color: var(--block-purple-ink); }
.role-teal { color: var(--block-teal-ink); } .role-green { color: var(--jade); } .role-grey { color: var(--ink-2); }
.word-tiles { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-2); }
.word-tile { display: grid; justify-items: center; width: 100%; min-height: 44px; padding: var(--sp-2) var(--sp-1); border: 1px solid var(--edge); border-radius: var(--radius-sm); background: var(--card); line-height: 1.2; }
/* The word box, in its point's card under the examples (Fei, 2026-11-12):
   the pinyin in front, the characters quiet, the meaning under. */
.word-box { margin: var(--sp-4) 0; padding: var(--sp-3); border: 1.5px dashed var(--line-strong); border-radius: var(--radius); background: var(--card); }
.word-box-title { margin: 0 0 var(--sp-2); font-size: var(--fs-xs); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2); }
.word-tiles { grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr)); }
.word-tile .pinyin { font-family: var(--sans); font-size: var(--fs-lg); font-weight: 800; color: var(--ink); }
.word-tile .hanzi { font-size: var(--fs-md); line-height: 1.3; color: color-mix(in srgb, var(--stone) 70%, var(--card)); }
.word-tile .tile-fr { font-family: var(--sans); font-size: var(--fs-xs); color: var(--ink-2); }
/* A word in the colour of its block (Fei, 2026-11-12): a blue word goes in the blue block. */
.word-tile[class*="block-"] { border-color: transparent; }
.word-tile[class*="block-"] .pinyin { color: inherit; }
.word-tile.block-blue { background: var(--block-blue); color: var(--block-blue-ink); }
.word-tile.block-orange { background: var(--block-orange); color: var(--block-orange-ink); }
.word-tile.block-green { background: var(--jade-2); color: var(--jade); }
.word-tile.block-purple { background: var(--block-purple); color: var(--block-purple-ink); }
.word-tile.block-teal { background: var(--block-teal); color: var(--block-teal-ink); }
.word-tile.block-grey { background: var(--paper-2); color: var(--ink-2); }
/* Its two groups when it has words seen before (Fei, 2026-11-12): the new
   ones marked « nouveau », the ones seen before in French; a tap says them
   and shows their pinyin and characters, small. */
.wb-group { margin: 0 0 var(--sp-3); }
.wb-group:last-child { margin-bottom: 0; }
.wb-label { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); font-weight: 800; color: var(--ink-2); }
.wb-hint { margin: -.3rem 0 var(--sp-2); font-size: var(--fs-xs); color: var(--ink-2); }
.wb-new { position: relative; }
.wb-new::after { content: "nouveau"; position: absolute; top: -.55rem; right: .3rem; padding: 0 .35rem; border-radius: 999px; background: var(--cinnabar); color: var(--card); font: 800 .62rem/1.4 var(--sans); }
.wb-group .word-tiles { row-gap: var(--sp-3); } /* room for the « nouveau » badges */
.wb-new .pinyin { font-size: var(--fs-md); }
.word-tile.wb-long .pinyin { font-size: var(--fs-sm); } /* wèishénme inside its tile on a phone */
.word-tile .tile-fr, .wb-recall .recall-fr { text-wrap: balance; }
.word-box .tile-fr { font-size: var(--fs-sm); font-weight: 600; } /* the French a student looks for: readable */
/* « impressionnant », « aujourd'hui » inside their tile, smaller, never cut. */
.word-box .tile-fr, .wb-recall .recall-fr { max-width: 100%; hyphens: manual; overflow-wrap: normal; }
.word-tile.wb-longfr .tile-fr { font-size: var(--fs-xs); }
.wb-recall.wb-longfr .recall-fr { font-size: var(--fs-sm); }
.word-tile:focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
.wb-recall .recall-fr { font-family: var(--sans); font-size: var(--fs-md); font-weight: 800; color: inherit; }
.wb-recall .pinyin, .wb-recall .hanzi { display: none; }
.wb-recall.is-open .recall-fr { order: 3; font-size: var(--fs-xs); font-weight: 700; color: var(--ink-2); }
.wb-recall.is-open .pinyin, .wb-recall.is-open .hanzi { display: block; font-size: var(--fs-sm); }
.wb-recall.is-open .hanzi { color: inherit; }
.word-tile[class*="block-"] .hanzi { color: inherit; opacity: .8; } /* readable on its colour, still quieter than the pinyin */
/* A long block (每天早上七点) wraps inside the card, its « + » kept. */
.structure-pair { max-width: 100%; min-width: 0; }
.structure .slot { flex-wrap: wrap; max-width: 100%; }
/* The words of an answer apart: « Zhōngguó péngyou », not run together. */
.say-it .slot .w + .w { margin-left: .25em; }
.practice-list { list-style: none; margin: 0; padding: 0; }
.practice-item { padding: var(--sp-3) 0; border-top: 1px solid var(--edge); }
.practice-item:first-child { padding-top: 0; border-top: 0; }
.practice-fr { margin: 0 0 var(--sp-2); font-weight: 700; text-align: left; hyphens: manual; -webkit-hyphens: manual; } /* no « Mau-rice », no lone « ? » */
.say-it .grammar-sentence { padding: 0; border-bottom: 0; }
.reveal > summary { display: inline-flex; align-items: center; gap: .4rem; min-height: 44px; padding: 0 var(--sp-4); border: 2px solid var(--cinnabar); border-radius: 999px; color: var(--cinnabar); font-weight: 800; font-size: var(--fs-sm); cursor: pointer; list-style: none; }
.reveal > summary::-webkit-details-marker { display: none; }
.reveal > summary:focus-visible { outline: 2px solid var(--cinnabar); outline-offset: 2px; }
.reveal[open] > summary { display: none; }
.guess { display: grid; grid-template-columns: auto auto 1fr; align-items: center; gap: var(--sp-3); margin: 0 0 var(--sp-2); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--edge); border-radius: var(--radius); }
.guess-parts { font-family: var(--hanzi); font-size: 1.15rem; color: var(--ink-2); white-space: nowrap; }
.guess-hanzi { font-family: var(--hanzi); font-size: 2.8rem; line-height: 1.1; }
.guess-answer strong { display: block; color: var(--jade); }
.guess-why { display: block; font-size: var(--fs-sm); color: var(--ink-2); }
.formula-eq { flex-basis: 100%; color: var(--ink-2); font-weight: 500; font-size: .92em; }
.lexicon-title { font-size: .85rem; color: var(--stone); }
.word-blocks { display: flex; flex-wrap: wrap; gap: .4rem; }
.wblock { display: inline-flex; flex-direction: column; align-items: center; padding: .2rem .4rem; border: 1px solid var(--line); border-radius: var(--radius); }
.wblock-pinyin, .wblock-fr { font-size: var(--fs-note); color: var(--ink-2); }
.wblock-h { border: 0; font-family: var(--hanzi); font-size: 1.4rem; color: var(--ink); }
.answer-pinyin { display: flex; align-items: center; gap: .6rem; margin-top: .4rem; color: var(--ink-2); }

/* Mini-tests and quiz */
.test { margin: 2.5rem 0 1rem; padding: 1rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2); }
.test-title { margin: 0 0 .5rem; font-size: 1.2rem; }
.test-body { display: grid; gap: .6rem; }
.test-body > p { margin: 0; }
.test-progress { font-size: var(--fs-sm); color: var(--stone); }
.item-card { display: grid; gap: .7rem; }
.item-card p { margin: 0; }
.item-task { font-weight: 700; }
.item-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.prompt-text { font-size: 1.15rem; }
.prompt-hanzi { font-family: var(--hanzi); font-size: 2.6rem; line-height: 1.2; }
.prompt-audio .play-big { min-width: 5rem; min-height: 5rem; font-size: 2rem; border-radius: 50%; background: var(--card); }
.prompt-sentence { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .6rem; }
.prompt-words { font-family: var(--hanzi); font-size: 1.5rem; }
.prompt-sentence .pinyin { flex-basis: 100%; color: var(--ink-2); }
.choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: .5rem; }
.choices-mcq { grid-template-columns: 1fr; }
.choice {
  font: inherit; min-height: 44px; padding: .6rem .8rem; cursor: pointer; text-align: center;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.choices-mcq .choice { text-align: left; }
.choice:hover:not(:disabled) { border-color: var(--cinnabar); }
.choice:disabled { cursor: default; }
.choice-hanzi { font-family: var(--hanzi); font-size: 1.8rem; }
.choice-pinyin { font-size: 1.15rem; }
.choices-radical .choice { display: flex; flex-direction: column; align-items: center; gap: .1rem; }
.choice-fr { font-size: .9rem; }
.choices-listen { grid-template-columns: 1fr; }
.choices-listen .choice { text-align: left; }
.prompt-char { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .8rem; }
.prompt-char .prompt-hanzi { background: none; border: 0; padding: 0; color: inherit; cursor: pointer; }
.choice.choice-wrong { border-color: var(--danger); background: var(--danger-bg); color: var(--danger-2); text-decoration: line-through; }
.choice.choice-right { border-color: var(--jade); background: var(--jade-2); color: var(--jade); }
.feedback { padding: .6rem .8rem; border-radius: var(--radius); margin: 0; animation: rise var(--dur) var(--ease) both; }
.feedback p { margin: 0; }
.feedback-ok { background: var(--jade-2); color: var(--jade); }
.feedback-correct { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .8rem; }
.feedback-correct p { margin: 0; }
.correction-title { margin-top: 1.5rem; }
.feedback-hint { background: var(--block-orange); color: var(--block-orange-ink); }
.feedback-reveal { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .8rem; background: var(--danger-bg); color: var(--danger-2); }
.feedback-reveal .small { flex-basis: 100%; color: inherit; opacity: .85; }
.order-label { font-size: var(--fs-sm); color: var(--stone); }
.blocks { display: flex; flex-wrap: wrap; gap: .4rem; min-height: 3.4rem; padding: .3rem; border-radius: var(--radius); }
.answer-line { border: 1px dashed var(--stone); background: var(--card); }
.oblock-btn {
  font: inherit; display: inline-flex; flex-direction: column; align-items: center; min-width: 44px; min-height: 44px;
  padding: .2rem .6rem; cursor: pointer; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink);
}
.oblock-btn:hover:not(:disabled) { border-color: var(--cinnabar); }
/* A block is dragged with the finger: the page does not scroll from it. */
.oblock-btn:not(:disabled) { touch-action: none; user-select: none; -webkit-user-select: none; }
.oblock-moving .oblock-btn { opacity: .35; }
.oblock-ghost {
  position: fixed; top: 0; left: 0; z-index: 60; margin: 0; pointer-events: none;
  border-color: var(--cinnabar); box-shadow: var(--shadow);
}
.oblock-h { font-family: var(--hanzi); font-size: 1.4rem; line-height: 1.3; }
.oblock-btn .pinyin { font-size: var(--fs-note); color: var(--ink-2); }
.oblock-bad .oblock-btn { border-color: var(--danger); background: var(--danger-bg); }
.reveal-answer { display: grid; gap: .4rem; }
.reveal-fr { font-size: 1.15rem; }
.self-rate { display: flex; flex-wrap: wrap; gap: .4rem; }
.self-rate .btn { min-height: 56px; }
.rated { color: var(--ink-2); }
.match-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.match-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.match-col { display: grid; gap: .4rem; align-content: start; }
.match-head { font-size: var(--fs-sm); color: var(--stone); text-align: center; }
.cell-wrap { display: flex; }
.cell {
  font: inherit; flex: 1; min-width: 0; min-height: 44px; padding: .35rem .4rem; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .1rem; position: relative;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink);
  overflow-wrap: anywhere; text-align: center;
}
.cell:hover { border-color: var(--stone); }
.cell[aria-pressed="true"] { border-color: var(--cinnabar); box-shadow: 0 0 0 2px var(--cinnabar) inset; }
.cell-hanzi { font-family: var(--hanzi); font-size: 1.35rem; line-height: 1.3; }
.cell-pinyin { font-size: var(--fs-note); color: var(--ink-2); }
.cell-fr { font-size: .95rem; }
.cell-locked { cursor: default; border-color: var(--jade); background: var(--jade-2); color: var(--jade); }
.match-quiz .cell-locked { border-color: var(--stone); background: var(--paper-2); color: var(--ink-2); }
.cell-link { position: absolute; top: .1rem; left: .3rem; font-size: var(--fs-xs); font-weight: 600; }
.cell-hint { border-color: var(--danger); animation: shake 300ms var(--ease); }
.item-lines .cell-hanzi { font-size: 1.15rem; }
.score { font-size: 1.15rem; }
.score-big { font-family: var(--serif); font-size: 1.6rem; }
.step-nav { margin: 0; }
.step-nav p { margin: 0; }
.quiz-results h2 { font-size: 1.2rem; }
.correction { padding-left: 1.2rem; display: grid; gap: .8rem; }
.correction li { padding-left: .2rem; }
.correction p { margin: .1rem 0; }
.correction .mark { font-weight: 700; }
.correction .right > .correction-head .mark, .links .right { color: var(--jade); }
.correction .wrong > .correction-head .mark, .links .wrong { color: var(--danger); }
.correction-answer { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; }
.answer-py { color: var(--ink-2); }
.links { margin: .1rem 0; padding-left: 1.1rem; }
.links li { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
@keyframes shake {
  0%, 100% { transform: none; }
  30% { transform: translateX(-3px); }
  60% { transform: translateX(3px); }
}
/* Three narrow columns on a phone: smaller characters, tighter cells. */
@media (max-width: 400px) {
  .match-grid.cols-3 { gap: .25rem; }
  .cols-3 .cell { padding: .3rem .15rem; }
  .cols-3 .cell-hanzi { font-size: 1.1rem; }
  .cols-3 .cell-pinyin, .cols-3 .cell-fr { font-size: .75rem; }
  .test { padding: .8rem .6rem; }
}
/* Lines stack: the dialogue lines, then their translations. */
@media (max-width: 480px) {
  .item-lines .match-grid { grid-template-columns: 1fr; }
}

/* Characters: a deck, one character at a time, on the page itself (no
   frame around it), everything from the same left edge: the character and
   its pinyin side by side, its meaning, its strokes in a big square, one
   big button to write it; the key and the origin wait behind « En savoir
   plus ». */
.char-deck { --deck: min(22rem, 100%); }
.char-card { display: grid; justify-items: start; gap: .6rem; }
.char-card[hidden] { display: none; }
.char-card .stroke, .char-card .stroke-actions, .char-card .practice-nav, .char-card .char-tip { width: var(--deck); }
.char-big { margin: 0; font-family: var(--hanzi); font-size: 7rem; line-height: 1.2; color: var(--ink); }
.char-card .char-big { display: grid; place-items: center; width: var(--deck); aspect-ratio: 1; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.char-head { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem .7rem; margin: 0; }
.char-small { font-family: var(--hanzi); font-size: 1.6rem; line-height: 1; }
.char-card .char-small { font-size: 2.6rem; }
.char-pinyin { font-size: 1.6rem; font-weight: 700; color: var(--violet); }
.char-fr { margin: -.3rem 0 .2rem; font-size: 1.15rem; color: var(--ink-2); }
.char-praise { margin: 0; font-weight: 700; color: var(--jade); animation: rise var(--dur) var(--ease) both; }
.char-radical { margin: .6rem 0 0; color: var(--ink-2); font-size: .95rem; }
/* The tip to remember a character, in sight under it (Fei, 2026-11-12). */
.char-tip { display: flex; gap: var(--sp-3); align-items: center; margin: var(--sp-2) 0 var(--sp-3); padding: var(--sp-3); border-radius: var(--radius); background: var(--paper-2); text-align: left; box-sizing: border-box; }
.char-tip .char-ancient img { width: 72px; height: 72px; }
.char-tip .char-etym { margin: 0; font-size: 1rem; line-height: 1.45; color: var(--ink); }
.char-tip .char-radical { margin: var(--sp-2) 0 0; font-size: 1rem; }
.char-key { font-family: var(--hanzi); font-size: 1.3rem; color: var(--cinnabar); }
.char-label { display: inline-block; margin-right: .2rem; padding: 0 .4rem; border-radius: 999px; background: var(--paper-2); color: var(--stone); font-size: var(--fs-xs); font-weight: 600; letter-spacing: .03em; text-transform: uppercase; vertical-align: .1em; }
.char-origin { display: flex; gap: .7rem; align-items: flex-start; margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line); }
.char-origin p { margin: 0; }
.char-ancient { flex: none; margin: 0; display: grid; justify-items: center; gap: .15rem; }
.char-ancient img { width: 56px; height: 56px; object-fit: contain; padding: 4px; border-radius: var(--radius-sm); background: #fbf8f2; }
.char-ancient figcaption { font-size: var(--fs-xs); color: var(--stone); text-align: center; max-width: 5rem; }
.char-etym { font-size: .92rem; line-height: 1.45; color: var(--ink-2); }
#apres-caracteres { scroll-margin-top: 1rem; }
/* A small strokes box under a character of /mot. */
.char-strokes { display: grid; justify-items: center; gap: .4rem; width: max-content; }
.char-strokes .stroke { width: 10rem; }
.char-strokes .stroke-actions .btn { min-height: 44px; }
/* The tracing square: a faint 米 grid, as in a Chinese exercise book. */
.stroke {
  width: min(18rem, 100%); aspect-ratio: 1; border: 1px solid var(--line); border-radius: var(--radius); touch-action: none;
  background-color: var(--card);
  background-image:
    linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line)),
    linear-gradient(45deg, transparent calc(50% - .5px), color-mix(in srgb, var(--line) 60%, transparent) calc(50% - .5px), color-mix(in srgb, var(--line) 60%, transparent) calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(-45deg, transparent calc(50% - .5px), color-mix(in srgb, var(--line) 60%, transparent) calc(50% - .5px), color-mix(in srgb, var(--line) 60%, transparent) calc(50% + .5px), transparent calc(50% + .5px));
  background-size: 100% 1px, 1px 100%, 100% 100%, 100% 100%;
  background-position: center, center, center, center;
  background-repeat: no-repeat;
}
.stroke-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.stroke-actions .btn { min-height: 56px; }

.learn .culture-image { display: block; max-width: 100%; border-radius: var(--radius); margin: 1rem 0; }
.culture-figure, .video { margin: 1.25rem 0; }
.culture-figure figcaption, .video figcaption { margin-top: .4rem; font-size: .9rem; color: var(--stone); }
.culture-figure .culture-image { margin: 0; }
.video { max-width: 40rem; }
.video-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: var(--radius); overflow: hidden; background: var(--paper-2); border: 1px solid var(--line); }
.video-frame iframe { display: block; width: 100%; height: 100%; border: 0; }
.video-play {
  font: inherit; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .5rem;
  width: 100%; height: 100%; min-height: 44px; padding: 1rem; border: 0; background: none; color: var(--ink); cursor: pointer; text-align: center;
}
.video-play:hover .video-icon, .video-play:focus-visible .video-icon { background: var(--cinnabar-2); }
.video-icon {
  display: grid; place-items: center; width: 3.5rem; height: 3.5rem; border-radius: 50%;
  background: var(--cinnabar); color: var(--on-accent); font-size: 1.3rem; padding-left: .2rem;
}
.culture-add { margin-top: .75rem; }

.fiche-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .8rem; align-items: flex-start; }
.fiche-pdf { flex-basis: 100%; }
.fiche-pdf a { display: inline-flex; align-items: center; min-height: 44px; overflow-wrap: anywhere; }
.fiche-image img { display: block; width: 9rem; height: 9rem; object-fit: cover; border: 1px solid var(--line); border-radius: var(--radius); }

@media (max-width: 480px) {
    /* The line takes the full width; its buttons sit in a row below it. */
    .dialogue-lines .line { grid-template-columns: 1fr; }
    .dialogue-lines .line-actions { grid-column: 1; grid-row: auto; flex-direction: row; flex-wrap: wrap; }
}

/* Tone chips. */
.tone {
  --tone-c: var(--tone-5);
  display: inline-block; margin: .1rem .3rem .1rem 0; padding: .1rem .55rem;
  border-radius: 999px; font-size: .8rem; font-weight: 500; white-space: nowrap;
  color: var(--tone-c);
  background: color-mix(in srgb, var(--tone-c) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--tone-c) 40%, transparent);
}
.tone-1 { --tone-c: var(--tone-1); }
.tone-2 { --tone-c: var(--tone-2); }
.tone-3 { --tone-c: var(--tone-3); }
.tone-4 { --tone-c: var(--tone-4); }
.tone-5 { --tone-c: var(--tone-5); }

/* htmx states */
.htmx-request .btn-primary, .btn-primary.htmx-request { opacity: .7; cursor: progress; }
.htmx-swapping { opacity: 0; transition: opacity 120ms ease-out; }
.htmx-settling { animation: rise var(--dur) var(--ease) both; }

@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* Tracing area: one large square, one character at a time; its tabs are
   the characters, a tick on each one written. */
.practice-tabs { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1rem; }
.panel-head .practice-tabs { margin: 0; }
.practice-tab { position: relative; font: inherit; font-family: var(--hanzi); font-size: 1.5rem; min-width: 3rem; min-height: 3rem; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card); color: var(--ink-2); cursor: pointer; transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); }
.practice-tab:hover { border-color: var(--line-strong); }
.practice-tab[aria-selected="true"] { border-color: var(--cinnabar); color: var(--cinnabar-2); background: var(--accent-bg); }
.practice-tab.done::after { content: "✓"; position: absolute; top: -.45rem; right: -.45rem; display: grid; place-items: center; width: 1.2rem; height: 1.2rem; border-radius: 50%; background: var(--ink); color: #fff; font-family: var(--sans); font-size: .7rem; font-weight: 700; }
.practice-panel { display: grid; justify-items: start; gap: .8rem; }
.practice-panel[hidden] { display: none; }
.practice-stroke { width: min(22rem, 100%); }
.practice-nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; width: min(22rem, 100%); margin-top: .2rem; }
.practice-nav:has(> :only-child) { justify-content: center; }
.practice-nav .btn { white-space: nowrap; }
.btn-round { min-width: 44px; padding-inline: 0; }
.memo-info { margin: 0; }
.memo-info .char-fr { color: var(--stone); }

/* Order questions: the teacher cuts a line between its words. */
.cut-row { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem; margin: .3rem 0 .6rem 1.6rem; }
.cut-word { font-size: 1.2rem; padding: 0 .1rem; }
.cut { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; min-height: 32px; border-radius: var(--radius); cursor: pointer; color: var(--line); }
.cut input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cut:has(input:checked) { color: var(--cinnabar); background: var(--accent-bg); }
.cut:has(input:focus-visible) { outline: 2px solid var(--cinnabar); }

/* Je retiens: the characters to learn to write are underlined. */
u.learn { text-decoration: underline 2px var(--cinnabar); text-underline-offset: .2em; }
/* In « Ton kit de survie », the characters of the lesson's « J'écris les
   caractères » (Fei, 2026-11-12). */
.memo-lines .known { color: var(--cinnabar); }
.memo { margin: 0 0 var(--sp-5); }

/* Ancient forms: black ink drawings, kept legible on the dark theme. */
.ancient-line img, .ancient-option img { background: #fbf8f2; border-radius: 4px; object-fit: contain; vertical-align: middle; }
.ancient-options { display: flex; flex-wrap: wrap; gap: .6rem; }
.ancient-option { display: grid; justify-items: center; gap: .2rem; padding: .4rem; border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer; font-size: .8rem; }
.ancient-option:has(input:checked) { border-color: var(--cinnabar); }
.ancient-option input { margin: 0; }
.ancient-none { display: grid; place-items: center; width: 56px; height: 56px; font-size: 1.6rem; color: var(--stone); }
.culture-heading { margin: var(--sp-5) 0 var(--sp-2); font-family: var(--serif); font-size: 1.25rem; }
.choices-ancient .choice { display: flex; flex-direction: column; align-items: center; gap: .1rem; }
.prompt-picture img { width: 120px; height: 120px; object-fit: contain; background: #fbf8f2; border-radius: var(--radius); padding: 6px; }
.ancient-thumb { background: #fbf8f2; border-radius: 4px; object-fit: contain; vertical-align: middle; }

/* Words: an emoji or a picture helps younger students. */
.word-emoji { font-size: 1.9rem; line-height: 1; }
.fr .word-emoji { font-size: 1.2rem; }
.word-pic { width: 28px; height: 28px; object-fit: cover; border-radius: 4px; vertical-align: middle; }
.emoji-field input { max-width: 8rem; font-size: 1.4rem; }
.form-warning { padding: .5rem .8rem; border-radius: var(--radius); background: var(--block-orange); color: var(--block-orange-ink); }
.held-files { margin-bottom: var(--sp-5); }
.held-files p { margin: 0; }
.held-files ul { margin: var(--sp-2) 0 0; padding-left: var(--sp-5); }

.book-card { margin: 1.5rem 0; padding: 1.1rem 1.25rem; border: 1px solid var(--cinnabar); border-radius: var(--radius); background: var(--card); }
.book-card p { margin: 0 0 .6rem; }
.book-card .book-more { margin: .8rem 0 0; color: var(--ink-2); font-size: .95rem; }
.did-you-know { margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid color-mix(in srgb, var(--jade) 25%, transparent); border-radius: var(--radius); background: var(--jade-2); color: var(--ink); }
.did-you-know .dyk-title { margin: 0 0 .3rem; font-weight: 700; color: var(--jade); }
.did-you-know .prose p { margin: .2rem 0; }

/* ---- Big targets ---- */
.choice { min-height: 64px; font-size: 1.3rem; border-width: 1.5px; border-color: var(--line-strong); }
.choices { gap: 1rem; }
.item-card { gap: 1.2rem; }
.test { padding: 1.25rem; border: 1px solid var(--edge); box-shadow: var(--shadow-soft); border-radius: var(--radius-lg); background: var(--card); }
.feedback { font-size: 1.15rem; border-radius: var(--radius); }
.feedback-correct p::first-line { font-weight: 800; }
.cell { min-height: 64px; font-size: 1.2rem; }

/* The words of a lesson (Fei, 2026-11-11): to learn by ear, one line
   each. The pinyin and the French are large, the characters small under
   the pinyin; a tap anywhere on the line says the word. */
.vocab-list { list-style: none; margin: 0; padding: 0; }
.vocab-row { display: grid; grid-template-columns: auto 2.6rem max-content minmax(0, 1fr); align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-2); border-bottom: 1px solid var(--line); cursor: pointer; }
.vocab-row:last-child { border-bottom: 0; }
.vocab-row .pic { display: grid; justify-items: center; gap: .15rem; }
.vocab-row .pic img { width: 44px; height: 44px; object-fit: cover; border-radius: var(--radius-sm); display: block; }
.vw-zh { display: grid; min-width: 0; }
.vw-pinyin { font-size: var(--fs-xl); font-weight: 800; line-height: 1.2; color: var(--violet); }
.vw-hanzi { font-size: var(--fs-lg); line-height: 1.3; color: var(--ink-2); }
.vw-fr { min-width: 0; overflow-wrap: break-word; font-size: var(--fs-lg); font-weight: 800; line-height: 1.25; text-align: right; color: var(--ink); }
/* On a phone there is no room for the pinyin and the French side by side
   (they overlapped, 2026-11-13): the French goes under the characters. */
@media (max-width: 560px) {
	.vocab-row { grid-template-columns: auto 2.6rem minmax(0, 1fr); row-gap: var(--sp-1); }
	.vocab-row > .play { grid-area: 1 / 1 / 3 / 2; }
	.vocab-row > .pic { grid-area: 1 / 2 / 3 / 3; }
	.vocab-row > .vw-zh { grid-area: 1 / 3; }
	.vocab-row > .vw-fr { grid-area: 2 / 3; text-align: left; overflow-wrap: break-word; }
	.vw-pinyin { font-size: var(--fs-lg); overflow-wrap: anywhere; }
}
.fc-pic:empty { display: none; }
.fc-pic { font-size: 4.5rem; line-height: 1; }
.fc-pic img { width: 8rem; height: 8rem; object-fit: cover; border-radius: 22px; }
.word-extra { font-size: .9em; }
/* Where a card is in its deck: « Mot n°1 sur 8 », « Phrase 2 sur 3 »,
   « Étape 1 sur 3 ». One style, readable; the number is the sheet's. */
.card-count, .trial-progress { margin: 0; font-size: var(--fs-sm); font-weight: 700; color: var(--stone); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Progress in tests. */
.test-progress-bar { height: 10px; border-radius: 999px; background: var(--line); overflow: hidden; }
.test-progress-bar span { display: block; height: 100%; background: var(--btn); border-radius: 999px; transition: width var(--dur) var(--ease); }
.test-start { display: flex; padding: 1rem 0; }
.panel-foot .test, .exo-panel .test { margin: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; }
.panel-foot .test-start, .exo-panel .test-start { padding: 0; }
.test-intro { display: grid; justify-items: start; gap: .8rem; }
.test-done .test-actions { display: flex; flex-wrap: wrap; gap: .6rem; }

/* Feedback: a small pop. */
@keyframes pop { 0% { transform: scale(.8); opacity: 0; } 60% { transform: scale(1.06); opacity: 1; } 100% { transform: scale(1); } }
.feedback-correct { animation: pop 380ms var(--ease) both; }
@media (prefers-reduced-motion: reduce) { .feedback-correct { animation: none; } }
.lesson-stars { font-size: 1.3rem; letter-spacing: .1em; color: var(--star); white-space: nowrap; }
.badge { display: inline-block; padding: .2rem .8rem; border-radius: 999px; background: var(--warm-bg); color: var(--warm-ink); font-size: .95rem; font-weight: 800; }
.cell-emoji { font-size: 2rem; line-height: 1; }
.cell-image { width: 3.2rem; height: 3.2rem; object-fit: cover; border-radius: var(--radius); }
.trace-box { display: grid; justify-items: start; gap: .5rem; }
.picture-preview { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem 1rem; }
.picture-preview img { object-fit: cover; border-radius: 6px; vertical-align: middle; }

/* Placement test */
#placement .switch { display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; font-weight: 500; }
#placement .switch input { accent-color: var(--jade); }
#placement h2 { margin-top: 2rem; }
#placement .test-actions { margin-top: 1.5rem; }
.placement .row-start td { background: var(--paper-2); font-weight: 600; }
.field-short { max-width: 10rem; }
.placement-skip { margin-top: .75rem; }
.placement-stage { font-weight: 600; color: var(--jade); margin-bottom: var(--sp-1); }
.placement-oral { margin-top: var(--gap-lg); text-align: left; }
.placement-oral > h2 { margin-bottom: var(--sp-2); }
.placement-oral-part { margin-top: var(--gap); }
details.placement-oral > summary { cursor: pointer; font-weight: 600; min-height: 44px; display: flex; align-items: center; }
.placement-offer { margin: 0; }
.placement-skills { margin-top: var(--sp-2); }
.placement-sample { margin-top: var(--gap); }
.placement-again { margin-top: var(--sp-2); }
.drafts-panel { margin: var(--sp-2) 0 var(--gap); }
.drafts-panel > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; gap: var(--sp-2); font-weight: 600; }
.placement-doc { border: 1px solid var(--line); border-radius: 12px; padding: var(--sp-2) var(--gap); margin: var(--sp-2) 0 var(--gap); }
.placement-doc-title { font-weight: 600; margin: 0; }
.placement-sample-list { margin-bottom: var(--sp-2); }
.placement-overview > h2 { margin-top: var(--gap-lg); }
/* The home page's one big action: continue the lesson, or take the
   placement test. The whole card is the button. */
/* Beside the rows of « Aujourd'hui » the card is as tall as they are: its
   content stays together in the middle, never stretched (the button grew
   with the rows). */
.hero-card {
  display: grid; justify-items: start; align-content: center; gap: .5rem; margin: 0 0 var(--sp-5); padding: var(--sp-5);
  border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow);
  border: 1px solid var(--edge); color: var(--ink); text-decoration: none;
  background-image: radial-gradient(120% 90% at 100% 0%, var(--accent-bg), transparent 60%);
}
a.hero-card { transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
a.hero-card:hover { transform: translateY(-2px); }
.hero-card p { margin: 0; }
.hero-card-kicker { text-align: left; color: var(--cinnabar-2); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
/* « Continuer » is the home page's main title: the biggest type of the page. */
.hero-card-title { font-family: var(--serif); font-size: clamp(1.9rem, 5vw, 2.6rem); font-weight: 700; line-height: 1.15; letter-spacing: -.01em; color: var(--heading); }
.hero-card-title .hanzi { display: block; margin-top: var(--sp-1); font-size: 1em; font-weight: 400; line-height: 1.2; }
.hero-card .btn { margin-top: var(--sp-3); min-width: 12rem; min-height: 56px; padding-inline: 1.6rem; }
@media (min-width: 48rem) { .hero-card { padding: var(--sp-6); } }
/* The quieter cards of the home page: reviews, a milestone to share. */
.home-card { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1rem; margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.home-card p { margin: 0; }
.milestone-card { background: var(--warm-bg); border-color: var(--gold); }
.milestone-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.milestone-actions form { margin: 0; }
.milestone-card .hint:empty { display: none; }
.access-notice { margin: 0 0 2rem; padding: 1rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.access-banner { margin: 0 0 1.2rem; padding: .75rem 1rem; border-radius: var(--radius-sm); background: var(--danger-bg); color: var(--danger-2); font-size: .95rem; }
/* The lesson's sheets and « Je veux aller plus loin »: a panel of their own. */
.further-card { display: flex; flex-direction: column; gap: .2rem; margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); color: var(--ink); text-decoration: none; }
@media (min-width: 48rem) { .further-card { padding: var(--sp-4) var(--sp-5); } }
.further-card:hover { border-color: var(--jade); }
.further-card span { color: var(--ink-2); }
.further-text summary { position: relative; display: flex; flex-direction: column; gap: .2rem; padding-right: 2rem; cursor: pointer; list-style: none; }
.further-text summary::-webkit-details-marker { display: none; }
.further-text summary::after { content: "›"; position: absolute; right: 0; top: 50%; transform: translateY(-50%); font-size: 1.8rem; line-height: 1; color: var(--cinnabar); transition: transform var(--dur) var(--ease); }
.further-text[open] summary::after { transform: translateY(-50%) rotate(90deg); }
/* With JavaScript, the tile « Plus loin » opens it: closed, it stays hidden. */
@media (scripting: enabled) { .further-text:not([open]) { display: none; } }
.further-pdf { margin: var(--sp-3) 0 0; font-weight: 700; }
.further-text[open] summary { margin-bottom: .6rem; }
.further-text .prose { color: var(--ink); }
/* The sheets: three small tiles under a quiet title, the teacher's files under them. */
.sheets-card { margin: 0 0 var(--sp-5); }
.sheets-title { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.sheet-tiles { display: grid; grid-auto-columns: minmax(0, 1fr); grid-auto-flow: column; gap: var(--sp-2); }
.sheet-tile { display: grid; justify-items: center; align-content: start; gap: 2px; min-height: 44px; padding: var(--sp-3) var(--sp-2); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); color: var(--ink); text-align: center; text-decoration: none; background-image: none; transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease); }
.sheet-tile:hover { border-color: var(--jade); transform: translateY(-2px); }
.sheet-icon { font-size: 1.7rem; line-height: 1.2; }
.sheet-tile strong { font-size: var(--fs-sm); font-weight: 800; line-height: 1.2; }
.sheet-tile small { font-size: var(--fs-xs); font-weight: 600; color: var(--stone); }
.sheets-card .fiche-list { margin-top: var(--sp-3); }
.sheet-link { display: flex; flex-direction: column; gap: .2rem; color: var(--ink); text-decoration: none; }
.sheet-link strong { color: var(--jade); }
.sheet-link:hover strong { text-decoration: underline; text-underline-offset: 3px; }
/* Source numbers, the same as on the printed sheet. */
.src-tag, .further-card .src-tag { font-family: var(--sans); font-size: var(--fs-xs); font-weight: 600; color: var(--stone); letter-spacing: .03em; white-space: nowrap; }
/* After a translation, a pill: a label, not the end of the sentence. */
.fr > span.src-tag { display: inline-block; margin-left: .4em; padding: .1em .55em; border-radius: 999px; background: var(--paper-2); line-height: 1.3; vertical-align: .12em; }
p.src-tag { margin: 0; }
.further-admin { margin-top: var(--sp-5); padding: var(--sp-4) var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.further-admin > h2:first-child { margin-top: 0; }

/* The structure helper of the editor: fields that write a structure in the
   text above it, with the structure as the student sees it. */
.structure-builder { margin: var(--sp-3) 0; border: 1px dashed var(--edge); border-radius: var(--radius); background: var(--paper-2); }
.structure-builder > summary { cursor: pointer; padding: .7rem 1rem; list-style: none; }
.structure-builder > summary::-webkit-details-marker { display: none; }
.structure-builder > summary span { display: block; font-size: var(--fs-sm); color: var(--stone); }
.structure-builder-body { display: grid; gap: var(--sp-3); padding: 0 1rem 1rem; }
.structure-builder .field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: var(--sp-3); }
.structure-builder .optional { font-weight: 400; color: var(--stone); }
.structure-builder .field-short-input input { max-width: 6rem; }
.structure-preview { padding: .6rem .8rem; border-radius: var(--radius-sm); background: var(--card); }
.structure-preview .eyebrow { margin: 0 0 .2rem; }
#placement .form .btn { justify-self: start; }
#placement .form-row { grid-template-columns: minmax(0, 24rem) auto; margin: 1.5rem 0 0; }
@media (max-width: 40rem) { #placement .form-row { grid-template-columns: 1fr; } }

/* Praise, streak and stickers */
.praise { margin: 0 0 .3rem; font-size: 1.35rem; }
.praise-zh { display: inline-block; font-family: var(--font-zh, inherit); font-size: 1.8rem; font-weight: 600; animation: grow 600ms var(--ease) both; }
.praise-py, .praise-fr { color: var(--ink-2); }
.praise-star { display: inline-block; animation: jump 700ms var(--ease) both; }
@keyframes grow { 0% { transform: scale(.6); } 50% { transform: scale(1.25); } 100% { transform: scale(1); } }
@keyframes jump { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(-.5em) rotate(15deg); } }
.streak { display: inline-block; margin: 0 0 .4rem; padding: .15rem .7rem; border-radius: 999px; background: var(--warm-bg); color: var(--warm-ink); font-size: 1.1rem; font-weight: 700; }
@media (prefers-reduced-motion: reduce) { .praise-zh, .praise-star { animation: none !important; } }
/* Where the child is in the lesson: one quiet line, the stickers won and
   the ones to win, the current block ringed. No card: it must not compete
   with the block itself. */
.child-progress { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .9rem; margin: -.4rem 0 1.5rem; }
.child-progress-head { margin: 0; font-size: .9rem; font-weight: 600; color: var(--stone); }
.stickers { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0; padding: 0; list-style: none; }
.stickers li { display: grid; place-items: center; width: 1.9rem; height: 1.9rem; border-radius: 50%; border: 1.5px dashed var(--line-strong); color: var(--stone); font-size: 1rem; }
.stickers li.won { border: 1.5px solid var(--gold); background: var(--warm-bg); font-size: 1.05rem; }
.stickers li.current { box-shadow: 0 0 0 2.5px color-mix(in srgb, var(--btn) 45%, transparent); }
.step-sticker { margin-left: .4rem; }
.sticker-won { font-size: 1.3rem; font-weight: 800; animation: pop 500ms var(--ease) both; }
@media (prefers-reduced-motion: reduce) { .sticker-won { animation: none; } }
.feedback .praise { flex-basis: 100%; }
.feedback .streak { flex-basis: auto; }

/* A celebration, in the middle of the screen: a finished test, a right answer */
.celebrate { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; background: var(--scrim); backdrop-filter: blur(2px); animation: fade-in 180ms ease-out both; cursor: pointer; }
.celebrate.out { animation: fade-out 280ms ease-in both; }
.celebrate-card { position: relative; max-width: calc(100vw - 2rem); overflow-wrap: anywhere; padding: 1.6rem 2.4rem; border-radius: 28px; background: var(--card); box-shadow: 0 18px 60px color-mix(in srgb, var(--btn) 35%, transparent); text-align: center; animation: burst 520ms cubic-bezier(.2, 1.4, .4, 1) both; }
.celebrate-big { margin: 0; font-size: clamp(3.5rem, 16vw, 6.5rem); line-height: 1.1; font-weight: 700; color: var(--btn); }
.celebrate-line { margin: .4rem 0 0; font-size: 1.5rem; font-weight: 800; color: var(--ink); }
.celebrate-sub { display: inline-block; margin: .6rem 0 0; padding: .2rem .9rem; border-radius: 999px; background: var(--warm-bg); color: var(--warm-ink); font-size: 1.2rem; font-weight: 800; }
.confetti { position: absolute; left: 50%; top: 50%; font-size: 2rem; transform: translate(-50%, -50%); animation: fly 900ms ease-out both; }
@keyframes fly { 0% { opacity: 1; transform: translate(-50%, -50%) rotate(var(--a)) translateY(0) scale(.4); } 100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a)) translateY(calc(-1 * var(--d) - 8rem)) scale(1.2); } }
@keyframes burst { 0% { transform: scale(.3); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .celebrate, .celebrate-card, .celebrate.out { animation: none; } .confetti { display: none; } }
/* The big card says the praise; the feedback keeps it for screen readers. */
.feedback .praise { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.icon { vertical-align: -0.15em; }

/* Admin messages */
.messages tr.unread td { font-weight: 600; }
.message-text { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Block instruction and oral tasks */
.block-intro { margin: 0 0 var(--sp-5); padding: .85rem 1.1rem; border-radius: var(--radius); background: var(--card); border: 1px solid var(--edge); color: var(--ink-2); box-shadow: var(--shadow-soft); }
.block-intro p { margin: .2rem 0; }
.oral-title { color: var(--cinnabar-2); }
.oral-task .prose p { margin: .2rem 0; }
/* The final task (2026-10-22): its title, the steps with their model
   sentence, the words to help, the written task. */
.task-title { margin: .2rem 0 0; font-size: var(--fs-lg); font-weight: 700; color: var(--ink); }
.task-kind { margin: var(--sp-4) 0 var(--sp-2); font-weight: 700; color: var(--ink-2); }
.task-steps { counter-reset: task-step; }
.task-steps .line { counter-increment: task-step; }
.task-step-label { display: block; font-weight: 700; color: var(--cinnabar-2); }
.task-step-label::before { content: counter(task-step) ". "; }
.task-word-list { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.task-word-list li {
  display: inline-flex; align-items: center; gap: .35rem; padding: .2rem .7rem .2rem .25rem;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
}
.task-word-list li { flex-wrap: wrap; max-width: 100%; }
.task-word-list .hanzi { font-family: var(--hanzi); font-size: 1.25rem; white-space: nowrap; }
.task-word-list .pinyin { color: var(--ink-2); }
.task-word-list .fr { color: var(--stone); font-size: var(--fs-sm); }
.task-written { margin-top: var(--sp-4); padding-top: var(--sp-2); border-top: 1px dashed var(--line); }
.task-preview { margin-top: var(--sp-4); padding: var(--sp-4); border: 1px dashed var(--line-strong); border-radius: var(--radius); background: var(--paper); }
/* The four défis of a chapter review: one card each, the défi's number
   and whether it is to do above its title. */
.defis { margin-top: var(--sp-6); }
.defis-title { margin: 0 0 var(--sp-1); font-size: var(--fs-xl); }
.defis-lead { margin: 0 0 var(--sp-4); color: var(--ink-2); }
.defi { margin-bottom: var(--sp-4); }
.defi-kicker { margin: 0 0 var(--sp-1); font-size: var(--fs-sm); color: var(--ink-2); }
.defi-audio { display: block; width: 100%; margin: var(--sp-2) 0; }
.defi-slow { margin-bottom: var(--sp-4); }
.defi-slow summary { cursor: pointer; color: var(--ink-2); }
.defi-text { margin-top: var(--sp-4); }
.defi-text summary { cursor: pointer; font-weight: 700; color: var(--ink-2); }
.defi-part { margin: var(--sp-4) 0 var(--sp-2); font-weight: 700; }
/* Défi 3's text: each word with the help it needs, the meaning of a new
   word under it. */
.reading-text { font-family: var(--hanzi); font-size: 1.6rem; line-height: 2.3; }
.reading-text p { margin: 0 0 var(--sp-2); }
.rw-new { display: inline-flex; flex-direction: column; align-items: center; vertical-align: top; line-height: 1.6; }
.rw-fr { font-family: var(--sans); font-size: var(--fs-xs); line-height: 1.1; color: var(--cinnabar-2); }
/* « Je lis » of a lesson: heard and translated once its questions are answered. */
.reading-line.playing { background: var(--panel-head); border-radius: var(--radius-sm); }
.reading-after { margin-top: var(--sp-4); }
.reading-after > :last-child { margin-bottom: 0; }
.reading-fr ol { margin: var(--sp-2) 0 0; }
.reading-questions { margin-top: var(--sp-4); padding-top: var(--sp-2); border-top: 1px dashed var(--line); }
.mission {
  margin: 0 0 var(--sp-5); padding: var(--sp-4) var(--sp-5); border: 1px solid var(--edge); border-left: 6px solid var(--cinnabar);
  border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft);
}
.mission p { margin: 0; }
.mission-title { font-weight: 700; color: var(--cinnabar-2); }
.mission-text { margin: .2rem 0 !important; font-size: var(--fs-lg); font-weight: 700; }
.mission-when { color: var(--ink-2); font-size: var(--fs-sm); }
/* In the editor: what a review takes, kept or left out. */
.recap-lesson { margin: 0 0 var(--sp-3); padding: var(--sp-2) var(--sp-4) var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius); min-width: 0; }
.recap-lesson legend { padding: 0 var(--sp-2); font-weight: 700; }
.recap-kind { margin: var(--sp-3) 0 var(--sp-1); font-size: var(--fs-note); font-weight: 700; color: var(--ink-2); }
.recap-form .chip span { font-size: 1.05rem; }
.recap-form .chip input:checked + span { background: var(--jade-2); border-color: var(--jade); color: var(--ink); }
.recap-form .chip input:not(:checked) + span { border-style: dashed; color: var(--stone); text-decoration: line-through; }
.item.copied { background: var(--paper); }
.recap-form .chip-own input + span { opacity: 1; cursor: default; border-style: solid; }
.recap-form .own-note { font-style: italic; color: var(--jade); }
.recording-list audio, .recordings audio { display: block; width: 100%; max-width: 22rem; margin: .4rem 0; }
.my-recordings:empty { display: none; }
.my-recordings { margin-top: .8rem; }
.recording-list { list-style: none; margin: .3rem 0 0; padding: 0; display: grid; gap: .5rem; }
.recording-list li { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .6rem; }
.recording-list li audio { flex: 1 1 14rem; margin: 0; }
.recording-item.unheard { border-color: var(--cinnabar); }
.rating-marks, .rating-note { flex-basis: 100%; margin: 0; }
.rating-marks { display: flex; flex-wrap: wrap; gap: .2rem .9rem; font-size: .95rem; }
.rating-note { font-style: italic; color: var(--ink-2); }
.rating-form { margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line); }
.rating-row { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .6rem; margin: 0 0 .3rem; padding: 0; border: 0; }
.rating-row legend { float: left; min-width: 7.5rem; padding: 0; font-weight: 600; }
.rating-choice { display: inline-flex; align-items: center; gap: .2rem; font-size: 1.25rem; cursor: pointer; }
.rating-form .field { margin: .5rem 0; }
.nav-count { display: inline-block; min-width: 1.3rem; margin-left: .25rem; padding: 0 .35rem; border-radius: 999px; background: var(--cinnabar); color: var(--on-accent); font-size: .75rem; line-height: 1.3rem; text-align: center; }
.nav-count[hidden] { display: none; }
.block-settings { margin-bottom: .6rem; }

/* Listening sequence: 4 numbered clips, 5 meanings to number. */
.seq-clips { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .6rem; justify-items: center; }
.seq-play { position: relative; }
.seq-play .seq-n { position: absolute; top: -.4rem; left: -.4rem; min-width: 1.7rem; height: 1.7rem; line-height: 1.7rem; border-radius: 999px; background: var(--violet); color: var(--card); font-size: 1rem; font-weight: 800; }
.seq-options { display: grid; gap: .6rem; }
.seq-card {
  font: inherit; display: flex; align-items: center; gap: .8rem; min-height: 64px; padding: .5rem .9rem; cursor: pointer; text-align: left;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--ink); font-size: 1.2rem;
}
.seq-card:disabled { cursor: default; }
.seq-badge { flex: none; display: inline-grid; place-items: center; width: 2.2rem; height: 2.2rem; border-radius: 999px; border: 2px dashed var(--line); font-weight: 800; }
.seq-card[aria-pressed="true"] { border-color: var(--violet); }
.seq-card[aria-pressed="true"] .seq-badge { border: none; background: var(--violet); color: var(--card); }
.seq-card.seq-right { border-color: var(--jade); }
.seq-card.seq-right .seq-badge { background: var(--jade); color: var(--card); }
.seq-card.seq-wrong { border-color: var(--danger); background: var(--danger-bg); }
.seq-answer { display: grid; gap: .3rem; margin: .4rem 0; padding-left: 1.6rem; }
.seq-answer li { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.seq-answer .hanzi-inline { font-size: 1.4rem; }

/* Word match: meanings with a slot each, then the pool of words. Side by
   side on a wide screen, the pool under the meanings on a phone. Once done,
   a row answered wrong shows its right word "corrigé", in jade with a mark. */
.wm { display: grid; gap: 1rem; }
@media (min-width: 720px) { .wm { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); align-items: start; } }
.wm-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.wm-row { display: flex; align-items: center; gap: .5rem; padding: .3rem; border: 2px solid transparent; border-radius: var(--radius); }
.wm-row.wm-selected { border-color: var(--violet); background: var(--violet-bg); }
.wm-row.wm-wrong { border-color: var(--danger); background: var(--danger-bg); }
.wm-row.wm-right { border-color: var(--jade); }
.wm-row.wm-fixed { border-color: var(--jade); border-style: dashed; background: var(--jade-2); }
.wm-meaning { font: inherit; font-size: 1.2rem; min-height: 56px; padding: .4rem .8rem; cursor: pointer; text-align: left; border: 1.5px solid var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--ink); overflow-wrap: anywhere; }
.wm-meaning[aria-pressed="true"] { border-color: var(--violet); }
.wm-meaning:disabled, .wm-card:disabled, .wm-empty:disabled { cursor: default; }
.wm-dots { flex: 1 1 1rem; min-width: .8rem; border-bottom: 3px dotted var(--line); }
.wm-slot { position: relative; flex: none; display: flex; }
.wm-empty { font: inherit; width: 88px; min-height: 72px; cursor: pointer; border: 2px dashed var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--stone); font-size: 1.4rem; }
.wm-empty[hidden] { display: none; }
.wm-pool { display: flex; flex-wrap: wrap; gap: .6rem; align-content: start; }
.wm-card {
  font: inherit; display: grid; justify-items: center; gap: .1rem; min-width: 88px; min-height: 72px; padding: .4rem .6rem; cursor: pointer;
  border: 2px solid var(--gold); border-radius: var(--radius); background: var(--card); color: var(--ink);
}
.wm-slot .wm-card { border-color: var(--violet); }
.wm-right .wm-slot .wm-card, .wm-fixed .wm-slot .wm-card { border-color: var(--jade); }
.wm-wrong .wm-slot .wm-card { border-color: var(--danger); }
.wm-fixed-mark { position: absolute; top: -.75rem; right: -.4rem; padding: 0 .45rem; border-radius: 999px; background: var(--jade); color: var(--card); font-size: .8rem; font-weight: 800; line-height: 1.5; white-space: nowrap; }
.wm-hanzi { font-family: var(--hanzi); font-size: 1.8rem; line-height: 1.1; }
.wm-pinyin { font-size: .9rem; color: var(--ink-2); }
@media (max-width: 400px) {
  .wm-meaning { min-height: 48px; font-size: 1.1rem; }
  .wm-empty, .wm-card { min-width: 72px; min-height: 60px; }
  .wm-empty { width: 72px; }
  .wm-hanzi { font-size: 1.5rem; }
}

/* Gaps: the sentence with its holes, pinyin under each word, then the bank
   of words. A hole is a dashed box until a word fills it; after a hint the
   wrong ones are red, once done the ones answered wrong are "corrigé". */
.gaps { display: grid; gap: 1rem; }
.gaps-sentence { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem .35rem; margin: 0; }
.gap-word { display: inline-grid; justify-items: center; min-height: 64px; align-content: end; padding: 0 .1rem; }
.gap-hanzi { font-family: var(--hanzi); font-size: 1.8rem; line-height: 1.1; }
.gap-pinyin { font-size: .9rem; color: var(--ink-2); }
.gap { position: relative; display: inline-flex; align-items: flex-end; gap: .1rem; padding: .15rem; border: 2px solid transparent; border-radius: var(--radius); }
.gap-mark { font-family: var(--hanzi); font-size: 1.8rem; line-height: 1.1; color: var(--ink); }
.gap-empty { font: inherit; min-width: 56px; min-height: 64px; cursor: pointer; border: 2px dashed var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--stone); font-size: 1.3rem; }
.gap-empty[hidden] { display: none; }
.gap-empty:disabled, .gap-card:disabled { cursor: default; }
.gap.gap-selected { border-color: var(--violet); background: var(--violet-bg); }
.gap.gap-selected .gap-empty { border-color: var(--violet); }
.gap.gap-wrong { border-color: var(--danger); background: var(--danger-bg); }
.gap.gap-right { border-color: var(--jade); }
.gap.gap-fixed { border-color: var(--jade); border-style: dashed; background: var(--jade-2); }
.gaps-bank { display: flex; flex-wrap: wrap; gap: .6rem; min-height: 44px; }
.gap-card {
  font: inherit; display: inline-grid; justify-items: center; gap: .1rem; min-width: 56px; min-height: 64px; padding: .3rem .5rem; cursor: pointer;
  border: 2px solid var(--gold); border-radius: var(--radius); background: var(--card); color: var(--ink);
}
.gap-card[aria-pressed="true"] { border-color: var(--violet); background: var(--violet-bg); }
.gap .gap-card { border-color: var(--violet); }
.gap-right .gap-card, .gap-fixed .gap-card { border-color: var(--jade); }
.gap-wrong .gap-card { border-color: var(--danger); }
.wblock-gap { border-color: var(--violet); border-width: 2px; background: var(--violet-bg); }
@media (max-width: 400px) {
  .gap-hanzi, .gap-mark { font-size: 1.5rem; }
  .gap-card { min-width: 44px; padding: .3rem .4rem; }
}

/* Dictation: one character per screen. */
.dictation-card .panel-body { display: grid; justify-items: start; gap: var(--sp-3); }
.dictation-card p { margin: 0; }
.dictation-intro, .dictation-screen, .dictation-end { display: grid; justify-items: center; text-align: center; gap: .8rem; padding: 1.5rem 1.25rem; border-radius: var(--radius-lg); background: var(--card); border: 1px solid var(--edge); box-shadow: var(--shadow-soft); }
.dictation-screen[hidden], .dictation-intro[hidden], .dictation-end[hidden], .dictation-answer[hidden] { display: none; }
.dictation-screen:focus, .dictation-end:focus { outline: none; }
.dictation-count { margin: 0; font-weight: 700; color: var(--stone); }
.dictation-fr { margin: 0; font-size: 2.2rem; font-weight: 800; line-height: 1.2; }
.dictation-pinyin { margin: 0; font-size: 1.3rem; color: var(--violet); }
.dictation-answer { display: grid; justify-items: center; gap: .5rem; }
.dictation-stroke { width: min(16rem, 70vw); }
/* The writing square: as wide as the card allows, up to 24rem. */
.dictation-write { width: min(24rem, 100%); }
.dictation-modes { display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem; }
.dictation-pad { display: grid; justify-items: center; gap: .5rem; width: 100%; }
.dictation-pad[hidden], .dictation-result[hidden], .dictation-screen .btn[hidden] { display: none; }
.dictation-write { aspect-ratio: 1; border: 2px dashed var(--line); border-radius: var(--radius); touch-action: none; }
.dictation-result { margin: 0; font-weight: 700; }
.dictation-result.is-perfect { color: var(--jade); }
.dictation-total { margin: 0; font-size: 1.2rem; font-weight: 700; }
.dictation-actions, .dictation-end .test-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem; }
.progress-reset { margin-top: var(--sp-6); }
.progress-reset button.link { font-size: var(--fs-sm); color: var(--stone); }
/* Instructions stand out: the block's first line, the grammar challenge
   (2026-11-13: a whole instruction in bold, a table in it too, read badly). */
.block-intro > :first-child, .block-intro.grammar-defi { font-weight: 700; }
/* A block's instruction is ragged right, never hyphenated: justified, its
   Chinese drifted apart on a phone. A table in it lines up like an
   explanation's, a table on every screen. */
.block-intro { text-align: left; }
.block-intro :is(p, li, th, td) { text-align: left; hyphens: manual; -webkit-hyphens: manual; }
.block-intro .word { min-height: 0; }
.block-intro table { border-collapse: collapse; margin: .4rem 0 .8rem; }
.block-intro th { font-size: var(--fs-sm); font-weight: 700; color: var(--stone); padding: 0 1rem .35rem 0; border-bottom: 1px solid var(--line); }
.block-intro td { padding: .4rem 1rem .4rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.block-intro td:first-child { color: var(--ink); }
.memo-lines .pinyin, .memo-lines .fr { white-space: pre-line; }
.grammar-review { margin-top: .5rem; }
.grammar-review summary { cursor: pointer; color: var(--cinnabar); font-weight: 600; }
.review-label { margin: .8rem 0 .2rem !important; font-weight: 700; }
.review-examples { margin: 0; padding-left: 1.4rem; display: grid; gap: .5rem; }
.review-examples .breakdown { margin-top: 0; }
/* Student text: as wide as the panel around it, ragged right. */
.learn .prose { max-width: none; }
.oblock-py { font-family: inherit; font-size: 1.1rem; }

/* Classes: the teacher's live classes, and their card on the student's home. */
.class-filter { max-width: 24rem; margin: 1.5rem 0 1rem; }
.class-pre { white-space: pre-line; }
.class-label { font-weight: 600; color: var(--ink-2); }
.class-now p, .class-entry p { margin: .3rem 0; }
/* The next class: to log once passed, to cancel, to move. */
/* The class at a glance: the last class to review, the new one (Fei, 2026-11-11). */
.class-glance { display: grid; gap: var(--sp-4); margin: var(--sp-5) 0 0; }
@media (min-width: 720px) { .class-glance { grid-template-columns: 1fr 1fr; } }
.class-glance-card { text-align: left; padding: var(--sp-4) 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.class-glance-card h2 { font-size: 1.1rem; margin: 0 0 var(--sp-2); }
.class-glance-card p { margin: 0 0 var(--sp-2); }
.class-glance-go { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3) !important; }
.class-seen { margin: 0 0 var(--sp-3); padding-left: 1.4rem; }
.class-seen li { margin: 0 0 var(--sp-1); }
.class-seen-go { font-size: .9em; white-space: nowrap; }
.class-last { margin-top: var(--sp-2); }
.class-last summary { cursor: pointer; color: var(--ink-2); }
.home-start { margin: 0 0 var(--sp-4); }
.home-start .btn { font-size: 1.1rem; padding-block: .9rem; }
.home-visio { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0 0 var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line); }
.home-visio .rules-visio { background: var(--card); color: var(--ink); border: 2px solid var(--cinnabar); }
.start-visio { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.rev-end { margin-top: var(--sp-4); }
.rev-end summary { cursor: pointer; }
.rev-end-list { margin: 0; padding-left: 1.3rem; }
.rev-end-list > li { margin: 0 0 var(--sp-2); }
.rev-end-list ul { margin: var(--sp-1) 0 0; padding-left: 1.1rem; }
.class-next-card { margin: 1.5rem 0 0; padding: 1rem 1.25rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.class-next-card.is-due { border-left: 4px solid var(--cinnabar); }
.class-next-card h2 { font-size: 1.1rem; margin: 0 0 .3rem; }
.class-next-card.is-due h2 { color: var(--danger-2); }
.class-next-card p { margin: .3rem 0; }
.class-next-when { font-size: 1.2rem; }
.class-next-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.2rem; margin-top: .8rem; }
.class-next-actions .class-move[open] { flex-basis: 100%; }
.class-move-form { display: flex; flex-wrap: wrap; align-items: end; gap: .75rem 1rem; }
.class-move-form .field { flex: 1 1 14rem; }
/* The log form: the date, what the students see, the teacher's own notes. */
.class-log-new > .hint { margin: -.4rem 0 1rem; }
.class-log-new { margin: 0 0 var(--sp-5); }
.class-log-new > summary { list-style: none; margin: 0 0 var(--sp-3); }
.class-log-new > summary::-webkit-details-marker { display: none; }
.class-log-date { max-width: 16rem; }
.class-group { display: grid; gap: 1.25rem; margin: 0; padding: .9rem 1.25rem 1.25rem; border: 1px solid var(--line); border-radius: var(--radius); min-width: 0; }
.class-group legend { padding: 0 .4rem; font-weight: 700; }
.class-group > .hint { margin-top: -.8rem; }
.class-cancelled .item-title { color: var(--stone); }
/* The presences and the word for each, in a group's log form. */
.class-presence-list { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-5); }
.class-words { display: block; }
.class-words > summary { font-weight: 700; cursor: pointer; }
.class-words[open] > summary { margin-bottom: var(--sp-3); }
.class-words > .hint { margin: 0 0 var(--sp-4); }
.class-words .field + .field { margin-top: var(--sp-4); }
.class-more > summary { cursor: pointer; font-weight: 600; color: var(--ink-2); }
.class-more[open] > summary { margin-bottom: var(--sp-4); }
.class-more .field + .field { margin-top: var(--sp-4); }
.class-planned { text-align: left; margin: 0 0 var(--sp-4); padding: var(--sp-3) var(--sp-4); border-left: 4px solid var(--cinnabar); border-radius: var(--radius-sm); background: var(--card); }
a.tag { text-decoration: none; }
/* « Les élèves » of a class page: one line per member, their payments,
   site, presences and work online. */
.pupils { margin: var(--sp-5) 0 0; }
.pupil-list { margin: 0; padding: 0; list-style: none; }
.pupil { display: grid; gap: var(--sp-1); padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); }
.pupil-remove { justify-self: start; margin: 0; }
.pupil:first-child { border-top: 0; }
.pupil p { margin: 0; }
.pupil-name { font-size: var(--fs-lg); font-weight: 700; }
.pupil-tags { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.pupil-dot { color: var(--jade); letter-spacing: .1em; }
.pupil-dot.is-absent { color: var(--danger-2); }
.panel-foot.small { margin: 0; }
.class-members, .class-slot { display: grid; gap: .5rem; margin: 0; padding: 0; border: 0; }
.class-members legend, .class-slot legend { margin-bottom: .4rem; font-size: .92rem; font-weight: 600; color: var(--ink-2); }
.member-row, .slot-row { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.slot-row { grid-template-columns: 1fr auto auto; align-items: center; }
.slot-minutes { display: inline-flex; align-items: center; gap: .4rem; }
.slot-minutes input { width: 5.5rem; }
@media (max-width: 40rem) {
  .member-row { grid-template-columns: 1fr; padding-bottom: .5rem; border-bottom: 1px solid var(--line); }
  .slot-row { grid-template-columns: 1fr 1fr; }
  .slot-row select { grid-column: 1 / -1; }
}
.class-entry { align-items: start; }
.class-memo { display: grid; gap: 1.25rem; }
.class-card { margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.class-card h2 { font-size: 1.1rem; margin: 1rem 0 .4rem; }
.class-card p { margin: .4rem 0; }
.class-card-next { margin: 0; }
.class-card .class-message { margin: .8rem 0 0; padding: .6rem .8rem; border-radius: var(--radius-sm); background: var(--tint); white-space: pre-line; }
/* « Mon calendrier » on the student's home (Fei, 2026-11-09): this week; a class with Fei is a red pill with
   its time (grey once past), a day with Chinese a green tick, today
   ringed. */
.cal-card { display: grid; gap: var(--sp-2); margin: 0; padding: var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.cal-card p { margin: 0; text-align: left; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--sp-1); text-align: center; }
.cal-nav { display: grid; grid-template-columns: 44px 1fr 44px; align-items: center; }
.cal-card p.cal-week-name { text-align: center; font-size: var(--fs-sm); font-weight: 700; color: var(--ink-2); }
.cal-arrow { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; color: var(--ink); font-size: 1.6rem; line-height: 1; text-decoration: none; }
a.cal-arrow:hover, a.cal-arrow:focus-visible { background: var(--paper-2); }
.cal-wd { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em; color: var(--stone); }
.cal-week { grid-column: 1 / -1; margin: var(--sp-2) 0 0; text-align: left; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.cal-day { display: grid; justify-items: center; align-content: start; gap: 2px; min-height: 48px; padding: 4px 0; border-radius: var(--radius-sm); background: var(--paper); }
.cal-n { font-size: 14px; font-weight: 700; line-height: 1.2; }
.cal-day.is-today { background: var(--card); box-shadow: inset 0 0 0 2px var(--cinnabar); }
.cal-day.is-today .cal-n { color: var(--cinnabar); }
.cal-cls { padding: 2px 4px; border-radius: 999px; background: var(--cinnabar); color: var(--on-accent); font-size: 11px; font-weight: 700; line-height: 1; white-space: nowrap; }
.cal-cls.is-past { background: var(--line); color: var(--ink-2); }
/* A class the teacher moved (Fei, 2026-11-09): dashed, so the student sees it changed. */
.cal-cls.is-moved { background: var(--card); color: var(--cinnabar); box-shadow: inset 0 0 0 1.5px var(--cinnabar); outline: 1.5px dashed var(--cinnabar); outline-offset: 1px; }
.cal-moved { display: inline-block; padding: 1px 8px; border-radius: 999px; background: var(--danger-bg); color: var(--danger-2); font-size: var(--fs-sm); font-weight: 700; }
.cal-ok { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 50%; background: var(--jade); color: var(--on-accent); font-size: 12px; font-weight: 700; }
.cal-sum { padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-sm); background: var(--jade-2); color: var(--jade); font-size: var(--fs-sm); font-weight: 700; }
.cal-legend { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); font-size: var(--fs-sm); color: var(--ink-2); }
.cal-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.cal-more { font-size: var(--fs-sm); }
.cal-more a { display: inline-block; padding: var(--sp-2) 0; }
@media (min-width: 48rem) { .cal-day { min-height: 56px; } .cal-n { font-size: 16px; } }
.class-chars { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0; padding: 0; list-style: none; }
.class-chars li { display: grid; justify-items: center; gap: .1rem; min-width: 4.5rem; max-width: 9rem; padding: .5rem .6rem; border: 1px solid var(--line); border-radius: var(--radius-sm); text-align: center; }
.class-chars .hanzi { font-size: 2.2rem; line-height: 1.2; }
.class-chars .fr { font-size: .85rem; color: var(--stone); }
.agenda-block h2 { font-size: 1.1rem; margin: 1.6rem 0 .5rem; }
.agenda-warn h2 { color: var(--danger-2); }
/* The charts of « En chiffres »: one thin bar a day, gray, today's in the
   accent. The gray is the stone pulled 20 % to the paper: 3:1 on the paper
   and apart from the accent in every palette. */
.trends { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.2rem 2.5rem; margin: 1.2rem 0 .4rem; }
.trend { margin: 0; }
.trend figcaption { margin-bottom: .9rem; text-align: left; hyphens: manual; }
.trend-plot { position: relative; height: 6rem; margin-left: 1.8rem; }
.trend-grid { position: absolute; left: 0; right: 0; height: 0; border-top: 1px solid var(--line); }
.trend-grid > span { position: absolute; right: calc(100% + .45rem); top: -.6em; font-size: .75rem; line-height: 1.2; color: var(--stone); font-variant-numeric: tabular-nums; }
.trend-bars { position: absolute; inset: 0; display: flex; }
.trend-day { position: relative; flex: 1; display: flex; align-items: flex-end; justify-content: center; padding: 0 1px; }
.trend-bar { width: 100%; max-width: 24px; background: color-mix(in srgb, var(--stone) 80%, var(--paper)); border-radius: 4px 4px 0 0; }
.trend-today .trend-bar { background: var(--cinnabar); }
.trend-day:hover, .trend-day.open { background: color-mix(in srgb, var(--line) 50%, transparent); }
.trend-day:hover .trend-bar, .trend-day.open .trend-bar { background: var(--stone); }
.trend-today:hover .trend-bar, .trend-today.open .trend-bar { background: var(--cinnabar-2); }
.trend-tip {
  display: none; position: absolute; bottom: calc(100% + .4rem); left: 50%; transform: translateX(-50%); z-index: 5;
  padding: .35rem .6rem; white-space: nowrap; font-size: var(--fs-sm); line-height: 1.4; color: var(--ink-2);
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm); box-shadow: var(--shadow-soft);
}
.trend-tip strong { color: var(--ink); }
.tip-start .trend-tip { left: 0; transform: none; }
.tip-end .trend-tip { left: auto; right: 0; transform: none; }
.trend-day:hover .trend-tip, .trend-day.open .trend-tip { display: block; }
.trend-x { display: flex; justify-content: space-between; margin: .3rem 0 0 1.8rem; font-size: .75rem; color: var(--stone); }
.agenda-list { display: grid; gap: .5rem; margin: 0; padding: 0; list-style: none; }
.agenda-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; padding: .6rem .8rem; border: 1px solid var(--edge); border-radius: var(--radius-sm); background: var(--card); box-shadow: var(--shadow-soft); }
.agenda-time { font-weight: 700; font-variant-numeric: tabular-nums; }
.agenda-class { display: grid; gap: .1rem; min-width: 0; }
/* « Mes cours » on the admin home: today and tomorrow, the top of the agenda
   (Fei, 2026-11-09). */
.agenda-today .panel-body { display: grid; gap: var(--sp-2); }
.agenda-today .agenda-day { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.agenda-today .agenda-day:first-child { margin-top: 0; }
.agenda-today .hint, .agenda-more { margin: 0; }
.agenda-more { margin-top: var(--sp-2); font-size: var(--fs-sm); }
@media (max-width: 40rem) {
  .class-entry { grid-template-columns: 1fr; }
  .class-entry .item-side { flex-direction: row; align-items: center; justify-content: space-between; }
}

/* Notifications: the profile's settings and the home prompt, shown by
   app.js once it knows what the device supports. */
.push-settings [hidden], .push-settings .btn[hidden], .push-prompt[hidden] { display: none; }
.push-device { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: .75rem 0; }
.push-prefs { display: grid; gap: 0; margin-top: .5rem; }
.push-prompt { margin: 0 0 1rem; }
.weekly-settings fieldset { display: grid; border: 0; margin: 0; padding: 0; min-width: 0; }
.weekly-settings legend { padding: 0; margin-bottom: .3rem; font-size: .9rem; color: var(--ink-2); }

/* /bilan: the weekly report as a page. */
.bilan-lines { padding-left: 1.2rem; }
.bilan-lines li { margin: .3rem 0; }

/* Privacy and account deletion pages; the profile's deletion form. */
.legal h2 { margin-top: 1.5rem; }
.legal ul, .legal ol { padding-left: 1.25rem; }
.legal li { margin: .3rem 0; }
/* Deleting the account: the red of the home page's band, white text, the
   button white (Jonathan, 2026-10-10). */
.account-delete { margin-top: 2.5rem; background: var(--cinnabar); border-color: var(--cinnabar); color: var(--on-accent); }
.account-delete :is(h2, .hint, label, a, a:hover) { color: var(--on-accent); }
.account-delete a { text-decoration: underline; }
.account-delete input[type="checkbox"] { accent-color: var(--on-accent); }
.account-delete input:focus { border-color: var(--on-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--on-accent) 55%, transparent); }
.account-delete .btn-danger { background: var(--on-accent); border-color: var(--on-accent); color: var(--cinnabar-2); box-shadow: 0 8px 20px -8px rgb(0 0 0 / 40%); }
.account-delete .btn-danger:hover { background: var(--paper); color: var(--cinnabar-2); }

/* /mot: the words of a shared text, each character opening its strokes. */
.mot-form .field { flex: 1 1 14rem; }
.mot-words { display: grid; gap: .8rem; margin: 1rem 0; }
.mot-word { padding: .9rem 1rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.mot-hanzi { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .2rem; margin: 0 0 .3rem; font-size: 2.2rem; line-height: 1.2; font-family: var(--hanzi); }
.mot-char summary { cursor: pointer; list-style: none; border-bottom: 2px dotted var(--line); }
.mot-char summary::-webkit-details-marker { display: none; }
.mot-char[open] { flex-basis: 100%; font-size: 1rem; }
.mot-char[open] summary { font-size: 2.2rem; display: inline-block; }
.mot-char .char-body { margin: .5rem 0; }
.mot-pinyin { margin: .2rem 0; font-size: 1.1rem; }
.py { color: var(--tone-c, var(--ink)); font-weight: 600; }
.py.tone-0 { --tone-c: var(--ink); }
.mot-glosses { margin: .2rem 0 .4rem; padding-left: 1.2rem; }

/* « Qui apprend aujourd'hui ? »: a card per learner of the family. */
.learners { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 1rem; margin: 1.5rem 0; }
.learner { display: flex; flex-direction: column; align-items: center; gap: .5rem; padding: 1.25rem .75rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); color: var(--ink); font: inherit; cursor: pointer; box-shadow: var(--shadow-soft); }
.learner:hover, .learner.current { border-color: var(--cinnabar); }
/* The same initial as the header's avatar, larger. */
.learner-seal { display: grid; place-items: center; width: 3.25rem; height: 3.25rem; border-radius: 50%; background: var(--accent-bg); color: var(--cinnabar-2); font-size: 1.4rem; font-weight: 700; line-height: 1; }
.learner-name { font-weight: 600; text-align: center; overflow-wrap: anywhere; }
.family-list { list-style: none; padding: 0; display: grid; gap: .5rem; }
.family-list li { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.family-list input[type=text] { flex: 1 1 10rem; min-width: 0; }
/* « Ajouter un enfant »: the first name, its button on the same line. */
#family > .form-row { grid-template-columns: minmax(0, 1fr) auto; }
@media (max-width: 40rem) { #family > .form-row { grid-template-columns: 1fr; } }

/* « Je répète »: the 🎤 of a word or a line, the recording, the result.
   Calm and soft, like the brand (docs/marque.md): white, fine lines,
   light type with one bold word, colour in small touches. Green says
   right, red says to redo (Jonathan); each step fades into the next. */
.repeat-btn[hidden], .repeat-result .btn[hidden] { display: none; }
.repeat-btn { font: inherit; display: inline-flex; align-items: center; justify-content: center; gap: .45rem; cursor: pointer; color: var(--ink-2); background: var(--card); border: 1px solid var(--line-strong); transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.repeat-btn:hover { border-color: var(--stone); color: var(--ink); }
.repeat-btn .hanzi { font-size: 1.15rem; line-height: 1.3; }
/* « À toi » is a full button like every other: a soft shadow of its
   colour, no edge under it. */
.repeat-card { min-height: 56px; padding: 0 1.75rem; border-radius: 999px; font-size: var(--fs-btn); font-weight: 700; background: var(--btn); border-color: transparent; color: var(--on-accent); box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--btn) 70%, transparent); }
.repeat-card:hover { background: var(--btn-hover); color: var(--on-accent); }
.repeat-card .icon { font-size: 1.25em; }
.repeat-line { min-width: 52px; min-height: 52px; padding: 0; border-radius: 50%; }
/* A part of a long line: its characters, their pinyin under them. */
.repeat-part { min-height: 52px; font-size: var(--fs-btn-sm); text-align: left; }
.part-say { display: grid; gap: .1rem; }
.part-say .hanzi { font-size: 1.2rem; font-weight: 400; line-height: 1.3; }
.part-py { font-family: var(--sans); font-size: var(--fs-note); font-weight: 600; line-height: 1.25; }
.repeat-btn.is-waiting, .repeat-btn.is-on { border-color: var(--cinnabar); color: var(--cinnabar); background: var(--card); box-shadow: none; }
.repeat-btn.is-on { animation: repeat-pulse 1.6s var(--ease) infinite; }
@keyframes repeat-pulse {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--cinnabar) 28%, transparent); }
  to   { box-shadow: 0 0 0 14px color-mix(in srgb, var(--cinnabar) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .repeat-btn.is-on { outline: 2px solid var(--cinnabar); outline-offset: 3px; } }

/* « Je me teste »: a « Je retiens » sentence in French, to say in Chinese
   from memory, one card at a time; « À toi » in « Mes nouveaux mots », a
   word from its picture and its meaning. */
.memo-test, .say-test, .role-play { margin: 0 0 var(--sp-5); }
/* The conversation's questions, for Fei's class (Fei, 2026-11-12): the
   question, 🔊 and « 🤔 Pas compris »; then « 💡 Indice » and « 👀
   Réponse ». Text only when asked. */
.role-first { margin: 0 0 var(--sp-4); }
.role-say { display: inline-grid; place-items: center; width: 56px; height: 56px; padding: 0; border: 1px solid var(--line-strong); border-radius: 50%; background: var(--card); color: var(--cinnabar); font-size: var(--fs-lg); cursor: pointer; }
.role-say:is(:hover, :focus-visible) { border-color: var(--stone); }
.role-card { gap: var(--sp-3); }
.role-row { display: flex; align-items: flex-start; gap: var(--sp-3); width: 100%; box-sizing: border-box; padding: var(--sp-3); border-radius: var(--radius); }
.role-row.role-partner { background: var(--paper); }
.role-row .role-line { flex: 1; min-width: 0; }
.role-btns { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: 0; }
.role-help { font: inherit; min-height: 44px; padding: .3rem .9rem; border: 1px solid var(--line-strong); border-radius: 999px; background: var(--card); color: var(--ink-2); font-size: var(--fs-sm); font-weight: 700; cursor: pointer; }
.role-help:is(:hover, :focus-visible) { border-color: var(--stone); color: var(--ink); }
.role-help[hidden] { display: none; }
.role-hint { margin: 0; padding: .5rem .9rem; border-radius: var(--radius-sm); background: var(--accent-bg); font-size: var(--fs-md); font-weight: 700; color: var(--ink); text-align: left; animation: repeat-in 280ms var(--ease) both; }
.role-line { display: grid; justify-items: start; gap: var(--sp-2); margin: 0; text-align: left; }
/* The pinyin over each character, aligned, and in front: bold, in violet;
   the characters under it, quieter (Fei, 2026-11-11). */
.role-ruby { font-family: var(--hanzi); font-size: 1.15rem; line-height: 2.7; color: color-mix(in srgb, var(--stone) 70%, var(--card)); }
.role-ruby ruby { margin: 0 .15em; }
.role-ruby rt { padding: 0 .18em; font-family: var(--sans); font-size: 1.05em; font-weight: 800; line-height: 1.2; color: var(--violet); }
.role-fr { font-size: var(--fs-md); color: var(--ink); }
.role-answer { width: 100%; box-sizing: border-box; margin: 0; padding: .4rem .9rem; border: 2px solid var(--jade); border-radius: var(--radius); background: color-mix(in srgb, var(--jade) 7%, var(--card)); animation: repeat-in 280ms var(--ease) both; }
.role-answer .role-ruby { margin: 0; }
.role-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2); }
/* « 📽️ Plein écran »: the role play alone, large, for the board or the
   video call (Fei, 2026-11-12). Fixed when the browser has no full screen. */
.role-play.is-full { position: fixed; inset: 0; z-index: 50; margin: 0; overflow: auto; border-radius: 0; background: var(--card); }
.role-play.is-full .panel-body { max-width: 40rem; margin: 0 auto; zoom: 1.5; }
/* A word of « À toi » is centred; its result reads left. */
.test-card.say-card { justify-items: center; text-align: center; }
.say-card .repeat-slot > * { text-align: left; }
.say-card .test-actions { justify-content: center; }
.say-end { margin: 0; font-weight: 700; color: var(--ink-2); }
.say-card .fc-pic { font-size: 3.6rem; }
.say-card .fc-pic img { width: 6.5rem; height: 6.5rem; }
.say-hint .hanzi-inline { font-family: var(--hanzi); font-size: 1.6rem; color: var(--ink); }
.panel .test-card { padding: 0; border: 0; border-radius: 0; box-shadow: none; }
.test-card { display: grid; justify-items: start; gap: .8rem; padding: 1.4rem 1.25rem 1.2rem; border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); animation: repeat-in 320ms var(--ease) both; }
.test-card[hidden] { display: none; }
.test-fr { margin: 0; max-width: 28rem; font-size: 1.45rem; font-weight: 300; line-height: 1.35; color: var(--ink); }
.test-answer { display: grid; justify-items: start; gap: .3rem; }
.test-answer[hidden] { display: none; }
.test-hanzi { margin: 0; padding: 0 .6rem; font-size: 1.8rem; line-height: 1.3; color: var(--ink); animation: repeat-in 280ms var(--ease) both; }
.test-pinyin { margin: 0; padding: .1rem .6rem; border-radius: var(--radius-sm); font-size: 1.15rem; color: var(--ink-2); animation: repeat-in 280ms var(--ease) both; }
.test-pinyin[hidden] { display: none; }
.test-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin: .2rem 0 0; }
.test-card .repeat-slot { width: 100%; }
.test-card .hint { margin: 0; text-align: left; }
.test-fr, .test-hanzi, .test-pinyin { text-align: left; }
.test-parts { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }
.test-parts li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .6rem; }
.test-part-n { font-size: var(--fs-sm); font-weight: 700; color: var(--stone); }
.test-parts .test-hanzi { padding: 0; font-size: 1.5rem; }
.test-parts .test-pinyin { padding: 0; }
.test-card .repeat-result { margin-top: .2rem; box-shadow: none; }
.test-nav { margin: 0; }

/* The line's 🎤 is its third button; a long line's parts sit under it. */
.line .repeat-parts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .2rem 1.6rem; margin: 0; }
.line .repeat-part { white-space: normal; }
.line .repeat-slot { grid-column: 1 / -1; }

.repeat-slot { width: 100%; }
.repeat-slot:empty { display: none; }
@keyframes repeat-in {
  from { opacity: 0; transform: translateY(6px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
/* The steps before the result: the countdown, « Parle ! », « Analyse en
   cours », all the same height, so that nothing jumps. */
.repeat-live { display: grid; align-content: center; justify-items: start; gap: .8rem; min-height: 7rem; margin: .8rem 0 .2rem; animation: repeat-in 280ms var(--ease) both; }
.repeat-say { margin: 0; font-size: 1.7rem; font-weight: 300; letter-spacing: .01em; color: var(--ink-2); }
.repeat-live.is-on .repeat-say { font-weight: 700; color: var(--cinnabar); }
/* The countdown: a fine ring, a light number that beats softly. */
.repeat-count { display: grid; place-items: center; width: 5.6rem; height: 5.6rem; margin: 0; border: 1.5px solid var(--line-strong); border-radius: 50%; font-size: 2.9rem; font-weight: 300; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; animation: repeat-beat 700ms cubic-bezier(.3, .7, .3, 1) both; }
@keyframes repeat-beat {
  0%   { opacity: 0; transform: scale(.82); }
  30%  { opacity: 1; transform: scale(1.07); }
  55%  { transform: scale(.98); }
  75%  { transform: scale(1.025); }
  100% { transform: scale(1); }
}
.repeat-busy { display: inline-flex; align-items: center; gap: .7rem; font-size: 1.2rem; }
.repeat-spin { width: 1.15em; height: 1.15em; border-radius: 50%; border: 2px solid var(--line); border-top-color: var(--cinnabar); animation: repeat-spin 900ms linear infinite; }
@keyframes repeat-spin { to { transform: rotate(1turn); } }
.repeat-bar { display: block; width: min(15rem, 75%); height: 3px; border-radius: 3px; background: var(--line); overflow: hidden; }
.repeat-bar span { display: block; height: 100%; border-radius: 3px; background: var(--cinnabar); transform-origin: left; transition: transform 100ms linear; }
.repeat-msg { margin: .8rem 0 0; padding: .9rem 1.1rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); color: var(--ink-2); animation: repeat-in 280ms var(--ease) both; box-shadow: var(--shadow-soft); }

/* The result: a white card, a fine stripe and a soft wash of its verdict's
   colour at the top. */
.repeat-result { position: relative; overflow: hidden; margin-top: 1rem; padding: 1.5rem 1rem 1.2rem; border: 1px solid var(--edge); border-radius: var(--radius-lg); background: linear-gradient(to bottom, var(--verdict-soft, var(--card)), var(--card) 9rem); box-shadow: var(--shadow-soft); animation: repeat-in 360ms var(--ease) both; }
.repeat-result::before { content: ""; position: absolute; inset: 0 0 auto; height: 3px; background: var(--verdict, transparent); }
.repeat-result.is-good { --verdict: var(--jade); --verdict-soft: color-mix(in srgb, var(--jade) 8%, var(--card)); }
/* Orange, not red: « say it again », never « wrong » (Fei, 2026-10-17). */
.repeat-result.is-redo { --verdict: var(--block-orange-ink); --verdict-soft: color-mix(in srgb, var(--block-orange-ink) 9%, var(--card)); }
.repeat-sorry { padding: .4rem 1rem 1rem; box-shadow: none; }
.repeat-sorry .repeat-msg { border: 0; padding: .6rem 0 .9rem; animation: none; }
.repeat-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .9rem; margin: 0; }
.repeat-badge { display: grid; place-items: center; width: 2.7rem; height: 2.7rem; border: 1.5px solid currentColor; border-radius: 50%; font-size: 1.3rem; font-weight: 600; line-height: 1; color: var(--verdict); background: var(--card); animation: repeat-pop 520ms var(--ease) 60ms both; }
@keyframes repeat-pop {
  0%   { opacity: 0; transform: scale(.6); }
  60%  { opacity: 1; transform: scale(1.08); }
  100% { transform: scale(1); }
}
/* The verdict in Chinese with its pinyin under it, then in French. */
.repeat-headline { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .7rem; line-height: 1.1; color: var(--verdict); }
.repeat-say-zh { display: inline-grid; justify-items: start; }
.repeat-zh { font-family: var(--hanzi); font-size: 2.6rem; font-weight: 400; }
.repeat-py { font-size: 1rem; font-weight: 400; color: var(--ink-2); }
.repeat-fr { font-size: 1.9rem; font-weight: 700; }
/* The syllables: soft tiles, a fine edge of their colour, coming in one
   after the other. */
.repeat-syllables { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem; margin: 1.3rem 0 1.1rem; padding: 0; }
.repeat-syl { display: grid; justify-items: center; align-content: start; gap: .2rem; min-width: 3.6rem; padding: .6rem .6rem .5rem; border-radius: var(--radius); background: var(--syl-bg); box-shadow: inset 0 0 0 1px var(--syl-edge); animation: repeat-in 380ms var(--ease) both; }
.repeat-syl.is-good { --syl-bg: color-mix(in srgb, var(--jade) 6%, var(--card)); --syl-edge: color-mix(in srgb, var(--jade) 30%, transparent); }
.repeat-syl.is-redo { --syl-bg: color-mix(in srgb, var(--block-orange-ink) 8%, var(--card)); --syl-edge: color-mix(in srgb, var(--block-orange-ink) 50%, transparent); }
.repeat-syl:nth-child(2) { animation-delay: 50ms; }
.repeat-syl:nth-child(3) { animation-delay: 100ms; }
.repeat-syl:nth-child(4) { animation-delay: 150ms; }
.repeat-syl:nth-child(5) { animation-delay: 200ms; }
.repeat-syl:nth-child(6) { animation-delay: 250ms; }
.repeat-syl:nth-child(n+7) { animation-delay: 300ms; }
.repeat-hanzi { font-family: var(--hanzi); font-size: 2.2rem; line-height: 1.15; color: var(--ink); }
.repeat-syl .py { font-size: 1rem; line-height: 1.2; color: var(--ink-2); }
.repeat-said { font-size: var(--fs-xs); line-height: 1.2; color: var(--stone); white-space: nowrap; }
.repeat-ok { font-size: 1.1rem; font-weight: 600; line-height: 1.1; color: var(--jade); }
.repeat-fix { font-size: var(--fs-xs); font-weight: 700; line-height: 1.2; color: var(--block-orange-ink); white-space: nowrap; }
.repeat-tip { display: flex; align-items: center; gap: .8rem; max-width: 30rem; margin: 0 0 1.2rem; font-size: 1.02rem; line-height: 1.55; color: var(--ink-2); text-align: left; animation: repeat-in 380ms var(--ease) 200ms both; }
.repeat-tip strong.hanzi { margin-right: .15rem; font-size: 1.35rem; font-weight: 400; color: var(--verdict); }
.is-good .repeat-tip { color: var(--jade); }
.repeat-tip-lead { display: block; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2); }
.repeat-contour { flex: none; overflow: visible; }
.repeat-contour polyline { fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.contour-model { stroke: var(--jade); }
.contour-heard { stroke: var(--danger); stroke-dasharray: 3 5; }
/* A good result's card, the one « Partager » sends: it comes in once the
   score has counted up, as a card laid on the table; a tap shares it. */
.repeat-share-card { display: block; width: min(100%, 18rem); height: auto; aspect-ratio: 1; margin: 0 0 1.2rem; border: 1px solid var(--edge); border-radius: var(--radius); background: var(--paper); box-shadow: var(--shadow-soft); cursor: pointer; animation: repeat-card-in 560ms var(--ease) 900ms both; }
@keyframes repeat-card-in {
  from { opacity: 0; transform: translateY(1rem) rotate(-2deg) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .repeat-share-card { animation: none; } }
.repeat-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0; }
.repeat-result .hint:empty { display: none; }
.repeat-result .hint { margin-top: .5rem; }
@media (max-width: 400px) {
  .repeat-result { padding: 1.3rem .7rem 1.1rem; }
  .repeat-syl { min-width: 3rem; padding: .5rem .4rem .45rem; }
  .repeat-hanzi { font-size: 1.9rem; }
  .repeat-zh { font-size: 2.2rem; }
  .repeat-fr { font-size: 1.6rem; }
  .repeat-tip { flex-direction: column; align-items: flex-start; }
}

/* Full screen (zgFocus in app.js): one box alone, to trace a character or
   to say a word or a line, zhiyin's answer with it. The whole screen on a
   phone; on a wider screen, as wide as the main column over a veil. The ×
   closes it. */
html:has(.focus-layer[open]) { overflow: hidden; }
.focus-layer {
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: transparent; color: var(--ink);
  overflow-y: auto; overscroll-behavior: contain;
}
.focus-layer::backdrop { background: rgb(20 16 12 / 45%); backdrop-filter: blur(4px); }
.focus-sheet {
  position: relative; box-sizing: border-box;
  width: min(100% - 2rem, 47rem); margin: 3vh auto; padding: 3.5rem 1.5rem 1.5rem;
  border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow);
  animation: fade-in var(--dur) var(--ease) both;
}
/* The × stays in the sheet's corner while the sheet scrolls. */
.focus-close {
  position: fixed; top: calc(3vh + .6rem); right: max(1.6rem, (100% - 47rem) / 2 + .6rem); z-index: 1;
  display: grid; place-items: center; width: 48px; height: 48px; padding: 0;
  border: 0; border-radius: 50%; background: var(--paper-2); color: var(--ink);
  font: inherit; font-size: 1.75rem; line-height: 1; cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}
.focus-close:hover { background: var(--line); }
.focus-status { margin: 1rem 0 0; font-size: 1.5rem; font-weight: 700; color: var(--jade); animation: rise var(--dur) var(--ease) both; }
.focus-status[hidden] { display: none; }
@media (max-width: 48rem) {
  .focus-sheet { width: 100%; min-height: 100%; margin: 0; padding: 4rem 1rem 1.5rem; border-radius: 0; box-shadow: none; }
  .focus-close { top: .6rem; right: .6rem; }
}
/* The box in the layer drops its own frame and what leads elsewhere. */
.focus-body > .in-focus { width: 100%; max-width: none; margin: 0; padding: 0; border: 0; border-radius: 0; box-shadow: none; background: none; }
/* A test card keeps its « Phrase suivante → »: it moves on full screen. */
.in-focus [data-practice-go]:not(.test-nav *), .in-focus .practice-nav, .in-focus .char-tip, .in-focus .char-praise { display: none; }
/* A character: its square as wide as the column, everything else starting
   at the square's left edge. */
.focus-body > .char-body { display: grid; justify-content: center; justify-items: start; gap: .8rem; }
.focus-body > .char-body .stroke { max-width: none; }
.focus-body > .char-body .stroke-actions { width: auto; justify-content: flex-start; }
/* A line: bigger, its buttons in a row under it. */
.focus-body > .line { grid-template-columns: 1fr; gap: .6rem; }
.focus-body > .line .text { font-size: clamp(1.7rem, 6vw, 2.1rem); }
.focus-body > .line .fr { font-size: 1.15rem; }
.focus-body > .line .line-actions { grid-column: 1; grid-row: auto; flex-direction: row; flex-wrap: wrap; }
/* The line is the whole sheet: no frame while it plays, it would cross its
   pinyin; its words still light up one by one. */
.focus-body > .line.playing { outline: none; background: none; }

/* The page head is a solid band of carbon, the text light (Jonathan,
   2026-10-04: no shade one has to guess; 2026-10-05: carbon, lighter on
   the eye than the ink). Everything in it is recolored here, last, so it
   wins over each block's own colors. */
.page-head {
  --head-soft: color-mix(in srgb, var(--on-carbon) 72%, var(--carbon));
  --head-accent: color-mix(in srgb, var(--cinnabar) 50%, var(--on-carbon));
  --head-line: color-mix(in srgb, var(--on-carbon) 35%, var(--carbon));
}
.page-head :is(h1, h1 .hanzi, .title-zh, .lesson-title h1 .hanzi, label) { color: inherit; }
.page-head :is(.page-sub, .hub-cheer, .child-progress-head, .title-pinyin, .muted, .hint, .step-sum, .lesson-state, p) { color: var(--head-soft); }
/* The lesson page's pinyin: under the characters, large and clear, a
   beginner reads it (Fei, 2026-11-11). */
.learn.hub .page-head .title-pinyin { display: block; margin-top: 2px; font-size: var(--fs-md); font-weight: 600; letter-spacing: .02em; color: var(--on-carbon); opacity: .9; white-space: normal; }
.page-head :is(.eyebrow, a:not(.btn), summary) { color: var(--head-accent); }
.page-head .back a { color: var(--head-soft); }
.page-head .back a:hover, .page-head a:not(.btn):hover { color: var(--on-carbon); }
.page-head .stickers li { border-color: var(--head-line); color: var(--head-soft); }
.page-head .stickers li.won { border-color: var(--gold); background: var(--warm-bg); }
.page-head :is(.btn-ghost, input, textarea, select) { color: var(--ink); }

/* The ambiances of the home page's red band (partials/ambiances.html):
   the lanterns swing from the top, in the side margins of a wide screen,
   one in the corner on a phone; at the foot, Maya's dragon (.dragon-*,
   static/dragon.js) or the celestial palace (.sky, .wk-*): a sea of clouds
   the colour of the paper below, the Southern Heavenly Gate, a pavilion,
   and Sun Wukong on his cloud, from edge to edge. */
.hero-landing { position: relative; overflow: hidden; }
.sprites { position: absolute; width: 0; height: 0; overflow: hidden; }
.lantern {
  position: absolute; top: 0; z-index: 1; width: 56px; height: auto; pointer-events: none;
  transform-origin: 50% 0; animation: lantern-swing 4.6s ease-in-out infinite alternate;
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--gold) 60%, transparent));
}
.lantern-a { left: calc((100% - 62rem) / 4 - 28px); }
.lantern-b { right: calc((100% - 62rem) / 4 - 28px); animation-duration: 5.3s; animation-delay: -2.1s; }
@media (max-width: 79.99rem) {
  .lantern-a { display: none; }
  .lantern-b { right: 6px; width: 38px; }
}
@keyframes lantern-swing { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
.lantern-cord { stroke: var(--gold); stroke-width: 2; }
.lantern-body { stroke: var(--gold); stroke-width: 1.2; }
.lantern-rib { fill: none; stroke: var(--btn-edge); stroke-width: 1.2; opacity: .6; }
.lantern-cap, .lantern-knot { fill: var(--gold); stroke: var(--btn-edge); stroke-width: 1; }
.lantern-tassel { fill: none; stroke: var(--gold); stroke-width: 1.6; stroke-linecap: round; }
.dragon-strip { grid-column: 1 / -1; height: clamp(110px, 14vw, 180px); margin: var(--sp-4) calc(50% - 50vw) calc(-1 * var(--band-pad)); pointer-events: none; }
.dragon { display: block; width: 100%; height: 100%; overflow: visible; }
.dragon-body, .dragon-head { fill: var(--gold); stroke: var(--btn-edge); stroke-width: 1.6; stroke-linejoin: round; }
.dragon-belly { fill: var(--paper); }
.dragon-scutes { fill: none; stroke: var(--gold); stroke-width: 1.4; }
.dragon-scales { fill: none; stroke: var(--btn-edge); stroke-width: 1.3; stroke-opacity: .5; stroke-linecap: round; }
.dragon-spikes, .dragon-tail, .dragon-mane, .dragon-beard, .dragon-mouth, .dragon-nostril { fill: var(--btn-edge); }
.dragon-leg-line { fill: none; stroke: var(--btn-edge); stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; }
.dragon-leg { fill: none; stroke: var(--gold); stroke-width: 6.5; stroke-linecap: round; stroke-linejoin: round; }
.dragon-claws { fill: none; stroke: var(--paper); stroke-width: 2.2; stroke-linecap: round; }
.dragon-teeth, .dragon-eye { fill: var(--paper); }
.dragon-pupil { fill: var(--ink); }
.dragon-brow { fill: none; stroke: var(--btn-edge); stroke-width: 2; stroke-linecap: round; }
.dragon-horn, .dragon-whisker { fill: none; stroke: var(--paper); stroke-linecap: round; }
.dragon-horn { stroke-width: 3.4; }
.dragon-whisker { stroke-width: 1.6; }
.dragon-pearl-glow { fill: var(--gold); opacity: .4; }
.dragon-pearl-core { fill: var(--paper); }
@media (prefers-reduced-motion: reduce) { .lantern { animation: none; } }
.sky { --wk-w: clamp(118px, 14vw, 190px); grid-column: 1 / -1; position: relative; height: clamp(180px, 20vw, 240px); margin: var(--sp-4) calc(50% - 50vw) calc(-1 * var(--band-pad)); pointer-events: none; }
.sky svg { position: absolute; overflow: visible; }
.sky-bank { left: 0; bottom: 0; width: 100%; height: 60px; }
.sky-gate { bottom: 26px; right: 9%; width: clamp(150px, 20vw, 260px); height: auto; }
.sky-pavilion { bottom: 34px; left: 8%; width: clamp(64px, 9vw, 120px); height: auto; }
.sky :is(.sky-pillar, .sky-beam, .sky-body, .sky-roof, .sky-pearl, .sky-plaque) { stroke: var(--ink); stroke-width: 1.6; stroke-linejoin: round; }
.sky-pillar, .sky-body { fill: var(--btn-edge); }
.sky-beam, .sky-roof, .sky-pearl { fill: var(--gold); }
.sky-tiles { fill: none; stroke: var(--btn-edge); stroke-width: 1.4; stroke-opacity: .55; }
.sky-finial { stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; }
.sky-plaque { fill: var(--ink); stroke: var(--gold); }
.sky-plaque-text { fill: var(--gold); font-family: var(--hanzi); font-size: 13px; letter-spacing: 2px; }
.bank-cloud { fill: var(--paper); }
.bank-edge, .bank-curl { fill: none; stroke: var(--gold); stroke-width: 2; stroke-linecap: round; }
.wukong {
  bottom: 30px; left: 0; width: var(--wk-w); height: auto;
  animation: wukong-travel 18s ease-in-out infinite;
}
@keyframes wukong-travel {
  0% { left: 0; transform: scaleX(1); }
  46% { left: calc(100% - var(--wk-w)); transform: scaleX(1); }
  50% { left: calc(100% - var(--wk-w)); transform: scaleX(-1); }
  96% { left: 0; transform: scaleX(-1); }
  100% { left: 0; transform: scaleX(1); }
}
.wk-bob { animation: wk-bob 1.4s ease-in-out infinite alternate; }
@keyframes wk-bob { from { transform: translateY(-5px); } to { transform: translateY(5px); } }
.wk-swing { transform-box: fill-box; transform-origin: 100% 100%; animation: wk-sway 1.6s ease-in-out infinite alternate; }
.wk-tail-g { animation-duration: 1.1s; }
@keyframes wk-sway { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
.wk-cape { transform-box: fill-box; transform-origin: 100% 0; animation: wk-cape .9s ease-in-out infinite alternate; }
@keyframes wk-cape { from { transform: skewY(-7deg) scaleX(.94); } to { transform: skewY(5deg) scaleX(1.06); } }
.wukong :is(.wk-cape, .wk-boot, .wk-cloud, .wk-skirt, .wk-torso, .wk-sash, .wk-fur, .wk-face, .wk-iris) { stroke: var(--ink); stroke-width: 1.6; stroke-linejoin: round; }
.wukong :is([class$="-line"], .wk-tail, .wk-feather, .wk-leg, .wk-staff, .wk-staff-end, .wk-arm, .wk-band, .wk-trail, .wk-stripes, .wk-stroke, .wk-cloud-curl) { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.wukong [class$="-line"] { stroke: var(--ink); }
.wk-trail { stroke: var(--paper); stroke-width: 3; }
.wk-tail-line { stroke-width: 6.5; }
.wk-tail { stroke: var(--tone-2); stroke-width: 3.8; }
.wk-cape { fill: var(--btn-edge); }
.wk-feather-line { stroke-width: 5; }
.wk-feather { stroke: var(--star); stroke-width: 2.8; }
.wk-leg-line { stroke-width: 8.5; }
.wk-leg { stroke: var(--warm-bg); stroke-width: 5.5; }
.wk-boot, .wk-pupil { fill: var(--ink); }
.wk-cloud { fill: var(--paper); stroke: var(--gold); }
.wk-cloud-curl { stroke: var(--gold); stroke-width: 1.6; }
.wk-skirt { fill: var(--star); }
.wk-stripes { stroke: var(--ink); stroke-width: 1.8; }
.wk-torso, .wk-iris { fill: var(--gold); }
.wk-sash { fill: var(--btn-edge); }
.wk-staff-line { stroke-width: 7; }
.wk-staff { stroke: var(--btn-edge); stroke-width: 4.2; }
.wk-staff-end { stroke: var(--gold); stroke-width: 4.2; stroke-linecap: butt; }
.wk-arm-line { stroke-width: 7.5; }
.wk-arm { stroke: var(--gold); stroke-width: 4.6; }
.wk-fur { fill: var(--tone-2); }
.wk-face { fill: var(--warm-bg); }
.wk-stroke { stroke: var(--ink); stroke-width: 1.5; }
.wk-band-line { stroke-width: 5.5; }
.wk-band { stroke: var(--gold); stroke-width: 3.2; }
@media (prefers-reduced-motion: reduce) {
  .wukong { animation: none; left: 30%; }
  .wk-bob, .wk-swing, .wk-cape { animation: none; }
}


/* Cours en groupe (/groupes and /admin/groupes): one card per group, its
   places as a row of dots, the first ones it needs to start ringed. */
.group-details { white-space: pre-line; }
.group-dots { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0 var(--sp-2); }
.group-dot { width: 18px; height: 18px; border-radius: 50%; border: 2px dashed var(--edge); background: var(--paper); }
.group-dot.is-needed { border: 2px solid var(--cinnabar); }
.group-dot.is-taken { border: 2px solid var(--cinnabar); background: var(--cinnabar); }
.group-state { margin: 0; }
.group-join > summary { list-style: none; display: inline-flex; }
.group-join > summary::-webkit-details-marker { display: none; }
.group-join[open] > summary { margin-bottom: var(--sp-4); }
.group-join .form { display: grid; gap: var(--sp-4); }
/* A field one tap away in a short form: a quiet link, the field under
   it once open. */
.group-rules-more { margin-top: var(--sp-3); }
.group-rules-more > summary { display: inline-flex; min-height: 44px; align-items: center; color: var(--cinnabar); font-weight: 600; cursor: pointer; list-style: none; }
.group-rules-more > summary::-webkit-details-marker { display: none; }
.group-rules-more > summary::after { content: " ›"; margin-left: .3rem; transition: transform var(--dur) var(--ease); }
.group-rules-more[open] > summary::after { transform: rotate(90deg); }
.form-more > summary { display: inline-flex; min-height: 44px; align-items: center; color: var(--cinnabar); font-weight: 600; cursor: pointer; list-style: none; }
.form-more > summary::-webkit-details-marker { display: none; }
.form-more[open] > summary { margin-bottom: var(--sp-2); }
.group-level-ask { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--ink-2); text-align: left; }
.group-level-ask a { font-weight: 700; }
.group-join-title { margin: 0 0 var(--sp-2); font-size: var(--fs-lg); }
.group-trial { margin: 0 0 var(--sp-4); padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--cinnabar) 9%, var(--card)); color: var(--ink); text-align: left; hyphens: manual; -webkit-hyphens: manual; }
.group-card .panel-foot .form-success { margin: 0; }
.groups-admin .group-admin { margin-top: var(--sp-5); }
.groups-admin .panel-foot > summary { cursor: pointer; font-weight: 600; }
.groups-admin .panel-foot[open] > summary { margin-bottom: var(--sp-4); }
.group-form { display: grid; gap: var(--sp-4); }
.group-size { margin: 0; padding: 0; border: 0; }
.group-size legend { margin-bottom: .4rem; font-size: .92rem; font-weight: 600; color: var(--ink-2); }
.group-size .slot-row { grid-template-columns: auto auto; justify-content: start; gap: var(--sp-4); }
.group-size label { display: inline-flex; align-items: center; gap: .4rem; }
.group-size input { width: 5rem; }
.group-seats { margin: var(--sp-3) 0 0; padding: 0; list-style: none; }
.group-seat { display: flex; align-items: start; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) 0; border-top: 1px solid var(--line); }
.group-seat p { margin: 0; }
.group-seat-main { min-width: 0; overflow-wrap: anywhere; }
.group-note { font-size: var(--fs-sm); color: var(--ink-2); font-style: italic; }
.group-seat .hint, .group-seat .group-note { text-align: left; }
.group-seat form { flex-shrink: 0; }
.group-seat .btn { white-space: nowrap; hyphens: manual; }
.group-size label { white-space: nowrap; }
.group-form .check { text-align: left; }
.group-legend { margin: 0 0 var(--sp-2); text-align: left; }
/* The group classes' band on the home page, under the hero. */
.groups-teaser { position: relative; max-width: none; margin: 0; padding: var(--sp-5); border: 2px solid var(--cinnabar); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); }
.groups-teaser > p { margin: 0 0 var(--sp-3); }
.groups-teaser-list { display: grid; gap: var(--sp-2); margin: var(--sp-4) 0; padding: 0; list-style: none; }
.groups-teaser-list a { position: relative; display: grid; gap: 2px; padding: var(--sp-3) 2.5rem var(--sp-3) var(--sp-4); border: 1px solid var(--edge); border-radius: var(--radius-sm); background: var(--panel-head); color: inherit; text-decoration: none; }
/* A slot is a link to its sign-up: an arrow says so. */
.groups-teaser-list a::after { content: "›"; position: absolute; right: 1rem; top: 50%; transform: translateY(-50%); font-size: 1.6rem; line-height: 1; color: var(--cinnabar); }
@media (min-width: 48rem) { .groups-teaser-list { grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); } }
.groups-teaser-list a:hover { border-color: var(--cinnabar); }
.groups-teaser-when { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--cinnabar-2); }
.groups-teaser-name { font-weight: 700; hyphens: manual; -webkit-hyphens: manual; text-align: left; }
.groups-teaser-state { font-size: var(--fs-sm); color: var(--ink-2); }
.group-rules-list { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.group-rules-h { margin: var(--sp-4) 0 var(--sp-2); font-size: var(--fs-md); text-align: left; }
.group-rules-h:first-child { margin-top: 0; }
.group-rules-list li { display: flex; gap: var(--sp-2); text-align: left; }
/* The pack of 12 classes: the gift stands out from the other rules. */
.group-rules-list .group-rule-gift { padding: var(--sp-2) var(--sp-3); border-left: 3px solid var(--cinnabar); background: color-mix(in srgb, var(--cinnabar) 7%, transparent); border-radius: var(--radius-sm, 6px); }
.group-pack { margin-top: var(--sp-1) !important; color: var(--ink-2); }
.group-pack strong { color: var(--ink); }
.group-rules-check { align-items: flex-start; text-align: left; }
.group-rules-check input { margin-top: .2rem; }
.groups-teaser { text-align: left; hyphens: manual; }
/* « Nouveau : cours en petit groupe » first and big (Fei, 2026-10-31), 一起学 after. */
.groups-teaser-new { display: inline-block; margin: 0 0 var(--sp-2); padding: 4px 14px; border-radius: 999px; background: var(--cinnabar); color: var(--on-accent); font-size: var(--fs-sm); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; transform: rotate(-3deg); }
.groups-teaser-title { margin: 0 0 var(--sp-2); font-size: clamp(2rem, 7vw, 2.8rem); font-weight: 300; line-height: 1.1; }
.groups-teaser-title strong { font-weight: 800; color: var(--cinnabar); }
.groups-teaser-hook { font-size: var(--fs-lg); font-weight: 700; }
.groups-teaser-hook .groups-teaser-zh { display: block; margin-top: .2rem; font-weight: 400; line-height: 1.4; }
.groups-teaser-hook .hanzi { font-size: 1.2em; color: var(--cinnabar); white-space: nowrap; }
.groups-teaser-hook .groups-teaser-py { font-size: var(--fs-sm); font-weight: 400; color: var(--stone); }
.groups-teaser p, .groups-teaser h2 { hyphens: manual; }
.group-price { margin-top: var(--sp-2) !important; font-size: var(--fs-md); color: var(--ink); }
.group-price strong { color: var(--cinnabar); font-size: var(--fs-lg); }
.groups-teaser-price { font-size: var(--fs-lg); }
.groups-teaser-price strong { color: var(--cinnabar); font-size: 1.5em; font-weight: 800; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
/* « Aucun créneau ne te va ? » */
.wish-choices { display: flex; flex-wrap: wrap; gap: 0 var(--sp-4); margin: 0; padding: 0; border: 0; }
.wish-choices legend { width: 100%; margin-bottom: .2rem; font-size: .92rem; font-weight: 600; color: var(--ink-2); }
.wish-choices .check { min-height: 40px; }
.groups-teaser-other { margin: var(--sp-3) 0 0 !important; font-size: var(--fs-sm); }
.groups-teaser-wish { margin: var(--sp-3) 0 0 !important; padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-sm); background: var(--panel-head); font-weight: 600; }
.trial-alt a { font-weight: 700; }
/* The lesson offered first, after the 你好 (Fei, 2026-11-11): the gift box of /signup. */
.trial-gift { padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--cinnabar) 9%, var(--card)); color: var(--ink); text-align: left; }
.trial-gift a { white-space: nowrap; }
/* /groupes: the slots by public and place. */
/* The planning of /groupes: the week in one grid, a row per start time.
   Soft tiles: red a group, green a private lesson, grey full. */
.planning { margin-top: var(--sp-5); text-align: left; scroll-margin-top: var(--sp-5); }
.planning-share-text { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); color: var(--ink-2); }
.planning-share-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0; }
/* The legend under « La semaine des cours » (Fei, 2026-11-09): where and
   the price first, then a colour per kind of class, side by side. */
.wk-prices { margin: 0; padding: var(--sp-3) var(--sp-3) 0; font-size: var(--fs-sm); font-weight: 700; color: var(--ink-2); text-align: left; }
.wk-legend { list-style: none; margin: 0; padding: var(--sp-2) var(--sp-3) 0; display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font-size: var(--fs-sm); font-weight: 700; line-height: 1.3; }
.wk-legend li { display: flex; align-items: center; gap: var(--sp-2); }
.wk-sw { flex: none; width: 18px; height: 18px; border-radius: 6px; }
.wk-grid { display: grid; grid-template-columns: 2.6rem repeat(var(--wk-days, 7), minmax(0, 1fr)); gap: var(--sp-1); padding: var(--sp-3); }
.wk-day { text-align: center; font-size: var(--fs-xs); font-weight: 700; color: var(--ink-2); padding-bottom: var(--sp-1); }
.wk-time { display: flex; align-items: center; font-size: 11px; font-weight: 600; color: var(--ink-2); opacity: .8; }
.wk-cell { display: grid; gap: var(--sp-1); min-height: 64px; border-radius: 12px; }
.wk-cell, .wk-tile { min-width: 0; }
/* A tile (Fei, 2026-11-09: « frais, jeune, pas trop sérieux », no word):
   a candy colour per kind of class, tilted a little; a face per person
   signed up, a beating « + » for your seat, pale rings for the others. */
.wk-tile { position: relative; min-height: 64px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; border-radius: 18px; text-decoration: none; }
.wk-icon { font-size: 16px; line-height: 1; }
.wk-dots { display: flex; gap: 2px; margin-top: 2px; font-size: 0; }
.wk-dots i { width: 5px; height: 5px; border-radius: 50%; box-shadow: inset 0 0 0 1.25px currentColor; }
.wk-dots i.is-on { background: currentColor; }
.wk-marks { display: grid; grid-template-columns: repeat(2, 18px); gap: 3px; }
.wk-marks i { width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; font-size: 14px; font-style: normal; line-height: 1; }
.wk-marks .is-you { background: var(--cinnabar); color: #fff; font-size: 16px; font-weight: 900; box-shadow: 0 0 0 1.5px var(--card), 0 2px 4px rgba(0, 0, 0, .2); animation: wk-beat 1.6s ease-in-out infinite; }
.wk-marks .is-free { border: 1.5px dashed color-mix(in srgb, var(--wk-ink) 45%, transparent); box-sizing: border-box; }
@keyframes wk-beat { 50% { transform: scale(1.18); } }
.wk-sw-you { flex: none; width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; background: var(--cinnabar); color: #fff; font-style: normal; font-size: 14px; font-weight: 900; line-height: 1; }
/* A full group: « Complet » on it, in ink (a ✓ read as « my seat »). */
.wk-ok { position: absolute; top: -8px; left: 50%; translate: -50% 0; padding: 1px 5px; border-radius: 999px; background: var(--ink); color: #fff; font-size: 9px; font-weight: 800; letter-spacing: .02em; box-shadow: 0 1px 3px rgba(0, 0, 0, .2); }
/* The kinds of class, in the site's block colours (Fei, 2026-11-09:
   « pas très harmonieux avec le site »): the only bright colour is the
   vermillon of « + », your seat. */
.t-deb { --wk-bg: var(--block-orange); --wk-ink: var(--block-orange-ink); }
.t-hsk { --wk-bg: var(--block-blue); --wk-ink: var(--block-blue-ink); }
.t-lvl { --wk-bg: var(--block-teal); --wk-ink: var(--block-teal-ink); }
.t-kid { --wk-bg: var(--block-purple); --wk-ink: var(--block-purple-ink); }
.wk-tile[class*=" t-"], .wk-sw[class*=" t-"] { background: var(--wk-bg); color: var(--wk-ink); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--wk-ink) 35%, transparent), 0 4px 10px color-mix(in srgb, var(--wk-ink) 18%, transparent); }
.wk-tile[class*=" t-"] { transform: rotate(-3deg); transition: transform .25s cubic-bezier(.34, 1.56, .64, 1); }
.wk-cell:nth-child(odd) .wk-tile[class*=" t-"] { transform: rotate(3deg); }
.wk-tile[class*=" t-"]:hover { transform: rotate(0) scale(1.08); }
/* A full group keeps its colour, paler, with a ✓: a full group shows the
   classes are liked. */
.wk-tile.is-f[class*=" t-"] { opacity: .75; }
.wk-tile.is-picked { box-shadow: 0 0 0 2.5px var(--wk-ink, var(--ink)), 0 4px 10px color-mix(in srgb, var(--wk-ink, var(--cinnabar)) 25%, transparent); }
@media (prefers-reduced-motion: reduce) {
  .wk-tile[class*=" t-"], .wk-cell:nth-child(odd) .wk-tile[class*=" t-"] { transform: none; transition: none; }
  .wk-marks .is-you { animation: none; }
}
.landing-planning { margin: 0 calc(-1 * var(--sp-2)); }
.landing-planning .wk-prices { padding-top: 0; }
.landing-planning .wk-grid { padding: var(--sp-3) 0; }
.landing-planning-tip { margin: 0 0 var(--sp-4); font-size: 1.05rem; font-weight: 800; color: var(--cinnabar); text-align: center; }
.wk-choice { scroll-margin-top: var(--sp-5); }
.wk-choice > .group-card { margin-top: var(--sp-5); }
.solo-card .panel-kicker, .solo-card .group-price strong { color: var(--jade); }
.solo-how { margin: 0 0 var(--sp-2); text-align: left; }
.solo-how:last-child { margin-bottom: 0; }
.solo-prices { list-style: none; margin: var(--sp-2) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.solo-prices li { padding: 4px 10px; border-radius: 999px; background: var(--jade-2); color: var(--jade); font-size: var(--fs-sm); }
.solo-form { display: grid; gap: var(--sp-4); }
.groups-page > .group-rules { margin-top: var(--sp-5); }
@media (min-width: 640px) { .wk-tile .wk-dots i { width: 9px; height: 9px; } .wk-grid { grid-template-columns: 3.4rem repeat(var(--wk-days, 7), minmax(0, 1fr)); } }
/* The filters on one line, swiped on a phone (Fei, 2026-11-09). */
.group-filters { display: flex; gap: var(--sp-2); margin: var(--sp-4) calc(-1 * var(--sp-4)) var(--sp-3); padding: 0 var(--sp-4) var(--sp-1); overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; scroll-margin-top: var(--sp-5); }
.group-filters::-webkit-scrollbar { display: none; }
@media (min-width: 48rem) { .group-filters { flex-wrap: wrap; margin-inline: 0; padding-inline: 0; overflow: visible; } }
.group-filters a { flex: none; white-space: nowrap; padding: .45rem .9rem; border: 1px solid var(--line-strong); border-radius: 999px; background: var(--card); color: var(--ink-2); font-weight: 600; text-decoration: none; }
.group-filters a:hover { border-color: var(--cinnabar); color: var(--ink); }
.group-filters a[aria-current="page"] { border-color: var(--cinnabar); background: var(--cinnabar); color: var(--on-accent); }
.slot-ask { margin: var(--sp-4) 0 0 !important; font-weight: 700; color: var(--heading); text-align: left; }
.slot-next { margin: var(--sp-4) 0 0 !important; font-size: var(--fs-sm); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2); text-align: left; }
.slot-next + .groups-teaser-list { margin-top: var(--sp-2); }
.groups-teaser-levels { display: grid; gap: var(--sp-2); margin: var(--sp-4) 0 0; padding: 0; list-style: none; }
.groups-teaser-levels li { padding-left: var(--sp-3); border-left: 3px solid var(--cinnabar); }
.groups-teaser-facts { margin: var(--sp-3) 0 0 !important; text-align: left; }
/* The band's buttons: a public and a place each, all its slots in place. */
.slot-tabs { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: var(--sp-2); margin: var(--sp-2) 0 0; }
.slot-tab { display: grid; gap: 2px; padding: .7rem 1rem; border: 1.5px solid var(--cinnabar); border-radius: var(--radius); background: var(--card); color: var(--cinnabar-2); text-align: left; text-decoration: none; }
.slot-tab-who { font-weight: 800; }
.slot-tab-where { font-size: .9rem; font-weight: 600; color: var(--ink-2); }
.slot-tab[aria-pressed="true"] .slot-tab-where { color: inherit; }
.slot-tab:hover { background: color-mix(in srgb, var(--cinnabar) 8%, var(--card)); }
.slot-tab[aria-pressed="true"] { background: var(--cinnabar); color: var(--on-accent); }
.groups-teaser-who { margin: var(--sp-4) 0 0; font-size: var(--fs-md); color: var(--heading); text-align: left; }
.groups-teaser-who + .groups-teaser-list { margin-top: var(--sp-2); }
.group-wishes { margin-bottom: var(--sp-5); }
.solo-admin .label { margin: var(--sp-4) 0 var(--sp-2); }
.solo-admin .label:first-child { margin-top: 0; }
.solo-hour-form { display: grid; gap: var(--sp-2); justify-items: start; margin-top: var(--sp-3); }
.solo-ask-actions { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
/* The lessons seen in a log form: a chapter to open, a lesson to tick,
   its blocks shown once it is ticked. */
.log-lessons { margin: 0; padding: 0; border: 0; min-width: 0; }
.log-lessons legend { padding: 0; font-weight: 600; margin-bottom: var(--sp-1); }
.log-chapter { border-top: 1px solid var(--line); padding: var(--sp-2) 0; }
.log-chapter > summary { cursor: pointer; font-weight: 600; }
.log-others > .log-chapter { margin-left: var(--sp-4); }
.log-others > .log-chapter > summary { font-weight: 500; }
.log-lesson-list { list-style: none; margin: var(--sp-2) 0 0; padding: 0; display: grid; gap: var(--sp-2); }
.log-blocks { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: var(--sp-1) 0 0 var(--sp-5); font-size: var(--fs-sm); }
.log-lesson:not(:has(> label input:checked)) .log-blocks { display: none; }

/* « Comme à l'examen »: a question as on the exam's paper, the pinyin over
   each character; 3 pictures side by side even on a phone; a group's
   elements as a list, each with a menu of letters, as on the answer sheet. */
.exam { display: grid; gap: var(--sp-4); }
.exam [lang="zh"] { font-family: var(--hanzi); }
.exam rt { font-family: var(--sans); font-size: .5em; font-weight: 400; color: var(--ink-2); }
.exam-text, .exam-ask { margin: 0; font-size: 1.4rem; line-height: 2.2; }
.exam-clip { display: grid; gap: var(--sp-2); justify-items: start; }
.exam-play .play-big { min-width: 4rem; min-height: 4rem; font-size: 1.6rem; border-radius: 50%; background: var(--card); }
.exam-heard { padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm); background: var(--panel-head); font-size: 1.25rem; line-height: 2.1; }
.exam-line { margin: 0; }
.exam-line.playing { color: var(--cinnabar-2); }
.exam-speaker { font-family: var(--sans); font-size: var(--fs-xs); color: var(--ink-2); }
.exam-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.exam-choices:not(.exam-pictures) { grid-template-columns: 1fr; }
.exam-l3 .exam-choices { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.exam-choice {
  display: flex; align-items: center; gap: var(--sp-2); min-width: 0; padding: var(--sp-2) var(--sp-3);
  border: 1.5px solid var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--ink); font: inherit; text-align: left;
}
button.exam-choice { min-height: 64px; cursor: pointer; }
button.exam-choice:hover:not(:disabled) { border-color: var(--cinnabar); }
button.exam-choice:disabled { cursor: default; }
.exam-pictures .exam-choice { flex-direction: column; justify-content: center; text-align: center; }
.exam-choice.choice-right { border-color: var(--jade); background: var(--jade-2); }
.exam-choice.choice-wrong { border-color: var(--danger); background: var(--danger-bg); }
.exam-letter { font-weight: 700; color: var(--ink-2); }
.exam-emoji { font-size: 48px; line-height: 1; }
.exam-image { width: 100%; max-width: 8rem; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-sm); }
.exam-alt {
  display: grid; place-items: center; width: 100%; min-height: 5rem; padding: var(--sp-2);
  border: 2px dashed var(--line-strong); border-radius: var(--radius-sm); font-size: 1rem; color: var(--ink-2);
}
.exam-words { font-size: 1.3rem; line-height: 2; }
.exam-rows { display: grid; margin: 0; padding: 0; list-style: none; }
.exam-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px dashed var(--line); }
.exam-n { min-width: 1.2rem; font-weight: 700; }
.exam-sentence, .exam-row .exam-clip { flex: 1 1 12rem; }
.exam-sentence { font-size: 1.3rem; line-height: 2.1; }
.exam-pick {
  min-width: 4.5rem; min-height: 44px; margin-left: auto; padding: 0 var(--sp-2); font: inherit; font-size: 1.1rem;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--card); color: var(--ink);
}
.exam-right .exam-pick { border-color: var(--jade); background: var(--jade-2); color: var(--jade); }
.exam-wrong .exam-pick { border-color: var(--danger); background: var(--danger-bg); color: var(--danger-2); text-decoration: line-through; }
.exam-fix { font-weight: 700; color: var(--jade); }
@media (max-width: 400px) {
  .exam-emoji { font-size: 40px; }
  .exam-choice { padding: var(--sp-2); }
}

/* The student's menu (Fei, 2026-11-09): four tabs, an icon and a word each,
   fixed at the bottom of the phone's screen, a row under the header on a
   computer. The tab of the page is red. It replaces the header's « Mes
   leçons ». */
body:has(.tabbar) .nav-home:not(.nav-home-admin) { display: none; }
.tabbar { display: grid; grid-template-columns: repeat(4, 1fr); background: var(--card); }
.tabbar a {
  position: relative; display: grid; justify-items: center; align-content: center; gap: 2px; min-height: 56px; padding: var(--sp-1) 0;
  color: var(--stone); font-size: var(--fs-xs); font-weight: 700; text-decoration: none; background-image: none;
}
.tabbar a:hover { color: var(--ink); }
.tab-ic { font-size: 22px; line-height: 1.2; filter: grayscale(1); opacity: .75; }
.tabbar a[aria-current="page"] { color: var(--cinnabar); }
.tabbar a[aria-current="page"] .tab-ic { filter: none; opacity: 1; }
.tabbar a[aria-current="page"]::before { content: ""; position: absolute; top: 0; left: 30%; right: 30%; height: 3px; border-radius: 0 0 3px 3px; background: var(--cinnabar); }
/* The class of the day with Fei: a red border, the visio's button. */
.today .class-today { border: 2px solid var(--cinnabar); }
.tab-badge.tab-dot { min-width: 10px; width: 10px; height: 10px; padding: 0; top: 4px; }
.tab-badge { position: absolute; top: 2px; left: 56%; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--cinnabar); color: var(--on-accent); font-size: 11px; line-height: 18px; }
@media (max-width: 47.99rem) {
  .tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; padding: 0 var(--sp-1) env(safe-area-inset-bottom); border-top: 1px solid var(--edge); box-shadow: 0 -4px 16px rgba(0, 0, 0, .06); }
  body:has(.tabbar) { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
}
@media (min-width: 48rem) {
  .tabbar { grid-template-columns: repeat(4, minmax(0, 10rem)); justify-content: center; border-bottom: 1px solid var(--edge); }
  .tabbar a { display: flex; justify-content: center; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); }
  .tab-ic { font-size: 18px; }
  .tabbar a[aria-current="page"]::before { top: auto; bottom: 0; left: 15%; right: 15%; border-radius: 3px 3px 0 0; }
  .tab-badge { position: static; }
}

/* « Réviser »: the two ways to review, a card each. */
.reviser-cards { display: grid; gap: var(--sp-5); }
.reviser-card { display: grid; justify-items: start; gap: var(--sp-3); padding: var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius-lg); background: var(--card); box-shadow: var(--shadow-soft); }
.reviser-card h2 { margin: 0; font-size: var(--fs-lg); }
.reviser-card p { margin: 0; color: var(--ink-2); text-align: left; }

/* The calendar feed's link, on the agenda page. */
.agenda-feed-steps { display: grid; gap: var(--sp-2); padding-left: var(--sp-5); margin: var(--sp-3) 0; text-align: left; }
.agenda-feed-url { width: 100%; margin-top: var(--sp-1); padding: .5rem .7rem; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-sm); background: var(--paper); }
/* The site writing in her Google Agenda (Fei, 2026-11-10): the form one
   field under the other, the old subscription link folded under it. */
.gcal-form { display: grid; gap: var(--sp-2); justify-items: start; margin: var(--sp-3) 0; }
.gcal-form input[type="email"] { width: 100%; max-width: 22rem; padding: .5rem .7rem; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); font: inherit; }
.gcal-robot { text-align: left; }
.gcal-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
details.agenda-feed { margin-top: var(--sp-5); }
details.agenda-feed > summary { cursor: pointer; color: var(--ink-2); font-size: var(--fs-sm); }

/* « Moi » (Fei, 2026-11-09): the progress at a glance on top, the account's
   settings folded under « Réglages du compte ». */
.me-card { display: grid; gap: var(--sp-3); }
.me-card h2 { margin: 0; }
.me-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.me-stats li { display: grid; justify-items: center; gap: 2px; padding: var(--sp-3) var(--sp-1); border-radius: var(--radius-sm); background: var(--paper); text-align: center; }
.me-stats strong { font-size: 1.5rem; line-height: 1.1; }
.me-stats li > span { font-size: var(--fs-sm); color: var(--ink-2); }
.settings { margin-top: var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); }
.settings > summary { display: flex; align-items: center; gap: var(--sp-2); min-height: 52px; padding: 0 var(--sp-4); font-weight: 700; cursor: pointer; list-style: none; }
.settings > summary::-webkit-details-marker { display: none; }
.settings > summary::after { content: "›"; margin-left: auto; font-size: 1.4rem; color: var(--stone); transition: transform var(--dur) var(--ease); }
.settings[open] > summary::after { transform: rotate(90deg); }
.settings[open] { padding-bottom: var(--sp-4); }
.settings > .card { margin-inline: var(--sp-3); }

/* The students' reviews (avis, Fei, 2026-11-09): the stars in vermillon,
   the brand's one vivid colour, never gold; what they like as small
   chips. The form at /avis, the review on the teacher's page, and a card
   each on the home page, three side by side on a wide screen. */
.avis-stars { margin: 0; color: var(--cinnabar); font-size: 1.35rem; line-height: 1; letter-spacing: .08em; }
.avis-stars-off { color: var(--line-strong); }
.avis-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); margin: 0; padding: 0; list-style: none; }
.avis-chips li { padding: .1rem .6rem; border: 1px solid var(--line); border-radius: 999px; background: var(--paper-2); color: var(--ink-2); font-size: var(--fs-note); }
.avis-text, .avis-text p { margin: 0; text-align: left; hyphens: manual; -webkit-hyphens: manual; }
.avis-sign { margin: 0; font-weight: 700; color: var(--heading); }
.avis-mine { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-5); }
.avis-mine > * { margin: 0; }
.avis-mine-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); font-size: var(--fs-lg); }
.avis-change > summary { list-style: none; }
.avis-change > summary::-webkit-details-marker { display: none; }
.avis-change[open] > summary { margin-bottom: var(--sp-4); }
.avis-step { display: grid; gap: var(--sp-2); min-width: 0; margin: 0; padding: 0; border: 0; }
.avis-step legend, .avis-step > label { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); padding: 0; margin-bottom: var(--sp-2); font-size: var(--fs-md); font-weight: 700; color: var(--heading); }
.avis-step .muted { font-size: var(--fs-sm); font-weight: 400; }
.avis-step-n { display: inline-grid; place-items: center; width: 1.7rem; height: 1.7rem; border-radius: 50%; background: var(--cinnabar); color: var(--on-accent); font-size: .9rem; line-height: 1; }
/* The five stars are radio buttons: the one chosen and those before it
   turn vermillon. */
.avis-pick { display: flex; gap: var(--sp-1); }
.avis-star { position: relative; display: inline-grid; place-items: center; min-width: 44px; min-height: 44px; cursor: pointer; }
.avis-star input { position: absolute; opacity: 0; pointer-events: none; }
.avis-star-glyph { font-size: 2.75rem; line-height: 1; color: var(--line-strong); transition: color var(--dur) var(--ease), transform var(--dur) var(--ease); }
.avis-star:has(input:checked) .avis-star-glyph,
.avis-star:has(~ .avis-star input:checked) .avis-star-glyph { color: var(--cinnabar); }
.avis-star:hover .avis-star-glyph { transform: scale(1.12); }
.avis-star input:focus-visible + .avis-star-glyph { outline: 2px solid var(--cinnabar); outline-offset: 2px; border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: reduce) { .avis-star-glyph { transition: none; } .avis-star:hover .avis-star-glyph { transform: none; } }
.avis-tags .chip span { font-family: var(--sans); font-size: var(--fs-sm); font-weight: 600; padding-inline: var(--sp-3); border-radius: 999px; }
.avis-consent { display: grid; gap: var(--sp-1); }
.avis-row .avis-stars { display: inline; font-size: 1rem; }
.avis-row .avis-chips { margin: var(--sp-1) 0; }
.avis-quotes { display: grid; gap: var(--sp-4); margin: var(--sp-4) 0 0; padding: 0; list-style: none; }
@media (min-width: 48rem) { .avis-quotes { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.avis-quote { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-5); border: 1px solid var(--edge); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-soft); }
.avis-quote .avis-sign { margin-top: auto; }

/* The teacher's calendar (Fei, 2026-11-09): the agenda's, and a student's
   on the fiche. On a phone, the days with a class, one under the other; from
   48rem, a grid of weeks, Monday first. A class is a pill: red to come,
   striped à noter, dashed when moved, struck when cancelled, grey when
   logged. Tapping one opens its box (#cal-choice) in the focus layer. */
.acal { display: grid; gap: var(--sp-3); }
.acal > p { margin: 0; }
.acal-nav { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.acal-head { display: none; }
.acal-week { display: grid; gap: var(--sp-2); margin: 0; }
.acal-week-label { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.acal-grid { display: grid; gap: var(--sp-2); }
.acal-day { display: grid; align-content: start; gap: var(--sp-1); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--edge); border-radius: var(--radius-sm); background: var(--card); }
.acal-day.is-empty:not(.is-today) { display: none; }
.acal-day.is-today { box-shadow: inset 0 0 0 2px var(--cinnabar); }
.acal-day.is-past { background: var(--paper); }
.acal-date { margin: 0; font-size: var(--fs-sm); font-weight: 700; text-align: left; }
.acal-day.is-today .acal-date { color: var(--cinnabar); }
.acal-n { display: none; }
.acal-ev { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 6px; width: 100%; padding: 6px 10px; border: 1px solid transparent; border-radius: 10px; background: var(--cinnabar); color: var(--on-accent); font: inherit; font-size: var(--fs-sm); line-height: 1.3; text-align: left; text-decoration: none; cursor: pointer; }
.acal-ev:hover, .acal-ev:focus-visible { filter: brightness(1.1); text-decoration: none; }
.acal-time { font-weight: 800; font-variant-numeric: tabular-nums; }
.acal-name { min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.acal-state { font-size: var(--fs-xs); font-weight: 800; letter-spacing: .02em; }
/* The tasks left on a class (Fei, 2026-11-11): 📝 🔔 💬, on the pill even on a phone, and at the top of its box. */
.acal-todo { margin-left: auto; padding: 0 4px; border-radius: 999px; background: var(--card); font-size: var(--fs-xs); line-height: 1.4; letter-spacing: 1px; white-space: nowrap; }
.acal-ev.is-moved { border: 2px dashed var(--on-accent); }
.acal-ev.is-log { background: var(--danger-bg); color: var(--danger-2); border-color: var(--danger-2); }
.acal-ev.is-cancelled { background: transparent; color: var(--stone); border-color: var(--line-strong); text-decoration: line-through; }
.acal-ev.is-cancelled .acal-state { text-decoration: none; }
.acal-ev.is-done { background: var(--paper-2); color: var(--ink-2); }
.acal-legend { margin: 0; }
.acal-choice:empty { display: none; }
.acal-pick { margin: 0; text-align: left; }
.acal-pick .panel-body { display: grid; gap: var(--sp-3); }
.acal-pick .panel-body > * { margin: 0; }
.acal-confirm { display: grid; gap: var(--sp-2); list-style: none; padding: 0; }
.acal-confirm > * { margin: 0; }
.acal-confirm textarea { width: 100%; }
.acal-pick-when { font-size: var(--fs-lg); }
.acal-move { display: flex; flex-wrap: wrap; align-items: end; gap: var(--sp-2); }
.acal-move .field { margin: 0; }
@media (min-width: 48rem) {
  .acal-head, .acal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--sp-1); }
  .acal-head { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--stone); text-align: center; }
  .acal-week-label { margin: 0; }
  .acal-day, .acal-day.is-empty:not(.is-today) { display: grid; min-height: 84px; padding: 6px; }
  .acal-label { display: none; }
  .acal-n { display: block; font-size: 15px; }
  .acal-ev { padding: 4px 6px; font-size: var(--fs-xs); }
}
/* The student's fiche: the next class first, large. */
.student-next { display: grid; gap: 2px; margin: 0 0 var(--sp-3); padding: var(--sp-3) var(--sp-4); border: 1px solid var(--edge); border-left: 4px solid var(--cinnabar); border-radius: var(--radius-sm); background: var(--card); box-shadow: var(--shadow-soft); }
.student-next p { margin: 0; text-align: left; }
.student-next-label { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--cinnabar); }
.student-next-when { font-size: var(--fs-lg); font-weight: 800; }
.student-cal { margin: 0 0 var(--sp-5); }
.acal-who { flex-basis: 100%; font-size: var(--fs-xs); font-weight: 600; opacity: .9; }
/* The agenda's week as a timetable (Fei, 2026-11-09: « un calendrier où je
   vois l'ensemble de mes cours »): a column per day on a real time scale,
   an hour line every --tt-hour, each class a block as long as it lasts,
   side by side with those at the same time (--lane of --lanes). An empty
   half hour shows a + on hover: tapped, it adds a class there. A class is
   dragged to another time (app.js). Wider than a phone, it scrolls
   sideways in its own box; the page does not. The planning's slots,
   paler, are those offered on the home page. */
.acal-tt-scroll { overflow-x: auto; margin: 0 calc(-1 * var(--sp-1)); padding: 0 var(--sp-1) var(--sp-1); }
.acal-tt { --tt-q: 14px; display: grid; grid-template-columns: 2.6rem repeat(7, minmax(84px, 1fr)); grid-template-rows: auto calc(var(--tt-rows) * var(--tt-q)); min-width: 680px; column-gap: 0; user-select: none; -webkit-user-select: none; }
.acal-tt-day { display: grid; gap: 3px; align-content: start; margin: 0 2px 6px; padding: 4px 0; border-radius: 8px; text-align: center; font-size: var(--fs-xs); font-weight: 700; color: var(--ink-2); }
.acal-tt-day b { font-size: var(--fs-sm); color: var(--ink); }
.acal-tt-day.is-today { background: var(--cinnabar); color: var(--on-accent); }
.acal-tt-day.is-today b { color: var(--on-accent); }
.acal-tt-hours, .acal-tt-body { display: grid; grid-template-rows: repeat(var(--tt-rows), var(--tt-q)); }
.acal-tt-hours, .acal-tt-corner { position: sticky; left: 0; z-index: 2; background: var(--paper); }
.panel .acal-tt-hours, .panel .acal-tt-corner { background: var(--card); }
.acal-tt-hours span { font-size: 11px; font-weight: 700; color: var(--stone); transform: translateY(-.55em); padding-right: 6px; text-align: right; }
.acal-tt-hours span:first-child { transform: none; }
.acal-tt-body { position: relative; border-left: 1px solid color-mix(in srgb, var(--line-strong) 45%, transparent); background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--line-strong) 45%, transparent) 0 1px, transparent 1px calc(4 * var(--tt-q))), var(--card); }
.acal-tt-body:last-child { border-right: 1px solid color-mix(in srgb, var(--line-strong) 45%, transparent); }
.acal-tt-body.is-past { background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--line-strong) 45%, transparent) 0 1px, transparent 1px calc(4 * var(--tt-q))), var(--paper); }
.acal-tt-body.is-today { background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--line-strong) 45%, transparent) 0 1px, transparent 1px calc(4 * var(--tt-q))), color-mix(in srgb, var(--cinnabar) 5%, var(--card)); }
.acal-tt-slot { grid-column: 1; display: grid; place-items: center; margin: 0; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--cinnabar); font: inherit; font-weight: 800; cursor: pointer; }
.acal-tt-slot.is-past { cursor: default; }
.acal-tt-slot:not(.is-past):hover { background: color-mix(in srgb, var(--cinnabar) 10%, transparent); }
.acal-tt-slot:not(.is-past):hover::after { content: "+"; }
.acal-tt-block { grid-column: 1; z-index: 1; justify-self: start; display: grid; width: calc(100% / var(--lanes)); margin-left: calc(100% * var(--lane) / var(--lanes)); min-height: 0; padding: 1px 2px; box-sizing: border-box; }
.acal-tt .acal-ev, .acal-tt .acal-offer { min-height: 0; height: 100%; overflow: hidden; align-content: start; }
.acal-tt .acal-ev { display: grid; gap: 0; padding: 3px 6px; border-radius: 8px; font-size: var(--fs-xs); line-height: 1.25; box-shadow: 0 1px 2px rgb(0 0 0 / .12); }
.acal-tt .acal-name { font-size: var(--fs-sm); line-height: 1.2; }
.acal-tt .acal-ev { position: relative; }
/* In the week's grid, the tasks sit in the pill's corner: a short class shows them too (Fei, 2026-11-11). */
.acal-tt .acal-todo { position: absolute; top: 1px; right: 1px; margin: 0; }
.acal-tt .acal-ev[data-drag] { cursor: grab; touch-action: manipulation; }
.acal-tt .acal-ev.is-extra { border: 2px solid var(--gold, var(--on-accent)); }
.acal-tt-day .acal-ev { font-size: 11px; padding: 2px 4px; }
.acal-tt .is-dragging { opacity: .35; }
.acal-tt-ghost { grid-column: 1; z-index: 3; display: grid; align-content: start; margin: 1px 2px; padding: 3px 6px; border: 2px dashed var(--cinnabar); border-radius: 8px; background: color-mix(in srgb, var(--cinnabar) 18%, var(--card)); color: var(--ink); font-size: var(--fs-xs); font-weight: 800; pointer-events: none; }
html.acal-dragging, html.acal-dragging * { cursor: grabbing !important; }
.acal-offer { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 4px; padding: 3px 5px; border: 1px dashed currentColor; border-radius: 8px; font: inherit; font-size: 11px; font-weight: 700; text-align: center; text-decoration: none; opacity: .9; cursor: pointer; }
.acal-offer:hover { opacity: 1; text-decoration: none; }
.acal-dots { flex-basis: 100%; justify-content: center; flex-wrap: wrap; }
.acal-dots i { width: 7px; height: 7px; }
.acal-offer.is-g, .acal-key-g { color: var(--cinnabar); background: color-mix(in srgb, var(--cinnabar) 8%, var(--card)); }
.acal-offer.is-s, .acal-key-s { color: var(--jade); background: color-mix(in srgb, var(--jade) 10%, var(--card)); }
.acal-offer.is-f, .acal-key-f { color: var(--stone); background: var(--paper-2); }
.acal-week-title { margin: 0; font-weight: 800; }
.acal-add-btn { margin-left: auto; }
.acal-add .panel-body { display: grid; gap: var(--sp-3); }
.acal-add-when { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: end; }
.acal-add-when .field, .acal-add-part .field { margin: 0; }
.acal-add-min { display: inline-flex; align-items: center; gap: 6px; }
.acal-add-min input { width: 5.5rem; }
.acal-add-part { display: grid; gap: var(--sp-2); margin: 0; padding: var(--sp-3); border: 1px solid var(--edge); border-radius: var(--radius-sm); }
.acal-add-part legend { padding: 0 6px; font-weight: 800; }
.acal-add-part > p { margin: 0; }
.acal-add-new { display: grid; gap: var(--sp-2); }
.acal-add-new[hidden] { display: none; }
.acal-add-go { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.acal-key { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); color: var(--ink-2); }
.acal-key li { display: inline-flex; align-items: center; gap: 6px; }
.acal-key i { display: inline-block; width: 22px; height: 14px; border-radius: 5px; border: 1px dashed currentColor; }
.acal-key i.acal-key-ev { background: var(--cinnabar); border: 0; }

/* The admin's « À faire » (Fei, 2026-11-09): what waits for her, a line
   each, first on the admin's page. */
.todo-list { display: grid; gap: var(--sp-2); margin: 0; padding-left: var(--sp-4); padding-right: var(--sp-4); list-style: none; }
.todo-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--sp-2); }
.todo-list a { font-weight: 700; }
.todo-none { margin: 0; color: var(--jade); font-weight: 700; }

/* Lessons on a phone (Fei, 2026-11-09): the title and the status only;
   the chapter, the audio and the buttons are on a computer, or in the
   lesson's editor. */
.theme-settings { margin: 0 0 var(--sp-3); }
@media (max-width: 47.99rem) {
  table.lessons th:nth-child(3), table.lessons td:nth-child(3),
  table.lessons th:nth-child(5), table.lessons td:nth-child(5),
  table.lessons th:nth-child(6), table.lessons td:nth-child(6) { display: none; }
}

.acal-delete { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); }
.student-class-name { font-size: var(--fs-lg); }
.class-log-older { margin: var(--sp-3) 0 0; }
.class-log-older > summary { cursor: pointer; font-weight: 700; color: var(--cinnabar); }
.date-words { display: block; margin-top: 4px; font-size: var(--fs-sm); font-weight: 700; color: var(--cinnabar); }
.date-words:empty { display: none; }

/* The agenda's week on a phone (Fei, 2026-11-09: « je veux que ça tienne
   en semaine complète sur l'écran »): the seven days side by side, no
   sideways scroll; each class its start and its name, a group its icon and
   its dots. */
.acal-start { display: none; }
@media (max-width: 47.99rem) {
  .acal-tt { --tt-q: 11px; min-width: 0; grid-template-columns: 1.5rem repeat(7, minmax(0, 1fr)); }
  .acal-tt-day { margin: 0 1px 4px; padding: 2px 0; font-size: 10px; }
  .acal-tt-day b { font-size: 13px; }
  .acal-tt-hours span { padding-right: 2px; font-size: 9px; }
  .acal-tt-block { padding: 1px; }
  .acal-tt .acal-ev { padding: 1px 2px; border-radius: 4px; font-size: 9px; line-height: 1.15; box-shadow: none; }
  .acal-tt .acal-name { font-size: 10px; line-height: 1.15; hyphens: auto; -webkit-hyphens: auto; overflow-wrap: anywhere; }
  .agenda-today > .panel-body { padding-left: 6px; padding-right: 6px; }
  .acal-tt .acal-who, .acal-tt .acal-state, .acal-tt .acal-span, .acal-offer-label { display: none; }
  .acal-tt .acal-start { display: inline; }
  .acal-tt .acal-todo { padding: 0 1px; font-size: 9px; letter-spacing: 0; }
  .acal-tt .acal-offer { padding: 1px; border-radius: 4px; font-size: 11px; }
  .acal-tt .acal-dots i { width: 5px; height: 5px; }
  .acal-tt .acal-ev.is-extra { border-width: 1px; }
}
.class-pause-go, .acal-pause form { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.acal-pause { flex-basis: 100%; }
.acal-pause > summary { list-style: none; }
.acal-pause > summary::-webkit-details-marker { display: none; }
.wish-open { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2); }
.wish-open select, .wish-open input { width: auto; }
.wish-opened { margin: var(--sp-2) 0 var(--sp-1); color: var(--jade); font-weight: 700; }
.class-log-more { margin: var(--sp-3) 0; }
.class-log-more > summary { cursor: pointer; padding: var(--sp-2) 0; font-weight: 700; color: var(--cinnabar); }
.acal-seat { display: grid; gap: var(--sp-2); }
.acal-seat .field { margin: 0; }
.acal-seat-new { display: grid; gap: var(--sp-2); }
.acal-seat-new[hidden] { display: none; }
.acal-hour-move > summary { list-style: none; }
.acal-hour-move > summary::-webkit-details-marker { display: none; }
/* Groupes de 1 à 3 (Fei, 2026-11-09): each slot, its students under it. */
.solo-slot { padding: var(--sp-3) 0; border-bottom: 1px solid var(--edge); }
.solo-slot-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-1) var(--sp-3); }
.solo-slot-head p { margin: 0; font-size: var(--fs-lg); text-align: left; }
.solo-slot-head .muted { font-size: var(--fs-sm); }
.solo-people p { text-align: left; }
.solo-people { display: grid; gap: var(--sp-2); margin: var(--sp-2) 0 0; padding: 0 0 0 var(--sp-3); border-left: 3px solid var(--edge); list-style: none; }
.solo-people li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-1) var(--sp-3); }
.solo-people p { margin: 0; }
.solo-slot > .hint { margin: var(--sp-1) 0 0; }
.solo-add { margin-top: var(--sp-4); }
.solo-add > summary { list-style: none; }
.solo-add > summary::-webkit-details-marker { display: none; }
.solo-add form { margin-top: var(--sp-3); }
.acal-or { margin: 0; text-align: center; font-weight: 700; color: var(--stone); }
.acal-offer.is-c { color: var(--stone); background: var(--paper-2); opacity: .7; }
/* A color per class (Fei, 2026-11-09: « des codes couleurs pour
   distinguer le même élève, les cours établis, les créneaux à remplir »):
   a class established is filled with its own color; a trial is gold; a
   slot to fill is dashed red; full or closed, grey. */
.acal-ev.c0 { background: #c0392b; } .acal-ev.c1 { background: #1f6f8b; }
.acal-ev.c2 { background: #7b4fa0; } .acal-ev.c3 { background: #2e7d32; }
.acal-ev.c4 { background: #a65400; } .acal-ev.c5 { background: #8e3b5e; }
.acal-ev.c6 { background: #3d5a99; } .acal-ev.c7 { background: #5f6b1f; }
.acal-ev.is-trial { background: #f5c542; color: #3b2a00; }
.acal-ev.is-log { background: var(--danger-bg); color: var(--danger-2); }
.acal-ev.is-cancelled { background: transparent; }
.acal-ev.is-done { background: var(--paper-2); color: var(--ink-2); }
.acal-key i.acal-key-ev { border: 0; background: linear-gradient(90deg, #c0392b 0 33%, #1f6f8b 33% 66%, #2e7d32 66%); }
.acal-key i.acal-key-trial { border: 0; background: #f5c542; }
.acal-key i.acal-key-c { color: var(--stone); background: var(--paper-2); opacity: .7; }
/* The names to pick for a trial in the agenda: one tap each. */
.acal-fill { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0 0 var(--sp-2); }
.acal-fill-name { min-height: 40px; padding: 0 var(--sp-3); border: 1px solid var(--line-strong); border-radius: 999px; background: var(--card); color: var(--ink); font: inherit; font-weight: 600; cursor: pointer; }
.acal-fill-name:hover, .acal-fill-name:focus-visible { border-color: var(--cinnabar); }
/* The box of a class, in Fei's order (Fei, 2026-11-13): its students,
   then « 1 · Avant », « 2 · Pendant », « 3 · Après », each under a small
   grey name, the rest folded in « Réglages du cours ». */
.acal-step { display: grid; gap: var(--sp-2); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.acal-step > * { margin: 0; }
.acal-step-name { margin: 0; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.acal-step-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.acal-step-done { color: var(--ink-2); }
.acal-students { display: grid; gap: var(--sp-2); list-style: none; margin: 0; padding: 0; }
.acal-student { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-1) var(--sp-2); padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line); }
.acal-student:last-child { padding-bottom: 0; border-bottom: 0; }
.acal-student-name { font-weight: 700; }
.acal-student-go { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.acal-pay { display: flex; align-items: center; gap: var(--sp-1); margin: 0; }
.acal-pill { font-size: var(--fs-md, 1rem); font-weight: 800; padding: .3rem .8rem; text-decoration: none; }
button.acal-pill { font-family: inherit; cursor: pointer; }
.acal-phone { flex-basis: 100%; }
.acal-phone > summary, .acal-link > summary, .acal-price > summary { list-style: none; cursor: pointer; }
.acal-phone > summary::-webkit-details-marker, .acal-link > summary::-webkit-details-marker, .acal-price > summary::-webkit-details-marker { display: none; }
.acal-phone[open] > form, details.acal-link[open] > form { margin-top: var(--sp-2); }
.acal-step-row > form { margin: 0; }
.acal-pick details > summary.btn-link { width: fit-content; min-height: 0; padding: var(--sp-1) 0; color: var(--ink-2); }
.acal-fit { field-sizing: content; }
.acal-pick .acal-quiet, .acal-pick .btn-link.acal-quiet { color: var(--ink-2); }
.acal-nonumber { display: inline-flex; align-items: center; gap: var(--sp-1); }
.acal-move .date-words { color: var(--stone); font-weight: 400; }
.acal-confirm[open] > textarea { margin-top: var(--sp-2); }
/* The price, asked once (Fei, 2026-11-13): its label, then the field,
   « Rs / élève » and « Enregistrer » on one line. */
.acal-price > summary { display: inline-block; }
.acal-price-form { display: grid; gap: var(--sp-1); margin-top: var(--sp-2); }
.acal-price-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.acal-price-row input.prepaid-n { width: 6rem; }
.acal-settings-last { margin-top: var(--sp-2); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.acal-confirm > summary { list-style: none; }
.acal-confirm > summary::-webkit-details-marker { display: none; }
.acal-confirm[open] { display: grid; gap: var(--sp-2); }
.acal-confirm[open] > summary { justify-self: start; }
.acal-settings { border-top: 1px solid var(--line); padding-top: var(--sp-3); }
.acal-settings > summary { list-style: none; }
.acal-settings > summary::-webkit-details-marker { display: none; }
.acal-settings > summary::before { content: "▸ "; }
.acal-settings[open] > summary::before { content: "▾ "; }
.acal-settings-body { display: grid; gap: var(--sp-4); margin-top: var(--sp-3); }
.acal-settings-body > * { margin: 0; }
.acal-settings-part { display: grid; gap: var(--sp-2); }
.acal-settings-part > * { margin: 0; }
.acal-link[open], div.acal-link { display: grid; gap: var(--sp-2); }
.acal-link > * { margin: 0; }
.acal-link-ask { font-weight: 700; }
.acal-link input[type=url] { flex: 1 1 14rem; min-width: 0; }
div.acal-link input[type=url], div.acal-link input[type=text] { flex: 1 1 9rem; min-width: 0; }

/* The family's monthly bill (« Compte »). */
.bill-line { display: flex; gap: .7rem; align-items: flex-start; padding: .45rem 0; }
.bill-tick { flex: none; width: 1.6rem; display: flex; justify-content: center; padding-top: .15rem; }
.bill-tick input { width: 1.25rem; height: 1.25rem; cursor: pointer; }
.bill-what { flex: 1; min-width: 0; }
.bill-what p { margin: 0; }
.bill-label { display: flex; justify-content: space-between; gap: .5rem; }
.bill-amount { white-space: nowrap; }
.bill-line.is-paid .bill-amount { text-decoration: line-through; color: var(--ink-2); }
.bill-paid { color: var(--jade); font-weight: 600; }
.bill-sum { margin: .6rem 0 .3rem; font-size: 1.05rem; }
.student-wa { margin: var(--sp-2) 0 var(--sp-4); display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* « 💰 À encaisser » and « 💰 Payé » (Fei, 2026-11-13): one line a
   family, the one gesture folded under its button. */
.cash-list { list-style: none; margin: 0; padding-top: 0; padding-bottom: 0; }
.cash-soon { margin: var(--sp-3) 0 0; padding: 0 var(--sp-4); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--stone); }
.cash-pill { margin: var(--sp-1) 0 0; }
.money p, .money .hint, .cash-item .hint, .my-bills p { text-align: left; hyphens: manual; }
.pupils-money { list-style: none; margin: 0; padding: var(--sp-3) var(--sp-4) 0; display: grid; gap: var(--sp-2); }
.pupils-money li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); }
.cash-item { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2); padding: var(--sp-3) 0; border-top: 1px solid var(--line); }
.cash-item:first-child { border-top: 0; }
.cash-who { min-width: 0; flex: 1 1 10rem; }
.cash-who p { margin: 0; }
.cash-btns, .money-btns { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sp-2); }
.money-btns { margin: var(--sp-2) 0; }
.money-state { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.money-state .tag { font-size: 1rem; }
.pay-sheet > summary { list-style: none; }
.pay-sheet > summary::-webkit-details-marker { display: none; }
.pay-sheet[open] { flex-basis: 100%; }
.pay-form { margin-top: var(--sp-2); padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2); display: grid; gap: var(--sp-2); text-align: left; }
.pay-form > * { margin: 0; }
.pay-opts { display: grid; gap: var(--sp-2); }
.pay-via { grid-template-columns: 1fr 1fr; }
.pay-opt { position: relative; display: block; cursor: pointer; }
.pay-opt input { position: absolute; opacity: 0; pointer-events: none; }
.pay-opt span { display: block; min-height: 44px; padding: .55rem .9rem; border: 1px solid var(--line); border-radius: 14px; background: var(--card); text-align: left; line-height: 1.35; }
.pay-via .pay-opt span { text-align: center; }
.pay-opt small { color: var(--ink-2); }
.pay-note { display: block; font-size: var(--fs-sm); }
.pay-plus { font-size: var(--fs-sm); font-weight: 700; color: var(--ink-2); }
.pay-check span::before { content: "☐ "; }
.pay-check input:checked + span::before { content: "☑ "; }
.pay-wait { display: none; }
.pay-form:not(:has(input[name=what]:checked:not([value=""]), input[name=site]:checked)) .pay-ok { opacity: .45; pointer-events: none; }
.pay-form:not(:has(input[name=what]:checked:not([value=""]), input[name=site]:checked)) .pay-wait { display: block; }
.pay-opt input:checked + span { border: 2px solid var(--ink); padding: calc(.55rem - 1px) calc(.9rem - 1px); }
.pay-opt input:focus-visible + span { outline: 2px solid var(--ink); outline-offset: 2px; }
.money-detail h3 { margin: var(--sp-3) 0 var(--sp-1); font-size: 1rem; }

/* The next class's Google Meet link, on the student's calendar (Fei, 2026-11-11). */
.cal-meet { margin: 0 0 var(--sp-2); }

/* a group of words kept on one line (« 500 Rs / mois ») */
.nowrap { white-space: nowrap; }
/* A button whose words wrap (a flex box): its text and such a group as
   centred lines, not two columns. */
.btn-wrap { flex-wrap: wrap; column-gap: .3em; row-gap: 0; }

/* « Je change les mots » (learn_grammar.html, app.js): the sentence on the
   point's structure, the fixed words in red, the slots in dashed blue;
   under it, each slot's words, in French until touched. */
.swap { margin: 0 0 var(--sp-5); }
.swap .panel-body { display: grid; gap: var(--sp-4); }
.swap-sent { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start; gap: 6px; margin: 0; padding: var(--sp-4) var(--sp-2); border-radius: var(--radius); background: var(--panel-head); }
.swap-b { position: relative; display: grid; justify-items: center; gap: 2px; min-width: 3rem; padding: 6px 10px; border-radius: var(--radius-sm); }
/* The pinyin in front, the characters quiet (Fei, 2026-11-12): the oral first. */
.swap-b .swap-py { font-size: 1.35rem; font-weight: 800; color: var(--violet); }
.swap-b .swap-hz { font-family: var(--hanzi); font-size: 1rem; line-height: 1.15; color: color-mix(in srgb, var(--stone) 70%, var(--card)); }
.swap-b .swap-gl { max-width: 7rem; font-size: var(--fs-xs); line-height: 1.2; text-align: center; color: var(--ink-2); }
.swap-fixed { border: 2px solid color-mix(in srgb, var(--cinnabar) 40%, var(--card)); background: var(--card); color: var(--cinnabar); }
.swap-var { border: 2px dashed var(--block-blue-ink); background: var(--block-blue); color: var(--block-blue-ink); }
.swap-var.is-new { animation: swap-pop .35s var(--ease); }
@keyframes swap-pop { 40% { transform: scale(1.15); } }
/* The end mark rides on the last word, never alone on a line. */
.swap-end { position: absolute; right: -1.1em; bottom: .4rem; font-family: var(--hanzi); font-size: 1rem; color: var(--stone); }
.swap-sent { padding-right: var(--sp-4); }
.swap-row { display: grid; gap: 6px; }
.swap-role { margin: 0; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--block-blue-ink); }
.swap-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.swap-chip { display: grid; place-items: center; min-width: 52px; min-height: 52px; padding: .25rem .8rem; border: 2px solid var(--block-blue); border-radius: 16px; background: var(--card); color: var(--block-blue-ink); font: inherit; line-height: 1.15; cursor: pointer; }
.swap-chip .swap-fr { font-weight: 700; }
.swap-chip .swap-py { font-size: 1rem; font-weight: 800; color: var(--violet); }
.swap-chip .swap-hz { font-family: var(--hanzi); font-size: .85rem; color: var(--stone); }
.swap-chip:not(.is-seen) :is(.swap-py, .swap-hz), .swap-chip.is-seen .swap-fr { display: none; }
.swap-chip.is-seen { background: var(--block-blue); }
.swap-chip[aria-pressed="true"] { border-color: currentColor; box-shadow: 0 3px 0 currentColor; }
@media (prefers-reduced-motion: reduce) { .swap-var.is-new { animation: none; } }

