// La Savane — Restaurant haut de gamme, Libreville (Gabon)
// Démo prospect VISIO-HOTEL · Direction artistique : « Éditorial print luxe »
// Inspirations : Mr Porter, Toiletpaper Magazine, Acne Studios, Apartamento, 032c.
// Le restaurant comme un numéro de magazine — couverture, cahiers, drop caps, numérotation monumentale.

const SAVANE = {
  // Surfaces
  ink:          '#0F0F0F',
  inkSoft:      '#1A1A1A',
  inkPaper:     '#15110D',
  inkLine:      'rgba(232,213,183,0.10)',
  inkLineMid:   'rgba(232,213,183,0.18)',
  // Mesh / glow ambient
  meshA:        'rgba(160,82,45,0.14)',
  meshB:        'rgba(232,213,183,0.06)',
  meshC:        'rgba(58,95,58,0.10)',
  // Texte
  paper:        '#F4E8D0',
  paperMute:    'rgba(244,232,208,0.66)',
  paperFaint:   'rgba(244,232,208,0.40)',
  paperGhost:   'rgba(244,232,208,0.18)',
  // Accents
  sage:         '#3A5F3A',
  sageHi:       '#5A7F5A',
  cuir:         '#A0522D',
  cuirHi:       '#C66B3A',
  laiton:       '#B5985A',
  laitonHi:     '#D4B97A',
  // Sémantique
  alert:        '#8B2F2F',
  success:      '#5A8C3A',
  // Typo
  display:      '"Cormorant Garamond", "Cormorant", Georgia, serif',
  sans:         '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
  mono:         '"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace',
};

// ─── Style local — éditorial papier ──────────────────────────────────────
if (typeof document !== 'undefined' && !document.getElementById('savane-style')) {
  const s = document.createElement('style');
  s.id = 'savane-style';
  s.textContent = `
    @keyframes savane-rise { from { opacity:0; transform: translateY(20px); } to { opacity:1; transform:none; } }
    @keyframes savane-fade { from { opacity:0; } to { opacity:1; } }
    @keyframes savane-mesh {
      0%,100% { background-position: 0% 0%, 100% 100%, 50% 50%; }
      50%     { background-position: 100% 0%, 0% 100%, 30% 70%; }
    }
    @keyframes savane-breath {
      0%,100% { transform: translateY(0) rotate(-0.4deg); }
      50%     { transform: translateY(-6px) rotate(-0.4deg); }
    }
    .savane-rise { animation: savane-rise 700ms cubic-bezier(0.16,1,0.3,1) both; }
    .savane-fade { animation: savane-fade 800ms ease both; }
    .savane-paper {
      background: ${SAVANE.inkSoft};
      box-shadow:
        0 1px 0 rgba(244,232,208,0.06) inset,
        0 -1px 0 rgba(160,82,45,0.10) inset,
        0 24px 48px -16px rgba(0,0,0,0.55),
        0 60px 80px -40px rgba(160,82,45,0.22);
    }
    .savane-paper-dark {
      background: ${SAVANE.inkPaper};
      box-shadow:
        0 1px 0 rgba(244,232,208,0.05) inset,
        0 24px 48px -16px rgba(0,0,0,0.65),
        0 60px 80px -40px rgba(160,82,45,0.18);
    }
    .savane-paper-flip {
      transition: transform 600ms cubic-bezier(0.16,1,0.3,1), box-shadow 600ms ease;
      transform-origin: top left;
      will-change: transform;
    }
    .savane-paper-flip:hover {
      transform: perspective(1400px) rotateY(-2deg) translateY(-4px);
      box-shadow:
        0 1px 0 rgba(244,232,208,0.08) inset,
        0 32px 64px -16px rgba(0,0,0,0.65),
        0 80px 100px -40px rgba(160,82,45,0.30);
    }
    .savane-dropcap::first-letter {
      font-family: ${SAVANE.display};
      font-size: 5em;
      font-weight: 500;
      font-style: italic;
      float: left;
      line-height: 0.82;
      padding: 8px 14px 0 0;
      color: ${SAVANE.laiton};
      font-variation-settings: "opsz" 144;
    }
    .savane-numero {
      position: absolute;
      font-family: ${SAVANE.display};
      font-style: italic;
      font-weight: 500;
      font-variation-settings: "opsz" 144;
      color: rgba(244,232,208,0.06);
      pointer-events: none;
      user-select: none;
      line-height: 0.8;
      letter-spacing: -0.04em;
    }
    .savane-mesh {
      background-image:
        radial-gradient(60% 50% at 12% 95%, ${SAVANE.meshA} 0%, transparent 60%),
        radial-gradient(50% 40% at 90% 8%, ${SAVANE.meshB} 0%, transparent 65%),
        radial-gradient(45% 35% at 50% 50%, ${SAVANE.meshC} 0%, transparent 70%);
      background-size: 140% 140%, 140% 140%, 140% 140%;
      animation: savane-mesh 24s ease-in-out infinite;
    }
    .savane-grain {
      position: absolute; inset: 0; pointer-events: none;
      background-image: repeating-linear-gradient(
        135deg,
        rgba(244,232,208,0.020) 0px,
        rgba(244,232,208,0.020) 1px,
        transparent 1px,
        transparent 9px
      );
      mix-blend-mode: overlay; opacity: 0.7;
    }
    .savane-display { font-family: ${SAVANE.display}; font-variation-settings: "opsz" 144; }
    .savane-eyebrow {
      font-family: ${SAVANE.mono};
      font-size: 10px;
      letter-spacing: 0.32em;
      text-transform: uppercase;
      color: ${SAVANE.paperFaint};
    }
    .savane-tag {
      font-family: ${SAVANE.mono};
      font-size: 9px;
      letter-spacing: 0.22em;
      text-transform: uppercase;
    }
    .savane-num { font-family: ${SAVANE.mono}; font-feature-settings: "tnum" 1; letter-spacing:-0.01em; }
    .savane-cta { transition: background 220ms ease, color 220ms ease, border-color 220ms ease, transform 220ms ease; }
    .savane-cta:hover { transform: translateY(-1px); }
    .savane-scroll::-webkit-scrollbar { display:none }
    .savane-scroll { scrollbar-width:none }
    .savane-quote {
      animation: savane-breath 7s ease-in-out infinite;
      transform: rotate(-0.4deg);
    }
    /* — Photos Unsplash (chargement progressif) — */
    @keyframes savane-shimmer {
      0%   { background-position: -200% 0; }
      100% { background-position:  200% 0; }
    }
    .savane-shimmer {
      background: linear-gradient(110deg, #1A1A1A 25%, #2A2A28 50%, #1A1A1A 75%);
      background-size: 200% 100%;
      animation: savane-shimmer 1.6s ease-in-out infinite;
    }
    .savane-img {
      opacity: 0;
      transition: opacity 600ms cubic-bezier(0.16,1,0.3,1), filter 800ms cubic-bezier(0.16,1,0.3,1);
      width: 100%; height: 100%;
      object-fit: cover; display: block;
      filter: blur(8px);
    }
    .savane-img.is-loaded { opacity: 1; filter: blur(0); }
    /* — Reveal scroll — */
    .savane-reveal {
      opacity: 0;
      transform: translateY(22px);
      transition: opacity 900ms cubic-bezier(0.16,1,0.3,1), transform 900ms cubic-bezier(0.16,1,0.3,1);
      will-change: opacity, transform;
    }
    .savane-reveal.is-visible { opacity: 1; transform: none; }
    /* — Page-flip 3D au hover (cards éditoriales) — */
    .savane-pageflip {
      transition: transform 600ms cubic-bezier(0.16,1,0.3,1), box-shadow 600ms ease;
      transform-origin: top left;
      will-change: transform;
    }
    .savane-pageflip:hover {
      transform: perspective(1400px) rotateY(-2.5deg) translateY(-5px);
      box-shadow:
        0 1px 0 rgba(244,232,208,0.10) inset,
        0 32px 64px -16px rgba(0,0,0,0.65),
        0 80px 100px -40px rgba(160,82,45,0.32);
    }
    /* — Hero blur-in mount — */
    @keyframes savane-blurin {
      0%   { opacity: 0; filter: blur(8px); transform: translateY(8px); }
      100% { opacity: 1; filter: blur(0);   transform: none; }
    }
    .savane-blurin { animation: savane-blurin 1100ms cubic-bezier(0.16,1,0.3,1) both; }
    /* — Clips organiques pour photos magazine — */
    .savane-clip-a { clip-path: polygon(4% 0%, 100% 2%, 96% 100%, 0% 98%); }
    .savane-clip-b { clip-path: polygon(0% 6%, 95% 0%, 100% 94%, 6% 100%); }
    .savane-clip-c { clip-path: polygon(2% 0%, 98% 4%, 100% 100%, 0% 96%); }
    /* — Watermark numéro outline cuir — */
    .savane-watermark {
      position: absolute;
      font-family: ${SAVANE.display};
      font-style: italic;
      font-weight: 300;
      -webkit-text-stroke: 1.5px ${SAVANE.laiton};
      color: transparent;
      pointer-events: none;
      user-select: none;
      line-height: 0.78;
      letter-spacing: -0.05em;
      opacity: 0.42;
    }
    /* — Signature animée Chef — */
    @keyframes savane-sigdraw {
      0%   { stroke-dashoffset: 380; }
      100% { stroke-dashoffset: 0; }
    }
    .savane-sigpath {
      stroke-dasharray: 380;
      stroke-dashoffset: 380;
      animation: savane-sigdraw 2400ms cubic-bezier(0.16,1,0.3,1) both;
    }
    @media (prefers-reduced-motion: reduce) {
      .savane-paper-flip, .savane-pageflip { transition: none !important; transform: none !important; }
      .savane-quote, .savane-mesh { animation: none !important; transform: none !important; }
      .savane-rise, .savane-fade, .savane-blurin, .savane-shimmer, .savane-sigpath { animation: none !important; }
      .savane-img { opacity: 1 !important; filter: none !important; transition: none !important; }
      .savane-reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
    }
  `;
  document.head.appendChild(s);
}

// ─── Photos Unsplash contextuelles (safari, plats, intérieurs) ─────────
const SAVANE_PHOTOS = {
  cover:        'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=1800&q=80',
  big5_lion:    'https://images.unsplash.com/photo-1546182990-dffeafbe841d?w=1200&q=80',
  big5_eleph:   'https://images.unsplash.com/photo-1503918503550-d6c1bd5e6c95?w=1200&q=80',
  big5_buffalo: 'https://images.unsplash.com/photo-1593179241555-29c80f041aab?w=1000&q=80',
  cuisine1:     'https://images.unsplash.com/photo-1542691457-cbe4df041eb2?w=1000&q=80',
  cuisine2:     'https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=1000&q=80',
  cuisine3:     'https://images.unsplash.com/photo-1544025162-d76694265947?w=1000&q=80',
  cuisine4:     'https://images.unsplash.com/photo-1473093295043-cdd812d0e601?w=1200&q=80',
  vins1:        'https://images.unsplash.com/photo-1506377247377-2a5b3b417ebb?w=900&q=80',
  vins2:        'https://images.unsplash.com/photo-1474722883778-792e7990302f?w=1200&q=80',
  cigare:       'https://images.unsplash.com/photo-1543361527-3a5cd2dc3037?w=900&q=80',
  interieur1:   'https://images.unsplash.com/photo-1564759224907-65b945ff0e84?w=1400&q=80',
  interieur2:   'https://images.unsplash.com/photo-1551218808-94e220e084d2?w=1200&q=80',
  cuir:         'https://images.unsplash.com/photo-1532634733-cae1395e440f?w=1200&q=80',
  chef:         'https://images.unsplash.com/photo-1577219491135-ce391730fb2c?w=900&q=80',
  table:        'https://images.unsplash.com/photo-1559339352-11d035aa65de?w=1200&q=80',
};

// ─── <SVImg> — image Unsplash avec shimmer + fade-in + fallback cuir ────
function SVImg({ src, alt = '', tone = 'cuir', clip, style = {}, kicker, label, captionItalic, h }) {
  const [loaded, setLoaded] = React.useState(false);
  const [errored, setErrored] = React.useState(false);
  const grads = {
    cuir:    `linear-gradient(135deg, #2A1A10 0%, #6B3A24 55%, #A0522D 100%)`,
    sage:    `linear-gradient(135deg, #14201A 0%, #2E472E 55%, #5A7F5A 100%)`,
    ivoire:  `linear-gradient(135deg, #1F1A12 0%, #5A4D3A 55%, #C8B68E 100%)`,
    nuit:    `linear-gradient(135deg, #050505 0%, #1A1A1A 50%, #2A2A28 100%)`,
    laiton:  `linear-gradient(135deg, #1A1410 0%, #5C4D2A 55%, #B5985A 100%)`,
  };
  return (
    <div className={clip ? `savane-clip-${clip}` : ''} style={{
      width: '100%', height: h || '100%',
      background: grads[tone] || grads.cuir,
      border: `0.5px solid ${SAVANE.laiton}`, borderRadius: 2,
      position: 'relative', overflow: 'hidden', ...style,
    }}>
      {!loaded && !errored && <div className="savane-shimmer" style={{ position: 'absolute', inset: 0 }} />}
      {!errored && (
        <img
          src={src} alt={alt} loading="lazy" decoding="async"
          onLoad={() => setLoaded(true)}
          onError={() => setErrored(true)}
          className={'savane-img' + (loaded ? ' is-loaded' : '')}
          style={{ position: 'absolute', inset: 0 }}
        />
      )}
      <div className="savane-grain" />
      {kicker && (
        <div className="savane-tag" style={{
          position: 'absolute', top: 12, left: 12, color: SAVANE.paper, opacity: 0.92,
          padding: '3px 8px', borderTop: `0.5px solid ${SAVANE.paper}`, borderBottom: `0.5px solid ${SAVANE.paper}`,
          background: 'rgba(15,15,15,0.32)', backdropFilter: 'blur(6px)',
          WebkitBackdropFilter: 'blur(6px)',
        }}>{kicker}</div>
      )}
      {label && (
        <div style={{
          position: 'absolute', left: 14, bottom: 10, right: 14,
          color: SAVANE.paper,
          fontFamily: captionItalic ? SAVANE.display : SAVANE.mono,
          fontStyle: captionItalic ? 'italic' : 'normal',
          fontSize: captionItalic ? 16 : 9,
          letterSpacing: captionItalic ? 0 : '0.22em',
          textTransform: captionItalic ? 'none' : 'uppercase',
          textShadow: '0 2px 12px rgba(0,0,0,0.55)',
          opacity: 0.95,
        }}>{label}</div>
      )}
    </div>
  );
}

