// Handbook manifest: one of every block, labelled with its `type`.
// Markdown fields are pre-rendered to HTML here because the build script is
// dependency-free; the production compiler would render Markdown to HTML
// during the normal content pipeline.

const handbook = {
  outputName: 'composed-blocks-handbook.single.html',
  title: 'Composed Blocks Handbook',
  subtitle: 'A reference catalogue of every reusable content block in the single-html-document skill. Each block appears once with its type label so you can map demonstration back to schema.',
  eyebrow: 'Pattern handbook',
  showBlockLabels: true,
  footer: 'Generated by <code>scripts/build-block-sample.mjs</code> from <code>scripts/manifests/composed-blocks-handbook.mjs</code>.',
  patterns: {
    toc: {
      enabled: true,
      depth: 4,
      search: true,
      shortcut: 't',
    },
    'global-search': {
      enabled: true,
      shortcut: '/',
      altShortcut: '\\',
    },
    'keyboard-help': {
      enabled: true,
      entries: [
        { keys: 'T',   label: 'Open / close contents (TOC)' },
        { keys: '↑ ↓', label: 'Navigate TOC entries (in search)' },
        { keys: '→',   label: 'Expand TOC node, or jump to first child' },
        { keys: '←',   label: 'Collapse TOC node, or jump to parent' },
        { keys: '/',   label: 'Search the page' },
        { keys: '\\',  label: 'Search the page (alt)' },
        { keys: '?',   label: 'Show this help' },
        { keys: 'Esc', label: 'Close any open overlay' },
      ],
    },
  },
  sections: [
    {
      id: 'overview',
      heading: 'How to read this handbook',
      kicker: 'Orientation',
      lead: 'Each block is shown in isolation, labelled with its type so you can match it to the spec under <code>references/blocks/&lt;name&gt;.md</code>. Static blocks render their final HTML once; interactive blocks are mounted on load by the shared bootstrap.',
      blocks: [
        {
          type: 'tldr',
          label: 'tldr',
          data: {
            title: 'Blocks separate content from presentation.',
            bodyHtml: '<p>Authors stay in YAML/JSON and Markdown. Renderers (vanilla JS, packaging-safe) turn structured content into rich HTML at compile time or on hydration.</p>',
            pointsHtml: [
              'One <code>type</code> per block, with a strict schema.',
              'Each block has colocated spec, renderer, and example.',
              'Interactive blocks survive single-file packaging.',
            ],
          },
        },
      ],
    },

    {
      id: 'scaffolding',
      heading: 'Scaffolding blocks',
      kicker: 'Group 1 of 4',
      lead: 'Static-mode blocks for opening, flagging, and structuring prose. Useful in every archetype.',
      blocks: [
        {
          type: 'callout',
          label: 'callout  (icon: shield-alert)',
          data: {
            tone: 'warning',
            icon: 'shield-alert',
            title: "Don't fight the release-age gate",
            bodyHtml: "<p>When <code>npm install foo@latest</code> resolves to an older version than expected, that's the <strong>seven-day buffer working</strong> — investigate the package, don't override the gate.</p>",
          },
        },
        {
          type: 'faq',
          label: 'faq',
          data: {
            title: 'Common questions',
            defaultOpen: 'first',
            items: [
              {
                question: 'Why not just generate one big HTML file directly?',
                answerHtml: "<p>Because authoring inside one giant HTML blob is brittle — you can't diff it, restyle it, or reuse content. Single-file is a <strong>delivery format</strong>, not an authoring format.</p>",
              },
              {
                question: 'What about MHTML or Web Bundles?',
                answerHtml: "<p>Web Bundles had their navigation implementation removed from Chromium in 2023; MHTML is a niche capture format. Plain <code>.html</code> is the only format that opens reliably across modern browsers from disk.</p>",
              },
              {
                question: 'Do I need React?',
                answerHtml: '<p>No. The block layer is pure vanilla JS. Use React when the site is complex enough to benefit from it; use static HTML when it\'s not.</p>',
              },
            ],
          },
        },
        {
          type: 'before-after',
          label: 'before-after',
          data: {
            axis: 'horizontal',
            before: {
              label: 'Before',
              bodyHtml: '<p>Generated HTML inline; no schema; long files; no way to reuse the pattern.</p>',
              code: {
                language: 'yaml',
                source: `- type: callout\n  html: "<div class='warn'>Don't override the gate</div>"`,
              },
            },
            after: {
              label: 'After',
              bodyHtml: '<p>Typed block, clear schema, renderer separate, packaging mode declared.</p>',
              code: {
                language: 'yaml',
                source: `- type: callout\n  data:\n    tone: warning\n    title: "Don't override the gate"\n    body: "Investigate the package first."`,
              },
            },
          },
        },
        {
          type: 'glossary',
          label: 'glossary',
          data: {
            title: 'Terms used in this handbook',
            layout: 'stacked',
            terms: [
              {
                term: 'Single-file HTML',
                definitionHtml: '<p>A complete website (text, styles, scripts, media) collapsed into one <code>.html</code> file that opens directly in a modern browser.</p>',
                aliases: ['self-contained HTML', 'portable HTML'],
                related: ['Packaging mode'],
              },
              {
                term: 'Packaging mode',
                definitionHtml: '<p>Build-time strategy for collapsing assets into the deliverable. The three named modes are <strong>direct inline</strong>, <strong>Blob-backed payload</strong>, and <strong>compressed bootstrap</strong>.</p>',
                aliases: ['delivery mode'],
              },
              {
                term: 'Block',
                definitionHtml: '<p>A typed piece of structured content with a colocated renderer. Blocks let authors stay in YAML/JSON while keeping rich visual treatment in the renderer layer.</p>',
              },
            ],
          },
        },
      ],
    },

    {
      id: 'diagram',
      heading: 'Diagram blocks',
      kicker: 'Group 2 of 4',
      lead: 'Visual structure blocks. Mostly static; flowchart adds click-reveal node details.',
      blocks: [
        {
          type: 'timeline',
          label: 'timeline',
          data: {
            title: 'Incident timeline · auth-svc 2026-04-12',
            events: [
              {
                date: '08:42',
                title: 'First failed login spike detected',
                bodyHtml: '<p>Grafana fired <code>auth-svc-error-rate-warn</code>. On-call paged.</p>',
                status: 'warning',
                tag: 'P2',
              },
              {
                date: '08:55',
                title: 'Root cause identified',
                bodyHtml: '<p>Token validator was pinned to a removed JWKS endpoint after the rotation at 08:30.</p>',
                status: 'danger',
                followUps: [
                  'Re-check JWKS rotation runbook',
                  'Add canary on validator config drift',
                ],
              },
              {
                date: '09:14',
                title: 'Mitigated',
                bodyHtml: '<p>Rolled forward to validator v2.18 with the new JWKS URL.</p>',
                status: 'success',
              },
            ],
          },
        },
        {
          type: 'flowchart',
          label: 'flowchart  (interactive · click a node)',
          data: {
            title: 'Token validation',
            direction: 'top-down',
            nodes: [
              { id: 'req',    label: 'Incoming request',  lane: 0, order: 0, kind: 'terminal' },
              { id: 'parse',  label: 'Parse JWT',         lane: 0, order: 1 },
              { id: 'ok',     label: 'Signature valid?',  lane: 0, order: 2, kind: 'decision' },
              { id: 'cache',  label: 'Cache validation',  lane: 0, order: 3, status: 'success',
                bodyHtml: '<p>Validator caches by token hash for up to 60 seconds. Cache hits skip the JWKS verify path.</p>' },
              { id: 'reject', label: 'Reject 401',        lane: 1, order: 3, kind: 'terminal', status: 'danger',
                bodyHtml: '<p>Logged with reason and request id; alerting fires only above threshold of 30 rejections per minute per upstream IP.</p>' },
            ],
            edges: [
              { from: 'req',   to: 'parse' },
              { from: 'parse', to: 'ok' },
              { from: 'ok',    to: 'cache',  label: 'yes' },
              { from: 'ok',    to: 'reject', label: 'no' },
            ],
          },
        },
        {
          type: 'module-map',
          label: 'module-map',
          data: {
            title: 'Repo layout · single-html-document',
            columns: [
              { label: 'Authoring' },
              { label: 'Build' },
              { label: 'Runtime' },
            ],
            modules: [
              { id: 'content',  label: 'content/',        column: 0, kind: 'entry',
                summaryHtml: '<p>Markdown + YAML sources</p>' },
              { id: 'compiler', label: 'scripts/build*',  column: 1,
                summaryHtml: '<p>Compiles content into typed JSON</p>' },
              { id: 'blocks',   label: 'scripts/blocks/', column: 1, kind: 'hot-path',
                summaryHtml: '<p>Vanilla JS renderers, one per block type</p>' },
              { id: 'site',     label: 'src/',            column: 2,
                summaryHtml: '<p>Vite/React app or static HTML host</p>' },
              { id: 'bundle',   label: 'report.html',     column: 2, kind: 'package',
                summaryHtml: '<p>Single packaged deliverable</p>' },
            ],
            connections: [
              { from: 'content',  to: 'compiler', label: 'read' },
              { from: 'compiler', to: 'site',     label: 'emit JSON' },
              { from: 'blocks',   to: 'site',     label: 'imported' },
              { from: 'site',     to: 'bundle',   label: 'package' },
            ],
          },
        },
        {
          type: 'mind-map',
          label: 'mind-map',
          data: {
            title: 'Scoping the workshop',
            direction: 'horizontal',
            root: {
              label: 'AI in higher education',
              bodyHtml: '<p>1-day workshop, ~30 participants</p>',
            },
            branches: [
              {
                label: 'Pedagogy',
                kind: 'info',
                icon: 'book-open',
                children: [
                  { label: 'Assessment redesign' },
                  { label: 'Plagiarism vs. authorship' },
                  { label: 'AI literacy curriculum' },
                ],
              },
              {
                label: 'Operations',
                kind: 'success',
                icon: 'layers',
                children: [
                  { label: 'Tooling procurement' },
                  { label: 'Staff training programme' },
                ],
              },
              {
                label: 'Risk and policy',
                kind: 'warning',
                icon: 'shield-alert',
                children: [
                  { label: 'Data protection (GDPR)' },
                  { label: 'Equitable access' },
                  { label: 'Compliance reporting' },
                ],
              },
            ],
          },
        },
        {
          type: 'concept-map',
          label: 'concept-map  (every edge labelled)',
          data: {
            title: 'Why typed blocks matter',
            nodes: [
              { id: 'blocks',          label: 'Typed blocks',         col: 0, row: 0, kind: 'key',     icon: 'package' },
              { id: 'schema',          label: 'Strict schema',        col: 0, row: 1, kind: 'success' },
              { id: 'drift',           label: 'Renderer/spec drift',  col: 1, row: 0, kind: 'danger',  icon: 'alert-triangle' },
              { id: 'reuse',           label: 'Reuse across pages',   col: 1, row: 1, kind: 'info' },
              { id: 'agent',           label: 'LLM authorability',    col: 2, row: 0, kind: 'success', icon: 'zap' },
              { id: 'maintainability', label: 'Long-term maintainability', col: 2, row: 1, kind: 'success' },
            ],
            edges: [
              { from: 'blocks', to: 'schema',          label: 'is governed by', kind: 'dependsOn' },
              { from: 'blocks', to: 'reuse',           label: 'enables',        kind: 'enables' },
              { from: 'schema', to: 'drift',           label: 'prevents',       kind: 'contradicts' },
              { from: 'schema', to: 'agent',           label: 'improves',       kind: 'causes' },
              { from: 'reuse',  to: 'maintainability', label: 'supports',       kind: 'causes' },
              { from: 'drift',  to: 'maintainability', label: 'undermines',     kind: 'contradicts' },
            ],
          },
        },
        {
          type: 'inline-svg',
          label: 'inline-svg',
          data: {
            align: 'center',
            maxHeight: '14rem',
            frame: true,
            svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 100">
              <rect x="10"  y="20" width="60" height="60" rx="6" fill="#1f5679"/>
              <rect x="140" y="20" width="60" height="60" rx="6" fill="#2e6e40"/>
              <path d="M 70 50 L 140 50" stroke="#333" stroke-width="2" fill="none"/>
              <polygon points="140,50 132,46 132,54" fill="#333"/>
              <text x="40"  y="55" text-anchor="middle" fill="white" font-family="sans-serif" font-size="11">author</text>
              <text x="170" y="55" text-anchor="middle" fill="white" font-family="sans-serif" font-size="11">package</text>
            </svg>`,
            captionHtml: '<p>Two-stage build: <strong>author</strong> ➜ <strong>package</strong>.</p>',
            attributionHtml: '<p>Source: this handbook&rsquo;s architecture diagram.</p>',
          },
        },
      ],
    },

    {
      id: 'comparison',
      heading: 'Comparison and token blocks',
      kicker: 'Group 3 of 4',
      lead: 'Side-by-side, design-system, and token blocks. Swatches and tabbed-code are interactive; the rest render purely from compile-time data.',
      blocks: [
        {
          type: 'side-by-side',
          label: 'side-by-side',
          data: {
            title: 'Direct inline vs Blob-backed payload',
            panels: [
              {
                label: 'Direct inline',
                title: 'Best for narrative reports',
                bodyHtml: '<p>All assets inlined as Base64 inside the HTML. Simple. Fast to author. Files stay small for narrative content.</p>',
                accent: 'info',
              },
              {
                label: 'Blob-backed payload',
                title: 'Best for binary-heavy reports',
                bodyHtml: '<p>Attachments embedded as data records, materialised as <code>Blob</code> URLs only when needed. Keeps the DOM clean even with many large downloads.</p>',
                accent: 'success',
                tag: 'recommended for media',
              },
            ],
          },
        },
        {
          type: 'contact-sheet',
          label: 'contact-sheet',
          data: {
            title: 'Callout tones',
            cellMinWidth: '11rem',
            cells: [
              { label: 'info',    sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(31,86,121,0.08);border:2px solid #1f5679;color:#1f5679;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Neutral commentary</div>` },
              { label: 'success', sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(46,110,64,0.08);border:2px solid #2e6e40;color:#2e6e40;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Working pattern</div>` },
              { label: 'warning', sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(190,132,22,0.10);border:2px solid #be8416;color:#be8416;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Footgun</div>` },
              { label: 'danger',  sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(170,50,60,0.08);border:2px solid #aa323c;color:#aa323c;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Don&rsquo;t do this</div>` },
              { label: 'key',     sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(120,78,152,0.08);border:2px solid #784e98;color:#784e98;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Most important</div>` },
              { label: 'neutral', sublabel: 'tone',
                html: `<div style="width:100%;display:flex;align-items:center;justify-content:center;padding:0.5rem 0.6rem;border-radius:6px;background:rgba(60,60,60,0.06);border:2px solid #555555;color:#555555;font-weight:600;font-size:0.92rem;text-align:center;line-height:1.3;overflow-wrap:anywhere;">Generic emphasis</div>` },
            ],
          },
        },
        {
          type: 'swatches',
          label: 'swatches  (interactive · click to copy)',
          data: {
            title: 'Institutional palette',
            kind: 'color',
            copyValue: 'var',
            swatches: [
              { name: '--ox-blue',    value: '#002147', role: 'primary brand' },
              { name: '--ox-stone',   value: '#f5f1eb', role: 'page background' },
              { name: '--ox-deep',    value: '#0f3d5e', role: 'headings' },
              { name: '--ox-accent',  value: '#ee6c4d', role: 'callout accents' },
              { name: '--ox-success', value: '#2e6e40', role: 'confirmed outcome' },
              { name: '--ox-danger',  value: '#aa323c', role: 'do-not / breaking' },
            ],
          },
        },
        {
          type: 'severity-tag',
          label: 'severity-tag',
          data: {
            title: 'Severity vocabulary used in this handbook',
            layout: 'grid',
            tags: [
              { name: 'P0', tone: 'danger',
                descriptionHtml: '<p>Customer impact, no workaround. Drop everything.</p>',
                when: 'Active outage, data loss, or compliance breach.',
                sla: 'Response under 15 min, mitigation under 1 h.' },
              { name: 'P1', tone: 'warning',
                descriptionHtml: '<p>Customer impact with workaround, or imminent risk of outage.</p>',
                when: 'Degraded service, escalating error rates.',
                sla: 'Response under 1 h, mitigation under 4 h.' },
              { name: 'P2', tone: 'info',
                descriptionHtml: '<p>Not customer-facing yet, but should be addressed this sprint.</p>',
                when: 'Internal warning thresholds tripped, single-replica failures.',
                sla: 'Response under 1 day.' },
              { name: 'P3', tone: 'neutral',
                descriptionHtml: '<p>Tracked but not prioritised.</p>',
                when: 'Minor noise, cosmetic issues.' },
            ],
          },
        },
        {
          type: 'tabbed-code',
          label: 'tabbed-code  (interactive · arrow keys to switch)',
          data: {
            title: 'Install commands',
            default: 0,
            tabs: [
              { label: 'npm',  language: 'bash', source: 'npm install gray-matter fast-glob\nnpm install -D vite-plugin-singlefile' },
              { label: 'pnpm', language: 'bash', source: 'pnpm add gray-matter fast-glob\npnpm add -D vite-plugin-singlefile' },
              { label: 'bun',  language: 'bash', source: 'bun add gray-matter fast-glob\nbun add -d vite-plugin-singlefile' },
            ],
          },
        },
      ],
    },

    {
      id: 'interactive',
      heading: 'Interactive blocks',
      kicker: 'Group 4 of 4',
      lead: 'Reader-driven blocks. All hydrate on load via the shared bootstrap and survive single-file packaging.',
      blocks: [
        {
          type: 'slider',
          label: 'slider  (drag the inputs)',
          data: {
            title: 'Base64 inflation',
            descriptionHtml: '<p>Base64 encoding adds roughly 33% overhead. Slide the raw size to see the effect on the packaged HTML.</p>',
            parameters: [
              { name: 'rawMb',   label: 'Raw asset size', min: 1, max: 50, step: 1, default: 10, unit: 'MB' },
              { name: 'extraMb', label: 'Other content',  min: 0, max: 20, step: 1, default: 2,  unit: 'MB' },
            ],
            outputs: [
              { label: 'Encoded size',   compute: 'rawMb * 1.33 + extraMb', format: 'number', decimals: 1, unit: 'MB' },
              { label: 'Inflation cost', compute: 'rawMb * 0.33',            format: 'number', decimals: 1, unit: 'MB' },
            ],
          },
        },
        {
          type: 'toggle-deps',
          label: 'toggle-deps  (try toggling cdnAssets)',
          data: {
            title: 'Build options',
            groups: [
              { id: 'assets',   label: 'Assets' },
              { id: 'delivery', label: 'Delivery' },
            ],
            toggles: [
              { id: 'inlineImages', group: 'assets', label: 'Inline images as Base64', default: true,
                descriptionHtml: '<p>Required for offline <code>file://</code> delivery.</p>' },
              { id: 'gzipPayload',  group: 'assets', label: 'Compress text payloads with gzip', default: false,
                dependencies: ['inlineImages'],
                descriptionHtml: '<p>Best with text-heavy reports. Requires inline images so the bootstrap can decompress on load.</p>' },
              { id: 'cdnAssets',    group: 'delivery', label: 'Pull fonts from a CDN', default: false,
                conflicts: ['inlineImages'],
                descriptionHtml: '<p>Faster initial paint but breaks offline delivery.</p>' },
              { id: 'blobAttachments', group: 'delivery', label: 'Materialise attachments as Blob URLs', default: true,
                descriptionHtml: '<p>Recommended for any report with downloadable PDFs, CSVs, or media.</p>' },
            ],
          },
        },
        {
          type: 'drag-reorder',
          label: 'drag-reorder  (drag cards or use space + arrows)',
          data: {
            title: 'Sort the workshop topics',
            lanes: [
              { id: 'now',   label: 'Cover today' },
              { id: 'maybe', label: 'If time', cap: 3 },
              { id: 'skip',  label: 'Skip' },
            ],
            items: [
              { id: 'deck-pattern', label: 'Single-file deck pattern', lane: 'now' },
              { id: 'blocks',       label: 'Component block layer',     lane: 'now' },
              { id: 'workbook',     label: 'Feedback workbooks',        lane: 'maybe', tag: 'long' },
              { id: 'catalogue',    label: 'Generated catalogue',       lane: 'skip' },
              { id: 'research',     label: 'Research synthesis',        lane: 'maybe' },
            ],
            exportFormat: 'markdown',
          },
        },
        {
          type: 'live-template',
          label: 'live-template  (edit any field)',
          data: {
            title: 'Stand-up message template',
            descriptionHtml: '<p>Edit any field and the message updates live.</p>',
            template: 'Hi {team}, quick stand-up:\n• Yesterday: {yesterday}\n• Today: {today}\n• Blockers: {blockers}',
            variables: [
              { name: 'team',      label: 'Team',       default: 'growth',                            kind: 'text' },
              { name: 'yesterday', label: 'Yesterday',  default: 'shipped the dashboard skeleton',    kind: 'multiline' },
              { name: 'today',     label: 'Today',      default: 'wire up search filters',            kind: 'multiline' },
              { name: 'blockers',  label: 'Blockers',   default: 'none',                              kind: 'text' },
            ],
            outputFormat: 'text',
          },
        },
        {
          type: 'annotated-diff',
          label: 'annotated-diff  (severity tag references the catalogue above)',
          data: {
            title: 'Token validator change',
            path: 'src/auth/validator.ts',
            language: 'typescript',
            lines: [
              { kind: 'context', oldNumber: 12, newNumber: 12, content: 'export async function validate(token: string) {' },
              { kind: 'remove',  oldNumber: 13,                 content: '  const jwks = await fetch(JWKS_URL).then((r) => r.json());' },
              { kind: 'add',                    newNumber: 13,  content: '  const jwks = await getJwks();  // cached, rotation-aware' },
              { kind: 'context', oldNumber: 14, newNumber: 14, content: '  return verify(token, jwks);' },
              { kind: 'context', oldNumber: 15, newNumber: 15, content: '}' },
            ],
            notes: [
              {
                onLines: [2, 3],
                severity: 'P1',
                bodyHtml: '<p>The previous version refetched the JWKS on every request. The new helper caches and respects rotation; see <code>getJwks</code> in <code>src/auth/jwks.ts</code>.</p>',
              },
            ],
            jumpLinks: [
              { label: 'Top of fix', atLine: 2 },
            ],
          },
        },
      ],
    },

    {
      id: 'closing',
      heading: 'Where to go next',
      kicker: 'After the catalogue',
      lead: 'For the contract every block follows, see <code>references/blocks/_protocol.md</code>. For the index of all blocks, see <code>references/blocks/_index.md</code>. For the narrative companion to this handbook, open <code>composed-blocks-walkthrough.single.html</code>.',
      blocks: [],
    },
  ],
};

export default handbook;
