// ─────────────────────────────────────────────────────────
// ChefVerse · Module detail screen
// Shown when a chef clicks a single module from the syllabus.
// Surfaces: status, duration, learning outcomes, prev/next modules
// (with done/current/next/locked states), and a primary CTA.
// ─────────────────────────────────────────────────────────
const MOD_STRINGS = {
  en: {
    eyebrow: "TRAINING",
    of: "of",
    in: "in",
    backToCourse: "Back to course",
    introHead: "Introduction",
    learnHere: "What this training covers",
    introLab: "Introduction",
    chapters: "Chapters in this module",
    prevNext: "Training sequence",
    prev: "Previous",
    next: "Up next",
    minutes: "min",
    start: "Start training",
    resume: "Resume module",
    rewatch: "Re-watch module",
    locked: "Locked",
    locked_sub: "Finish the previous modules to unlock this one.",
    done: "Done",
    current: "In progress",
    nextLab: "Next up",
    lockedLab: "Locked",
    chapter: "Chapter",
    seeAll: "See all trainings",
    practice: "Practice brief",
    practiceSub: "A short shoot-along you can finish in 30 minutes after watching.",
    chaptersDone: "chapters complete",
    chaptersShort: "chapters",
    toastChapterDone: "Chapter complete",
    toastModuleDone: "Training complete — next one unlocked",
    toastModuleSub: "Opening the next module…",
  },
  de: {
    eyebrow: "SCHULUNG",
    of: "von",
    in: "in",
    backToCourse: "Zurück zum Pfad",
    introHead: "Einführung",
    learnHere: "Was dieses Modul abdeckt",
    introLab: "Einführung",
    chapters: "Kapitel in diesem Modul",
    prevNext: "Schulungsreihenfolge",
    prev: "Zurück",
    next: "Als Nächstes",
    minutes: "Min.",
    start: "Schulung starten",
    resume: "Schulung fortsetzen",
    rewatch: "Schulung erneut ansehen",
    locked: "Gesperrt",
    locked_sub: "Schließe die vorherigen Module ab, um dieses freizuschalten.",
    done: "Fertig",
    current: "Läuft",
    nextLab: "Als Nächstes",
    lockedLab: "Gesperrt",
    chapter: "Kapitel",
    seeAll: "Alle Schulungen",
    practice: "Übungs-Brief",
    practiceSub: "Ein kurzer Mit-Dreh, den du danach in 30 Minuten schaffst.",
    chaptersDone: "Kapitel abgeschlossen",
    chaptersShort: "Kapitel",
    toastChapterDone: "Kapitel abgeschlossen",
    toastModuleDone: "Schulung abgeschlossen — nächstes freigeschaltet",
    toastModuleSub: "Öffne das nächste Modul…",
  },
  id: {
    eyebrow: "SCHULUNG",
    of: "dari",
    in: "di",
    backToCourse: "Kembali ke jalur",
    introHead: "Pendahuluan",
    learnHere: "Yang dibahas di modul ini",
    introLab: "Pengantar",
    chapters: "Bab di modul ini",
    prevNext: "Urutan modul",
    prev: "Sebelumnya",
    next: "Berikutnya",
    minutes: "mnt",
    start: "Mulai modul",
    resume: "Lanjut modul",
    rewatch: "Tonton ulang",
    locked: "Terkunci",
    locked_sub: "Selesaikan modul sebelumnya untuk membuka yang ini.",
    done: "Selesai",
    current: "Sedang dijalani",
    nextLab: "Berikutnya",
    lockedLab: "Terkunci",
    chapter: "Bab",
    seeAll: "Semua pelatihan",
    practice: "Brief latihan",
    practiceSub: "Shoot-along singkat yang bisa kamu selesaikan dalam 30 menit.",
    chaptersDone: "bab selesai",
    chaptersShort: "bab",
    toastChapterDone: "Bab selesai",
    toastModuleDone: "Modul selesai — yang berikutnya terbuka",
    toastModuleSub: "Membuka modul berikutnya…",
  },
};

// Brief outcome bullets per module index (kept locale-agnostic, short)