// ─── Hook reveal scroll (IntersectionObserver) ──────────────────────────
function useSavaneReveal(rootRef) {
  React.useEffect(() => {
    if (typeof window === 'undefined') return;
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    const root = rootRef && rootRef.current ? rootRef.current : document;
    const targets = (root.querySelectorAll ? root.querySelectorAll('.savane-reveal') : []);
    if (reduce) {
      targets.forEach(el => el.classList.add('is-visible'));
      return;
    }
    if (typeof IntersectionObserver === 'undefined') {
      targets.forEach(el => el.classList.add('is-visible'));
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(en => {
        if (en.isIntersecting) {
          en.target.classList.add('is-visible');
          io.unobserve(en.target);
        }
      });
    }, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
    targets.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, [rootRef]);
}

// ─── Logo Mark — fil de plume ───────────────────────────────────────────
function SavaneMark({ size = 28, color, withWord = true }) {
  const c = color || SAVANE.paper;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: size * 0.42, lineHeight: 1 }}>
      <svg width={size * 0.92} height={size * 0.92} viewBox="0 0 32 32" fill="none">
        <path d="M16 30 L16 18" stroke={c} strokeWidth="1.2" strokeLinecap="round" />
        <path d="M16 18 C 13 18 11 16 11 13" stroke={c} strokeWidth="1" strokeLinecap="round" fill="none" />
        <path d="M16 18 C 19 18 21 16 21 13" stroke={c} strokeWidth="1" strokeLinecap="round" fill="none" />
        <path d="M5 11 Q 16 7 27 11" stroke={c} strokeWidth="1.3" strokeLinecap="round" fill="none" />
        <path d="M7 9 Q 16 5 25 9" stroke={c} strokeWidth="1" strokeLinecap="round" fill="none" opacity="0.7" />
        <path d="M3 30 L29 30" stroke={c} strokeWidth="0.6" strokeLinecap="round" opacity="0.45" />
      </svg>
      {withWord && (
        <span style={{
          fontFamily: SAVANE.mono, fontWeight: 500, fontSize: size * 0.42,
          color: c, letterSpacing: '0.32em', textTransform: 'uppercase',
        }}>
          La Savane
        </span>
      )}
    </span>
  );
}

// ─── Avatar — délègue à window.Avatar si dispo ──────────────────────────
function SavaneAvatar({ name, size = 42, ring = false, style = {} }) {
  if (typeof window !== 'undefined' && window.Avatar) {
    const Av = window.Avatar;
    return (
      <div style={{
        display: 'inline-block',
        boxShadow: ring ? `0 0 0 2px ${SAVANE.ink}, 0 0 0 3px ${SAVANE.laiton}` : 'none',
        borderRadius: '50%', ...style,
      }}>
        <Av name={name} size={size} />
      </div>
    );
  }
  const initials = (name || '?').split(/\s+/).map(w => w[0]).slice(0, 2).join('').toUpperCase();
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: SAVANE.inkSoft,
      border: `1px solid ${ring ? SAVANE.laiton : 'rgba(181,152,90,0.32)'}`,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      boxShadow: ring ? `0 0 0 2px ${SAVANE.ink}, 0 0 0 3px ${SAVANE.laiton}` : 'none',
      ...style,
    }}>
      <span style={{ fontFamily: SAVANE.display, fontStyle: 'italic', fontWeight: 500, fontSize: size * 0.42, color: SAVANE.laiton }}>
        {initials}
      </span>
    </div>
  );
}

// ─── Prix FCFA mono laiton ──────────────────────────────────────────────
function Price({ value, size = 14, accent = true, suffix }) {
  return (
    <span style={{
      fontFamily: SAVANE.mono, fontWeight: 500, fontSize: size,
      color: accent ? SAVANE.laiton : SAVANE.paper,
      letterSpacing: '-0.01em', whiteSpace: 'nowrap',
    }}>
      <span>{value.toLocaleString('fr-FR')}</span>
      <span style={{ fontSize: size * 0.62, marginLeft: 4, opacity: 0.75, letterSpacing: '0.10em' }}>FCFA</span>
      {suffix && <span style={{ fontSize: size * 0.62, marginLeft: 3, opacity: 0.55 }}>{suffix}</span>}
    </span>
  );
}

// ─── Eyebrow tag ─────────────────────────────────────────────────────────
function Eyebrow({ children, color, style = {} }) {
  return (
    <div className="savane-eyebrow" style={{ color: color || SAVANE.paperFaint, ...style }}>{children}</div>
  );
}

// ─── Tag éditorial (EXCLUSIF, ÉDITORIAL, ACTUEL) ───────────────────────
function Tag({ children, tone = 'laiton' }) {
  const c = tone === 'cuir' ? SAVANE.cuir : tone === 'sage' ? SAVANE.sageHi : tone === 'paper' ? SAVANE.paper : SAVANE.laiton;
  return (
    <span className="savane-tag" style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      color: c,
      borderTop: `0.5px solid ${c}`,
      borderBottom: `0.5px solid ${c}`,
      padding: '4px 10px',
    }}>{children}</span>
  );
}

// ─── Numéro monumental absolute ─────────────────────────────────────────
function Numero({ n, size = 140, top = 'auto', right = 'auto', bottom = -20, left = -10, opacity = 0.06 }) {
  return (
    <span className="savane-numero" style={{
      top, right, bottom, left,
      fontSize: size,
      color: `rgba(244,232,208,${opacity})`,
    }}>{n}</span>
  );
}

// ─── KPI éditorial paper ────────────────────────────────────────────────
function KPI({ label, value, sub, tone = 'laiton', big = false }) {
  const accent = tone === 'sage' ? SAVANE.sageHi : tone === 'cuir' ? SAVANE.cuir : tone === 'alert' ? SAVANE.alert : SAVANE.laiton;
  return (
    <div className="savane-paper" style={{
      padding: big ? '28px 30px' : '20px 22px', borderRadius: 4,
      flex: 1, minWidth: 150, position: 'relative', overflow: 'hidden',
    }}>
      <Eyebrow style={{ marginBottom: big ? 14 : 10 }}>{label}</Eyebrow>
      <div className="savane-display" style={{
        color: accent,
        fontStyle: 'italic',
        fontSize: big ? 56 : 30,
        fontWeight: 500,
        lineHeight: 0.95,
        letterSpacing: '-0.02em',
      }}>{value}</div>
      {sub && <div style={{ color: SAVANE.paperMute, fontSize: 11, marginTop: 8, fontFamily: SAVANE.sans }}>{sub}</div>}
    </div>
  );
}

// ─── Badge ───────────────────────────────────────────────────────────────
function Badge({ children, tone = 'sage' }) {
  const map = {
    sage:    { fg: SAVANE.success },
    laiton:  { fg: SAVANE.laitonHi },
    cuir:    { fg: SAVANE.cuirHi },
    alert:   { fg: '#D46666' },
  };
  const c = map[tone] || map.sage;
  return (
    <span className="savane-tag" style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: '3px 9px', color: c.fg,
      border: `0.5px solid ${c.fg}`,
      borderRadius: 1,
    }}>{children}</span>
  );
}

// ─── Bouton CTA ──────────────────────────────────────────────────────────
function CTA({ children, tone = 'laiton', onClick, ghost = false, size = 'md' }) {
  const tones = {
    laiton: { bg: SAVANE.laiton, fg: SAVANE.ink, br: SAVANE.laiton },
    cuir:   { bg: SAVANE.cuir, fg: SAVANE.paper, br: SAVANE.cuir },
    sage:   { bg: SAVANE.sage, fg: SAVANE.paper, br: SAVANE.sage },
    wa:     { bg: '#25D366', fg: '#FFFFFF', br: '#25D366' },
  };
  const t = tones[tone] || tones.laiton;
  const pad = size === 'sm' ? '8px 16px' : size === 'lg' ? '16px 32px' : '12px 24px';
  const fz = size === 'sm' ? 10 : size === 'lg' ? 12 : 11;
  return (
    <button onClick={onClick} className="savane-cta" style={{
      background: ghost ? 'transparent' : t.bg,
      color: ghost ? t.br : t.fg,
      border: `0.5px solid ${t.br}`,
      fontFamily: SAVANE.mono, fontSize: fz, letterSpacing: '0.22em', textTransform: 'uppercase',
      padding: pad, cursor: 'pointer', borderRadius: 1, fontWeight: 500,
    }}>{children}</button>
  );
}

// ─── Photo placeholder éditorial ────────────────────────────────────────
function Photo({ tone = 'cuir', h = 200, w, label, style = {}, kicker, captionItalic }) {
  const grads = {
    cuir:    `linear-gradient(135deg, #2A1A10 0%, #6B3A24 55%, #A0522D 100%)`,
    sage:    `linear-gradient(135deg, #14201A 0%, #2E472E 55%, #5A7F5A 100%)`,
    ivoire:  `linear-gradient(135deg, #1F1A12 0%, #5A4D3A 55%, #C8B68E 100%)`,
    nuit:    `linear-gradient(135deg, #050505 0%, #1A1A1A 50%, #2A2A28 100%)`,
    laiton:  `linear-gradient(135deg, #1A1410 0%, #5C4D2A 55%, #B5985A 100%)`,
  };
  return (
    <div style={{
      width: w || '100%', height: h, background: grads[tone] || grads.cuir,
      border: `0.5px solid ${SAVANE.laiton}`, borderRadius: 2,
      position: 'relative', overflow: 'hidden', ...style,
    }}>
      <div className="savane-grain" />
      {kicker && (
        <div className="savane-tag" style={{
          position: 'absolute', top: 12, left: 12,
          color: SAVANE.paper, opacity: 0.85,
          padding: '3px 8px', borderTop: `0.5px solid ${SAVANE.paper}`, borderBottom: `0.5px solid ${SAVANE.paper}`,
        }}>{kicker}</div>
      )}
      {label && (
        <div style={{
          position: 'absolute', left: 14, bottom: 10, right: 14,
          color: SAVANE.paper,
          fontFamily: captionItalic ? SAVANE.display : SAVANE.mono,
          fontStyle: captionItalic ? 'italic' : 'normal',
          fontSize: captionItalic ? 16 : 9,
          letterSpacing: captionItalic ? 0 : '0.22em',
          textTransform: captionItalic ? 'none' : 'uppercase',
          opacity: 0.92,
        }}>{label}</div>
      )}
    </div>
  );
}

// ─── Hairline laiton (signature) ────────────────────────────────────────
function Hairline({ w = 60, color, style = {} }) {
  return <div style={{
    width: w, height: 1, background: color || SAVANE.laiton, opacity: 0.85, ...style,
  }} />;
}

// ─── SVG signature manuscrite chef ──────────────────────────────────────
function SignatureChef({ color, w = 140 }) {
  const c = color || SAVANE.laiton;
  return (
    <svg viewBox="0 0 200 50" width={w} height={w * 0.25} style={{ display: 'block' }}>
      <path d="M5 30 C 15 5, 28 50, 40 25 S 60 8, 75 32 Q 90 50, 105 18 T 140 28 Q 160 38, 185 15"
        stroke={c} strokeWidth="1.4" strokeLinecap="round" fill="none" />
      <path d="M55 38 L 165 38" stroke={c} strokeWidth="0.5" opacity="0.5" />
    </svg>
  );
}

