const TOTAL_SLIDES = 20;

function Slide01_Cover() {
  return <>
    <ChromeTop section="Waka Academy" slideNum={1} total={TOTAL_SLIDES} />
    <div className="slide-body" style={{ position: 'relative', overflow: 'hidden' }}>
      <div className="grid-bg"></div>
      <div className="orbit-ring" style={{ position: 'absolute', right: 120, top: 170, width: 520, height: 520 }}></div>

      <div style={{ position: 'relative', zIndex: 1, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 34 }}>
        <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
          <span className="tag orange">Atelier code</span>
          <span className="tag cyan">Demi-journée</span>
          <span className="tag green">Développeurs juniors</span>
        </div>

        <div style={{ maxWidth: 1320 }}>
          <div className="eyebrow">FaceCheck App</div>
          <div className="title-xl" style={{ lineHeight: 0.94 }}>
            Waka Academy<br />
            Créer une application<br />
            <span style={{ color: 'var(--waka-orange)' }}>Face Recognition</span>
          </div>
          <div className="subtitle" style={{ marginTop: 28, maxWidth: 920 }}>
            FastAPI + Azure AI Face + JSON brut
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20, maxWidth: 1120 }}>
          <div className="card surface">
            <div className="label">Objectif</div>
            <div className="body-m" style={{ marginTop: 10 }}>Construire une app web pédagogique de bout en bout.</div>
          </div>
          <div className="card surface">
            <div className="label">Focus</div>
            <div className="body-m" style={{ marginTop: 10 }}>Lire Detect, Verify, Identify via les réponses JSON.</div>
          </div>
          <div className="card surface">
            <div className="label">Cadre</div>
            <div className="body-m" style={{ marginTop: 10 }}>Consentement, minimisation et suppression des données.</div>
          </div>
        </div>
      </div>

      <div className="strip" style={{ position: 'absolute', left: 80, right: 80, bottom: 78 }}>
        Azure AI Face REST API · sorties brutes · interprétation UI · conformité
      </div>
    </div>
    <ChromeBottom chapter="Cover" />
  </>;
}

function Slide02_FaceCheckApp() {
  return <>
    <ChromeTop section="Application cible" slideNum={2} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Ce que nous construisons</div>
      <div className="title-l">FaceCheck App : une app web pour comprendre Azure AI Face</div>
      <div className="subtitle" style={{ maxWidth: 1120 }}>
        Le livrable est volontairement simple : uploader, appeler l’API, afficher le JSON brut, expliquer.
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 28, marginTop: 58 }}>
        <div className="card surface">
          <span className="tag orange">Detect</span>
          <div className="title-m" style={{ marginTop: 24 }}>Une image</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Upload d’une image, appel Detect, affichage du tableau JSON et des rectangles de visage.
          </div>
        </div>
        <div className="card surface">
          <span className="tag cyan">Verify</span>
          <div className="title-m" style={{ marginTop: 24 }}>Deux images</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Détection des deux visages, appel Verify, lecture de isIdentical et confidence.
          </div>
        </div>
        <div className="card surface">
          <span className="tag green">Identify</span>
          <div className="title-m" style={{ marginTop: 24 }}>Option</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Si l’accès est autorisé : groupe de personnes, visages consentis, candidates JSON.
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Application cible" />
  </>;
}

function Slide03_ArchitectureApp() {
  return <>
    <ChromeTop section="Architecture" slideNum={3} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Vue d’ensemble</div>
      <div className="title-l">Le flux complet : formulaire → API Azure → JSON → UI</div>
      <div className="subtitle" style={{ maxWidth: 1140 }}>
        Nous séparons interface, backend, appel REST et nettoyage des fichiers temporaires.
      </div>

      <div style={{ marginTop: 70, display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 16, alignItems: 'stretch' }}>
        {[
          ['01', 'Browser', 'Formulaire upload'],
          ['02', 'FastAPI', 'Backend atelier'],
          ['03', 'Azure AI Face', 'API REST'],
          ['04', 'JSON brut', 'Body de réponse'],
          ['05', 'UI', 'Interprétation visuelle'],
          ['06', 'Cleanup', 'Suppression temporaire']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 210 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 34, marginTop: 24 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 14 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, marginTop: 44 }}>
        <div className="card">
          <span className="tag orange">Secrets</span>
          <div className="body-m" style={{ marginTop: 14 }}>
            Endpoint et clé restent hors du code : variables d’environnement ou Key Vault.
          </div>
        </div>
        <div className="card">
          <span className="tag green">Données</span>
          <div className="body-m" style={{ marginTop: 14 }}>
            Les fichiers d’upload servent à l’appel API puis sont supprimés.
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Architecture" />
  </>;
}