// Intro paragraphs per module — matches the UFS template ("Brief summary, max 100 words")
// Shown above the "What this training covers" bullets. Indexed by [courseId][moduleIdx].
const MOD_INTROS = {
  fundamentals: [
    {
      en: "Most kitchen lighting fights you on camera. This module shows you how to read your space in 60 seconds, spot the colour casts that ruin a clean white plate, and find the one surface in your kitchen that's already studio-ready. By the end, you'll know exactly where to set up before you press record.",
      de: "Die meiste Küchenbeleuchtung arbeitet vor der Kamera gegen dich. Dieses Modul zeigt dir, wie du deinen Raum in 60 Sekunden liest, die Farbstiche erkennst, die einen sauberen weißen Teller ruinieren, und die eine Stelle in deiner Küche findest, die bereits studio-reif ist. Am Ende weißt du genau, wo du dich aufstellen musst, bevor du auf Aufnahme drückst.",
      id: "Sebagian besar pencahayaan dapur justru menyulitkan saat direkam. Modul ini menunjukkan cara membaca ruangmu dalam 60 detik, mengenali cast warna yang merusak piring putih bersih, dan menemukan satu permukaan di dapurmu yang sudah siap-studio. Di akhir, kamu akan tahu persis di mana harus setup sebelum mulai merekam.",
    },
    {
      en: "Composition isn't art-school theory — it's the difference between a viewer scrolling past and stopping. This module walks the pass with you, teaching how to spot the one frame that actually works, build a key light from a window plus a warm bulb, and position yourself relative to the food so the dish stays the hero.",
      de: "Komposition ist keine Kunstschultheorie — sie ist der Unterschied zwischen Wegscrollen und Anhalten. Dieses Modul geht mit dir den Pass entlang, zeigt dir, wie du das eine funktionierende Bild erkennst, ein Schlüssellicht aus Fenster und warmer Glühbirne baust und dich relativ zum Essen positionierst, damit das Gericht der Star bleibt.",
      id: "Komposisi bukan teori sekolah seni — ini perbedaan antara penonton scroll dan berhenti. Modul ini mengajak kamu jalan di pass, mengajarkan cara menemukan satu frame yang benar-benar bekerja, membangun key light dari jendela plus lampu hangat, dan memposisikan diri terhadap makanan agar hidangan tetap jadi bintang.",
    },
    {
      en: "Phones are powerful — but their defaults are designed for selfies, not for chef content. This module locks down the four settings that turn your phone into a real camera: exposure that doesn't drift, frame rates that match your edit, white balance that keeps the white plate white, and the auto-correct toggles you need off.",
      de: "Handys sind leistungsstark — aber ihre Standardeinstellungen sind für Selfies gemacht, nicht für Chef-Content. Dieses Modul fixiert die vier Einstellungen, die dein Handy in eine echte Kamera verwandeln: stabile Belichtung, Bildraten passend zum Schnitt, Weißabgleich, der den weißen Teller weiß lässt, und die Auto-Korrektur-Schalter, die du ausschalten musst.",
      id: "HP itu powerful — tapi pengaturan default-nya dirancang untuk selfie, bukan untuk konten chef. Modul ini mengunci empat pengaturan yang mengubah HP-mu jadi kamera sungguhan: exposure yang tidak goyang, frame rate yang sesuai edit, white balance yang menjaga piring putih tetap putih, dan toggle auto-correct yang perlu dimatikan.",
    },
    {
      en: "A wobbly handheld shot kills authority. This module shows you how to steady your phone with what's already on the line — a sheet pan, a stack of cutting boards, a wall corner — and hold a 30-second take without buying a tripod. Practical stability tricks built around how a real kitchen actually flows.",
      de: "Eine wackelige Aufnahme zerstört die Autorität. Dieses Modul zeigt dir, wie du dein Handy mit dem stabilisierst, was bereits auf dem Pass liegt — ein Backblech, ein Stapel Schneidebretter, eine Wandecke — und eine 30-Sekunden-Aufnahme ohne Stativ hältst. Praktische Stabilitäts-Tricks, gebaut um den Ablauf einer echten Küche.",
      id: "Shot handheld yang goyang mematikan otoritas. Modul ini menunjukkan cara menstabilkan HP-mu dengan apa yang sudah ada di line — loyang, tumpukan talenan, sudut dinding — dan memegang take 30-detik tanpa beli tripod. Trik stabilitas praktis dibangun seputar alur dapur sungguhan.",
    },
    {
      en: "Three angles do 90% of the work in food content: side-light, top-down, and the 30° hero shot. This module shows you when to use each, how to build a three-shot sequence in a single frame, and how to direct the viewer's eye to the exact part of the dish you're proudest of.",
      de: "Drei Winkel erledigen 90% der Arbeit in Food-Content: Seitenlicht, Top-Down und der 30°-Hero-Shot. Dieses Modul zeigt dir, wann du welchen einsetzt, wie du eine Drei-Shot-Sequenz in einem einzigen Frame baust und wie du den Blick des Betrachters genau auf den Teil des Gerichts lenkst, auf den du am stolzesten bist.",
      id: "Tiga sudut mengerjakan 90% pekerjaan di konten makanan: side-light, top-down, dan 30° hero shot. Modul ini menunjukkan kapan menggunakan masing-masing, cara membangun sekuens tiga-shot dalam satu frame, dan cara mengarahkan mata penonton ke bagian hidangan yang paling kamu banggakan.",
    },
    {
      en: "Editing during prep — not after service — is what separates chefs who post consistently from those who don't. This module shows you a 10-minute edit ritual you can actually run on a phone between covers: trim, cut to the beat, add captions, export, ship.",
      de: "Schnitt während der Prep — nicht nach dem Service — ist das, was Köche, die konsistent posten, von denen unterscheidet, die es nicht tun. Dieses Modul zeigt dir ein 10-Minuten-Schnitt-Ritual, das du tatsächlich auf einem Handy zwischen den Gedecken durchziehen kannst: Trimmen, im Takt schneiden, Untertitel hinzufügen, exportieren, abschicken.",
      id: "Edit saat prep — bukan setelah service — yang membedakan chef yang posting konsisten dari yang tidak. Modul ini menunjukkan ritual edit 10-menit yang benar-benar bisa kamu jalankan di HP antar cover: trim, potong sesuai beat, tambah caption, export, kirim.",
    },
    {
      en: "Captions earn the watch even when sound is off. This module teaches the read-speed test — captions that work in 1.6 seconds — and how to localise your tone for EN, DE, and ID without losing the chef's voice. Burned-in captions that pass for any aspect ratio.",
      de: "Untertitel verdienen das Anschauen, selbst wenn der Ton aus ist. Dieses Modul lehrt den Lesegeschwindigkeitstest — Untertitel, die in 1,6 Sekunden funktionieren — und wie du deinen Ton für EN, DE und ID lokalisierst, ohne die Stimme des Koches zu verlieren. Eingebrannte Untertitel, die für jedes Seitenverhältnis funktionieren.",
      id: "Caption memenangkan tontonan bahkan saat suara mati. Modul ini mengajarkan tes kecepatan baca — caption yang bekerja dalam 1.6 detik — dan cara melokalisasi tone-mu untuk EN, DE, dan ID tanpa kehilangan suara chef. Burned-in caption yang lolos untuk aspect ratio apa pun.",
    },
    {
      en: "Your first brief submission is the moment everything you've learned becomes real. This module walks you through choosing the right brief for your style, what brands look for, how to prep your submission, and what happens after you hit send.",
      de: "Deine erste Briefing-Einreichung ist der Moment, in dem alles, was du gelernt hast, real wird. Dieses Modul führt dich durch die Auswahl des richtigen Briefings für deinen Stil, worauf Marken achten, wie du deine Einreichung vorbereitest und was passiert, nachdem du auf Senden drückst.",
      id: "Submisi brief pertamamu adalah momen di mana semua yang kamu pelajari jadi nyata. Modul ini memandumu memilih brief yang tepat untuk style-mu, apa yang brand cari, cara prep submisi-mu, dan apa yang terjadi setelah kamu kirim.",
    },
  ],
  story: [
    { en: "Every video earns three seconds. This module teaches you how to open with the one image that makes a viewer stop scrolling — replacing the cold open with a working hook you can audit in two reads.", de: "Jedes Video verdient drei Sekunden. Dieses Modul lehrt dich, mit dem einen Bild zu öffnen, das einen Betrachter aufhören lässt zu scrollen.", id: "Setiap video memenangkan tiga detik. Modul ini mengajarkan cara membuka dengan satu gambar yang membuat penonton berhenti scroll." },
    { en: "The thumbnail does what 30 seconds of footage can't. This module shows you how to pick the moment that pulls a thumb back from a feed — even from a quick 30-second shoot.", de: "Das Thumbnail macht, was 30 Sekunden Material nicht können. Dieses Modul zeigt dir, wie du den Moment auswählst, der einen Daumen vom Feed zurückzieht.", id: "Thumbnail melakukan yang tidak bisa dilakukan 30 detik footage. Modul ini menunjukkan cara memilih momen yang menarik jempol kembali dari feed." },
    { en: "Rhythm is invisible until it isn't. This module teaches you how to stack 12 frames into a beat people feel — using cuts to manufacture tension and vary shot length on purpose.", de: "Rhythmus ist unsichtbar, bis er es nicht ist. Dieses Modul lehrt dich, 12 Frames zu einem Takt zu stapeln, den Leute fühlen.", id: "Ritme tidak terlihat sampai terlihat. Modul ini mengajarkan cara menumpuk 12 frame menjadi beat yang dirasakan orang." },
    { en: "Voice-over versus on-camera isn't preference — it's situational. This module helps you choose voice-over when the kitchen is loud, on-camera when the room is calm, and match the audio to the energy of the dish.", de: "Voice-over vs. On-Camera ist keine Vorliebe — es ist situativ. Dieses Modul hilft dir, Voice-over zu wählen, wenn die Küche laut ist, On-Camera, wenn der Raum ruhig ist.", id: "Voice-over versus on-camera bukan preferensi — itu situasional. Modul ini membantu memilih voice-over saat dapur ramai, on-camera saat ruangan tenang." },
    { en: "Pace is a kitchen skill. This module shows you how to pick a pace your viewer can chew on, slow down for texture, speed up for action, and read pace off your own breath.", de: "Tempo ist eine Küchenfähigkeit. Dieses Modul zeigt dir, wie du ein Tempo wählst, an dem dein Betrachter kauen kann.", id: "Pace adalah skill dapur. Modul ini menunjukkan cara memilih pace yang bisa dikunyah penonton." },
    { en: "Platforms reward and punish different things. This module teaches you which rule each platform enforces, when to break one on purpose, and how to track which break earned the watch-time.", de: "Plattformen belohnen und bestrafen Verschiedenes. Dieses Modul lehrt dich, welche Regel jede Plattform durchsetzt.", id: "Platform menghargai dan menghukum hal berbeda. Modul ini mengajarkan aturan mana yang ditegakkan setiap platform." },
    { en: "Endings drive saves, not likes. This module teaches you how to end on a frame that earns the save, test two endings on the same shoot, and resist the urge to wave goodbye.", de: "Endings treiben Saves, nicht Likes. Dieses Modul lehrt dich, mit einem Frame zu enden, der das Save verdient.", id: "Ending mendorong save, bukan like. Modul ini mengajarkan cara mengakhiri dengan frame yang memenangkan save." },
    { en: "Theory ends here. This module ships your first 30-second recipe story this week — with a peer review built in, and a framework for taking feedback without flinching.", de: "Theorie endet hier. Dieses Modul liefert deine erste 30-Sekunden-Rezeptgeschichte diese Woche aus.", id: "Teori berakhir di sini. Modul ini mengirim cerita resep 30-detik pertamamu minggu ini." },
  ],
  voice: [
    { en: "Speaking to everyone reaches no one. This module helps you pick one viewer, write to them, and drop the crowd-pleaser instincts that flatten your voice on camera.", de: "Zu allen sprechen erreicht niemanden. Dieses Modul hilft dir, einen Betrachter auszuwählen und ihm zu schreiben.", id: "Berbicara ke semua orang tidak menjangkau siapa pun. Modul ini membantu memilih satu penonton dan menulis untuknya." },
    { en: "Scripts make you sound like a script. This module shows you how to hold structure with bullet points instead of full lines, and trust your training over your wording.", de: "Skripte lassen dich wie ein Skript klingen. Dieses Modul zeigt, wie du Struktur mit Aufzählungspunkten hältst.", id: "Skrip membuatmu terdengar seperti skrip. Modul ini menunjukkan cara mempertahankan struktur dengan bullet point." },
    { en: "Camera anxiety is breath anxiety. This module teaches you to find your camera-breathing pattern, pace the line without rushing it, and pause without panicking.", de: "Kameraangst ist Atemangst. Dieses Modul lehrt dich, dein Kamera-Atemmuster zu finden.", id: "Kecemasan kamera adalah kecemasan nafas. Modul ini mengajarkan cara menemukan pola nafas kameramu." },
    { en: "Watching yourself is the worst part of this work — and the most useful. This module teaches you to read your own video without flinching, and spot what's not 'you' in 60 seconds.", de: "Sich selbst zu sehen ist der schlimmste Teil dieser Arbeit — und der nützlichste.", id: "Menonton diri sendiri adalah bagian terburuk dari pekerjaan ini — dan paling berguna." },
    { en: "Mistakes are the take, not the take after. This module teaches you to recover a take after a mid-sentence break, use the break instead of hiding it, and plan a fallback line.", de: "Fehler sind die Aufnahme, nicht die Aufnahme danach.", id: "Kesalahan adalah take itu, bukan take setelahnya." },
  ],
  edit: [
    { en: "Editing kills more posts than filming. This module teaches you a 10-minute edit ritual you can run end-to-end on a phone, skip the parts editors do for ego, and ship before service — not after.", de: "Editing tötet mehr Posts als Filmen. Dieses Modul lehrt dich ein 10-Minuten-Edit-Ritual.", id: "Editing membunuh lebih banyak post daripada filming. Modul ini mengajarkan ritual edit 10-menit." },
    { en: "The right track does half the work. This module shows you how to cut to the beat without thinking, pick a track that earns the edit, and sync motion to audio in 3 minutes.", de: "Der richtige Track macht die halbe Arbeit.", id: "Track yang tepat melakukan setengah pekerjaan." },
    { en: "Audio sync ruins more shoots than bad lighting. This module teaches you to sync audio without nudging clips, trust your phone's sync engine, and patch silence in two taps.", de: "Audio-Sync ruiniert mehr Aufnahmen als schlechte Beleuchtung.", id: "Sync audio merusak lebih banyak shoot daripada pencahayaan buruk." },
    { en: "Export presets save hours. This module shows you how to save export presets per platform, pick one quality bar instead of three, and skip 'final-final' versioning forever.", de: "Export-Voreinstellungen sparen Stunden.", id: "Preset export menghemat berjam-jam." },
  ],
  brand: [
    { en: "Positioning is the choice of what you won't be. This module helps you map your kitchen against three rivals, find the one promise none of them make, and decide what you'll never claim.", de: "Positionierung ist die Wahl, was du nicht sein wirst.", id: "Positioning adalah pilihan tentang apa yang tidak akan kamu jadi." },
    { en: "A one-line promise beats a brand book. This module teaches you to write your kitchen's one-line promise, audit it against a real customer, and hold the line for one full quarter.", de: "Ein einzeiliges Versprechen schlägt ein Markenbuch.", id: "Janji satu baris mengalahkan brand book." },
    { en: "Brand systems beat brand redesigns. This module shows you how to pick 5 visual decisions you won't change, stop redesigning every post, and hand the system to a freelancer.", de: "Markensysteme schlagen Marken-Neugestaltungen.", id: "Sistem brand mengalahkan redesain brand." },
    { en: "Three pillars beat seven. This module teaches you to pick 3 content pillars, hold ratio to them over a quarter, and spot when a pillar isn't earning its slot.", de: "Drei Säulen schlagen sieben.", id: "Tiga pilar mengalahkan tujuh." },
    { en: "Pricing your voice is pricing your time. This module helps you price your kitchen's voice for paid work, reject jobs that hurt the brand, and walk into a deal with one number ready.", de: "Deine Stimme zu bepreisen heißt deine Zeit zu bepreisen.", id: "Memberi harga suaramu adalah memberi harga waktumu." },
    { en: "Audiences without lists are rented attention. This module teaches you to convert audience to a wait-list, run it as a kitchen instead of a list, and earn the right to ask.", de: "Publikum ohne Listen ist gemietete Aufmerksamkeit.", id: "Audiens tanpa list adalah perhatian yang disewa." },
    { en: "Collaborations either compound or distract. This module shows you how to pick a collaborator who scales you, avoid trades that look like favors, and ship one collaboration this quarter.", de: "Kollaborationen verstärken oder lenken ab.", id: "Kolaborasi entah berlipat atau mengganggu." },
    { en: "Press is a craft. This module teaches you to pitch press without a publicist, apply only to awards you'll actually win, and show up at the festival — not the booth.", de: "Presse ist ein Handwerk.", id: "Press adalah keahlian." },
    { en: "Scale is hiring before you're ready. This module helps you build the org chart you'll need at scale, hire the role you hate doing, and document so you can step away.", de: "Skalierung heißt einstellen, bevor du bereit bist.", id: "Skala adalah merekrut sebelum kamu siap." },
    { en: "Growth needs a number, not a vibe. This module helps you plan year-one growth around one number, skip vanity metrics for cash-flow signals, and decide what year-two unlocks.", de: "Wachstum braucht eine Zahl, keine Stimmung.", id: "Pertumbuhan butuh angka, bukan vibe." },
  ],
  analytics: [
    { en: "Watch-time is the ticket window. This module teaches you to read watch-time like a ticket count, spot the cut that lost the room, and trust the data over the anecdote.", de: "Watch-Time ist das Ticket-Fenster.", id: "Watch-time adalah loket tiket." },
    { en: "The first hour beats the first day. This module shows you how to decide in the first hour of a post's life, cut budget toward the early signal, and stop A/B-ing what should be killed.", de: "Die erste Stunde schlägt den ersten Tag.", id: "Jam pertama mengalahkan hari pertama." },
    { en: "Saves are the real metric. This module teaches you to tell saves apart from follows, optimise for the metric that pays you, and stop chasing the loudest line.", de: "Saves sind die echte Metrik.", id: "Save adalah metrik sebenarnya." },
    { en: "Killing winners is a skill. This module helps you kill formats that don't earn, build the courage to drop a winner before it dies, and replace ego with a calendar.", de: "Gewinner zu töten ist eine Fähigkeit.", id: "Membunuh pemenang adalah skill." },
    { en: "Trends hide under noise. This module teaches you to read a 28-day trend without panic, spot a slow climb under daily noise, and build patience as a skill.", de: "Trends verstecken sich unter Lärm.", id: "Tren bersembunyi di balik noise." },
    { en: "One dashboard you'll actually open beats five you won't. This module shows you how to set up one dashboard, track 4 metrics instead of 40, and share it with your team.", de: "Ein Dashboard, das du tatsächlich öffnest, schlägt fünf, die du nicht öffnest.", id: "Satu dashboard yang benar-benar dibuka mengalahkan lima yang tidak." },
  ],
};

