/* ════════════════════════════════════════════════════════════════════════════
   Caribbein · app famille — le vocabulaire partagé.  TOKENS ONLY (Seam 3 §3.3 rule 1).
   Every colour here is a var() from tokens.css. A literal hex in this file is a
   review-blocking defect (PRODUCT-PLAN.md §P2).

   Split out of app.css by S0 (FAMILLE-12-PLAN.md §4). What lives here is what MORE
   THAN ONE screen uses: the reset, the shell, the cards, the buttons, the three
   states (loading · empty · error), the nav, the quote. Anything a single screen
   owns moved to screens/<name>.css.

   Structure transcribed from app/prototype/famille.html — same class vocabulary,
   same geometry — with the values lifted into the token layer so the Terrain de
   jeu twin is a value swap and not a second stylesheet.

   Owners after Wave 0: S1 (géométrie + safe-area) · S3 (transitions) — distinct
   commented blocks, coordinate before moving a line.
   ════════════════════════════════════════════════════════════════════════════ */

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: var(--sky-0);
  background-image: linear-gradient(180deg, var(--sky-0) 0%, var(--sky-1) 46%, var(--sky-2) 78%, var(--sky-3) 100%);
  background-attachment: fixed;
  color: var(--ink);
  font: 16px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }

/* ── shell ─────────────────────────────────────────────────────────────── */
.shell { max-width: var(--shell-w); margin: 0 auto; min-height: 100vh; display: flex; flex-direction: column; position: relative; }
/* ── S1 geometry: main clears the fixed nav, nav clears the home indicator (D2) ─────────────── */
main { flex: 1; padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
/* `backwards`, NOT `both`: with `both`, a finished entrance animation keeps a resolved
   transform matrix on .screen forever, which makes it a containing block for position:fixed
   descendants — trapping the mode-table and « la fin » overlays inside the screen box instead
   of the viewport (S15, Wave 3). `backwards` drops the fill once the 180ms entrance ends, so
   .screen reverts to transform:none and fullscreen overlays escape to the real viewport. */
.screen { display: none; animation: up .18s ease backwards; }
.screen.on { display: block; }
@keyframes up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .screen, .list > .card { animation: none; } }

/* S1 geometry: safe-area at the top too, notch/Dynamic Island (D2) */
.sbar { display: flex; justify-content: space-between; align-items: center; padding: calc(14px + env(safe-area-inset-top)) 26px 6px; font: 600 13px/1 var(--font); }

.head { padding: 14px 24px 0; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.when { display: flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--ink-muted); }
.when svg { width: 16px; height: 16px; color: var(--sport); }
.kids { display: flex; gap: 7px; flex-wrap: wrap; }
.kids button { font: 600 12px/1 var(--font); padding: 7px 13px; border-radius: var(--r-pill); background: var(--sport-soft); color: var(--sport-ink); }
.kids button.off { background: var(--surface-2); color: var(--ink-muted); }

.title { padding: 26px 24px 6px; }
.title h1 { font: 300 29px/1.2 var(--font); text-wrap: balance; }
.title h1 b { font-weight: 600; }
.sub { font-size: 13.5px; color: var(--ink-muted); margin-top: 8px; }
.sub b { color: var(--ink); font-weight: 600; }

/* ── cards ─────────────────────────────────────────────────────────────── */
.list { padding: 18px 20px 0; display: flex; flex-direction: column; gap: 12px; }
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 15px 16px; text-align: left; width: 100%; box-shadow: var(--shadow-card);
}
.card.lit  { background: var(--sport-soft);  border-color: var(--sport-line);  box-shadow: inset 3px 0 0 var(--sport),  var(--sport-glow); }
.card.litB { background: var(--school-soft); border-color: var(--school-line); box-shadow: inset 3px 0 0 var(--school), var(--school-glow); }
/* .litV — la troisième lumière, la parole. Elle MANQUAIT : le plan la nomme dans son vocabulaire
   partagé et deux unités à venir la demandent (le cahier de vacances est ambre), mais seules .lit
   et .litB existaient — une carte litV serait sortie ÉTEINTE, sans erreur et sans bruit. Trouvée
   par la lane C en portant le salon ; le jeton --voice-glow naît du même constat. */