function Slide04_JSONBrut() {
  return <>
    <ChromeTop section="Principe pédagogique" slideNum={4} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Pourquoi afficher le JSON ?</div>
      <div className="title-l">On apprend l’API avant de cacher sa réponse</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32, marginTop: 64 }}>
        <div className="card surface">
          <span className="tag orange">API brute</span>
          <div className="title-m" style={{ marginTop: 24 }}>Ce que l’on affiche</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Status code, quelques headers utiles, puis body JSON renvoyé par Azure AI Face.
          </div>
          <pre style={{ marginTop: 24, padding: 22, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.45 }}>{`HTTP 200
content-type: application/json

[
  { "faceId": "...", "faceRectangle": { ... } }
]`}</pre>
        </div>

        <div className="card surface">
          <span className="tag cyan">Logique app</span>
          <div className="title-m" style={{ marginTop: 24 }}>Ce que l’on transforme</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Lire les champs, dessiner un rectangle, écrire un message et gérer les erreurs.
          </div>
          <pre style={{ marginTop: 24, padding: 22, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.45 }}>{`rectangle = raw_json[0]["faceRectangle"]
message = build_message(raw_json)
return { "raw_json": raw_json, "ui": message }`}</pre>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="JSON brut" />
  </>;
}

function Slide05_CreerProjet() {
  return <>
    <ChromeTop section="Étape 1" slideNum={5} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Créer le projet</div>
      <div className="title-l">On démarre avec une structure lisible</div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 18, marginTop: 72 }}>
        {[
          ['01', 'Dossier', 'mkdir facecheck-app'],
          ['02', 'Venv', 'Créer l’environnement Python'],
          ['03', 'Packages', 'fastapi, uvicorn, requests, python-dotenv'],
          ['04', 'Backend', 'Créer app/main.py'],
          ['05', 'Front', 'templates/index.html et static/']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 260 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 34, marginTop: 24 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 14 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <pre style={{ marginTop: 52, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 20, lineHeight: 1.55 }}>{`facecheck-app/
  app/main.py
  app/face_api.py
  templates/index.html
  static/`}</pre>
    </div>
    <ChromeBottom chapter="Créer le projet" />
  </>;
}

function Slide06_PageAccueil() {
  return <>
    <ChromeTop section="Étape 2" slideNum={6} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Page d’accueil</div>
      <div className="title-l">Une route GET / sert l’interface minimale</div>
      <div className="subtitle" style={{ maxWidth: 1060 }}>
        L’objectif n’est pas le design : c’est une page claire avec Detect et Verify.
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1.05fr 0.95fr', gap: 32, marginTop: 58 }}>
        <div className="card surface">
          <span className="tag orange">app/main.py</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 19, lineHeight: 1.55 }}>{`from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates("templates")

@app.get("/")
def home(request: Request):
    return templates.TemplateResponse(
        "index.html",
        {"request": request}
    )`}</pre>
        </div>

        <div className="card surface">
          <span className="tag cyan">templates/index.html</span>
          <div className="title-m" style={{ marginTop: 24 }}>Deux zones</div>
          <div className="body-m" style={{ marginTop: 20 }}>
            Une section Detect pour une image. Une section Verify pour comparer deux images.
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 16, marginTop: 28 }}>
            <div className="card">
              <div className="label">Zone 1</div>
              <div className="body-s" style={{ marginTop: 8 }}>Upload image → /detect</div>
            </div>
            <div className="card">
              <div className="label">Zone 2</div>
              <div className="body-s" style={{ marginTop: 8 }}>Upload image A + B → /verify</div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Page accueil" />
  </>;
}