window.__MOD_INTROS__ = MOD_INTROS;
const MOD_OUTCOMES = {
  fundamentals: [
    ["Diagnose any kitchen's lighting in 60 seconds", "Spot a green-cast before you press record", "Know which surface in your kitchen is your studio"],
    ["Walk a pass and pick the one frame that works", "Use one window + one warm bulb as a key", "Place yourself relative to the food, not vice versa"],
    ["Lock exposure so the white plate stays white", "Set a frame rate that matches your edit", "Stop your phone auto-correcting your hard work"],
    ["Steady a phone with what's already on the line", "Hold a 30-second take without a tripod", "Eliminate jitter without buying anything"],
    ["Choose between side-light, top-down, and 30°", "Build a 3-shot series in one frame", "Direct the eye to the part of the dish you love"],
    ["Write captions that read in 1.6 seconds", "Localise tone for EN, DE, and ID", "Pass burned-in captions for any aspect ratio"],
  ],
  story: [
    ["Open with one image that earns 3 seconds", "Replace the cold open with a working hook", "Audit a hook in two reads"],
    ["Pick the moment that pulls a thumb back", "Build a 3-second hook from a 30-second shoot", "Stop apologising in your first frame"],
    ["Stack 12 frames into a beat people feel", "Use cuts to manufacture tension", "Vary your shot length on purpose"],
    ["Choose voice-over when the kitchen is loud", "Choose on-camera when the room is calm", "Match audio to the energy of the dish"],
    ["Pick a pace your viewer can chew on", "Slow down for the texture, speed up for action", "Read pace off your own breath"],
    ["Know which rule each platform punishes", "Break a rule on purpose, not on accident", "Track which break earned the watch-time"],
    ["End on a frame that drives saves, not likes", "Test two endings on the same shoot", "Resist the urge to wave goodbye"],
    ["Ship a 30-second recipe story this week", "Get a peer review before you post", "Take feedback without flinching"],
  ],
  voice: [
    ["Pick one viewer and write to them", "Drop crowd-pleaser instincts", "Sound like one person, not a brand"],
    ["Throw the script away after take 3", "Hold a structure with bullet points", "Trust your training, not your wording"],
    ["Find your camera-breathing pattern", "Pace the line, don't rush the line", "Pause without panicking"],
    ["Read your own video and not flinch", "Spot what's not 'you' in 60 seconds", "Edit for cadence, not perfection"],
    ["Recover a take after a mid-sentence break", "Use the break, don't hide it", "Plan a fallback line for the day it goes wrong"],
  ],
  edit: [
    ["Run the 10-minute edit ritual end-to-end", "Skip the parts editors do for ego", "Ship before service, not after"],
    ["Cut to the beat without thinking", "Pick a track that does half the work", "Sync motion to audio in 3 minutes"],
    ["Sync audio without nudging clips", "Trust your phone's sync engine", "Patch silence in two taps"],
    ["Save export presets per platform", "Pick one quality bar, not three", "Skip 'final-final' versioning"],
  ],
  brand: [
    ["Map your kitchen vs three rivals", "Find the one promise none of them make", "Decide what you won't be"],
    ["Write your one-line promise", "Audit it against a real customer", "Hold the line for one quarter"],
    ["Pick 5 visual decisions you won't change", "Stop redesigning every post", "Hand the system to a freelancer"],
    ["Pick 3 content pillars, not seven", "Hold ratio to those pillars over a quarter", "Spot when a pillar isn't earning"],
    ["Price your kitchen's voice for paid work", "Reject jobs that hurt the brand", "Walk into a deal with one number ready"],
    ["Convert audience to a wait-list", "Run the wait-list as a kitchen, not a list", "Earn the right to ask"],
    ["Pick a collaborator who scales you", "Avoid trades that look like favors", "Ship one collaboration this quarter"],
    ["Pitch press without a publicist", "Apply to one award you'll actually win", "Show up at the festival, not the booth"],
    ["Build the org chart you need at scale", "Hire the role you hate doing", "Document so you can step away"],
    ["Plan year-one growth with one number", "Skip vanity metrics for cash-flow signals", "Decide what year-two unlocks"],
  ],
  analytics: [
    ["Read watch-time like a ticket window", "Spot the cut that lost the room", "Trust the data, not the anecdote"],
    ["Decide in the first hour, not the first day", "Cut budget toward the early signal", "Stop A/B-ing what should be killed"],
    ["Tell saves apart from follows", "Optimise for the metric that pays you", "Stop chasing the loudest line"],
    ["Kill formats that don't earn", "Build the courage to drop a winner", "Replace ego with a calendar"],
    ["Read a 28-day trend without panic", "Spot a slow climb under the noise", "Build patience as a skill"],
    ["Set up the one dashboard you'll actually open", "Track 4 metrics, not 40", "Share the dashboard with your team"],
  ],
};

