// Sample content per layout type, used by both the editors (as default values)
// and the live PlayerPreview. Field names match catalogue §1/§3 exactly.
//
// One entry per layout type. Each entry is the layout's full data object —
// the structure the Player would read out of <layout> in content.xml.
//
// To keep the preview clean, image fields use solid color/gradient backgrounds
// keyed by purpose; no real photography is required.

// HTML media samples for quiz_images / hidden_items mini-quizzes live at
// uploads/html-samples/ and are catalogued in src/data.jsx (the HTML_SAMPLES
// export). Eight ORIGINAL, brand-free awareness samples authored for the
// OQ-077 spike — phishy emails, an oversharing social post, a fake login
// screen, a smishing text, etc. (No real organisation or product is
// reproduced; the source DFTI assets are not redistributed here.) The
// quiz_images sample below points its HTML question at the typical sample
// (module_4/03). See the catalogue notes in data.jsx for per-sample rationale.
const LAYOUT_CONTENT_SAMPLES = {

  // ─── 3.1 fullscreen_text_and_image ─────────────────────────────────────
  fullscreen_text_and_image: {
    type: 'fullscreen_text_and_image',
    titleMain: { en: 'When does a conversation become difficult?' },
    titleTextColor: '#ffffff',
    text: { en: "It rarely starts with the words. It starts with the temperature shift — the moment the room tightens and one of you starts choosing words more carefully than usual." },
    textColor: '#ffffff',
    backgroundImageUrl: 'placeholder:scene-tense-room',
    blockingSection: { state: 'false', color: '#0f172a', textColor: '#cccccc' },
  },

  // ─── 3.2 fullscreen_video ──────────────────────────────────────────────
  fullscreen_video: {
    type: 'fullscreen_video',
    titleMain: { en: 'Sarah & Jordan' },
    titleSub: { en: 'Watch the full scenario before answering the question that follows.' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: 'rgba(0,0,0,0.4)',
    /* Start blank — author uploads / generates the video themselves from the
       VideoMediaBlock toolbar. In-video overlays only surface once a video
       exists, so the inspector doesn't lead with a section the user can't
       actually use yet. */
    videoUrl: '',
    videoThumbUrl: '',
    subtitlesUrl: '',
    transcriptUrl: '',
    blockingSection: { state: 'true', color: '#0f172a', textColor: '#ffffff' },
    interactions: [],
  },

  // ─── 3.3 text_and_image ────────────────────────────────────────────────
  text_and_image: {
    type: 'text_and_image',
    titleMain: { en: "Five signals it's becoming difficult" },
    titleSub: { en: "Each row pairs the signal with what it tends to mean." },
    titleTextColor: '#0f172a',
    titleBackgroundColor: '#ffffff',
    blockingSection: { state: 'false' },
    rows: [
      { kind: 'image', backgroundColor: '#ffffff', imageUrl: 'placeholder:scene-volume',
        text: { en: "When the volume rises in the room." }, textColor: '#0f172a', imageOnTheLeft: true },
      { kind: 'image', backgroundColor: '#f8fafc', imageUrl: 'placeholder:scene-eyecontact',
        text: { en: "When two people stop making eye contact." }, textColor: '#0f172a', imageOnTheLeft: false },
      { kind: 'video', backgroundColor: '#ffffff', videoUrl: 'content/silence.mp4',
        videoThumbUrl: 'placeholder:video-silence',
        text: { en: "When silence stretches longer than feels natural." }, textColor: '#0f172a', imageOnTheLeft: true },
    ],
  },

  // ─── 3.4 small_video ───────────────────────────────────────────────────
  small_video: {
    type: 'small_video',
    titleMain: { en: 'SME interview · Dr. Priya Naidu' },
    titleSub: { en: '02:14 · what cues do senior managers actually catch first?' },
    titleTextColor: '#0f172a',
    /* External background — the strip around the small video inside the
       layout. Surfaced as the "External bg" swatch in the Video media
       block's header (see docs/video-media-patterns.md §10). */
    contentBackgroundColor: '#a0a8b0',
    videoUrl: 'content/en/modules/m1/03/priya.mp4',
    videoThumbUrl: 'placeholder:video-priya',
    subtitlesUrl: 'content/en/modules/m1/03/priya.txt',
    blockingSection: { state: 'false' },
    /* Three in-video overlays demonstrating each marker type, so the
       inspector (docs/video-media-patterns.md §11) has something to surface
       on first open. Question is selected by default below — see the
       SmallVideoEditor3's controlled selectedIndex if/when wired up. */
    interactions: [
      { id: 'iv_disc', type: 'discover',
        timeStarts: 42, timeEnds: 58,
        interactionText: { en: 'Watch this' },
        descriptionTitle: { en: 'SBI in action' },
        descriptionText: { en: 'Notice how she names the situation first, before naming the behaviour.' } },
      { id: 'iv_q',    type: 'question',
        timeStarts: 67,
        interactionText: { en: 'Your turn' },
        text: { en: 'The question to surface to the learner at this moment.' },
        options: [
          { text: { en: 'Option A' }, isCorrect: true },
          { text: { en: 'Option B' } },
        ] },
      { id: 'iv_mand', type: 'mandatoryQuestion',
        timeStarts: 73,
        interactionText: { en: 'Answer to continue' },
        text: { en: '' },
        options: [
          { text: { en: '' }, isCorrect: true },
          { text: { en: '' } },
        ] },
    ],
  },

  // ─── 3.5 sequence ──────────────────────────────────────────────────────
  sequence: {
    type: 'sequence',
    titleMain: { en: 'Sarah & Jordan: a feedback scenario' },
    titleSub: { en: "How to use SBI when you'd rather not have the conversation" },
    titleTextColor: '#ffffff',
    titleBackgroundColor: 'rgba(0,0,0,0.5)',
    contentBackgroundColor: '#000000',
    dotSelectedColor: '#FFD166',
    blockingSection: { state: 'true' },
    bgColor: '#ffffff',
    textColor: '#0f172a',
    btnBgColor: '#0f172a',
    btnTextColor: '#ffffff',
    tabs: [
      { id: 't1', mobileText: { en: 'Set-up' }, tabTitle: { en: 'The missed deadline' }, kind: 'text',
        imageUrl: 'placeholder:scene-office',
        tabText: { en: 'Sarah notices Jordan has missed two consecutive deliverables. The team is starting to whisper.' } },
      { id: 't2', mobileText: { en: 'Prep' }, tabTitle: { en: 'Sarah prepares' }, kind: 'video',
        videoUrl: 'content/m2-03/02.mp4', videoThumbUrl: 'placeholder:video-sarah-prep',
        interactions: [] },
      /* Quiz step — new shape (kind:'question' with options[]), same as
         in-video Question interactions. See docs/video-media-patterns.md
         §11 and §13. */
      { id: 't3', mobileText: { en: 'Decide' }, tabTitle: { en: 'Which opener works best?' }, kind: 'question',
        imageUrl: 'placeholder:scene-office',
        text: { en: 'Pick the opener that uses SBI most cleanly.' },
        options: [
          { text: { en: '"When you missed the API spec review on Tuesday…"' },
            isCorrect: true,
            feedback: { en: 'Correct — that names the situation and the behaviour precisely.' } },
          { text: { en: '"You\'ve been disengaged from the team lately."' },
            isCorrect: false,
            feedback: { en: 'That\'s a judgement, not a behaviour — SBI asks you to name what you observed.' } },
          { text: { en: '"Your work has been below expectations."' },
            isCorrect: false,
            feedback: { en: 'Vague; the learner can\'t tell what specifically to change.' } },
        ] },
      { id: 't4', mobileText: { en: 'Feedback' }, tabTitle: { en: "That's an SBI opener" }, kind: 'feedback',
        imageUrl: 'placeholder:scene-office',
        text: { en: 'Naming the specific behaviour without judgement is the SBI core move.' },
        forAnswerId: '1' },
      { id: 't5', mobileText: { en: 'The talk' }, tabTitle: { en: 'The conversation' }, kind: 'video',
        videoUrl: 'content/m2-03/05.mp4', videoThumbUrl: 'placeholder:video-talk',
        interactions: [] },
      { id: 't6', mobileText: { en: 'Wrap' }, tabTitle: { en: 'After the talk' }, kind: 'text',
        imageUrl: 'placeholder:scene-handshake',
        tabText: { en: 'Both walk away with a concrete next step and a follow-up scheduled.' } },
    ],
  },

  // ─── 3.6 horizontal_tabs ───────────────────────────────────────────────
  horizontal_tabs: {
    type: 'horizontal_tabs',
    titleMain: { en: 'CLEAR vs SBI' },
    titleSub: { en: 'When does each framework serve the conversation better?' },
    titleTextColor: '#0f172a',
    selectedBgColor: '#D32F2F',
    selectedTextColor: '#ffffff',
    tabTextColor: '#475569',
    blockingSection: { state: 'false' },
    tabs: [
      { tabTitle: { en: 'Coaching' }, tabText: { en: 'CLEAR is built for the coaching context — when the other person needs space to think, not to be told.' }, imageUrl: 'placeholder:scene-coaching' },
      { tabTitle: { en: 'Giving' }, tabText: { en: 'SBI is built for giving feedback — specific behaviour, specific impact, specific moment.' }, imageUrl: 'placeholder:scene-giving' },
      { tabTitle: { en: 'Receiving' }, tabText: { en: 'When receiving, the same scaffolds work in reverse — ask for the situation, the behaviour, then the impact.' }, imageUrl: 'placeholder:scene-receiving' },
    ],
  },

  // ─── 3.7 vertical_tabs ─────────────────────────────────────────────────
  vertical_tabs: {
    type: 'vertical_tabs',
    titleMain: { en: 'Government Dealings' },
    titleSub: { en: 'We respect the political processes in each country where we operate.' },
    titleTextColor: '#0f172a',
    contentBackgroundColor: '#B8932D',
    backgroundImageUrl: '',
    blockingSection: { state: 'false' },
    tabs: [
      { tabTitle: { en: 'Payments to Government Officials' },
        tabText: { en: "We don't offer or accept any bribes or benefits to officials or colleagues of a political party, government or government body. If any official asks you for such a payment or benefit, you should refuse and tell your line manager or the Director of Finance or P&C right away." } },
      { tabTitle: { en: 'Political Activities' },
        tabText: { en: "We respect the political opinions of every colleague, but the company itself does not engage in party-political activity. Personal activity must stay personal and outside work time." } },
    ],
  },

  // ─── 3.8 icons_discover ────────────────────────────────────────────────
  icons_discover: {
    type: 'icons_discover',
    titleMain: { en: 'Diversity and Inclusion' },
    titleSub: { en: 'At Mandarin Oriental Hotel Group, we have colleagues from all over the world with different backgrounds, experiences, and cultures. This makes us a strong and diverse team.' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#3F4E66',
    contentBackgroundColor: '#3F4E66',
    selectedBgColor: '#B8932D',
    blockingSection: { state: 'false' },
    icons: [
      { imageUrl: 'placeholder:icon-people',
        text: { en: 'We value the physical and mental health, safety and well-being of our colleagues. This is key to the success of MOHG.' } },
      { imageUrl: 'placeholder:icon-mind',
        text: { en: "We embrace different ways of thinking and seeing the world." } },
      { imageUrl: 'placeholder:icon-gear',
        text: { en: "We build inclusive systems and processes — not exceptions." } },
      { imageUrl: 'placeholder:icon-headset',
        text: { en: "We listen actively, then act on what we hear." } },
    ],
  },

  // ─── 3.9a hidden_items_flat_image ───────────────────────────────────────
  hidden_items_flat_image: {
    type: 'hidden_items',
    titleMain: { en: 'Fire awareness' },
    titleSub: { en: "It's important to remember that fires can start easily and spread rapidly with devastating costs to both business organisations and our personal lives. Select each item to learn more." },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#5514B4',
    contentBackgroundColor: '#5514B4',
    imageUrl: 'placeholder:scene-office-fire',
    blockingSection: { state: 'true' },
    bgColor: '#ffffff',
    borderColor: '#FF80FF',
    itemColor: '#FF80FF',
    textColor: '#0f172a',
    items: [
      { itemPosition: '64,40', itemTitle: { en: 'Blocked exit' }, itemText: { en: 'Doors must never be wedged open or obstructed — they are the only safe escape route in a fire.' } },
      { itemPosition: '38,58', itemTitle: { en: 'Cables under desk' }, itemText: { en: 'Loose cabling under a desk is a tripping hazard and a fire risk if it overheats.' } },
      { itemPosition: '40,82', itemTitle: { en: 'Cardboard near socket' }, itemText: { en: 'Combustible storage should never sit beside an electrical outlet.' } },
      { itemPosition: '20,38', itemTitle: { en: 'Portable heater' }, kind: 'question',
        itemText: { en: 'A portable heater has been left running next to a stack of paper. What should you do?' },
        answers: [
          { text: { en: 'Switch it off and move the paper away' }, isCorrect: true,
            feedback: { en: 'Correct — an unattended heat source beside combustibles is a serious fire risk.' } },
          { text: { en: 'Leave it — modern heaters cut out automatically' }, isCorrect: false,
            feedback: { en: 'Not quite — never rely on cut-outs near combustible material.' } },
        ] },
    ],
  },

  // ─── 3.9b hidden_items_360_image ────────────────────────────────────────
  hidden_items_360_image: {
    type: 'hidden_items',
    titleMain: { en: 'Spot the hazards · 360° walkthrough' },
    titleSub: { en: 'Drag to look around the kitchen, then tap each hotspot to identify the hazard.' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#5514B4',
    contentBackgroundColor: '#5514B4',
    image360Url: 'placeholder:panorama-kitchen',
    image360CoverText: { en: 'Drag to look around' },
    blockingSection: { state: 'true' },
    bgColor: '#ffffff',
    borderColor: '#FF80FF',
    itemColor: '#FF80FF',
    textColor: '#0f172a',
    items: [
      { itemPosition: '50,50', itemTitle: { en: 'Unattended hob' },    itemText: { en: 'A pan left on a lit hob is a leading cause of kitchen fires.' } },
      { itemPosition: '40,55', itemTitle: { en: 'Tea-towel near flame' }, itemText: { en: 'Fabric near an open flame can ignite in seconds.' } },
      { itemPosition: '60,52', itemTitle: { en: 'Overloaded socket' }, itemText: { en: 'Multiple high-draw appliances on one socket can overheat the wiring.' } },
    ],
  },

  // ─── 3.9 hidden_items (legacy alias — surface-layout-editor-3 demo) ─────
  // Older static demo screens (and any pre-split saved layouts) still
  // look up LAYOUT_CONTENT_SAMPLES.hidden_items. Point it at the flat
  // variant so nothing breaks while authors migrate to the explicit ids.
  get hidden_items() { return this.hidden_items_flat_image; },

  // ─── 3.10 quiz_images ──────────────────────────────────────────────────
  quiz_images: {
    type: 'quiz_images',
    titleMain: { en: 'Quiz — Inclusive or Accessible?' },
    titleSub: { en: "A genuinely inclusive environment is more than just providing access. Indicate which image shows the genuinely inclusive solution rather than the solution that merely provides access." },
    titleTextColor: '#ffffff',
    contentBackgroundColor: '#000000',
    contentTextColor: '#ffffff',
    selectedBgColor: '#F26430',
    selectedTextColor: '#ffffff',
    dotSelectedColor: '#F26430',
    blockingSection: { state: 'true' },
    questions: [
      { text: { en: 'What option below delivers genuine inclusion, the revolving doors or the sliding double doors?' },
        content: { image: { imageUrl: 'placeholder:scene-doors-split' } },
        answers: [
          { text: { en: 'Sliding doors' }, isCorrect: true,
            feedback: { en: 'The sliding doors are a single entry for all users.' } },
          { text: { en: 'Revolving doors' }, isCorrect: false,
            feedback: { en: 'Not quite — look again at which option works for everyone without a separate route.' } },
        ] },
      { text: { en: 'Which kitchen setup includes everyone?' },
        content: { image: { imageUrl: 'placeholder:scene-kitchen' } },
        answers: [
          { text: { en: 'Adjustable-height counter' }, isCorrect: true,
            feedback: { en: 'One shared kitchen with adjustable counters means nobody is set apart.' } },
          { text: { en: 'Two separate kitchens' }, isCorrect: false,
            feedback: { en: 'Not quite — separate facilities set people apart.' } },
        ] },
      // Hotspot question: the pins are DISCOVERY elements (tap to reveal extra
      // context in the image) — NOT answers. The question is answered via the
      // separate answers + feedback below.
      { text: { en: 'Explore the entrance, then choose which option works for everyone without a separate route.' },
        content: { image: { imageUrl: 'placeholder:scene-doors-split',
          itemColor: '#FF80FF', bgColor: '#ffffff', borderColor: '#FF80FF', textColor: '#0f172a',
          items: [
            { id: 'h1', kind: 'text', itemPosition: '30,55', itemTitle: { en: 'Sliding doors' },
              itemText: { en: 'A single wide entry that opens automatically — usable by everyone at once.' } },
            { id: 'h2', kind: 'text', itemPosition: '70,52', itemTitle: { en: 'Revolving doors' },
              itemText: { en: 'Narrow segmented bays that are hard to use with a wheelchair, pram or luggage.' } },
          ] } },
        answers: [
          { id: '1', text: { en: 'Sliding doors' }, isCorrect: true,
            feedback: { en: 'Right — one shared entry serves everyone.' } },
          { id: '2', text: { en: 'Revolving doors' }, isCorrect: false,
            feedback: { en: 'These exclude wheelchair and pram users — look for the shared entry.' } },
        ] },
      // HTML-media question — the question media is an embedded HTML document
      // (nested content.html, mirroring content.image / content.video) rather
      // than an image. Points at the typical brand-free phishy-email sample;
      // see src/data.jsx HTML_SAMPLES.
      { text: { en: 'Read the embedded email below. What is the strongest sign it is phishy?' },
        content: { html: {
          htmlUrl: 'uploads/html-samples/content/en/modules/module_4/03.html',
          htmlAlt: { en: 'A suspicious email from a "Freight Express" billing address asking you to download an invoice attachment and reply with your full name, address and phone number to release a parcel.' } } },
        answers: [
          { text: { en: "Sender's email address" }, isCorrect: false,
            feedback: { en: "The sender address is one signal — but here it's not the strongest." } },
          { text: { en: 'Bad grammar' }, isCorrect: false,
            feedback: { en: 'The grammar is fine in this one — not the right reason.' } },
          { text: { en: 'Request for personal information' }, isCorrect: true,
            feedback: { en: 'Right — it asks you to reply with your name, address and phone number. That request for personal information is the classic phishing tell.' } },
        ] },
    ],
  },

  // ─── 3.10b quiz_gaming ─────────────────────────────────────────────────
  // Same question shape as quiz_images, plus the gaming wrapper: a running
  // correct/wrong counter header and start / win / fail screens. When
  // countAsPostAssessment is on, passing completes the course.
  quiz_gaming: {
    type: 'quiz_gaming',
    titleMain: { en: 'Challenge — Spot the inclusive choice' },
    titleSub: { en: 'Answer each question to build your score. Reach the pass threshold to complete the course.' },
    titleTextColor: '#ffffff',
    contentBackgroundColor: '#0b1020',
    contentTextColor: '#ffffff',
    selectedBgColor: '#F26430',
    selectedTextColor: '#ffffff',
    dotSelectedColor: '#F26430',
    blockingSection: { state: 'true' },
    showCorrectWrongAnswers: true,
    countAsPostAssessment: true,
    correctAnswersNeeded: 2,
    questions: [
      { text: { en: 'Which entrance works for the most people without a separate route?' },
        content: { image: { imageUrl: 'placeholder:scene-doors-split' } },
        answers: [
          { text: { en: 'Sliding doors' }, isCorrect: true, feedback: { en: 'Right — one entry serves everyone.' } },
          { text: { en: 'Revolving doors' }, isCorrect: false, feedback: { en: 'These exclude wheelchair and pram users.' } },
        ] },
      { text: { en: 'Which kitchen setup includes everyone?' },
        content: { image: { imageUrl: 'placeholder:scene-kitchen' } },
        answers: [
          { text: { en: 'Adjustable-height counter' }, isCorrect: true, feedback: { en: 'Right — one space adapts to all.' } },
          { text: { en: 'Two separate kitchens' }, isCorrect: false, feedback: { en: 'Separate facilities set people apart.' } },
        ] },
      // HTML-media question whose template (Phishy email — typical) exposes
      // tooltip slots. The author-filled `tooltipValues` are localised on the
      // Localisation surface's Quiz Gaming tab; the slot labels come from the
      // template's tooltipParams (see src/html-templates.jsx).
      { text: { en: 'Read the embedded email. What is the strongest sign it is phishy?' },
        content: { html: {
            htmlUrl: 'uploads/html-samples/content/en/modules/module_4/03.html',
            htmlAlt: { en: 'A suspicious "Freight Express" invoice email asking you to reply with your full name, address and phone number to release a parcel.' } },
          _templateId: 'tpl-phishy-email-typical',
          tooltipValues: {
            sender_domain: { en: 'The display name says "Freight Notifications", but the real address is a free mailbox — not a company domain.' },
            personal_info_request: { en: 'Asking you to reply with your name, address and phone number by email is a classic phishing pattern.' },
            suspicious_link: { en: 'The button is labelled "invoice" but points at an unfamiliar link — hover before you ever click.' },
            urgency_deadline: { en: 'A "within 24 hours" deadline is designed to make you act before you think.' },
          } },
        answers: [
          { text: { en: 'It asks you to reply with personal details' }, isCorrect: true, feedback: { en: 'Right — the request for personal information is the strongest tell.' } },
          { text: { en: 'The logo looks slightly off' }, isCorrect: false, feedback: { en: 'The branding isn\'t the giveaway here — look at what it asks you to do.' } },
        ] },
    ],
    gamingHeader: { enabled: true,
      imgUrlCorrect: 'placeholder:icon-correct', imgUrlWrong: 'placeholder:icon-wrong' },
    gamingStartScreen: { title: { en: 'Ready for a challenge?' },
      titleColor: '#FF80FF', bgColor: '#3b1d63',
      body: { en: 'Answer {totalAnswers} questions. Get {neededAnswers} right to pass.' },
      bgImgUrl: 'placeholder:gaming-start' },
    gamingEndScreens: {
      win: { title: { en: 'Challenge complete' }, titleColor: '#FF80FF', bgColor: '#3b1d63',
        body: { en: 'You answered {neededAnswers} of {totalAnswers} correctly — well done.' },
        bgImgUrl: 'placeholder:gaming-win' },
      failure: { title: { en: 'Not quite' }, titleColor: '#FF80FF', bgColor: '#3b1d63',
        body: { en: 'You missed {wrongAnswers}. Review the module and try again.' },
        bgImgUrl: 'placeholder:gaming-fail' },
    },
  },

  // ─── 3.11 object_viewer ────────────────────────────────────────────────
  // Player consumes only two scene-level tags: <backgroundColor> and
  // <objectUrl>. The other fields here are convenience state for the
  // editor (hotspot list, panel chrome, behaviour toggles) — keep the
  // shape minimal at the scene level; everything else hangs off items.
  object_viewer: {
    type: 'object_viewer',
    titleMain: { en: '3D Product Overview' },
    titleSub: { en: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pretium lobortis felis, et cursus urna luctus ac. Duis ultrices enim molestie nibh venenatis commodo.' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#0f172a',
    textColor: '#0f172a',
    backgroundColor: '#000000',
    objectUrl: 'content/dfti/models/centrifuge.glb',
    objectAltText: { en: 'Interactive 3D product model — rotate and zoom to explore.' },
    initialCoverText: { en: 'Drag to explore' },
    mobileArEnabled: true,
    disableZoom: false,
    autoRotate: false,
    disablePan: false,
    disableInteractionPrompt: false,
    bgColor: '#ffffff',
    borderColor: '#D32F2F',
    itemColor: '#D32F2F',
    blockingSection: { state: 'true' },
    items: [
      { kind: 'text', itemPosition: '0.4,0.5,0.2', itemTitle: { en: 'Lid latch' },
        itemText: { en: 'The latch must click fully closed before the rotor will spin.' },
        itemImageUrl: 'placeholder:hotspot-image' },
      { kind: 'text', itemPosition: '0.5,0.2,0.3', itemTitle: { en: 'Rotor' },
        itemText: { en: 'The 6-tube rotor; check balance before every run.' },
        itemImageUrl: 'placeholder:hotspot-image' },
      { kind: 'text', itemPosition: '0.6,0.6,0.2', itemTitle: { en: 'Control dial' },
        itemText: { en: 'Set RPM and time here. Hold to confirm.' },
        itemImageUrl: 'placeholder:hotspot-image' },
    ],
  },

  // ─── 3.12 two_columns_text ─────────────────────────────────────────────
  two_columns_text: {
    type: 'two_columns_text',
    titleMain: { en: 'Do · Avoid' },
    titleSub: { en: 'Two columns of guidance for the tense moment.' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#000000',
    blockingSection: { state: 'false' },
    leftColumnContent: { en: '<h3>Do</h3><ul><li>Name the behaviour, not the person.</li><li>Wait for the other person to bring context before you do.</li><li>Offer a concrete next step.</li></ul>' },
    leftColumnTextColor: '#ffffff',
    leftColumnBackgroundColor: '#5514B4',
    rightColumnContent: { en: '<h3>Avoid</h3><ul><li>Open with the impact before the behaviour.</li><li>Stockpile grievances for one big release.</li><li>Read minds — assume their intent.</li></ul>' },
    rightColumnTextColor: '#ffffff',
    rightColumnBackgroundColor: '#3D0D85',
  },

  // ─── 3.13 mandatoryQuestion ────────────────────────────────────────────
  mandatoryQuestion: {
    type: 'mandatoryQuestion',
    titleMain: { en: 'Knowledge check' },
    titleSub: { en: "Answer correctly to move on." },
    titleTextColor: '#0f172a',
    text: { en: 'What should Sarah do first when she sits down with Jordan?' },
    bgColor: '#ffffff',
    textColor: '#0f172a',
    blockingSection: { state: 'true' },
    options: [
      { text: { en: 'Start with the behaviour: "When you missed the spec review on Tuesday…"' }, isCorrect: true },
      { text: { en: 'Open with the impact first to soften the message.' } },
      { text: { en: 'Suggest Jordan take some time off to think it over.' } },
      { text: { en: 'Hand Jordan a 47-page deck and leave the room.' } },
    ],
    feedbacks: {
      correct: { en: "That's the SBI opener — behaviour first, then impact." },
      wrong: { en: "Not quite — SBI starts with the specific behaviour, not the impact." },
    },
  },

  // ─── 3.14 discover — REMOVED as a standalone layout. Lives only as an
  //                   in-video interaction now (see editor-tools.jsx
  //                   `addInteraction('discover')`).

  // ─── 3.15 question (standalone, non-blocking) ──────────────────────────
  question: {
    type: 'question',
    titleMain: { en: 'Quick check' },
    text: { en: 'Which of these is the SBI core move?' },
    bgColor: '#ffffff',
    textColor: '#0f172a',
    blockingSection: { state: '' },
    answers: [
      { text: { en: 'Naming the behaviour without judgement.' }, isCorrect: true },
      { text: { en: 'Asking the other person to explain themselves.' } },
      { text: { en: 'Restating the impact in your own words.' } },
    ],
    feedbackText: { en: 'Behaviour first — then impact, then context.' },
  },

  // ─── 3.16 title ────────────────────────────────────────────────────────
  title: {
    type: 'title',
    titleMain: { en: 'Section 2' },
    titleSub: { en: 'Frameworks for tough talks' },
    titleTextColor: '#ffffff',
    titleBackgroundColor: '#0f172a',
    blockingSection: { state: '' },
  },
};

// ── Sample content for the 7 course framing screens (§4.6) ──────────────────
const FRAMING_CONTENT_SAMPLES = {
  cover: {
    backgroundImageUrl: 'placeholder:scene-cover-hospitality',
    brandLogoUrl: 'placeholder:logo-mohg',
    titleMain: { en: 'Code of Conduct' },
    introText: { en: 'The code of conduct establishes the standards and values which Mandarin Oriental Hotel Group wishes to uphold in the operation of our business.' },
    ctaLabel: { en: 'Start Course' },
    ctaColor: '#ffffff',
    ctaTextColor: '#0f172a',
    have_startingCover: true,
  },
  language: {
    enabled: ['en','it','jp','hk','ch','ar','fr','es','de'],
  },
  brand: {
    selectBrand: true,
    dynamicBrandLogo: false,
    brands: [
      { code: 'mohg-hotels',  label: { en: 'MOHG Hotels' },  glyph: 'M' },
      { code: 'mohg-residences', label: { en: 'MOHG Residences' }, glyph: 'R' },
      { code: 'mohg-spa', label: { en: 'MOHG Spa' }, glyph: 'S' },
    ],
    promptText: { en: 'Select the part of the business you work for' },
  },
  role: {
    promptText: { en: 'Select your role' },
    helperText: { en: 'To make sure you get the right training, select your role from the list below.' },
    titleMain: { en: 'Environmental Compliance and You' },
    titleColor: '#0f172a',
    bgColor: '#f1f5f9',
    selectedBgColor: '#5514B4',
    selectedTextColor: '#ffffff',
    roles: [
      { code: 'eng', label: { en: 'Engineering and technician roles/managers (excluding Networks)' } },
      { code: 'office', label: { en: 'Office-based roles/managers (including Supply Chain)' } },
      { code: 'net',   label: { en: 'Networks engineers/managers' } },
    ],
  },
  home: {
    brandLogoUrl: 'placeholder:logo-mohg',
    titleMain: { en: 'Code of Conduct' },
    introText: { en: 'The code of conduct establishes the standards and values which Mandarin Oriental Hotel Group wishes to uphold in the operation of our business.' },
    postAssessmentLabel: { en: 'Start Post-assessment' },
    modules: [
      { id: 'M1', n: 1, title: { en: 'Recognising tension early' }, summary: { en: 'Spot the verbal and non-verbal cues that signal a conversation is sliding sideways.' }, duration: '04:48', state: 'done', thumbBg: 'placeholder:scene-m1' },
      { id: 'M2', n: 2, title: { en: 'Frameworks for tough talks' }, summary: { en: 'Two named frameworks (CLEAR and SBI) and when each one fits.' }, duration: '05:04', state: 'done', thumbBg: 'placeholder:scene-m2' },
      { id: 'M3', n: 3, title: { en: 'De-escalation in practice' }, summary: { en: 'When tempers rise — concrete moves to slow the conversation down.' }, duration: '02:39', state: 'open', thumbBg: 'placeholder:scene-m3' },
      { id: 'M4', n: 4, title: { en: 'After the conversation' }, summary: { en: "Repair, follow-through, and what to write down." }, duration: '01:57', state: 'locked', thumbBg: 'placeholder:scene-m4' },
    ],
  },
  preAssessment: {
    titleMain: { en: 'Pre-assessment' },
    backgroundImageUrl: 'placeholder:scene-cover-hospitality',
    mode: 'completion',
    passMark: 70,
    randomise: true,
    introTitle: { en: 'Thank you for taking the pre-assessment' },
    introBody: { en: 'This will help us recommend the right modules for you.' },
    resultTitle: { en: 'Thank you' },
    resultBody: { en: "You've completed your pre-assessment with {correct} out of {total} correct answers." },
    sampleQuestion: {
      text: { en: 'A colleague with a visible disability joins your team. Which of the following actions best supports an inclusive work culture?' },
      answers: [
        { text: { en: 'Avoid discussing the disability to respect personal boundaries and prevent discomfort.' } },
        { text: { en: 'Invite them to share how you can support their working needs.' }, isCorrect: true },
        { text: { en: 'Check with other colleagues to get their perspective on whether adjustments might be necessary.' } },
        { text: { en: 'Adjust their workload based on what you think might be challenging for them.' } },
      ],
    },
  },
  postAssessment: {
    titleMain: { en: 'Post-assessment' },
    mode: 'threshold',
    passMark: 80,
    requireExplicitPostStart: true,
    showPreAssessmentChoice: false,
    passTitle: { en: 'Well done!' },
    passBody: { en: "You've completed the course. Please confirm you understand the requirements." },
    acknowledgement: { en: 'I confirm I understand my responsibility to follow the legal, regulatory and policy requirements contained in this course.' },
    policyLinkUrl: 'policy.mohg/code-of-conduct',
    policyLinkLabel: { en: 'View policy' },
    failTitle: { en: 'Not quite yet' },
    failBody: { en: 'Review the modules above and try again. You scored {correct} of {total}.' },
  },
};

// ── Map module-layout ids to a content-sample key ───────────────────────────
// This wires the existing course architecture (data.jsx) to renderable content.
const LAYOUT_ID_TO_TYPE = {};
(SAMPLE_COURSE.modules || []).forEach(m => {
  (m.layouts || []).forEach(l => { LAYOUT_ID_TO_TYPE[l.id] = l.type; });
});

function getLayoutContent(layoutId, fallbackType) {
  const type = LAYOUT_ID_TO_TYPE[layoutId] || fallbackType;
  return LAYOUT_CONTENT_SAMPLES[type] || LAYOUT_CONTENT_SAMPLES.fullscreen_text_and_image;
}

Object.assign(window, { LAYOUT_CONTENT_SAMPLES, FRAMING_CONTENT_SAMPLES, LAYOUT_ID_TO_TYPE, getLayoutContent });