function Slide07_FormUpload() {
  return <>
    <ChromeTop section="Étape 3" slideNum={7} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Formulaires upload</div>
      <div className="title-l">Les images partent en multipart/form-data</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32, marginTop: 64 }}>
        <div className="card surface">
          <span className="tag orange">Detect</span>
          <div className="body-m" style={{ marginTop: 18 }}>
            Un seul fichier image, envoyé vers la route POST /detect.
          </div>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.45 }}>{`<form action="/detect" method="post"
      enctype="multipart/form-data">
  <input type="file" name="image">
  <button>Detect</button>
</form>`}</pre>
        </div>

        <div className="card surface">
          <span className="tag cyan">Verify</span>
          <div className="body-m" style={{ marginTop: 18 }}>
            Deux fichiers image, envoyés vers la route POST /verify.
          </div>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.45 }}>{`<form action="/verify" method="post"
      enctype="multipart/form-data">
  <input type="file" name="image_a">
  <input type="file" name="image_b">
  <button>Verify</button>
</form>`}</pre>
        </div>
      </div>

      <div className="card" style={{ marginTop: 36 }}>
        <span className="tag green">Règle atelier</span>
        <span className="body-m" style={{ marginLeft: 18 }}>
          Les fichiers sont temporaires : pas de stockage durable par défaut.
        </span>
      </div>
    </div>
    <ChromeBottom chapter="Upload" />
  </>;
}

function Slide08_ConfigAzureFace() {
  return <>
    <ChromeTop section="Étape 4" slideNum={8} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Configurer Azure AI Face</div>
      <div className="title-l">On prépare la ressource, l’accès et les contraintes d’entrée</div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 28, marginTop: 64 }}>
        <div className="card surface">
          <span className="tag orange">Ressource</span>
          <div className="title-m" style={{ marginTop: 24 }}>Azure AI Face</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Créer la ressource, puis récupérer endpoint et clé pour les appels REST.
          </div>
        </div>
        <div className="card surface">
          <span className="tag cyan">Accès</span>
          <div className="title-m" style={{ marginTop: 24 }}>Recognition</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Certaines capacités Face Recognition peuvent nécessiter un accès limité.
          </div>
        </div>
        <div className="card surface">
          <span className="tag green">Images</span>
          <div className="title-m" style={{ marginTop: 24 }}>Formats</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            JPEG, PNG, GIF première frame ou BMP. Taille image maximum : 6 MB.
          </div>
        </div>
      </div>

      <div className="quote-block" style={{ marginTop: 54 }}>
        L’atelier montre comment l’API répond. L’usage réel doit respecter consentement, finalité et suppression des données biométriques.
      </div>
    </div>
    <ChromeBottom chapter="Azure AI Face" />
  </>;
}

function Slide09_ClientFace() {
  return <>
    <ChromeTop section="Étape 5" slideNum={9} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Client REST</div>
      <div className="title-l">Un helper face_api.py centralise les appels Azure AI Face</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 0.9fr', gap: 32, marginTop: 58 }}>
        <div className="card surface">
          <span className="tag orange">app/face_api.py</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.48 }}>{`import os, requests

FACE_ENDPOINT = os.getenv("FACE_ENDPOINT")
FACE_KEY = os.getenv("FACE_KEY")

headers = {
  "Ocp-Apim-Subscription-Key": FACE_KEY,
  "Content-Type": "application/octet-stream"
}

def post_image(path, operation):
    url = f"{FACE_ENDPOINT}/face/v1.0/{operation}"
    with open(path, "rb") as image:
        return requests.post(url, headers=headers, data=image)`}</pre>
        </div>

        <div className="card surface">
          <span className="tag cyan">Secrets hors code</span>
          <div className="title-m" style={{ marginTop: 24 }}>Jamais dans Git</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            En local : variables d’environnement ou fichier .env non versionné.
          </div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Sur Azure : Key Vault ou configuration sécurisée de l’application.
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Client Face" />
  </>;
}