function ModuleDetail({ courseId, moduleIdx, locale, onBack, onOpenModule, onResume, onOpenCourseOverview }) {
  const s = MOD_STRINGS[locale];
  const t = window.I18N[locale];
  const course = window.COURSES.find(c => c.id === courseId) || window.COURSES[0];
  const pillar = window.PILLARS[course.pillar];
  const modules = window.COURSE_MODULES_BY_ID[course.id] || [];
  const progress = window.useCourseProgress();
  const mod = modules[moduleIdx] || modules[0];
  // Live status from the progress store
  const liveStatus = course.enrolled ? progress.moduleStatus(course.id, moduleIdx) : (mod.status || "next");
  const prev = moduleIdx > 0 ? { ...modules[moduleIdx - 1], status: course.enrolled ? progress.moduleStatus(course.id, moduleIdx - 1) : modules[moduleIdx - 1].status } : null;
  const next = moduleIdx < modules.length - 1 ? { ...modules[moduleIdx + 1], status: course.enrolled ? progress.moduleStatus(course.id, moduleIdx + 1) : modules[moduleIdx + 1].status } : null;
  const outcomes = (MOD_OUTCOMES[course.id] || [])[moduleIdx] || [];
  const introObj = (MOD_INTROS[course.id] || [])[moduleIdx];
  const introText = introObj ? (introObj[locale] || introObj.en) : null;
  const fundamentals = course.id === "fundamentals";
  // Use the rich chapter list for the fundamentals course's middle module — otherwise generate
  const chapters = (fundamentals && moduleIdx === 4)
    ? window.ACTIVE_MODULE.chapters
    : Array.from({ length: progress.chapterCount(course.id, moduleIdx) }, (_, i) => ({
        t: i * 90,
        en: `${s.chapter} ${i + 1}`,
        de: `${s.chapter} ${i + 1}`,
        id: `${s.chapter} ${i + 1}`,
      }));

  const isLocked = liveStatus === "locked";
  const ctaLabel =
    liveStatus === "done" ? s.rewatch :
    liveStatus === "current" ? s.resume : s.start;
  const pct = course.enrolled ? progress.modulePct(course.id, moduleIdx) : 0;
  const { done: chDone, total: chTotal } = course.enrolled ? progress.moduleChaptersDone(course.id, moduleIdx) : { done: 0, total: chapters.length };

  return (
    <div className="page md-page">
      {/* HERO */}
      <header className="md-hero">
        <div className="md-hero-cover" style={{ background: course.cover }}>
          <img src={window.PHOTOS && window.PHOTOS.moduleCoverFor(courseId, moduleIdx)} alt="" className="md-hero-img" />
          <div className="md-hero-overlay" />
          <div className="md-hero-num">{String(moduleIdx + 1).padStart(2, "0")}</div>
          {!isLocked && (
            <button className="md-hero-play" onClick={() => !isLocked && onResume(course.id, moduleIdx)} aria-label="Play">
              <Icon name="play" size={26} style={{ marginLeft: 3 }} />
            </button>
          )}
          {isLocked && (
            <div className="md-hero-lock">
              <Icon name="lock" size={26} />
            </div>
          )}
          <div className="md-hero-tag" style={{ background: "rgba(255,255,255,0.92)", color: pillar.color }}>
            {pillar.name[locale]}
          </div>
        </div>
        <div className="md-hero-meta">
          <div className="md-eyebrow-row">
            <span className="eyebrow">{s.eyebrow} {moduleIdx + 1} {s.of} {modules.length}</span>
            <ModuleStatusPill status={liveStatus} s={s} />
          </div>
          <h1 className="md-title">{mod.title[locale]}</h1>
          <p className="md-course display-italic">
            with {course.instructor}
          </p>

          {course.enrolled && !isLocked && (
            <div className="md-progress-strip">
              <div className="md-progress-meta">
                <span><Icon name="check-circle" size={13} /> {chDone} / {chTotal} {s.chapter.toLowerCase()}{chTotal === 1 ? "" : "s"}</span>
                <span className="md-progress-pct">{Math.round(pct * 100)}%</span>
              </div>
              <div className="md-progress-track">
                <div className="md-progress-fill" style={{ width: `${pct * 100}%` }} />
              </div>
            </div>
          )}

          <div className="md-stats">
            <div className="md-stat"><Icon name="clock" size={14} /> {Math.floor(mod.dur / 60)} {s.minutes}</div>
            <div className="md-stat"><Icon name="list-video" size={14} /> {chapters.length} {locale === "de" ? "Kapitel" : locale === "id" ? "bab" : "chapters"}</div>
            <div className="md-stat"><Icon name="captions" size={14} /> EN · DE · ID</div>
          </div>

          {isLocked ? (
            <div className="md-locked-card">
              <Icon name="lock" size={16} />
              <div>
                <div className="md-locked-title">{s.locked}</div>
                <div className="md-locked-sub">{s.locked_sub}</div>
              </div>
            </div>
          ) : (
            <div className="md-actions">
              <button className="btn btn-primary btn-lg" onClick={() => onResume(course.id, moduleIdx)}>
                <Icon name="play" size={14} /> {ctaLabel}
              </button>
              <button className="btn btn-outline btn-lg" onClick={onOpenCourseOverview}>
                <Icon name="list" size={14} /> {s.seeAll}
              </button>
            </div>
          )}
        </div>
      </header>

      {/* MODULE INTRO + COVERS — follows the UFS template: Introduction heading → summary paragraph → "What this training covers" → bullets */}
      {(introText || outcomes.length > 0) && (
        <section className="md-section">
          {introText && (
            <>
              <h2 className="md-h2">{s.introLab}</h2>
              <p className="md-intro">{introText}</p>
            </>
          )}
          {outcomes.length > 0 && (
            <>
              <h2 className="md-h2 md-h2-covers">{s.learnHere}</h2>
              <ul className="md-outcomes">
                {outcomes.map((o, i) => (
                  <li key={i} className="md-outcome">
                    <div className="md-outcome-pin"><Icon name="check" size={13} /></div>
                    <div>{o}</div>
                  </li>
                ))}
              </ul>
            </>
          )}
        </section>
      )}

      {/* CHAPTERS */}
      <section className="md-section">
        <h2 className="md-h2">{s.chapters}</h2>
        <ol className="md-chapters">
          {chapters.map((c, i) => {
            const chDone_i = course.enrolled && progress.isChapterDone(course.id, moduleIdx, i);
            return (
              <li key={i} className={"md-chapter" + (chDone_i ? " is-done" : "")}>
                <div className="md-chapter-num">
                  {chDone_i ? <Icon name="check" size={13} /> : String(i + 1).padStart(2, "0")}
                </div>
                <div className="md-chapter-title">{c[locale]}</div>
                <div className="md-chapter-right">
                  {chDone_i && (
                    <span className="md-chapter-pill">
                      <Icon name="check" size={10} /> {locale === "de" ? "Abgeschlossen" : locale === "id" ? "Selesai" : "Complete"}
                    </span>
                  )}
                  <span className="md-chapter-time">{fmtTime(c.t)}</span>
                </div>
              </li>
            );
          })}
        </ol>
      </section>

      {/* PRACTICE BRIEF */}
      <section className="md-section md-practice">
        <div className="md-practice-icon"><Icon name="flame" size={20} /></div>
        <div className="md-practice-body">
          <div className="md-practice-h">{s.practice}</div>
          <div className="md-practice-sub">{s.practiceSub}</div>
        </div>
      </section>

      {/* PREV / NEXT */}
      <section className="md-section">
        <h2 className="md-h2">{s.prevNext}</h2>
        <div className={"md-prev-next " + (prev && next ? "two-up" : "one-up")}>
          {prev && (
            <ModuleNavCard
              dir="prev"
              label={s.prev}
              mod={prev}
              locale={locale}
              s={s}
              onClick={() => onOpenModule(course.id, prev.idx)}
            />
          )}
          {next && (
            <ModuleNavCard
              dir="next"
              label={s.next}
              mod={next}
              locale={locale}
              s={s}
              onClick={() => onOpenModule(course.id, next.idx)}
            />
          )}
        </div>
      </section>
    </div>
  );
}