// ─── Données fictives ────────────────────────────────────────────────────
const SAVANE_MENU = [
  { cat: 'Entrées',           name: 'Carpaccio de gambas',          desc: 'Citron vert · huile d\'avocat du Gabon · pousses sauvages',                price: 9500,  allerg: ['crustacés'], tone: 'cuir' },
  { cat: 'Entrées',           name: 'Tartare de mangue verte',      desc: 'Coriandre · piment doux · crevettes croustillantes',                       price: 8500,  allerg: ['crustacés'], tone: 'sage' },
  { cat: 'Entrées',           name: 'Foie gras du Gabon',           desc: 'Chutney de papaye · brioche maison toastée',                                price: 14500, allerg: ['gluten'],    tone: 'ivoire' },
  { cat: 'Plats du chef',     name: 'Antilope rôtie sauce nokoss',  desc: 'Purée de manioc fumé · jus corsé aux baies de brousse',                     price: 22500, allerg: [],            tone: 'cuir' },
  { cat: 'Plats du chef',     name: 'Filet de capitaine au sel rose', desc: 'Légumes racines glacés · beurre noisette aux herbes',                     price: 19500, allerg: ['poisson'],   tone: 'ivoire' },
  { cat: 'Plats du chef',     name: 'Suprême de pintadeau',         desc: 'Saveurs sauvages · pommes grenailles · feuilles de baobab',                price: 18500, allerg: [],            tone: 'sage' },
  { cat: 'Spécialités safari', name: 'Brochettes de potamochère',   desc: 'Marinade aux épices de forêt · semoule épicée · oignons confits',          price: 24500, allerg: [],            tone: 'cuir' },
  { cat: 'Spécialités safari', name: 'Curry de bœuf brousse',       desc: 'Lait de coco · citronnelle · riz parfumé du nord',                         price: 17500, allerg: ['lait'],      tone: 'laiton' },
  { cat: 'Spécialités safari', name: 'Capitaine en feuilles',       desc: 'Cuit à l\'étouffée dans la feuille de bananier · riz blanc',               price: 19500, allerg: ['poisson'],   tone: 'sage' },
  { cat: 'Desserts',          name: 'Mignardises au chocolat',      desc: 'Cacao 72% · fève de tonka · crème glacée vanille',                          price: 7500,  allerg: ['lait','gluten'], tone: 'nuit' },
  { cat: 'Desserts',          name: 'Sorbet baobab',                desc: 'Pulpe fraîche · miel sauvage · biscuit aux noix',                           price: 6500,  allerg: ['fruits à coque'], tone: 'ivoire' },
  { cat: 'Desserts',          name: 'Plateau de fromages affinés',  desc: 'Sélection française et locale · confit de figues',                          price: 9500,  allerg: ['lait'],      tone: 'laiton' },
  { cat: 'Cellier',           name: 'Margaux 2010 — verre',         desc: 'Servi décanté · accord plats du chef',                                      price: 35000, allerg: [],            tone: 'cuir' },
  { cat: 'Cellier',           name: 'Krug Grande Cuvée — coupe',    desc: 'Édition 170 · pour célébrations',                                           price: 48000, allerg: [],            tone: 'laiton' },
  { cat: 'Cigares',           name: 'Cohiba Robusto',               desc: 'Cuba · vitole 124mm · au lounge cigares uniquement',                        price: 28000, allerg: [],            tone: 'nuit' },
  { cat: 'Cigares',           name: 'Macallan 18 ans',              desc: 'Single malt Highlands · servi dans verre Glencairn',                        price: 32000, allerg: [],            tone: 'laiton' },
];

const SAVANE_MENU_CATS = ['Entrées', 'Plats du chef', 'Spécialités safari', 'Desserts', 'Cellier', 'Cigares'];

const SAVANE_CELLIER = [
  { name: 'Château Petrus 2015',     cat: 'Vins rouges',  qty: 6,  pu: 1850000, badge: 'OK' },
  { name: 'Château Margaux 2010',    cat: 'Vins rouges',  qty: 8,  pu: 1250000, badge: 'OK' },
  { name: 'Cheval Blanc 2009',       cat: 'Vins rouges',  qty: 4,  pu: 1450000, badge: 'OK' },
  { name: 'Vega Sicilia Unico 2009', cat: 'Vins rouges',  qty: 2,  pu: 720000,  badge: 'ALERTE' },
  { name: 'Dom Pérignon 2010',       cat: 'Champagnes',   qty: 18, pu: 285000,  badge: 'OK' },
  { name: 'Krug Grande Cuvée',       cat: 'Champagnes',   qty: 12, pu: 220000,  badge: 'OK' },
  { name: 'Macallan 18 ans',         cat: 'Spiritueux',   qty: 5,  pu: 380000,  badge: 'OK' },
  { name: 'Hennessy Paradis',        cat: 'Spiritueux',   qty: 3,  pu: 520000,  badge: 'ALERTE' },
  { name: 'Cohiba Robusto',          cat: 'Cigares',      qty: 24, pu: 28000,   badge: 'OK' },
  { name: 'Montecristo No.2',        cat: 'Cigares',      qty: 14, pu: 22000,   badge: 'OK' },
  { name: 'Romeo y Julieta Wide',    cat: 'Cigares',      qty: 9,  pu: 24000,   badge: 'OK' },
  { name: 'Davidoff Aniversario',    cat: 'Cigares',      qty: 6,  pu: 32000,   badge: 'ALERTE' },
];

const SAVANE_TABLES = [
  { id: 'A1', salle: 'Acacia',  cvts: 4, total: 86500,  open: '19h32', timer: '42 min' },
  { id: 'A3', salle: 'Acacia',  cvts: 2, total: 34000,  open: '19h45', timer: '29 min' },
  { id: 'T1', salle: 'Tamarin', cvts: 8, total: 158500, open: '20h05', timer: '14 min' },
  { id: 'T4', salle: 'Tamarin', cvts: 6, total: 122000, open: '20h12', timer: '7 min' },
  { id: 'L2', salle: 'Lounge',  cvts: 3, total: 78000,  open: '20h00', timer: '19 min' },
  { id: 'A5', salle: 'Acacia',  cvts: 2, total: 24500,  open: '20h18', timer: '1 min' },
];

const SAVANE_PERSONNEL = [
  { name: 'Gaëlle Akagah',    role: 'Chef exécutif · Propriétaire',  anc: '8 ans', presence: 'Service', portrait: 'sage' },
  { name: 'Henri Mbadinga',   role: 'Sommelier',                     anc: '5 ans', presence: 'Service', portrait: 'cuir' },
  { name: 'Sébastien Ondo',   role: 'Maître d\'hôtel',               anc: '4 ans', presence: 'Service', portrait: 'laiton' },
  { name: 'Cyril Mboumba',    role: 'Serveur',                       anc: '2 ans', presence: 'Service', portrait: 'ivoire' },
  { name: 'Marlène Nzamba',   role: 'Serveuse',                      anc: '3 ans', presence: 'Repos',   portrait: 'cuir' },
];

// Modules du hub — disposition magazine asymétrique
const SAVANE_MODULES = [
  { key: 'site',         num: '01', tag: 'EXCLUSIF',  label: 'Le Numéro',           dek: 'Site éditorial · cuisine · privatisation', span: 'wide' },
  { key: 'menu',         num: '02', tag: 'ACTUEL',    label: 'La Carte',            dek: 'Service du soir · QR de table',            span: 'tall' },
  { key: 'cellier',      num: '03', tag: 'ÉDITORIAL', label: 'Le Chai',             dek: '200 références · sommelier dédié',          span: 'normal' },
  { key: 'caisse',       num: '04', tag: 'COULISSES', label: 'La Caisse',           dek: 'Encaissement Mobile Money · ticket fin',    span: 'normal' },
  { key: 'personnel',    num: '05', tag: 'PORTRAITS', label: 'La Maison',           dek: 'Le chef · le sommelier · la salle',         span: 'wide' },
  { key: 'gerant',       num: '06', tag: 'GESTION',   label: 'Le Pavillon',         dek: 'Couverts · ticket moyen · ratios',          span: 'tall' },
  { key: 'proprietaire', num: '07', tag: 'TABLEAU',   label: 'La Direction',        dek: 'Vue mensuelle · trésorerie · projection',   span: 'normal' },
];

// ─── HUB — Couverture magazine ─────────────────────────────────────────
function SavaneHub({ goto, vp }) {
  const phone = vp.w < 600;
  const tablet = vp.w >= 600 && vp.w < 1024;

  // Disposition masonry asymétrique : 4 colonnes desktop avec mix 2-col/2-row.
  const moduleGridStyle = phone
    ? { display: 'grid', gridTemplateColumns: '1fr', gap: 32 }
    : tablet
      ? { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gridAutoRows: '180px', gap: 28 }
      : { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gridAutoRows: '220px', gap: 36 };

  const cardSpan = (span) => {
    if (phone) return {};
    if (tablet) return span === 'wide' ? { gridColumn: 'span 2' } : span === 'tall' ? { gridRow: 'span 2' } : {};
    if (span === 'wide') return { gridColumn: 'span 2' };
    if (span === 'tall') return { gridRow: 'span 2' };
    return {};
  };

  return (
    <div className="savane-fade" style={{
      minHeight: '100%', background: SAVANE.ink, color: SAVANE.paper,
      fontFamily: SAVANE.sans, position: 'relative', overflow: 'hidden',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} />

      <div style={{ position: 'relative', padding: phone ? '20px 18px 60px' : '36px 56px 80px' }}>

        {/* ── Bandeau magazine ── */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          paddingBottom: 18, borderBottom: `0.5px solid ${SAVANE.inkLineMid}`, marginBottom: phone ? 28 : 40,
          gap: 12, flexWrap: 'wrap',
        }}>
          <SavaneMark size={phone ? 22 : 28} />
          <div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
            <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>
              ÉDITION 47 · AVRIL 2026
            </span>
            <Hairline w={phone ? 20 : 50} />
          </div>
        </div>

        {/* ── Hero couverture ── */}
        <div style={{
          position: 'relative',
          minHeight: phone ? '70vh' : tablet ? '78vh' : '88vh',
          display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
          paddingBottom: phone ? 40 : 60,
        }}>

          {/* Numéro géant 47 — placement bottom-right décalé */}
          <div style={{
            position: 'absolute',
            bottom: phone ? -20 : -40,
            right: phone ? -20 : -40,
            fontFamily: SAVANE.display,
            fontStyle: 'italic',
            fontWeight: 500,
            fontVariationSettings: '"opsz" 144',
            fontSize: phone ? 220 : tablet ? 320 : 460,
            color: 'transparent',
            WebkitTextStroke: `1px ${SAVANE.cuir}`,
            opacity: 0.32,
            lineHeight: 0.8,
            pointerEvents: 'none',
            letterSpacing: '-0.06em',
          }}>47</div>

          {/* Top eyebrow */}
          <div style={{ marginTop: phone ? 16 : 32, position: 'relative' }}>
            <Tag tone="laiton">EXCLUSIF · COUVERTURE</Tag>
          </div>

          {/* Titre central */}
          <div style={{ position: 'relative', maxWidth: 1100, marginTop: phone ? 32 : 48 }}>
            <h1 className="savane-display savane-rise" style={{
              fontStyle: 'italic', fontWeight: 400,
              fontSize: phone ? 64 : tablet ? 110 : 168,
              lineHeight: 0.92, color: SAVANE.paper, margin: 0,
              letterSpacing: '-0.035em',
              fontFeatureSettings: '"liga" 1, "dlig" 1',
            }}>La Savane</h1>
            <div style={{ marginTop: phone ? 14 : 22, display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
              <Hairline w={phone ? 36 : 60} color={SAVANE.laiton} />
              <span className="savane-display" style={{
                fontStyle: 'italic', fontWeight: 400,
                fontSize: phone ? 18 : 26, color: SAVANE.paperMute,
              }}>Restaurant · Libreville · Édition mensuelle</span>
            </div>
          </div>

          {/* Citation flottante encadrée */}
          <div className="savane-quote savane-rise" style={{
            position: 'relative', alignSelf: phone ? 'stretch' : 'flex-end',
            marginTop: phone ? 36 : 60,
            maxWidth: phone ? '100%' : 440,
            background: SAVANE.inkPaper,
            border: `0.5px solid ${SAVANE.laiton}`,
            padding: phone ? '24px 22px 22px' : '32px 30px 28px',
            boxShadow: `0 1px 0 rgba(244,232,208,0.08) inset, 0 32px 64px -16px rgba(0,0,0,0.7), 0 80px 110px -40px rgba(160,82,45,0.32)`,
            borderRadius: 2,
          }}>
            <div style={{
              position: 'absolute', top: phone ? -22 : -34, left: phone ? 16 : 22,
              fontFamily: SAVANE.display, fontStyle: 'italic',
              fontSize: phone ? 80 : 120, color: SAVANE.laiton,
              lineHeight: 1, opacity: 0.85,
              fontVariationSettings: '"opsz" 144',
            }}>«</div>
            <p className="savane-display" style={{
              fontStyle: 'italic', fontSize: phone ? 22 : 30,
              lineHeight: 1.25, color: SAVANE.paper,
              margin: 0, fontWeight: 400,
              fontVariationSettings: '"opsz" 144',
            }}>Là où la savane rencontre la table.</p>
            <div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 10 }}>
              <Hairline w={28} />
              <span className="savane-tag" style={{ color: SAVANE.laiton }}>Le Chef · Gaëlle Akagah</span>
            </div>
          </div>

          {/* Bandeau bas — éléments éditoriaux */}
          <div style={{
            position: 'relative',
            display: 'flex', flexWrap: 'wrap', gap: phone ? 10 : 24,
            marginTop: phone ? 36 : 60,
            paddingTop: phone ? 18 : 24,
            borderTop: `0.5px solid ${SAVANE.inkLineMid}`,
          }}>
            <span className="savane-tag" style={{ color: SAVANE.paper }}>120 COUVERTS</span>
            <span style={{ color: SAVANE.paperGhost }}>·</span>
            <span className="savane-tag" style={{ color: SAVANE.paper }}>ROTARY DEPUIS 2018</span>
            <span style={{ color: SAVANE.paperGhost }}>·</span>
            <span className="savane-tag" style={{ color: SAVANE.paper }}>ÉTOILÉ ANNUAIRE 2025</span>
          </div>
        </div>

        {/* ── Sommaire ── */}
        <div style={{
          marginTop: phone ? 60 : 100, marginBottom: 30,
          display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 14, flexWrap: 'wrap',
        }}>
          <div>
            <Eyebrow style={{ marginBottom: 10 }}>P. 02 — AU SOMMAIRE</Eyebrow>
            <h2 className="savane-display" style={{
              fontStyle: 'italic', fontSize: phone ? 38 : 60,
              color: SAVANE.paper, margin: 0, letterSpacing: '-0.02em', fontWeight: 400,
            }}>Le numéro en sept articles.</h2>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <Hairline w={50} />
            <Tag tone="paper">07 ARTICLES</Tag>
          </div>
        </div>

        {/* ── Grid masonry asymétrique ── */}
        <div style={moduleGridStyle}>
          {SAVANE_MODULES.map((m, i) => {
            const altPaper = i % 3 === 1 ? 'savane-paper-dark' : 'savane-paper';
            const numPosLeft = i % 2 === 0;
            return (
              <button
                key={m.key}
                onClick={() => goto(m.key)}
                className={`${altPaper} savane-paper-flip savane-rise`}
                style={{
                  ...cardSpan(m.span),
                  position: 'relative', overflow: 'hidden',
                  border: `0.5px solid ${SAVANE.inkLineMid}`,
                  borderRadius: 4,
                  padding: phone ? '28px 26px' : '36px 38px',
                  textAlign: 'left', cursor: 'pointer',
                  color: SAVANE.paper, fontFamily: SAVANE.sans,
                  animationDelay: `${i * 100}ms`,
                  display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                  minHeight: phone ? 240 : 'auto',
                }}
              >
                <Numero
                  n={m.num}
                  size={m.span === 'tall' ? 220 : m.span === 'wide' ? 180 : 160}
                  bottom={numPosLeft ? -30 : 'auto'}
                  top={numPosLeft ? 'auto' : -40}
                  left={numPosLeft ? -8 : 'auto'}
                  right={numPosLeft ? 'auto' : -10}
                  opacity={0.07}
                />

                {/* En-tête : tag + numéro mono */}
                <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
                  <Tag tone={i % 2 === 0 ? 'laiton' : 'cuir'}>{m.tag}</Tag>
                  <span className="savane-tag" style={{ color: SAVANE.paperGhost }}>P. {String((i + 1) * 7).padStart(2, '0')}</span>
                </div>

                {/* Titre + dek */}
                <div style={{ position: 'relative' }}>
                  <h3 className="savane-display" style={{
                    margin: 0, fontStyle: 'italic',
                    fontSize: m.span === 'wide' ? (phone ? 30 : 42) : (phone ? 28 : 34),
                    color: SAVANE.paper, fontWeight: 400, lineHeight: 1.05,
                    letterSpacing: '-0.02em',
                  }}>{m.label}</h3>
                  <p style={{
                    margin: '10px 0 0', color: SAVANE.paperMute,
                    fontSize: 13, lineHeight: 1.5, maxWidth: 280,
                  }}>{m.dek}</p>
                </div>

                {/* Pied : hairline + lire */}
                <div style={{ position: 'relative', marginTop: 18, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                  <Hairline w={70} />
                  <span className="savane-tag" style={{ color: SAVANE.laiton }}>Lire →</span>
                </div>
              </button>
            );
          })}
        </div>

        {/* ── Footer magazine ── */}
        <div style={{
          marginTop: phone ? 60 : 100, paddingTop: 30,
          borderTop: `0.5px solid ${SAVANE.laiton}`,
          display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 12,
        }}>
          <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>
            LA SAVANE · 14, BD DU BORD DE MER · LIBREVILLE · GA
          </span>
          <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>
            ROTARY 7100 · @LASAVANE_GABON
          </span>
          <span className="savane-tag" style={{ color: SAVANE.paperGhost }}>
            MAGAZINE DÉPOSÉ · 2026
          </span>
        </div>

      </div>
    </div>
  );
}

// ─── HEADER VUE INTÉRIEURE ──────────────────────────────────────────────
function ViewHeader({ goto, vp, eyebrow, title, dek, page }) {
  const phone = vp.w < 600;
  return (
    <div style={{
      paddingBottom: 24, borderBottom: `0.5px solid ${SAVANE.inkLineMid}`,
      marginBottom: phone ? 28 : 44,
    }}>
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        marginBottom: phone ? 22 : 30, gap: 12, flexWrap: 'wrap',
      }}>
        <button onClick={() => goto('hub')} className="savane-cta" style={{
          background: 'transparent', border: 'none', color: SAVANE.paperFaint,
          fontFamily: SAVANE.mono, fontSize: 9, letterSpacing: '0.32em',
          textTransform: 'uppercase', cursor: 'pointer', padding: 0,
        }}
        onMouseEnter={(e) => e.target.style.color = SAVANE.laiton}
        onMouseLeave={(e) => e.target.style.color = SAVANE.paperFaint}>
          ← Sommaire
        </button>
        <SavaneMark size={phone ? 20 : 24} />
        <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>P. {page} · ÉDITION 47</span>
      </div>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', flexWrap: 'wrap', gap: 16 }}>
        <div style={{ flex: 1, minWidth: 280 }}>
          <Eyebrow style={{ marginBottom: 10 }}>{eyebrow}</Eyebrow>
          <h1 className="savane-display" style={{
            margin: 0, fontStyle: 'italic', fontWeight: 400,
            fontSize: phone ? 42 : 72, color: SAVANE.paper,
            letterSpacing: '-0.03em', lineHeight: 0.95,
          }}>{title}</h1>
          {dek && <p style={{
            margin: '14px 0 0', color: SAVANE.paperMute,
            fontSize: phone ? 14 : 15, lineHeight: 1.55, maxWidth: 560,
          }}>{dek}</p>}
        </div>
      </div>
    </div>
  );
}