.card.litV { background: var(--voice-soft);  border-color: var(--voice-line);  box-shadow: inset 3px 0 0 var(--voice),  var(--voice-glow); }
.row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.card strong { font-size: 15.5px; font-weight: 600; }
.min  { font: 600 11.5px/1 var(--font); color: var(--sport-ink); flex: none; }
.minB { color: var(--school); }
.card p { font-size: 13px; color: var(--ink-muted); margin-top: 4px; }
.acts { display: flex; gap: 9px; margin-top: 12px; flex-wrap: wrap; }
.btn {
  font: 600 12.5px/1 var(--font); border-radius: var(--r-pill); padding: 9px 16px;
  background: var(--btn-bg); color: var(--btn-ink); box-shadow: var(--btn-shadow);
  display: inline-flex; align-items: center; gap: 6px;
}
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .55; cursor: default; }
.btn.pri  { background: var(--sport);  color: var(--ink-on-sport); }
.btn.priB { background: var(--school); color: var(--ink-on-school); }
.btn.priV { background: var(--voice);  color: var(--ink-on-voice); }
.btn svg { width: 12px; height: 12px; }
.role  { font: italic 400 14px/1.5 var(--serif); color: var(--sport-ink);  margin-top: 8px; }
.roleB { font: italic 400 14px/1.5 var(--serif); color: var(--school); margin-top: 8px; }
.need { font-size: 12px; color: var(--ink-muted); margin-top: 6px; }
.slim { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.clhead { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 4px; }
.shield {
  font: 600 10px/1.3 var(--font); letter-spacing: .05em; color: var(--ink-muted);
  border: 1px solid var(--line-2); border-radius: var(--r-pill); padding: 5px 9px; text-align: right;
}
.ag { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; padding: 7px 0; border-bottom: 1px dashed var(--line-dashed); }
.ag:last-of-type { border-bottom: none; }
.ag b { font-weight: 600; color: var(--ink); }
.ag span { color: var(--ink-muted); }
.ag .school { color: var(--school); }
.ag .sport  { color: var(--sport-ink); }

/* ── la voix de l'enfant, rendue par core.js voiceQuote() — Ce soir · Apprendre · Bouger ── */
.quote {
  margin: 14px 20px 0; padding: 15px 16px; border-radius: var(--r-lg);
  background: var(--voice-wash); border: 1px solid var(--voice-line);
  font: italic 400 15px/1.5 var(--serif); color: var(--ink);
}
.quote span { display: block; font: 600 10.5px/1 var(--font); letter-spacing: .1em; text-transform: uppercase; color: var(--voice); margin-top: 9px; font-style: normal; }
/* ⚠ `padding: 0` sur une ligne de 11px donnait une cible de 239×11 — MESURÉ, et sous le
   plancher WCAG 2.2 AA (2.5.8 : 24×24). Le texte garde sa taille (c’est un filet, pas un
   bouton plein) ; c’est la ZONE qui s’ouvre. Tenu par scripts/a11y-famille.js §6. */
.quote button { font: 600 11px/1 var(--font); color: var(--voice); margin-top: 6px; font-style: normal; padding: 12px 0; min-height: 44px; text-align: left; }

/* ── state: loading · empty · error (no DB is a first-class state) ──────── */
.skel { background: var(--surface-2); border-radius: var(--r-md); height: 74px; animation: pulse 1.4s ease-in-out infinite; }
.skel + .skel { margin-top: 12px; }
@keyframes pulse { 0%, 100% { opacity: .55; } 50% { opacity: .9; } }
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }
.note {
  margin: 12px 20px 0; padding: 11px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12.5px; color: var(--ink-muted);
}
.note b { color: var(--ink); font-weight: 600; }
.note.warn { border-color: var(--danger-line); color: var(--danger); }
.empty { padding: 26px 20px; text-align: center; color: var(--ink-muted); font-size: 13.5px; }

