// Guestmaker · Data Layer capabilities grid · 3×4 cards beneath the contact
// showcase. Each card has a small fuchsia line-art illustration up top, an
// eyebrow + title, a body, bullets, and a serif italic tag line.
// All linework is hand-coded SVG in the hero's fuchsia palette.

const { useState: dUseState, useEffect: dUseEffect } = React;

// ────────────────────────── tokens (match hero) ──────────────────────────
const D_FUCHSIA      = "#ED4D86";
const D_FUCHSIA_DEEP = "#C93A6B";
const D_FUCHSIA_DARK = "#8E2148";
const D_FUCHSIA_SOFT = "#F8B7CF";
const D_FUCHSIA_PALE = "#FCE4EE";
const D_FUCHSIA_WASH = "#FDF0F5";
const D_PLUM         = "#211A22";
const D_INK     = "#1a1a1a";
const D_DIM     = "#6e6363";
const D_FAINT   = "#a99fa3";
const D_RULE    = "#e9e3d4";
const D_RULE_S  = "#f0ebde";
const D_PAPER   = "#fbfaf6";
const D_CREAM   = "#f6f4ef";
const D_MONO    = "'JetBrains Mono', ui-monospace, monospace";
const D_SERIF   = "var(--f-display)";

// ────────────────────────── illustration ──────────────────────────
// One reusable canvas. Each `name` draws a distinct fuchsia line sketch.
// All sketches share a 280 × 90 viewBox so the grid stays clean.
function Sketch({ name }) {
  const s = D_FUCHSIA;
  const sd = D_FUCHSIA_DEEP;
  const sp = D_PLUM;
  const ink = D_INK;

  const wrap = (children) => (
    <svg viewBox="0 0 280 90" width="100%" height="86" style={{ display: "block" }}>
      {children}
    </svg>
  );

  switch (name) {
    case "ingestion": {
      // 8 source rails on the left → converge into a single bus on the right
      const rails = [
        { y: 12, label: "PMS" },
        { y: 22, label: "CM"  },
        { y: 32, label: "BE"  },
        { y: 42, label: "Web" },
        { y: 52, label: "WA"  },
        { y: 62, label: "IG"  },
        { y: 72, label: "Mail"},
        { y: 82, label: "Vox" },
      ];
      return wrap(
        <g>
          {rails.map((r, i) => (
            <g key={i}>
              <text x="2" y={r.y + 3} fill={D_FAINT} fontFamily={D_MONO} fontSize="6" letterSpacing="0.5">
                {r.label}
              </text>
              <path d={`M 26 ${r.y} C 80 ${r.y}, 130 45, 175 45`} fill="none" stroke={s} strokeWidth="0.9" strokeOpacity="0.7" />
              <circle cx={26 + (i % 4) * 7 + 18} cy={r.y - (r.y - 45) * 0.3} r="1.5" fill={s}/>
            </g>
          ))}
          <line x1="175" y1="45" x2="262" y2="45" stroke={s} strokeWidth="2" />
          <circle cx="175" cy="45" r="3.5" fill={s} />
          <circle cx="175" cy="45" r="7" fill="none" stroke={s} strokeOpacity="0.35" />
          <text x="190" y="40" fill={ink} fontFamily={D_MONO} fontSize="6.5" fontWeight="700" letterSpacing="1">NORMALISE</text>
          <text x="190" y="52" fill={D_DIM} fontFamily={D_MONO} fontSize="6" letterSpacing="0.6">→ contact + reservation</text>
        </g>
      );
    }

    case "identity": {
      // Two clusters of nodes merging with a "MERGE" arrow between them
      return wrap(
        <g>
          {/* left cluster: phone + email + temp-ota */}
          <g>
            <circle cx="38" cy="22" r="5"  fill={D_FUCHSIA_WASH} stroke={s} strokeWidth="1"/>
            <text x="46" y="24" fill={ink} fontFamily={D_MONO} fontSize="6" fontWeight="700">+31 ••• 8851</text>
            <circle cx="38" cy="42" r="5"  fill={D_FUCHSIA_WASH} stroke={s} strokeWidth="1"/>
            <text x="46" y="44" fill={ink} fontFamily={D_MONO} fontSize="6" fontWeight="700">anouk@…</text>
            <circle cx="38" cy="62" r="5"  fill={D_FUCHSIA_WASH} stroke={sd} strokeWidth="1" strokeDasharray="2 2"/>
            <text x="46" y="64" fill={sd} fontFamily={D_MONO} fontSize="6" fontWeight="700">ota+9f3@…</text>
            <line x1="38" y1="27" x2="38" y2="37" stroke={s} strokeOpacity="0.4" />
            <line x1="38" y1="47" x2="38" y2="57" stroke={s} strokeOpacity="0.4" />
          </g>
          {/* merge arrow */}
          <g transform="translate(135, 42)">
            <path d="M 0 -6 L 14 0 L 0 6" fill="none" stroke={s} strokeWidth="1.5"/>
            <text x="-4" y="18" fill={s} fontFamily={D_MONO} fontSize="6" fontWeight="700" letterSpacing="1">MERGE</text>
          </g>
          {/* resolved profile on right */}
          <g transform="translate(170, 18)">
            <rect width="92" height="54" rx="8" fill="#fff" stroke={s} strokeWidth="1.2"/>
            <circle cx="13" cy="13" r="6" fill={s}/>
            <text x="23" y="11" fill={ink} fontFamily={D_SERIF} fontSize="9" fontWeight="700">Anouk V.</text>
            <text x="23" y="20" fill={D_DIM} fontFamily={D_MONO} fontSize="5.5">C-04821 · 100% match</text>
            <line x1="8" y1="28" x2="84" y2="28" stroke={D_RULE} />
            <text x="8" y="38" fill={ink} fontFamily={D_MONO} fontSize="5.5">3 ids · 7 stays · 24 mems</text>
            <text x="8" y="47" fill={D_FUCHSIA_DEEP} fontFamily={D_MONO} fontSize="5.5" fontWeight="700">single record</text>
          </g>
        </g>
      );
    }

    case "stage": {
      // Lifecycle bar: Pre → In → Post → Returning, with marker on "In-stay"
      const stages = ["Pre-Stay", "In-Stay", "Post-Stay", "Returning"];
      return wrap(
        <g>
          <line x1="20" y1="50" x2="260" y2="50" stroke={s} strokeOpacity="0.35" strokeWidth="1.4"/>
          <line x1="20" y1="50" x2="100" y2="50" stroke={s} strokeWidth="2.2"/>
          {stages.map((st, i) => {
            const cx = 20 + (240 / 3) * i;
            const active = i === 1;
            return (
              <g key={st}>
                <circle cx={cx} cy="50" r={active ? 7 : 4.2} fill={active ? s : "#fff"} stroke={s} strokeWidth="1.5"/>
                {active && <circle cx={cx} cy="50" r="11" fill="none" stroke={s} strokeOpacity="0.35"/>}
                <text x={cx} y="22" fill={active ? ink : D_DIM} fontFamily={D_MONO} fontSize="6.5"
                  fontWeight={active ? 700 : 500} letterSpacing="0.6" textAnchor="middle">{st.toUpperCase()}</text>
                <text x={cx} y="76" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" textAnchor="middle">
                  {i === 0 ? "T-21d" : i === 1 ? "T-0 → T+n" : i === 2 ? "T+n → T+30" : "T+30+"}
                </text>
              </g>
            );
          })}
        </g>
      );
    }

    case "reservations": {
      // mini-table: 3 rows with room/board/rate columns
      const rows = [
        { rt: "Suite Vista Mar", bd: "HB",  rp: "FLEX",  rev: "€2,140" },
        { rt: "Doble Vista Mar", bd: "BB",  rp: "NRF",   rev: "€1,820" },
        { rt: "Suite Vista Mar", bd: "AI",  rp: "ADV",   rev: "€2,931" },
      ];
      return wrap(
        <g>
          {/* header */}
          <g>
            <text x="8"   y="10" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">ROOM</text>
            <text x="135" y="10" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">BOARD</text>
            <text x="178" y="10" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">RATE</text>
            <text x="265" y="10" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700" textAnchor="end">REVENUE</text>
            <line x1="6" y1="14" x2="272" y2="14" stroke={D_RULE}/>
          </g>
          {rows.map((r, i) => (
            <g key={i} transform={`translate(0, ${20 + i * 20})`}>
              <text x="8"  y="9" fill={ink} fontFamily={D_MONO} fontSize="6.5" fontWeight="600">{r.rt}</text>
              <rect x="135" y="1" width="20" height="10" rx="2" fill={D_FUCHSIA_WASH} stroke={s} strokeOpacity="0.5"/>
              <text x="145" y="9" fill={D_FUCHSIA_DEEP} fontFamily={D_MONO} fontSize="6" fontWeight="700" textAnchor="middle">{r.bd}</text>
              <rect x="178" y="1" width="26" height="10" rx="2" fill={D_FUCHSIA_PALE} stroke={D_PLUM} strokeOpacity="0.45"/>
              <text x="191" y="9" fill={D_PLUM} fontFamily={D_MONO} fontSize="6" fontWeight="700" textAnchor="middle">{r.rp}</text>
              <text x="265" y="9" fill={ink} fontFamily={D_MONO} fontSize="6.5" fontWeight="700" textAnchor="end">{r.rev}</text>
            </g>
          ))}
        </g>
      );
    }

    case "folio": {
      // receipt strip with line items, checkmarks down the right
      const items = [
        { l: "Spa · Aphrodite ritual", v: "+€340" },
        { l: "F&B · Verde tasting",    v: "+€180" },
        { l: "Excursion · catamaran",  v: "+€220" },
        { l: "Minibar · day 3",        v: "+€38"  },
      ];
      return wrap(
        <g>
          <rect x="8" y="6" width="264" height="78" rx="3" fill="#fff" stroke={D_RULE}/>
          <text x="16" y="17" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">FOLIO · BMH0019842</text>
          <line x1="14" y1="21" x2="266" y2="21" stroke={D_RULE} strokeDasharray="2 2"/>
          {items.map((it, i) => (
            <g key={i} transform={`translate(0, ${28 + i * 13})`}>
              <text x="16" y="6" fill={ink} fontFamily={D_MONO} fontSize="6.5">{it.l}</text>
              <text x="232" y="6" fill={D_FUCHSIA_DEEP} fontFamily={D_MONO} fontSize="6.5" fontWeight="700" textAnchor="end">{it.v}</text>
              <path d={`M 240 3 L 244 7 L 252 -1`} stroke={s} strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
            </g>
          ))}
        </g>
      );
    }

    case "memory": {
      // constellation of memory nodes with edges, sized by importance
      const nodes = [
        { x: 30,  y: 50, r: 8, kind: "P" },
        { x: 75,  y: 22, r: 5, kind: "I" },
        { x: 90,  y: 68, r: 6, kind: "P" },
        { x: 130, y: 40, r: 10, kind: "P" },
        { x: 180, y: 65, r: 4, kind: "C" },
        { x: 210, y: 30, r: 7, kind: "I" },
        { x: 255, y: 55, r: 5, kind: "Co"},
      ];
      const colors = { P: s, I: D_FUCHSIA_DEEP, C: D_PLUM, Co: D_FUCHSIA_DARK };
      const edges = [[0,1],[0,2],[0,3],[1,3],[2,3],[3,4],[3,5],[4,5],[5,6]];
      return wrap(
        <g>
          {edges.map(([a, b], i) => (
            <line key={i} x1={nodes[a].x} y1={nodes[a].y} x2={nodes[b].x} y2={nodes[b].y} stroke={s} strokeOpacity="0.3" strokeWidth="0.8"/>
          ))}
          {nodes.map((n, i) => (
            <g key={i}>
              <circle cx={n.x} cy={n.y} r={n.r} fill={colors[n.kind]} fillOpacity="0.15" stroke={colors[n.kind]} strokeWidth="1.2"/>
              <text x={n.x} y={n.y + 1.6} fill={colors[n.kind]} fontFamily={D_MONO} fontSize="5.5" fontWeight="700" textAnchor="middle">{n.kind}</text>
            </g>
          ))}
          <text x="8" y="84" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.6" fontWeight="700">24 MEMORIES · VECTOR-INDEXED</text>
        </g>
      );
    }

    case "segments": {
      // 3 overlapping circles (Venn) with a small "184" count in the centre
      return wrap(
        <g>
          <circle cx="100" cy="48" r="28" fill={s} fillOpacity="0.12" stroke={s} strokeWidth="1"/>
          <circle cx="140" cy="48" r="28" fill={sd} fillOpacity="0.12" stroke={sd} strokeWidth="1"/>
          <circle cx="120" cy="22" r="22" fill={sp} fillOpacity="0.12" stroke={sp} strokeWidth="1"/>
          <text x="76"  y="78" fill={s}  fontFamily={D_MONO} fontSize="6" fontWeight="700" letterSpacing="0.5">repeat</text>
          <text x="148" y="78" fill={sd} fontFamily={D_MONO} fontSize="6" fontWeight="700" letterSpacing="0.5">sea-view</text>
          <text x="100" y="10" fill={sp} fontFamily={D_MONO} fontSize="6" fontWeight="700" letterSpacing="0.5">vegan</text>
          <text x="120" y="48" fill={ink} fontFamily={D_SERIF} fontSize="14" fontWeight="700" textAnchor="middle">184</text>
          <text x="120" y="58" fill={D_DIM} fontFamily={D_MONO} fontSize="5" textAnchor="middle" letterSpacing="0.6">CONTACTS</text>

          <text x="230" y="40" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.6" fontWeight="700">LIVE</text>
          <circle cx="244" cy="38" r="2" fill={s}>
            <animate attributeName="opacity" values="1;0.3;1" dur="1.8s" repeatCount="indefinite"/>
          </circle>
          <text x="230" y="52" fill={D_DIM} fontFamily={D_MONO} fontSize="5.5">on-write</text>
        </g>
      );
    }

    case "fields": {
      // 3 mini form fields with labels + values
      const rows = [
        { l: "ALLERGY",         v: "Lactose, gluten",     t: "tag" },
        { l: "ROOM PREFERENCE", v: "Sea-view · high floor",t: "text" },
        { l: "LOYALTY TIER",    v: "Silver · 4 stays",     t: "tag" },
      ];
      return wrap(
        <g>
          {rows.map((r, i) => (
            <g key={i} transform={`translate(0, ${4 + i * 26})`}>
              <text x="8" y="8" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">{r.l}</text>
              <rect x="8" y="11" width="260" height="14" rx="3" fill="#fff" stroke={D_RULE}/>
              <text x="14" y="21" fill={ink} fontFamily={D_MONO} fontSize="6.5" fontWeight="600">{r.v}</text>
              {r.t === "tag" && <rect x="246" y="14" width="16" height="8" rx="2" fill={D_FUCHSIA_WASH} stroke={s} strokeOpacity="0.6"/>}
              {r.t === "tag" && <text x="254" y="20" fill={D_FUCHSIA_DEEP} fontFamily={D_MONO} fontSize="5" fontWeight="700" textAnchor="middle">TAG</text>}
            </g>
          ))}
        </g>
      );
    }

    case "rag": {
      // speech bubble on right, citation lines pointing into knowledge docs on left
      return wrap(
        <g>
          {/* knowledge docs */}
          {[10, 32, 54].map((y, i) => (
            <g key={i}>
              <rect x="8" y={y} width="60" height="14" rx="2" fill="#fff" stroke={D_RULE}/>
              <line x1="12" y1={y + 5} x2="60" y2={y + 5} stroke={D_RULE_S}/>
              <line x1="12" y1={y + 9} x2="50" y2={y + 9} stroke={D_RULE_S}/>
              <text x="12" y={y + 5} fill={s} fontFamily={D_MONO} fontSize="4.5" fontWeight="700">{["KB-04","KB-12","KB-19"][i]}</text>
            </g>
          ))}
          {/* citation arrows */}
          <path d="M 72 18 C 100 22, 110 32, 150 32" fill="none" stroke={s} strokeWidth="1" strokeDasharray="2 3"/>
          <path d="M 72 40 C 100 40, 110 40, 150 40" fill="none" stroke={s} strokeWidth="1" strokeDasharray="2 3"/>
          <path d="M 72 62 C 100 60, 110 50, 150 48" fill="none" stroke={s} strokeWidth="1" strokeDasharray="2 3"/>
          {/* AI reply bubble */}
          <g transform="translate(150, 20)">
            <rect width="118" height="50" rx="8" fill={D_FUCHSIA_WASH} stroke={s} strokeWidth="1.2"/>
            <text x="8" y="11" fill={s} fontFamily={D_MONO} fontSize="5.5" fontWeight="700" letterSpacing="0.6">AI REPLY · GROUNDED</text>
            <text x="8" y="22" fill={ink} fontFamily={D_SERIF} fontSize="6.5">Late check-out free for</text>
            <text x="8" y="31" fill={ink} fontFamily={D_SERIF} fontSize="6.5">Cala Marina suite guests</text>
            <text x="8" y="43" fill={D_DIM} fontFamily={D_MONO} fontSize="5">cited · KB-04, KB-12, KB-19</text>
          </g>
        </g>
      );
    }

    case "reach": {
      // 6×4 matrix of dots · channel × engagement state (sent / opened / replied / -)
      const channels = ["WA", "Web", "IG", "FB", "Mail", "SMS"];
      const states = ["sent", "open", "reply", "off"];
      const cells = [
        [1,1,1,0], [1,1,1,0], [1,1,0,0],
        [1,0,0,0], [1,1,1,0], [0,0,0,1],
      ];
      const tone = (v, c) => v === 0 ? "#fff" : c === 0 ? D_FUCHSIA_PALE : c === 1 ? D_FUCHSIA_SOFT : D_FUCHSIA;
      return wrap(
        <g>
          {/* col labels (states) */}
          {states.map((st, i) => (
            <text key={st} x={70 + i * 38} y="13" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.6" fontWeight="700" textAnchor="middle">
              {st.toUpperCase()}
            </text>
          ))}
          {channels.map((ch, ci) => (
            <g key={ch} transform={`translate(0, ${22 + ci * 11})`}>
              <text x="8" y="7" fill={ink} fontFamily={D_MONO} fontSize="6" fontWeight="700">{ch}</text>
              {states.map((_, si) => (
                <rect key={si} x={60 + si * 38} y="1" width="14" height="7" rx="1.5"
                  fill={tone(cells[ci][si], si)} stroke={s} strokeOpacity={cells[ci][si] ? 0.6 : 0.2}/>
              ))}
            </g>
          ))}
        </g>
      );
    }

    case "events": {
      // packets flowing along a timeline with timestamps
      return wrap(
        <g>
          <line x1="10" y1="50" x2="270" y2="50" stroke={s} strokeOpacity="0.35"/>
          {[20, 60, 96, 138, 178, 212, 244].map((x, i) => (
            <g key={i}>
              <line x1={x} y1="44" x2={x} y2="56" stroke={s} strokeOpacity="0.5"/>
              <text x={x} y="68" fill={D_FAINT} fontFamily={D_MONO} fontSize="5" textAnchor="middle">{["t0","t1","t2","t3","t4","t5","t6"][i]}</text>
            </g>
          ))}
          {[
            { x: 30,  l: "memory.created" },
            { x: 76,  l: "stage.transition" },
            { x: 120, l: "segment.entered" },
            { x: 158, l: "reservation.modified" },
            { x: 200, l: "extras.charged" },
            { x: 232, l: "consent.updated" },
          ].map((p, i) => (
            <g key={i}>
              <rect x={p.x - 2.5} y="38" width="5" height="5" rx="1" fill={s}>
                <animate attributeName="opacity" values="1;0.35;1" dur="2.4s" begin={`${i * 0.4}s`} repeatCount="indefinite"/>
              </rect>
            </g>
          ))}
          <text x="10" y="22" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">EVENT STREAM</text>
          <text x="100" y="22" fill={s} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.6" fontWeight="700">memory.created</text>
          <text x="218" y="86" fill={D_DIM} fontFamily={D_MONO} fontSize="5.5">→ Snowflake · Segment</text>
        </g>
      );
    }

    case "audit": {
      // clock face with rewind arrow, and a small change log
      return wrap(
        <g>
          {/* clock */}
          <g transform="translate(50, 45)">
            <circle r="28" fill="#fff" stroke={s} strokeWidth="1.5"/>
            <path d="M 0 -22 A 22 22 0 1 1 -16 -16" fill="none" stroke={s} strokeWidth="2"/>
            <path d="M -16 -16 L -19 -10 L -10 -12" fill="none" stroke={s} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
            <line x1="0" y1="0" x2="0" y2="-14" stroke={ink} strokeWidth="1.2"/>
            <line x1="0" y1="0" x2="9" y2="4" stroke={ink} strokeWidth="1.2"/>
            <circle r="2" fill={s}/>
          </g>
          {/* changelog */}
          <g transform="translate(105, 14)">
            <text x="0" y="0" fill={D_FAINT} fontFamily={D_MONO} fontSize="5.5" letterSpacing="0.8" fontWeight="700">FIELD HISTORY</text>
            {[
              { t: "16 May · 12:36", a: "email · ota→real",  by: "captive" },
              { t: "15 May · 09:14", a: "stage · Pre→In",     by: "cron"    },
              { t: "12 May · 18:02", a: "memory · added",     by: "AI"      },
              { t: "11 May · 10:55", a: "consent · WA on",    by: "guest"   },
            ].map((e, i) => (
              <g key={i} transform={`translate(0, ${10 + i * 12})`}>
                <circle cx="3" cy="3" r="1.8" fill={s}/>
                <text x="10" y="5" fill={ink} fontFamily={D_MONO} fontSize="5.5" fontWeight="600">{e.a}</text>
                <text x="105" y="5" fill={D_DIM} fontFamily={D_MONO} fontSize="5" textAnchor="end">{e.by}</text>
                <text x="160" y="5" fill={D_FAINT} fontFamily={D_MONO} fontSize="5">{e.t}</text>
              </g>
            ))}
          </g>
        </g>
      );
    }

    default: return wrap(null);
  }
}

