// template-pack-1-universal.jsx
// PACK 1 · UNIVERSAL TEMPLATES — Allo Education Books v1.2
// 10 production-ready templates. Built strictly from locked blocks
// (PageShell, PageTitle, Box variants, Cols, KettyBubble, QRBlock).
// No header/footer, no margins, no spacing, no typo redefined locally.
// All icons via <Icon k="…" /> or block iconKey prop. Zero emoji.

const { Fragment: U_F } = React;

/* ────────────────────────────────────────────────────────────────────────
   Local helpers (composed from locked primitives only — never restyle them)
   ──────────────────────────────────────────────────────────────────────── */

// Tag with a number: used in match / TF / writing rows
function PackNumTag({ n, tone = "primary" }) {
  return (
    <span style={{
      display: "inline-grid", placeItems: "center",
      width: "0.22in", height: "0.22in", borderRadius: "999px",
      background: tone === "orange" ? "var(--ae-orange)" : "var(--ae-primary)",
      color: "white",
      fontFamily: "var(--ae-font-title)", fontWeight: 600,
      fontSize: "0.12in", flex: "0 0 auto",
    }}>{n}</span>
  );
}

// V / F square checkboxes used in true/false rows
function VFCell() {
  return (
    <span style={{
      display: "inline-block", width: "0.22in", height: "0.22in",
      border: "1.5px solid var(--ae-text-soft)", borderRadius: "0.04in",
      verticalAlign: "middle",
    }} />
  );
}

// Letter chip (A, B, C…) used in match-exercise
function LetterChip({ ch, tone = "primary" }) {
  return (
    <span style={{
      display: "inline-grid", placeItems: "center",
      width: "0.26in", height: "0.26in", borderRadius: "0.06in",
      background: tone === "orange" ? "var(--ae-orange-soft)" : "var(--ae-primary-soft)",
      color: tone === "orange" ? "var(--ae-orange-deep, var(--ae-orange))" : "var(--ae-primary-deep)",
      fontFamily: "var(--ae-font-title)", fontWeight: 700,
      fontSize: "0.13in", flex: "0 0 auto",
    }}>{ch}</span>
  );
}