function Slide10_DetectRoute() {
  return <>
    <ChromeTop section="Étape 6" slideNum={10} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Route /detect</div>
      <div className="title-l">Recevoir une image, appeler Detect, retourner raw_json</div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 18, marginTop: 52 }}>
        {[
          ['01', 'UploadFile', 'FastAPI reçoit image'],
          ['02', 'Temp', 'Sauvegarde temporaire'],
          ['03', 'REST', 'Appel Detect Azure'],
          ['04', 'Retour', 'raw_json + champs utiles']
        ].map((item) =>
          <div className="card surface" key={item[0]}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 32, marginTop: 22 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 12 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <pre style={{ marginTop: 42, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.48 }}>{`@app.post("/detect")
async def detect(image: UploadFile):
    path = save_temp(image)
    response = post_image(path, "detect")
    raw_json = response.json()
    cleanup(path)
    return {
      "status_code": response.status_code,
      "raw_json": raw_json
    }`}</pre>
    </div>
    <ChromeBottom chapter="Detect route" />
  </>;
}

function Slide11_JSONDetect() {
  return <>
    <ChromeTop section="JSON Detect" slideNum={11} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Lire la réponse Detect</div>
      <div className="title-l">Detect renvoie une liste de visages détectés</div>
      <div className="subtitle">Exemple conceptuel représentatif, à afficher tel quel dans l’app.</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 0.9fr', gap: 32, marginTop: 52 }}>
        <div className="card surface">
          <span className="tag orange">raw_json</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 20, lineHeight: 1.5 }}>{`[
  {
    "faceId": "temp-face-id",
    "faceRectangle": {
      "top": 128,
      "left": 220,
      "width": 180,
      "height": 180
    }
  }
]`}</pre>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 18 }}>
          <div className="kpi">
            <div className="label">faceId</div>
            <div className="val orange">ID</div>
            <div className="body-s">Identifiant temporaire utilisable selon l’accès et l’opération.</div>
          </div>
          <div className="kpi">
            <div className="label">faceRectangle</div>
            <div className="val cyan">BOX</div>
            <div className="body-s">Coordonnées utiles pour dessiner le cadre sur l’image.</div>
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="JSON Detect" />
  </>;
}

function Slide12_AfficherRectangles() {
  return <>
    <ChromeTop section="UI Detect" slideNum={12} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Transformer sans décider</div>
      <div className="title-l">Le JSON devient un overlay visuel</div>
      <div className="subtitle" style={{ maxWidth: 1120 }}>
        On dessine ce que l’API retourne. On ne prend pas de décision biométrique.
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 18, marginTop: 70 }}>
        {[
          ['01', 'raw JSON', 'Réponse Detect'],
          ['02', 'Extraire', 'faceRectangle'],
          ['03', 'Calculer', 'Coordonnées overlay'],
          ['04', 'Afficher', 'Image + cadre'],
          ['05', 'Expliquer', 'JSON à côté']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 250 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 32, marginTop: 22 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 12 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <div className="card" style={{ marginTop: 48 }}>
        <span className="tag green">Message atelier</span>
        <span className="body-m" style={{ marginLeft: 18 }}>
          L’UI explique la donnée ; elle ne remplace pas le contrôle humain.
        </span>
      </div>
    </div>
    <ChromeBottom chapter="UI Detect" />
  </>;
}

function Slide13_VerifyRoute() {
  return <>
    <ChromeTop section="Étape 7" slideNum={13} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Route /verify</div>
      <div className="title-l">Comparer deux images en deux temps</div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 18, marginTop: 64 }}>
        {[
          ['01', 'Recevoir', 'image_a et image_b'],
          ['02', 'Detect A', 'Récupérer faceId A'],
          ['03', 'Detect B', 'Récupérer faceId B'],
          ['04', 'Verify', 'Envoyer les deux IDs'],
          ['05', 'Retour', 'JSON brut + résumé']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 250 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 31, marginTop: 22 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 12 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <pre style={{ marginTop: 48, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 18, lineHeight: 1.45 }}>{`verify_payload = {
  "faceId1": face_id_a,
  "faceId2": face_id_b
}
verify_json = post_json("verify", verify_payload).json()`}</pre>
    </div>
    <ChromeBottom chapter="Verify route" />
  </>;
}

function Slide14_JSONVerify() {
  return <>
    <ChromeTop section="JSON Verify" slideNum={14} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Lire la réponse Verify</div>
      <div className="title-l">Verify répond à une question précise</div>
      <div className="subtitle">Ces deux visages appartiennent-ils à la même personne ?</div>

      <div style={{ display: 'grid', gridTemplateColumns: '0.95fr 1.05fr', gap: 32, marginTop: 52 }}>
        <div className="card surface">
          <span className="tag orange">Exemple représentatif</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 22, lineHeight: 1.55 }}>{`{
  "isIdentical": true,
  "confidence": 0.82
}`}</pre>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 18 }}>
          <div className="card surface">
            <span className="tag cyan">isIdentical</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Résultat booléen renvoyé par l’API.
            </div>
          </div>
          <div className="card surface">
            <span className="tag green">confidence</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Score fourni par l’API dans la réponse.
            </div>
          </div>
          <div className="card surface">
            <span className="tag orange">Décision</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              L’app explique ; la décision métier reste séparée.
            </div>
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="JSON Verify" />
  </>;
}