// ─── 2. SITE WEB — « LE NUMÉRO » (édition vitrine enrichie) ───────────
function SavaneSite({ goto, vp }) {
  // Détection device (phone / tablet / desktop, touch, reduced motion)
  const isPhone   = vp.w < 600;
  const isTablet  = vp.w >= 600 && vp.w < 1024;
  const isDesktop = vp.w >= 1024;
  const phone = isPhone, tablet = isTablet; // alias compat
  const [isTouch, setIsTouch] = React.useState(false);
  const [reduceMotion, setReduceMotion] = React.useState(false);
  React.useEffect(() => {
    if (typeof window === 'undefined') return;
    setIsTouch(!!(window.matchMedia && window.matchMedia('(pointer: coarse)').matches));
    setReduceMotion(!!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches));
  }, []);

  // Reveal scroll : on observe tout `.savane-reveal` à l'intérieur du wrapper
  const rootRef = React.useRef(null);
  useSavaneReveal(rootRef);

  // Page-flip uniquement sur desktop non-touch
  const flipClass = (!isTouch && isDesktop && !reduceMotion) ? 'savane-pageflip' : '';

  // Données éditoriales
  const SOMMAIRE = [
    { num: '01', title: "L'art du dressage",       page: '04', kicker: 'GASTRONOMIE' },
    { num: '02', title: 'La carte safari',         page: '12', kicker: 'PORTFOLIO' },
    { num: '03', title: 'Le chai',                 page: '22', kicker: 'CELLIER' },
    { num: '04', title: 'Le cigare',               page: '28', kicker: 'LOUNGE' },
    { num: '05', title: "L'expérience Big Five",   page: '34', kicker: 'SAVANE' },
    { num: '06', title: 'Privatisations',          page: '40', kicker: 'ESPACES' },
  ];

  const PLATS = [
    { n: '01', name: 'Antilope nokoss',         desc: 'purée de manioc fumé · jus aux baies de brousse',  price: '22 500', photo: SAVANE_PHOTOS.cuisine1, span: 'tall' },
    { n: '02', name: 'Capitaine en feuilles',   desc: 'étouffée à la feuille de bananier · riz parfumé',  price: '19 500', photo: SAVANE_PHOTOS.cuisine2, span: 'normal' },
    { n: '03', name: 'Foie gras du Gabon',      desc: 'chutney de papaye · brioche maison toastée',        price: '14 500', photo: null,                    span: 'normal', tone: 'laiton' },
    { n: '04', name: 'Filet de capitaine',      desc: 'sel rose · légumes glacés · beurre noisette',       price: '19 500', photo: SAVANE_PHOTOS.cuisine3, span: 'wide'   },
    { n: '05', name: 'Suprême de pintadeau',    desc: 'feuilles de baobab · pommes grenailles',            price: '18 500', photo: SAVANE_PHOTOS.cuisine4, span: 'normal' },
    { n: '06', name: 'Mignardises tonka',       desc: 'cacao 72% · fève de tonka · glace vanille',         price: '7 500',  photo: null,                    span: 'normal', tone: 'cuir' },
  ];

  const VIGNOBLES = [
    { house: 'Château Petrus',   region: 'Pomerol · 2015',     refs: '06 bouteilles' },
    { house: 'Château Margaux',  region: 'Margaux · 2010',     refs: '08 bouteilles' },
    { house: 'Dom Pérignon',     region: 'Champagne · 2010',   refs: '18 bouteilles' },
    { house: 'Krug',             region: 'Grande Cuvée · 170', refs: '12 coupes' },
  ];

  const BIG5 = [
    { salle: 'Salle Lion',     cap: '12 couverts', photo: SAVANE_PHOTOS.big5_lion,     clip: 'a' },
    { salle: 'Salle Éléphant', cap: '24 couverts', photo: SAVANE_PHOTOS.big5_eleph,    clip: 'b' },
    { salle: 'Salle Buffle',   cap: '08 couverts', photo: SAVANE_PHOTOS.big5_buffalo,  clip: 'c' },
  ];

  const ESPACES = [
    { name: 'Acacia',         cap: '40 couverts', desc: 'Salon principal · feuillages tressés · lumière chaude.',     photo: SAVANE_PHOTOS.interieur1, kicker: '01' },
    { name: 'Tamarin',        cap: '80 couverts', desc: 'Grande salle · soirées Rotary, anniversaires, mariages.',     photo: SAVANE_PHOTOS.interieur2, kicker: '02' },
    { name: 'Lounge cigares', cap: '15 couverts', desc: 'Alcôves cuir · ventilation dédiée · cave humidifiée.',         photo: SAVANE_PHOTOS.cuir,        kicker: '03' },
  ];

  // Effet papier reusable
  const paperShadow = '0 1px 0 rgba(244,232,208,0.06) inset, 0 24px 48px -16px rgba(0,0,0,0.55), 0 60px 80px -40px rgba(160,82,45,0.22)';

  return (
    <div ref={rootRef} className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper,
      fontFamily: SAVANE.sans, padding: isPhone ? '20px 18px 60px' : isTablet ? '28px 36px 70px' : '32px 56px 96px',
      position: 'relative', overflow: 'hidden',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.7 }} />

      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="ARTICLE 01 · LE NUMÉRO"
          title="La Savane"
          dek="Le restaurant comme un magazine. Cuisine d'auteur · cellier deux cents références · lounge cigares en lounge feutré."
          page="07" />

        {/* ═══════════════ HERO COUVERTURE INTÉRIEURE — DOUBLE PAGE MAGAZINE ═══════════════ */}
        <div className="savane-blurin" style={{
          position: 'relative', marginBottom: isPhone ? 56 : 110,
          paddingTop: 8,
        }}>
          {/* Bandeau haut "édition" */}
          <div style={{
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            paddingBottom: 14, marginBottom: isPhone ? 24 : 36,
            borderBottom: `0.5px solid ${SAVANE.inkLineMid}`,
            flexWrap: 'wrap', gap: 10,
          }}>
            <span style={{
              fontFamily: SAVANE.mono, fontSize: isPhone ? 10 : 12,
              letterSpacing: '0.32em', textTransform: 'uppercase',
              color: SAVANE.paper, fontWeight: 500,
            }}>LA SAVANE</span>
            <span className="savane-tag" style={{ color: SAVANE.laiton }}>
              ÉDITION 47 · NUMÉRO INTÉRIEUR · AVRIL 2026
            </span>
          </div>

          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : isTablet ? '1fr 1fr' : '1.05fr 1fr',
            gap: isPhone ? 32 : isTablet ? 44 : 64,
            alignItems: 'center',
          }}>
            {/* Colonne gauche — visuel cover + watermark 47 */}
            <div style={{ position: 'relative', minHeight: isPhone ? 360 : 520 }}>
              {/* Watermark 47 */}
              <span className="savane-watermark" style={{
                fontSize: isPhone ? 200 : isTablet ? 280 : 380,
                top: isPhone ? -40 : -70,
                right: isPhone ? -10 : -30,
                zIndex: 0,
              }}>47</span>
              <div style={{
                position: 'relative', zIndex: 1,
                height: isPhone ? 360 : isTablet ? 460 : 540,
                boxShadow: paperShadow,
              }}>
                <SVImg
                  src={SAVANE_PHOTOS.cover}
                  alt="Baobab et savane au crépuscule"
                  tone="cuir" clip="a"
                  kicker="COUVERTURE 47"
                  label="Là où la savane rencontre la table."
                  captionItalic
                />
              </div>
            </div>

            {/* Colonne droite — titre, sous-titre, citation flottante, CTAs */}
            <div style={{ position: 'relative' }}>
              <Numero n="01" size={isPhone ? 130 : 200} top={-44} right={-10} opacity={0.07} />
              <span className="savane-tag" style={{ color: SAVANE.laiton, display: 'block', marginBottom: 14 }}>
                P. 02 — COUVERTURE INTÉRIEURE
              </span>
              <h2 className="savane-display" style={{
                margin: 0, fontStyle: 'italic', fontWeight: 400,
                fontSize: isPhone ? 72 : isTablet ? 108 : 156,
                color: SAVANE.paper, lineHeight: 0.92, letterSpacing: '-0.035em',
                position: 'relative',
              }}>La Savane</h2>
              <p className="savane-display" style={{
                margin: '22px 0 0', fontStyle: 'italic',
                fontSize: isPhone ? 19 : isTablet ? 24 : 30,
                color: SAVANE.paperMute, lineHeight: 1.3,
                position: 'relative', maxWidth: 520,
              }}>Là où la savane rencontre la table.</p>

              {/* Citation flottante encadrée laiton — breathing */}
              <div className="savane-quote" style={{
                position: 'relative', marginTop: isPhone ? 28 : 40,
                padding: isPhone ? '22px 20px' : '28px 32px',
                background: SAVANE.inkPaper,
                border: `0.5px solid ${SAVANE.laiton}`,
                borderRadius: 2, maxWidth: 520,
                boxShadow: paperShadow,
              }}>
                <span style={{
                  position: 'absolute', top: -28, left: 18,
                  fontFamily: SAVANE.display, fontStyle: 'italic',
                  fontSize: 86, color: SAVANE.laiton, lineHeight: 1,
                }}>«</span>
                <blockquote className="savane-display" style={{
                  margin: 0, fontStyle: 'italic',
                  fontSize: isPhone ? 20 : 26, lineHeight: 1.32,
                  color: SAVANE.paper, fontWeight: 400,
                }}>Mille ans de palais, deux saisons de cuisine.</blockquote>
                <div className="savane-tag" style={{ color: SAVANE.laiton, marginTop: 12 }}>
                  — LE CHEF · GAËLLE AKAGAH
                </div>
              </div>

              <div style={{ marginTop: isPhone ? 26 : 36, display: 'flex', gap: 14, flexWrap: 'wrap' }}>
                <CTA tone="laiton" onClick={() => goto('menu')} size="lg">Réserver une table</CTA>
                <CTA tone="cuir" ghost onClick={() => goto('menu')} size="lg">Voir la carte</CTA>
              </div>
            </div>
          </div>
        </div>

        {/* ═══════════════ AU SOMMAIRE — LISTE ÉDITORIALE ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 60 : 96, position: 'relative' }}>
          <div style={{
            display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
            marginBottom: 28, paddingBottom: 14,
            borderBottom: `0.5px solid ${SAVANE.laiton}`, flexWrap: 'wrap', gap: 10,
          }}>
            <span style={{
              fontFamily: SAVANE.mono, fontSize: isPhone ? 11 : 13,
              letterSpacing: '0.36em', textTransform: 'uppercase',
              color: SAVANE.paper, fontWeight: 500,
            }}>AU SOMMAIRE</span>
            <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>
              SIX ARTICLES · ÉDITION 47
            </span>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : 'repeat(2, 1fr)',
            columnGap: isPhone ? 0 : 56, rowGap: 0,
          }}>
            {SOMMAIRE.map((s, i) => (
              <div key={s.num} style={{
                display: 'grid', gridTemplateColumns: 'auto 1fr auto', gap: 18,
                alignItems: 'baseline',
                padding: '20px 0',
                borderBottom: `0.5px solid ${SAVANE.inkLine}`,
              }}>
                <span className="savane-num" style={{
                  fontSize: isPhone ? 13 : 15, color: SAVANE.laiton, fontWeight: 500,
                  letterSpacing: '0.04em',
                }}>{s.num}.</span>
                <div>
                  <span className="savane-tag" style={{ color: SAVANE.paperFaint, display: 'block', marginBottom: 4 }}>
                    {s.kicker}
                  </span>
                  <span className="savane-display" style={{
                    fontStyle: 'italic', fontSize: isPhone ? 22 : 26,
                    color: SAVANE.paper, letterSpacing: '-0.01em',
                  }}>{s.title}</span>
                </div>
                <span className="savane-num" style={{
                  fontSize: 11, color: SAVANE.paperFaint,
                  letterSpacing: '0.16em',
                }}>P. {s.page}</span>
              </div>
            ))}
          </div>
        </section>

        {/* ═══════════════ ARTICLE 01 — L'ART DU DRESSAGE ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="01" size={isPhone ? 140 : 220} top={-50} right={-20} opacity={0.05} />
          <div style={{ marginBottom: 28 }}>
            <Eyebrow style={{ marginBottom: 12 }}>P. 04 — ARTICLE 01 · GASTRONOMIE</Eyebrow>
            <h3 className="savane-display" style={{
              margin: 0, fontStyle: 'italic', fontWeight: 400,
              fontSize: isPhone ? 38 : isTablet ? 56 : 72,
              color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
              maxWidth: 880,
            }}>L'art du dressage.</h3>
            <p className="savane-display" style={{
              margin: '14px 0 0', fontStyle: 'italic',
              fontSize: isPhone ? 16 : 22, color: SAVANE.paperMute, lineHeight: 1.4,
              maxWidth: 640,
            }}>Une table d'auteur, signée Gaëlle Akagah.</p>
          </div>

          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : '1fr 1.05fr',
            gap: isPhone ? 30 : 56, alignItems: 'start',
          }}>
            {/* Photo Chef — masquage organique */}
            <div style={{ position: 'relative', height: isPhone ? 380 : 560, boxShadow: paperShadow }}>
              <SVImg
                src={SAVANE_PHOTOS.chef}
                alt="Le Chef Gaëlle Akagah en cuisine"
                tone="ivoire" clip="b"
                kicker="EN CUISINE"
                label="Le Chef · service du soir."
                captionItalic
              />
            </div>

            {/* Texte — drop cap + citation */}
            <div>
              <p className="savane-dropcap" style={{
                margin: 0, fontSize: isPhone ? 15 : 16, lineHeight: 1.72,
                color: SAVANE.paper, fontFamily: SAVANE.sans,
              }}>
                Le Chef Gaëlle Akagah revisite, depuis 2018, les terroirs gabonais avec une exigence française obsessionnelle. Sa cuisine puise dans la forêt — feuilles de baobab, manioc fumé, baies de brousse — et l'élève au standard des grandes tables. Le menu change avec les saisons sèches et humides, jamais figé. Le service est rythmé, le décor sobre : cuir patiné, laiton brossé, lin écru. Une maison où l'on vient célébrer, parler affaires ou simplement bien dîner. La discrétion fait partie du service.
              </p>

              <div style={{
                position: 'relative', marginTop: isPhone ? 28 : 40,
                padding: isPhone ? '24px 22px' : '32px 32px',
                background: SAVANE.inkPaper,
                border: `0.5px solid ${SAVANE.laiton}`,
                borderRadius: 2,
                boxShadow: paperShadow,
              }}>
                <span style={{
                  position: 'absolute', top: -26, left: 18,
                  fontFamily: SAVANE.display, fontStyle: 'italic',
                  fontSize: 80, color: SAVANE.laiton, lineHeight: 1,
                }}>«</span>
                <blockquote className="savane-display" style={{
                  margin: 0, fontStyle: 'italic',
                  fontSize: isPhone ? 22 : 32, lineHeight: 1.28,
                  color: SAVANE.paper, fontWeight: 400,
                }}>Je cuisine la forêt comme un parfumeur — par touches, jamais en force.</blockquote>
                {/* Signature manuscrite SVG cursive (path animé) */}
                <svg viewBox="0 0 200 50" width={150} height={38} style={{ display: 'block', marginTop: 16 }}>
                  <path className="savane-sigpath"
                    d="M5 30 C 15 5, 28 50, 40 25 S 60 8, 75 32 Q 90 50, 105 18 T 140 28 Q 160 38, 185 15"
                    stroke={SAVANE.laiton} strokeWidth="1.4" strokeLinecap="round" fill="none" />
                  <path d="M55 38 L 165 38" stroke={SAVANE.laiton} strokeWidth="0.5" opacity="0.5" />
                </svg>
                <div className="savane-tag" style={{ color: SAVANE.laiton, marginTop: 4 }}>
                  GAËLLE AKAGAH · CHEF EXÉCUTIF · PROPRIÉTAIRE
                </div>
              </div>
            </div>
          </div>
        </section>

        {/* ═══════════════ ARTICLE 02 — LA CARTE SAFARI (masonry asymétrique) ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="02" size={isPhone ? 140 : 220} top={-50} left={-10} opacity={0.05} />
          <div style={{
            display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
            marginBottom: 28, flexWrap: 'wrap', gap: 12,
          }}>
            <div>
              <Eyebrow style={{ marginBottom: 12 }}>P. 12 — ARTICLE 02 · PORTFOLIO</Eyebrow>
              <h3 className="savane-display" style={{
                margin: 0, fontStyle: 'italic', fontWeight: 400,
                fontSize: isPhone ? 38 : isTablet ? 54 : 68,
                color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
              }}>La carte safari.</h3>
              <p className="savane-display" style={{
                margin: '14px 0 0', fontStyle: 'italic',
                fontSize: isPhone ? 16 : 22, color: SAVANE.paperMute, lineHeight: 1.4,
                maxWidth: 580,
              }}>Six plats · une saison · une géographie.</p>
            </div>
            <Tag tone="laiton">PORTFOLIO</Tag>
          </div>

          {/* Grille masonry asymétrique : phone 1 col, tablet 2 cols, desktop 4 cols avec span */}
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : isTablet ? 'repeat(2, 1fr)' : 'repeat(4, 1fr)',
            gridAutoRows: isPhone ? 'auto' : isTablet ? '260px' : '230px',
            gap: isPhone ? 22 : 26,
          }}>
            {PLATS.map((p, i) => {
              const span = isPhone ? {} :
                p.span === 'tall' ? { gridRow: 'span 2' } :
                p.span === 'wide' ? { gridColumn: isTablet ? 'span 2' : 'span 2' } :
                {};
              return (
                <div key={p.n} className={`savane-paper ${flipClass} savane-reveal`}
                  style={{
                    position: 'relative', borderRadius: 4, overflow: 'hidden',
                    background: SAVANE.inkSoft,
                    transitionDelay: `${i * 80}ms`,
                    display: 'flex', flexDirection: 'column',
                    minHeight: isPhone ? 320 : 'auto',
                    ...span,
                  }}>
                  <div style={{ position: 'relative', flex: 1, minHeight: 140 }}>
                    {p.photo ? (
                      <SVImg src={p.photo} alt={p.name} tone="cuir" />
                    ) : (
                      <div style={{ position: 'absolute', inset: 0, background: p.tone === 'laiton'
                        ? `linear-gradient(135deg, #1A1410 0%, #5C4D2A 55%, #B5985A 100%)`
                        : `linear-gradient(135deg, #2A1A10 0%, #6B3A24 55%, #A0522D 100%)` }}>
                        <div className="savane-grain" />
                      </div>
                    )}
                  </div>
                  <div style={{ padding: isPhone ? '16px 18px 18px' : '18px 22px 22px' }}>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 6 }}>
                      <span className="savane-num" style={{ fontSize: 10, color: SAVANE.laiton, letterSpacing: '0.18em' }}>
                        N° {p.n}
                      </span>
                      <span className="savane-num" style={{ fontSize: 12, color: SAVANE.laiton, fontWeight: 500 }}>
                        {p.price} <span style={{ fontSize: 9, opacity: 0.7 }}>XAF</span>
                      </span>
                    </div>
                    <h4 className="savane-display" style={{
                      margin: '0 0 6px', fontStyle: 'italic', fontWeight: 500,
                      fontSize: isPhone ? 22 : 24, color: SAVANE.paper, letterSpacing: '-0.01em',
                      lineHeight: 1.1,
                    }}>{p.name}</h4>
                    <p style={{
                      margin: 0, color: SAVANE.paperMute,
                      fontSize: 12, lineHeight: 1.5, fontFamily: SAVANE.sans,
                    }}>{p.desc}</p>
                  </div>
                </div>
              );
            })}
          </div>
        </section>

        {/* ═══════════════ ARTICLE 03 — LE CHAI ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="03" size={isPhone ? 140 : 220} bottom={-30} right={-20} opacity={0.05} />
          <div style={{ marginBottom: 28 }}>
            <Eyebrow style={{ marginBottom: 12 }}>P. 22 — ARTICLE 03 · CELLIER</Eyebrow>
            <h3 className="savane-display" style={{
              margin: 0, fontStyle: 'italic', fontWeight: 400,
              fontSize: isPhone ? 38 : isTablet ? 54 : 68,
              color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
            }}>Le chai.</h3>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : '1fr 1.1fr',
            gap: isPhone ? 30 : 56, alignItems: 'center',
          }}>
            <div>
              <p style={{
                margin: '0 0 26px', color: SAVANE.paper, fontSize: 15, lineHeight: 1.7,
                maxWidth: 520,
              }}>
                Bordeaux, Bourgogne, Vallée du Rhône, Loire — sélection française classique enrichie de découvertes
                Italie, Espagne et Liban. Cellier climatisé sur deux niveaux. Carte des spiritueux (Macallan 18,
                Hennessy Paradis) et cigares (Cohiba, Montecristo).
              </p>
              {/* Citation italic encadrée */}
              <div style={{
                position: 'relative', marginBottom: 28,
                padding: isPhone ? '22px 20px' : '26px 28px',
                background: SAVANE.inkPaper,
                border: `0.5px solid ${SAVANE.laiton}`,
                borderRadius: 2, maxWidth: 520,
                boxShadow: paperShadow,
              }}>
                <blockquote className="savane-display" style={{
                  margin: 0, fontStyle: 'italic',
                  fontSize: isPhone ? 20 : 26, lineHeight: 1.3,
                  color: SAVANE.paper, fontWeight: 400,
                }}>Deux cents références sélectionnées par notre sommelier.</blockquote>
                <div className="savane-tag" style={{ color: SAVANE.laiton, marginTop: 12 }}>
                  — HENRI MBADINGA · SOMMELIER
                </div>
              </div>
              {/* Sous-grille 4 vignobles */}
              <div style={{
                display: 'grid',
                gridTemplateColumns: isPhone ? 'repeat(2, 1fr)' : 'repeat(2, 1fr)',
                gap: 12, maxWidth: 520,
              }}>
                {VIGNOBLES.map((v, i) => (
                  <div key={v.house} className={flipClass} style={{
                    padding: '14px 16px',
                    background: SAVANE.inkSoft,
                    border: `0.5px solid ${SAVANE.inkLineMid}`,
                    borderRadius: 2,
                    boxShadow: '0 1px 0 rgba(244,232,208,0.04) inset, 0 12px 24px -12px rgba(0,0,0,0.55)',
                  }}>
                    <span className="savane-tag" style={{ color: SAVANE.laiton, display: 'block', marginBottom: 4 }}>
                      0{i + 1} · CUVÉE
                    </span>
                    <div className="savane-display" style={{
                      fontStyle: 'italic', fontSize: 18, color: SAVANE.paper,
                      letterSpacing: '-0.005em', lineHeight: 1.15,
                    }}>{v.house}</div>
                    <div style={{ fontSize: 11, color: SAVANE.paperMute, marginTop: 4 }}>{v.region}</div>
                    <div className="savane-num" style={{ fontSize: 10, color: SAVANE.laiton, marginTop: 6 }}>{v.refs}</div>
                  </div>
                ))}
              </div>
            </div>

            {/* Photo cellier — masquage organique */}
            <div style={{ position: 'relative', height: isPhone ? 320 : 540, boxShadow: paperShadow }}>
              <SVImg
                src={SAVANE_PHOTOS.vins1}
                alt="Cellier de La Savane"
                tone="cuir" clip="c"
                kicker="LE CHAI"
                label="Cellier sur deux niveaux."
                captionItalic
              />
            </div>
          </div>
        </section>

        {/* ═══════════════ ARTICLE 04 — LE CIGARE (lounge feutré) ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="04" size={isPhone ? 140 : 220} top={-50} right={-10} opacity={0.05} />
          <div style={{ marginBottom: 24 }}>
            <Eyebrow style={{ marginBottom: 12 }}>P. 28 — ARTICLE 04 · LOUNGE</Eyebrow>
            <h3 className="savane-display" style={{
              margin: 0, fontStyle: 'italic', fontWeight: 400,
              fontSize: isPhone ? 38 : isTablet ? 54 : 68,
              color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
            }}>Le cigare.</h3>
          </div>
          {/* Photo plein cadre + overlay sombre + texte glassmorphism */}
          <div style={{
            position: 'relative', borderRadius: 4, overflow: 'hidden',
            height: isPhone ? 400 : isTablet ? 480 : 560,
            boxShadow: paperShadow,
          }}>
            <SVImg src={SAVANE_PHOTOS.cigare} alt="Lounge cigares" tone="nuit" />
            {/* Overlay sombre profond */}
            <div style={{
              position: 'absolute', inset: 0,
              background: 'linear-gradient(180deg, rgba(15,15,15,0.10) 0%, rgba(15,15,15,0.40) 50%, rgba(15,15,15,0.85) 100%)',
              pointerEvents: 'none',
            }} />
            {/* Glassmorphism flou en bas */}
            <div style={{
              position: 'absolute', left: 0, right: 0, bottom: 0,
              padding: isPhone ? '28px 24px 32px' : '40px 56px 48px',
              background: 'rgba(15,15,15,0.42)',
              backdropFilter: 'blur(14px)',
              WebkitBackdropFilter: 'blur(14px)',
              borderTop: `0.5px solid ${SAVANE.laiton}`,
            }}>
              <span className="savane-tag" style={{ color: SAVANE.laiton, display: 'block', marginBottom: 14 }}>
                LOUNGE PRIVÉ · 15 COUVERTS
              </span>
              <p className="savane-display" style={{
                margin: 0, fontStyle: 'italic', fontWeight: 400,
                fontSize: isPhone ? 26 : isTablet ? 36 : 44,
                color: SAVANE.paper, letterSpacing: '-0.015em', lineHeight: 1.15,
                maxWidth: 720,
              }}>Un fauteuil. Un Cohiba. Le silence.</p>
              <p style={{
                margin: '14px 0 0', color: SAVANE.paperMute,
                fontSize: isPhone ? 13 : 14, lineHeight: 1.55, maxWidth: 620,
              }}>
                Cohiba Robusto · Montecristo No.2 · Davidoff Aniversario · Romeo y Julieta. Cave humidifiée à 70%,
                ventilation dédiée, alcôves cuir. Service de fin de soirée jusqu'à 02h.
              </p>
            </div>
          </div>
        </section>

        {/* ═══════════════ ARTICLE 05 — L'EXPÉRIENCE BIG FIVE ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="05" size={isPhone ? 140 : 220} top={-50} left={-10} opacity={0.05} />
          <div style={{ marginBottom: 28 }}>
            <Eyebrow style={{ marginBottom: 12 }}>P. 34 — ARTICLE 05 · SAVANE</Eyebrow>
            <h3 className="savane-display" style={{
              margin: 0, fontStyle: 'italic', fontWeight: 400,
              fontSize: isPhone ? 38 : isTablet ? 54 : 68,
              color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
              maxWidth: 880,
            }}>L'expérience Big Five.</h3>
            <p className="savane-display" style={{
              margin: '14px 0 0', fontStyle: 'italic',
              fontSize: isPhone ? 18 : 32, color: SAVANE.paperMute, lineHeight: 1.3,
              maxWidth: 760,
            }}>Cinq tables. Cinq voyages. Une savane.</p>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : isTablet ? 'repeat(3, 1fr)' : 'repeat(3, 1fr)',
            gap: isPhone ? 22 : 28,
          }}>
            {BIG5.map((b, i) => (
              <div key={b.salle} className="savane-reveal" style={{
                position: 'relative', transitionDelay: `${i * 100}ms`,
              }}>
                <div className={flipClass} style={{
                  height: isPhone ? 280 : 360,
                  boxShadow: paperShadow,
                  marginBottom: 16,
                }}>
                  <SVImg
                    src={b.photo}
                    alt={b.salle}
                    tone="sage" clip={b.clip}
                    kicker={`SALLE 0${i + 1}`}
                  />
                </div>
                <div style={{ paddingLeft: 4 }}>
                  <h4 className="savane-display" style={{
                    margin: 0, fontStyle: 'italic', fontWeight: 500,
                    fontSize: isPhone ? 24 : 28, color: SAVANE.paper, letterSpacing: '-0.01em',
                  }}>{b.salle}</h4>
                  <div className="savane-num" style={{
                    fontSize: 11, color: SAVANE.laiton, marginTop: 6,
                    letterSpacing: '0.18em',
                  }}>{b.cap.toUpperCase()}</div>
                  <Hairline w={42} style={{ marginTop: 12 }} />
                </div>
              </div>
            ))}
          </div>
        </section>

        {/* ═══════════════ ARTICLE 06 — PRIVATISATIONS ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 64 : 104, position: 'relative' }}>
          <Numero n="06" size={isPhone ? 140 : 220} top={-50} right={-10} opacity={0.05} />
          <div style={{ marginBottom: 28 }}>
            <Eyebrow style={{ marginBottom: 12 }}>P. 40 — ARTICLE 06 · ESPACES</Eyebrow>
            <h3 className="savane-display" style={{
              margin: 0, fontStyle: 'italic', fontWeight: 400,
              fontSize: isPhone ? 38 : isTablet ? 54 : 68,
              color: SAVANE.paper, letterSpacing: '-0.025em', lineHeight: 1.0,
            }}>Privatisations.</h3>
            <p className="savane-display" style={{
              margin: '14px 0 0', fontStyle: 'italic',
              fontSize: isPhone ? 16 : 22, color: SAVANE.paperMute, lineHeight: 1.4,
              maxWidth: 580,
            }}>Trois espaces, trois ambiances.</p>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : isTablet ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)',
            gap: isPhone ? 22 : 28,
          }}>
            {ESPACES.map((s, i) => (
              <div key={s.name} className={`savane-paper ${flipClass} savane-reveal`} style={{
                padding: 0, borderRadius: 4, position: 'relative', overflow: 'hidden',
                transitionDelay: `${i * 90}ms`,
              }}>
                <div style={{ height: isPhone ? 200 : 230, position: 'relative' }}>
                  <SVImg src={s.photo} alt={s.name} tone="cuir" />
                </div>
                <div style={{ padding: isPhone ? '20px 22px 22px' : '24px 26px 26px' }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
                    <Eyebrow style={{ color: SAVANE.laiton }}>0{i + 1} · {s.cap.toUpperCase()}</Eyebrow>
                    <span className="savane-num" style={{ fontSize: 10, color: SAVANE.paperFaint }}>{s.kicker}</span>
                  </div>
                  <h4 className="savane-display" style={{
                    margin: 0, fontStyle: 'italic', fontWeight: 500,
                    fontSize: isPhone ? 26 : 30, color: SAVANE.paper, letterSpacing: '-0.01em',
                  }}>Salle {s.name}</h4>
                  <p style={{ margin: '10px 0 0', color: SAVANE.paperMute, fontSize: 13, lineHeight: 1.55 }}>{s.desc}</p>
                  <Hairline w={50} style={{ marginTop: 18 }} />
                </div>
              </div>
            ))}
          </div>
        </section>

        {/* ═══════════════ ROTARY · INSERT ═══════════════ */}
        <section className="savane-reveal" style={{ marginBottom: isPhone ? 56 : 88, position: 'relative' }}>
          <div className="savane-paper-dark" style={{
            padding: isPhone ? '26px 22px' : '36px 40px', borderRadius: 4,
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : '120px 1fr auto',
            gap: 24, alignItems: 'center',
            border: `0.5px solid ${SAVANE.laiton}`,
          }}>
            <div style={{
              width: 100, height: 100, borderRadius: '50%',
              background: `radial-gradient(circle, ${SAVANE.laiton}, ${SAVANE.cuir})`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: SAVANE.display, fontStyle: 'italic', fontWeight: 500,
              fontSize: 38, color: SAVANE.ink, border: `0.5px solid ${SAVANE.laiton}`,
              boxShadow: '0 12px 24px -8px rgba(0,0,0,0.55)',
            }}>R</div>
            <div>
              <Eyebrow style={{ color: SAVANE.laiton, marginBottom: 8 }}>ADHÉRENT · DEPUIS 2018</Eyebrow>
              <h4 className="savane-display" style={{
                margin: 0, fontStyle: 'italic', fontWeight: 400,
                fontSize: isPhone ? 24 : 32, color: SAVANE.paper, letterSpacing: '-0.01em',
              }}>Rotary Club Libreville · District 7100</h4>
              <p style={{ margin: '8px 0 0', color: SAVANE.paperMute, fontSize: 13, lineHeight: 1.55 }}>
                Lieu de réunion mensuel · soirées de gala · soutien aux actions locales.
              </p>
            </div>
            <CTA tone="laiton" ghost size="sm">En savoir plus</CTA>
          </div>
        </section>

        {/* ═══════════════ FOOTER ÉDITORIAL — « OURS » ═══════════════ */}
        <section style={{
          paddingTop: 28, marginTop: 12,
          borderTop: `0.5px solid ${SAVANE.laiton}`,
        }}>
          <div style={{
            display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
            marginBottom: 22, flexWrap: 'wrap', gap: 10,
          }}>
            <span style={{
              fontFamily: SAVANE.mono, fontSize: 11,
              letterSpacing: '0.32em', textTransform: 'uppercase',
              color: SAVANE.paper, fontWeight: 500,
            }}>L'OURS</span>
            <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>
              ÉDITION MENSUELLE · MAGAZINE DÉPOSÉ
            </span>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isPhone ? '1fr' : 'repeat(4, 1fr)',
            gap: 20, marginBottom: 20,
          }}>
            <div>
              <Eyebrow style={{ marginBottom: 8 }}>ADRESSE</Eyebrow>
              <p style={{ margin: 0, color: SAVANE.paper, fontSize: 13, lineHeight: 1.55 }}>
                14, Boulevard du Bord de Mer<br/>Avenue Sergent Gabriel<br/>Libreville · GA
              </p>
            </div>
            <div>
              <Eyebrow style={{ marginBottom: 8 }}>RÉSERVATIONS</Eyebrow>
              <p style={{ margin: 0, color: SAVANE.paper, fontSize: 13, lineHeight: 1.55 }}>
                +241 11 73 84 92<br/>reservation@lasavane.ga
              </p>
            </div>
            <div>
              <Eyebrow style={{ marginBottom: 8 }}>SOCIAL</Eyebrow>
              <p style={{ margin: 0, color: SAVANE.paper, fontSize: 13, lineHeight: 1.55 }}>
                @lasavane_gabon<br/>#lasavanegabon
              </p>
            </div>
            <div>
              <Eyebrow style={{ marginBottom: 8 }}>CLUB</Eyebrow>
              <p style={{ margin: 0, color: SAVANE.paper, fontSize: 13, lineHeight: 1.55 }}>
                ROTARY 7100<br/>Adhérent depuis 2018
              </p>
            </div>
          </div>
          <p style={{
            margin: 0, paddingTop: 14,
            borderTop: `0.5px solid ${SAVANE.inkLine}`,
            color: SAVANE.paperFaint,
            fontFamily: SAVANE.mono, fontSize: 10,
            letterSpacing: '0.18em', textTransform: 'uppercase', lineHeight: 1.7,
          }}>
            Édition mensuelle · La Savane · Libreville Gabon · Avenue Sergent Gabriel · ROTARY 7100 · @lasavane_gabon · Magazine déposé · Imprimé localement
          </p>
        </section>
      </div>
    </div>
  );
}