// Ruled writing area (multi-line) — composes AnswerBox to keep typo locked
function WritingLines({ lines = 8 }) {
  // Render N stacked AnswerBox-equivalent rules, each 0.28in tall.
  // Using inline div instead of AnswerBox to control exact line spacing.
  return (
    <div style={{
      borderRadius: "0.06in",
      border: "1px solid var(--ae-border)",
      background: "white",
      padding: "0.06in 0.1in",
    }}>
      {Array.from({ length: lines }).map((_, i) => (
        <div key={i} style={{
          height: "0.28in",
          borderBottom: "1px dashed var(--ae-border)",
        }} />
      ))}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   1 · DIAGNOSTIC DE DÉPART  (template: "diagnostic")
   Test rapide multi-thèmes · 8–12 questions · score · Ketty · QR optionnel
   ════════════════════════════════════════════════════════════════════════ */
function U1Diagnostic({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Maths" niveau="CM2" page={"Diagnostic · Page 5"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="diag-cm2-rentree"
      qrLabel="Auto-correction"
      scale={scale}
    >
      <PageTitle
        eyebrow="Diagnostic de rentrée · 20 min"
        title="Mes acquis du CM1"
        subtitle="10 questions courtes pour repérer ce qui est solide et ce qu'on va consolider."
      />

      <ObjectiveBox
        title="Consigne"
        items={[
          "Travaille seul, sans aide. Tu peux passer une question difficile et y revenir.",
          "Réponds dans la case ou coche la bonne proposition.",
          "À la fin, additionne tes points et reporte le score sur le footer.",
        ]}
      />

      <Cols ratio="1fr 1fr">
        <ExerciseCard number={1} title="Calcul" consigne="Coche la bonne réponse.">
          <QInput n={1}>247 + 158 = <QCMRow options={[["A","395"],["B","405"],["C","415"]]} /></QInput>
          <QInput n={2}>8 × 9 = <QCMRow options={[["A","63"],["B","72"],["C","81"]]} /></QInput>
          <QInput n={3}>1 000 − 367 = <QCMRow options={[["A","633"],["B","643"],["C","733"]]} /></QInput>
          <QInput n={4}>Quart de 80 = <QCMRow options={[["A","16"],["B","20"],["C","24"]]} /></QInput>
          <QInput n={5}>72 ÷ 9 = <QCMRow options={[["A","7"],["B","8"],["C","9"]]} /></QInput>
        </ExerciseCard>
        <ExerciseCard number={2} tone="orange" title="Nombres &amp; mesures" consigne="Réponds par un nombre.">
          <QInput n={6}>Écris en chiffres : « cinq mille soixante-trois » <AnswerLine width="1in" /></QInput>
          <QInput n={7}>Combien de cm dans 1,25 m ? <AnswerLine width="0.7in" /></QInput>
          <QInput n={8}>Périmètre d'un carré de 7 cm ? <AnswerLine width="0.9in" /></QInput>
          <QInput n={9}>Aire d'un rectangle 4 × 6 cm ? <AnswerLine width="0.9in" /></QInput>
          <QInput n={10}>30 minutes = <AnswerLine width="0.7in" /> heure</QInput>
        </ExerciseCard>
      </Cols>

      <Cols ratio="1.4fr 1fr">
        <KettyBubble pose="thinking" tone="primary" label="Le mot de Ketty">
          Ce diagnostic ne compte pas pour la note. Il sert à cibler ce qu'on
          travaillera ensemble dans les premières pages du cahier.
        </KettyBubble>
        <ScoreBox label="Mon score de départ" message="Je le compare à mon score final en page 96." />
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   2 · EXERCICE PROBLÈME  (template: "problem-exercise")
   ════════════════════════════════════════════════════════════════════════ */
function U2Problem({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Maths" niveau="4ᵉ" page={42}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="pb-4-pourcentages"
      qrLabel="Étape par étape"
      scale={scale}
    >
      <PageTitle
        eyebrow="Problème ouvert · Pourcentages"
        title="Le panier de la cantine"
        subtitle="Lis attentivement, organise tes données, pose ta démarche."
      />

      <Box tone="neutral" label="Énoncé" iconKey="icon-section">
        <p style={{ fontSize: "var(--ae-fs-body)", lineHeight: "var(--ae-line-h)" }}>
          La cantine du collège commande chaque mois <b>180 kg de fruits</b> auprès
          d'un producteur local. Le tarif normal est de <b>2,80 €/kg</b>. Comme le
          collège passe une commande importante, le producteur applique une
          <b> remise de 15 %</b> sur la facture totale, mais ajoute des frais de
          livraison fixes de <b>22 €</b>. Le collège souhaite savoir combien
          coûte réellement le kilo de fruits livrés, et combien il économise
          par rapport au tarif normal.
        </p>
      </Box>

      <Cols ratio="1fr 1fr">
        <Box tone="cool" label="Données utiles" iconKey="icon-info">
          <ul className="bk-bullets">
            <li><span className="dot"></span>Quantité : 180 kg</li>
            <li><span className="dot"></span>Prix tarif normal : 2,80 €/kg</li>
            <li><span className="dot"></span>Remise : 15 % sur le total</li>
            <li><span className="dot"></span>Frais de livraison : 22 €</li>
          </ul>
        </Box>
        <MethodSteps
          title="Étapes guidées"
          steps={[
            "Calcule le prix sans remise (180 × 2,80).",
            "Applique la remise de 15 %, puis ajoute les frais.",
            "Divise le prix payé par 180 kg pour le prix réel au kilo.",
            "Calcule l'économie réalisée.",
          ]}
        />
      </Cols>

      <ExerciseCard number={1} consigne="Rédige tes calculs puis ta réponse en une phrase complète.">
        <div className="bk-text" style={{ marginBottom: "0.08in", fontWeight: 600 }}>Mes calculs</div>
        <AnswerBox height="1.15in" ruled />
        <div className="bk-text" style={{ margin: "0.1in 0 0.06in", fontWeight: 600 }}>Ma réponse en phrase</div>
        <AnswerBox height="0.5in" ruled />
      </ExerciseCard>

      <Cols ratio="1.2fr 1fr">
        <SelfCheck
          title="Auto-vérification"
          items={[
            "J'ai posé une opération par étape.",
            "J'ai gardé deux décimales pour les prix en €.",
            "Ma phrase de réponse contient l'unité (€/kg).",
          ]}
        />
        <KettyBubble pose="pointing" tone="yellow" label="Astuce Ketty">
          Une remise s'applique <b>après</b> avoir calculé le total. Frais
          fixes ajoutés <b>après</b> la remise.
        </KettyBubble>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   3 · TABLEAU À COMPLÉTER  (template: "table-exercise")
   ════════════════════════════════════════════════════════════════════════ */
function U3Table({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Français" niveau="6ᵉ" page={28}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="conj-6-present"
      scale={scale}
    >
      <PageTitle
        eyebrow="Conjugaison · Application"
        title="Le présent des verbes irréguliers"
        subtitle="Complète chaque case en t'appuyant sur le rappel ci-dessous."
      />

      <LessonBox title="Rappel">
        Les verbes <b>être, avoir, aller, faire, dire, voir</b> sont irréguliers
        au présent : leur radical change selon la personne. À mémoriser absolument.
      </LessonBox>

      <ExerciseCard number={1} consigne="Complète le tableau au présent de l'indicatif.">
        <TableBlock
          columns={["Personne", "être", "avoir", "aller", "faire", "dire"]}
          rows={[
            ["je / j'",   "suis",  "ai",    "vais",   "fais",  "…"   ],
            ["tu",        "es",    "…",     "vas",    "…",     "dis" ],
            ["il / elle", "…",     "a",     "va",     "fait",  "…"   ],
            ["nous",      "sommes","avons", "…",      "…",     "disons"],
            ["vous",      "êtes",  "…",     "allez",  "faites","…"   ],
            ["ils",       "sont",  "ont",   "…",      "font",  "disent"],
          ]}
        />
      </ExerciseCard>

      <Cols ratio="1fr 1fr">
        <Box tone="neutral" label="Exemple mini" iconKey="icon-loupe">
          <div className="bk-text">
            <b>aller</b> · 1ʳᵉ pers. plur. → <i>nous </i>
            <span style={{ background: "var(--ae-yellow-soft)", padding: "0 0.06in", borderRadius: "0.04in" }}>
              <b>allons</b>
            </span>
          </div>
        </Box>
        <SelfCheck
          title="Je vérifie"
          items={[
            "Toutes les cases sont remplies.",
            "Les terminaisons -ons / -ez / -ent sont écrites correctement.",
            "Les radicaux changent quand c'est nécessaire.",
          ]}
        />
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   4 · RELIER / ASSOCIER  (template: "match-exercise")
   ════════════════════════════════════════════════════════════════════════ */
function U4Match({ density, qrVisible, scoreVisible, scale = 1 }) {
  const left = [
    ["1", "Photosynthèse"],
    ["2", "Respiration cellulaire"],
    ["3", "Transpiration"],
    ["4", "Germination"],
  ];
  const right = [
    ["A", "Production de glucose à partir de CO₂ et de lumière."],
    ["B", "Évaporation d'eau au niveau des feuilles."],
    ["C", "Développement d'une graine en plantule."],
    ["D", "Libération d'énergie à partir du glucose et de l'O₂."],
  ];

  return (
    <PageShell
      matiere="SVT" niveau="5ᵉ" page={47}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="svt-5-relier-vivant"
      scale={scale}
    >
      <PageTitle
        eyebrow="Vocabulaire du vivant · Réviser"
        title="Relie chaque mot à sa définition"
        subtitle="5 notions clés du chapitre — un seul lien correct par ligne."
      />

      <ObjectiveBox
        title="Consigne"
        items={[
          "Relie chaque mot de la colonne A à sa définition de la colonne B.",
          "Reporte ensuite ta paire dans le tableau de réponses.",
          "Justifie en une phrase la définition la plus difficile.",
        ]}
      />

      <ExerciseCard number={1} consigne="Trace une ligne entre chaque mot et sa définition, sans croiser inutilement.">
        <div style={{
          display: "grid",
          gridTemplateColumns: "1.1fr 0.4fr 1.5fr",
          gap: "0.1in",
          alignItems: "stretch",
        }}>
          {/* Column A */}
          <div style={{ display: "flex", flexDirection: "column", gap: "0.08in" }}>
            <div className="bk-col-head">Colonne A — Mot</div>
            {left.map(([n, w]) => (
              <div key={n} style={{
                display: "flex", alignItems: "center", gap: "0.1in",
                padding: "0.1in 0.12in", border: "1.4px solid var(--ae-primary-soft)",
                background: "white", borderRadius: "0.08in", minHeight: "0.4in",
                fontFamily: "var(--ae-font-title)", fontWeight: 600,
                color: "var(--ae-primary-deep)", fontSize: "var(--ae-fs-body)",
              }}>
                <PackNumTag n={n} />
                <span>{w}</span>
                <span style={{ marginLeft: "auto", width: "0.1in", height: "0.1in", borderRadius: "999px", background: "var(--ae-primary)" }} />
              </div>
            ))}
          </div>
          {/* Connector lane (visual) */}
          <div style={{ position: "relative" }}>
            <div style={{
              position: "absolute", top: "0.42in", bottom: "0.42in", left: "50%",
              width: 1, borderLeft: "1.5px dashed var(--ae-border)",
            }} />
            <div style={{
              position: "absolute", top: "50%", left: 0, right: 0,
              transform: "translateY(-50%)", textAlign: "center",
              fontFamily: "var(--ae-font-title)", color: "var(--ae-text-soft)",
              fontSize: "0.11in", letterSpacing: "0.06em", textTransform: "uppercase",
            }}>relier</div>
          </div>
          {/* Column B */}
          <div style={{ display: "flex", flexDirection: "column", gap: "0.08in" }}>
            <div className="bk-col-head">Colonne B — Définition</div>
            {right.map(([ch, d]) => (
              <div key={ch} style={{
                display: "flex", alignItems: "center", gap: "0.1in",
                padding: "0.1in 0.12in", border: "1.4px solid var(--ae-orange-soft, #FFE0C2)",
                background: "white", borderRadius: "0.08in", minHeight: "0.4in",
                fontSize: "var(--ae-fs-body)", lineHeight: 1.35,
              }}>
                <span style={{ width: "0.1in", height: "0.1in", borderRadius: "999px", background: "var(--ae-orange)" }} />
                <LetterChip ch={ch} tone="orange" />
                <span style={{ flex: 1 }}>{d}</span>
              </div>
            ))}
          </div>
        </div>

        <div style={{ marginTop: "0.14in" }}>
          <div className="bk-col-head" style={{ marginBottom: "0.06in" }}>Mes réponses</div>
          <TableBlock
            columns={["1", "2", "3", "4"]}
            rows={[["…", "…", "…", "…"]]}
          />
        </div>
      </ExerciseCard>

      <Cols ratio="1fr 1fr">
        <ChallengeBox title="Pour aller plus loin">
          Choisis la définition qui te paraît la plus difficile et reformule-la
          avec tes mots, en 2 lignes maximum.
          <div style={{ marginTop: "0.08in" }}><AnswerBox height="0.5in" ruled /></div>
        </ChallengeBox>
        <KettyBubble pose="thumbsup" tone="primary" label="Astuce mémo">
          Pour fixer le vocabulaire, lis chaque mot à voix haute avec sa
          définition, puis ferme le cahier et réessaie.
        </KettyBubble>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   5 · VRAI / FAUX  (template: "true-false")
   ════════════════════════════════════════════════════════════════════════ */
function U5TrueFalse({ density, qrVisible, scoreVisible, scale = 1 }) {
  const items = [
    ["L'eau bout à 100 °C au niveau de la mer.", true],
    ["Le Soleil tourne autour de la Terre.", false],
    ["Un kilomètre vaut 1 000 mètres.", true],
    ["Tous les nombres pairs sont divisibles par 4.", false],
    ["La Lune émet sa propre lumière.", false],
    ["Le triangle rectangle a un angle de 90°.", true],
    ["Un litre d'eau pèse environ 1 kilogramme.", true],
    ["L'air est composé majoritairement d'oxygène.", false],
  ];
  return (
    <PageShell
      matiere="Sciences" niveau="6ᵉ" page={"Révisions · Page 11"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="rev-6-vraifaux"
      qrLabel="Voir les explications"
      scale={scale}
    >
      <PageTitle
        eyebrow="Quiz éclair · Vrai ou faux"
        title="Sais-tu reconnaître l'idée juste ?"
        subtitle="Coche V ou F. Justifie en une phrase quand c'est demandé."
      />

      <ObjectiveBox
        title="Consigne"
        items={[
          "Lis chaque affirmation entièrement avant de cocher.",
          "Si tu coches « Faux », écris la version correcte sur la ligne dessous.",
          "Compte 1 point par bonne réponse, 1 point bonus par justification juste.",
        ]}
      />

      <ExerciseCard number={1} consigne="V = Vrai · F = Faux. Une seule case par ligne.">
        <div style={{
          display: "grid",
          gridTemplateColumns: "0.32in 1fr 0.4in 0.4in",
          rowGap: "0.08in", columnGap: "0.08in",
          alignItems: "center",
          fontSize: "var(--ae-fs-body)", lineHeight: 1.35,
        }}>
          <div className="bk-col-head" style={{ gridColumn: "1 / 2" }}>N°</div>
          <div className="bk-col-head" style={{ gridColumn: "2 / 3" }}>Affirmation</div>
          <div className="bk-col-head" style={{ gridColumn: "3 / 4", textAlign: "center" }}>V</div>
          <div className="bk-col-head" style={{ gridColumn: "4 / 5", textAlign: "center" }}>F</div>

          {items.map(([txt], i) => (
            <U_F key={i}>
              <PackNumTag n={i + 1} tone={i % 2 ? "orange" : "primary"} />
              <span style={{ paddingRight: "0.1in" }}>{txt}</span>
              <span style={{ display: "grid", placeItems: "center" }}><VFCell /></span>
              <span style={{ display: "grid", placeItems: "center" }}><VFCell /></span>
            </U_F>
          ))}
        </div>

        <div style={{ marginTop: "0.14in" }}>
          <div className="bk-col-head" style={{ marginBottom: "0.06in" }}>Justification (1 ligne pour chaque « Faux ») </div>
          <WritingLines lines={4} />
        </div>
      </ExerciseCard>

      <Cols ratio="1fr 1fr">
        <ScoreBox label="Mon score" message="Affirmations correctes + justifications justes." />
        <KettyBubble pose="pointing" tone="yellow" label="Méthode Ketty">
          Une affirmation est <b>fausse</b> dès qu'<b>un seul</b> mot la rend
          incorrecte. Cherche ce mot avant de te décider.
        </KettyBubble>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   6 · MINI-SUJET EXAMEN  (template: "mini-exam")
   ════════════════════════════════════════════════════════════════════════ */
function U6Exam({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Maths" niveau="3ᵉ" page={"Sujet 02 · Page 1/2"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="brevet-blanc-02"
      qrLabel="Correction détaillée"
      scale={scale}
    >
      <PageTitle
        eyebrow="Brevet blanc · Sujet 02"
        title="Entraînement type examen"
        subtitle="3 exercices indépendants. Calculatrice autorisée. Tu peux traiter dans n'importe quel ordre."
      />

      <Box tone="primary" label="Conditions" iconKey="icon-horloge">
        <div style={{
          display: "grid", gridTemplateColumns: "1fr 1fr 1fr",
          gap: "0.12in", fontSize: "var(--ae-fs-body)",
        }}>
          <div>
            <div style={{ fontFamily: "var(--ae-font-title)", fontWeight: 600, color: "var(--ae-primary-deep)" }}>Durée conseillée</div>
            <div>45 minutes</div>
          </div>
          <div>
            <div style={{ fontFamily: "var(--ae-font-title)", fontWeight: 600, color: "var(--ae-primary-deep)" }}>Barème</div>
            <div>Ex. 1 : /6 · Ex. 2 : /7 · Ex. 3 : /7 — Total /20</div>
          </div>
          <div>
            <div style={{ fontFamily: "var(--ae-font-title)", fontWeight: 600, color: "var(--ae-primary-deep)" }}>Matériel</div>
            <div>Calculatrice, règle, équerre. Brouillon à part.</div>
          </div>
        </div>
      </Box>

      <ExerciseCard number={1} consigne="Calcul littéral · Développer et réduire.">
        <div className="bk-text">
          a) Développe : (2x + 3)(x − 5).<br/>
          b) Réduis : 4x² − 3x + 2x² + 7x − 1.<br/>
          c) Factorise : 9x² − 25.
        </div>
        <AnswerBox height="0.6in" ruled />
      </ExerciseCard>

      <ExerciseCard number={2} tone="orange" consigne="Théorème de Pythagore — ABC rectangle en A, AB = 6 cm, BC = 10 cm.">
        <div className="bk-text">
          a) Calcule AC. b) Calcule l'aire du triangle ABC.
        </div>
        <AnswerBox height="0.6in" ruled />
      </ExerciseCard>

      <ExerciseCard number={3} consigne="Statistiques · Série : 12 · 14 · 9 · 15 · 11 · 13 · 10.">
        <div className="bk-text">
          a) Moyenne. b) Médiane. c) Étendue.
        </div>
        <AnswerBox height="0.55in" ruled />
      </ExerciseCard>

      <Box tone="primary" label="Stratégie examen" iconKey="icon-info">
        <p style={{ fontSize: "var(--ae-fs-body)", margin: 0, lineHeight: 1.4 }}>
          Lis les 3 exercices avant de démarrer. Commence par celui que tu maîtrises
          le mieux pour sécuriser des points. Reporte ton total après auto-correction au QR.
        </p>
      </Box>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   7 · ÉTUDE DE DOCUMENT  (template: "document-study")
   ════════════════════════════════════════════════════════════════════════ */
function DocSourceCard() {
  return (
    <div style={{
      width: "100%", height: "100%", padding: "0.18in 0.22in",
      background: "white", color: "var(--ae-text)",
      fontFamily: "var(--ae-font-body)", fontSize: "0.13in",
      lineHeight: 1.45, position: "relative",
    }}>
      <div style={{
        fontFamily: "var(--ae-font-title)", fontWeight: 600,
        color: "var(--ae-primary-deep)", fontSize: "0.16in",
        marginBottom: "0.08in",
      }}>« Lettre d'un poilu à sa femme — 14 octobre 1916 »</div>
      <p style={{ margin: 0 }}>
        « Ma chère Marie, voilà six jours que nous tenons la cote 304. Les obus
        tombent sans relâche, mais le moral des hommes est bon. La pluie a fait
        des tranchées une mer de boue. J'ai reçu ton colis hier — le pain et la
        confiture ont été partagés avec mes camarades, ils t'embrassent tous.
        Le caporal Lefèvre, qui était de Verdun, m'a dit hier que les Allemands
        étaient à bout : c'est ce qui nous fait tenir. Embrasse les enfants. »
      </p>
      <p style={{ margin: "0.1in 0 0", fontStyle: "italic", color: "var(--ae-text-soft)" }}>
        — Henri Vaillant, soldat au 152ᵉ régiment d'infanterie.
      </p>
    </div>
  );
}

function U7Document({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Histoire" niveau="3ᵉ" page={"Chapitre 4 · Page 58"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="hist-3-poilu"
      scale={scale}
    >
      <PageTitle
        eyebrow="Étude de document · Première Guerre mondiale"
        title="La voix d'un poilu, octobre 1916"
        subtitle="Lis le document, identifie sa nature, puis réponds aux questions."
      />

      <FigureBlock label="Document 1" caption="Source : lettre privée transmise par la famille Vaillant. Bataille de Verdun (1916)." height="2.0in">
        <DocSourceCard />
      </FigureBlock>

      <Cols ratio="0.7fr 1fr">
        <Box tone="cool" label="Identifier le document" iconKey="icon-info">
          <ul className="bk-bullets" style={{ marginBottom: 0 }}>
            <li><span className="dot"></span><b>Nature :</b> <AnswerLine width="1.6in" /></li>
            <li><span className="dot"></span><b>Auteur :</b> <AnswerLine width="1.6in" /></li>
            <li><span className="dot"></span><b>Date :</b> <AnswerLine width="0.9in" /></li>
            <li><span className="dot"></span><b>Lieu :</b> <AnswerLine width="1.4in" /></li>
          </ul>
        </Box>
        <ExerciseCard number={1} consigne="Comprendre le document.">
          <QInput n={1}>Quels éléments du quotidien des soldats sont décrits ?</QInput>
          <AnswerBox height="0.55in" ruled />
          <QInput n={2}>Que révèle la lettre sur le moral des troupes ?</QInput>
          <AnswerBox height="0.55in" ruled />
        </ExerciseCard>
      </Cols>

      <Cols ratio="1fr 1fr">
        <ExerciseCard number={2} tone="orange" consigne="Analyser et mettre en perspective.">
          <QInput n={3}>En quoi cette lettre est-elle un témoignage <i>partial</i> ?</QInput>
          <AnswerBox height="0.55in" ruled />
        </ExerciseCard>
        <Box tone="green" label="Conclusion en 2 phrases" iconKey="icon-fleche">
          <AnswerBox height="0.7in" ruled />
        </Box>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   8 · EXPRESSION ÉCRITE  (template: "writing-task")
   ════════════════════════════════════════════════════════════════════════ */
function U8Writing({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Français" niveau="4ᵉ" page={"Chapitre 6 · Page 71"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="fr-4-recit-fantastique"
      qrLabel="Exemples de copies"
      scale={scale}
    >
      <PageTitle
        eyebrow="Expression écrite · Le récit fantastique"
        title="Une nuit étrange à la bibliothèque"
        subtitle="Rédige un court récit fantastique en t'appuyant sur les codes du genre."
      />

      <Box tone="primary" label="Sujet" iconKey="icon-section">
        <p style={{ fontSize: "var(--ae-fs-body)", lineHeight: 1.45, margin: 0 }}>
          Tu rentres tard de la bibliothèque municipale après une journée de
          révisions. La porte se referme derrière toi. Soudain, l'un des livres
          glisse seul de l'étagère. <b>Raconte la suite</b> en respectant les
          codes du récit fantastique : un cadre réaliste, un évènement
          inexplicable, l'hésitation du narrateur entre rationnel et surnaturel.
        </p>
      </Box>

      <Cols ratio="1fr 1fr">
        <MethodSteps
          title="Consignes de rédaction"
          steps={[
            "Récit à la première personne, au passé.",
            "Entre 200 et 300 mots, soit 25 à 35 lignes.",
            "Au moins une description sensorielle (bruit, odeur, lumière).",
            "Ne révèle pas tout : laisse planer le doute jusqu'à la fin.",
          ]}
        />
        <SelfCheck
          title="Critères de réussite"
          items={[
            "Les temps du passé sont correctement utilisés.",
            "Le cadre réaliste est posé dès les premières lignes.",
            "L'évènement étrange crée une vraie tension.",
            "La chute laisse le lecteur dans l'incertitude.",
          ]}
        />
      </Cols>

      <ExerciseCard number={1} consigne="Mon brouillon → ma version définitive (recopie au propre).">
        <WritingLines lines={9} />
      </ExerciseCard>

      <Cols ratio="1fr 1fr">
        <Box tone="cool" label="Mon auto-évaluation" iconKey="icon-check">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 0.32in", rowGap: "0.06in", fontSize: "var(--ae-fs-body)" }}>
            <span>Mon récit respecte le genre fantastique.</span><span style={{ display: "grid", placeItems: "center" }}><VFCell /></span>
            <span>Mes phrases sont correctement ponctuées.</span><span style={{ display: "grid", placeItems: "center" }}><VFCell /></span>
            <span>J'ai relu et corrigé les fautes d'accord.</span><span style={{ display: "grid", placeItems: "center" }}><VFCell /></span>
          </div>
        </Box>
        <KettyBubble pose="thinking" tone="yellow" label="Conseil rédaction">
          Avant la version au propre, écris ton brouillon et lis-le à voix
          haute : tu repèreras les répétitions et les phrases trop longues.
        </KettyBubble>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   9 · CARTE MENTALE  (template: "mindmap")
   ════════════════════════════════════════════════════════════════════════ */
function MindMapSVG() {
  // 6 branches autour d'un nœud central — version "universelle" (claire & aérée)
  const branches = [
    { x:  90, y:  60, label: "Causes",        color: "var(--ae-primary)",      align: "end" },
    { x: 360, y:  60, label: "Acteurs",       color: "var(--ae-orange)",       align: "start" },
    { x:  60, y: 160, label: "Dates clés",    color: "var(--ae-primary)",      align: "end" },
    { x: 390, y: 160, label: "Lieux",         color: "var(--ae-orange)",       align: "start" },
    { x:  90, y: 260, label: "Conséquences",  color: "var(--ae-primary)",      align: "end" },
    { x: 360, y: 260, label: "Mots clés",     color: "var(--ae-orange)",       align: "start" },
  ];
  return (
    <svg viewBox="0 0 460 320" style={{ width: "100%", height: "100%" }}>
      {/* central node */}
      <ellipse cx="225" cy="160" rx="92" ry="40" fill="var(--ae-primary)" />
      <text x="225" y="155" textAnchor="middle"
            fontFamily="var(--ae-font-title)" fontWeight="700"
            fontSize="16" fill="white">Sujet central</text>
      <text x="225" y="175" textAnchor="middle"
            fontFamily="var(--ae-font-body)" fontSize="11" fill="rgba(255,255,255,.85)">
        À écrire au centre
      </text>

      {/* branches */}
      {branches.map((b, i) => {
        const cx = 225, cy = 160;
        return (
          <g key={i}>
            <path d={`M ${cx} ${cy} Q ${(cx+b.x)/2} ${b.y} ${b.x} ${b.y}`}
                  stroke={b.color} strokeWidth="2" fill="none" strokeLinecap="round" />
            <circle cx={b.x} cy={b.y} r="6" fill={b.color} />
            <text x={b.align === "end" ? b.x - 14 : b.x + 14} y={b.y + 5}
                  textAnchor={b.align}
                  fontFamily="var(--ae-font-title)"
                  fontWeight="600" fontSize="14" fill="var(--ae-text)">
              {b.label}
            </text>
            {/* sub-line */}
            <line x1={b.align === "end" ? b.x - 14 : b.x + 14}
                  x2={b.align === "end" ? b.x - 100 : b.x + 100}
                  y1={b.y + 18} y2={b.y + 18}
                  stroke="var(--ae-border)" strokeWidth="1" strokeDasharray="3 3" />
          </g>
        );
      })}
    </svg>
  );
}

function U9Mindmap({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Histoire" niveau="1ʳᵉ" page={"Synthèse · Page 96"}
      density={density} qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="synth-hist-revolution"
      qrLabel="Carte modèle complète"
      scale={scale}
    >
      <PageTitle
        eyebrow="Carte mentale · Révision finale"
        title="Organise tes idées en une seule page"
        subtitle="6 branches autour du sujet central. Une couleur = une grande catégorie."
      />

      <ObjectiveBox
        title="Mode d'emploi"
        items={[
          "Écris le sujet central dans l'ellipse bleue.",
          "Sur chaque branche, écris 3 à 5 mots-clés maximum, jamais des phrases.",
          "Relie les idées entre elles si tu vois une connexion.",
        ]}
      />

      <FigureBlock label="Mon schéma à compléter" caption="Couleur bleue = analyse · Couleur orange = repères. Ne te perds pas dans le détail." height="3.0in">
        <MindMapSVG />
      </FigureBlock>

      <Cols ratio="1fr 1fr">
        <ChallengeBox title="Branche personnelle">
          Ajoute une 7ᵉ branche avec ton angle préféré du chapitre, en 3
          mots-clés.
          <div style={{ marginTop: "0.08in" }}><AnswerBox height="0.5in" ruled /></div>
        </ChallengeBox>
        <KettyBubble pose="pointing" tone="primary" label="Astuce révision">
          Une carte mentale efficace tient sur <b>une seule page</b>. Si tu
          écris des phrases entières, ce n'est plus une carte mais un cours.
        </KettyBubble>
      </Cols>
    </PageShell>
  );
}

/* ════════════════════════════════════════════════════════════════════════
   10 · CORRECTION COMPACTE  (template: "compact-correction")
   ════════════════════════════════════════════════════════════════════════ */
function CorrigeCard({ ref: refId, title, color = "primary", children }) {
  return (
    <div style={{
      border: "1.4px solid var(--ae-border)",
      borderTop: `3px solid ${color === "orange" ? "var(--ae-orange)" : "var(--ae-primary)"}`,
      background: "white", borderRadius: "0.08in",
      padding: "0.1in 0.12in",
    }}>
      <div style={{
        display: "flex", alignItems: "center", gap: "0.08in",
        marginBottom: "0.06in",
      }}>
        <span style={{
          fontFamily: "var(--ae-font-title)", fontWeight: 700,
          color: color === "orange" ? "var(--ae-orange)" : "var(--ae-primary-deep)",
          fontSize: "0.13in",
        }}>{refId}</span>
        <span style={{
          fontFamily: "var(--ae-font-title)", fontWeight: 600,
          color: "var(--ae-text)", fontSize: "0.13in",
        }}>{title}</span>
      </div>
      <div style={{ fontSize: "0.115in", lineHeight: 1.4, color: "var(--ae-text)" }}>
        {children}
      </div>
    </div>
  );
}

function U10Compact({ density, qrVisible, scoreVisible, scale = 1 }) {
  return (
    <PageShell
      matiere="Maths" niveau="3ᵉ" page={"Corrigés · Page 138"}
      density="dense" qrVisible={qrVisible} scoreVisible={scoreVisible}
      qrSlug="cor-3-chap-09"
      qrLabel="Corrections enrichies"
      qrSubLabel="Vidéos étape par étape"
      scale={scale}
    >
      <PageTitle
        eyebrow="Corrections · Chapitre 9 — Pythagore &amp; Thalès"
        title="Corrections compactes · pages 122 → 134"
        subtitle="Réponses essentielles + 1 étape clé par exercice. Détail complet via le QR."
      />

      <Box tone="cool" label="Pages corrigées" iconKey="icon-livre">
        <div style={{ display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: "0.08in", fontSize: "0.115in" }}>
          {[
            ["Ex. 1", "p. 122"], ["Ex. 2", "p. 124"], ["Ex. 3", "p. 126"],
            ["Ex. 4", "p. 128"], ["Ex. 5", "p. 131"], ["Ex. 6", "p. 134"],
          ].map(([n, p]) => (
            <div key={n} style={{
              padding: "0.06in 0.08in", borderRadius: "0.06in",
              background: "white", border: "1px solid var(--ae-border)",
              fontFamily: "var(--ae-font-title)", fontWeight: 600,
              color: "var(--ae-primary-deep)", textAlign: "center",
            }}>
              <div>{n}</div>
              <div style={{ fontSize: "0.1in", color: "var(--ae-text-soft)", fontWeight: 400 }}>{p}</div>
            </div>
          ))}
        </div>
      </Box>

      <div style={{
        display: "grid", gridTemplateColumns: "1fr 1fr",
        gap: "0.12in",
      }}>
        <CorrigeCard refId="Ex. 1" title="Pythagore — calcul d'hypoténuse">
          AB = 6 cm, AC = 8 cm → BC² = 36 + 64 = 100, donc <b>BC = 10 cm</b>.<br/>
          <i>Étape clé :</i> bien identifier les deux côtés adjacents à l'angle droit.
        </CorrigeCard>
        <CorrigeCard refId="Ex. 2" title="Pythagore — réciproque" color="orange">
          5² + 12² = 25 + 144 = 169 = 13². <b>Triangle rectangle</b> (en l'angle opposé à 13).<br/>
          <i>Étape clé :</i> comparer la somme des carrés des petits côtés au carré du plus grand.
        </CorrigeCard>
        <CorrigeCard refId="Ex. 3" title="Thalès — segment manquant">
          AM/AB = AN/AC → 3/9 = AN/12 → <b>AN = 4 cm</b>.<br/>
          <i>Étape clé :</i> écrire le rapport dans le bon sens (numérateurs sur la même demi-droite).
        </CorrigeCard>
        <CorrigeCard refId="Ex. 4" title="Thalès — réciproque" color="orange">
          On vérifie l'égalité <i>AM/AB = AN/AC</i> et l'alignement sur deux droites sécantes.<br/>
          <i>Étape clé :</i> ne pas oublier la condition d'alignement avant de conclure.
        </CorrigeCard>
        <CorrigeCard refId="Ex. 5" title="Cosinus dans un triangle rectangle">
          cos(α) = adjacent / hypoténuse = 4 / 5 → <b>α ≈ 36,9°</b>.<br/>
          <i>Étape clé :</i> identifier hypoténuse <b>avant</b> côté adjacent.
        </CorrigeCard>
        <CorrigeCard refId="Ex. 6" title="Problème combiné" color="orange">
          Hauteur du mât = 2,40 / tan(20°) ≈ <b>6,6 m</b>.<br/>
          <i>Étape clé :</i> faire un schéma à main levée avant tout calcul.
        </CorrigeCard>
      </div>

      <Cols ratio="1fr 1fr">
        <KettyBubble pose="thumbsup" tone="primary" label="Avant le contrôle">
          Recopie l'<b>étape clé</b> de chaque exercice sur ta fiche de
          révision. C'est ce que tu dois retenir, pas le résultat numérique.
        </KettyBubble>
        <Box tone="green" label="Aller plus loin" iconKey="icon-fleche">
          <p style={{ fontSize: "0.12in", margin: 0 }}>
            Pour les démonstrations détaillées, scanne le QR du footer :
            chaque exercice est repris en vidéo (1 min 30 max).
          </p>
        </Box>
      </Cols>
    </PageShell>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Export
   ──────────────────────────────────────────────────────────────────────── */
Object.assign(window, {
  U1Diagnostic, U2Problem, U3Table, U4Match, U5TrueFalse,
  U6Exam, U7Document, U8Writing, U9Mindmap, U10Compact,
});