function Slide15_ResultatUtilisateur() {
  return <>
    <ChromeTop section="UI Verify" slideNum={15} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Résultat côté utilisateur</div>
      <div className="title-l">Montrer le JSON et un message compréhensible</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32, marginTop: 64 }}>
        <div className="card surface">
          <span className="tag orange">Colonne gauche</span>
          <div className="title-m" style={{ marginTop: 24 }}>JSON brut</div>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 20, lineHeight: 1.5 }}>{`{
  "isIdentical": true,
  "confidence": 0.82
}`}</pre>
        </div>

        <div className="card surface">
          <span className="tag cyan">Colonne droite</span>
          <div className="title-m" style={{ marginTop: 24 }}>Message UI</div>
          <div className="body-m" style={{ marginTop: 18 }}>
            « Correspondance probable », « Non correspondance » ou « À vérifier ».
          </div>
          <div className="body-m" style={{ marginTop: 18 }}>
            Pas de seuil fixé dans l’atelier. On garde une revue humaine.
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Résultat utilisateur" />
  </>;
}

function Slide16_IdentifyOption() {
  return <>
    <ChromeTop section="Option Identify" slideNum={16} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Option avancée</div>
      <div className="title-l">Identify : chercher un visage dans un groupe</div>
      <div className="subtitle" style={{ maxWidth: 1120 }}>
        À utiliser uniquement si l’accès est autorisé et avec opt-in des personnes.
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 18, marginTop: 70 }}>
        {[
          ['01', 'Créer', 'Groupe / person'],
          ['02', 'Ajouter', 'Faces consenties'],
          ['03', 'Préparer', 'Train selon API'],
          ['04', 'Détecter', 'Image test'],
          ['05', 'Identifier', 'Appel Identify']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 250 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 31, marginTop: 22 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 12 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <div className="card" style={{ marginTop: 46 }}>
        <span className="tag green">Opt-in</span>
        <span className="body-m" style={{ marginLeft: 18 }}>
          Les visages du groupe doivent être consentis et liés à une finalité explicite.
        </span>
      </div>
    </div>
    <ChromeBottom chapter="Identify" />
  </>;
}

function Slide17_JSONIdentify() {
  return <>
    <ChromeTop section="JSON Identify" slideNum={17} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Lire la réponse Identify</div>
      <div className="title-l">Identify renvoie des candidats possibles</div>
      <div className="subtitle">Exemple conceptuel représentatif, à ne pas confondre avec une décision automatique.</div>

      <div style={{ display: 'grid', gridTemplateColumns: '1.05fr 0.95fr', gap: 32, marginTop: 52 }}>
        <div className="card surface">
          <span className="tag orange">raw_json</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 19, lineHeight: 1.48 }}>{`[
  {
    "faceId": "temp-face-id",
    "candidates": [
      {
        "personId": "person-id",
        "confidence": 0.78
      }
    ]
  }
]`}</pre>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 18 }}>
          <div className="card surface">
            <span className="tag cyan">candidates</span>
            <div className="body-m" style={{ marginTop: 14 }}>
              Liste de correspondances candidates pour le visage détecté.
            </div>
          </div>
          <div className="card surface">
            <span className="tag green">personId</span>
            <div className="body-m" style={{ marginTop: 14 }}>
              Identifiant de la personne candidate dans le groupe.
            </div>
          </div>
          <div className="card surface">
            <span className="tag orange">Liste vide</span>
            <div className="body-m" style={{ marginTop: 14 }}>
              Aucun candidat exploitable n’est renvoyé pour ce visage.
            </div>
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="JSON Identify" />
  </>;
}

function Slide18_ErreursAPI() {
  return <>
    <ChromeTop section="Erreurs API" slideNum={18} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Robustesse</div>
      <div className="title-l">Une bonne app affiche l’erreur brute et l’explication</div>

      <div style={{ display: 'grid', gridTemplateColumns: '0.9fr 1.1fr', gap: 32, marginTop: 54 }}>
        <div className="card surface">
          <span className="tag orange">Exemple représentatif</span>
          <pre style={{ marginTop: 24, padding: 24, background: 'var(--orbit-bg)', color: 'var(--orbit-text-2)', border: '1px solid var(--orbit-border)', borderRadius: 16, fontSize: 21, lineHeight: 1.5 }}>{`{
  "error": {
    "code": "InvalidRequest",
    "message": "Image is invalid."
  }
}`}</pre>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 18 }}>
          <div className="card surface">
            <span className="tag cyan">Accès</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Clé invalide, endpoint incorrect ou opération non autorisée.
            </div>
          </div>
          <div className="card surface">
            <span className="tag orange">Image</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Format invalide, fichier trop lourd ou image inexploitable.
            </div>
          </div>
          <div className="card surface">
            <span className="tag green">Visage</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Aucun visage détecté ou qualité insuffisante pour l’opération.
            </div>
          </div>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Erreurs API" />
  </>;
}