// ─── 3. MENU QR — « LA CARTE » ─────────────────────────────────────────
function SavaneMenu({ goto, vp }) {
  const phone = vp.w < 600;
  const [cat, setCat] = React.useState('Plats du chef');
  const [cart, setCart] = React.useState([]);
  const [table, setTable] = React.useState('Salon Acacia');

  React.useEffect(() => {
    if (typeof window !== 'undefined') {
      const params = new URLSearchParams(window.location.search);
      const t = params.get('t');
      if (t) setTable(t);
    }
  }, []);

  const filtered = SAVANE_MENU.filter(p => p.cat === cat);
  const total = cart.reduce((sum, c) => sum + c.price * c.qty, 0);

  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 140px' : '32px 56px 140px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow={`P. 02 · LA CARTE · TABLE ${table.toUpperCase()}`}
          title="La Carte"
          dek="Service du soir · 19h-22h · cuisine signée Gaëlle Akagah · sélection cellier 200 références."
          page="14" />

        {/* Tabs catégories */}
        <div className="savane-scroll" style={{
          display: 'flex', gap: phone ? 14 : 28, overflowX: 'auto', marginBottom: phone ? 28 : 36,
          paddingBottom: 14, borderBottom: `0.5px solid ${SAVANE.inkLineMid}`,
        }}>
          {SAVANE_MENU_CATS.map(c => {
            const active = c === cat;
            return (
              <button key={c} onClick={() => setCat(c)} className="savane-cta" style={{
                background: 'transparent', border: 'none',
                color: active ? SAVANE.laiton : SAVANE.paperFaint,
                fontFamily: SAVANE.mono, fontSize: phone ? 10 : 11,
                letterSpacing: '0.22em', textTransform: 'uppercase',
                padding: '6px 0', cursor: 'pointer', whiteSpace: 'nowrap',
                borderBottom: active ? `1px solid ${SAVANE.laiton}` : '1px solid transparent',
              }}>{c}</button>
            );
          })}
        </div>

        {/* Liste plats — disposition magazine */}
        <div style={{
          display: 'grid', gridTemplateColumns: phone ? '1fr' : 'repeat(2, 1fr)',
          gap: phone ? 22 : 36,
        }}>
          {filtered.map((p, i) => {
            const inCart = cart.find(c => c.name === p.name);
            const altLayout = i % 2 === 1;
            return (
              <article key={p.name} className="savane-paper" style={{
                position: 'relative', overflow: 'hidden', borderRadius: 4,
                padding: phone ? 18 : 22,
                display: 'grid',
                gridTemplateColumns: altLayout ? '1fr 110px' : '110px 1fr',
                gap: 18, alignItems: 'flex-start',
              }}>
                {!altLayout && (
                  <Photo tone={p.tone} h={120} w={110} style={{ borderRadius: 2 }} />
                )}
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
                    <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>N° {String(i + 1).padStart(2, '0')}</span>
                    {p.allerg.length > 0 && (
                      <span className="savane-tag" style={{ color: SAVANE.cuirHi }}>
                        {p.allerg.join(' · ').toUpperCase()}
                      </span>
                    )}
                  </div>
                  <h4 className="savane-display" style={{
                    margin: 0, fontStyle: 'italic', fontWeight: 500,
                    fontSize: phone ? 20 : 22, color: SAVANE.paper,
                    letterSpacing: '-0.01em', lineHeight: 1.15,
                  }}>{p.name}</h4>
                  <p style={{
                    margin: '8px 0 12px', color: SAVANE.paperMute,
                    fontSize: 12.5, lineHeight: 1.5,
                  }}>{p.desc}</p>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
                    <Price value={p.price} size={14} />
                    <button onClick={() => {
                      setCart(prev => {
                        const ex = prev.find(c => c.name === p.name);
                        if (ex) return prev.map(c => c.name === p.name ? { ...c, qty: c.qty + 1 } : c);
                        return [...prev, { ...p, qty: 1 }];
                      });
                    }} className="savane-cta" style={{
                      background: inCart ? SAVANE.laiton : 'transparent',
                      color: inCart ? SAVANE.ink : SAVANE.laiton,
                      border: `0.5px solid ${SAVANE.laiton}`,
                      fontFamily: SAVANE.mono, fontSize: 9.5,
                      letterSpacing: '0.22em', textTransform: 'uppercase',
                      padding: '6px 12px', cursor: 'pointer', borderRadius: 1,
                    }}>{inCart ? `${inCart.qty} ✓` : '+ ajouter'}</button>
                  </div>
                </div>
                {altLayout && (
                  <Photo tone={p.tone} h={120} w={110} style={{ borderRadius: 2 }} />
                )}
              </article>
            );
          })}
        </div>
      </div>

      {/* Panier sticky */}
      {cart.length > 0 && (
        <div style={{
          position: 'fixed', bottom: 0, left: 0, right: 0,
          background: 'rgba(15,15,15,0.92)',
          backdropFilter: 'blur(14px)',
          WebkitBackdropFilter: 'blur(14px)',
          borderTop: `0.5px solid ${SAVANE.laiton}`,
          padding: phone ? '14px 18px' : '18px 56px',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          gap: 18, zIndex: 50,
          boxShadow: '0 -24px 48px -16px rgba(0,0,0,0.7)',
        }}>
          <div>
            <Eyebrow>{cart.length} ARTICLE{cart.length > 1 ? 'S' : ''} · TABLE {table.toUpperCase()}</Eyebrow>
            <div className="savane-display" style={{
              fontStyle: 'italic', fontSize: phone ? 22 : 28,
              color: SAVANE.laiton, marginTop: 4, fontWeight: 500,
            }}>{total.toLocaleString('fr-FR')} <span style={{ fontSize: 13, opacity: 0.7 }}>FCFA</span></div>
          </div>
          <CTA tone="laiton" size="lg">Commander →</CTA>
        </div>
      )}
    </div>
  );
}