function ModuleStatusPill({ status, s }) {
  if (status === "done") {
    return <span className="md-pill done"><Icon name="check" size={11} /> {s.done}</span>;
  }
  if (status === "current") {
    return <span className="md-pill current"><span className="md-pill-dot" /> {s.current}</span>;
  }
  if (status === "next") {
    return <span className="md-pill next">{s.nextLab} <Icon name="arrow-right" size={11} /></span>;
  }
  return <span className="md-pill locked"><Icon name="lock" size={11} /> {s.lockedLab}</span>;
}

function ModuleNavCard({ dir, label, mod, locale, s, onClick }) {
  const locked = mod.status === "locked";
  return (
    <button
      className={"md-nav-card " + dir + (locked ? " is-locked" : "")}
      onClick={locked ? undefined : onClick}
      disabled={locked}
    >
      <div className="md-nav-meta">
        {dir === "prev" && <Icon name="arrow-left" size={11} />}
        <span className="md-nav-lab">{label}</span>
        {dir === "next" && <Icon name="arrow-right" size={11} />}
      </div>
      <div className="md-nav-title-row">
        <span className="md-nav-num">{String(mod.idx + 1).padStart(2, "0")}</span>
        <span className="md-nav-title">{mod.title[locale]}</span>
        {locked && <Icon name="lock" size={12} />}
      </div>
      <div className="md-nav-foot">
        <span className="md-nav-dur"><Icon name="clock" size={11} /> {Math.floor(mod.dur / 60)} {s.minutes}</span>
        <ModuleStatusPill status={mod.status} s={s} />
      </div>
    </button>
  );
}

window.MOD_STRINGS = MOD_STRINGS;
window.ModuleDetail = ModuleDetail;
window.ModuleStatusPill = ModuleStatusPill;
