// V3 — Affiche × ACHAT Design System
// Poster energy grounded in the ACHAT brand.
// Palette: ink + paper-warm + vert + prune + bleu (no yellow/orange).
// Type: Bricolage Grotesque display, Quicksand body, Playfair Display italic for editorial.

const DS = {
  ink:        '#1A1A1A',
  inkSoft:    '#565556',
  inkMuted:   '#9C989C',
  paperWarm:  '#FBF7F1',
  paper:      '#FFFFFF',
  vert:       '#698762',
  vertPale:   '#AFE3A3',
  prune:      '#6F5A51',
  prunePale:  '#A3928B',
  bleu:       '#B9E0F6',
  line:       '#E8E3DD',
  lineStrong: '#C9C2BA',
};

const F = {
  display:   "'Bricolage Grotesque', 'Montserrat', system-ui, sans-serif",
  body:      "'Quicksand', 'Inter', system-ui, sans-serif",
  editorial: "'Playfair Display', Georgia, serif",
  mono:      "ui-monospace, 'SF Mono', Menlo, monospace",
};

function Eyebrow({ label, color = DS.vert, style }) {
  return (
    <div style={{
      fontFamily: F.display, fontSize: 11, fontWeight: 600,
      letterSpacing: "0.18em", textTransform: "uppercase",
      color, marginBottom: 20, ...style,
    }}>{label}</div>
  );
}

// ── Accordéons ────────────────────────────────────────────────
function AccordionChevron({ open, color }) {
  var c = color || DS.ink;
  return (
    <span aria-hidden="true" style={{
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      width: 36, height: 36, borderRadius: "50%",
      border: "1.5px solid " + c, color: c, flexShrink: 0,
      transition: "transform 0.3s ease",
      transform: open ? "rotate(180deg)" : "rotate(0deg)",
    }}>
      <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M3 5.5L7 9.5L11 5.5" stroke="currentColor" strokeWidth="1.8"
          strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    </span>
  );
}

function AccordionItem({ id, label, background, color, borderBottom, defaultOpen, children }) {
  var _s = React.useState(!!defaultOpen);
  var open = _s[0], setOpen = _s[1];
  var bodyId = id + "-body";
  var fg = color || DS.ink;
  return (
    <div id={id} style={{ background: background || DS.paperWarm, color: fg }}>
      <button
        type="button"
        aria-expanded={open}
        aria-controls={bodyId}
        onClick={function () { setOpen(!open); }}
        style={{
          display: "flex", alignItems: "center", justifyContent: "space-between",
          gap: 20, width: "100%", boxSizing: "border-box",
          padding: "30px 5%", background: "none", border: "none",
          borderBottom: borderBottom || `1px solid ${DS.line}`,
          cursor: "pointer", textAlign: "left",
          fontFamily: F.display, fontWeight: 700,
          fontSize: "clamp(24px, 4vw, 44px)", letterSpacing: "-0.02em",
          color: fg, lineHeight: 1.05,
        }}
      >
        <span>{label}</span>
        <AccordionChevron open={open} color={fg} />
      </button>
      <div id={bodyId} role="region" aria-label={label}
        style={{ display: open ? "block" : "none" }}>
        {children}
      </div>
    </div>
  );
}

// ── Hero ─────────────────────────────────────────────────────
function V3Hero() {
  return (
    <section data-animate="hero" style={{
      background: DS.ink, color: DS.paperWarm,
      padding: "8vh 5% 0", position: "relative", overflow: "hidden",
      minHeight: "100vh", display: "flex", flexDirection: "column", justifyContent: "center",
    }}>
      <svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0.03, pointerEvents: "none" }}>
        <defs>
          <pattern id="v3dots" width="20" height="20" patternUnits="userSpaceOnUse">
            <circle cx="2" cy="2" r="1" fill={DS.paperWarm}/>
          </pattern>
        </defs>
        <rect width="100%" height="100%" fill="url(#v3dots)"/>
      </svg>

      <div style={{ position: "relative", maxWidth: 1100, margin: "0 auto", width: "100%" }}>
        <div style={{
          fontFamily: F.mono, fontSize: 11, letterSpacing: "0.22em",
          textTransform: "uppercase", color: DS.bleu, marginBottom: "6vh", opacity: 0.9,
        }}>
          {EVENT.edition} · {EVENT.date} · {EVENT.venue}
        </div>

        <h1 style={{ margin: 0, lineHeight: 0.88 }}>
          <span style={{
            display: "block",
            fontFamily: F.display, fontWeight: 800,
            fontSize: "clamp(64px, 14vw, 196px)", letterSpacing: "-0.04em",
            color: DS.paperWarm,
          }}>{HERO.line1}</span>
          <span style={{
            display: "block",
            fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500,
            fontSize: "clamp(48px, 12vw, 172px)", letterSpacing: "-0.02em",
            color: DS.bleu,
            marginTop: -4, marginBottom: -4,
          }}>{HERO.line2}</span>
          <span style={{
            display: "block",
            fontFamily: F.display, fontWeight: 800,
            fontSize: "clamp(64px, 14vw, 196px)", letterSpacing: "-0.04em",
            color: DS.paperWarm,
          }}>{HERO.line3}</span>
        </h1>

        <div style={{ marginTop: "6vh", display: "flex", alignItems: "center", justifyContent: "center", gap: 24 }}>
          <div style={{ position: "relative", flexShrink: 0 }}>
            <img src={(HOST.photo || "assets/josee-boileau.jpg") + "?v=2"} alt={HOST.name} style={{
              width: 120, height: 120, borderRadius: "50%", objectFit: "cover",
              objectPosition: "center 20%",
              border: "2px solid rgba(251,247,241,0.25)",
            }} />
          </div>
          <div>
            <div style={{
              fontFamily: F.editorial, fontStyle: "italic", fontSize: 18,
              color: "rgba(251,247,241,0.6)",
            }}>{HOST.label || "Animé par"}</div>
            <div style={{
              fontFamily: F.display, fontWeight: 700, fontSize: 26,
              color: DS.paperWarm, letterSpacing: "-0.01em",
            }}>{HOST.name}</div>
            {HOST.photoCredit && (
              <div style={{
                fontFamily: F.mono, fontSize: 10, letterSpacing: "0.06em",
                color: "rgba(251,247,241,0.35)", marginTop: 6,
              }}>
                Photo · {HOST.photoCredit}
              </div>
            )}
          </div>
        </div>

        <div style={{
          marginTop: "4vh",
          padding: "28px 0",
          borderTop: "1px solid rgba(251,247,241,0.12)",
          display: "flex", justifyContent: "space-between", alignItems: "center",
          flexWrap: "wrap", gap: 16,
          fontFamily: F.body, fontSize: 14, color: "rgba(251,247,241,0.6)", fontWeight: 500,
        }}>
          <span>{EVENT.address}</span>
          <span style={{ color: "rgba(251,247,241,0.35)", fontSize: 12 }}>
            {EVENT.tagline}
          </span>
        </div>
      </div>
    </section>
  );
}