// ─── 4. CELLIER — « LE CHAI » ──────────────────────────────────────────
function SavaneCellier({ goto, vp }) {
  const phone = vp.w < 600;
  const [filt, setFilt] = React.useState('Tous');
  const cats = ['Tous', 'Vins rouges', 'Champagnes', 'Spiritueux', 'Cigares'];
  const filtered = filt === 'Tous' ? SAVANE_CELLIER : SAVANE_CELLIER.filter(c => c.cat === filt);

  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 60px' : '32px 56px 80px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="P. 03 · LE CHAI"
          title="Le Chai"
          dek="Deux cents références. Sommelier dédié, Henri Mbadinga. Cellier climatisé sur deux niveaux."
          page="28" />

        {/* KPIs flottants */}
        <div style={{
          display: 'grid',
          gridTemplateColumns: phone ? 'repeat(2, 1fr)' : 'repeat(4, 1fr)',
          gap: phone ? 14 : 22, marginBottom: phone ? 30 : 44,
        }}>
          <KPI label="VALEUR CELLIER" value="8,4M" sub="FCFA · valorisé" />
          <KPI label="ALERTES SEUIL" value="12" sub="à recommander" tone="cuir" />
          <KPI label="RÉFÉRENCES PREMIUM" value="23" sub="Petrus, Margaux, etc." tone="sage" />
          <KPI label="DERNIER INVENTAIRE" value="J-5" sub="Sébastien Ondo" />
        </div>

        {/* Filtres */}
        <div className="savane-scroll" style={{
          display: 'flex', gap: 24, overflowX: 'auto', marginBottom: 24,
          paddingBottom: 14, borderBottom: `0.5px solid ${SAVANE.inkLineMid}`,
        }}>
          {cats.map(c => {
            const active = c === filt;
            const count = c === 'Tous' ? SAVANE_CELLIER.length : SAVANE_CELLIER.filter(x => x.cat === c).length;
            return (
              <button key={c} onClick={() => setFilt(c)} className="savane-cta" style={{
                background: 'transparent', border: 'none',
                color: active ? SAVANE.laiton : SAVANE.paperFaint,
                fontFamily: SAVANE.mono, fontSize: 10,
                letterSpacing: '0.22em', textTransform: 'uppercase',
                padding: '6px 0', cursor: 'pointer', whiteSpace: 'nowrap',
                borderBottom: active ? `1px solid ${SAVANE.laiton}` : '1px solid transparent',
              }}>{c} · {count}</button>
            );
          })}
        </div>

        {/* Tableau cellier */}
        <div className="savane-paper" style={{
          borderRadius: 4, overflow: 'hidden', marginBottom: 30,
        }}>
          <div style={{
            display: phone ? 'none' : 'grid',
            gridTemplateColumns: '2.4fr 1fr 0.6fr 1.2fr 1.2fr 0.8fr',
            gap: 16, padding: '14px 22px',
            borderBottom: `0.5px solid ${SAVANE.inkLineMid}`,
            background: SAVANE.inkPaper,
          }}>
            {['Référence', 'Catégorie', 'Stock', 'PU', 'Valorisation', 'Statut'].map(h => (
              <Eyebrow key={h}>{h}</Eyebrow>
            ))}
          </div>
          {filtered.map((r, i) => (
            <div key={r.name} style={{
              display: 'grid',
              gridTemplateColumns: phone ? '1fr' : '2.4fr 1fr 0.6fr 1.2fr 1.2fr 0.8fr',
              gap: phone ? 8 : 16, padding: phone ? '16px 18px' : '16px 22px',
              borderBottom: i < filtered.length - 1 ? `0.5px solid ${SAVANE.inkLine}` : 'none',
              alignItems: 'center',
            }}>
              <div className="savane-display" style={{ fontStyle: 'italic', fontSize: phone ? 18 : 18, color: SAVANE.paper, fontWeight: 500 }}>{r.name}</div>
              <div style={{ color: SAVANE.paperMute, fontSize: 12 }}>{r.cat}</div>
              <div className="savane-num" style={{ color: SAVANE.paper, fontSize: 13 }}>{r.qty}</div>
              <div className="savane-num" style={{ color: SAVANE.paperMute, fontSize: 12 }}>{r.pu.toLocaleString('fr-FR')}</div>
              <div><Price value={r.qty * r.pu} size={13} /></div>
              <div><Badge tone={r.badge === 'OK' ? 'sage' : 'alert'}>{r.badge}</Badge></div>
            </div>
          ))}
        </div>

        <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
          <CTA tone="laiton">Démarrer inventaire</CTA>
          <CTA tone="cuir" ghost>Demander commande sommelier</CTA>
        </div>
      </div>
    </div>
  );
}

