/* GUIGKO App — CORE
   Data store (localStorage-persisted), hash router, i18n, toasts.
   Everything reactive: mutating the store re-renders the app.
*/

const GK = {
  colors: {
    ink: "#0B0F1F", paper: "#F5F0E6", cream: "#EDE6D6",
    flame: "#FF5A1F", signal: "#0FB76B", sky: "#3B6FF8",
    slate: "#6B7280", line: "#1A1F33", softLine: "#E3DCC9",
    danger: "#E11D48", purple: "#A78BFA",
  },
};

// ---------- i18n ----------
const I18N = React.createContext({ lang: "fr", t: (fr) => fr, setLang: () => {} });
function useI18n() { return React.useContext(I18N); }

// ---------- Toasts ----------
const ToastCtx = React.createContext({ push: () => {} });
function useToast() { return React.useContext(ToastCtx); }

function ToastHost({ toasts }) {
  const C = GK.colors;
  return (
    <div style={{ position: "fixed", bottom: 24, left: "50%", transform: "translateX(-50%)", zIndex: 99999, display: "flex", flexDirection: "column", gap: 8, alignItems: "center" }}>
      {toasts.map(t => (
        <div key={t.id} style={{
          display: "flex", alignItems: "center", gap: 10, padding: "12px 18px",
          background: C.ink, color: "#fff", borderRadius: 10, fontSize: 14,
          boxShadow: "0 12px 40px rgba(0,0,0,0.25)", fontFamily: "'Geist', sans-serif",
          border: `1px solid ${t.tone === "danger" ? C.danger : t.tone === "signal" ? C.signal : "#1F2542"}`,
          animation: "gkToastIn .25s ease",
        }}>
          <span style={{ color: t.tone === "danger" ? "#FF6B8A" : t.tone === "signal" ? "#3DE08C" : C.flame, fontSize: 16 }}>
            {t.tone === "danger" ? "✕" : t.tone === "signal" ? "✓" : "●"}
          </span>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

// ---------- Hash router ----------
function useHashRoute() {
  const [hash, setHash] = React.useState(() => window.location.hash.slice(1) || "/discover");
  React.useEffect(() => {
    const h = () => setHash(window.location.hash.slice(1) || "/discover");
    window.addEventListener("hashchange", h);
    return () => window.removeEventListener("hashchange", h);
  }, []);
  const navigate = React.useCallback((to) => {
    if (("#" + to) === window.location.hash) setHash(to);
    else window.location.hash = to;
    document.querySelector(".gk-scroll")?.scrollTo(0, 0);
  }, []);
  return [hash, navigate];
}

// ---------- Seed data ----------
function seedData() {
  const instructors = [
    { id: "m1", name: "Marc Lefèvre",  tone: 0, vehicle: "Renault Clio · AB-123-CD", color: "#FF5A1F" },
    { id: "m2", name: "Sophie Chen",   tone: 1, vehicle: "Peugeot 208 · CD-456-EF",  color: "#3B6FF8" },
    { id: "m3", name: "Karim Benali",  tone: 2, vehicle: "Citroën C3 · EF-789-GH",   color: "#0FB76B" },
    { id: "m4", name: "Émilie Dubois", tone: 3, vehicle: "Toyota Yaris · GH-012-IJ", color: "#A78BFA" },
  ];
  const students = [
    { id: "s1", name: "Léa Mercier",    age: 23, tone: 1, code: 36, hours: 10, hoursTotal: 20, progress: 82, instructorId: "m1", balance: 420, status: "active", phase: "Conduite", enrolled: "Jan 2026" },
    { id: "s2", name: "Aïcha Bakouri",  age: 19, tone: 0, code: 32, hours: 6,  hoursTotal: 20, progress: 54, instructorId: "m1", balance: 600, status: "active", phase: "Code", enrolled: "Fév 2026" },
    { id: "s3", name: "Tom Vasseur",    age: 21, tone: 2, code: 28, hours: 14, hoursTotal: 20, progress: 68, instructorId: "m2", balance: 280, status: "active", phase: "Conduite", enrolled: "Jan 2026" },
    { id: "s4", name: "Yasmine Alaoui", age: 25, tone: 4, code: 40, hours: 18, hoursTotal: 20, progress: 94, instructorId: "m2", balance: 0,   status: "exam", phase: "Examen", enrolled: "Déc 2025" },
    { id: "s5", name: "Hugo Bélanger",  age: 18, tone: 3, code: 24, hours: 4,  hoursTotal: 20, progress: 38, instructorId: "m3", balance: 720, status: "active", phase: "Code", enrolled: "Mar 2026" },
    { id: "s6", name: "Camille Roy",    age: 22, tone: 1, code: 30, hours: 8,  hoursTotal: 30, progress: 47, instructorId: "m3", balance: 900, status: "active", phase: "Code", enrolled: "Mar 2026" },
    { id: "s7", name: "Adama Diallo",   age: 20, tone: 2, code: 38, hours: 16, hoursTotal: 20, progress: 88, instructorId: "m4", balance: 120, status: "active", phase: "Examen", enrolled: "Jan 2026" },
    { id: "s8", name: "Manon Girard",   age: 24, tone: 0, code: 22, hours: 2,  hoursTotal: 20, progress: 28, instructorId: "m4", balance: 800, status: "new", phase: "Code", enrolled: "Mar 2026" },
  ];
  // Lessons: day 0..5 (Mon-Sat), hour 8..18
  const types = {
    code: { l: "Code", color: "#0FB76B" },
    broute: { l: "B-route", color: "#FF5A1F" },
    maneuver: { l: "Manœuvres", color: "#3B6FF8" },
    exam: { l: "Examen blanc", color: "#0B0F1F" },
  };
  const lessons = [
    { id: "l1", day: 0, hour: 9,  dur: 1, studentId: "s2", instructorId: "m1", type: "code",     status: "done" },
    { id: "l2", day: 0, hour: 10, dur: 1, studentId: "s1", instructorId: "m1", type: "broute",   status: "live" },
    { id: "l3", day: 0, hour: 11, dur: 1, studentId: "s3", instructorId: "m2", type: "maneuver", status: "upcoming" },
    { id: "l4", day: 0, hour: 13, dur: 1, studentId: "s4", instructorId: "m2", type: "exam",     status: "upcoming" },
    { id: "l5", day: 0, hour: 15, dur: 1, studentId: "s5", instructorId: "m3", type: "code",     status: "upcoming" },
    { id: "l6", day: 1, hour: 9,  dur: 1, studentId: "s1", instructorId: "m1", type: "broute",   status: "upcoming" },
    { id: "l7", day: 1, hour: 11, dur: 1, studentId: "s4", instructorId: "m2", type: "exam",     status: "upcoming" },
    { id: "l8", day: 1, hour: 14, dur: 1, studentId: "s6", instructorId: "m3", type: "code",     status: "upcoming" },
    { id: "l9", day: 2, hour: 10, dur: 1, studentId: "s3", instructorId: "m1", type: "broute",   status: "upcoming" },
    { id: "l10", day: 2, hour: 13, dur: 1, studentId: "s7", instructorId: "m4", type: "exam",    status: "upcoming" },
    { id: "l11", day: 3, hour: 9,  dur: 1, studentId: "s8", instructorId: "m4", type: "code",    status: "upcoming" },
    { id: "l12", day: 3, hour: 14, dur: 1, studentId: "s1", instructorId: "m1", type: "maneuver", status: "upcoming" },
    { id: "l13", day: 4, hour: 10, dur: 1, studentId: "s5", instructorId: "m3", type: "broute",  status: "upcoming" },
    { id: "l14", day: 4, hour: 15, dur: 1, studentId: "s7", instructorId: "m4", type: "exam",    status: "upcoming" },
    { id: "l15", day: 5, hour: 9,  dur: 2, studentId: "s2", instructorId: "m1", type: "code",    status: "upcoming" },
  ];

  const vehicles = [
    { id: "v1", brand: "Peugeot", model: "208 GT Line", year: 2022, km: 38240, price: 12400, fuel: "Essence", gear: "Manuelle", seller: "Auto-École Lubasa", sellerType: "pro", verified: true, badge: "boost", fav: false },
    { id: "v2", brand: "Renault", model: "Clio V", year: 2021, km: 52100, price: 11200, fuel: "Diesel", gear: "Manuelle", seller: "Garage 75", sellerType: "pro", verified: true, badge: "new", fav: false },
    { id: "v3", brand: "Citroën", model: "C3 Shine", year: 2023, km: 21480, price: 14900, fuel: "Essence", gear: "Auto", seller: "ConcessLyon", sellerType: "pro", verified: true, badge: null, fav: false },
    { id: "v4", brand: "Tesla", model: "Model 3 SR", year: 2022, km: 44800, price: 29900, fuel: "Élec.", gear: "Auto", seller: "EV Pro Network", sellerType: "pro", verified: true, badge: "boost", fav: true },
    { id: "v5", brand: "Volkswagen", model: "Polo VI", year: 2020, km: 68200, price: 10800, fuel: "Essence", gear: "Manuelle", seller: "Thomas R. (particulier)", sellerType: "particulier", verified: false, badge: null, fav: false },
    { id: "v6", brand: "Toyota", model: "Yaris Hybrid", year: 2023, km: 12600, price: 17200, fuel: "Hybride", gear: "Auto", seller: "DriveAcademy Paris", sellerType: "pro", verified: true, badge: null, fav: false },
    { id: "v7", brand: "Ford", model: "Puma ST-Line", year: 2022, km: 31480, price: 18400, fuel: "Essence", gear: "Manuelle", seller: "ECF Marseille", sellerType: "pro", verified: true, badge: "new", fav: false },
    { id: "v8", brand: "Dacia", model: "Sandero", year: 2021, km: 59300, price: 8900, fuel: "Essence", gear: "Manuelle", seller: "Sarah B. (particulier)", sellerType: "particulier", verified: false, badge: null, fav: false },
    { id: "v9", brand: "Renault", model: "Twingo III", year: 2019, km: 71200, price: 7400, fuel: "Essence", gear: "Manuelle", seller: "Mehdi K. (particulier)", sellerType: "particulier", verified: false, badge: null, fav: false },
    { id: "v10", brand: "BMW", model: "Série 1", year: 2021, km: 41000, price: 22900, fuel: "Diesel", gear: "Auto", seller: "Premium Motors", sellerType: "pro", verified: true, badge: "boost", fav: false },
  ];

  // Location / rental — multiple modes
  const rentals = [
    { id: "r1", brand: "Peugeot", model: "208 école", year: 2022, mode: "instructor", pricePerDay: 49, pricePerMonth: 890, seller: "Flotte Lubasa", sellerType: "pro", doublePedal: true, city: "Paris 11e", verified: true, fav: false },
    { id: "r2", brand: "Renault", model: "Clio école", year: 2023, mode: "school", pricePerDay: 55, pricePerMonth: 990, seller: "AutoLoc Pro", sellerType: "pro", doublePedal: true, city: "Lyon", verified: true, fav: false },
    { id: "r3", brand: "Citroën", model: "C3", year: 2022, mode: "short", pricePerDay: 39, pricePerMonth: 720, seller: "Julie M. (particulier)", sellerType: "particulier", doublePedal: false, city: "Paris 18e", verified: false, fav: false },
    { id: "r4", brand: "Tesla", model: "Model 3", year: 2023, mode: "long", pricePerDay: 89, pricePerMonth: 1490, seller: "EV Rent", sellerType: "pro", doublePedal: false, city: "Paris", verified: true, fav: false },
    { id: "r5", brand: "Toyota", model: "Yaris double commande", year: 2023, mode: "instructor", pricePerDay: 52, pricePerMonth: 940, seller: "Karim B. (particulier)", sellerType: "particulier", doublePedal: true, city: "Marseille", verified: false, fav: false },
    { id: "r6", brand: "Volkswagen", model: "Polo", year: 2021, mode: "p2p", pricePerDay: 34, pricePerMonth: 640, seller: "Antoine D. (particulier)", sellerType: "particulier", doublePedal: false, city: "Bordeaux", verified: false, fav: false },
    { id: "r7", brand: "Dacia", model: "Duster", year: 2022, mode: "short", pricePerDay: 44, pricePerMonth: 820, seller: "LocaCar", sellerType: "pro", doublePedal: false, city: "Lille", verified: true, fav: false },
    { id: "r8", brand: "Renault", model: "Clio double commande", year: 2024, mode: "school", pricePerDay: 58, pricePerMonth: 1050, seller: "Flotte Partagée", sellerType: "pro", doublePedal: true, city: "Nantes", verified: true, fav: false },
  ];

  // Pièces détachées / parts
  const parts = [
    { id: "p1", name: "Plaquettes de frein avant", category: "Freinage", condition: "neuf", price: 48, compatible: "Peugeot 208 / 2008", seller: "PiècesPro 75", sellerType: "pro", fav: false },
    { id: "p2", name: "Jeu de 4 pneus 195/65 R15", category: "Pneus", condition: "neuf", price: 280, compatible: "Universel", seller: "PneuStore", sellerType: "pro", fav: false },
    { id: "p3", name: "Rétroviseur droit", category: "Carrosserie", condition: "occasion", price: 35, compatible: "Clio IV", seller: "Lucas P. (particulier)", sellerType: "particulier", fav: false },
    { id: "p4", name: "Batterie 12V 60Ah", category: "Électronique", condition: "neuf", price: 89, compatible: "Universel", seller: "BatExpress", sellerType: "pro", fav: false },
    { id: "p5", name: "Kit double commande amovible", category: "Auto-école", condition: "neuf", price: 420, compatible: "Toutes voitures", seller: "EquipÉcole", sellerType: "pro", fav: false },
    { id: "p6", name: "Filtre à huile + vidange", category: "Moteur", condition: "neuf", price: 24, compatible: "Essence / Diesel", seller: "MécaParts", sellerType: "pro", fav: false },
    { id: "p7", name: "Phare avant LED", category: "Électronique", condition: "occasion", price: 65, compatible: "Golf VII", seller: "Sophie L. (particulier)", sellerType: "particulier", fav: false },
    { id: "p8", name: "Pare-chocs arrière", category: "Carrosserie", condition: "occasion", price: 120, compatible: "C3 2017+", seller: "CassAuto", sellerType: "pro", fav: false },
  ];

  // --- Catégories de véhicules (auto/moto/util/pl/bateau) + élargissement du marché ---
  vehicles.forEach(v => { v.cat = v.cat || "auto"; });
  vehicles.push(
    { id: "v11", brand: "Yamaha", model: "MT-07", year: 2022, km: 14200, price: 6900, fuel: "Essence", gear: "Manuelle", seller: "MotoStore 75", sellerType: "pro", verified: true, badge: "new", fav: false, cat: "moto" },
    { id: "v12", brand: "Honda", model: "CB500F", year: 2021, km: 22800, price: 5400, fuel: "Essence", gear: "Manuelle", seller: "Yanis T. (particulier)", sellerType: "particulier", verified: false, badge: null, fav: false, cat: "moto" },
    { id: "v13", brand: "Renault", model: "Trafic", year: 2020, km: 89200, price: 16900, fuel: "Diesel", gear: "Manuelle", seller: "UtilPro", sellerType: "pro", verified: true, badge: null, fav: false, cat: "util" },
    { id: "v14", brand: "Jeanneau", model: "Cap Camarat 6.5", year: 2019, km: 0, price: 38500, fuel: "Essence", gear: "Auto", seller: "Nautic Sud", sellerType: "pro", verified: true, badge: "boost", fav: false, cat: "bateau" },
    { id: "v15", brand: "Iveco", model: "Daily 35", year: 2019, km: 134000, price: 21900, fuel: "Diesel", gear: "Manuelle", seller: "PoidsLourd Pro", sellerType: "pro", verified: true, badge: null, fav: false, cat: "pl" },
  );

  rentals.forEach(r => { r.cat = r.cat || "auto"; });
  rentals.push(
    { id: "r11", brand: "Yamaha", model: "MT-07 école A2", year: 2023, mode: "instructor", pricePerDay: 42, pricePerMonth: 760, seller: "Moto Flotte", sellerType: "pro", doublePedal: false, city: "Paris", verified: true, fav: false, cat: "moto" },
    { id: "r12", brand: "Honda", model: "PCX 125", year: 2023, mode: "short", pricePerDay: 29, pricePerMonth: 520, seller: "Nadia S. (particulier)", sellerType: "particulier", doublePedal: false, city: "Lyon", verified: false, fav: false, cat: "moto" },
    { id: "r13", brand: "Jeanneau", model: "Merry Fisher", year: 2021, mode: "short", pricePerDay: 240, pricePerMonth: 0, seller: "Nautic Loc", sellerType: "pro", doublePedal: false, city: "Marseille", verified: true, fav: false, cat: "bateau" },
    { id: "r14", brand: "Renault", model: "Master", year: 2022, mode: "long", pricePerDay: 75, pricePerMonth: 1290, seller: "UtilLoc", sellerType: "pro", doublePedal: false, city: "Lille", verified: true, fav: false, cat: "util" },
  );

  // Covoiturage — trajets
  const trips = [
    { id: "tr1", from: "Paris 11e", to: "Lyon Part-Dieu", date: "Ven 22", time: "08:30", seats: 3, booked: 1, price: 28, driver: "Marc Lefèvre", tone: 0, kind: "moniteur" },
    { id: "tr2", from: "Auto-École Lubasa", to: "Centre d'examen Vincennes", date: "Lun 18", time: "13:00", seats: 3, booked: 2, price: 6, driver: "Sophie Chen", tone: 1, kind: "moniteur" },
    { id: "tr3", from: "Nation", to: "Marne-la-Vallée", date: "Sam 23", time: "10:00", seats: 2, booked: 0, price: 9, driver: "Léa Mercier", tone: 1, kind: "eleve" },
    { id: "tr4", from: "Lyon", to: "Grenoble", date: "Dim 24", time: "17:30", seats: 4, booked: 2, price: 12, driver: "Karim B. (particulier)", tone: 2, kind: "particulier" },
    { id: "tr5", from: "Bordeaux", to: "Bayonne", date: "Ven 22", time: "18:00", seats: 2, booked: 1, price: 18, driver: "Manon Girard", tone: 0, kind: "eleve" },
  ];

  // Founder CRM
  const leads = [
    { id: "d1", name: "Auto-École Cazeneuve", value: 1188, stage: "cold", note: "B-tour 06/2025", tone: 0, hot: false },
    { id: "d2", name: "Permis 360 Reims", value: 2388, stage: "cold", note: "LinkedIn", tone: 1, hot: false },
    { id: "d3", name: "Drive Strasbourg", value: 588, stage: "cold", note: "Email cold", tone: 2, hot: false },
    { id: "d4", name: "ECF Grenoble", value: 2388, stage: "qualified", note: "Démo planifiée", tone: 3, hot: false },
    { id: "d5", name: "Conduite+ Rennes", value: 1188, stage: "qualified", note: "Intéressé Pro", tone: 4, hot: true },
    { id: "d6", name: "AutoForm Lille", value: 4776, stage: "qualified", note: "Multi-sites", tone: 0, hot: false },
    { id: "d7", name: "PermisAcademy Paris", value: 2388, stage: "demo", note: "POC en cours", tone: 1, hot: true },
    { id: "d8", name: "Conduite Express", value: 1188, stage: "demo", note: "Décide cette sem.", tone: 2, hot: true },
    { id: "d9", name: "DriveSchool Cannes", value: 588, stage: "demo", note: "Suivi prévu", tone: 3, hot: false },
    { id: "d10", name: "Auto-Centre Toulouse", value: 2388, stage: "closing", note: "Devis envoyé", tone: 0, hot: true },
    { id: "d11", name: "Conduite Bordeaux", value: 1188, stage: "closing", note: "Signature ven.", tone: 1, hot: true },
  ];
  const tasks = [
    { id: "t1", text: "Relancer Auto-Centre Toulouse (devis)", tag: "sales", prio: "URGENT", done: false },
    { id: "t2", text: "Démo PermisAcademy · 14h", tag: "demo", prio: "14:00", done: false },
    { id: "t3", text: "Appel investisseur — A. Dupré", tag: "raise", prio: "16:30", done: false },
    { id: "t4", text: "Ship feature : export PDF", tag: "product", prio: "SHIP", done: false },
    { id: "t5", text: "Répondre ticket #421 (DriveAcademy)", tag: "support", prio: "P1", done: true },
    { id: "t6", text: "Rédiger newsletter clients", tag: "marketing", prio: "—", done: true },
    { id: "t7", text: "Revue financière mensuelle", tag: "ops", prio: "—", done: true },
  ];
  return {
    v: 6,
    user: null,
    students, instructors, lessons, vehicles, rentals, parts, trips, leads, tasks,
    types,
    messages: [
      { id: 1, from: "m1", to: "s1", text: "Salut Léa ! Bien préparée pour demain ?", at: "Hier 18:42" },
      { id: 2, from: "s1", to: "m1", text: "Oui ! J'ai révisé les manœuvres de stationnement.", at: "Hier 18:50" },
      { id: 3, from: "m1", to: "s1", text: "Super. On va travailler le créneau bataille.", at: "Hier 18:52" },
      { id: 4, from: "m1", to: "s1", text: "Je suis sur place. À tout de suite !", at: "Auj. 09:58" },
    ],
  };
}

// ---------- Store ----------
const StoreCtx = React.createContext(null);
function useStore() { return React.useContext(StoreCtx); }

const STORE_KEY = "guidko_app_v6";

function StoreProvider({ children }) {
  const [state, setState] = React.useState(() => {
    try {
      const raw = localStorage.getItem(STORE_KEY);
      if (raw) {
        const parsed = JSON.parse(raw);
        if (parsed.v === 6) return parsed;
      }
    } catch (e) {}
    return seedData();
  });

  React.useEffect(() => {
    try { localStorage.setItem(STORE_KEY, JSON.stringify(state)); } catch (e) {}
  }, [state]);

  // Generic helpers
  const api = React.useMemo(() => ({
    state,
    set: (patch) => setState(s => ({ ...s, ...(typeof patch === "function" ? patch(s) : patch) })),
    login: (user) => setState(s => ({ ...s, user })),
    logout: () => setState(s => ({ ...s, user: null })),
    addStudent: (st) => setState(s => ({ ...s, students: [{ ...st, id: "s" + Date.now() }, ...s.students] })),
    updateStudent: (id, patch) => setState(s => ({ ...s, students: s.students.map(x => x.id === id ? { ...x, ...patch } : x) })),
    removeStudent: (id) => setState(s => ({ ...s, students: s.students.filter(x => x.id !== id) })),
    addLesson: (ls) => setState(s => ({ ...s, lessons: [...s.lessons, { ...ls, id: "l" + Date.now(), status: "upcoming", dur: 1 }] })),
    removeLesson: (id) => setState(s => ({ ...s, lessons: s.lessons.filter(x => x.id !== id) })),
    setLessonStatus: (id, status) => setState(s => ({ ...s, lessons: s.lessons.map(x => x.id === id ? { ...x, status } : x) })),
    toggleFav: (id) => setState(s => ({ ...s, vehicles: s.vehicles.map(v => v.id === id ? { ...v, fav: !v.fav } : v) })),
    toggleRentalFav: (id) => setState(s => ({ ...s, rentals: s.rentals.map(v => v.id === id ? { ...v, fav: !v.fav } : v) })),
    togglePartFav: (id) => setState(s => ({ ...s, parts: s.parts.map(v => v.id === id ? { ...v, fav: !v.fav } : v) })),
    addRental: (r) => setState(s => ({ ...s, rentals: [{ ...r, id: "r" + Date.now(), fav: false, verified: false }, ...s.rentals] })),
    addTrip: (tp) => setState(s => ({ ...s, trips: [{ ...tp, id: "tr" + Date.now(), booked: 0 }, ...s.trips] })),
    bookTrip: (id) => setState(s => ({ ...s, trips: s.trips.map(t => t.id === id ? { ...t, booked: Math.min(t.seats, t.booked + 1) } : t) })),
    addPart: (p) => setState(s => ({ ...s, parts: [{ ...p, id: "p" + Date.now(), fav: false }, ...s.parts] })),
    addVehicle: (v) => setState(s => ({ ...s, vehicles: [{ ...v, id: "v" + Date.now(), fav: false, badge: "new", verified: true }, ...s.vehicles] })),
    moveLead: (id, stage) => setState(s => ({ ...s, leads: s.leads.map(l => l.id === id ? { ...l, stage } : l) })),
    addLead: (l) => setState(s => ({ ...s, leads: [{ ...l, id: "d" + Date.now(), stage: "cold", hot: false, tone: Math.floor(Math.random()*5) }, ...s.leads] })),
    toggleTask: (id) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === id ? { ...t, done: !t.done } : t) })),
    addTask: (text) => setState(s => ({ ...s, tasks: [{ id: "t" + Date.now(), text, tag: "ops", prio: "—", done: false }, ...s.tasks] })),
    sendMessage: (from, to, text) => setState(s => ({ ...s, messages: [...s.messages, { id: Date.now(), from, to, text, at: "À l'instant" }] })),
    resetAll: () => { localStorage.removeItem(STORE_KEY); setState(seedData()); },
  }), [state]);

  return <StoreCtx.Provider value={api}>{children}</StoreCtx.Provider>;
}

// ---------- Derived helpers ----------
function studentById(state, id) { return state.students.find(s => s.id === id); }
function instructorById(state, id) { return state.instructors.find(m => m.id === id); }

Object.assign(window, {
  GK, I18N, useI18n, ToastCtx, useToast, ToastHost,
  useHashRoute, StoreProvider, useStore, seedData,
  studentById, instructorById,
});
