$ cat node-template.py
S
Slide Generator
// Generates a single presentation slide from text content and an instruction. Connect multiple Slide Generator outputs to a Presentation Composer to build a full presentation.
Process
Document
template.py
1import sys2import json3import re4import base645import mimetypes6import traceback7from html.parser import HTMLParser89from gais import Gais1011SYSTEM_PROMPT = '''You are a presentation slide designer. Your task is to generate exactly ONE Reveal.js slide as an HTML <section> element.1213RULES:141. Output ONLY a single <section>...</section> element. No <!DOCTYPE>, no <html>, no <head>, no wrapper.152. Use <h2> for the slide title (not <h1>).163. Use semantic HTML: <ul>/<ol> for lists, <p> for text, <table> for data, <blockquote> for quotes.174. Use class="fragment" on elements that should appear one-by-one during the presentation.185. Use <aside class="notes">...</aside> for speaker notes (always include brief notes).196. Do NOT set data-background-color on <section>. The presentation theme controls all colors — never override it.207. Keep content concise — slides should be scannable, not walls of text.218. Do NOT include any CSS <style> blocks or <script> tags.229. Do NOT set color, background-color, or any color-related inline styles. Let the Reveal.js theme handle all colors.2310. Only use inline styles for LAYOUT purposes (display:flex, display:grid, gap, font-size). Never for colors or backgrounds.2411. Use plain HTML that Reveal.js themes will style automatically.2512. THEME × TEMPLATE COUPLING: each slide layout template in this system is theme-specific. marina-* templates (marina-intro, marina-content, marina-metrics, marina-timeline, marina-comparison, marina-outro) require the 'marina' Reveal theme and use Marina-only classes (.title-slide, .closing-slide, data-header-title, .card-grid, .tag.commercial, etc.). emblema-* templates (emblema-cover, emblema-agenda, emblema-section, …, emblema-closing) require the 'emblema' theme and use Emblema-only classes (.slide-cover, .slide-section, .slide-pad, .slide-bullets, etc.). NEVER emit a marina-* slide structure when the deck theme is emblema (the cover and closing collapse to unstyled white pages because emblema CSS does not define .title-slide / .closing-slide). NEVER emit an emblema-* slide structure when the deck theme is marina. If you are asked to produce a cover or closing for an emblema deck, use emblema-cover and emblema-closing — not marina-intro / marina-outro.2627IMAGE PLACEHOLDERS:28When a slide layout requires images, use picsum.photos for royalty-free placeholder images:29- Landscape: <img src="https://picsum.photos/seed/{topic}/600/400" alt="description">30- Portrait: <img src="https://picsum.photos/seed/{topic}/400/600" alt="description">31- Square: <img src="https://picsum.photos/seed/{topic}/400/400" alt="description">32Replace {topic} with a short keyword related to the slide content (e.g. "analytics", "team", "growth").33Always include a descriptive alt attribute. Set width via style if needed for layout.3435LAYOUT PATTERNS you can use:36- Title slide: <h2> + <p> subtitle37- Bullet points: <h2> + <ul> with <li class="fragment">38- Two columns: <div style="display:flex;gap:2em"> with two <div> children39- Data table: <h2> + <table>40- Quote: <blockquote> + attribution41- Image + text: <h2> + <div style="display:flex;gap:2em"> with <img> and text side by side4243Always output valid HTML. The content should be based ONLY on the provided source material — do not invent facts or data.'''4445# Layout instructions for each slide template46TEMPLATE_INSTRUCTIONS = {47 # ─── Marina Militare layouts ───48 "marina-intro": "[THEME GATE: this template is valid ONLY when the active deck theme is 'marina'. If the active theme is 'emblema', DO NOT emit this structure — switch to the 'emblema-cover' template instead, which is the Emblema brand cover. The Marina .title-slide class is unstyled in the Emblema theme and would render as a blank white slide.] "49 "Create a Marina Militare INTRO/COVER slide. The <section> MUST start with class='title-slide' — WITHOUT THIS CLASS, the Marina Militare crest will NOT render and the Graduate uppercase title font will NOT apply. The slide will collapse to a plain navy background with default text. Use this EXACT structure:\n"50 "<section class='title-slide'>\n"51 " <h1>OPERATION OR BRIEFING TITLE</h1>\n"52 " <p class='subtitle'>Subtitle line — short, two lines maximum</p>\n"53 " <p class='meta'>Mission ID | Unit | Date | Classification</p>\n"54 " <aside class='notes'>Cover slide — set the audience and the mission.</aside>\n"55 "</section>\n"56 "The Marina Militare crest is injected by the theme via a CSS pseudo-element above the title — do NOT add an <img> for the logo. The <h1> renders in uppercase Graduate font and tolerates a <br> mid-line for a two-line title. Keep the subtitle to one or two lines using <p class='subtitle'>. Use <p class='meta'> for the small operational metadata (mission ID, unit, date, classification). Keep all class names EXACTLY as shown — class='title-slide' on the section is LOAD-BEARING.",5758 "marina-content": "Create a Marina Militare CONTENT slide. This is the marina theme's GENERAL-PURPOSE / FREE-FORM content layout — use it for any textual, discursive, narrative or free-structure content, and whenever the user asks for a 'content' / 'testuale' / 'free-form' / 'libera' slide. The ONLY fixed parts are the <section data-header-title='...'> wrapper and the <h2> heading; everything below the <h2> is HTML you choose to fit the content. "59 "CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source → Italian heading). Extract the heading from the actual source content — do NOT invent generic English dashboard phrases. BANNED headings that you MUST NOT use as <h2>: 'Overview', 'Summary', 'Key Points', 'Main Content', 'Content Summary', 'Information', 'Details'. These are hallucinations from other templates and look unprofessional. Good examples for an Italian Navy briefing: 'Identificazione Missione', 'Assetto della Piattaforma', 'Quadro Operativo Generale'. "60 "The <section> MUST have a data-header-title='SHORT TITLE' attribute — WITHOUT THIS ATTRIBUTE, the top header bar with the Marina Militare logo and uppercase title will NOT render and the slide will look unbranded. After the <h2> you may put any mix of: prose <p> paragraphs, <ul>/<ol> bullet lists, sub-<h3> headings, a <table> for genuinely tabular data (fully theme-styled — navy header row, gold column headers, bordered rows; you do NOT need to switch to marina-comparison for a small table inside a content slide), an optional <p class='section-label'> small subtitle, and/or the marina .card-grid / .card blocks for numeric/KPI/dashboard data — in any order. Do NOT add background-color or color inline styles — the theme owns them. "61 "ALIGNMENT: the outer <section> MUST carry class='marina-content' in addition to data-header-title='SHORT TITLE' — that class is the CSS hook the theme uses to flush body content left. Inside that section everything below the header bar is LEFT-aligned by the theme — the <h2>, every <h3>, all <p> paragraphs, all <ul>/<ol> lists, and every <table>. The one EXCEPTION is <p class='section-label'>: it is the uppercase brand subtitle and the theme keeps it centered — leave it alone, don't try to left-align it. The theme owns alignment, so do NOT add style='text-align:center', style='text-align:left', a <center> tag, an align='center' attribute, or a centered wrapper <div> around any body element — just emit clean HTML and the CSS handles it. (Centered slide bodies belong only to the separate marina-intro and marina-outro layouts, which use different classes — never here.) "62 "EXAMPLE — free-form / discursive content (the default for narrative text):\n"63 "<section class='marina-content' data-header-title='SHORT SLIDE TITLE'>\n"64 " <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n"65 " <p class='section-label'>Optional small label / subtitle</p>\n"66 " <h3>Optional sub-heading</h3>\n"67 " <p>A paragraph of discursive content drawn from the source.</p>\n"68 " <p>Another paragraph.</p>\n"69 " <ul>\n"70 " <li>A bullet point.</li>\n"71 " <li>Another bullet point.</li>\n"72 " </ul>\n"73 " <aside class='notes'>Speaker notes.</aside>\n"74 "</section>\n"75 "EXAMPLE — data / KPI content (use cards when the slide is a set of numbers or capability readings):\n"76 "<section class='marina-content' data-header-title='SHORT SLIDE TITLE'>\n"77 " <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n"78 " <p class='section-label'>Optional small label / subtitle</p>\n"79 " <div class='card-grid cols-3'>\n"80 " <div class='card'><div class='label'>LABEL</div><div class='value gold'>VALUE</div><div class='detail'>Detail</div></div>\n"81 " <!-- ...more cards... -->\n"82 " </div>\n"83 " <aside class='notes'>Speaker notes.</aside>\n"84 "</section>\n"85 "The data-header-title attribute on <section> drives the fixed header bar — the theme injects the Marina Militare logo on the left and this title text in uppercase on the right. The <h2> is the slide body heading. CHOOSING THE BODY: when the slide content is numeric / KPI / capability-reading / dashboard-shaped, PREFER a <div class='card-grid cols-2|cols-3|cols-4'> with <div class='card'> children over a plain <ul> — cards are the marina theme's signature visual for data. When the slide content is narrative / discursive, prose paragraphs, sub-headings and bullet lists are the right choice — that is NOT 'unbranded', it is exactly what this layout is for. "86 "WHEN YOU DO USE CARDS: each card contains <div class='label'> (uppercase blue), <div class='value'> (large, MUST add a color modifier class: .gold for highlights, .cyan for cool readings, .red for alerts, .green for OK status), and optional <div class='detail'>. "87 "CRITICAL CARD STATE COUPLING RULE (applies only when you use cards): the color modifier on <div class='value'> MUST be mirrored by a matching class on the parent <div class='card'>: "88 "<div class='value red'> (alert/threat/critical) → parent MUST be <div class='card alert'> (red border + red glow + red-tinted background + red label); "89 "<div class='value gold'> (the single most important metric) → parent MUST be <div class='card highlight'> (gold border + gold label); "90 "<div class='value cyan'> or <div class='value green'> → parent stays as plain <div class='card'> (no parent modifier). "91 "Do NOT emit <div class='card'><div class='value red'>...</div></div> — a red value inside a blue card looks inconsistent and unprofessional. Always pair .value.red with .card.alert and .value.gold with .card.highlight. "92 "Anti-pattern to AVOID: forcing a metrics / KPI dashboard (a set of numbers or capability readings) into a plain <ul> instead of a .card-grid — use cards there. (A <ul> for genuine narrative bullet points, or prose paragraphs for discursive content, is correct in a content slide — do not 'card-ify' everything.)",9394 "marina-metrics": "Create a Marina Militare METRICS / CAPABILITY DASHBOARD slide. "95 "CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's metric set, written in the SAME LANGUAGE as the source document. Extract the heading from the actual source content — do NOT invent generic English dashboard phrases. BANNED headings that you MUST NOT use as <h2>: 'Capability Snapshot', 'Key Metrics', 'Performance Overview', 'KPI Dashboard', 'Metrics Dashboard', 'Operational Metrics', 'Overview', 'Summary', 'Snapshot', 'Performance Dashboard'. These are hallucinations from other dashboard templates and look unprofessional in a naval briefing. Good examples for an Italian Navy briefing: 'ITS Margottini — Snapshot T+12 h', 'Assetto Piattaforma al T+24 h', 'Meteo e Condizioni del Mare', 'Readiness e Asset Operativi'. "96 "The <section> MUST have data-header-title='SHORT TITLE' AND the body MUST contain a <div class='card-grid cols-N'> with at least 3 <div class='card'> children — WITHOUT THE CARD GRID, this layout has no visual and degrades to plain text. Each card's <div class='value'> MUST have a color modifier class (.gold, .cyan, .red, or .green) — without it, values render in plain white and lose all semantic meaning. Use this EXACT structure:\n"97 "<section data-header-title='SHORT SLIDE TITLE'>\n"98 " <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n"99 " <p class='section-label'>Optional small label (e.g. timestamp or unit)</p>\n"100 " <div class='card-grid cols-3'>\n"101 " <div class='card'>\n"102 " <div class='label'>METRIC LABEL</div>\n"103 " <div class='value cyan'>VALUE</div>\n"104 " <div class='detail'>Brief detail</div>\n"105 " </div>\n"106 " <!-- Repeat for 3-6 metric cards -->\n"107 " </div>\n"108 " <div class='metrics-callout highlight'> <!-- OPTIONAL: include only if the source content has operational interpretation of the metrics -->\n"109 " <div class='label'>IMPATTO OPERATIVO</div>\n"110 " <p>Short editorial paragraph interpreting the metrics. Use <strong>bold</strong> on the most important phrases to highlight them in gold.</p>\n"111 " </div>\n"112 " <aside class='notes'>Speaker notes.</aside>\n"113 "</section>\n"114 "Produce 3-6 capability cards in a grid row (cols-2 / cols-3 / cols-4 depending on count). Each card MUST contain: <div class='label'> (uppercase blue), <div class='value COLOR'> with one of {gold|cyan|red|green} (gold for highlights, cyan for cool/neutral readings, red for alerts/threats, green for OK/operational), and <div class='detail'> for a short subtitle. Vary the colors based on the meaning of each value — do NOT make every card the same color. "115 "CRITICAL CARD STATE COUPLING RULE: the color modifier on <div class='value'> MUST be mirrored by a matching class on the parent <div class='card'>: "116 "<div class='value red'> (alert/threat/critical) → parent MUST be <div class='card alert'> (red border + red glow + red-tinted background + red label); "117 "<div class='value gold'> (the single most important metric) → parent MUST be <div class='card highlight'> (gold border + gold label); "118 "<div class='value cyan'> or <div class='value green'> → parent stays as plain <div class='card'> (no parent modifier). "119 "Do NOT emit <div class='card'><div class='value red'>...</div></div> — a red value floating inside a blue card looks broken and unprofessional. Always pair .value.red with .card.alert and .value.gold with .card.highlight. "120 "Extract real numbers from the source content. Optional class='fragment' on each card for staggered reveal. "121 "The <div class='metrics-callout highlight'> below the card grid is OPTIONAL — include it ONLY when the source content contains a short editorial interpretation of the metrics (e.g. 'IMPATTO OPERATIVO', 'VALUTAZIONE', 'SIGNIFICATO OPERATIVO'). It has a <div class='label'> header (uppercase, short) and a single <p> body with 1-3 sentences; use <strong> on key phrases to highlight them in gold. The .highlight variant has a gold left border and gold label; omit .highlight for a blue-accent variant. When there is no editorial interpretation to show, OMIT the whole .metrics-callout block. "122 "Anti-pattern to AVOID: producing plain text rows or <ul><li>Metric: value</li></ul> instead of the card grid.",123124 "marina-timeline": "Create a Marina Militare VERTICAL TIMELINE slide. "125 "CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing what this specific timeline covers, written in the SAME LANGUAGE as the source document. Extract the heading from the actual source content — do NOT invent generic English phrases. BANNED headings that you MUST NOT use as <h2>: 'Timeline Overview', 'Chronology', 'Events', 'Timeline', 'Event Timeline', 'Operational Timeline', 'Timeline Summary', 'Schedule'. These are hallucinations from generic templates. Good examples for an Italian Navy briefing: 'Cronologia Operativa — 13 Aprile 2026', 'Sequenza Eventi T-004', 'Fasi della Missione MV-2026-0413', 'Log Operativo'. "126 "The <section> MUST have data-header-title='SHORT TITLE' AND the body MUST contain a <div class='timeline'> with <div class='event'> children — WITHOUT THIS STRUCTURE, the vertical rail and circular markers will NOT render and the slide will degrade to plain text. Do NOT produce <ul><li>HH:MM Event</li></ul> for chronological content — always use the .timeline/.event structure. Use this EXACT structure:\n"127 "<section data-header-title='SHORT SLIDE TITLE'>\n"128 " <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n"129 " <div class='timeline-layout'>\n"130 " <div class='timeline'>\n"131 " <div class='event'><span class='time'>HH:MM</span> Event description</div>\n"132 " <div class='event alert'><span class='time'>HH:MM</span> Critical event (red marker)</div>\n"133 " <div class='event gold'><span class='time'>HH:MM</span> Milestone event (gold marker)</div>\n"134 " <!-- Repeat for 4-8 events -->\n"135 " </div>\n"136 " <div class='timeline-aside'> <!-- OPTIONAL: include only if the source content has a natural sidebar (next-actions, summary, related contacts, etc.) -->\n"137 " <div class='card highlight'>\n"138 " <div class='label'>PROSSIME AZIONI</div>\n"139 " <ul><li>Action one</li><li>Action two</li></ul>\n"140 " </div>\n"141 " <div class='card'>\n"142 " <div class='label'>RIEPILOGO FINALE</div>\n"143 " <ul><li>Summary point one</li><li>Summary point two</li></ul>\n"144 " </div>\n"145 " </div>\n"146 " </div>\n"147 " <aside class='notes'>Speaker notes.</aside>\n"148 "</section>\n"149 "Vertical timeline with circular markers along a left rail. Each event is <div class='event'> with a leading <span class='time'> for the timestamp and the description text after the closing </span>. Add class='event alert' for critical events (red marker with glow halo) and class='event gold' for milestones (gold marker). Extract real timestamps and event descriptions from the source content (4-8 events ideal). "150 "The <div class='timeline-aside'> right column is OPTIONAL — include it ONLY when the source content naturally has sidebar information (next-actions, summary of contacts, related entities, follow-up items). Each aside card has a <div class='label'> header and a short <ul> or <p> body. Use class='card highlight' for the primary aside card (gold accent) and plain class='card' for secondary cards. When there is no sidebar content, OMIT the whole .timeline-layout wrapper and .timeline-aside — use just a bare <div class='timeline'>. "151 "Anti-pattern to AVOID: <ul><li>10:00 - Event</li><li>11:00 - Event</li></ul> — this completely bypasses the timeline visual.",152153 "marina-comparison": "Create a Marina Militare COMPARISON / TRACKING TABLE slide. "154 "CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing what this specific table shows, written in the SAME LANGUAGE as the source document. Extract the heading from the actual source content — do NOT invent generic English phrases. BANNED headings that you MUST NOT use as <h2>: 'Comparison Table', 'Data Table', 'Summary Table', 'Overview Table', 'Tracking Table', 'Comparison', 'Summary', 'Overview', 'Data'. These are hallucinations from generic templates. Good examples for an Italian Navy briefing: 'Tabella Tracce — T+12 h', 'Contatti Attivi nello Stretto', 'Confronto Assetti Alleati', 'Situazione Tattica Corrente'. "155 "The <section> MUST have data-header-title='SHORT TITLE' AND the body MUST contain a real <table> with <thead> and <tbody> — WITHOUT THE TABLE, this layout has no visual and degrades to plain text columns. Do NOT produce two-column <div> layouts or bullet lists for tabular content. Use this EXACT structure:\n"156 "<section data-header-title='SHORT SLIDE TITLE'>\n"157 " <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n"158 " <p class='section-label'>Optional context line (row count, source, etc.)</p>\n"159 " <table>\n"160 " <thead><tr><th>Col 1</th><th>Col 2</th><th>Col 3</th><th>Status</th></tr></thead>\n"161 " <tbody>\n"162 " <tr><td>Row 1</td><td>Value</td><td>Value</td><td><span class='tag commercial'>Commercial</span></td></tr>\n"163 " <!-- Repeat for 4-10 rows -->\n"164 " </tbody>\n"165 " </table>\n"166 " <aside class='notes'>Speaker notes.</aside>\n"167 "</section>\n"168 "Wide HTML table with a navy header (gold accent column labels) and bordered rows. For status pills inside cells, use <span class='tag X'>...</span> where X is one of: commercial (blue), allied (green), friendly (cyan), fishing (olive), high-threat (red), humanitarian (gold). For inline status indicators, use <span class='status-dot Y'></span> where Y is one of: green, gold, red, blue. To highlight a critical row, add class='alert' to the <tr> (red row background). Extract real comparison/tracking data from the source content (4-10 rows ideal). Anti-pattern to AVOID: producing <div> column layouts or <ul> lists instead of a real <table>.",169170 "marina-outro": "[THEME GATE: this template is valid ONLY when the active deck theme is 'marina'. If the active theme is 'emblema', DO NOT emit this structure — switch to the 'emblema-closing' template instead, which is the Emblema brand closing slide. The Marina .closing-slide class is unstyled in the Emblema theme and would render as a blank white slide.] "171 "Create a Marina Militare CLOSING slide. The <section> MUST start with class='closing-slide' — WITHOUT THIS CLASS, the Marina Militare crest will NOT render and the slide will collapse to a plain navy background with default text. Use this EXACT structure:\n"172 "<section class='closing-slide'>\n"173 " <h1>Grazie</h1>\n"174 " <p class='hashtag'>#NoiSiamoLaMarina</p>\n"175 " <aside class='notes'>Closing slide.</aside>\n"176 "</section>\n"177 "The Marina Militare crest is injected by the theme via a CSS pseudo-element above the headline — do NOT add an <img> for the logo. The <h1> renders in Montserrat bold (use 'Grazie' / 'Grazie dell'attenzione' / 'Domande?' for Italian briefings, 'Thank You' for English). The <p class='hashtag'> renders in normal weight below — keep it relevant to the unit/operation (e.g. #NoiSiamoLaMarina, #MarinaMilitare). Keep all class names EXACTLY as shown — class='closing-slide' on the section is LOAD-BEARING.",178179 # ─── Emblema layouts ───180 "emblema-cover": "Create an Emblema COVER slide. The <section> MUST contain a <div class='slide-cover'> with two child columns; WITHOUT THIS WRAPPER the title will not render against the brand split-panel layout (white left, full-bleed teal right). CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h1> values: 'Welcome', 'Title Slide', 'Cover', 'Introduction', 'Presentation'. Good examples: 'Building thoughtful AI products.', 'Operazione Mediterraneo Sicuro 2026'. Use this EXACT structure:\n<section>\n <div class='slide-cover'>\n <div class='left'>\n <div class='brand'><span class='eyebrow'>SHORT EYEBROW (2-4 words, uppercase)</span></div>\n <div>\n <h1>Headline derived<br>from source content.</h1>\n <p class='subtitle'>One- or two-line subtitle pulled from the source.</p>\n </div>\n <p class='meta'>AUTHOR · DATE · VERSION</p>\n </div>\n <div class='right'></div>\n </div>\n <aside class='notes'>One-line speaker note.</aside>\n</section>\nThe <div class='right'> is intentionally empty -- the theme renders the teal panel + decorative gradients via CSS. The <h1> may use a <br> to break across lines. The eyebrow is the small uppercase label above the title, the subtitle is one or two lines of secondary copy, and the meta line carries author / date / version separated by middots. Do NOT add background-color or color inline styles -- the theme owns them.",181 "emblema-agenda": "Create an Emblema AGENDA slide. The body MUST contain a <div class='slide-pad slide-agenda'> with an <ol> of agenda items; WITHOUT THE .slide-agenda CLASS the numbered counters and 2-column grid will not render and the items will fall back to default <ol> bullets. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Agenda', 'Outline', 'Overview', 'Topics', 'Schedule', 'Today'. Good examples: 'What we'll cover today.', 'Programma del briefing.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-agenda'>\n <div>\n <span class='eyebrow'>Agenda</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <ol>\n <li><span class='topic'>Topic name</span><span class='duration'>X min</span></li>\n <!-- 4-8 items total -->\n </ol>\n </div>\n <div class='brand-mark'>Brand</div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nEach <li> MUST contain a <span class='topic'> for the topic and <span class='duration'> for the duration -- these are CSS-targeted for two-column alignment. The leading numbered counter (01, 02, ...) is auto-generated by CSS counter, do NOT add it as text. Use 4-8 items; fewer collapses the layout, more wraps awkwardly. Anti-pattern to AVOID: <ul> bullets or plain <p> lines without <span> wrappers.",182 "emblema-section": "Create an Emblema SECTION DIVIDER slide. The body MUST contain a <div class='slide-section'> with a <div class='num'> and a <div class='meta'>; WITHOUT THIS STRUCTURE the big section number, teal background, and 2-column layout will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Section Two', 'Part One', 'Section Divider', 'Next Section', 'Chapter', 'Overview'. Good examples: 'The opportunity in front of us.', 'I numeri dietro la storia.'. Use this EXACT structure:\n<section>\n <div class='slide-section'>\n <div class='num'>NN</div>\n <div class='meta'>\n <span class='label'>Section N or Capitolo N</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>One- or two-sentence framing pulled from the source content.</p>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <div class='num'> renders as a 280px Plex Sans Light digit pair (use '01', '02', etc.). The <span class='label'> is the small uppercase label above the section title. The <p> body is restrained to 1-3 sentences -- this is a divider, not a content slide. Anti-pattern to AVOID: cramming bullet lists or KPI cards into a section divider.",183 "emblema-statement": "Create an Emblema STATEMENT / PULL QUOTE slide. The body MUST contain a <div class='slide-statement'> with a <p class='quote'> that highlights one phrase via <em>; WITHOUT THE <em> the teal accent on the key phrase will not appear and the slide loses its rhetorical hook. CRITICAL HEADING RULE: The pull quote MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. The <em> wraps the SINGLE most important phrase in the quote (2-6 words). Use this EXACT structure:\n<section>\n <div class='slide-statement'>\n <div>\n <p class='quote'>The quote text with one <em>key phrase</em> highlighted in teal.</p>\n <p class='attribution'>— Source or speaker, ROLE</p>\n </div>\n </div>\n <div class='corner-index'>SECTION <span class='num'>NN</span> · SLIDE_NUM</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nDo NOT use <h1> or <h2> on this layout -- the quote IS the headline and is styled via .slide-statement .quote (64px Plex Light). Use exactly ONE <em> in the quote (zero loses the accent, multiple looks busy). The <p class='attribution'> is uppercase tracked-out text below the quote. Anti-pattern to AVOID: <blockquote> or <h2> instead of <p class='quote'>.",184 "emblema-two-col": "Create an Emblema TWO-COLUMN slide. The body MUST contain a <div class='slide-grid-2 slide-two-col'> with exactly two child <div>s; WITHOUT BOTH CLASSES the 50/50 grid and centered alignment will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Two Columns', 'Comparison', 'Side by Side', 'Overview', 'Summary'. Good examples: 'Knowledge work is drowning.', 'Domanda e offerta.'. Use this EXACT structure:\n<section>\n <div class='slide-grid-2 slide-two-col'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>Body paragraph 1 from source.</p>\n <p class='muted' style='font-size:16px'>Source or footnote.</p>\n </div>\n <div class='visual'>\n <!-- Either an <img> or a decorative SVG. Theme styles .visual as an et-50 panel. -->\n </div>\n </div>\n <div class='brand-mark'>Brand</div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nLeft column is text; right column has class='visual' which the theme styles as a soft teal-tinted panel for an SVG/image. If no visual is available, leave the .visual div empty -- do NOT replace it with a body paragraph. Anti-pattern to AVOID: 50/50 with TWO text columns (use emblema-bullets or emblema-compare instead).",185 "emblema-bullets": "Create an Emblema BULLET TAKEAWAYS slide. The body MUST contain a <div class='slide-pad slide-bullets'> with a <ul> of 3-4 <li> items; EACH <li> MUST contain a <span class='marker'>NN</span> and a <div> wrapping <h4>+<p>; WITHOUT THE .marker SPAN the numbered teal-tint circles will not render and bullets will fall back to plain dots. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Key Takeaways', 'Bullet Points', 'Summary', 'Main Points', 'Highlights', 'Overview'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-bullets'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <ul>\n <li>\n <span class='marker'>01</span>\n <div>\n <h4>Bullet headline.</h4>\n <p>1-2 sentence body for this bullet.</p>\n </div>\n </li>\n <!-- 3-4 bullets total -->\n </ul>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse 3 or 4 bullets in a 2-column grid. Marker numbers MUST use leading zeros: 01, 02, 03, 04. Anti-pattern to AVOID: plain <ul><li>text</li></ul> without the marker/div structure -- the theme requires the wrapper to render the numbered circles.",186 "emblema-image": "Create an Emblema FULL-BLEED IMAGE slide. The body MUST contain a <div class='slide-image'> with a <div class='overlay'> child for the text; WITHOUT THIS STRUCTURE the bottom dark-gradient overlay and white text positioning will not render. The .slide-image background is a teal gradient by default; an <img> tag may be appended INSIDE .slide-image to override. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Image', 'Photo', 'Visual', 'Picture', 'Background'. Use this EXACT structure:\n<section>\n <div class='slide-image'>\n <!-- Optional: <img src='...' alt='...' style='position:absolute;inset:0;width:100%;height:100%;object-fit:cover;'> -->\n <div class='overlay'>\n <span class='eyebrow eyebrow--inverse'>SHORT LOCATION OR LABEL</span>\n <h2>Image headline pulled from source.</h2>\n <p>Caption or context, 1-2 sentences.</p>\n </div>\n </div>\n <div class='page-num' style='color: rgba(255,255,255,0.7);'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe .eyebrow MUST also have eyebrow--inverse so the small uppercase label renders in light teal against the dark background. Page number is forced to translucent white via inline style (the only inline color allowed on this layout, and only because the chrome lives outside the gradient). Anti-pattern to AVOID: omitting the .overlay wrapper -- the text falls through onto a transparent background.",187 "emblema-magazine": "Create an Emblema MAGAZINE SPREAD slide. The body MUST contain a <div class='slide-mag'> with a <div class='photo-col'> on the left and <div class='text-col'> on the right; WITHOUT BOTH CHILDREN the asymmetric photo-left / text-right grid will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Magazine', 'Spread', 'Story', 'Customer Story', 'Case Study'. Good examples: 'From four spreadsheets to one workflow.', 'Da quattro fogli a un flusso.'. Use this EXACT structure:\n<section>\n <div class='slide-mag'>\n <div class='photo-col'>\n <div class='photo photo--var2'><span class='photo-tag'>Photo · MM.YYYY</span></div>\n </div>\n <div class='text-col'>\n <span class='eyebrow'>Customer story · Subject</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p class='body'>Long-form body paragraph -- 3-5 sentences pulled from the source.</p>\n <div class='meta'>\n <span><strong>Photographed at</strong> — Place</span>\n <span><strong>Words by</strong> — Author</span>\n <span><strong>Month YYYY</strong></span>\n </div>\n </div>\n </div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nPick ONE of .photo--var1 through .photo--var8 to set the gradient placeholder colour. To use a real image, replace the .photo div with an <img> covering the photo-col area. The .meta uses <strong> labels for byline-style metadata. Anti-pattern to AVOID: <table> or <ul> instead of the .meta <span> structure.",188 "emblema-two-img": "Create an Emblema TWO IMAGES slide. The body MUST contain a <div class='slide-pad slide-two-img'> with a <div class='grid'> of exactly two <div class='item'> children; WITHOUT THE .grid AND .item CLASSES the side-by-side layout and per-item caption styling will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Two Images', 'Photos', 'Pictures', 'Gallery', 'Examples'. Good examples: 'Two teams, two transformations.', 'Field studies — Spring 2026'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-two-img'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='item'>\n <div class='photo photo--var1'><span class='photo-tag'>Tag 1</span></div>\n <h4>Item 1 title</h4>\n <p>One- or two-sentence description.</p>\n <div class='cap'>— Credit, Date</div>\n </div>\n <div class='item'>\n <div class='photo photo--var6'><span class='photo-tag'>Tag 2</span></div>\n <h4>Item 2 title</h4>\n <p>One- or two-sentence description.</p>\n <div class='cap'>— Credit, Date</div>\n </div>\n </div>\n </div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nExactly TWO .item children -- adding a third breaks the equal-width grid. Each .item has photo + h4 + p + .cap. Anti-pattern to AVOID: omitting the .photo wrapper around an <img> (the .photo class is what gives images their teal-tint placeholder fallback).",189 "emblema-grid-img": "Create an Emblema 2x2 IMAGE GRID slide. The body MUST contain a <div class='slide-grid-img'> with a <div class='header'> and a <div class='grid'> of exactly four <div class='photo'> children; WITHOUT THIS STRUCTURE the full-bleed teal background, white header, and 2x2 image grid will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Image Grid', 'Gallery', 'Photos', '2x2', 'Examples'. Use this EXACT structure:\n<section>\n <div class='slide-grid-img'>\n <div class='header'>\n <span class='eyebrow eyebrow--inverse'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>One- or two-sentence framing.</p>\n </div>\n <div class='grid'>\n <div class='photo photo--var4'><span class='photo-tag'>Caption 1</span></div>\n <div class='photo photo--var5'><span class='photo-tag'>Caption 2</span></div>\n <div class='photo photo--var7'><span class='photo-tag'>Caption 3</span></div>\n <div class='photo photo--var8'><span class='photo-tag'>Caption 4</span></div>\n </div>\n </div>\n <div class='page-num' style='color: rgba(255,255,255,0.7);'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse eyebrow--inverse for light teal text against the dark background. Pick distinct .photo--varN values for visual variety. Exactly 4 photos. Anti-pattern to AVOID: 3 or 5 photos -- breaks the 2x2 grid.",190 "emblema-hero-centered": "Create an Emblema CENTERED HERO slide. The body MUST contain a <div class='slide-hero-centered'> with a <div class='header'>, a <div class='photo'>, and a <p class='caption'>; WITHOUT THIS STRUCTURE the centered alignment, hero photo sizing, and italic caption styling will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Hero', 'Image', 'Photo', 'Featured Image'. Good examples: 'The room where the first prototype shipped.', 'La sala riunioni del comando.'. Use this EXACT structure:\n<section>\n <div class='slide-hero-centered'>\n <div class='header'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n </div>\n <div class='photo photo--var2'><span class='photo-tag'>Photo · MM.YYYY</span></div>\n <p class='caption'>Italic caption text. <em>Photographed by NAME · Month YYYY</em></p>\n </div>\n <div class='brand-mark'>Brand</div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe .photo grows to fill the available vertical space between header and caption. To use a real <img>, place it INSIDE the .photo div with object-fit:cover. Anti-pattern to AVOID: large <img> without the .photo wrapper -- inherits composer's max-width:100% rule and shrinks awkwardly.",191 "emblema-hero-card": "Create an Emblema HERO + FLOATING CARD slide. The body MUST contain a <div class='slide-hero-card'> with a full-bleed <div class='photo'> and a floating <div class='card'>; WITHOUT BOTH CHILDREN the hero image background and white card with shadow will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Spotlight', 'Featured', 'Card', 'Highlight'. Good examples: 'Inside the launch of our largest deployment yet.', 'Dietro le quinte della missione X.'. Use this EXACT structure:\n<section>\n <div class='slide-hero-card'>\n <div class='photo photo--var5'><span class='photo-tag'>SHORT TAG</span></div>\n <div class='card'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>1-3 sentence body pulled from source.</p>\n <div class='meta'>Subject · Place · Month YYYY</div>\n </div>\n </div>\n <div class='page-num' style='color: rgba(255,255,255,0.7);'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe .card floats absolute bottom-left over the hero photo with a drop-shadow. The .meta is a tracked-out uppercase metadata line at the card's bottom. Anti-pattern to AVOID: putting all content inside .photo -- the card layer is what creates the magazine-style legibility against the photo.",192 "emblema-collage": "Create an Emblema IMAGE COLLAGE slide. The body MUST contain a <div class='slide-collage'> with a <div class='text-col'> and a <div class='stack'> of exactly THREE <div class='photo'> children; WITHOUT THE .stack AND THREE PHOTOS the rotated layered photo collage will not render (each photo has a nth-child rule that positions it). CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Collage', 'Gallery', 'Photos', 'Year in Review'. Use this EXACT structure:\n<section>\n <div class='slide-collage'>\n <div class='text-col'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>One paragraph framing the collage.</p>\n <p class='caption'>Optional caption identifying the photos clockwise.</p>\n </div>\n <div class='stack'>\n <div class='photo photo--var1'><span class='photo-tag'>01</span></div>\n <div class='photo photo--var3'><span class='photo-tag'>02</span></div>\n <div class='photo photo--var5'><span class='photo-tag'>03</span></div>\n </div>\n </div>\n <div class='brand-mark'>Brand</div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nExactly THREE .photo children -- the CSS targets :nth-child(1), :nth-child(2), :nth-child(3) for rotation/positioning. Anti-pattern to AVOID: 2 or 4 photos -- breaks the layered effect.",193 "emblema-stat": "Create an Emblema BIG STAT slide. The body MUST contain a <div class='slide-stat'> with a <div class='number'> and a <div class='copy'>; WITHOUT THIS STRUCTURE the 280px teal stat number and side-by-side copy will not render. CRITICAL HEADING RULE: The <h3> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h3> values: 'Big Number', 'Stat', 'Metric', 'Key Number'. The <h3> is the supporting headline -- the BIG NUMBER itself goes in <div class='number'> NOT in a heading. Use this EXACT structure:\n<section>\n <div class='slide-stat'>\n <div class='number'>NN<span class='unit'>%</span></div>\n <div class='copy'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h3>One-sentence interpretation derived from the source.</h3>\n <p>1-2 sentence body explaining the number.</p>\n </div>\n </div>\n <div class='brand-mark'>Brand</div>\n <div class='page-num'>NN / NN</div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <div class='number'> takes the bare numeric (no euro/dollar sign in front -- prefix those into the <span class='unit'> if needed; e.g. '<span class=\"unit\">€</span>1.2M'). The <span class='unit'> renders at half size in lighter teal. Use a single number per slide -- for multiples use emblema-kpis. Anti-pattern to AVOID: <h2>NN%</h2> -- defeats the purpose of the layout.",194 "emblema-kpis": "Create an Emblema KPI GRID slide. The body MUST contain a <div class='slide-pad slide-kpis'> with a <div class='grid'> of BETWEEN 3 AND 9 <div class='kpi'> children — prefer multiples of 3 (3, 6, or 9); WITHOUT THE .kpi WRAPPER each metric loses its top teal accent, soft background, and large-number styling. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'KPIs', 'Key Metrics', 'Numbers', 'Performance', 'Dashboard', 'Overview', 'Stats'. Good examples: 'Three numbers we're proud of.', 'Tre KPI del Q1 2026'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-kpis'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='kpi'>\n <div class='num'>NN<span class='delta'>↑ X</span></div>\n <h4>KPI title</h4>\n <p>One-line context derived from source.</p>\n </div>\n <!-- 3 to 9 KPIs, prefer multiples of 3 -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse 3, 6, or 9 KPIs based on content density: 3 (single row) for headline metrics, 6 (2 rows) for paired-metric dashboards, 9 (3 rows) for full quarterly scorecards. CSS uses grid-template-columns: repeat(3, 1fr) — counts that aren't multiples of 3 leave a partial last row. The <span class='delta'> is OPTIONAL and renders in green by default; for a NEGATIVE delta change the arrow to ↓. Anti-pattern to AVOID: more than 9 KPIs (likely belong in emblema-table or emblema-chart instead — the kpi card design loses impact at higher counts). Counts that aren't multiples of 3 (4, 5, 7, 8) are acceptable but visually weaker than a clean 3/6/9 grid.",195 "emblema-compare": "Create an Emblema BEFORE/AFTER COMPARISON slide. The body MUST contain a <div class='slide-pad slide-compare'> with a <div class='grid'> containing a .col, a .arrow span, and a .col.col--after; WITHOUT THE 1fr-80px-1fr GRID and the .col--after class on the right column, the highlighted 'after' state will not render and the arrow will not be centered. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Comparison', 'Before/After', 'Then/Now', 'Old vs New'. Good examples: 'How a quarterly review changes after Emblema.', 'Da manuale ad automatico.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-compare'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='col'>\n <h4>BEFORE / OLD STATE</h4>\n <ul>\n <li>Pain point 1</li>\n <li>Pain point 2</li>\n <li>Pain point 3</li>\n </ul>\n </div>\n <div class='arrow'>→</div>\n <div class='col col--after'>\n <h4>AFTER / NEW STATE</h4>\n <ul>\n <li>Resolution 1</li>\n <li>Resolution 2</li>\n <li>Resolution 3</li>\n </ul>\n </div>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nCRITICAL: the AFTER column MUST have BOTH .col AND .col--after classes -- without .col--after it loses the teal-tint background and teal border that signal 'this is the improved state'. Match item count between columns (3-5 items each). Anti-pattern to AVOID: <table> with 2 columns, or putting both lists in a single .col div.",196 "emblema-chart": "Create an Emblema CHART slide. The body MUST contain a <div class='slide-pad slide-chart'> with a <div class='chart-wrap'> wrapping the visualization, plus a <div class='legend'>; WITHOUT THE .chart-wrap container the chart panel border and padding will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Chart', 'Graph', 'Data', 'Trend'. Good examples: 'Revenue lift across the cohort.', 'Andamento clienti 2024-2026'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-chart'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='chart-wrap'>\n <!-- An inline <svg viewBox='0 0 W H'> for bar/line/area chart, OR a placeholder. -->\n <svg viewBox='0 0 800 320' style='width:100%;height:auto'>\n <!-- bars/lines using <rect>, <line>, <path>; reuse var(--et-500) color -->\n </svg>\n </div>\n <div class='legend'>\n <span><i style='background:var(--et-500)'></i>Series 1</span>\n <span><i style='background:var(--et-300)'></i>Series 2</span>\n </div>\n <p class='muted' style='font-size:14px;margin-top:1em'>Source — reference</p>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <i> in legend items is intentionally empty -- CSS sizes it as a 12px swatch. Inline style `background:var(--et-500)` is the ONLY allowed inline color (legend swatches need it because there is no enumerable class for arbitrary swatch colors). Anti-pattern to AVOID: <table> instead of an SVG chart -- use emblema-table for data tables.",197 "emblema-table": "Create an Emblema DATA TABLE slide. The body MUST contain a <div class='slide-pad slide-table'> with a real <table> containing <thead>, <tbody>, and optionally <tfoot>; WITHOUT A REAL <table> the column-aligned numerics and uppercase tracked-out header styling will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Data Table', 'Table', 'Numbers', 'Financials', 'Metrics'. Good examples: 'Q1 2026 financial summary.', 'Bilancio operativo del primo trimestre.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-table'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <table>\n <thead><tr><th>Metric</th><th class='num'>Period A</th><th class='num'>Period B</th><th class='num'>Delta</th></tr></thead>\n <tbody>\n <tr><td class='label'>Row name</td><td class='num'>1,234</td><td class='num'>2,345</td><td class='num up'>+90%</td></tr>\n <!-- 4-8 rows -->\n </tbody>\n <tfoot><tr><td>Total</td><td class='num'>X</td><td class='num'>Y</td><td class='num up'>Z</td></tr></tfoot>\n </table>\n <p class='src'>Source — reference</p>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nNumeric columns MUST have class='num' on every <th> and <td> -- this right-aligns and applies tabular-nums. For deltas, use class='up' (green), class='down' (red), or class='flat' (grey) on the <td>. The class='label' on the first body cell makes the row label semibold. Anti-pattern to AVOID: <div> grids with manual alignment -- use a real table.",198 "emblema-matrix": "Create an Emblema COMPARISON MATRIX slide. The body MUST contain a <div class='slide-pad slide-matrix'> with a <table> whose <thead> has EXACTLY ONE <th class='featured'>; WITHOUT .featured on one column the 'OUR PICK' badge and teal column highlight will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Comparison', 'Matrix', 'Options', 'Compare'. Good examples: 'Why Emblema vs the build/buy alternatives.', 'Confronto con le alternative di mercato.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-matrix'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <table>\n <thead><tr><th>Feature</th><th>Option A</th><th class='featured'>Emblema</th><th>Option C</th></tr></thead>\n <tbody>\n <tr><td>Capability label</td><td><span class='no'>×</span></td><td class='featured'><span class='yes'>✓</span></td><td><span class='partial'>partial</span></td></tr>\n <!-- 4-8 rows -->\n </tbody>\n </table>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nEXACTLY ONE <th class='featured'> -- having two means the 'OUR PICK' badge appears twice and the visual hierarchy collapses. Each row's cell under the featured column ALSO needs class='featured' so it inherits the teal-tint background. Cell content uses <span class='yes'>✓</span>, <span class='no'>×</span>, or <span class='partial'>partial</span>. Anti-pattern to AVOID: emoji ✓/✗ instead of the styled span variants -- emoji bypasses the theme color logic.",199 "emblema-spec": "Create an Emblema SPEC SHEET slide. The body MUST contain a <div class='slide-pad slide-spec'> with a <div class='spec-grid'> of 2 or 4 <div class='spec-section'> children, each with an <h4> and a <dl>; WITHOUT THIS STRUCTURE the 2-column spec layout, dashed dividers, and uppercase teal section headers will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Specifications', 'Specs', 'Details', 'Properties', 'Information'. Good examples: 'Platform technical specifications.', 'Specifiche piattaforma ITS Margottini.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-spec'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='spec-grid'>\n <div class='spec-section'>\n <h4>Section Name<span class='num'>01</span></h4>\n <dl>\n <div><dt>Key</dt><dd>Value <span class='badge'>BADGE</span></dd></div>\n <div><dt>Key</dt><dd>Value</dd></div>\n <!-- 3-6 entries per section -->\n </dl>\n </div>\n <!-- 2 or 4 .spec-section blocks -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <dl> uses a non-standard <div> wrapper around each <dt>/<dd> pair -- this is REQUIRED because the CSS uses .slide-spec dl > div for grid alignment. The <span class='badge'> is OPTIONAL inline highlight inside a <dd>. Anti-pattern to AVOID: flat <dl><dt><dd></dl> without div wrappers (loses the per-row alignment) or <table> (visual is wrong for spec sheets).",200 "emblema-status": "Create an Emblema PROJECT STATUS slide. The body MUST contain a <div class='slide-pad slide-status'> with a <table> whose status cells contain <span class='dot dot--COLOR'> elements; WITHOUT THE .dot--COLOR CLASSES the green/amber/red/gray RAG indicators will not render and the table looks like generic data. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Status', 'RAG', 'Project Status', 'Portfolio', 'Health'. Good examples: 'Q1 portfolio — status at week 11.', 'Stato del portafoglio progetti.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-status'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <table>\n <thead><tr><th>Project</th><th class='owner'>Owner</th><th class='note'>Note</th><th class='status'>Status</th></tr></thead>\n <tbody>\n <tr><td>Project A</td><td class='owner'>Owner</td><td class='note'>Short note.</td><td class='status'><span class='dot dot--green'></span></td></tr>\n <tr><td>Project B</td><td class='owner'>Owner</td><td class='note'>Short note.</td><td class='status'><span class='dot dot--amber'></span></td></tr>\n <tr><td>Project C</td><td class='owner'>Owner</td><td class='note'>Short note.</td><td class='status'><span class='dot dot--red'></span></td></tr>\n <!-- 4-8 rows -->\n </tbody>\n </table>\n <div class='legend'>\n <span><span class='dot dot--green'></span>On track</span>\n <span><span class='dot dot--amber'></span>At risk</span>\n <span><span class='dot dot--red'></span>Off track</span>\n <span><span class='dot dot--gray'></span>Not started</span>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nSTATUS COUPLING RULE: dot--green = on track, dot--amber = at risk, dot--red = off track, dot--gray = not started. Use the dot color that matches the SEMANTICS of each project's status -- do NOT assign colors arbitrarily. Anti-pattern to AVOID: emoji 🟢🟡🔴 instead of styled spans (no theme control over color), or omitting the dot wrapper inside the .status td.",201 "emblema-process": "Create an Emblema PROCESS / STEPS slide. The body MUST contain a <div class='slide-pad slide-process'> with a <div class='steps'> of BETWEEN 4 AND 8 <div class='step'> children — prefer multiples of 4 (4 or 8); WITHOUT THE .step WRAPPER each card loses its left teal border and uppercase step label. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Process', 'Steps', 'Workflow', 'How it works', 'Methodology'. Good examples: 'How an Emblema rollout flows.', 'Le quattro fasi del nostro metodo.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-process'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='steps'>\n <div class='step'>\n <div class='icon'><svg viewBox='0 0 22 22'><!-- 22x22 icon --></svg></div>\n <span class='num'>STEP 01</span>\n <h4>Step title</h4>\n <p>1-2 sentences describing the step.</p>\n </div>\n <!-- 4 to 8 .step children, prefer multiples of 4 -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse 4 (single row) or 8 (two rows) based on process complexity. CSS uses grid-template-columns: repeat(4, 1fr) — counts other than 4 or 8 (5, 6, 7) produce a partial last row that's acceptable but visually unbalanced. Use leading zeros on the step numbers (01, 02, ..., 0N). The <div class='icon'> can be empty if no icon is available -- it still renders as a teal-tinted square placeholder. Anti-pattern to AVOID: fewer than 4 steps (use emblema-statement or emblema-bullets for short sequences). Counts other than 4 or 8 are acceptable when content has that many genuine steps, but 4 and 8 are visually cleanest.",202 "emblema-timeline": "Create an Emblema HORIZONTAL TIMELINE slide. The body MUST contain a <div class='slide-pad slide-timeline'> with a <div class='track'> wrapping a <div class='line'> and a <div class='points'> of EXACTLY FIVE <div class='point'> children; STATE COUPLING RULE: at most ONE <div class='point now'> (the present moment), all earlier points use <div class='point done'>, all later points use plain <div class='point'>. Without these state classes the dots are blank circles. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Timeline', 'Schedule', 'Roadmap', 'Milestones', 'Plan'. Good examples: 'A year of Emblema, milestone by milestone.', 'I cinque snodi del 2025.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-timeline'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='track'>\n <div class='line'></div>\n <div class='points'>\n <div class='point done'><div class='dot'></div><span class='date'>Q1 2025</span><h4>Milestone</h4><p>1-line description.</p></div>\n <div class='point done'><div class='dot'></div><span class='date'>Q2 2025</span><h4>Milestone</h4><p>1-line description.</p></div>\n <div class='point now'><div class='dot'></div><span class='date'>NOW</span><h4>Current focus</h4><p>1-line description.</p></div>\n <div class='point'><div class='dot'></div><span class='date'>Q4 2025</span><h4>Upcoming</h4><p>1-line description.</p></div>\n <div class='point'><div class='dot'></div><span class='date'>Q1 2026</span><h4>Upcoming</h4><p>1-line description.</p></div>\n </div>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nEXACTLY 5 .point children -- the CSS uses grid-template-columns: repeat(5, 1fr). At most one .now; .done points must come before .now in DOM order; plain .point come after. The <div class='line'> is intentionally empty (CSS-styled track). Anti-pattern to AVOID: vertical <ul><li> timeline (use a different layout for vertical chronology) or 4/6 points (breaks the grid).",203 "emblema-roadmap": "Create an Emblema ROADMAP slide. The body MUST contain a <div class='slide-pad slide-roadmap'> with a <div class='phases'> of EXACTLY THREE <div class='phase'> children: 'Now' (default), 'Next' (.phase--next), and 'Later' (.phase--later); WITHOUT THE .phase--next AND .phase--later modifiers the three phases all render with the same teal header and the time hierarchy is lost. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Roadmap', 'Plan', 'Phases', 'Schedule', 'Timeline'. Good examples: 'What ships next, and what comes after.', 'Roadmap del prodotto 2026.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-roadmap'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='phases'>\n <div class='phase'><header><h4>Now</h4><span class='when'>Q1 2026</span></header><ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul></div>\n <div class='phase phase--next'><header><h4>Next</h4><span class='when'>Q2 2026</span></header><ul><li>Item 1</li><li>Item 2</li></ul></div>\n <div class='phase phase--later'><header><h4>Later</h4><span class='when'>H2 2026</span></header><ul><li>Item 1</li><li>Item 2</li></ul></div>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nPHASE COUPLING RULE: first .phase = default (teal header), second = .phase.phase--next (teal-mid header), third = .phase.phase--later (gray header). The DOM order encodes the time order. Each .phase has a <header> with <h4> + <span class='when'> followed by a <ul>. Anti-pattern to AVOID: 4 phases (breaks the 3-col grid) or omitting the modifier classes (all phases look identical).",204 "emblema-team": "Create an Emblema TEAM GRID slide. The body MUST contain a <div class='slide-pad slide-team'> with a <div class='grid'> of EXACTLY FOUR <div class='person'> children, each with .avatar + h4 + .role + p.bio; WITHOUT THE .person AND .avatar CLASSES the square avatar tiles and team-card layout will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Team', 'Our Team', 'Founders', 'People', 'Who We Are'. Good examples: 'Four people, fifteen years of compounding craft.', 'Il team che fa funzionare tutto.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-team'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='person'>\n <div class='avatar'>FN</div> <!-- Two-letter initials, OR replace with <img src='...' alt='...'> -->\n <h4>Full Name</h4>\n <p class='role'>Role · Department</p>\n <p class='bio'>1-2 sentence bio derived from source content.</p>\n </div>\n <!-- Exactly 4 .person children -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nEXACTLY 4 people -- the CSS uses repeat(4, 1fr). The .avatar div renders as a teal-tinted square; if no real photo, place 2-letter initials inside; if a photo is available, use <img> with width/height filling the .avatar. Anti-pattern to AVOID: <ul> of names (loses the grid + avatar) or 5+ people (rows wrap awkwardly).",205 "emblema-testimonial": "Create an Emblema TESTIMONIAL slide. The body MUST contain a <div class='slide-testimonial'> with a <div class='photo'> on the left and a <div> wrapping <p class='quote'> + <p class='who'> + <p class='who-meta'> on the right; WITHOUT THE .quote CLASS the leading large opening-quote pseudo-element will not render. CRITICAL HEADING RULE: The pull quote MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. The quote is the headline, NOT a separate <h2>. Use this EXACT structure:\n<section>\n <div class='slide-testimonial'>\n <div class='photo'>FN</div> <!-- Initials, OR replace with <img> -->\n <div>\n <p class='quote'>The full quote text from the source.</p>\n <p class='who'>Speaker Name</p>\n <p class='who-meta'>Role · Company · Subject</p>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nDo NOT use <blockquote> or <h2> for the quote -- the <p class='quote'> is what triggers the styled opening-quote pseudo-element and 36px Plex Light styling. The .photo renders as a circular gradient by default; replace with an <img> for a real headshot. Anti-pattern to AVOID: <em>quote</em> wrappers (italics fight the existing styling) or splitting attribution across multiple unstyled <p>.",206 "emblema-logos": "Create an Emblema LOGO CLOUD slide. The body MUST contain a <div class='slide-pad slide-logos'> with a <div class='grid'> of <div class='cell'> children (5 columns); WITHOUT THE .cell WRAPPER the 16:9 logo cells with hairline borders will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Logos', 'Customers', 'Partners', 'Clients', 'Brands'. Good examples: 'Teams that quietly run on Emblema.', 'I clienti che ci hanno scelto nel 2025.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-logos'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='cell'>BRAND</div>\n <!-- 10 or 15 cells (5 per row, 2-3 rows) -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nEach .cell contains a brand wordmark as plain text (Plex Sans 600), or replace with an <img>. Use a multiple of 5 cells (10 or 15) for clean rows. Anti-pattern to AVOID: <ul><li>brand</li></ul> (loses the cell border + 16:9 aspect ratio).",207 "emblema-ui": "Create an Emblema UI SHOWCASE slide. The body MUST contain a <div class='slide-ui'> with a <div class='left'> for copy and a <div class='browser'> for the screenshot mockup; WITHOUT THE .browser WRAPPER the chrome bar with traffic-light dots and URL bar will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'UI', 'Product', 'Screenshot', 'App', 'Interface'. Good examples: 'Inside the workspace -- one panel, one source of truth.', 'Una sola interfaccia per tutto il flusso operativo.'. Use this EXACT structure:\n<section>\n <div class='slide-ui'>\n <div class='left'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <ul>\n <li>Feature point 1</li>\n <li>Feature point 2</li>\n <li>Feature point 3</li>\n <li>Feature point 4</li>\n </ul>\n </div>\n <div class='browser'>\n <div class='chrome'>\n <span></span><span></span><span></span>\n <div class='url'>app.emblema.ai/workspaces</div>\n </div>\n <div class='body'>\n <!-- Optional: <img> for a real screenshot. Otherwise placeholder rows: -->\n <div class='ui-row'></div>\n <div class='ui-row short'></div>\n <div class='ui-card'><h5>Card label</h5><p>Card body.</p></div>\n </div>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe three empty <span> elements in .chrome are the traffic-light dots (CSS sizes them). The .url div is the address bar. Inside .body, use empty <div class='ui-row'> for placeholder lines or insert a real <img> screenshot. Anti-pattern to AVOID: full-bleed <img> outside the .browser wrapper (loses the framing chrome).",208 "emblema-features": "Create an Emblema FEATURE GRID slide. The body MUST contain a <div class='slide-pad slide-features'> with a <div class='grid'> of BETWEEN 6 AND 12 <div class='card'> children — prefer multiples of 3 (6, 9, or 12) so the 3-col grid wraps cleanly, each with .icon + h4 + p; WITHOUT THE .card AND .icon WRAPPERS the cards lose their hairline borders and teal-tint icon tiles. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Features', 'Capabilities', 'What we do', 'Highlights', 'Functionality'. Good examples: 'Six things Emblema does that others don't.', 'Sei capacita' che ci distinguono.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-features'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='card'>\n <div class='icon'><svg viewBox='0 0 22 22'><!-- 22x22 icon --></svg></div>\n <h4>Feature title</h4>\n <p>1-2 sentences derived from source.</p>\n </div>\n <!-- 6 to 12 .card children, prefer multiples of 3 -->\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse 6, 9, or 12 cards based on content density: 6 (3x2) for sparse content, 9 (3x3) for medium-dense feature lists, 12 (3x4) for very dense lists. CSS uses grid-template-columns: repeat(3, 1fr) — counts that aren't multiples of 3 (7, 8, 10, 11) leave a partial last row that's acceptable but visually weaker. The .icon div is a teal-tinted square; the inline SVG is OPTIONAL (an empty .icon still renders as the swatch). Anti-pattern to AVOID: fewer than 6 cards (under-fills the layout, leaves the slide looking sparse). Counts that aren't multiples of 3 are acceptable when content genuinely has that many discrete points, but multiples of 3 always look cleaner.",209 "emblema-pricing": "Create an Emblema PRICING TIERS slide. The body MUST contain a <div class='slide-pad slide-pricing'> with a <div class='grid'> of EXACTLY THREE <div class='tier'> children, with EXACTLY ONE marked .tier--featured (the recommended tier); WITHOUT .tier--featured on one card, the lifted shadow + teal CTA + 'Most popular' badge will not render. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Pricing', 'Plans', 'Tiers', 'Subscriptions', 'Cost'. Good examples: 'Pricing for teams of every size.', 'I tre piani di Emblema.'. Use this EXACT structure:\n<section>\n <div class='slide-pad slide-pricing'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <hr class='divider'>\n </div>\n <div class='grid'>\n <div class='tier'>\n <h4>Tier name</h4>\n <div class='price'>€X<span class='per'>/mo</span></div>\n <p class='desc'>Short description of who it's for.</p>\n <ul class='feats'><li>Feature 1</li><li>Feature 2</li></ul>\n <a class='cta'>Choose plan</a>\n </div>\n <div class='tier tier--featured'>\n <span class='badge'>Most popular</span>\n <h4>Tier name</h4>\n <div class='price'>€Y<span class='per'>/mo</span></div>\n <p class='desc'>Description.</p>\n <ul class='feats'><li>Feature 1</li><li>Feature 2</li><li>Feature 3</li></ul>\n <a class='cta'>Choose plan</a>\n </div>\n <div class='tier'>\n <!-- Same structure as tier 1 -->\n </div>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nTIER COUPLING RULE: EXACTLY ONE .tier--featured (typically the middle one). The .badge span ONLY appears inside .tier--featured. Each tier has h4 (label) + .price (with .per for the period suffix) + .desc + ul.feats + a.cta. Anti-pattern to AVOID: zero or two .tier--featured (visual hierarchy collapses) or 4 tiers (breaks the 3-col grid).",210 "emblema-question": "Create an Emblema BIG QUESTION slide. The body MUST contain a <div class='slide-question'> with a <p class='q'> for the question; the CSS pseudo-element appends a TEAL '?' automatically -- do NOT include the question mark in the text. CRITICAL HEADING RULE: The question text MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. Use this EXACT structure:\n<section>\n <div class='slide-question'>\n <p class='q'>What if the smallest team did the most thoughtful work</p>\n <p class='hint'>Optional one-line hint or framing for the audience.</p>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <p class='q'> renders at 96px Plex Light. The CSS adds a teal '?' via ::after -- so the text inside <p class='q'> must NOT end with a question mark (otherwise you get two: '??'). The <p class='hint'> is OPTIONAL and renders smaller and tracked-out below. Anti-pattern to AVOID: <h1> or <h2> for the question text -- loses the styled '?' pseudo-element.",211 "emblema-definition": "Create an Emblema DEFINITION slide. The body MUST contain a <div class='slide-definition'> with a <div class='term'> on the left (with optional <span class='ipa'>) and a body block on the right with .pos + .meaning + .example; WITHOUT THE .term AND .meaning CLASSES the dictionary-style typography and teal vertical rule will not render. CRITICAL HEADING RULE: The term MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. Use this EXACT structure:\n<section>\n <div class='slide-definition'>\n <div class='term'>\n Word or phrase\n <span class='ipa'>/æIpa\u02c8notæSiΗn/</span>\n </div>\n <div>\n <p class='pos'>Part of Speech</p>\n <p class='meaning'>Plain-language definition derived from source.</p>\n <p class='example'>An italic example sentence using the term.</p>\n </div>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nThe <div class='term'> sits left of a teal vertical rule (CSS-applied). The <span class='ipa'> is OPTIONAL pronunciation; omit if the source doesn't include it. The .pos is the small uppercase part-of-speech label. Anti-pattern to AVOID: <dl><dt><dd> (semantically tempting but the styling is wrong) or wrapping everything in a single <p>.",212 "emblema-code": "Create an Emblema CODE slide. The body MUST contain a <div class='slide-code'> with a <div> for copy on the left and a <pre> on the right; WITHOUT <pre> THE DARK SYNTAX BACKGROUND AND IBM PLEX MONO STYLING will not render -- the theme styles `.slide-code pre` specifically. CRITICAL HEADING RULE: The <h2> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h2> values: 'Code', 'Snippet', 'Example', 'Programming'. Use this EXACT structure:\n<section>\n <div class='slide-code'>\n <div>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h2>HEADING_DERIVED_FROM_SOURCE_CONTENT</h2>\n <p>1-2 sentence framing of what the code does.</p>\n </div>\n <pre>\n<span class='c'># Comment</span>\n<span class='k'>def</span> <span class='f'>function_name</span>(<span class='n'>arg</span>):\n <span class='k'>return</span> <span class='s'>'string'</span>\n </pre>\n </div>\n <aside class='notes'>Speaker notes.</aside>\n</section>\nUse the theme's syntax-color spans inside <pre>: .k = keyword (cyan), .s = string (yellow), .c = comment (grey italic), .f = function name (teal), .n = identifier (white). The <pre>::before pseudo-element shows a 'python' label in the top-right corner -- this is hardcoded in the theme; if you need a different language, the label still says 'python' (limitation of the theme; do not try to override). Indent code with spaces, not tabs. Anti-pattern to AVOID: emitting `<script>` (composer strips it) or styling code with inline color (bypasses the theme).",213 "emblema-closing": "Create an Emblema CLOSING slide. The body MUST contain a <div class='slide-closing'> with a <div class='left'> and a <div class='right'>; WITHOUT BOTH the teal split layout, gradient right panel, and contact-pair layout will not render. CRITICAL HEADING RULE: The <h1> MUST be 3-8 words describing THIS specific slide's content, written in the SAME LANGUAGE as the source document (Italian source -> Italian heading, English source -> English heading). Extract the heading from the actual source content -- do NOT invent generic phrases. BANNED <h1> values: 'Thank you', 'Questions?', 'The end' (use them ONLY if they're the literal close in the source language and tone). Good examples: 'Let's build something thoughtful together.', 'Costruiamo qualcosa di importante.'. Use this EXACT structure:\n<section>\n <div class='slide-closing'>\n <div class='left'>\n <span class='eyebrow'>SHORT EYEBROW</span>\n <h1>Closing headline derived from source.</h1>\n <dl class='contacts'>\n <div><dt>Email</dt><dd>hello@example.com</dd></div>\n <div><dt>Web</dt><dd>example.com</dd></div>\n <div><dt>Phone</dt><dd>+39 02 1234 5678</dd></div>\n <div><dt>Address</dt><dd>City, Country</dd></div>\n </dl>\n </div>\n <div class='right'></div>\n </div>\n <aside class='notes'>Closing slide.</aside>\n</section>\nThe <div class='right'> is intentionally empty -- the theme renders the radial gradient via CSS. The <dl class='contacts'> uses the same div-per-pair wrapper as the spec sheet. Up to 4 contact pairs (CSS uses 2x2 grid). Anti-pattern to AVOID: putting contact info inline as <p> rows -- breaks the styled key:value alignment.",214}215216217# Output-language directive (language-persistence workstream). Values are218# platform locale codes; legacy full names are accepted via the allowlist.219# Anything else (wired upstream output, free-form strings) falls back to220# auto: the value is interpolated into the system prompt, so unknown221# strings must never pass through.222LANGUAGE_NAMES = {223 "de": "German", "en": "English", "es": "Spanish", "fr": "French",224 "it": "Italian", "ja": "Japanese", "ko": "Korean", "nl": "Dutch",225 "pt": "Portuguese", "ru": "Russian", "zh": "Chinese",226}227228229def resolve_language_name(language):230 """Full display name for an allowlisted language value, else None."""231 if not isinstance(language, str):232 return None233 value = language.strip()234 if not value or value == "auto":235 return None236 if value in LANGUAGE_NAMES:237 return LANGUAGE_NAMES[value]238 if value in LANGUAGE_NAMES.values():239 return value240 print(f"[language] unrecognized value {value!r} ignored - using auto", file=sys.stderr)241 return None242243244def language_directive(language):245 """System-prompt suffix forcing the output language; '' when auto."""246 name = resolve_language_name(language)247 if not name:248 return ""249 return (250 f"\n\nOUTPUT LANGUAGE: {name}. All slide text — headings, body copy, "251 f"table/chart labels, and speaker notes — MUST be written in {name}. "252 "This OVERRIDES any 'same language as the source document' rule in the "253 "layout instructions above. Keep proper nouns, product names, and "254 "verbatim quotes in their original language."255 )256257258def build_layout_instruction(template_name):259 """Build additional system prompt instruction for the selected layout template."""260 if not template_name or template_name == "auto":261 return ""262 instruction = TEMPLATE_INSTRUCTIONS.get(template_name, "")263 if instruction:264 result = f"\n\nIMPORTANT LAYOUT INSTRUCTION: The user selected the '{template_name}' layout. {instruction}\nFollow this layout pattern precisely while adapting the content."265 if template_name.startswith("marina-") or template_name.startswith("emblema-"):266 result += "\n\nANIMATION: Do NOT add class='fragment' to any element. The entire slide MUST appear at once — no staggered reveal. Arrow keys move between slides, not between items within a slide."267 return result268 return f"\n\nThe user selected the '{template_name}' layout. Use this layout pattern."269270271# Tags that have no closing tag in HTML — they never go on the open-tag stack.272_VOID_TAGS = frozenset({273 "area", "base", "br", "col", "embed", "hr", "img", "input",274 "link", "meta", "param", "source", "track", "wbr",275})276277# Tags whose closing tag is optional in HTML — leftover open ones at end-of-parse278# are tolerated (browsers auto-close them) so we don't fail otherwise-valid slides.279_OPTIONAL_CLOSE_TAGS = frozenset({280 "p", "li", "td", "th", "tr", "thead", "tbody", "tfoot",281 "option", "dt", "dd", "colgroup", "caption",282})283284# Literal placeholder tokens from the prompt's layout templates. If any survive285# into the output, the model echoed the template scaffold or leaked its scratch286# work instead of producing a real slide — treat that as invalid so we retry.287_PLACEHOLDER_TOKENS = (288 "HEADING_DERIVED_FROM_SOURCE_CONTENT",289 "HEADING_DERIVED_FROM",290 "SHORT EYEBROW",291 "SHORT SLIDE TITLE",292 "...code...",293)294295296class _SlideHtmlValidator(HTMLParser):297 """Parse a slide candidate and record structural problems.298299 A valid slide is EXACTLY ONE well-formed top-level <section> element with no300 stray text or extra elements outside it and no unclosed structural tags.301 """302303 def __init__(self):304 super().__init__(convert_charrefs=True)305 self.stack = []306 self.root_tag = None307 self.top_level_starts = 0308 self.text_outside = False309 self.parse_error = None310311 def _note_start(self, tag):312 if len(self.stack) == 0:313 self.top_level_starts += 1314 if self.root_tag is None:315 self.root_tag = tag316317 def handle_starttag(self, tag, attrs):318 self._note_start(tag)319 if tag not in _VOID_TAGS:320 self.stack.append(tag)321322 def handle_startendtag(self, tag, attrs):323 # Self-closing tag, e.g. <img/> or <br/> — counts toward top level but324 # never goes on the open-tag stack.325 self._note_start(tag)326327 def handle_endtag(self, tag):328 if tag in _VOID_TAGS:329 return330 if tag not in self.stack:331 # Closing tag with no matching open tag → malformed.332 if self.parse_error is None:333 self.parse_error = f"stray closing tag </{tag}>"334 return335 # Pop down to the matching open tag. Optional-close tags (<li>, <p>, …)336 # left open in between are fine — browsers auto-close them. But a337 # structural tag (<div>, <ul>, …) being closed implicitly by an338 # ancestor's end tag means it was never closed → malformed nesting.339 while self.stack and self.stack[-1] != tag:340 popped = self.stack.pop()341 if popped not in _OPTIONAL_CLOSE_TAGS and self.parse_error is None:342 self.parse_error = f"unclosed <{popped}> before </{tag}>"343 if self.stack:344 self.stack.pop()345346 def handle_data(self, data):347 if len(self.stack) == 0 and data.strip():348 self.text_outside = True349350 def error(self, message): # pragma: no cover - legacy HTMLParser hook351 if self.parse_error is None:352 self.parse_error = message353354355def validate_slide_html(html):356 """Validate that `html` is a single, well-formed Reveal.js <section>.357358 Returns (is_valid, reason). `reason` is empty when valid.359 """360 if not html or not html.strip():361 return False, "empty output"362363 stripped = html.strip()364365 for token in _PLACEHOLDER_TOKENS:366 if token in stripped:367 return False, f"contains unfilled template placeholder '{token}'"368369 parser = _SlideHtmlValidator()370 try:371 parser.feed(stripped)372 parser.close()373 except Exception as exc: # noqa: BLE001 - any parse failure means "invalid"374 return False, f"unparseable HTML: {exc}"375376 if parser.parse_error:377 return False, parser.parse_error378 if parser.root_tag != "section":379 return False, f"top-level element is <{parser.root_tag}>, expected <section>"380 if parser.top_level_starts != 1:381 return False, (382 f"expected exactly one top-level <section>, found "383 f"{parser.top_level_starts} top-level elements (leaked reasoning or "384 f"multiple draft slides)"385 )386 if parser.text_outside:387 return False, "stray text outside the <section> (leaked reasoning or prose)"388389 unclosed = [t for t in parser.stack if t not in _OPTIONAL_CLOSE_TAGS]390 if unclosed:391 return False, f"unclosed tag(s): {', '.join(unclosed)}"392393 return True, ""394395396def _strip_code_fences(text):397 text = re.sub(r"^```(?:html)?\s*", "", text, flags=re.MULTILINE)398 text = re.sub(r"```\s*$", "", text, flags=re.MULTILINE)399 return text.strip()400401402def extract_section(html: str) -> str:403 # Extract the <section>...</section> from LLM output, handling markdown404 # fences and reasoning-model output that interleaves prose with one or more405 # draft sections.406 html = _strip_code_fences(html)407408 # NON-greedy so reasoning prose between draft sections is NOT swallowed by a409 # single greedy first-open-to-last-close match (the bug that produced decks410 # full of leaked scratch work and blank renders).411 candidates = re.findall(r"<section\b[\s\S]*?</section>", html, re.IGNORECASE)412 if candidates:413 # Reasoning models put the final answer last — prefer the last candidate414 # that validates; otherwise return the last one and let the caller's415 # retry loop catch the invalid output.416 for candidate in reversed(candidates):417 is_valid, _ = validate_slide_html(candidate)418 if is_valid:419 return candidate.strip()420 return candidates[-1].strip()421422 # No <section> at all — return the raw text so validation fails and the423 # caller retries instead of shipping a wrapped blob of prose.424 return html425426427def _fix_flex_styles(html):428 # Ensure flex children have min-width:0 to prevent overflow. LLMs output429 # flex:1 without min-width:0, causing text to expand and hide images.430 result = []431 i = 0432 tag = 'style="'433 while i < len(html):434 pos = html.find(tag, i)435 if pos == -1:436 # Also try single quotes437 pos2 = html.find("style='", i)438 if pos2 == -1:439 result.append(html[i:])440 break441 pos = pos2442 tag = "style='"443 q = "'"444 else:445 q = '"'446 result.append(html[i:pos])447 # Find end of style attribute448 start = pos + len('style=') + 1449 end = html.find(q, start)450 if end == -1:451 result.append(html[pos:])452 break453 style_val = html[start:end]454 # Fix: add min-width:0 if has flex:1 but not min-width455 if 'flex:' in style_val and '1' in style_val.split('flex:')[-1][:3] and 'min-width' not in style_val:456 style_val = style_val.rstrip(';') + ';min-width:0;overflow:hidden'457 result.append(f'style={q}{style_val}{q}')458 i = end + 1459 tag = 'style="'460 return ''.join(result)461462463def main():464 try:465 execution_input = json.loads(sys.stdin.read())466 inputs = execution_input.get("inputs", {})467468 content = inputs.get("content")469 instruction = inputs.get("instruction")470 image_filename = inputs.get("image", "")471 template_name = inputs.get("template_name", "auto")472 llm_model_id = inputs.get("llmModelId")473 language = inputs.get("language", "auto")474475 if not content:476 raise ValueError("Required input 'content' not provided")477 if not instruction:478 raise ValueError("Required input 'instruction' not provided")479 if not llm_model_id:480 raise ValueError("Required input 'llmModelId' not provided")481482 # Read image and convert to base64 data URI if provided.483 # os/pathlib are lint-blocked in new-format nodes, so probe the input by484 # attempting to open it (the executor mounts inputs under /data/input).485 image_data_uri = ""486 if image_filename:487 image_path = f"/data/input/{image_filename}"488 try:489 with open(image_path, "rb") as f:490 b64 = base64.b64encode(f.read()).decode("ascii")491 mime = mimetypes.guess_type(image_filename)[0] or "image/png"492 image_data_uri = f"data:{mime};base64,{b64}"493 print(f"Image loaded: {image_filename} ({len(b64)} base64 chars)", file=sys.stderr)494 except FileNotFoundError:495 print(f"Image not found: {image_path}", file=sys.stderr)496497 print(f"Generating slide with model_id: {llm_model_id}", file=sys.stderr)498 print(f"Content length: {len(content)} chars", file=sys.stderr)499 print(f"Instruction: {instruction[:100]}...", file=sys.stderr)500 print(f"Layout template: {template_name}", file=sys.stderr)501502 # Build system prompt with optional layout instruction503 system_prompt = SYSTEM_PROMPT + build_layout_instruction(template_name)504 system_prompt += language_directive(language)505506 # If image provided, add instruction to use {{IMAGE}} placeholder507 if image_data_uri:508 system_prompt += (509 "\n\nIMAGE INPUT: The user has provided an image. "510 "Use {{IMAGE}} as the exact src attribute value for the <img> tag. "511 "CRITICAL: Always wrap the <img> inside a <div style='flex:1;min-width:0'> when using flex layout. "512 "Never place <img> as a direct flex child — it will overflow. "513 "Example: <div style='flex:1;min-width:0'><img src='{{IMAGE}}' alt='description' style='width:100%;border-radius:8px'></div>. "514 "Do NOT use picsum.photos — use {{IMAGE}} instead."515 )516517 user_message = (518 f"<instruction>{instruction}</instruction>\n\n"519 f"<source_content>\n{content}\n</source_content>"520 )521522 base_messages = [523 {"role": "system", "content": system_prompt},524 {"role": "user", "content": user_message},525 ]526527 # Generate the slide, validating that the model returned a single528 # well-formed <section>. Reasoning models sometimes leak scratch work or529 # emit several draft sections, which produced decks that rendered blank530 # (malformed HTML). Retry up to MAX_ATTEMPTS, nudging the model to fix531 # the specific problem, then fail loudly rather than emit bad HTML.532 MAX_ATTEMPTS = 3533 messages = list(base_messages)534 slide_html = None535 last_reason = ""536 for attempt in range(1, MAX_ATTEMPTS + 1):537 print(f"Slide generation attempt {attempt}/{MAX_ATTEMPTS}", file=sys.stderr)538 # thinking=False: reasoning models leak <think> scratch work + draft539 # sections into the HTML, which breaks the slide (blank decks) and540 # forces the retry loop below. Suppressing reasoning removes the541 # cause — cleaner single-<section> output, fewer/no retries.542 raw_output = Gais.llm.chat(543 messages, model_id=llm_model_id, temperature=0.3, thinking=False544 ).text545 candidate = _fix_flex_styles(extract_section(raw_output))546 is_valid, reason = validate_slide_html(candidate)547 if is_valid:548 slide_html = candidate549 print(f"Attempt {attempt}: valid slide HTML", file=sys.stderr)550 break551 last_reason = reason552 print(f"Attempt {attempt}: invalid slide HTML — {reason}", file=sys.stderr)553 # Add a corrective instruction for the next attempt.554 messages = base_messages + [555 {556 "role": "user",557 "content": (558 f"Your previous response was not valid slide HTML: {reason}. "559 "Respond with EXACTLY ONE well-formed <section>...</section> "560 "element and NOTHING else: no reasoning, no explanation, no "561 "markdown code fences, no extra <section> blocks, and every "562 "tag properly closed."563 ),564 }565 ]566567 if slide_html is None:568 raise ValueError(569 f"Slide generator could not produce valid HTML after {MAX_ATTEMPTS} "570 f"attempts. Last validation error: {last_reason}"571 )572573 # Replace {{IMAGE}} placeholder with actual base64 data URI574 if image_data_uri and "{{IMAGE}}" in slide_html:575 slide_html = slide_html.replace("{{IMAGE}}", image_data_uri)576 print("Replaced {{IMAGE}} placeholder with base64 data URI", file=sys.stderr)577578 print(f"Generated slide: {len(slide_html)} chars", file=sys.stderr)579580 output = {"slide_html": slide_html}581 print(json.dumps(output))582583 except Exception as e:584 error_output = {585 "error": str(e),586 "errorType": type(e).__name__,587 "traceback": traceback.format_exc(),588 }589 print(json.dumps(error_output), file=sys.stderr)590 sys.exit(1)591592593if __name__ == "__main__":594 main()$ git log --oneline
v1.10.0
HEAD
2026-08-18v1.9.02026-06-27
v1.8.12026-06-26
v1.8.02026-06-24
v1.6.72026-05-22
v1.6.62026-05-19
v1.6.32026-05-19
v1.6.22026-05-19
v1.5.02026-05-07
v1.3.12026-04-23
v1.1.02026-04-07
v1.0.02026-03-29