// ─── 5. CAISSE ─────────────────────────────────────────────────────────
function SavaneCaisse({ goto, vp }) {
  const phone = vp.w < 600;
  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 60px' : '32px 56px 80px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="P. 04 · LA CAISSE"
          title="Service du soir"
          dek="Encaissement Mobile Money · ticket numérique · clôture de service en fin de soirée."
          page="35" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: phone ? 28 : 40 }}>
          <SavaneAvatar name="Gaëlle Akagah" size={56} ring />
          <div>
            <Eyebrow>EN POSTE</Eyebrow>
            <div className="savane-display" style={{ fontStyle: 'italic', fontSize: phone ? 24 : 30, color: SAVANE.paper, marginTop: 4 }}>Gaëlle Akagah</div>
          </div>
        </div>

        <div style={{
          display: 'grid',
          gridTemplateColumns: phone ? 'repeat(2, 1fr)' : 'repeat(4, 1fr)',
          gap: phone ? 14 : 22, marginBottom: phone ? 30 : 40,
        }}>
          <KPI label="RECETTE SERVICE" value="720k" sub="FCFA — au 21h00" />
          <KPI label="MOBILE MONEY" value="65%" sub="MoMo / Carte / Cash" tone="sage" />
          <KPI label="ANOMALIES" value="0" sub="aucune sur le service" tone="sage" />
          <KPI label="SERVICE MOYEN" value="8 min" sub="entrée / plat" />
        </div>

        <Eyebrow style={{ marginBottom: 14 }}>P. 36 · 6 TABLES OUVERTES</Eyebrow>
        <div style={{
          display: 'grid', gridTemplateColumns: phone ? '1fr' : 'repeat(2, 1fr)', gap: phone ? 14 : 18,
          marginBottom: 30,
        }}>
          {SAVANE_TABLES.map((t, i) => (
            <div key={t.id} className="savane-paper" style={{
              padding: phone ? '18px 20px' : '22px 24px', borderRadius: 4,
              display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16,
              position: 'relative', overflow: 'hidden',
            }}>
              <Numero n={String(i + 1).padStart(2, '0')} size={120} bottom={-30} right={-10} opacity={0.05} />
              <div style={{ position: 'relative' }}>
                <Eyebrow>{t.salle.toUpperCase()} · {t.id}</Eyebrow>
                <div className="savane-display" style={{ fontStyle: 'italic', fontSize: 22, color: SAVANE.paper, marginTop: 4 }}>{t.cvts} couverts</div>
                <div style={{ color: SAVANE.paperMute, fontSize: 11, marginTop: 4, fontFamily: SAVANE.mono, letterSpacing: '0.08em' }}>OUVERTE {t.open} · {t.timer}</div>
              </div>
              <div style={{ textAlign: 'right', position: 'relative' }}>
                <Price value={t.total} size={15} />
              </div>
            </div>
          ))}
        </div>

        <CTA tone="laiton" size="lg">Clôturer service</CTA>
      </div>
    </div>
  );
}