// ────────────────────────── cards content ──────────────────────────
const CARDS = [
  {
    idx: "01", eyebrow: "INGESTION", title: "Eight live pipelines, one schema",
    sketch: "ingestion",
    body: "Webhook listeners on PMS, Channel Manager, Booking Engine, Captive Portal, Reception, Voice, Web Chat and OTA. Every event is normalised into the same contact and reservation schema before it lands.",
    bullets: ["Per-source normalisers · Opera · Mews · SiteMinder", "Dead-letter queue with a replay UI", "30-day backfill window, idempotent"],
    quote: "Eight sources, one shape. Your data warehouse stops being a graveyard of CSV imports.",
  },
  {
    idx: "02", eyebrow: "IDENTITY", title: "Deterministic plus probabilistic",
    sketch: "identity",
    body: "Phone, email, WhatsApp ID, document number, payment-card hash, all matched on the way in, never as a nightly job. OTA temp emails replace themselves the moment the captive portal captures the real one.",
    bullets: ["Match-on rules configurable per source", "Confidence score on every merge · full undo trail", "Manual review queue for low-confidence pairs"],
    quote: "OTA temp aliases die at check-in. Real contact, real conversation, every time.",
  },
  {
    idx: "03", eyebrow: "GUEST STAGE", title: "Lifecycle, auto-calculated",
    sketch: "stage",
    body: "A cron at 03:00 hotel-local recomputes Pre-Stay, In-Stay, Post-Stay and Returning across every contact, and re-fires journeys, segments and consent rules the moment a guest crosses a boundary.",
    bullets: ["Per-property stage thresholds", "On-event recompute on every PMS sync", "Full audit trail of every transition"],
    quote: "Stages move while you sleep. Your morning starts with the right cohort already in motion.",
  },
  {
    idx: "04", eyebrow: "RESERVATIONS", title: "Decomposed and queryable",
    sketch: "reservations",
    body: "Every booking carries room type, board basis, rate plan, occupancy, cancellation policy, source channel and split-night pricing. Indexed for the agent to reason over without re-reading a PDF.",
    bullets: ["Board: RO · BB · HB · FB · AI · TI", "Rate: NRF · FLEX · ADV · CORP · PROMO", "Per-channel attribution incl. TTOO rooming lists"],
    quote: "“My All-Inclusive includes the spa, right?” Answered in 1.2 s, straight from the rate plan itself.",
  },
  {
    idx: "05", eyebrow: "FOLIO EXTRAS", title: "Beyond room revenue",
    sketch: "folio",
    body: "Spa, F&B outlets, minibar, excursions, parking, transfers. Every charge attaches to the guest as a typed line item, available to segment on and to ground the agent's next message.",
    bullets: ["Auto-categorised by POS code", "Per-outlet revenue attribution", "Surfaces in memory (\"spa-positive guest\")"],
    quote: "The €340 spa visit becomes the reason the welcome-back campaign actually wins her back.",
  },
  {
    idx: "06", eyebrow: "MEMORY GRAPH", title: "Typed, scored, sourced",
    sketch: "memory",
    body: "Preferences, interests, context, communication and issues. Each memory carries a type, an importance score from 1 to 10, a confidence band and the channel it was captured on.",
    bullets: ["Vector-indexed for semantic recall", "Auto-decay without reinforcement", "Provenance back to the exact message"],
    quote: "Not a CRM note field. A scored, sourced, semantically searchable record of who the guest is.",
  },
  {
    idx: "07", eyebrow: "SEGMENTS", title: "Stays, memories and signals",
    sketch: "segments",
    body: "Compose cohorts across reservation history, memory graph, website behaviour, reachability and consent. Live counts, on-write refresh, no nightly stalls and no stale numbers.",
    bullets: ["Natural-language → filter expression", "Reachability and consent baked into the query", "Cohort → campaign or journey in one click"],
    quote: "\"Repeat sea-view, vegan, WA-reachable\" → 184 contacts, all already on the right journey.",
  },
  {
    idx: "08", eyebrow: "FIELDS & TAGS", title: "Your taxonomy",
    sketch: "fields",
    body: "Per-property field schemas: allergies, accessibility, special occasions, loyalty tier, partner ID. Every field queryable, every tag searchable, every value surfaced in the agent's context window.",
    bullets: ["Strict types and validation per field", "Bulk apply via segment", "Versioned schema migrations"],
    quote: "Your hotel's vocabulary is a first-class citizen. The agent learns it in one config.",
  },
  {
    idx: "09", eyebrow: "RAG GROUNDING", title: "One source of truth per reply",
    sketch: "rag",
    body: "Every agent message is grounded on the guest record, the reservation, the memory graph and your property knowledge base, and ships with the citations that proved the answer.",
    bullets: ["Per-property knowledge base", "Cited passages on every reply", "Refusal fallback when confidence drops"],
    quote: "No hallucinations. No invented amenities. Every claim points back to a document you control.",
  },
  {
    idx: "10", eyebrow: "REACHABILITY", title: "Channel × consent × engagement",
    sketch: "reach",
    body: "Last message, last open, last reply per channel, combined with per-channel consent, picks the next-best channel for every outbound automatically.",
    bullets: ["Engagement score per channel", "Auto-fallback WhatsApp → Email → SMS", "Per-guest deliverability score"],
    quote: "The campaign doesn't broadcast. It picks the channel each guest is actually answering on.",
  },
  {
    idx: "11", eyebrow: "EVENT STREAM", title: "Typed, signed, replayable",
    sketch: "events",
    body: "Every state change (memory written, segment entered, stage transitioned, reservation modified) emits a typed event to your sinks: Segment, Snowflake, your warehouse, your automation tool.",
    bullets: ["HMAC-signed payloads", "7-day replay window", "Backpressure-safe, exactly-once semantics"],
    quote: "18,420 events today, zero dropped, all replayable. Your BI team finally sleeps.",
  },
  {
    idx: "12", eyebrow: "AUDIT & GDPR", title: "Time-travel on the record",
    sketch: "audit",
    body: "Field-level history with who, what and when. Forget-Guest cascades through memories, conversations, segments and the event stream, and the cascade is verifiable in one click.",
    bullets: ["Per-field change log, forever", "GDPR delete cascade with proof-of-erasure", "Time-travel queries on segments and counts"],
    quote: "DPOs sign off. Auditors stop calling. The record always knows where every byte came from.",
  },
];