/* ── nav ───────────────────────────────────────────────────────────────── */
.nav { position: fixed; bottom: 0; left: 0; right: 0; z-index: 20; }
/* S1 geometry: the iPhone home indicator sits under the nav otherwise (D2) */
.nav-in {
  max-width: var(--shell-w); margin: 0 auto; display: flex; justify-content: space-around; align-items: center;
  padding: 10px 8px calc(20px + env(safe-area-inset-bottom)); background: var(--nav-bg); backdrop-filter: blur(14px);
  border-top: 1px solid var(--nav-line); color: var(--nav-ink);
}
.nav-in button { display: flex; flex-direction: column; align-items: center; gap: 4px; font: 500 9.5px/1 var(--font); position: relative; padding: 2px 6px; color: var(--nav-ink); }
.nav-in button svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.nav-in button.on  { color: var(--nav-ink-on); }
.dot { position: absolute; top: 0; right: -1px; width: 6px; height: 6px; border-radius: 50%; background: var(--voice); }

svg.ic { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.foot-note { padding: 26px 24px 10px; font-size: 11.5px; color: var(--ink-muted); text-align: center; }
.foot-note b { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════
   S3 motion (FAMILLE-12-PLAN.md §S3). Distinct from the S1 geometry block
   above — coordinate before moving a line either way.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. cards cascade in behind the .screen entry — pure CSS, no JS.
   Reuses the `up` keyframe declared above for .screen. Capped at the 8th
   child so a long list never keeps growing its delay. ────────────────────── */
.list > .card { animation: up .18s ease both; }
.list > .card:nth-child(1) { animation-delay: 0ms; }
.list > .card:nth-child(2) { animation-delay: 40ms; }
.list > .card:nth-child(3) { animation-delay: 80ms; }
.list > .card:nth-child(4) { animation-delay: 120ms; }
.list > .card:nth-child(5) { animation-delay: 160ms; }
.list > .card:nth-child(6) { animation-delay: 200ms; }
.list > .card:nth-child(7) { animation-delay: 240ms; }
.list > .card:nth-child(n+8) { animation-delay: 280ms; }

/* ── 2. state transitions — boutons, puces, chips, soumission de score — 120ms.
   The "Noter" score-submit button (screens/player.js scoreCard) is a plain
   `.btn.pri`, so it is covered here without touching player.css. ──────────── */
.btn { transition: transform .12s ease, background-color .12s ease, opacity .12s ease, box-shadow .12s ease; }
.btn:active { transition-duration: .06s; }
.kids button { transition: background-color .12s ease, color .12s ease; }
.nav-in button { transition: color .12s ease; }
.dot { transition: opacity .12s ease; }

/* ── 3. le fondu du monde. theme.js toggles `.world-turning` on <html> for
   ~2s whenever the resolved theme actually flips (crépuscule ⇄ terrain)
   after boot — the automatic 19h/7h tick, or a parent's manual pin.
   Scoped to the class + its duration so ordinary interactions never pay a
   transition tax the rest of the time. ────────────────────────────────────── */
.world-turning,
.world-turning * {
  transition: background-color 2s ease, background-image 2s ease, border-color 2s ease,
              color 2s ease, box-shadow 2s ease, fill 2s ease, stroke 2s ease;
}

/* ── 4. prefers-reduced-motion neutralises everything in this block ──────── */
@media (prefers-reduced-motion: reduce) {
  .btn, .btn:active, .kids button, .nav-in button, .dot { transition: none; }
  .world-turning, .world-turning * { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   U2 — LA LECTURE (UX-UPLIFT-PLAN.md §3 U2). Deux réglages posés dans « Ma famille »
   (screens/famille.js, la carte « La lecture » sous « L'ambiance »), persistés dans F.S
   EXACTEMENT comme le pin de thème : deux attributs sur <html>, posés/retirés par
   screens/famille.js:applyLectureNow() au chargement du script et à chaque changement —
   même mécanique que theme.js applyTheme(). core.js ne bouge pas : ce stream ne le possède
   pas (§5 du plan) ; applyLectureNow() vit donc dans screens/famille.js et s'exécute dès
   que ce fichier se charge, avant tout rendu d'écran.

   data-lecture="large" — le texte s'agrandit et respire davantage. body applique le
   réglage littéral du plan (18px / 1.7 d'interlignage). Ce ratio (18/16) est ensuite
   repris, en rem, pour les quelques classes de LECTURE que base.css partage avec tous les
   écrans (le titre d'écran, les paragraphes et intitulés de carte, la ligne « need », la
   voix de l'enfant, les boutons) — ce bloc est ADDITIF : aucune déclaration au-dessus n'est
   réécrite, chaque règle ici ne touche que la propriété font-size. --shell-w ne bouge pas :
   tout ici est en unités relatives, le reflow suffit. Les classes propres à un seul écran
   (screens/*.css) sont hors périmètre de ce stream et restent à leur taille d'origine.

   data-calme="1" — le masque de concentration : LES MÊMES neutralisations que
   prefers-reduced-motion (bloc S3 ci-dessus), mais choisies DANS l'app plutôt que
   dépendantes du seul système. CSS ne sait pas additionner une media query et un sélecteur
   par un OU : chaque règle prefers-reduced-motion DE CE FICHIER est donc redite ici,
   sélecteur pour sélecteur, sans logique nouvelle. Les sons suivent la même bascule :
   feedback.js:enabled() lit F.S.calme au même endroit où il lit déjà prefers-reduced-motion.
   Les blocs prefers-reduced-motion d'autres fichiers (modals.css, screens/player.css,
   screens/soir.css, screens/apprendre.css) portent chacun leur duplicata data-calme, posé à
   la passe de fusion de la Wave 1 ; engine.js et screens/player.js lisent l'attribut au même
   endroit où ils lisent déjà matchMedia. La couverture est entière, fichier par fichier.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── data-lecture="large" ─────────────────────────────────────────────────── */
:root[data-lecture="large"] { font-size: 18px; }
:root[data-lecture="large"] body { font-size: 18px; line-height: 1.7; }
:root[data-lecture="large"] .title h1 { font-size: 1.8125rem; }     /* 29px de base */
:root[data-lecture="large"] .sub { font-size: 0.84375rem; }         /* 13.5px de base */
:root[data-lecture="large"] .card strong { font-size: 0.96875rem; } /* 15.5px de base */
:root[data-lecture="large"] .card p { font-size: 0.8125rem; }       /* 13px de base */
:root[data-lecture="large"] .need { font-size: 0.75rem; }           /* 12px de base */
:root[data-lecture="large"] .note { font-size: 0.78125rem; }        /* 12.5px de base */
:root[data-lecture="large"] .empty { font-size: 0.84375rem; }       /* 13.5px de base */
:root[data-lecture="large"] .quote { font-size: 0.9375rem; }        /* 15px de base — la voix de l'enfant */
:root[data-lecture="large"] .btn { font-size: 0.78125rem; }         /* 12.5px de base */

/* ── data-calme="1" — dupliqué depuis les trois blocs prefers-reduced-motion ci-dessus ── */
:root[data-calme="1"] .screen, :root[data-calme="1"] .list > .card { animation: none; }
:root[data-calme="1"] .skel { animation: none; }
:root[data-calme="1"] .btn, :root[data-calme="1"] .btn:active,
:root[data-calme="1"] .kids button, :root[data-calme="1"] .nav-in button,
:root[data-calme="1"] .dot { transition: none; }
:root[data-calme="1"] .world-turning, :root[data-calme="1"] .world-turning * { transition: none; }
