/* ══════════════════════════════════════════════════════════
   STRICTLY BUSINESS — HOW TO PLAY TUTORIAL
   ══════════════════════════════════════════════════════════
   Loaded as a separate script after game.jsx. Reads shared
   components from window.SB_EXPORTS and attaches its own
   Tutorial component back onto the same object.

   Architecture:
   - Slide registry (SLIDES) drives order + progress.
   - Each slide is a React component receiving { ctx } where
     ctx provides access to shared exports and helpers.
   - Progress persists to localStorage as an integer index.
   - No game dispatches leak into tutorial state; interactive
     slides manage their own local state.
   ══════════════════════════════════════════════════════════ */

(function () {
  if (typeof window === "undefined" || !window.SB_EXPORTS) {
    console.error("[tutorial] SB_EXPORTS not found — game.jsx must load first.");
    return;
  }
  var SB = window.SB_EXPORTS;
  var React = SB.React;
  var useState = SB.useState;
  var useEffect = SB.useEffect;
  var useRef = SB.useRef;
  var CardComp = SB.CardComp;
  var ACTIONS_T = SB.ACTIONS_T;
  var REACTIONS_T = SB.REACTIONS_T;
  var ASSETS_T = SB.ASSETS_T;
  var C = SB.C;
  var CT = SB.CT;
  var AS = SB.AS;
  var ST = SB.ST;
  var SUB_UI = SB.SUB_UI;
  var CSS = SB.CSS;
  var getDesc = SB.getDesc;
  var dollars = SB.dollars;
  var uid = SB.uid;

  var h = React.createElement;

  /* - Storage key - */
  var STORAGE_KEY = "sb_tutorial_progress";

  /* - Tutorial-specific colors (extending C) - */
  var TC = {
    pageBg: "#0a0f1a",
    chromeBg: "#0d1520",
    chromeBorder: "#1e293b",
    accent: "#f59e0b",
    accentDim: "#d97706",
    pillOff: "#1e293b",
    pillOn: "#f59e0b",
    text: "#f1f5f9",
    sub: "#94a3b8",
    muted: "#64748b",
    blue: "#60a5fa",
    red: "#f87171",
    green: "#4ade80",
    purple: "#a78bfa",
  };

  /* ══════════════════════════════════════════════════════════
     UTILITIES
     ══════════════════════════════════════════════════════════ */

  // Given a template name, find it and mint a card instance (uses same
  // shape as game.jsx's mkCards but without shuffling — for tutorial display).
  function mintCard(name) {
    var all = [].concat(
      ACTIONS_T.map(function (t) { return { tpl: t, type: CT.ACTION }; }),
      REACTIONS_T.map(function (t) { return { tpl: t, type: CT.REACTION }; }),
      ASSETS_T.map(function (t) { return { tpl: t, type: CT.ASSET }; })
    );
    var found = all.find(function (x) { return x.tpl.name === name; });
    if (!found) {
      console.warn("[tutorial] card not found:", name);
      return null;
    }
    return Object.assign({}, found.tpl, {
      id: uid(),
      type: found.type,
      value: found.tpl.val,
      origVal: found.tpl.val,
      tokens: [],
      status: ST.READY,
      disabled: false,
    });
  }

  /* ══════════════════════════════════════════════════════════
     PRIMITIVE LAYOUT HELPERS
     Used by slide components to keep consistent rhythm.
     ══════════════════════════════════════════════════════════ */

  function SlideTitle(props) {
    return h("div", { style: { marginBottom: 18 } },
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 3,
        color: TC.muted, marginBottom: 6,
      }}, props.eyebrow || ""),
      h("div", { style: {
        fontFamily: "'Rubik',sans-serif", fontSize: 28, fontWeight: 800,
        color: TC.text, lineHeight: 1.1, letterSpacing: -0.4,
      }}, props.title),
      props.subtitle && h("div", { style: {
        fontSize: 14, color: TC.sub, marginTop: 8, lineHeight: 1.5, maxWidth: 560,
      }}, props.subtitle)
    );
  }

  function Body(props) {
    return h("div", { style: {
      fontSize: 14, color: TC.text, lineHeight: 1.65,
      maxWidth: 640, marginBottom: 18,
    }}, props.children);
  }

  function Callout(props) {
    var col = props.color || TC.accent;
    return h("div", { style: {
      borderLeft: "3px solid " + col, padding: "10px 16px",
      background: "#0c1420", marginBottom: 16, maxWidth: 640,
      fontSize: 13, color: TC.text, lineHeight: 1.55,
    }}, props.children);
  }

  function CardRow(props) {
    return h("div", {
      style: {
        display: "flex", gap: 14, flexWrap: "wrap",
        justifyContent: props.center ? "center" : "flex-start",
        marginBottom: 18,
      }
    }, props.children);
  }

  // Wrapper that renders a CardComp with a light caption below,
  // and a click handler to open the detail modal if supplied.
  function CardDisplay(props) {
    return h("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 6 } },
      h(CardComp, {
        card: props.card,
        onClick: props.onClick,
        onDetail: props.onDetail,
      }),
      props.caption && h("div", { style: {
        fontSize: 11, color: TC.sub, maxWidth: 120, textAlign: "center", lineHeight: 1.4,
      }}, props.caption)
    );
  }

  /* ══════════════════════════════════════════════════════════
     ANNOTATED CARD — used once per card type to show anatomy
     ══════════════════════════════════════════════════════════ */

  function AnnotatedCard(props) {
    var card = props.card;
    return h("div", {
      style: { display: "flex", alignItems: "flex-start", gap: 32, marginBottom: 20, flexWrap: "wrap" }
    },
      // Card on left
      h("div", { style: { flexShrink: 0 } },
        h(CardComp, { card: card, onDetail: props.onDetail })
      ),
      // Anatomy list on right
      h("div", { style: { flex: 1, minWidth: 240, maxWidth: 400 } },
        h("div", {
          style: {
            fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
            letterSpacing: 2, color: TC.muted, marginBottom: 10,
          }
        }, "ANATOMY"),
        [
          { label: "Name", value: card.name, col: TC.text },
          { label: "Value (cost / sell price)", value: "$" + card.val + "k", col: TC.accent },
          { label: "Description", value: getDesc(card), col: TC.sub, small: true },
          { label: "Type", value: card.type, col: card.type === CT.ACTION ? TC.blue : card.type === CT.REACTION ? TC.red : TC.green },
        ].map(function (row, i) {
          return h("div", {
            key: i,
            style: {
              borderLeft: "2px solid " + row.col,
              padding: "6px 12px", marginBottom: 8,
              background: "#0c1420",
            }
          },
            h("div", { style: { fontSize: 10, color: TC.muted, letterSpacing: 1, marginBottom: 2 } }, row.label),
            h("div", { style: { fontSize: row.small ? 12 : 13, color: row.col, fontWeight: row.small ? 400 : 600, lineHeight: 1.4 } }, row.value)
          );
        })
      )
    );
  }

  /* ══════════════════════════════════════════════════════════
     DETAIL MODAL — lightweight standalone for tutorial use
     (Uses the game's DetailModal if possible, otherwise a small one)
     ══════════════════════════════════════════════════════════ */

  function TutorialDetailModal(props) {
    if (!props.card) return null;
    // Backdrop closes
    return h("div", {
      onClick: props.onClose,
      style: {
        position: "fixed", inset: 0, background: "rgba(0,0,0,0.85)",
        display: "flex", alignItems: "center", justifyContent: "center",
        zIndex: 2000, padding: 20, animation: "popIn .15s ease",
      }
    },
      h("div", {
        onClick: function (e) { e.stopPropagation(); },
        style: {
          background: "#0d1520", border: "1px solid #1e3a5f",
          borderRadius: 14, padding: 24, maxWidth: 360, width: "100%",
          display: "flex", flexDirection: "column", alignItems: "center", gap: 16,
        }
      },
        h(CardComp, { card: props.card }),
        h("div", { style: { fontSize: 13, color: TC.sub, textAlign: "center", lineHeight: 1.5, maxWidth: 300 } },
          getDesc(props.card)
        ),
        h("button", {
          onClick: props.onClose,
          style: {
            background: "#1e293b", border: "1px solid #334155",
            color: TC.sub, padding: "8px 20px", borderRadius: 8,
            cursor: "pointer", fontSize: 12, fontWeight: 600,
          }
        }, "Close")
      )
    );
  }

  /* ══════════════════════════════════════════════════════════
     CARD LINK — bold, clickable card name that opens detail modal
     Usage: h(CardLink, { name: "Pay Day", ctx: ctx })
     Optional displayName overrides the label while still looking
     up the card by its real name.
     ══════════════════════════════════════════════════════════ */

  function CardLink(props) {
    var card = mintCard(props.name);
    var label = props.displayName || props.name;
    if (!card) return h("strong", null, label);
    return h("strong", {
      style: {
        cursor: "pointer",
        textDecoration: "underline dotted",
        textUnderlineOffset: "3px",
        color: "inherit",
      },
      onClick: function (e) { e.stopPropagation(); props.ctx.setDetailCard(card); },
      title: "Click to view: " + props.name,
    }, label);
  }

  /* ══════════════════════════════════════════════════════════
     SLIDE CONTENT
     Each slide is `function(ctx) { return element; }`.
     ctx: { openDetail, closeDetail, detailCard, setDetailCard }
     ══════════════════════════════════════════════════════════ */

  /* --- Slide 1: What is this game + the goal --- */
  function Slide_Welcome(props) {
    var ctx = props.ctx;
    return h("div", null,
      h(SlideTitle, {
        eyebrow: "01 · WELCOME",
        title: "What is Strictly Business?",
        subtitle: "A competitive card game for 2 to 5 players where you build wealth, disrupt opponents, and score the most points when the game ends.",
      }),

      h("div", { style: {
        background: "#0d1520", border: "1px solid #1e3a5f",
        padding: "24px 28px", marginBottom: 20, borderRadius: 10,
        maxWidth: 640,
      }},
        h("div", { style: { fontSize: 11, color: TC.muted, letterSpacing: 2, marginBottom: 10 } }, "YOUR GOAL"),
        h("div", { style: {
          fontFamily: "'Rubik',sans-serif", fontSize: 22, fontWeight: 700,
          color: TC.text, marginBottom: 14, lineHeight: 1.2,
        }}, "Finish with the highest score."),
        h("div", { style: {
          display: "flex", flexDirection: "column", gap: 10,
        }},
          h("div", { style: {
            display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap",
            fontFamily: "'JetBrains Mono',monospace", fontSize: 14,
          }},
            h("span", { style: { color: TC.muted, fontSize: 11, minWidth: 80 } }, "1 PT / $10K"),
            h("span", { style: { color: TC.accent, fontWeight: 700 } }, "TOTAL CASH"),
            h("span", { style: { color: TC.muted, fontSize: 12 } }, "(money on hand + cards in hand value + asset values)"),
          ),
          h("div", { style: {
            display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap",
            fontFamily: "'JetBrains Mono',monospace", fontSize: 14,
          }},
            h("span", { style: { color: TC.muted, fontSize: 11, minWidth: 80 } }, "1 PT EACH"),
            h("span", { style: { color: TC.green, fontWeight: 700 } }, "ASSETS OWNED"),
          ),
          h("div", { style: {
            display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap",
            fontFamily: "'JetBrains Mono',monospace", fontSize: 14,
          }},
            h("span", { style: { color: TC.muted, fontSize: 11, minWidth: 80 } }, "15 TURNS"),
            h("span", { style: { color: TC.blue, fontWeight: 700 } }, "DEFAULT GAME LENGTH"),
            h("span", { style: { color: TC.muted, fontSize: 12 } }, "(adjustable in settings — scores tallied when turns run out)"),
          ),
        )
      ),

      h(Body, null,
        "Each turn you'll play cards, buy assets, and respond to your opponents. Cards let you gain money, steal from others, or disrupt their plans. Assets sit in front of you as your long-term wealth and often grant ongoing passive effects.",
      ),
      h(Body, null,
        "Turns are fast. The game rewards reading the board, knowing when to spend, and using reactions at the right moment. You'll learn everything in the next few slides.",
      ),
    );
  }

  /* --- Slide 2: Components overview --- */
  function Slide_Components(props) {
    var ctx = props.ctx;
    var sampleAction = mintCard("Pay Day");
    var sampleReaction = mintCard("Not A Chance");
    var sampleAsset = mintCard("Bank Branch");

    var Col = function (props) {
      return h("div", {
        style: {
          flex: 1, minWidth: 180, padding: "14px 16px",
          background: "#0c1420", border: "1px solid " + TC.chromeBorder,
          borderLeft: "3px solid " + props.color, borderRadius: 8,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
          letterSpacing: 2, color: props.color, marginBottom: 4,
        }}, props.label),
        h("div", { style: { fontFamily: "'Rubik',sans-serif", fontSize: 14, fontWeight: 700, color: TC.text, marginBottom: 6 } }, props.title),
        h("div", { style: { fontSize: 12, color: TC.sub, lineHeight: 1.5 } }, props.desc)
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "02 · COMPONENTS",
        title: "The pieces of the game",
        subtitle: "Two decks, three card types, and a shop. That's it.",
      }),

      h("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", marginBottom: 22, maxWidth: 720 } },
        h(Col, {
          color: TC.blue, label: "MAIN DECK · BLUE + RED",
          title: "Actions + Reactions",
          desc: "Where your hand comes from. Contains both action cards (what you play on your turn) and reaction cards (what you play in response to others).",
        }),
        h(Col, {
          color: TC.green, label: "ASSET DECK · GREEN",
          title: "Asset Cards",
          desc: "The shop pulls from this deck. Buy assets to build your wealth and unlock passive effects.",
        })
      ),

      h("div", { style: { fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2, color: TC.muted, marginBottom: 10 } },
        "ONE OF EACH · CLICK FOR DETAILS"
      ),

      h(CardRow, null,
        sampleAction && h(CardDisplay, { card: sampleAction, onClick: function(){ ctx.setDetailCard(sampleAction); }, caption: "Action" }),
        sampleReaction && h(CardDisplay, { card: sampleReaction, onClick: function(){ ctx.setDetailCard(sampleReaction); }, caption: "Reaction" }),
        sampleAsset && h(CardDisplay, { card: sampleAsset, onClick: function(){ ctx.setDetailCard(sampleAsset); }, caption: "Asset" }),
      ),

      h(Callout, { color: TC.accent },
        h("strong", { style: { color: TC.accent } }, "Heads up: "),
        "the card examples throughout this tutorial are the real cards you'll see in the game. Click any of them to open the full detail view.",
      ),
    );
  }

  /* --- Slide 3: Mobile UI walkthrough --- */
  function Slide_MobileUI(props) {
    var _active = useState(null);
    var active = _active[0];
    var setActive = _active[1];
    var _tab = useState("main");
    var tabView = _tab[0];
    var setTabView = _tab[1];

    var SPOTS = [
      { id:1, key:"opp", label:"Opponents",
        desc:"The top section shows your opponents. The colored border indicates their assigned color. Name and current money are shown at a glance. The VIEWING chips above let you switch whose perspective you are looking at — tap any name to view their hand and assets." },
      { id:2, key:"stats", label:"Your status",
        desc:"Your current action points (purple number on the left), your money (center, large), and your overdraft balance if you have gone below $0k (right). You start each turn with 2 action points. Playing or selling a card costs 1 action point each." },
      { id:3, key:"tabs", label:"Tabs",
        desc:"Four tabs organize the game. Main shows the deck, discard pile, shop tile, and recent activity. Assets shows your owned cards. Shop lets you browse and buy. Log shows the full history. Tap a tab or swipe left and right to switch. The number badges show count at a glance." },
      { id:4, key:"content", label:"Main view",
        desc:"The Main tab shows the current state of the game: how many cards are left in the deck, the discard pile, and a shortcut to the shop. Below that, recent activity shows the last few game events so you can quickly catch up on what just happened." },
      { id:5, key:"bar", label:"Action bar",
        desc:"Your controls for the current turn. Draw takes a card from the deck. Play uses your selected hand card. Sell discards it for money equal to its face value. End passes to the next player. Buttons gray out when they are not currently available." },
      { id:6, key:"hand", label:"Your hand",
        desc:"Your hand cards live at the very bottom of the screen, below the action buttons. Scroll left and right to browse all your cards. Tap a card to select it — a detail panel opens above the action bar showing the card's effect and your available options." },
    ];

    var activeSpot = active !== null ? SPOTS.find(function(s){ return s.id === active; }) : null;
    function activate(sp) {
      if (active === sp.id) { setActive(null); return; }
      setActive(sp.id);
    }

    var TABS_LABELS = ["Main","Assets","Shop","Log"];
    var TABS_KEYS   = ["main","assets","shop","log"];

    function hl(key) { return activeSpot && activeSpot.key === key; }
    function zone(key, top, height, bg, children) {
      var on = hl(key);
      return h("div",{style:{
        position:"absolute",left:0,right:0,top:top,height:height,
        background:bg||"transparent",overflow:"hidden",boxSizing:"border-box",
        border:on?"2px solid "+TC.accent:"2px solid transparent",
        boxShadow:on?"inset 0 0 14px "+TC.accent+"28":"none",
        transition:"border 0.18s,box-shadow 0.18s",
      }},children);
    }

    function miniOppMat(name, money, borderCol) {
      return h("div",{style:{
        flex:1,background:"#080d18",border:"1.5px solid "+borderCol,
        borderRadius:7,padding:"5px 6px",
      }},
        h("div",{style:{fontSize:9,fontWeight:700,color:borderCol,fontFamily:"'Rubik',sans-serif",marginBottom:3}},name),
        h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:14,color:"#f59e0b",fontWeight:700}},money),
      );
    }

    function miniAsset(name, sub, subCol, usable) {
      return h("div",{style:{background:"#0d1a0d",border:"1px solid #16a34a44",borderRadius:6,padding:"5px 7px",marginBottom:4,display:"flex",justifyContent:"space-between",alignItems:"center"}},
        h("div",null,
          h("div",{style:{fontSize:5.5,fontWeight:700,color:subCol,background:"#0a1208",borderRadius:2,padding:"1px 3px",display:"inline-block",marginBottom:1}},sub),
          h("div",{style:{fontSize:8.5,fontWeight:700,color:"#f1f5f9",fontFamily:"'Rubik',sans-serif"}},name),
        ),
        usable&&h("div",{style:{fontSize:7,fontWeight:700,color:"#4ade80",border:"1px solid #16a34a",borderRadius:4,padding:"2px 5px"}},"USE"),
      );
    }

    function miniShopItem(name, cost, canAfford) {
      return h("div",{style:{background:"#140f00",border:"1px solid #92400e",borderRadius:6,padding:"5px 7px",marginBottom:4}},
        h("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"}},
          h("div",{style:{fontSize:8.5,fontWeight:700,color:"#f1f5f9",fontFamily:"'Rubik',sans-serif"}},name),
          h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:9,color:canAfford?"#fbbf24":"#ef4444",fontWeight:700}},"$"+cost+"k"),
        ),
      );
    }

    function miniLog(msg, col) {
      return h("div",{style:{fontSize:7,color:col||"#94a3b8",lineHeight:1.4,borderBottom:"1px solid #0f1826",padding:"3px 0"}},msg);
    }

    function tabContent(view) {
      if (view==="main") return h("div",{style:{padding:"6px 5px"}},
        h("div",{style:{display:"flex",gap:4,marginBottom:5}},
          h("div",{style:{flex:1,height:46,background:"#1a1200",border:"1px solid #92400e",borderRadius:7,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"}},
            h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:15,fontWeight:900,color:"#f1f5f9"}},"68"),
            h("div",{style:{fontSize:6,color:"#64748b",letterSpacing:1,fontWeight:700}},"DECK"),
          ),
          h("div",{style:{flex:1,height:46,background:"#0d1520",border:"1px solid #1e293b",borderRadius:7,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"}},
            h("div",{style:{fontSize:7,color:"#16a34a",fontWeight:700,marginBottom:2}},"DISCARD"),
            h("div",{style:{fontSize:7,color:"#475569"}},"Empty"),
          ),
          h("div",{style:{flex:1,height:46,background:"#052e16",border:"1px solid #16a34a",borderRadius:7,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"}},
            h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:15,fontWeight:900,color:"#4ade80"}},"2"),
            h("div",{style:{fontSize:6,color:"#4ade80",letterSpacing:1,fontWeight:700}},"SHOP"),
            h("div",{style:{fontSize:5,color:"#16a34a"}},"TAP TO VIEW"),
          ),
        ),
        h("div",{style:{background:"#0d1520",border:"1px solid #1e293b",borderRadius:6,padding:"5px 6px"}},
          h("div",{style:{fontSize:5.5,color:"#475569",letterSpacing:1.5,fontWeight:700,marginBottom:3}},"RECENT ACTIVITY"),
          h("div",{style:{fontSize:7,color:"#94a3b8",lineHeight:1.5,marginBottom:1}},"Casey's turn begins."),
          h("div",{style:{fontSize:7,color:"#94a3b8",lineHeight:1.5,marginBottom:1}},"Casey's turn · Round 1 · Turns left: 15"),
          h("div",{style:{fontSize:7,lineHeight:1.5}},
            h("span",{style:{color:"#94a3b8"}},'Casey draws '),
            h("span",{style:{color:"#60a5fa",textDecoration:"underline"}},'"A Minor Loss"'),
            h("span",{style:{color:"#94a3b8"}},'.'),
          ),
        ),
      );
      if (view==="assets") return h("div",{style:{padding:"7px 6px"}},
        h("div",{style:{fontSize:6.5,color:"#475569",letterSpacing:1.5,fontWeight:700,marginBottom:5}},"YOUR ASSETS (2)"),
        miniAsset("Revenue Stream","PASSIVE","#a78bfa",false),
        miniAsset("Startup Costs","DURING","#60a5fa",true),
      );
      if (view==="shop") return h("div",{style:{padding:"7px 6px",background:"#0c0a00",height:"100%"}},
        h("div",{style:{fontSize:6.5,color:"#92400e",letterSpacing:1.5,fontWeight:700,marginBottom:5}},"SHOP (2)"),
        miniShopItem("Portfolio",6,true),
        miniShopItem("Pressure Campaign",7,false),
      );
      if (view==="log") return h("div",{style:{padding:"7px 6px"}},
        h("div",{style:{fontSize:6.5,color:"#475569",letterSpacing:1.5,fontWeight:700,marginBottom:4}},"GAME LOG"),
        miniLog("— Casey's turn · Round 1 —","#475569"),
        miniLog("Casey's turn begins.","#94a3b8"),
        miniLog('Casey draws "A Minor Loss".',"#60a5fa"),
      );
      return null;
    }

    function miniHandCard(type, name, val) {
      var col = type==="REACTION"?"#dc2626":"#2563eb";
      var bg  = type==="REACTION"?"#1e0d0d":"#0e1520";
      return h("div",{style:{
        flex:"0 0 36px",height:46,borderRadius:5,boxSizing:"border-box",
        background:bg,border:"1px solid "+col+"88",
        padding:"3px 3px",display:"flex",flexDirection:"column",justifyContent:"space-between",overflow:"hidden",
      }},
        h("div",{style:{fontSize:5.5,color:"#f1f5f9",lineHeight:1.15,fontFamily:"'Rubik',sans-serif",fontWeight:700}},name),
        h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:10,color:"#f59e0b",fontWeight:700,textAlign:"center"}},"$"+val+"k"),
        h("div",{style:{fontSize:5,color:col,fontWeight:700,letterSpacing:.3}},type),
      );
    }

    // Layout constants
    var cTop=22, oppH=72, statsH=18, tabH=24, contentH=84, barH=36, handH=50, cBot=18;
    var phoneW=202, phoneH=cTop+oppH+statsH+tabH+contentH+barH+handH+cBot; // 324
    var y = { opp:cTop, stats:cTop+oppH, tabs:cTop+oppH+statsH, content:cTop+oppH+statsH+tabH,
              bar:cTop+oppH+statsH+tabH+contentH, hand:cTop+oppH+statsH+tabH+contentH+barH };

    function PhoneMockup() {
      return h("div",{style:{width:phoneW,height:phoneH,flexShrink:0,background:"#101826",borderRadius:26,border:"2px solid #2d3a4f",position:"relative",overflow:"hidden",boxSizing:"border-box"}},
        // Chrome top
        h("div",{style:{position:"absolute",top:0,left:0,right:0,height:cTop,background:"#0d1520",display:"flex",alignItems:"center",justifyContent:"center"}},
          h("div",{style:{width:32,height:5,background:"#1e293b",borderRadius:3}}),
        ),
        // Chrome bottom
        h("div",{style:{position:"absolute",bottom:0,left:0,right:0,height:cBot,background:"#101826",display:"flex",alignItems:"center",justifyContent:"center"}},
          h("div",{style:{width:50,height:4,background:"#1e293b",borderRadius:2}}),
        ),

        // OPPONENTS ZONE
        zone("opp", y.opp, oppH, "#0d1520",
          h("div",{style:{height:"100%",display:"flex",flexDirection:"column",padding:"3px 5px",gap:3}},
            // Viewing chips row
            h("div",{style:{display:"flex",gap:3,alignItems:"center"}},
              h("div",{style:{fontSize:5.5,color:"#475569",marginRight:1}},"VIEWING"),
              h("div",{style:{fontSize:7,background:"#052e16",border:"1px solid #16a34a",borderRadius:10,padding:"1px 6px",color:"#4ade80",fontWeight:700}},"Casey"),
              h("div",{style:{fontSize:7,background:"#0a0f1a",border:"1px solid #334155",borderRadius:10,padding:"1px 6px",color:"#64748b",fontWeight:700}},"Alex"),
              h("div",{style:{fontSize:7,background:"#0a0f1a",border:"1px solid #334155",borderRadius:10,padding:"1px 6px",color:"#64748b",fontWeight:700}},"Blake"),
            ),
            // Opponent mats
            h("div",{style:{display:"flex",gap:4,flex:1}},
              miniOppMat("Alex","$3k","#2563eb"),
              miniOppMat("Blake","$1k","#dc2626"),
            ),
          ),
        ),

        // STATS BAR
        zone("stats", y.stats, statsH, "#080d18",
          h("div",{style:{height:"100%",display:"flex",alignItems:"center",padding:"0 8px",borderTop:"1px solid #0f1826",borderBottom:"1px solid #0f1826"}},
            h("div",{style:{flex:1}},
              h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:11,fontWeight:700,color:"#7c3aed",lineHeight:1}},"2"),
              h("div",{style:{fontSize:5,color:"#7c3aed",letterSpacing:.8,fontWeight:700}},"ACTIONS"),
            ),
            h("div",{style:{flex:2,textAlign:"center"}},
              h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:13,fontWeight:700,color:"#f59e0b",lineHeight:1}},"$4k"),
              h("div",{style:{fontSize:5.5,color:"#475569"}},"Casey"),
            ),
            h("div",{style:{flex:1.5,textAlign:"right"}},
              h("div",{style:{fontFamily:"'JetBrains Mono',monospace",fontSize:9,color:"#64748b",lineHeight:1}},"-$0k"),
              h("div",{style:{fontSize:5,color:"#475569",letterSpacing:.5,fontWeight:700}},"OVERDRAFT"),
            ),
          ),
        ),

        // TAB BAR
        zone("tabs", y.tabs, tabH, "#080f1a",
          h("div",{style:{height:"100%",display:"flex"}},
            TABS_LABELS.map(function(lbl,i){
              var isAct=tabView===TABS_KEYS[i];
              var badge = i===1?"1":i===2?"2":null;
              return h("div",{
                key:lbl,
                onClick:function(){ setTabView(TABS_KEYS[i]); },
                style:{
                  flex:1,display:"flex",alignItems:"center",justifyContent:"center",gap:2,
                  fontSize:7.5,fontWeight:700,fontFamily:"'Rubik',sans-serif",
                  background:isAct?"#0d1520":"transparent",
                  color:isAct?"#f1f5f9":"#475569",
                  borderBottom:"2px solid "+(isAct?TC.accent:"transparent"),
                  cursor:"pointer",boxSizing:"border-box",userSelect:"none",
                }
              },
                lbl,
                badge&&h("div",{style:{width:10,height:10,borderRadius:"50%",background:"#1e3a5f",fontSize:5.5,color:"#60a5fa",fontWeight:700,display:"flex",alignItems:"center",justifyContent:"center"}},badge),
              );
            }),
          ),
        ),

        // CONTENT AREA
        zone("content", y.content, contentH, "#0a0f1a",
          h("div",{style:{height:"100%",overflow:"hidden"}}, tabContent(tabView)),
        ),

        // ACTION BAR
        zone("bar", y.bar, barH, "#0d1520",
          h("div",{style:{height:"100%",padding:"5px 5px",display:"flex",gap:3,alignItems:"center",borderTop:"1px solid #1e293b"}},
            h("div",{style:{flex:1,background:"#0c1833",border:"1px solid #2563eb",borderRadius:5,padding:"5px 0",textAlign:"center",fontSize:7.5,fontWeight:700,color:"#60a5fa",fontFamily:"'Rubik',sans-serif"}},"DRAW"),
            h("div",{style:{flex:1,background:"#0a0f1a",border:"1px solid #1e293b",borderRadius:5,padding:"5px 0",textAlign:"center",fontSize:7.5,fontWeight:700,color:"#334155",fontFamily:"'Rubik',sans-serif"}},"PLAY"),
            h("div",{style:{flex:1,background:"#0a0f1a",border:"1px solid #1e293b",borderRadius:5,padding:"5px 0",textAlign:"center",fontSize:7.5,fontWeight:700,color:"#334155",fontFamily:"'Rubik',sans-serif"}},"SELL"),
            h("div",{style:{flex:1,background:"#1f0a0a",border:"1px solid #dc2626",borderRadius:5,padding:"5px 0",textAlign:"center",fontSize:7,fontWeight:700,color:"#f87171",fontFamily:"'Rubik',sans-serif"}},"END"),
          ),
        ),

        // HAND CARDS (below action bar)
        zone("hand", y.hand, handH, "#0d1520",
          h("div",{style:{height:"100%",padding:"4px 5px",borderTop:"1px solid #1e293b",position:"relative"}},
            h("div",{style:{display:"flex",gap:3}},
              miniHandCard("ACTION","Cost of Bu...",2),
              miniHandCard("REACTION","Risky Inve...",3),
              miniHandCard("ACTION","Double Shift",4),
              miniHandCard("ACTION","Pay Day",1),
              miniHandCard("REACTION","A Minor Loss",2),
            ),
            h("div",{style:{position:"absolute",top:6,right:6,fontSize:7,color:"#475569",fontFamily:"'JetBrains Mono',monospace",fontWeight:700}},"5/5"),
          ),
        ),
      );
    }

    return h("div",null,
      h(SlideTitle,{
        eyebrow:"03 · YOUR SCREEN",
        title:"Your screen.",
        subtitle:"Here is what the mobile game looks like. Tap the labels to highlight each area, or tap the tabs on the phone directly to see what each one shows.",
      }),
      h("div",{style:{display:"flex",justifyContent:"center",marginBottom:18}}, PhoneMockup()),
      h("div",{style:{display:"flex",gap:6,flexWrap:"wrap",marginBottom:14}},
        SPOTS.map(function(sp){
          var on=active===sp.id;
          return h("button",{key:sp.id,onClick:function(){activate(sp);},style:{
            background:on?TC.accent:"transparent",
            border:"1px solid "+(on?TC.accent:"#334155"),
            color:on?"#000":TC.sub,
            borderRadius:20,padding:"5px 14px",
            fontSize:11,fontWeight:700,cursor:"pointer",fontFamily:"'Rubik',sans-serif",
          }}, String(sp.id)+". "+sp.label);
        }),
      ),
      activeSpot
        ? h("div",{style:{background:"#0c1420",borderLeft:"3px solid "+TC.accent,padding:"12px 16px",maxWidth:500,fontSize:13,color:TC.text,lineHeight:1.6}},
            h("div",{style:{fontSize:10,letterSpacing:1.5,fontWeight:700,color:TC.accent,marginBottom:5,fontFamily:"'JetBrains Mono',monospace"}},activeSpot.label.toUpperCase()),
            activeSpot.desc,
          )
        : h("div",{style:{fontSize:12,color:TC.muted,fontStyle:"italic"}},"Tap a label above to learn what it does."),
    );
  }

  /* --- Slide 4: Action cards --- */
  function Slide_Actions(props) {
    var ctx = props.ctx;
    var c1 = mintCard("Pay Day");
    var c3 = mintCard("Negative Reaction");
    var c5 = mintCard("Kickstarter");

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "04 · ACTION CARDS",
        title: "Action cards",
        subtitle: "Your main tool on your turn. Play them to gain money, steal, disrupt, or manipulate the board.",
      }),

      // Anatomy for first card
      c1 && h(AnnotatedCard, { card: c1, onDetail: function(){ ctx.setDetailCard(c1); } }),

      h(Body, null,
        "Action card values range from ",
        h("strong", { style: { color: TC.accent, fontFamily: "'JetBrains Mono',monospace" } }, "$1k to $5k"),
        ". The value is the card's sell price and gives you a rough sense of how impactful the card's effect is. Playing an action card ",
        h("strong", null, "does not cost any money"),
        ". It simply costs one of your actions. Higher-value cards tend to have stronger or more disruptive effects.",
      ),

      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10, marginTop: 6,
      }}, "MORE EXAMPLES · CLICK FOR DETAILS"),

      h(CardRow, null,
        c3 && h(CardDisplay, { card: c3, onClick: function(){ ctx.setDetailCard(c3); }, caption: "$3k · hand disruption" }),
        c5 && h(CardDisplay, { card: c5, onClick: function(){ ctx.setDetailCard(c5); }, caption: "$5k · big swing" }),
      ),

      h(Callout, { color: TC.blue },
        h("strong", { style: { color: TC.blue } }, "When can I play them? "),
        "On your turn, during the planning phase. You get ",
        h("strong", null, "2 action points"),
        " per turn. In ",
        h("strong", null, "Free mode"),
        " (the default) you can spend both points however you like — two card plays, a play and a draw, two draws, any combo. In ",
        h("strong", null, "Limited mode"),
        " (opt-in in settings) each action type can only be used once. Either way, playing an action card costs 1 point.",
      ),
    );
  }

  /* --- Slide 4: Reaction cards --- */
  function Slide_Reactions(props) {
    var ctx = props.ctx;
    var r1 = mintCard("Ignored");
    var r2 = mintCard("Not A Chance");
    var r4 = mintCard("Scammed");

    var subCaption = function (card, text) {
      return h("div", { style: {
        fontSize: 11, color: TC.sub, maxWidth: 150, textAlign: "center",
        lineHeight: 1.45, marginTop: 4,
      }}, text);
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "05 · REACTION CARDS",
        title: "Reaction cards",
        subtitle: "Red cards you play in response to what someone else just did. They are played for free and don't cost you any actions.",
      }),

      r2 && h(AnnotatedCard, { card: r2, onDetail: function(){ ctx.setDetailCard(r2); } }),

      h(Body, null,
        "Reactions fire during a ",
        h("strong", { style: { color: TC.red } }, "reaction window"),
        ", a short period after certain triggers (like an opponent playing a card or gaining money) where anyone with a matching reaction can play it. Reaction values also range from ",
        h("strong", { style: { color: TC.accent, fontFamily: "'JetBrains Mono',monospace" } }, "$1k to $5k",),
        ". Like action cards, the value is the sell price and reflects the card's strength, not any cost to play.",
      ),

      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10, marginTop: 6,
      }}, "MORE EXAMPLES · CLICK FOR DETAILS"),

      h("div", { style: { display: "flex", gap: 18, flexWrap: "wrap", marginBottom: 16 } },
        r1 && h("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" } },
          h(CardComp, {
            card: r1,
            onClick: function(){ ctx.setDetailCard(r1); },
            reactionOnly: r1.hook !== "minor_loss",
          }),
          subCaption(r1, "Play when: an opponent plays an action card"),
        ),
        r4 && h("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" } },
          h(CardComp, {
            card: r4,
            onClick: function(){ ctx.setDetailCard(r4); },
            reactionOnly: r4.hook !== "minor_loss",
          }),
          subCaption(r4, "Play when: an opponent gains money"),
        ),
      ),

      h(Callout, { color: TC.red },
        h("strong", { style: { color: TC.red } }, "The AUTO badge: "),
        "notice the small ",
        h("span", { style: { fontFamily: "'JetBrains Mono',monospace", color: TC.red, fontSize: 11 } }, "🛡 AUTO"),
        " tag on those cards. It means they fire automatically inside their reaction window. You cannot manually play them on your turn. The one exception is ",
        h(CardLink, { name: "A Minor Loss", ctx: ctx }),
        ", which is a survival card you can play proactively.",
      ),
    );
  }

  /* --- Slide 11: Fees, overdraft, bankruptcy --- */
  function Slide_Fees(props) {
    var ctx = props.ctx;

    var TurnRow = function (p) {
      return h("div", {
        style: {
          display: "flex", alignItems: "center", gap: 0,
          background: "#0c1420", marginBottom: 6, overflow: "hidden", borderRadius: 6,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
          color: TC.muted, padding: "8px 10px",
          background: "#080d14", minWidth: 60, textAlign: "center",
        }}, p.turn),
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 11,
          color: p.inRed ? TC.red : TC.green, fontWeight: 700,
          padding: "8px 10px", minWidth: 80,
        }}, p.balance),
        h("div", { style: {
          fontSize: 11, color: TC.text, padding: "8px 10px", flex: 1, lineHeight: 1.4,
        }}, p.what),
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 11,
          color: p.feeCol || TC.muted, fontWeight: 700,
          padding: "8px 10px", textAlign: "right", minWidth: 80,
        }}, p.tracker),
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "12 · FEES & BANKRUPTCY",
        title: "Going into the red",
        subtitle: "You can dip into the negatives, but each turn you start there costs you.",
      }),

      h(Body, null,
        "To play an action card or buy an asset you need at least $1k on hand. The purchase itself can push you into the negatives, but you must start from $1k or above. If you begin your turn with a negative balance, an ",
        h("strong", { style: { color: TC.red } }, "overdraft fee"),
        " is charged immediately.",
      ),

      h("div", { style: { marginBottom: 18 } },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.muted, marginBottom: 8,
        }}, "HOW THE FEE TRACKER WORKS"),
        h("div", { style: {
          background: "#0c1420", borderLeft: "3px solid " + TC.accent,
          padding: "12px 14px", marginBottom: 14, fontSize: 12, color: TC.text, lineHeight: 1.7,
        }},
          h("div", null, "The fee starts at ", h("strong", null, "$1k"), " (the minimum) and increases by $1k each turn you start in the red, up to a cap of ", h("strong", null, "$3k"), "."),
          h("div", { style: { marginTop: 6 } }, "If you start a turn with a positive balance, the tracker drops back down by $1k (still minimum $1k)."),
          h("div", { style: { marginTop: 6, color: TC.sub } }, "Order each turn: pay the current fee first, then the tracker increments."),
        ),

        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.muted, marginBottom: 8,
        }}, "EXAMPLE SCENARIO"),
        h("div", { style: {
          display: "grid", gridTemplateColumns: "auto auto 1fr auto",
          gap: 0, marginBottom: 4,
          fontFamily: "'JetBrains Mono',monospace", fontSize: 9, color: TC.muted,
          background: "#080d14", padding: "4px 10px",
        }},
          h("div", { style: { minWidth: 60, textAlign: "center" }}, "TURN"),
          h("div", { style: { minWidth: 80 }}, "START $"),
          h("div", null, "WHAT HAPPENS"),
          h("div", { style: { minWidth: 80, textAlign: "right" }}, "FEE AFTER"),
        ),
        h(TurnRow, { turn: "1", balance: "$-2k", inRed: true, what: "Pay $1k fee (tracker is at min $1k). Balance drops to $-3k.", feeCol: "#facc15", tracker: "fee: $2k" }),
        h(TurnRow, { turn: "2", balance: "$-3k", inRed: true, what: "Pay $2k fee. Balance drops to $-5k.", feeCol: "#f97316", tracker: "fee: $3k" }),
        h(TurnRow, { turn: "3", balance: "$-5k", inRed: true, what: "Pay $3k fee (max). Balance drops to $-8k.", feeCol: TC.red, tracker: "fee: $3k" }),
        h(TurnRow, { turn: "4", balance: "$3k", inRed: false, what: "Positive! No fee this turn. Tracker decreases.", feeCol: TC.green, tracker: "fee: $2k" }),
      ),

      h(Callout, { color: TC.red },
        h("strong", { style: { color: TC.red } }, "Bankruptcy. "),
        "If your balance ever hits ",
        h("span", { style: { fontFamily: "'JetBrains Mono',monospace", fontWeight: 700 } }, "$-10k"),
        " you go bankrupt. Your balance resets to $0k, your hand is discarded - but you gain $1k for each card discarded this way - and you lose your single most valuable asset (the rest are kept). You're still in the game, but you've lost a lot of ground.",
      ),
    );
  }

  /* --- Slide 12: You're ready --- */
  function Slide_Ready(props) {
    var ctx = props.ctx;
    var Tip = function (p) {
      return h("div", {
        style: {
          padding: "10px 14px", marginBottom: 8,
          background: "#0c1420", borderLeft: "3px solid " + p.color,
          fontSize: 13, color: TC.text, lineHeight: 1.5,
        }
      },
        h("strong", { style: { color: p.color, marginRight: 8 } }, p.label),
        p.text
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "13 · READY TO PLAY",
        title: "You're ready.",
        subtitle: "That's the whole game. You'll pick up the nuance fast. Most cards explain themselves.",
      }),

      h("div", { style: { marginBottom: 24 } },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.muted, marginBottom: 10,
        }}, "QUICK CHEAT SHEET"),
        h(Tip, { color: TC.accent, label: "Points win.", text: "1 pt per $10k total (cash + hand + assets) plus 1 pt per asset owned. The game ends when turns run out." }),
        h(Tip, { color: TC.red, label: "Reactions are free.", text: "They don't cost action points and can be played during any reaction window — including on opponents' turns." }),
        h(Tip, { color: TC.blue, label: "2 action points.", text: "Free mode (default): any combo — play twice, draw twice, play and sell, etc. Limited mode (settings): each type once per turn." }),
        h(Tip, { color: TC.green, label: "Assets & buys are free.", text: "Buying assets and activating your DURING assets cost nothing. They're outside the action point system entirely." }),
        h(Tip, { color: TC.purple, label: "Hand limit is 5.", text: "You can hold more than 5 mid-turn, but you'll discard down to 5 at end of turn. Plan accordingly." }),
      ),

      h(Body, null, "Pick a mode to start your first game. You can revisit this tutorial any time from the menu."),

      h("div", { style: { marginTop: 16, display: "flex", gap: 10, flexWrap: "wrap" } },
        h("button", {
          onClick: ctx.onRestart,
          style: {
            background: "transparent", border: "1px solid " + TC.chromeBorder,
            color: TC.sub, padding: "9px 18px", borderRadius: 8,
            cursor: "pointer", fontSize: 11, fontWeight: 600, letterSpacing: 1,
          }
        }, "↺ START FROM BEGINNING"),
      ),
    );
  }

  /* ══════════════════════════════════════════════════════════
     INTERACTIVE SLIDES
     These use local component state to simulate real gameplay
     without touching the actual game reducer.
     ══════════════════════════════════════════════════════════ */

  /* Shared visual helpers for interactive slides */

  function MoneyCounter(props) {
    // Animated-ish money counter — just a styled number.
    var val = props.value;
    var neg = val < 0;
    return h("div", {
      style: {
        display: "flex", alignItems: "baseline", gap: 8,
        padding: "10px 16px", background: "#0c1420",
        border: "1px solid " + TC.chromeBorder, borderRadius: 8,
      }
    },
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
        letterSpacing: 2, color: TC.muted,
      }}, props.label || "YOUR CASH"),
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 22, fontWeight: 700,
        color: neg ? TC.red : TC.accent, transition: "color 0.2s",
      }}, (neg ? "-$" : "$") + Math.abs(val) + "k")
    );
  }

  function ResetButton(props) {
    return h("button", {
      onClick: props.onReset,
      style: {
        background: "transparent", border: "1px solid " + TC.chromeBorder,
        color: TC.muted, padding: "6px 12px", borderRadius: 6,
        cursor: "pointer", fontSize: 10, fontWeight: 600, letterSpacing: 1,
      }
    }, "↻ RESET");
  }

  /* --- Slide 5: Reaction window (interactive) --- */
  function Slide_ReactionWindow(props) {
    var ctx = props.ctx;
    // Scripted scenario: Alex plays "Budget Cut" targeting you. You have "Not A Chance".
    // Actions: play reaction (cancelled), pass (you take the hit), or skip.

    var attackCard = mintCard("Budget Cuts");
    var reactionCard = mintCard("Not A Chance");

    var _st = useState({ phase: "window", result: null });  // "window" | "resolved"
    var state = _st[0];
    var setState = _st[1];

    function reset() { setState({ phase: "window", result: null }); }
    function playReaction() { setState({ phase: "resolved", result: "reacted" }); }
    function passReaction() { setState({ phase: "resolved", result: "passed" }); }

    // The mock reaction window — styled to match the real ReactionWindow's DNA
    var WindowPanel = function () {
      return h("div", {
        style: {
          background: "#1a1410", border: "2px solid #f97316",
          borderRadius: 14, padding: 22,
          boxShadow: "0 0 40px #f9731655, 0 12px 28px #00000088",
          maxWidth: 520, margin: "0 auto",
        }
      },
        // Header
        h("div", { style: { marginBottom: 14 } },
          h("div", { style: {
            fontSize: 10, letterSpacing: 2, fontWeight: 700,
            color: "#f97316", fontFamily: "'Rubik',sans-serif",
          }}, "🃏 ACTION PLAYED"),
          h("div", { style: {
            fontSize: 18, fontFamily: "'Rubik',sans-serif", fontWeight: 700,
            color: TC.text, marginTop: 4,
          }}, '"Budget Cut"'),
          h("div", { style: { fontSize: 11, color: TC.muted, marginTop: 4 } },
            "Alex → you · you can cancel this"
          ),
        ),

        // Divider
        h("div", { style: { height: 1, background: "#2a1f15", margin: "12px 0" } }),

        // Your reaction options
        h("div", { style: { fontSize: 11, color: TC.sub, marginBottom: 10 } },
          "Your reactable cards:"
        ),
        h("div", { style: { display: "flex", gap: 12, alignItems: "center", marginBottom: 16 } },
          h(CardComp, { card: reactionCard, onClick: function(){ ctx.setDetailCard(reactionCard); } }),
          h("div", { style: { flex: 1, fontSize: 12, color: TC.sub, lineHeight: 1.5 } },
            "Click the card for details. ",
            h(CardLink, { name: "Not A Chance", ctx: ctx }),
            " ignores effects targeting you. The action fizzles and Alex loses the card.",
          ),
        ),

        // Action buttons
        h("div", { style: { display: "flex", gap: 10 } },
          h("button", {
            onClick: playReaction,
            style: {
              flex: 1, background: "#1f0a0a", border: "1px solid #dc2626",
              color: "#f87171", padding: "10px 0", borderRadius: 8,
              cursor: "pointer", fontFamily: "'Rubik',sans-serif",
              fontSize: 12, fontWeight: 700, letterSpacing: 1,
            }
          }, "REACT · NOT A CHANCE"),
          h("button", {
            onClick: passReaction,
            style: {
              flex: 1, background: "#1e293b", border: "1px solid #334155",
              color: TC.sub, padding: "10px 0", borderRadius: 8,
              cursor: "pointer", fontFamily: "'Rubik',sans-serif",
              fontSize: 12, fontWeight: 700, letterSpacing: 1,
            }
          }, "PASS"),
        ),
      );
    };

    var ResultPanel = function () {
      var reacted = state.result === "reacted";
      var col = reacted ? TC.green : TC.red;
      var icon = reacted ? "✓" : "✗";
      var title = reacted ? "Cancelled!" : "You took the hit";
      var body = reacted
        ? "Budget Cut was cancelled before it resolved. Alex's card is discarded; you keep your hand intact. Not A Chance goes to the discard pile. That's one fewer you have for later."
        : "You chose not to react. Budget Cut resolves and you discard a card from your hand. Not A Chance stays in your hand for a later threat.";

      return h("div", {
        style: {
          background: "#0c1420", border: "2px solid " + col,
          borderRadius: 14, padding: 22, maxWidth: 520, margin: "0 auto",
          textAlign: "center",
        }
      },
        h("div", { style: { fontSize: 36, color: col, marginBottom: 8 } }, icon),
        h("div", { style: {
          fontFamily: "'Rubik',sans-serif", fontSize: 18, fontWeight: 700,
          color: TC.text, marginBottom: 8,
        }}, title),
        h("div", { style: { fontSize: 13, color: TC.sub, lineHeight: 1.6 } }, body),
        h("button", {
          onClick: reset,
          style: {
            marginTop: 16, background: "transparent",
            border: "1px solid " + TC.chromeBorder,
            color: TC.sub, padding: "8px 20px", borderRadius: 8,
            cursor: "pointer", fontSize: 11, fontWeight: 600, letterSpacing: 1,
          }
        }, "↻ TRY AGAIN"),
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "06 · REACTION WINDOW",
        title: "Responding in real time",
        subtitle: "When an opponent plays a card that can be reacted to, everyone at the table gets a short window to respond.",
      }),

      h(Body, null,
        "Here's a scenario: Alex just played ",
        h(CardLink, { name: "Budget Cuts", displayName: '"Budget Cuts"', ctx: ctx }),
        " targeting you. It'll make you discard a card unless someone cancels it. You happen to have ",
        h(CardLink, { name: "Not A Chance", ctx: ctx }),
        ". You have a reaction card that can cancel effects targeting you.",
      ),

      h("div", { style: { marginTop: 20, marginBottom: 20 } },
        state.phase === "window" ? WindowPanel() : ResultPanel()
      ),

      h(Callout, { color: TC.purple },
        h("strong", { style: { color: TC.purple } }, "A few things to know: "),
        "reaction cards are played for free and don't cost you any actions. The real game gives you a timer (around 20 seconds) before it auto-passes. If you don't have a valid reaction, no window opens for you and you won't be interrupted.",
      ),
    );
  }

  /* --- Slide 6: Stacked reactions / trigger stack (interactive walkthrough) --- */
  function Slide_Stack(props) {
    var ctx = props.ctx;
    // Scenario: Alex plays Budget Cut → You play Not A Chance → Alex plays Fired.
    // Three steps the player advances through.
    var budgetCut = mintCard("Budget Cuts");
    var notAChance = mintCard("Not A Chance");
    var fired = mintCard("Fired");

    var _st = useState(0);  // 0, 1, 2, 3 (final summary)
    var step = _st[0];
    var setStep = _st[1];

    var steps = [
      {
        title: "1. Alex plays Budget Cuts",
        actor: "Alex", card: budgetCut, actorCol: TC.blue,
        body: ["Alex targets you. Normally you'd discard a card, but you have a reaction."],
        stackLabel: "TRIGGER: ACTION PLAYED",
      },
      {
        title: "2. You play Not A Chance",
        actor: "You", card: notAChance, actorCol: TC.green,
        body: ["You react to cancel ", h(CardLink, { name: "Budget Cuts", ctx: ctx }), ". But playing a reaction is itself a trigger. Alex can still respond to your response."],
        stackLabel: "TRIGGER: REACTION PLAYED",
      },
      {
        title: "3. Alex plays Fired",
        actor: "Alex", card: fired, actorCol: TC.red,
        body: [h(CardLink, { name: "Fired", ctx: ctx }), " cancels a reaction. Your ", h(CardLink, { name: "Not A Chance", ctx: ctx }), " is voided. ", h(CardLink, { name: "Budget Cuts", ctx: ctx }), " resolves and you discard a card after all."],
        stackLabel: "TRIGGER: REACTION PLAYED (cancelled)",
      },
    ];

    // Stack visualization showing cards piling up
    var Stack = function () {
      var shown = steps.slice(0, Math.min(step + 1, 3));
      return h("div", {
        style: {
          background: "#0c1420", border: "1px solid " + TC.chromeBorder,
          padding: "14px 16px", borderRadius: 8, minWidth: 220,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
          letterSpacing: 2, color: TC.muted, marginBottom: 10,
        }}, "TRIGGER STACK"),
        shown.map(function (s, i) {
          var isTop = i === shown.length - 1 && step < 3;
          return h("div", {
            key: i,
            style: {
              display: "flex", alignItems: "center", gap: 8,
              padding: "8px 10px", marginBottom: 4,
              background: isTop ? "#1a1410" : "#0a0f18",
              borderLeft: "3px solid " + s.actorCol,
              transition: "all 0.3s ease",
            }
          },
            h("div", { style: {
              fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
              color: TC.muted, minWidth: 14,
            }}, String(i + 1)),
            h("div", { style: { flex: 1 } },
              h("div", { style: { fontSize: 11, color: s.actorCol, fontWeight: 700 } },
                s.actor + " · " + s.card.name
              ),
              isTop && h("div", { style: { fontSize: 9, color: TC.muted, marginTop: 2 } }, "top of stack")
            )
          );
        })
      );
    };

    var SummaryPanel = function () {
      return h("div", {
        style: {
          background: "#0c1420", border: "2px solid " + TC.purple,
          borderRadius: 12, padding: 22, marginTop: 20, maxWidth: 640,
        }
      },
        h("div", { style: {
          fontFamily: "'Rubik',sans-serif", fontSize: 16, fontWeight: 700,
          color: TC.text, marginBottom: 10,
        }}, "The rule"),
        h("div", { style: { fontSize: 13, color: TC.sub, lineHeight: 1.6, marginBottom: 14 } },
          "Reactions resolve in reverse order: last played, first to resolve. That's why the ",
          h("strong", { style: { color: TC.text } }, "top of the stack"),
          " gets handled first. Any reaction played opens a new window where others can react to it, potentially cancelling the whole chain.",
        ),
        h("button", {
          onClick: function () { setStep(0); },
          style: {
            background: "transparent", border: "1px solid " + TC.chromeBorder,
            color: TC.sub, padding: "8px 20px", borderRadius: 8,
            cursor: "pointer", fontSize: 11, fontWeight: 600, letterSpacing: 1,
          }
        }, "↻ REPLAY"),
      );
    };

    var current = step < 3 ? steps[step] : null;

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "07 · TRIGGER STACK",
        title: "Reactions can be reacted to",
        subtitle: "When multiple reactions fire on the same event, the game resolves them in order. Step through a scenario to see it.",
      }),

      h("div", { style: { display: "flex", gap: 20, flexWrap: "wrap", marginTop: 20, marginBottom: 20 } },
        // Left: stack
        h("div", null, Stack()),

        // Right: current event details
        h("div", { style: { flex: 1, minWidth: 280 } },
          current ? h("div", null,
            h("div", { style: {
              fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
              letterSpacing: 2, color: TC.muted, marginBottom: 8,
            }}, current.stackLabel),
            h("div", { style: {
              fontFamily: "'Rubik',sans-serif", fontSize: 16, fontWeight: 700,
              color: TC.text, marginBottom: 12,
            }}, current.title),
            h("div", { style: { display: "flex", gap: 14, alignItems: "flex-start", marginBottom: 14 } },
              h(CardComp, { card: current.card, onClick: function(){ ctx.setDetailCard(current.card); } }),
              h("div", { style: { flex: 1, fontSize: 12, color: TC.sub, lineHeight: 1.6 } },
                current.body
              )
            ),
          ) : SummaryPanel()
        ),
      ),

      // Controls
      step < 3 && h("div", { style: { display: "flex", gap: 10 } },
        h("button", {
          onClick: function () { if (step > 0) setStep(step - 1); },
          disabled: step === 0,
          style: {
            background: step === 0 ? "transparent" : "#1e293b",
            border: "1px solid " + TC.chromeBorder, color: TC.sub,
            padding: "8px 18px", borderRadius: 8,
            cursor: step === 0 ? "not-allowed" : "pointer",
            opacity: step === 0 ? 0.4 : 1,
            fontSize: 11, fontWeight: 600, letterSpacing: 1,
          }
        }, "← BACK"),
        h("button", {
          onClick: function () { setStep(step + 1); },
          style: {
            background: "#1c1004", border: "1px solid " + TC.accentDim,
            color: TC.accent, padding: "8px 18px", borderRadius: 8,
            cursor: "pointer", fontSize: 11, fontWeight: 700, letterSpacing: 1,
          }
        }, step === 2 ? "SEE RESULT →" : "NEXT STEP →"),
      ),
    );
  }

  /* --- Slide 7: Asset cards + sub-types (interactive activation) --- */
  function Slide_Assets(props) {
    var ctx = props.ctx;
    var passive = mintCard("Bank Branch");
    var during = mintCard("Coming Soon");
    var once = mintCard("BOGO");
    var anytime = mintCard("Paid Work");

    // Simulate the peek: show a random asset from ASSETS_T
    var peekCard = mintCard("Portfolio");

    // Interactive mini-mat: DURING asset starts READY, click to activate
    var _st = useState({ activated: false });
    var state = _st[0];
    var setState = _st[1];

    var activate = function () {
      if (state.activated) return;
      setState({ activated: true });
    };
    var reset = function () { setState({ activated: false }); };

    var SubTypeRow = function (p) {
      return h("div", {
        style: {
          display: "flex", alignItems: "center", gap: 14,
          padding: "10px 14px", background: "#0c1420",
          borderLeft: "3px solid " + p.color,
          marginBottom: 8,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
          letterSpacing: 2, color: p.color, fontWeight: 700,
          minWidth: 100,
        }}, p.label),
        h("div", { style: { fontSize: 12, color: TC.text, lineHeight: 1.5 } }, p.desc)
      );
    };

    // Build activating-ready asset copy for the mat
    var matAsset = Object.assign({}, during || {}, {
      status: state.activated ? ST.USED : ST.READY,
    });

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "08 · ASSETS",
        title: "Building your wealth",
        subtitle: "Assets sit in front of you and stay in play. They're your long-term value and often grant passive effects. Everyone can see what everyone owns.",
      }),

      // Sub-types explanation
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10, marginTop: 6,
      }}, "FOUR SUB-TYPES"),

      h("div", { style: { marginBottom: 16 } },
        h(SubTypeRow, { color: "#86efac", label: "PASSIVE", desc: "Always on. Triggers automatically when conditions are met (e.g. start of turn, someone gains money)." }),
        h(SubTypeRow, { color: "#fde68a", label: "DURING", desc: "Can be activated only on your own turn. Activation is free and costs no actions." }),
        h(SubTypeRow, { color: "#67e8f9", label: "ANYTIME", desc: "Activated during a relevant reaction window, even on other players' turns." }),
        h(SubTypeRow, { color: "#fdba74", label: "ONE TIME", desc: "Single activation, then it's gone. Usually a powerful effect to compensate." }),
      ),

      // Card examples
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10,
      }}, "ONE OF EACH · CLICK FOR DETAILS"),

      h(CardRow, null,
        passive && h(CardDisplay, { card: passive, onClick: function(){ ctx.setDetailCard(passive); }, caption: "PASSIVE" }),
        during && h(CardDisplay, { card: during, onClick: function(){ ctx.setDetailCard(during); }, caption: "DURING" }),
        anytime && h(CardDisplay, { card: anytime, onClick: function(){ ctx.setDetailCard(anytime); }, caption: "ANYTIME" }),
        once && h(CardDisplay, { card: once, onClick: function(){ ctx.setDetailCard(once); }, caption: "ONE TIME" }),
      ),

      // Interactive activation demo
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10, marginTop: 12,
      }}, "TRY ACTIVATING · YOU OWN THIS ASSET"),

      h("div", {
        style: {
          background: "#0c1420", border: "1px solid " + TC.chromeBorder,
          borderRadius: 10, padding: 18, display: "flex", gap: 20,
          alignItems: "flex-start", flexWrap: "wrap", marginBottom: 16,
        }
      },
        // The asset with its status
        matAsset && h("div", { style: { position: "relative" } },
          h(CardComp, {
            card: matAsset,
            onClick: state.activated ? undefined : activate,
            onDetail: function(){ ctx.setDetailCard(during); },
          }),
          !state.activated && h("div", {
            style: {
              position: "absolute", top: -6, right: -6,
              background: TC.accent, color: "#0a0f1a",
              fontSize: 9, fontWeight: 800, letterSpacing: 1,
              padding: "2px 6px", borderRadius: 4,
              fontFamily: "'Rubik',sans-serif",
            }
          }, "CLICK"),
        ),
        h("div", { style: { flex: 1, minWidth: 220 } },
          !state.activated
            ? h("div", { style: { fontSize: 12, color: TC.sub, lineHeight: 1.6 } },
                "Click the asset to activate it. ",
                h(CardLink, { name: "Coming Soon", ctx: ctx }),
                " lets you peek at the top card of the Asset Deck for a few seconds. Since it's a DURING asset, you can use it freely on your turn without spending an action."
              )
            : h("div", null,
                h("div", { style: {
                  fontSize: 11, color: TC.muted, letterSpacing: 2,
                  fontFamily: "'JetBrains Mono',monospace", marginBottom: 8,
                }}, "TOP OF ASSET DECK · 10s PEEK"),
                peekCard && h(CardComp, {
                  card: peekCard,
                  onClick: function(){ ctx.setDetailCard(peekCard); },
                }),
                h("div", { style: {
                  fontSize: 11, color: TC.sub, marginTop: 8, lineHeight: 1.5,
                }}, "The card is now USED and will reset to READY at the start of your next turn. Now you know what's coming up in the shop."),
              )
        ),
        h("div", null, h(ResetButton, { onReset: reset })),
      ),

      h(Callout, { color: TC.green },
        h("strong", { style: { color: TC.green } }, "Start of turn. "),
        "All your USED assets automatically reset to READY at the start of each of your turns. You don't have to manage this yourself.",
      ),
    );
  }

  /* --- Slide 8: Selling a card (interactive) --- */
  function Slide_Selling(props) {
    var ctx = props.ctx;
    var initialHand = function () {
      return [
        Object.assign({}, mintCard("Pay Day"), { _tutId: "a" }),
        Object.assign({}, mintCard("Budget Cuts"), { _tutId: "b" }),
        Object.assign({}, mintCard("Kickstarter"), { _tutId: "c" }),
      ];
    };

    var _st = useState({ hand: initialHand(), cash: 5, sold: null });
    var state = _st[0];
    var setState = _st[1];

    var sell = function (card) {
      setState({
        hand: state.hand.filter(function(c){ return c._tutId !== card._tutId; }),
        cash: state.cash + card.val,
        sold: card,
      });
    };
    var reset = function () { setState({ hand: initialHand(), cash: 5, sold: null }); };

    var WhyPanel = function () {
      return h("div", {
        style: {
          background: "#0c1420", borderLeft: "3px solid " + TC.accent,
          padding: "12px 16px", marginTop: 14,
          fontSize: 12, color: TC.sub, lineHeight: 1.6, maxWidth: 640,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.muted, marginBottom: 6,
        }}, "WHY SELL?"),
        "Quick cash when you're short. Dumping a card you can't use this turn. Triggering sell-based effects like ",
        h(CardLink, { name: "Last Minute Bid", ctx: ctx }),
        " or ",
        h(CardLink, { name: "Equal Pay", ctx: ctx }),
        ". Selling costs 1 action point — in Free mode you could even sell twice in one turn if you wanted to burn both points on cash.",
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "09 · SELLING",
        title: "Trading cards for cash",
        subtitle: "Every card has a value. That value is the card's sell price. Click any card below to sell it.",
      }),

      h("div", { style: {
        display: "flex", gap: 16, alignItems: "center", flexWrap: "wrap",
        marginBottom: 18,
      }},
        h(MoneyCounter, { value: state.cash }),
        state.sold && h("div", {
          style: {
            fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TC.green,
            padding: "8px 14px", background: "#052e16",
            border: "1px solid #16a34a", borderRadius: 6,
            animation: "popIn 0.3s ease",
          }
        }, "+ $" + state.sold.val + "k  · SOLD: " + state.sold.name),
        h(ResetButton, { onReset: reset }),
      ),

      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10,
      }}, "YOUR HAND · CLICK TO SELL"),

      h("div", {
        style: {
          display: "flex", gap: 14, flexWrap: "wrap",
          minHeight: 170, alignItems: "flex-start",
          marginBottom: 14,
        }
      },
        state.hand.length === 0
          ? h("div", {
              style: {
                color: TC.muted, fontSize: 13, fontStyle: "italic",
                padding: 20, alignSelf: "center",
              }
            }, "Hand empty. Hit reset to try again.")
          : state.hand.map(function (card) {
              return h(CardDisplay, {
                key: card._tutId,
                card: card,
                onClick: function () { sell(card); },
                onDetail: function () { ctx.setDetailCard(card); },
                caption: "Click to sell for $" + card.val + "k",
              });
            })
      ),

      WhyPanel(),
    );
  }

  /* --- Slide 9: The shop (interactive refresh + buy) --- */
  function Slide_Shop(props) {
    var ctx = props.ctx;
    var POOL = ["Bank Branch", "Coming Soon", "Side Hustle", "Portfolio", "Leave A Tip", "Paid Work", "Accountant", "Prospects"];
    var BLIND_BUY_COST = 7;

    var makeShop = function (seed, size) {
      var pool = POOL.slice();
      var out = [];
      var s = seed || 1;
      var n = size || 2;
      while (pool.length > 0 && out.length < n) {
        s = (s * 9301 + 49297) % 233280;
        var idx = Math.floor((s / 233280) * pool.length);
        out.push(mintCard(pool[idx]));
        pool.splice(idx, 1);
      }
      return out;
    };

    // hasRefreshed tracks whether we've already done the simulated refresh
    var _st = useState({ shop: makeShop(1, 2), cash: 15, owned: [], hasRefreshed: false, message: "" });
    var state = _st[0];
    var setState = _st[1];

    // First click: simulate a real refresh (reshuffles + adds 1 slot = 3 cards)
    // After that, button becomes reset
    var refreshOrReset = function () {
      if (!state.hasRefreshed) {
        setState({
          shop: makeShop(5, 3),  // refresh adds one extra slot
          cash: state.cash,
          owned: state.owned,
          hasRefreshed: true,
          message: "Shop refreshed. Assets reshuffled and one new slot added.",
        });
      } else {
        setState({ shop: makeShop(1, 2), cash: 15, owned: [], hasRefreshed: false, message: "" });
      }
    };

    var buy = function (asset) {
      if (state.cash < 1) {
        setState(Object.assign({}, state, {
          message: "Need at least $1k to buy. You can go negative after paying, but you need to start at $1k or above.",
        }));
        return;
      }
      var newShop = state.shop.filter(function(a){ return a.id !== asset.id; });
      setState({
        shop: newShop,
        cash: state.cash - asset.val,
        owned: state.owned.concat([asset]),
        hasRefreshed: state.hasRefreshed,
        message: "Bought " + asset.name + " for $" + asset.val + "k.",
      });
    };

    var blindBuy = function () {
      if (state.cash < 1) {
        setState(Object.assign({}, state, { message: "Need at least $1k to blind buy." }));
        return;
      }
      var pool = POOL.filter(function(n) {
        return !state.owned.some(function(o){ return o.name === n; })
            && !state.shop.some(function(s){ return s.name === n; });
      });
      if (!pool.length) {
        setState(Object.assign({}, state, { message: "No blind-buy assets left in this demo." }));
        return;
      }
      var pick = mintCard(pool[0]);
      setState({
        shop: state.shop,
        cash: state.cash - BLIND_BUY_COST,
        owned: state.owned.concat([pick]),
        hasRefreshed: state.hasRefreshed,
        message: "Blind buy! Paid $" + BLIND_BUY_COST + "k and received " + pick.name + ".",
      });
    };

    var ShopTile = function (asset) {
      var affordable = state.cash >= 1;
      return h("div", {
        key: asset.id,
        style: {
          background: "#0c1420",
          border: "1px solid " + (affordable ? "#166534" : TC.chromeBorder),
          borderRadius: 10, padding: 14,
          display: "flex", gap: 14, alignItems: "center",
          opacity: affordable ? 1 : 0.65,
        }
      },
        h(CardComp, { card: asset, onDetail: function(){ ctx.setDetailCard(asset); } }),
        h("div", { style: { flex: 1, minWidth: 140 } },
          h("div", { style: {
            fontFamily: "'Rubik',sans-serif", fontSize: 13, fontWeight: 700,
            color: TC.text, marginBottom: 4,
          }}, asset.name),
          h("div", { style: {
            fontFamily: "'JetBrains Mono',monospace", fontSize: 14,
            color: TC.accent, fontWeight: 700, marginBottom: 10,
          }}, "$" + asset.val + "k"),
          h("button", {
            onClick: function () { buy(asset); },
            disabled: !affordable,
            style: {
              background: affordable ? "#052e16" : "#1e293b",
              border: "1px solid " + (affordable ? "#16a34a" : "#334155"),
              color: affordable ? "#4ade80" : TC.muted,
              padding: "7px 14px", borderRadius: 6,
              cursor: affordable ? "pointer" : "not-allowed",
              fontSize: 11, fontWeight: 700, letterSpacing: 1,
            }
          }, "BUY · $" + asset.val + "k"),
        ),
      );
    };

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "10 · THE SHOP",
        title: "The shop",
        subtitle: "Where you buy assets to grow your collection and boost your score. Always visible to all players.",
      }),

      h(Body, null,
        "The shop is a row of face-up assets available for purchase. At the start of the game it has a set number of slots. You can buy one asset per turn without spending an action, as long as you have at least $1k. The cost is deducted immediately and can push you into the negatives.",
      ),

      h("div", { style: {
        display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap",
        marginBottom: 16,
      }},
        h(MoneyCounter, { value: state.cash }),
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 11, color: TC.sub,
        }},
          h("span", { style: { color: TC.green, fontWeight: 700 } }, String(state.owned.length)),
          h("span", { style: { color: TC.muted } }, " owned"),
        ),
      ),

      state.message && h("div", {
        style: {
          fontSize: 12, color: TC.sub, padding: "8px 12px",
          background: "#0c1420", borderLeft: "2px solid " + TC.accent,
          marginBottom: 14, maxWidth: 640,
        }
      }, state.message),

      // Shop tiles
      h("div", { style: {
        fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
        color: TC.muted, marginBottom: 10,
      }}, "SHOP · " + state.shop.length + " SLOT" + (state.shop.length === 1 ? "" : "S")),

      h("div", {
        style: {
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
          gap: 12, marginBottom: 16,
        }
      },
        state.shop.length
          ? state.shop.map(function(a){ return ShopTile(a); })
          : h("div", {
              style: {
                padding: 20, textAlign: "center",
                color: TC.muted, fontSize: 12, fontStyle: "italic",
                background: "#0c1420", border: "1px dashed " + TC.chromeBorder, borderRadius: 10,
              }
            }, "Shop empty. Reset to start over."),
      ),

      // Action buttons
      h("div", { style: { display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 16 } },
        h("button", {
          onClick: refreshOrReset,
          style: {
            background: state.hasRefreshed ? "transparent" : "#1c1004",
            border: "1px solid " + (state.hasRefreshed ? TC.chromeBorder : TC.accentDim),
            color: state.hasRefreshed ? TC.muted : TC.accent,
            padding: "9px 16px", borderRadius: 8,
            cursor: "pointer", fontSize: 11, fontWeight: 700, letterSpacing: 1,
          }
        }, state.hasRefreshed ? "↻ RESET DEMO" : "↻ SIMULATE SHOP REFRESH"),
        h("button", {
          onClick: blindBuy,
          disabled: state.cash < 1,
          style: {
            background: state.cash >= 1 ? "#1e1b3a" : "#1e293b",
            border: "1px solid " + (state.cash >= 1 ? "#6d28d9" : "#334155"),
            color: state.cash >= 1 ? "#c4b5fd" : TC.muted,
            padding: "9px 16px", borderRadius: 8,
            cursor: state.cash >= 1 ? "pointer" : "not-allowed",
            fontSize: 11, fontWeight: 700, letterSpacing: 1,
          }
        }, "? BLIND BUY · $" + BLIND_BUY_COST + "K"),
      ),

      // Owned section
      state.owned.length > 0 && h("div", null,
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.green, marginBottom: 10,
        }}, "YOUR ASSETS"),
        h("div", { style: { display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 14 } },
          state.owned.map(function (a) {
            return h(CardDisplay, {
              key: a.id, card: a,
              onClick: function(){ ctx.setDetailCard(a); },
            });
          })
        ),
      ),

      h("div", { style: { marginBottom: 14 } },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: TC.muted, marginBottom: 8,
        }}, "SHOP REFRESHES"),
        h("div", { style: {
          background: "#0c1420", borderLeft: "3px solid " + TC.accent,
          padding: "10px 14px", fontSize: 12, color: TC.text, lineHeight: 1.6,
        }},
          "A shop refresh reshuffles all asset deck cards and adds one new slot. In a real game, refreshes happen in two ways: a card effect triggers one, or the main deck runs out of cards. The button above simulates this so you can see what it looks like."
        ),
      ),

      h(Callout, { color: TC.purple },
        h("strong", { style: { color: TC.purple } }, "Blind buy: "),
        "Pay $" + BLIND_BUY_COST + "k to draw the top card from the Asset Deck without looking at it first. It costs more than some shop assets but can net you something valuable. You can blind buy once per turn, same as a regular shop purchase.",
      ),
    );
  }

  /* --- Slide 10: A full turn (scripted walkthrough) --- */
  function Slide_Turn(props) {
    var ctx = props.ctx;
    // Five phases the player steps through
    var _st = useState(0);
    var phase = _st[0];
    var setPhase = _st[1];

    var phases = [
      {
        label: "START OF TURN", color: "#f59e0b",
        title: "Automatic bookkeeping",
        bullets: [
          "Pay any overdraft fees (if you're in the red)",
          "Adjust your fee tracker if needed",
          "All your USED assets reset to READY",
          "Passive assets with start-of-turn effects trigger",
          "Draw 1 card from the Main Deck",
        ],
        body: "You don't do any of this. The game handles it automatically.",
      },
      {
        label: "PLANNING PHASE", color: TC.blue,
        title: "You have 2 action points",
        bullets: [
          "Play an action card — costs 1 action point",
          "Sell a card for its face value — costs 1 action point",
          "Draw an extra card from the deck — costs 1 action point",
        ],
        body: "Free mode (default): spend your 2 points however you like. Play twice, sell twice, draw twice, or any mix — there are no per-type limits. Limited mode (opt-in in game settings): each action type can only be used once per turn, so you could play one card and sell one, but not play two. Some cards grant bonus action points on top of your base 2.",
      },
      {
        label: "FREE EXTRAS", color: TC.green,
        title: "No action cost",
        bullets: [
          "Buy one asset from the shop, or blind-buy from the deck (once per turn)",
          "Activate any DURING assets you own (no limit, each costs nothing)",
        ],
        body: "Buying assets and activating DURING assets are completely outside the action point system — they're free and don't count against your 2 points. You can buy before, during, or after using your action points.",
      },
      {
        label: "DECISION", color: TC.purple,
        title: "Your choice",
        bullets: [
          "React to opponents throughout the round",
          "Watch for passive triggers firing automatically",
          "Decide when you're ready to end the turn",
        ],
        body: "Reactions can happen anytime, not just during your turn. Stay alert even when it is someone else's turn.",
      },
      {
        label: "END TURN", color: TC.red,
        title: "Wrap up",
        bullets: [
          "If your hand has more than 5 cards, pick which to discard",
          "Turn passes to the next player",
        ],
        body: "Going over the hand limit is fine. You just have to decide what to lose, then play moves on.",
      },
    ];

    var cur = phases[phase];

    return h("div", null,
      h(SlideTitle, {
        eyebrow: "11 · A TURN, START TO FINISH",
        title: "One turn at a time",
        subtitle: "Each turn has five phases. Most are automatic. Only the planning phase requires a decision.",
      }),

      // Phase stepper
      h("div", { style: {
        display: "flex", gap: 6, marginBottom: 20, flexWrap: "wrap",
      }},
        phases.map(function (p, i) {
          var done = i < phase, active = i === phase;
          return h("button", {
            key: i,
            onClick: function () { setPhase(i); },
            style: {
              flex: "1 1 auto", minWidth: 110,
              background: active ? "#0c1420" : "transparent",
              borderTop: "3px solid " + (active ? p.color : done ? TC.chromeBorder : "#0e1624"),
              border: "1px solid " + (active ? p.color : TC.chromeBorder),
              color: active ? p.color : done ? TC.sub : TC.muted,
              padding: "10px 8px 8px 8px", cursor: "pointer",
              fontFamily: "'JetBrains Mono',monospace", fontSize: 9,
              fontWeight: 700, letterSpacing: 1,
              transition: "all 0.2s ease",
            }
          },
            h("div", null, String(i + 1) + ". " + p.label)
          );
        })
      ),

      // Phase content
      h("div", {
        key: phase,
        style: {
          background: "#0c1420", border: "2px solid " + cur.color,
          borderRadius: 12, padding: 22,
          animation: "popIn 0.25s ease",
          marginBottom: 18, maxWidth: 720,
        }
      },
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: 2,
          color: cur.color, fontWeight: 700, marginBottom: 6,
        }}, "PHASE " + (phase + 1) + " · " + cur.label),
        h("div", { style: {
          fontFamily: "'Rubik',sans-serif", fontSize: 20, fontWeight: 700,
          color: TC.text, marginBottom: 14, lineHeight: 1.2,
        }}, cur.title),
        h("ul", { style: {
          listStyle: "none", padding: 0, margin: "0 0 14px 0",
        }},
          cur.bullets.map(function (b, i) {
            return h("li", {
              key: i,
              style: {
                padding: "6px 0 6px 20px", position: "relative",
                fontSize: 13, color: TC.text, lineHeight: 1.5,
              }
            },
              h("span", { style: {
                position: "absolute", left: 0, top: 11,
                width: 8, height: 2, background: cur.color,
              }}),
              b
            );
          })
        ),
        h("div", { style: { fontSize: 12, color: TC.sub, lineHeight: 1.6, fontStyle: "italic" } }, cur.body),
      ),

      // Step nav
      h("div", { style: { display: "flex", gap: 10 } },
        h("button", {
          onClick: function () { if (phase > 0) setPhase(phase - 1); },
          disabled: phase === 0,
          style: {
            background: "#1e293b", border: "1px solid " + TC.chromeBorder,
            color: TC.sub, padding: "8px 18px", borderRadius: 8,
            cursor: phase === 0 ? "not-allowed" : "pointer",
            opacity: phase === 0 ? 0.4 : 1,
            fontSize: 11, fontWeight: 600, letterSpacing: 1,
          }
        }, "← PREV PHASE"),
        h("button", {
          onClick: function () { if (phase < phases.length - 1) setPhase(phase + 1); },
          disabled: phase === phases.length - 1,
          style: {
            background: phase === phases.length - 1 ? "#1e293b" : "#1c1004",
            border: "1px solid " + (phase === phases.length - 1 ? TC.chromeBorder : TC.accentDim),
            color: phase === phases.length - 1 ? TC.muted : TC.accent,
            padding: "8px 18px", borderRadius: 8,
            cursor: phase === phases.length - 1 ? "not-allowed" : "pointer",
            opacity: phase === phases.length - 1 ? 0.4 : 1,
            fontSize: 11, fontWeight: 700, letterSpacing: 1,
          }
        }, "NEXT PHASE →"),
      ),
    );
  }

  /* ══════════════════════════════════════════════════════════
     SLIDE REGISTRY
     Order = tutorial order. Add/remove here to restructure.
     ══════════════════════════════════════════════════════════ */

  var SLIDES = [
    { id: "welcome",    section: "START",       title: "Welcome",               Component: Slide_Welcome },
    { id: "components", section: "START",       title: "Components",            Component: Slide_Components },
    { id: "ui",         section: "START",       title: "Your screen",           Component: Slide_MobileUI },
    { id: "actions",    section: "CARDS",       title: "Action cards",          Component: Slide_Actions },
    { id: "reactions",  section: "CARDS",       title: "Reaction cards",        Component: Slide_Reactions },
    { id: "window",     section: "CARDS",       title: "Reaction window",       Component: Slide_ReactionWindow },
    { id: "stack",      section: "CARDS",       title: "Stacked reactions",     Component: Slide_Stack },
    { id: "assets",     section: "CARDS",       title: "Asset cards",           Component: Slide_Assets },
    { id: "selling",    section: "GAMEPLAY",    title: "Selling a card",        Component: Slide_Selling },
    { id: "shop",       section: "GAMEPLAY",    title: "The shop",              Component: Slide_Shop },
    { id: "turn",       section: "GAMEPLAY",    title: "A full turn",           Component: Slide_Turn },
    { id: "fees",       section: "GAMEPLAY",    title: "Fees & bankruptcy",     Component: Slide_Fees },
    { id: "ready",      section: "FINISH",      title: "You're ready",          Component: Slide_Ready },
  ];

  /* ══════════════════════════════════════════════════════════
     PROGRESS BAR
     ══════════════════════════════════════════════════════════ */

  function ProgressBar(props) {
    var pct = ((props.current + 1) / props.total) * 100;
    return h("div", {
      style: {
        position: "relative", height: 3, background: "#1e293b",
        width: "100%", overflow: "hidden",
      }
    },
      h("div", {
        style: {
          height: "100%", width: pct + "%",
          background: TC.accent,
          transition: "width 0.3s ease",
        }
      })
    );
  }

  /* ══════════════════════════════════════════════════════════
     SLIDE DOT INDICATOR (bottom, shows section groupings)
     ══════════════════════════════════════════════════════════ */

  function SlideDots(props) {
    return h("div", {
      style: { display: "flex", gap: 6, alignItems: "center", justifyContent: "center" }
    },
      SLIDES.map(function (s, i) {
        var isActive = i === props.current;
        var isDone = i < props.current;
        return h("button", {
          key: s.id,
          onClick: function () { props.onJump(i); },
          title: s.title,
          style: {
            width: isActive ? 22 : 8, height: 8, borderRadius: 4,
            background: isActive ? TC.accent : isDone ? TC.accentDim : TC.pillOff,
            border: "none", cursor: "pointer", padding: 0,
            transition: "all 0.2s ease",
          }
        });
      })
    );
  }

  /* ══════════════════════════════════════════════════════════
     MAIN TUTORIAL COMPONENT
     ══════════════════════════════════════════════════════════ */

  function Tutorial(props) {
    // Hydrate current slide from localStorage (gracefully fall back to 0)
    var initial = 0;
    try {
      var raw = localStorage.getItem(STORAGE_KEY);
      if (raw != null) {
        var n = parseInt(raw, 10);
        if (!isNaN(n) && n >= 0 && n < SLIDES.length) initial = n;
      }
    } catch (e) {}

    var _cur = useState(initial);
    var current = _cur[0];
    var setCurrent = _cur[1];

    var _detail = useState(null);
    var detailCard = _detail[0];
    var setDetailCard = _detail[1];

    var scrollRef = useRef(null);

    // Persist progress to localStorage on change
    useEffect(function () {
      try { localStorage.setItem(STORAGE_KEY, String(current)); } catch (e) {}
    }, [current]);

    // Close detail modal and scroll to top when slide changes
    useEffect(function () {
      setDetailCard(null);
      if (scrollRef.current) scrollRef.current.scrollTop = 0;
    }, [current]);

    // Keyboard navigation: arrow keys + escape
    useEffect(function () {
      function onKey(e) {
        if (detailCard) {
          if (e.key === "Escape") { setDetailCard(null); }
          return;
        }
        if (e.key === "ArrowRight" && current < SLIDES.length - 1) setCurrent(current + 1);
        else if (e.key === "ArrowLeft" && current > 0) setCurrent(current - 1);
        else if (e.key === "Escape") props.onExit();
      }
      window.addEventListener("keydown", onKey);
      return function () { window.removeEventListener("keydown", onKey); };
    }, [current, detailCard]);

    var slide = SLIDES[current];
    var SlideComp = slide.Component;
    var ctx = {
      setDetailCard: setDetailCard,
      onRestart: function() {
        setCurrent(0);
        try { localStorage.setItem(STORAGE_KEY, "0"); } catch(e) {}
      },
    };

    var isFirst = current === 0;
    var isLast = current === SLIDES.length - 1;

    return h("div", {
      style: {
        position: "fixed", inset: 0, background: TC.pageBg,
        color: TC.text, display: "flex", flexDirection: "column",
        fontFamily: "'Inter', system-ui, -apple-system, sans-serif",
        overflow: "hidden",
      }
    },
      // Inject the game's CSS so animations (popIn, etc.) work
      h("style", null, CSS),

      /* ─── HEADER ─────────────────────────────────────── */
      h("div", {
        style: {
          padding: "14px 22px",
          borderBottom: "1px solid " + TC.chromeBorder,
          background: TC.chromeBg,
          display: "flex", alignItems: "center", gap: 16,
          flexShrink: 0,
        }
      },
        // Left: title
        h("div", { style: { display: "flex", alignItems: "center", gap: 10, flex: 1, minWidth: 0 } },
          h("div", {
            style: {
              fontFamily: "'Rubik',sans-serif", fontSize: 13, fontWeight: 900,
              whiteSpace: "nowrap",
            }
          },
            h("span", { style: { color: "#ef4444" } }, "STRICTLY "),
            h("span", { style: { color: "#22c55e" } }, "BUSINESS"),
          ),
          h("div", {
            style: {
              fontFamily: "'JetBrains Mono',monospace", fontSize: 10,
              letterSpacing: 2, color: TC.muted,
              paddingLeft: 10, borderLeft: "1px solid " + TC.chromeBorder,
            }
          }, "HOW TO PLAY"),
        ),

        // Center: slide counter
        h("div", { style: {
          fontFamily: "'JetBrains Mono',monospace", fontSize: 11, color: TC.sub,
          whiteSpace: "nowrap",
        }},
          h("span", { style: { color: TC.accent, fontWeight: 700 } }, String(current + 1).padStart(2, "0")),
          h("span", { style: { color: TC.muted } }, " / " + String(SLIDES.length).padStart(2, "0")),
        ),

        // Right: exit
        h("button", {
          onClick: props.onExit,
          style: {
            background: "transparent", border: "1px solid " + TC.chromeBorder,
            color: TC.sub, padding: "6px 14px", borderRadius: 6,
            cursor: "pointer", fontSize: 11, fontWeight: 600,
            letterSpacing: 1, flexShrink: 0,
          }
        }, "✕ EXIT"),
      ),

      /* ─── PROGRESS BAR ────────────────────────────────── */
      h(ProgressBar, { current: current, total: SLIDES.length }),

      /* ─── CONTENT ────────────────────────────────────── */
      h("div", {
        ref: scrollRef,
        style: {
          flex: 1, overflowY: "auto", overflowX: "hidden",
          padding: "28px 32px 32px 32px",
        }
      },
        h("div", {
          key: slide.id,  // force remount on slide change so animations replay
          style: { maxWidth: 800, margin: "0 auto", animation: "popIn .3s ease" }
        }, h(SlideComp, { ctx: ctx }))
      ),

      /* ─── FOOTER NAV ─────────────────────────────────── */
      h("div", {
        style: {
          padding: "14px max(22px, env(safe-area-inset-right, 22px)) max(14px, env(safe-area-inset-bottom, 14px)) max(22px, env(safe-area-inset-left, 22px))",
          borderTop: "1px solid " + TC.chromeBorder,
          background: TC.chromeBg, display: "flex", alignItems: "center",
          gap: 16, flexShrink: 0, boxSizing: "border-box",
        }
      },
        // Prev
        h("button", {
          onClick: function () { if (!isFirst) setCurrent(current - 1); },
          disabled: isFirst,
          style: {
            background: isFirst ? "transparent" : "#1e293b",
            border: "1px solid " + (isFirst ? "#1e293b" : "#334155"),
            color: isFirst ? TC.muted : TC.sub,
            padding: "9px 18px", borderRadius: 8,
            cursor: isFirst ? "not-allowed" : "pointer",
            fontSize: 12, fontWeight: 600, letterSpacing: 1,
            opacity: isFirst ? 0.4 : 1,
          }
        }, "← PREV"),

        // Dots
        h("div", { style: { flex: 1, display: "flex", justifyContent: "center" } },
          h(SlideDots, { current: current, onJump: setCurrent })
        ),

        // Next / Finish
        isLast
          ? h("button", {
              onClick: props.onExit,
              style: {
                background: "#052e16", border: "2px solid #16a34a",
                color: "#4ade80", padding: "9px 22px", borderRadius: 8,
                cursor: "pointer", fontSize: 12, fontWeight: 700, letterSpacing: 1,
                flexShrink: 0, whiteSpace: "nowrap",
              }
            }, "FINISH ✓")
          : h("button", {
              onClick: function () { setCurrent(current + 1); },
              style: {
                background: "#1c1004", border: "1px solid " + TC.accentDim,
                color: TC.accent, padding: "9px 22px", borderRadius: 8,
                cursor: "pointer", fontSize: 12, fontWeight: 700, letterSpacing: 1,
                flexShrink: 0, whiteSpace: "nowrap",
              }
            }, "NEXT →"),
      ),

      /* ─── DETAIL MODAL ──────────────────────────────── */
      detailCard && h(TutorialDetailModal, {
        card: detailCard,
        onClose: function () { setDetailCard(null); }
      }),
    );
  }

  // Attach back to SB_EXPORTS for game.jsx's App to pick up
  window.SB_EXPORTS.Tutorial = Tutorial;

})();