// ────────────────────────── card ──────────────────────────
function DCard({ c }) {
  const [hover, setHover] = dUseState(false);
  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        background: D_PAPER,
        border: `1px solid ${hover ? D_FUCHSIA + "55" : D_RULE}`,
        borderRadius: 12,
        padding: "20px 22px 22px",
        position: "relative",
        boxShadow: hover
          ? `0 24px 40px -24px ${D_FUCHSIA}55, 0 1px 0 rgba(0,0,0,0.02)`
          : "0 1px 0 rgba(0,0,0,0.02), 0 6px 14px -10px rgba(0,0,0,0.08)",
        transition: "all 0.25s ease",
        display: "flex", flexDirection: "column",
      }}>
      {/* sketch */}
      <div style={{
        background: "#fff",
        border: `1px solid ${D_RULE_S}`,
        borderRadius: 8,
        padding: "10px 12px",
        marginBottom: 14,
        height: 110,
        display: "flex", alignItems: "center",
      }}>
        <Sketch name={c.sketch} />
      </div>

      {/* eyebrow */}
      <div style={{
        fontFamily: D_MONO, fontSize: 10.5, color: D_FUCHSIA,
        fontWeight: 700, letterSpacing: "0.22em", marginBottom: 8,
      }}>
        {c.idx} · {c.eyebrow}
      </div>

      {/* title */}
      <div style={{
        fontFamily: D_SERIF, fontSize: 19.5, color: D_INK,
        fontWeight: 600, letterSpacing: "-0.018em", lineHeight: 1.15,
        marginBottom: 8,
      }}>
        {c.title}
      </div>

      {/* body */}
      <div style={{
        fontSize: 13, color: D_DIM, lineHeight: 1.55,
        marginBottom: 12, letterSpacing: "-0.005em",
      }}>
        {c.body}
      </div>

      {/* bullets */}
      <ul style={{
        margin: 0, padding: 0, listStyle: "none",
        display: "flex", flexDirection: "column", gap: 5,
        marginBottom: 14,
      }}>
        {c.bullets.map((b, i) => (
          <li key={i} style={{
            display: "flex", alignItems: "flex-start", gap: 8,
            fontSize: 12, color: D_INK, lineHeight: 1.45,
          }}>
            <span style={{
              width: 4, height: 4, borderRadius: "50%",
              background: D_FUCHSIA, marginTop: 7, flexShrink: 0,
            }} />
            {b}
          </li>
        ))}
      </ul>

      <div style={{ flex: 1 }} />

      {/* quote */}
      <div style={{
        paddingTop: 12, borderTop: `1px dashed ${D_RULE_S}`,
        fontFamily: D_SERIF, fontStyle: "italic", fontSize: 12.5,
        color: D_FUCHSIA_DEEP, lineHeight: 1.45,
        letterSpacing: "-0.005em",
      }}>
        “{c.quote}”
      </div>
    </div>
  );
}