// ─── 6. PERSONNEL — « LA MAISON » ─────────────────────────────────────
function SavanePersonnel({ goto, vp }) {
  const phone = vp.w < 600;
  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 60px' : '32px 56px 80px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="P. 05 · PORTRAITS"
          title="La Maison"
          dek="Le chef · le sommelier · la salle. Cinq personnes pour 120 couverts par service."
          page="42" />

        <div style={{
          display: 'grid', gridTemplateColumns: phone ? '1fr' : 'repeat(2, 1fr)', gap: phone ? 22 : 36,
        }}>
          {SAVANE_PERSONNEL.map((p, i) => {
            const isChef = p.role.includes('Chef');
            const wide = isChef && !phone;
            return (
              <div key={p.name} className={isChef ? 'savane-paper-dark' : 'savane-paper'} style={{
                gridColumn: wide ? 'span 2' : 'auto',
                padding: phone ? '24px 22px' : '32px 32px', borderRadius: 4,
                position: 'relative', overflow: 'hidden',
                border: isChef ? `0.5px solid ${SAVANE.laiton}` : `0.5px solid ${SAVANE.inkLine}`,
                display: 'grid',
                gridTemplateColumns: phone ? '80px 1fr' : '120px 1fr auto',
                gap: phone ? 18 : 28, alignItems: 'center',
              }}>
                <Numero n={String(i + 1).padStart(2, '0')} size={140} bottom={-30} right={-10} opacity={0.06} />
                <SavaneAvatar name={p.name} size={phone ? 76 : 110} ring={isChef} />
                <div style={{ position: 'relative' }}>
                  {isChef && <Tag tone="laiton">CHEF EXÉCUTIF · MAISON MÈRE</Tag>}
                  <h3 className="savane-display" style={{
                    margin: '8px 0 4px', fontStyle: 'italic', fontWeight: 400,
                    fontSize: phone ? 26 : isChef ? 44 : 30, color: SAVANE.paper,
                    letterSpacing: '-0.02em', lineHeight: 1,
                  }}>{p.name}</h3>
                  <div style={{ color: SAVANE.paperMute, fontSize: 13, marginTop: 6 }}>{p.role}</div>
                  {isChef && (
                    <p style={{
                      margin: '14px 0 0', color: SAVANE.paper, fontSize: 13.5,
                      lineHeight: 1.6, maxWidth: 480, fontStyle: 'italic',
                      fontFamily: SAVANE.display, fontVariationSettings: '"opsz" 144',
                    }}>«&nbsp;Je cuisine la forêt comme un parfumeur — par touches, jamais en force.&nbsp;»</p>
                  )}
                </div>
                <div style={{ position: 'relative', display: 'flex', flexDirection: 'column', gap: 8, alignItems: phone ? 'flex-start' : 'flex-end' }}>
                  <Badge tone={p.presence === 'Service' ? 'sage' : 'cuir'}>{p.presence.toUpperCase()}</Badge>
                  <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>ANC. {p.anc.toUpperCase()}</span>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ─── 7. GÉRANT — « LE PAVILLON » ──────────────────────────────────────
function SavaneGerant({ goto, vp }) {
  const phone = vp.w < 600;
  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 60px' : '32px 56px 80px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="P. 06 · GESTION"
          title="Le Pavillon"
          dek="Salle · couverts · ratios · clôture de service. Vue gérante."
          page="49" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: phone ? 30 : 44 }}>
          <SavaneAvatar name="Gaëlle Akagah" size={64} ring />
          <div>
            <Eyebrow>BONSOIR</Eyebrow>
            <div className="savane-display" style={{ fontStyle: 'italic', fontSize: phone ? 28 : 40, color: SAVANE.paper, marginTop: 4 }}>Gaëlle.</div>
          </div>
        </div>

        <div style={{
          display: 'grid',
          gridTemplateColumns: phone ? 'repeat(2, 1fr)' : 'repeat(4, 1fr)',
          gap: phone ? 14 : 22, marginBottom: phone ? 30 : 44,
        }}>
          <KPI label="COUVERTS" value="64/120" sub="53% · service avancé" />
          <KPI label="TICKET MOYEN" value="11 250" sub="FCFA · -3% vs 30j" />
          <KPI label="MOBILE MONEY" value="65%" sub="Airtel + MTN" tone="sage" />
          <KPI label="ANOMALIES" value="0" sub="service propre" tone="sage" />
        </div>

        <Eyebrow style={{ marginBottom: 14 }}>P. 50 · TABLES EN COURS</Eyebrow>
        <div className="savane-paper" style={{ borderRadius: 4, overflow: 'hidden', marginBottom: 30 }}>
          {SAVANE_TABLES.map((t, i) => (
            <div key={t.id} style={{
              padding: phone ? '14px 18px' : '16px 22px',
              borderBottom: i < SAVANE_TABLES.length - 1 ? `0.5px solid ${SAVANE.inkLine}` : 'none',
              display: 'grid',
              gridTemplateColumns: phone ? '1fr auto' : '50px 1fr 1fr 1fr auto',
              gap: 14, alignItems: 'center',
            }}>
              <span className="savane-display" style={{ fontStyle: 'italic', fontSize: 22, color: SAVANE.laiton, fontWeight: 500 }}>{t.id}</span>
              <div style={{ color: SAVANE.paper, fontSize: 13 }}>{t.salle}</div>
              {!phone && <div style={{ color: SAVANE.paperMute, fontSize: 12, fontFamily: SAVANE.mono }}>{t.cvts} cvts · {t.timer}</div>}
              {!phone && <div style={{ color: SAVANE.paperMute, fontSize: 12, fontFamily: SAVANE.mono }}>OUV. {t.open}</div>}
              <Price value={t.total} size={13} />
            </div>
          ))}
        </div>

        <CTA tone="laiton" size="lg">Clôturer service</CTA>
      </div>
    </div>
  );
}

// ─── 8. PROPRIÉTAIRE — « LA DIRECTION » ───────────────────────────────
function SavaneProprio({ goto, vp }) {
  const phone = vp.w < 600;
  const months = [
    { m: 'OCT', ca: 14.2, marge: 56 },
    { m: 'NOV', ca: 15.8, marge: 60 },
    { m: 'DÉC', ca: 21.4, marge: 68 },
    { m: 'JAN', ca: 16.2, marge: 62 },
    { m: 'FÉV', ca: 17.0, marge: 63 },
    { m: 'MAR', ca: 18.6, marge: 64 },
  ];
  const max = 22;
  const barW = phone ? 32 : 56;
  const barGap = phone ? 12 : 24;
  const chartH = phone ? 180 : 240;

  return (
    <div className="savane-fade" style={{
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      padding: phone ? '20px 16px 60px' : '32px 56px 80px',
      position: 'relative', overflow: 'hidden', minHeight: '100vh',
    }}>
      <div className="savane-mesh" style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.5 }} />
      <div style={{ position: 'relative' }}>
        <ViewHeader goto={goto} vp={vp}
          eyebrow="P. 07 · TABLEAU DE BORD"
          title="La Direction"
          dek="Vue mensuelle de Mars 2026. CA, marge, trésorerie. Rapport WhatsApp envoyé le 1er du mois."
          page="56" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: phone ? 30 : 44 }}>
          <SavaneAvatar name="Gaëlle Akagah" size={64} ring />
          <div>
            <Eyebrow>VUE MENSUELLE</Eyebrow>
            <div className="savane-display" style={{ fontStyle: 'italic', fontSize: phone ? 28 : 40, color: SAVANE.paper, marginTop: 4 }}>Mars 2026.</div>
          </div>
        </div>

        {/* 3 KPIs gros chiffres optical sizing */}
        <div style={{
          display: 'grid',
          gridTemplateColumns: phone ? '1fr' : 'repeat(3, 1fr)',
          gap: phone ? 14 : 22, marginBottom: phone ? 36 : 56,
        }}>
          <KPI label="CHIFFRE D'AFFAIRES" value="18,6M" sub="FCFA · +9% vs Fév" big tone="laiton" />
          <KPI label="MARGE BRUTE" value="64%" sub="cible 60% — atteinte" big tone="sage" />
          <KPI label="RÉSULTAT NET" value="4,2M" sub="FCFA · après charges" big tone="cuir" />
        </div>

        {/* Barres 6 mois */}
        <div className="savane-paper" style={{ padding: phone ? '20px 18px' : '32px 36px', borderRadius: 4, marginBottom: 30, position: 'relative', overflow: 'hidden' }}>
          <Numero n="06M" size={180} bottom={-40} right={-20} opacity={0.05} />
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 22, position: 'relative' }}>
            <Eyebrow>CA & MARGE · 6 MOIS</Eyebrow>
            <span className="savane-tag" style={{ color: SAVANE.paperFaint }}>EN MILLIONS FCFA</span>
          </div>
          <svg width="100%" height={chartH + 34} style={{ overflow: 'visible', position: 'relative' }}>
            {months.map((m, i) => {
              const x = i * (barW + barGap) + 8;
              const h = (m.ca / max) * chartH;
              const margeH = (m.marge / 100) * chartH;
              return (
                <g key={m.m}>
                  <rect x={x} y={chartH - h} width={barW} height={h} fill={SAVANE.laiton} opacity="0.85" />
                  <rect x={x + barW * 0.55} y={chartH - margeH} width={barW * 0.4} height={margeH} fill={SAVANE.sage} opacity="0.85" />
                  <text x={x + barW / 2} y={chartH + 18} textAnchor="middle" fontFamily={SAVANE.mono} fontSize="9" letterSpacing="0.22em" fill={SAVANE.paperFaint}>{m.m}</text>
                  <text x={x + barW / 2} y={chartH - h - 6} textAnchor="middle" fontFamily={SAVANE.mono} fontSize="10" fill={SAVANE.laiton}>{m.ca}</text>
                </g>
              );
            })}
          </svg>
          <div style={{ marginTop: 14, display: 'flex', gap: 18 }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
              <span style={{ width: 12, height: 12, background: SAVANE.laiton }} />
              <span className="savane-tag" style={{ color: SAVANE.paperMute }}>CA</span>
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
              <span style={{ width: 12, height: 12, background: SAVANE.sage }} />
              <span className="savane-tag" style={{ color: SAVANE.paperMute }}>MARGE %</span>
            </span>
          </div>
        </div>

        {/* Soldes banques */}
        <div style={{
          display: 'grid', gridTemplateColumns: phone ? '1fr' : '1fr 1fr', gap: phone ? 14 : 22, marginBottom: 30,
        }}>
          <div className="savane-paper" style={{ padding: '22px 26px', borderRadius: 4, position: 'relative', overflow: 'hidden' }}>
            <Numero n="01" size={120} bottom={-30} right={-10} opacity={0.06} />
            <Eyebrow style={{ marginBottom: 8 }}>BGFI GABON · COMPTE COURANT</Eyebrow>
            <div className="savane-display" style={{ fontStyle: 'italic', fontSize: 36, color: SAVANE.paper, fontWeight: 500 }}>4,8M <span style={{ fontSize: 14, color: SAVANE.paperMute }}>FCFA</span></div>
          </div>
          <div className="savane-paper" style={{ padding: '22px 26px', borderRadius: 4, position: 'relative', overflow: 'hidden' }}>
            <Numero n="02" size={120} bottom={-30} right={-10} opacity={0.06} />
            <Eyebrow style={{ marginBottom: 8 }}>BANQUE POPULAIRE GABON · ÉPARGNE</Eyebrow>
            <div className="savane-display" style={{ fontStyle: 'italic', fontSize: 36, color: SAVANE.paper, fontWeight: 500 }}>1,2M <span style={{ fontSize: 14, color: SAVANE.paperMute }}>FCFA</span></div>
          </div>
        </div>

        <CTA tone="wa" size="lg">Rapport WhatsApp · 1er du mois</CTA>
      </div>
    </div>
  );
}

// ─── SHELL ──────────────────────────────────────────────────────────────
function SavaneShell() {
  const [vp, setVp] = React.useState({
    w: typeof window !== 'undefined' ? window.innerWidth : 1280,
    h: typeof window !== 'undefined' ? window.innerHeight : 800,
  });
  const [view, setView] = React.useState('hub');

  React.useEffect(() => {
    const onR = () => setVp({ w: window.innerWidth, h: window.innerHeight });
    window.addEventListener('resize', onR);
    return () => window.removeEventListener('resize', onR);
  }, []);

  const goto = (v) => setView(v);

  let content;
  switch (view) {
    case 'site':         content = <SavaneSite        goto={goto} vp={vp} />; break;
    case 'menu':         content = <SavaneMenu        goto={goto} vp={vp} />; break;
    case 'cellier':      content = <SavaneCellier     goto={goto} vp={vp} />; break;
    case 'caisse':       content = <SavaneCaisse      goto={goto} vp={vp} />; break;
    case 'personnel':    content = <SavanePersonnel   goto={goto} vp={vp} />; break;
    case 'gerant':       content = <SavaneGerant      goto={goto} vp={vp} />; break;
    case 'proprietaire': content = <SavaneProprio     goto={goto} vp={vp} />; break;
    case 'hub':
    default:             content = <SavaneHub         goto={goto} vp={vp} />; break;
  }

  return (
    <div style={{
      width: '100%', height: '100%', minHeight: '100vh',
      background: SAVANE.ink, color: SAVANE.paper, fontFamily: SAVANE.sans,
      overflowY: 'auto', overflowX: 'hidden',
    }}>
      {content}
    </div>
  );
}

window.SavaneShell = SavaneShell;