function Slide19_RGPDDeploiement() {
  return <>
    <ChromeTop section="RGPD + déploiement" slideNum={19} total={TOTAL_SLIDES} />
    <div className="slide-body">
      <div className="eyebrow">Mettre en ligne proprement</div>
      <div className="title-l">Sécurité, conformité et déploiement Azure</div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 18, marginTop: 64 }}>
        {[
          ['01', 'Consentement', 'Explicite avant upload'],
          ['02', 'Finalité', 'Affichée dans l’UI'],
          ['03', 'Données', 'Suppression temporaire'],
          ['04', 'Secrets', 'Key Vault'],
          ['05', 'Run', 'HTTPS + monitoring']
        ].map((item) =>
          <div className="card surface" key={item[0]} style={{ minHeight: 248 }}>
            <div className="step-num">{item[0]}</div>
            <div className="title-m" style={{ fontSize: 31, marginTop: 22 }}>{item[1]}</div>
            <div className="body-s" style={{ marginTop: 12 }}>{item[2]}</div>
          </div>
        )}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, marginTop: 46 }}>
        <div className="card">
          <span className="tag orange">Déploiement</span>
          <span className="body-m" style={{ marginLeft: 18 }}>App Service ou Container App.</span>
        </div>
        <div className="card">
          <span className="tag cyan">Logs</span>
          <span className="body-m" style={{ marginLeft: 18 }}>Minimiser les données enregistrées.</span>
        </div>
      </div>
    </div>
    <ChromeBottom chapter="RGPD + déploiement" />
  </>;
}

function Slide20_Checklist() {
  return <>
    <ChromeTop section="Checklist finale" slideNum={20} total={TOTAL_SLIDES} />
    <div className="slide-body" style={{ position: 'relative', overflow: 'hidden' }}>
      <div className="grid-bg"></div>
      <div style={{ position: 'relative', zIndex: 1 }}>
        <div className="eyebrow">Fin de l’atelier</div>
        <div className="title-xl" style={{ lineHeight: 0.92, maxWidth: 1260 }}>
          Votre app<br />
          FaceCheck<br />
          est <span style={{ color: 'var(--waka-orange)' }}>prête</span>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24, marginTop: 56 }}>
          <div className="card surface">
            <span className="tag green">Construit</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              App web, routes Detect et Verify, option Identify comprise.
            </div>
          </div>
          <div className="card surface">
            <span className="tag orange">Compris</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              JSON bruts, erreurs API, champs utiles et interprétation UI.
            </div>
          </div>
          <div className="card surface">
            <span className="tag cyan">Sécurisé</span>
            <div className="body-m" style={{ marginTop: 18 }}>
              Secrets hors code, RGPD traité, suppression et logs minimisés.
            </div>
          </div>
        </div>

        <div className="strip" style={{ marginTop: 42 }}>
          Prochaines améliorations · tests · auth · stockage chiffré si nécessaire · validation conformité
        </div>
      </div>
    </div>
    <ChromeBottom chapter="Checklist finale" />
  </>;
}

Object.assign(window, {
  Slide01_Cover,
  Slide02_FaceCheckApp,
  Slide03_ArchitectureApp,
  Slide04_JSONBrut,
  Slide05_CreerProjet,
  Slide06_PageAccueil,
  Slide07_FormUpload,
  Slide08_ConfigAzureFace,
  Slide09_ClientFace,
  Slide10_DetectRoute,
  Slide11_JSONDetect,
  Slide12_AfficherRectangles,
  Slide13_VerifyRoute,
  Slide14_JSONVerify,
  Slide15_ResultatUtilisateur,
  Slide16_IdentifyOption,
  Slide17_JSONIdentify,
  Slide18_ErreursAPI,
  Slide19_RGPDDeploiement,
  Slide20_Checklist
});