function scheduleOrgKey(detail) {
  if (!detail) return "";
  var m = detail.match(/\(([^)]+)\)\s*$/);
  if (m) return m[1];
  var parts = detail.split(",");
  return parts[parts.length - 1].trim();
}

var PARTY_COLORS = {
  "Québec solidaire": { color: "#FF7A00", abbr: "QS" },
  "Parti libéral du Québec": { color: "#E63B2E", abbr: "PLQ" },
  "Parti québécois": { color: "#1E62C5", abbr: "PQ" },
  "CAQ": { color: "#00A0B0", abbr: "CAQ" },
  "Coalition avenir Québec": { color: "#00A0B0", abbr: "CAQ" },
};

function partyMatch(detail) {
  if (!detail) return null;
  for (var key in PARTY_COLORS) {
    if (detail.indexOf(key) !== -1) return PARTY_COLORS[key];
  }
  return null;
}

function participantSortKey(p) {
  if (partyMatch(p.detail)) {
    var parts = (p.name || "").trim().split(/\s+/);
    return "A" + (parts.length > 1 ? parts.slice(1).join(" ") : p.name);
  }
  return "B" + scheduleOrgKey(p.detail);
}

function ScheduleDesc({ text }) {
  if (!text.includes("\n---\n")) {
    var parts = text.split(/(\*\*[^*]+\*\*)/g);
    return (
      <div style={{
        marginTop: 14,
        fontFamily: F.body, fontSize: 15, lineHeight: 1.65,
        color: DS.inkSoft, fontWeight: 500,
        whiteSpace: "pre-line", maxWidth: 640,
      }}>
        {parts.map(function(part, i) {
          if (part.indexOf("**") === 0 && part.lastIndexOf("**") === part.length - 2) {
            return (
              <strong key={i} style={{
                fontFamily: F.display,
                fontWeight: 700,
                color: DS.ink,
                letterSpacing: "-0.01em",
              }}>{part.slice(2, -2)}</strong>
            );
          }
          return part;
        })}
      </div>
    );
  }

  const splitAt = text.indexOf("\n---\n");
  const intro = text.slice(0, splitAt).trim();
  const participants = text.slice(splitAt + 5).split("\n---\n").map(function(block) {
    var lines = block.trim().split("\n").filter(Boolean);
    return { name: lines[0], detail: lines.slice(1).join(" ") };
  }).sort(function(a, b) {
    return participantSortKey(a).localeCompare(participantSortKey(b), "fr", { sensitivity: "base" });
  });

  return (
    <div style={{ marginTop: 14 }}>
      <div style={{
        fontFamily: F.body, fontSize: 15, lineHeight: 1.65,
        color: DS.inkSoft, fontWeight: 500, whiteSpace: "pre-line",
      }}>{intro}</div>
      <div style={{
        marginTop: 20, display: "grid",
        gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
        gap: "12px 24px",
      }}>
        {participants.map(function(p, i) {
          var party = partyMatch(p.detail);
          return (
            <div key={i} style={{
              padding: "14px 0",
              borderTop: `1px solid ${DS.line}`,
            }}>
              <div style={{
                fontFamily: F.display, fontSize: 15, fontWeight: 600,
                color: DS.ink, letterSpacing: "-0.01em", lineHeight: 1.3,
              }}>
                {p.name}
                {party && (
                  <span style={{
                    display: "inline-flex", alignItems: "center", gap: 5,
                    marginLeft: 8, verticalAlign: "middle",
                    fontFamily: F.body, fontSize: 10, fontWeight: 700,
                    letterSpacing: "0.08em", color: party.color,
                    textTransform: "uppercase",
                  }}>
                    <span style={{
                      width: 8, height: 8, borderRadius: "50%",
                      background: party.color, flexShrink: 0,
                    }} />
                    {party.abbr}
                  </span>
                )}
              </div>
              {p.detail && (
                <div style={{
                  fontFamily: F.body, fontSize: 14, lineHeight: 1.5,
                  color: DS.inkSoft, fontWeight: 500, marginTop: 4,
                }}>{p.detail}</div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── À propos ────────────────────────────────────────────────
function AboutInline({ text }) {
  var parts = text.split(/(__[^_]+__)/g);
  return parts.map(function(part, i) {
    if (part.indexOf("__") === 0 && part.lastIndexOf("__") === part.length - 2) {
      return (
        <span key={i} style={{
          background: "rgba(175, 227, 163, 0.45)",
          boxDecorationBreak: "clone",
          WebkitBoxDecorationBreak: "clone",
          padding: "0 0.12em",
          borderRadius: 3,
        }}>{part.slice(2, -2)}</span>
      );
    }
    return part;
  });
}

function V3APropos() {
  var paragraphs = (ABOUT.body || "").split(/\n\n+/).filter(Boolean);
  return (
    <section data-animate style={{
      padding: "6vh 5% 14vh",
      background: DS.paperWarm,
      borderBottom: `1px solid ${DS.line}`,
    }}>
      <div style={{ maxWidth: 860, margin: "0 auto", textAlign: "center" }}>
        <blockquote style={{
          margin: 0,
          fontFamily: F.editorial, fontStyle: "italic",
          fontSize: "clamp(32px, 6vw, 80px)", lineHeight: 1.05, fontWeight: 500,
          letterSpacing: "-0.02em", color: DS.ink, textWrap: "balance",
        }}>
          {ABOUT.quote}
        </blockquote>
        <div style={{ maxWidth: 640, margin: "48px auto 0", textAlign: "left" }}>
          {paragraphs.map(function(p, i) {
            return (
              <p key={i} style={{
                margin: i === 0 ? 0 : "20px 0 0",
                fontFamily: F.body, fontSize: 18, lineHeight: 1.65,
                color: DS.inkSoft, textWrap: "pretty", fontWeight: 500,
              }}><AboutInline text={p} /></p>
            );
          })}
        </div>
      </div>
    </section>
  );
}

// ── Programme ─────────────────────────────────────────────────
function V3Programme() {
  const hasSchedule = SCHEDULE && SCHEDULE.length > 0;
  return (
    <section data-animate style={{ padding: "6vh 5% 12vh", background: DS.paper, borderBottom: `1px solid ${DS.line}` }}>
      <div style={{ maxWidth: 1000, margin: "0 auto" }}>
        {!hasSchedule ? (
          <div style={{
            marginTop: "8vh", textAlign: "center",
            fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500,
            fontSize: "clamp(28px, 4vw, 52px)", letterSpacing: "-0.02em",
            color: DS.inkSoft,
          }}>
            À venir.
          </div>
        ) : (
          <div data-stagger style={{ marginTop: "8vh", borderTop: `1.5px solid ${DS.ink}` }}>
            {SCHEDULE.map((item, i) => (
              <div key={i} style={{
                display: "grid",
                gridTemplateColumns: "minmax(140px, 180px) 1fr",
                gap: "16px 32px",
                padding: "32px 0",
                borderBottom: `1px solid ${DS.line}`,
                alignItems: "start",
              }}>
                <div style={{
                  fontFamily: F.mono, fontSize: 13, fontWeight: 500,
                  letterSpacing: "0.04em", color: item.accent ? DS.vert : DS.inkMuted,
                  paddingTop: 4, lineHeight: 1.5,
                }}>
                  {item.time}
                </div>
                <div>
                  <div style={{
                    fontFamily: F.display, fontWeight: item.accent ? 700 : 600,
                    fontSize: item.accent ? 22 : 20,
                    letterSpacing: "-0.02em",
                    color: DS.ink,
                    lineHeight: 1.2,
                  }}>
                    {item.title}
                  </div>
                  {item.desc && <ScheduleDesc text={item.desc} />}
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </section>
  );
}

// ── Personnalités ─────────────────────────────────────────────
function V3Personnalites() {
  const [active, setActive] = React.useState(null);
  const initials = (name) => name.split(" ").map(w => w[0]).join("").replace(/[^A-Z]/g, "").slice(0, 2);

  React.useEffect(function () {
    if (!active) return;
    function onKey(e) { if (e.key === "Escape") setActive(null); }
    window.addEventListener("keydown", onKey);
    return function () { window.removeEventListener("keydown", onKey); };
  }, [active]);

  function Avatar({ sp, i, size, flush }) {
    return sp.photo ? (
      <img src={sp.photo} alt={sp.name} style={{
        width: size, height: size, borderRadius: "50%", objectFit: "cover",
        objectPosition: sp.photoPos || "center",
        marginBottom: flush ? 0 : 20,
        flexShrink: 0,
      }} />
    ) : (
      <div style={{
        width: size, height: size, borderRadius: "50%",
        background: [DS.vert, DS.prune, DS.ink][i % 3],
        display: "flex", alignItems: "center", justifyContent: "center",
        fontFamily: F.display, fontSize: Math.round(size * 0.32), fontWeight: 700,
        color: DS.paperWarm, marginBottom: flush ? 0 : 20,
        flexShrink: 0,
      }}>{initials(sp.name)}</div>
    );
  }

  if (!SPEAKERS || !SPEAKERS.length) return null;

  return (
    <section data-animate style={{
      padding: "6vh 5% 12vh",
      background: DS.bleu, position: "relative", overflow: "hidden",
    }}>
      <div style={{
        position: "absolute", top: "-20px", right: "-40px",
        fontFamily: F.display, fontWeight: 800,
        fontSize: 280, letterSpacing: "-0.06em",
        color: "rgba(255,255,255,0.12)", pointerEvents: "none",
        lineHeight: 1,
      }}>qui</div>

      <div style={{ maxWidth: 1000, margin: "0 auto", position: "relative" }}>
        <h2 style={{
          fontFamily: F.display, fontWeight: 800,
          fontSize: "clamp(40px, 8vw, 96px)", letterSpacing: "-0.04em", lineHeight: 0.9,
          color: DS.ink, marginBottom: "6vh",
        }}>
          Qui <span style={{ fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500 }}>prend</span> la parole.
        </h2>

        <div data-stagger style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 32 }}>
          {SPEAKERS.map((sp, i) => {
            const prev = SPEAKERS[i - 1];
            const groupChanged = prev && prev.group !== sp.group;
            const featured = sp.group === "honneur";
            return (
              <React.Fragment key={i}>
                {groupChanged && (
                  <div style={{
                    gridColumn: "1 / -1",
                    borderTop: "1px solid rgba(26,26,26,0.18)",
                    margin: "10px 0 20px",
                  }} />
                )}
                <div style={{
                  background: DS.paperWarm, borderRadius: 16,
                  padding: featured ? "36px 32px" : "36px 28px 28px",
                  boxShadow: "0 4px 20px rgba(111,90,81,0.08)",
                  gridColumn: featured ? "1 / -1" : undefined,
                  display: featured ? "flex" : "block",
                  flexWrap: "wrap",
                  alignItems: "center",
                  gap: featured ? 28 : undefined,
                }}>
                  <Avatar sp={sp} i={i} size={featured ? 112 : 84} flush={featured} />
                  <div style={{ flex: featured ? "1 1 220px" : undefined }}>
                    <div style={{ fontFamily: F.display, fontSize: featured ? 26 : 20, fontWeight: 700, color: DS.ink, letterSpacing: "-0.01em" }}>
                      {sp.name}
                    </div>
                    <div style={{ fontFamily: F.body, fontSize: featured ? 15 : 13, fontWeight: 600, color: DS.prune, marginTop: 4, letterSpacing: "0.02em" }}>
                      {sp.role}
                    </div>
                    {sp.bio && (
                      <button onClick={function () { setActive(sp); }} style={{
                        marginTop: 14, padding: 0, background: "none", border: "none", cursor: "pointer",
                        fontFamily: F.body, fontSize: 13, fontWeight: 700, color: DS.vert,
                        textDecoration: "underline", textUnderlineOffset: 3,
                      }}>Lire la biographie</button>
                    )}
                  </div>
                </div>
              </React.Fragment>
            );
          })}
        </div>
      </div>

      {active && (
        <div
          role="dialog" aria-modal="true" aria-labelledby="bio-title"
          onClick={function () { setActive(null); }}
          style={{
            position: "fixed", inset: 0, zIndex: 1000,
            background: "rgba(26,26,26,0.55)",
            display: "flex", alignItems: "center", justifyContent: "center",
            padding: "5%",
          }}
        >
          <div onClick={function (e) { e.stopPropagation(); }} style={{
            background: DS.paperWarm, borderRadius: 20,
            maxWidth: 560, width: "100%", maxHeight: "80vh", overflowY: "auto",
            padding: "40px 36px 32px", position: "relative",
            boxShadow: "0 24px 60px rgba(26,26,26,0.35)",
          }}>
            <button onClick={function () { setActive(null); }} aria-label="Fermer" style={{
              position: "absolute", top: 10, right: 14,
              background: "none", border: "none", cursor: "pointer",
              fontFamily: F.display, fontSize: 28, fontWeight: 600, lineHeight: 1,
              color: DS.inkSoft,
            }}>×</button>
            <Avatar sp={active} i={0} size={96} />
            <h3 id="bio-title" style={{ fontFamily: F.display, fontSize: 26, fontWeight: 700, color: DS.ink, letterSpacing: "-0.01em" }}>
              {active.name}
            </h3>
            <div style={{ fontFamily: F.body, fontSize: 14, fontWeight: 600, color: DS.prune, marginTop: 6, letterSpacing: "0.02em" }}>
              {active.role}
            </div>
            <div style={{ height: 1, background: DS.lineStrong, margin: "20px 0" }} />
            {active.bio.split("\n\n").map((p, j) => (
              <p key={j} style={{ fontFamily: F.body, fontSize: 15, lineHeight: 1.65, color: DS.inkSoft, margin: "0 0 14px", fontWeight: 500 }}>
                {p}
              </p>
            ))}
          </div>
        </div>
      )}
    </section>
  );
}

// ── Lieu ──────────────────────────────────────────────────────
function V3Lieu() {
  return (
    <section data-animate style={{
      padding: "6vh 5% 12vh",
      background: DS.paperWarm,
      borderBottom: `1px solid ${DS.line}`,
      textAlign: "center",
    }}>
      <h2 style={{
        fontFamily: F.display, fontWeight: 800,
        fontSize: "clamp(56px, 10vw, 120px)", letterSpacing: "-0.04em", lineHeight: 0.88,
        color: DS.ink, margin: 0,
      }}>BAnQ</h2>
      <div style={{
        fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500,
        fontSize: "clamp(32px, 6vw, 80px)", letterSpacing: "-0.02em", lineHeight: 1,
        color: DS.prune, marginTop: 8,
      }}>Grande Bibliothèque.</div>
      <div style={{
        marginTop: 48,
        fontFamily: F.body, fontSize: 16, fontWeight: 500,
        color: DS.inkSoft, letterSpacing: "0.02em",
      }}>
        {EVENT.address}<br />Métro {EVENT.metro}
      </div>
      {EVENT.mapsEmbedUrl && (
        <div style={{
          marginTop: 48, maxWidth: 900, marginLeft: "auto", marginRight: "auto",
          borderRadius: 12, overflow: "hidden",
          border: `1px solid ${DS.line}`,
          boxShadow: "0 4px 24px rgba(111,90,81,0.08)",
        }}>
          <iframe
            src={EVENT.mapsEmbedUrl}
            title={"Carte — " + EVENT.venue}
            width="100%"
            height="400"
            style={{ border: 0, display: "block" }}
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
            allowFullScreen
          />
        </div>
      )}
    </section>
  );
}

// ── Partenaires ───────────────────────────────────────────────
// Per-partner scale tweaks — normalize optical size in the logo slot
var LOGO_SCALES = {
  RQOH: 0.88,
  FHCQ: 0.90,
  CQCH: 0.68,
  ROHQ: 0.90,
  UTILE: 2.35,
  Interloge: 1.2,
  CSQ: 3.1,
  VEV: 1.6,
  APCHQ: 1.1,
  AGRTQ: 1.0,
  CEGQ: 0.9,
};

function PartnerLogo({ partner }) {
  if (partner.logo) {
    var useFilter = partner.logoInvert !== false;
    var src = partner.logo + (partner.logo.indexOf("?") < 0 ? "?v=19" : "");
    var scale = partner.logoScale || LOGO_SCALES[partner.acro] || 1;
    return (
      <span style={{
        display: "flex", alignItems: "center", justifyContent: "center",
        width: 148, height: 52, lineHeight: 0, margin: "0 auto",
      }}>
        <img src={src} alt={partner.acro} style={{
          maxHeight: 52 * scale,
          maxWidth: 136 * scale,
          width: "auto", height: "auto",
          objectFit: "contain", display: "block",
          filter: useFilter ? "brightness(0) invert(1)" : "none",
          opacity: 0.92,
        }} />
      </span>
    );
  }
  return (
    <div style={{
      height: 64, display: "flex", alignItems: "center", justifyContent: "center",
      fontFamily: F.display, fontSize: 22, fontWeight: 700,
      letterSpacing: "0.06em", color: "rgba(251,247,241,0.9)",
    }}>{partner.acro}</div>
  );
}

function PartnerGrid({ partners }) {
  if (!partners.length) return null;
  return (
    <div style={{
      display: "flex", flexWrap: "wrap",
      gap: "32px 48px", justifyContent: "center", alignItems: "center",
    }}>
      {partners.map(function(p, i) {
        return (
          <div key={i} style={{ textAlign: "center", maxWidth: 220 }}>
            <PartnerLogo partner={p} />
            <div style={{
              fontFamily: F.body, fontSize: 12, fontWeight: 500,
              color: "rgba(251,247,241,0.55)", marginTop: 8, letterSpacing: "0.02em",
            }}>{p.name}</div>
          </div>
        );
      })}
    </div>
  );
}

function V3Partenaires() {
  var groups = [
    { key: "Principal", label: "Partenaire principal" },
    { key: "Secteur", label: "Partenaires secteur" },
    { key: "Bâtisseurs", label: "Partenaires Bâtisseurs" },
    { key: "Allié", label: "Partenaires alliés" },
  ];

  return (
    <section data-animate style={{
      padding: "6vh 5% 10vh",
      background: DS.prune, color: DS.paperWarm,
      borderTop: `1.5px solid ${DS.ink}`,
      borderBottom: `1.5px solid ${DS.ink}`,
      textAlign: "center",
    }}>
      <div style={{ marginBottom: 48 }}>
        <img src="assets/icone_blanche.png" alt="ACHAT" style={{
          height: 60,
          opacity: 0.85, marginBottom: 16,
        }} />
        <div style={{
          fontFamily: F.body, fontSize: 15, fontWeight: 500,
          color: "rgba(251,247,241,0.7)",
          maxWidth: 600, margin: "0 auto",
        }}>
          À l'initiative de l'Alliance des corporations d'habitations abordables du territoire du Québec (ACHAT)
        </div>
      </div>

      <div style={{ maxWidth: 1000, margin: "0 auto" }}>
        {groups.map(function(group, gi) {
          var partners = (PARTNERS || []).filter(function(p) {
            return p.partnership === group.key;
          }).sort(function(a, b) {
            return a.name.localeCompare(b.name, "fr", { sensitivity: "base" });
          });
          if (!partners.length) return null;
          return (
            <div key={group.key} style={{ marginBottom: gi < groups.length - 1 ? 56 : 0 }}>
              <div style={{
                fontFamily: F.display, fontSize: 11, fontWeight: 600,
                letterSpacing: "0.18em", textTransform: "uppercase",
                color: "rgba(251,247,241,0.45)", marginBottom: 28,
              }}>{group.label}</div>
              <PartnerGrid partners={partners} />
            </div>
          );
        })}
      </div>
    </section>
  );
}

// ── Priorités électorales ─────────────────────────────────────
function V3Priorites() {
  var items = (typeof PRIORITIES !== "undefined" && PRIORITIES) ? PRIORITIES.slice() : [];
  if (!items.length) return null;

  items.sort(function(a, b) {
    return a.acro.localeCompare(b.acro, "fr", { sensitivity: "base" });
  });

  var partnersByAcro = {};
  (PARTNERS || []).forEach(function(p) { partnersByAcro[p.acro] = p; });
  if (!partnersByAcro.ACHAT) {
    partnersByAcro.ACHAT = {
      acro: "ACHAT",
      name: (typeof EVENT !== "undefined" && EVENT.organizerFull) || "ACHAT",
      logo: "assets/trimmed/achat.png",
      logoInvert: false,
    };
  }

  return (
    <section data-animate style={{
      padding: "6vh 5% 12vh",
      background: DS.paperWarm,
      borderBottom: `1px solid ${DS.line}`,
    }}>
      <div style={{ maxWidth: 1000, margin: "0 auto" }}>
        <h2 style={{
          fontFamily: F.display, fontWeight: 800,
          fontSize: "clamp(32px, 6vw, 72px)", letterSpacing: "-0.04em", lineHeight: 0.95,
          color: DS.ink, margin: "0 0 6vh", textWrap: "balance",
        }}>
          Consultez les{" "}
          <span style={{ fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500 }}>
            priorités électorales
          </span>
          {" "}des partenaires.
        </h2>

        <div data-stagger style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
          gap: 20,
        }}>
          {items.map(function(item, i) {
            var partner = partnersByAcro[item.acro];
            var partnerName = partner ? partner.name : item.acro;
            return (
              <a
                key={item.acro + i}
                className="priority-card"
                href={item.url}
                target="_blank"
                rel="noopener noreferrer"
                aria-label={partnerName + " — " + item.title + " (nouvel onglet)"}
                style={{
                  display: "flex",
                  flexDirection: "column",
                  background: DS.paper,
                  borderRadius: 16,
                  overflow: "hidden",
                  border: `1px solid ${DS.line}`,
                  boxShadow: "0 4px 20px rgba(111,90,81,0.08)",
                }}
              >
                <div style={{
                  background: DS.prune,
                  padding: "28px 16px 24px",
                  minHeight: 108,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                }}>
                  {partner ? (
                    <PartnerLogo partner={partner} />
                  ) : (
                    <div style={{
                      fontFamily: F.display, fontSize: 22, fontWeight: 700,
                      letterSpacing: "0.06em", color: "rgba(251,247,241,0.9)",
                    }}>{item.acro}</div>
                  )}
                </div>
                <div style={{
                  padding: "22px 22px 20px",
                  display: "flex",
                  flexDirection: "column",
                  flex: 1,
                }}>
                  <div style={{
                    fontFamily: F.display, fontSize: 11, fontWeight: 600,
                    letterSpacing: "0.16em", textTransform: "uppercase",
                    color: DS.prune, marginBottom: 10,
                  }}>{item.acro}</div>
                  <div style={{
                    fontFamily: F.display, fontSize: 18, fontWeight: 700,
                    letterSpacing: "-0.02em", lineHeight: 1.25,
                    color: DS.ink, flex: 1,
                  }}>{item.title}</div>
                  <div className="priority-cta" style={{
                    display: "flex", alignItems: "center", gap: 6,
                    marginTop: 18,
                    fontFamily: F.body, fontSize: 13, fontWeight: 700,
                    color: DS.inkSoft, letterSpacing: "0.02em",
                  }}>
                    <span>Consulter</span>
                    <span aria-hidden="true">→</span>
                  </div>
                </div>
              </a>
            );
          })}
        </div>
      </div>
    </section>
  );
}

// ── FAQ ────────────────────────────────────────────────────────
function V3FAQ() {
  const [open, setOpen] = React.useState(-1);
  return (
    <section data-animate style={{ padding: "6vh 5% 12vh", background: DS.paper, borderBottom: `1px solid ${DS.line}` }}>
      <div style={{ maxWidth: 900, margin: "0 auto" }}>
        <h2 style={{
          fontFamily: F.display, fontWeight: 800,
          fontSize: "clamp(40px, 8vw, 80px)", letterSpacing: "-0.04em", lineHeight: 0.9,
          color: DS.ink, marginBottom: "6vh",
        }}>Questions.</h2>
        <div style={{ borderTop: `1.5px solid ${DS.ink}` }}>
          {FAQS.map((f, i) => (
            <div key={i} onClick={() => setOpen(open === i ? -1 : i)}
              style={{
                borderBottom: `1px solid ${DS.line}`,
                padding: "28px 0", cursor: "pointer",
              }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 16 }}>
                <span style={{
                  fontFamily: F.display, fontSize: 20, fontWeight: 600,
                  letterSpacing: "-0.01em", color: DS.ink,
                }}>{f.q}</span>
                <span style={{
                  fontFamily: F.mono, fontSize: 16, color: DS.vert,
                  flexShrink: 0, width: 28, height: 28,
                  border: `1.5px solid ${DS.vert}`, borderRadius: "50%",
                  display: "inline-flex", alignItems: "center", justifyContent: "center",
                  transition: "transform 0.3s ease",
                  transform: open === i ? "rotate(45deg)" : "rotate(0deg)",
                }}>+</span>
              </div>
              {open === i && (
                <div style={{
                  fontFamily: F.body, fontSize: 16, lineHeight: 1.65,
                  color: DS.inkSoft, marginTop: 16, maxWidth: 700, fontWeight: 500,
                }}>{f.a}</div>
              )}
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ── Presse ────────────────────────────────────────────────────
var PRESSE_FORM = "presse";

function PresseField({ label, required, children }) {
  return (
    <label style={{ display: "block", textAlign: "left", marginBottom: 20 }}>
      <span style={{
        display: "block", marginBottom: 8,
        fontFamily: F.display, fontSize: 12, fontWeight: 600,
        letterSpacing: "0.08em", textTransform: "uppercase",
        color: DS.inkSoft,
      }}>
        {label}{required ? " *" : ""}
      </span>
      {children}
    </label>
  );
}

var presseInputStyle = {
  width: "100%", boxSizing: "border-box",
  padding: "14px 16px", borderRadius: 10,
  border: `1.5px solid ${DS.line}`,
  background: DS.paper, color: DS.ink,
  fontFamily: F.body, fontSize: 16, fontWeight: 500,
  lineHeight: 1.4,
};

function V3Presse() {
  var _s = React.useState("idle");
  var status = _s[0], setStatus = _s[1];
  var _e = React.useState("");
  var errorMsg = _e[0], setErrorMsg = _e[1];

  function handleSubmit(e) {
    e.preventDefault();
    setStatus("sending");
    setErrorMsg("");
    var form = e.currentTarget;
    var body = new URLSearchParams(new FormData(form)).toString();
    fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: body,
    }).then(function(res) {
      if (res.ok) {
        setStatus("success");
        form.reset();
        return;
      }
      throw new Error("HTTP " + res.status);
    }).catch(function() {
      setStatus("error");
      setErrorMsg("Une erreur est survenue. Réessayez ou écrivez à medias@achat-habitation.org.");
    });
  }

  return (
    <section data-animate style={{ padding: "6vh 5% 12vh", background: DS.paperWarm, borderBottom: `1px solid ${DS.line}` }}>
      <div style={{ maxWidth: 520, margin: "0 auto", textAlign: "center" }}>
        <h2 style={{
          fontFamily: F.display, fontWeight: 800,
          fontSize: "clamp(40px, 8vw, 80px)", letterSpacing: "-0.04em", lineHeight: 0.9,
          color: DS.ink, marginBottom: 24,
        }}>Salle de presse.</h2>
        <p style={{
          fontFamily: F.body, fontSize: 17, color: DS.inkSoft,
          lineHeight: 1.6, margin: "0 auto 40px", fontWeight: 500,
        }}>
          Demande d'accréditation ou d'entrevue :
        </p>

        {status === "success" ? (
          <div style={{
            padding: "32px 28px", borderRadius: 12,
            background: DS.paper, border: `1.5px solid ${DS.line}`,
            textAlign: "left",
          }}>
            <div style={{
              fontFamily: F.display, fontWeight: 700, fontSize: 20,
              color: DS.vert, marginBottom: 10,
            }}>Demande envoyée</div>
            <p style={{
              margin: 0, fontFamily: F.body, fontSize: 16,
              color: DS.inkSoft, lineHeight: 1.6, fontWeight: 500,
            }}>
              Merci — nous vous répondrons sous peu à l'adresse indiquée.
            </p>
            <button type="button" onClick={function() { setStatus("idle"); }} style={{
              marginTop: 20, padding: 0, border: "none", background: "none",
              fontFamily: F.body, fontSize: 14, fontWeight: 600,
              color: DS.vert, cursor: "pointer", textDecoration: "underline",
              textUnderlineOffset: "3px",
            }}>Envoyer une autre demande</button>
          </div>
        ) : (
          <form name={PRESSE_FORM} method="POST" action="/" onSubmit={handleSubmit} noValidate style={{ textAlign: "left" }}>
            <input type="hidden" name="form-name" value={PRESSE_FORM} />
            <p style={{ display: "none" }} aria-hidden="true">
              <label>Ne pas remplir : <input name="bot-field" tabIndex={-1} autoComplete="off" /></label>
            </p>

            <PresseField label="Nom complet" required>
              <input type="text" name="nom" required autoComplete="name" style={presseInputStyle} />
            </PresseField>
            <PresseField label="Média / organisation" required>
              <input type="text" name="media" required autoComplete="organization" style={presseInputStyle} />
            </PresseField>
            <PresseField label="Courriel" required>
              <input type="email" name="email" required autoComplete="email" style={presseInputStyle} />
            </PresseField>
            <PresseField label="Téléphone">
              <input type="tel" name="telephone" autoComplete="tel" style={presseInputStyle} />
            </PresseField>
            <PresseField label="Message" required>
              <textarea name="message" required rows={4} placeholder="Accréditation, entrevue, autres demandes…"
                style={{ ...presseInputStyle, resize: "vertical", minHeight: 120 }}
              />
            </PresseField>

            {status === "error" && errorMsg && (
              <p style={{
                margin: "0 0 20px", padding: "12px 16px", borderRadius: 8,
                background: "rgba(111,90,81,0.08)", color: DS.prune,
                fontFamily: F.body, fontSize: 14, lineHeight: 1.5, fontWeight: 500,
              }}>{errorMsg}</p>
            )}

            <button type="submit" disabled={status === "sending"} style={{
              display: "block", width: "100%",
              background: DS.vert, color: DS.paperWarm,
              padding: "16px 36px", borderRadius: 999, border: "none",
              fontFamily: F.display, fontSize: 15, fontWeight: 700,
              letterSpacing: "0.02em", cursor: status === "sending" ? "wait" : "pointer",
              opacity: status === "sending" ? 0.7 : 1,
            }}>
              {status === "sending" ? "Envoi en cours…" : "Envoyer ma demande →"}
            </button>
          </form>
        )}
      </div>
    </section>
  );
}

// ── CTA ────────────────────────────────────────────────────────
function V3CTA() {
  return (
    <section id="inscription" data-animate style={{
      padding: "16vh 5%",
      background: DS.vert, color: DS.paperWarm,
      textAlign: "center", position: "relative", overflow: "hidden",
    }}>
      <div style={{
        position: "absolute", inset: 0, display: "flex",
        alignItems: "center", justifyContent: "center",
        fontFamily: F.display, fontWeight: 800,
        fontSize: "clamp(160px, 22vw, 320px)", letterSpacing: "-0.06em",
        color: "rgba(251,247,241,0.07)", pointerEvents: "none",
        whiteSpace: "nowrap", lineHeight: 1,
      }}>03.09</div>

      <div style={{ position: "relative", maxWidth: 900, margin: "0 auto" }}>
        <Eyebrow label="Complet" color="rgba(251,247,241,0.7)"
          style={{ display: "block", textAlign: "center" }} />
        <h2 style={{
          fontFamily: F.display, fontWeight: 800,
          fontSize: "clamp(52px, 10vw, 140px)", letterSpacing: "-0.04em", lineHeight: 0.9,
          color: DS.paperWarm, margin: "0 0 16px", textWrap: "balance",
        }}>
          <span style={{ fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500 }}>C'est</span>
          <br />complet.
        </h2>
        <div style={{
          fontFamily: F.body, fontSize: 15, color: "rgba(251,247,241,0.8)",
          fontWeight: 500,
        }}>
          Toutes les places sont réservées
        </div>
      </div>
    </section>
  );
}

// ── Footer ─────────────────────────────────────────────────────
function V3Footer() {
  return (
    <footer style={{
      background: DS.ink, padding: "5% 5% 32px",
      borderTop: "1px solid rgba(255,255,255,0.06)",
    }}>
      <div style={{
        display: "flex", justifyContent: "space-between", alignItems: "flex-start",
        paddingBottom: 40, borderBottom: "1px solid rgba(255,255,255,0.08)", gap: 40,
        flexWrap: "wrap",
      }}>
        <div>
          <img src="assets/icone_blanche.png" alt={EVENT.organizer} style={{ height: 48, opacity: 0.85 }} />
          <p style={{
            marginTop: 16, fontFamily: F.body, fontSize: 13,
            color: "rgba(255,255,255,0.45)", lineHeight: 1.6, maxWidth: 400, fontWeight: 500,
          }}>
            {EVENT.organizerFull}.
          </p>
        </div>
        <div>
          <div style={{ fontFamily: F.display, fontSize: 11, fontWeight: 600,
            letterSpacing: "0.18em", textTransform: "uppercase",
            color: "rgba(255,255,255,0.4)", marginBottom: 14 }}>Contact</div>
          <a href={"mailto:" + (EVENT.contactEmail || "info@achat-habitation.org")} style={{
            fontFamily: F.body, fontSize: 14, color: "rgba(255,255,255,0.7)",
            fontWeight: 500, textDecoration: "underline",
            textUnderlineOffset: "3px",
          }}>
            {EVENT.contactEmail || "info@achat-habitation.org"}
          </a>
        </div>
      </div>
      <div style={{
        paddingTop: 28,
        fontFamily: F.mono, fontSize: 11, color: "rgba(255,255,255,0.3)",
        display: "flex", justifyContent: "space-between", alignItems: "center",
        gap: 16, flexWrap: "wrap",
      }}>
        <span>
          © 2026 {EVENT.organizer} ·{" "}
          <a href={"https://" + (EVENT.siteUrl || "habitation2026.org")} style={{
            color: "rgba(255,255,255,0.3)", textDecoration: "underline",
            textUnderlineOffset: "2px",
          }}>
            {EVENT.siteUrl || "habitation2026.org"}
          </a>
        </span>
        <a href="https://www.logo-es.quebec/" target="_blank" rel="noopener"
          title="Entreprise d'économie sociale" aria-label="Entreprise d'économie sociale" style={{
          display: "inline-flex", alignItems: "center", opacity: 0.55,
        }}>
          <img src="assets/logo-es.svg" alt="Logo ÉS — économie sociale" style={{ height: 26, width: 26, display: "block" }} />
        </a>
      </div>
    </footer>
  );
}

// ── Contenus de l'événement ───────────────────────────────────
function V3Contenus() {
  var items = (typeof VIDEOS !== "undefined" && VIDEOS) ? VIDEOS.slice() : [];
  var _s = React.useState(null);
  var active = _s[0], setActive = _s[1];

  React.useEffect(function () {
    if (!active) return;
    function onKey(e) { if (e.key === "Escape") setActive(null); }
    window.addEventListener("keydown", onKey);
    return function () { window.removeEventListener("keydown", onKey); };
  }, [active]);

  if (!items.length) {
    return (
      <div style={{
        padding: "6vh 5% 12vh", textAlign: "center",
        fontFamily: F.editorial, fontStyle: "italic", fontWeight: 500,
        fontSize: "clamp(28px, 4vw, 52px)", letterSpacing: "-0.02em", color: DS.inkSoft,
      }}>À venir.</div>
    );
  }

  return (
    <div>
      <div style={{ padding: "0 5% 12vh" }}>
        <div style={{ maxWidth: 1100, margin: "0 auto" }}>
          <div data-stagger style={{
            display: "grid",
            gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
            gap: 20,
          }}>
            {items.map(function (v) {
              return (
                <button key={v.id} type="button"
                  onClick={function () { setActive(v); }}
                  aria-label={"Lire la vidéo : " + v.title}
                  style={{
                    display: "block", width: "100%", padding: 0,
                    border: `1px solid ${DS.line}`, borderRadius: 16, overflow: "hidden",
                    background: DS.paper, cursor: "pointer", textAlign: "left",
                    boxShadow: "0 4px 20px rgba(111,90,81,0.08)",
                  }}>
                  <span style={{
                    position: "relative", display: "block", aspectRatio: "16 / 9",
                    background: DS.ink,
                  }}>
                    <img src={"https://i.ytimg.com/vi/" + v.id + "/hqdefault.jpg"} alt=""
                      loading="lazy" style={{
                        width: "100%", height: "100%", objectFit: "cover",
                        display: "block", opacity: 0.92,
                      }} />
                    <span style={{
                      position: "absolute", inset: 0,
                      display: "flex", alignItems: "center", justifyContent: "center",
                    }}>
                      <span style={{
                        width: 58, height: 58, borderRadius: "50%",
                        background: "rgba(26,26,26,0.6)",
                        border: "1.5px solid rgba(251,247,241,0.75)",
                        display: "inline-flex", alignItems: "center", justifyContent: "center",
                      }}>
                        <svg width="18" height="20" viewBox="0 0 18 20" aria-hidden="true"
                          style={{ marginLeft: 3 }}>
                          <path d="M2 2L16 10L2 18Z" fill={DS.paperWarm} />
                        </svg>
                      </span>
                    </span>
                  </span>
                  <span style={{ display: "block", padding: "16px 18px 18px" }}>
                    <span style={{
                      display: "block", fontFamily: F.display, fontSize: 11, fontWeight: 600,
                      letterSpacing: "0.16em", textTransform: "uppercase",
                      color: DS.prune, marginBottom: 6,
                    }}>{v.kind}</span>
                    <span style={{
                      display: "block", fontFamily: F.display, fontSize: 18, fontWeight: 700,
                      letterSpacing: "-0.02em", lineHeight: 1.25, color: DS.ink,
                    }}>{v.title}</span>
                  </span>
                </button>
              );
            })}
          </div>
        </div>
      </div>

      {active && (
        <div
          role="dialog" aria-modal="true" aria-label={active.title}
          onClick={function () { setActive(null); }}
          style={{
            position: "fixed", inset: 0, zIndex: 1000,
            background: "rgba(26,26,26,0.78)",
            display: "flex", alignItems: "center", justifyContent: "center",
            padding: "5%",
          }}
        >
          <div onClick={function (e) { e.stopPropagation(); }}
            style={{ width: "100%", maxWidth: 960, position: "relative" }}>
            <button onClick={function () { setActive(null); }} aria-label="Fermer" style={{
              position: "absolute", top: -44, right: 0,
              background: "none", border: "none", cursor: "pointer",
              fontFamily: F.display, fontSize: 28, fontWeight: 600, lineHeight: 1,
              color: DS.paperWarm,
            }}>×</button>
            <div style={{
              position: "relative", aspectRatio: "16 / 9",
              background: "#000", borderRadius: 12, overflow: "hidden",
            }}>
              <iframe
                src={"https://www.youtube.com/embed/" + active.id + "?autoplay=1&rel=0"}
                title={active.title}
                allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
                allowFullScreen
                style={{ position: "absolute", inset: 0, width: "100%", height: "100%", border: 0 }}
              />
            </div>
            <div style={{
              fontFamily: F.display, fontSize: 18, fontWeight: 700,
              color: DS.paperWarm, marginTop: 14,
            }}>{active.title}</div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Page Assembly ─────────────────────────────────────────────
function V3Affiche() {
  return (
    <div style={{ background: DS.paperWarm, color: DS.ink, width: "100%", overflow: "hidden" }}>
      <V3Hero />
      <AccordionItem id="contenus" label="Contenus de l'événement"
        background={DS.paperWarm} defaultOpen>
        <V3Contenus />
      </AccordionItem>
      <AccordionItem id="a-propos" label="À propos" background={DS.paperWarm}>
        <V3APropos />
      </AccordionItem>
      <AccordionItem id="programme" label={"Horaire · " + EVENT.date} background={DS.paper}>
        <V3Programme />
      </AccordionItem>
      {SPEAKERS && SPEAKERS.length > 0 && (
        <AccordionItem id="personnalites" label="Personnalités" background={DS.bleu} color={DS.ink}>
          <V3Personnalites />
        </AccordionItem>
      )}
      <AccordionItem id="lieu" label="Lieu" background={DS.paperWarm}>
        <V3Lieu />
      </AccordionItem>
      <AccordionItem id="partenaires" label="Partenaires" background={DS.prune}
        color={DS.paperWarm} borderBottom="1px solid rgba(251,247,241,0.22)">
        <V3Partenaires />
      </AccordionItem>
      {PRIORITIES && PRIORITIES.length > 0 && (
        <AccordionItem id="priorites" label="Priorités électorales" background={DS.paperWarm}>
          <V3Priorites />
        </AccordionItem>
      )}
      <AccordionItem id="faq" label="FAQ" background={DS.paper}>
        <V3FAQ />
      </AccordionItem>
      <AccordionItem id="presse" label="Presse" background={DS.paperWarm}>
        <V3Presse />
      </AccordionItem>
      <V3CTA />
      <V3Footer />
    </div>
  );
}

window.V3Affiche = V3Affiche;

// ── Maintenance page ─────────────────────────────────────────
function MaintenancePage() {
  return (
    <div style={{
      background: DS.ink, color: DS.paperWarm,
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
      minHeight: "100vh", padding: "5%", textAlign: "center",
    }}>
      <img src="assets/icone_blanche.png" alt={EVENT.organizer} style={{ height: 64, opacity: 0.8, marginBottom: 32 }} />
      <div style={{
        fontFamily: F.body, fontSize: 20, lineHeight: 1.6, fontWeight: 500,
        color: "rgba(251,247,241,0.7)", maxWidth: 500,
      }}>{SITE.maintenanceMessage}</div>
    </div>
  );
}

// ── Boot ──────────────────────────────────────────────────────
(function boot() {
  fetch('content/content.json?t=' + Date.now())
    .then(function(r) { return r.json(); })
    .then(function(data) {
      Object.assign(window, data);
      if (EVENT && EVENT.titleFr) {
        document.title = EVENT.titleFr + " 2026";
      }
      var Page = SITE.online ? V3Affiche : MaintenancePage;
      ReactDOM.createRoot(document.getElementById("root")).render(React.createElement(Page));
    })
    .catch(function() {
      // Fallback — render site from shared.jsx defaults
      ReactDOM.createRoot(document.getElementById("root")).render(React.createElement(V3Affiche));
    });
})();