// ────────────────────────── section ──────────────────────────
function DataLayerSection() {
  return (
    <section data-screen-label="Data layer capabilities" style={{
      position: "relative",
      background: D_CREAM,
      color: D_INK,
      fontFamily: "'Inter', system-ui, sans-serif",
      padding: "96px clamp(20px,4vw,48px) 96px",
      overflow: "hidden",
    }}>
      {/* ambient */}
      <div style={{
        position: "absolute", top: -80, left: "50%", width: 900, height: 700,
        transform: "translateX(-50%)",
        background: `radial-gradient(ellipse, ${D_FUCHSIA}0E 0%, transparent 60%)`,
        filter: "blur(40px)", pointerEvents: "none",
      }} />

      {/* header */}
      <div style={{ maxWidth: "var(--rail)", margin: "0 auto 18px" }}>
        <div className="eyebrow">
          12 primitives · one source of truth
        </div>
        <h2 className="h-2" style={{ marginTop: 14, maxWidth: 940 }}>
          A guest record that earns its place at the centre of your stack.{" "}
          <em className="italic-accent">
            One source of truth for marketing, operations, revenue, and AI.
          </em>
        </h2>
        <p className="lead" style={{ maxWidth: 820, marginTop: 18 }}>
          Unified, deduped, stage-aware. Every reservation decomposed by room, board and rate.
          Every folio extra attached. Every memory scored and sourced. Every segment recomputed live.
          The data isn't a side-effect of the CRM. It is the CRM, and it powers every campaign,
          journey, BI dashboard, revenue cut and agentic reply you build on top.
        </p>
      </div>

      {/* grid */}
      <div style={{
        maxWidth: "var(--rail)", margin: "44px auto 0",
        display: "grid", gridTemplateColumns: "repeat(3, 1fr)",
        gap: 18,
      }}>
        {CARDS.map(c => <DCard key={c.idx} c={c} />)}
      </div>
    </section>
  );
}

window.DataLayerSection = DataLayerSection;
