feat: publish researched offline HTML presentation skill

This commit is contained in:
jarvis-at-skic committed 2026-10-21 20:27:50 +00:00
commit 50eab114ae
35 files changed
+1201

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
node_modules/
__pycache__/
*.pyc
+23
View File
@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2026 RootAtSkic, Hermes Agent
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Third-party files retain their own notices in assets/vendor.
+37
View File
@@ -0,0 +1,37 @@
# presentation-html
Reusable global skill for editable, offline HTML presentations built with **Reveal.js 6.0.2 + Anime.js 4.5.0**. Canonical name normalizes the user typo `presentation-htlm`.
## Quick start
Run from this package directory:
```sh
python3 scripts/build.py
# Open dist/presentation-html-demo-0.1.0.html directly, fully offline.
npm ci --ignore-scripts
npx playwright install chromium
npm test
```
Python builds with no network and no npm installation. npm/Chromium are needed only for browser tests. To use an existing compatible Chromium, set `CHROMIUM_EXECUTABLE` to its full path. Runtime playback has no external dependencies. The first dependency/browser installation needs network; the subsequent test runs disable browser networking.
## Package
- `SKILL.md`: operating workflow and verification gates.
- `references/`: dated official-source research, established-example catalog and QA contract.
- `assets/starter.html`, `assets/app.js`: original editable starter and lifecycle behavior.
- `assets/vendor/`: complete required libraries/fonts and their licenses.
- `scripts/build.py`: reproducible single-file build.
- `scripts/test.mjs`: real offline Chromium interaction checks (default `npm test`).
- `dist/`: ready-to-open six-slide example and hash manifest.
- `evidence/`: exact-artifact test report and individually rendered slides.
- `SOURCE.md`, `vendor-manifest.json`, `package-lock.json`: provenance and integrity.
Use six slides as examples, not a fixed narrative. Copy the package to a deck workspace before editing. Adapt test inventory when slide/node counts change. Work Sans is optional example typography. Channel-specific preferences and authority remain outside this global skill.
## Licenses and scope
Original code/documentation: MIT, RootAtSkic and Hermes Agent. Reveal.js and Anime.js: MIT; Work Sans: SIL OFL 1.1. Notices are preserved alongside vendor bytes and embedded in the output. No copyrighted showcase artwork was scraped into the distributable. Research documents disclose unavailable pages and link-only references. See `references/examples.md` for attribution and reuse decisions.
Local Chromium verification is included; no hosted CI or cross-browser certification is claimed. No automation credentials belong in this repository.
+83
View File
@@ -0,0 +1,83 @@
---
name: presentation-html
description: Build offline HTML decks with deliberate motion.
version: 0.1.0
author: RootAtSkic, Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [presentations, html, revealjs, animejs, offline, accessibility]
related_skills: []
---
# HTML Presentations
Build editable, offline-first browser presentations using Reveal.js for navigation and Anime.js for intentional diagram motion. This is a global production workflow, not a fixed narrative, slide count, brand, or channel policy. Canonical spelling is **presentation-html**; `presentation-htlm` is a user typo, not a second skill.
## When to Use
- Create, revise, review or deliver an HTML slide deck.
- Add accessible interactive diagrams or purposeful motion to a browser presentation.
- Convert source material into a portable single-file deck without rasterizing text.
- Do not use this as the PowerPoint production workflow or authority for channel-specific claims.
## Prerequisites
- Python 3 for the dependency-free offline builder.
- Node.js 20+ and npm for the Playwright verification suite; install browser once while online.
- Full skill directory, including `assets/vendor`, not just this Markdown file.
- Read [research](references/research.md), [example catalog](references/examples.md) and [QA contract](references/qa.md) before changing the starter.
- Optional design/review skills may supplement this workflow; channel instructions remain channel-owned.
## How to Run
Copy this complete package into a new working directory. Never edit an installed skill to create a client deck. Use `terminal` with that directory as `workdir`:
```text
python3 scripts/build.py
npm ci --ignore-scripts
npx playwright install chromium
npm test
```
The builder works entirely offline using vendored assets. Dependency/browser installation requires network once; deck playback and build do not. If a compatible Chromium is already installed, set `CHROMIUM_EXECUTABLE` to its actual executable when invoking `npm test`. Do not silently download a new runtime during an offline test.
Open `dist/presentation-html-demo-0.1.0.html` directly. The six-slide demo is a teaching example, not a mandated length. Source: `assets/starter.html`, `assets/app.js`, `scripts/build.py`. Tests: `scripts/test.mjs`.
## Procedure
1. **Freeze the brief.** Record audience, decision, duration, latest approved artifact, content sources and exact requested changes. Resolve slide feedback by visible content rather than unreliable numbering. Completion: every change maps to source and an acceptance check.
2. **Outline the narrative.** Context → mechanism → evidence → decision is a useful structure, not a compulsory sequence. One concept per slide; remove repetition rather than shrink text. End on the audience's decision, not provenance. Completion: each slide adds a unique reason to continue.
3. **Choose the visual system.** Use native HTML text and editable SVG. Choose a fixed aspect ratio and scale with Reveal. Use semantic grids for prose, geometry for real relationships, and one restrained accent. Work Sans is bundled as an optional example—not a universal brand. Completion: palette, typography, spacing and diagram vocabulary are explicit.
4. **Pin the runtime.** This package uses Reveal.js **6.0.2** and Anime.js **4.5.0**. Inspect actual package metadata and licenses before upgrading. The UMD bundles work from `file://`; keep ESM/build-system concerns out of the offline artifact. Completion: exact versions, hashes and notices match embedded bytes.
5. **Design interactions honestly.** A pointer cursor means a real action. Use native buttons or SVG groups with button role, accessible name, focusability, `aria-pressed`, Enter and Space handling. Do not give static diagram labels fake affordances. Stop propagation so activation does not advance Reveal. Completion: every interactive node works with mouse and keyboard.
6. **Add bounded motion.** Start with `transition: 'none'`; animate only to clarify grouping, sequence or relationships. Use Anime v4 `animate(targets, options)`, `createTimeline`, `stagger`, `ease`. Never paste v3 `anime({targets:…})` or `easing` options into v4. Settle active animations on departure, revisit, reduced-motion change and print. Final content must remain fully visible. Completion: no infinite decoration, flash, hidden final state or duplicate animation ownership.
7. **Make presentation controls explicit.** Include visible fullscreen enter/exit plus F; handle promise rejection and `fullscreenchange`. Preserve arrows, overview and focus. Restore the slide via hash with guarded local storage fallback; storage may be denied. Hide controls in print. Completion: real enter/exit—not a mocked call—passes.
8. **Build a portable release.** Embed CSS, JS, fonts and legal notices; no analytics, CDN calls, remote images or runtime fetches. Preserve previous delivered versions. Completion: a named single HTML file opens in an offline browser and has a SHA-256 manifest.
9. **Test the exact deliverable.** Update count/title/node expectations for the actual deck. Test every slide and every node, normal and reduced motion, early navigation, re-entry, overview, font loading, console, network, bounds, fullscreen, hash reload and print. A six-node sample does not prove a 46-node production deck. Completion: report identifies exact file hash and actual tested counts.
10. **Inspect and fix.** Render every slide separately after motion settles. Personally inspect the exact screenshots; geometry tests do not catch low contrast, hierarchy or unwanted default theme colors. Fix findings, rebuild and rerun. Independent review supplements self-review when available; disclose when unavailable. Completion: all final slides inspected and material defects resolved.
11. **Publish and deliver.** Include source, licenses, research links, final file, manifest and real evidence. If publishing to Git, verify immutable remote ref and file bytes, not just push output. Completion: attached artifact hash equals tested artifact hash, and limitations are stated.
## Pitfalls
- Reveal's `.reveal-viewport` rule can override a lower-specificity `body` palette; override `body.reveal-viewport` explicitly. Assert computed colors and inspect screenshots.
- Reveal hash writes are asynchronous. Wait for the expected hash before testing reload.
- Current Anime v4 API differs from legacy demos; some old minified bundles carry a version banner different from their advertised download version. Inspect actual bytes.
- Do not let Reveal Auto-Animate and Anime animate the same properties on the same element.
- Print is a separate state. A hidden toolbar check is not proof of paginated PDF quality.
- A license on a library does not license every third-party showcase's images, logos or music.
- Offline means no requests; a cache-warmed CDN presentation is not offline.
- Keep project names, channel identifiers, chosen slide count and user-specific review delivery preferences out of global defaults.
## Verification
- [ ] Complete package copied; exact pins and vendor hashes verified.
- [ ] Content/claim sources and all third-party licenses recorded.
- [ ] Source, output version and manifest agree.
- [ ] Every slide and actual interactive node tested, including Enter/Space and real action.
- [ ] Offline browser reports zero network attempts and zero console/page errors.
- [ ] Normal/reduced motion, fullscreen, navigation, overview and reload pass.
- [ ] Font and bounds checks pass; every final screenshot personally reviewed.
- [ ] Print state and any required PDF export independently inspected.
- [ ] Immutable publication verified; caveats distinguish automated checks from human review.
+18
View File
@@ -0,0 +1,18 @@
# Provenance
Original package commissioned by RootAtSkic in presentation message `1562557683339362304`; authored with Hermes Agent, 2026-10-21 UTC. Canonical global name: presentation-html. This is original work informed by official documentation, not a mirrored third-party skill.
## Immutable dependencies
- Reveal.js 6.0.2, npm exact version and integrity in `package-lock.json`; MIT notice in assets/vendor/reveal-LICENSE.
- Anime.js 4.5.0, official tag source commit `2c9cf8ea00329f6768c7d7902252ed977d75ce42`; npm integrity in lockfile; MIT notice in assets/vendor/anime-LICENSE.md.
- Work Sans via @fontsource/work-sans 5.2.8, exact npm version/integrity in lockfile; unmodified Latin normal 400/700 WOFF2 files, SIL OFL notice in assets/vendor/work-sans-LICENSE.
- Playwright test dependency 1.64.1 is tooling, not embedded in the deck.
`vendor-manifest.json` enumerates every redistributed vendor file with package, version, source URL, license, byte count and SHA-256. Build embeds those exact bytes except removing source-map comments from JS to prevent irrelevant lookup attempts; source binaries remain unmodified in assets/vendor. Font and library notices are embedded in the HTML.
## Research boundaries
`references/source-audit.json` records live HTTP results, including failures; `references/upstream-sources.json` records fetched official example/migration sources by URL and hash. Research HTML/wiki snapshots were kept outside this distributable; no branded example artwork is redistributed. Catalog links distinguish MIT library code from unlicensed third-party talk media.
The prior approved WestWorld Genesis 6.4.1 HTML was inspected for general interaction and portability lessons. No channel narrative, logos, governance claims or proprietary slide artwork was imported. Original starter patterns are independently authored MIT code.
+41
View File
@@ -0,0 +1,41 @@
(() => {
const {animate, createTimeline, stagger} = anime;
const reduced = matchMedia('(prefers-reduced-motion: reduce)');
const printMode = location.search.includes('print-pdf');
const seen = new Set(); let active = []; let motion = !reduced.matches;
const motionButton = document.querySelector('#motion');
function settle() {
active.forEach(a => { a.complete(); }); active = [];
document.querySelectorAll('[data-motion]').forEach(e => {e.style.opacity='1';e.style.transform='none';});
}
function run() {
settle(); const slide=Reveal.getCurrentSlide();
if (!slide || seen.has(slide.id) || !motion || reduced.matches || printMode) return;
seen.add(slide.id); const targets=slide.querySelectorAll('[data-motion]');
if (!targets.length) return;
if(slide.id==='sequence') {
const tl=createTimeline({defaults:{duration:300,ease:'outCubic'}});
targets.forEach((e,i)=>tl.add(e,{opacity:[0,1],translateY:[12,0]},i*180));active.push(tl);
} else active.push(animate(targets,{opacity:[0,1],translateY:[12,0],delay:stagger(70),duration:400,ease:'outCubic'}));
}
function motionLabel(){motionButton.textContent=motion&&!reduced.matches?'Motion on':'Motion off';motionButton.setAttribute('aria-pressed',String(motion&&!reduced.matches));}
motionButton.onclick=()=>{motion=!motion;settle();motionLabel();};
reduced.addEventListener('change',()=>{if(reduced.matches)settle();motionLabel();});motionLabel();
addEventListener('beforeprint',settle);
const fs=document.querySelector('#fullscreen');
async function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await document.documentElement.requestFullscreen();}catch{fs.textContent='Fullscreen unavailable';}}
fs.onclick=fullscreen;
document.addEventListener('fullscreenchange',()=>{fs.textContent=document.fullscreenElement?'Exit fullscreen':'Enter fullscreen';fs.setAttribute('aria-pressed',String(!!document.fullscreenElement));});
document.addEventListener('keydown',e=>{if(e.key.toLowerCase()==='f'&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.target.matches('input,textarea,[contenteditable]')){e.preventDefault();e.stopPropagation();fullscreen();}},true);
document.querySelectorAll('[data-node]').forEach(node=>{
function select(){const was=node.getAttribute('aria-pressed')==='true';document.querySelectorAll('[data-node]').forEach(n=>n.setAttribute('aria-pressed','false'));node.setAttribute('aria-pressed',String(!was));document.querySelector('#detail').textContent=was?'Select a source to reveal its role. Tab + Enter or Space also works.':node.dataset.detail;}
node.addEventListener('click',select);
node.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();e.stopPropagation();select();}});
});
Reveal.initialize({width:1240,height:700,margin:.08,center:false,hash:true,transition:'none',backgroundTransition:'none',slideNumber:'c/t',view:'slide',keyboard:{70:null},autoAnimate:false}).then(()=>{
if(!location.hash){try{const i=Number(localStorage.getItem('presentation-html-demo-slide'));if(Number.isInteger(i)&&i>=0&&i<Reveal.getTotalSlides())Reveal.slide(i);}catch{}}
run(); window.deckReady=true;
});
Reveal.on('slidechanged',()=>{try{localStorage.setItem('presentation-html-demo-slide',String(Reveal.getIndices().h));}catch{}run();});
window.presentationQA={settle,reduced:()=>reduced.matches,active:()=>active.filter(a=>!a.completed).length};
})();
+23
View File
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>From signal to decision — offline HTML starter</title>
<style>/* REVEAL_CSS */</style><style>
/* FONT_CSS */
:root{--r-background-color:#101e25;--r-main-color:#f4f3ed;--ink:#f4f3ed;--muted:#b8c4c7;--bg:#101e25;--accent:#c5f27a;--line:#57717c}*{box-sizing:border-box}body.reveal-viewport{background:var(--bg);color:var(--ink)}.reveal{font-family:'Work Sans',sans-serif;font-size:28px}.reveal .slides{text-align:left}.reveal .slides>section{height:700px;padding:45px 48px}.eyebrow{color:var(--accent);font-size:20px;letter-spacing:.15em;text-transform:uppercase;margin:0 0 24px}h1{font-size:86px;line-height:1.05;letter-spacing:-.045em;margin:24px 0}h2{font-size:55px;line-height:1.1;margin:0 0 30px;letter-spacing:-.025em}p{line-height:1.5;max-width:1000px}.muted{color:var(--muted)}.big{font-size:38px;max-width:900px}.accent{color:var(--accent)}.rule{border-top:2px solid var(--line);padding-top:20px;margin-top:42px}button{font:inherit;color:var(--ink);background:#20343e;border:2px solid var(--line);padding:10px 16px;cursor:pointer;min-height:44px}button:hover,button[aria-pressed=true]{border-color:var(--accent);color:var(--accent)}button:focus-visible,[role=button]:focus-visible{outline:4px solid var(--accent);outline-offset:5px}.toolbar{position:fixed;right:22px;top:14px;z-index:30;display:flex;gap:12px;font:16px 'Work Sans',sans-serif}.toolbar button{font-size:16px}.reveal .slide-number{font:18px 'Work Sans';background:transparent;color:var(--muted);left:25px;right:auto;bottom:22px}.reveal .controls{color:var(--accent)}svg{width:100%;height:370px;overflow:visible}svg text{font-family:inherit;fill:var(--ink);font-size:24px;text-anchor:middle;dominant-baseline:middle}svg .edge{stroke:var(--line);stroke-width:3;fill:none}svg rect{fill:#20343e;stroke:var(--line);stroke-width:2}svg [role=button]{cursor:pointer}svg [aria-pressed=true] rect{stroke:var(--accent)}svg .hub{fill:var(--accent)}svg .hub-text{fill:var(--bg);font-weight:700}.detail{margin:5px 0;font-size:24px;min-height:40px}.stages{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:60px}.stage{border-top:3px solid var(--accent);padding-top:20px}.stage strong{font-size:36px}.stage p{font-size:25px}.dots{display:flex;gap:20px;margin:65px 0}.dot{width:70px;height:70px;background:var(--accent)}.ending{font-size:70px;line-height:1.12}.reveal .progress{color:var(--accent)}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
@media print{.toolbar,.controls{display:none!important}.reveal .slides section [data-motion]{opacity:1!important;transform:none!important}}
</style></head><body>
<div class="toolbar"><button id="motion" aria-pressed="true">Motion on</button><button id="fullscreen" aria-pressed="false">Enter fullscreen</button></div>
<div class="reveal"><div class="slides">
<section id="signal"><p class="eyebrow">Original example · presentation-html</p><h1>From signal<br>to <span class="accent">decision.</span></h1><p class="big">Use structure to explain the system.<br>Use motion only to explain change.</p><p class="rule muted">Reveal.js 6.0.2 + Anime.js 4.5.0 · All assets embedded</p></section>
<section id="story"><p class="eyebrow">01 / Narrative</p><h2>One question. One visual answer.</h2><div class="stages"><div class="stage" data-motion><strong>Context</strong><p>What changed, and why does it matter?</p></div><div class="stage" data-motion><strong>Mechanism</strong><p>What connects the evidence to the choice?</p></div><div class="stage" data-motion><strong>Decision</strong><p>What should the audience do next?</p></div></div><p class="rule muted">Choose the slide count from the story—not a template quota.</p></section>
<section id="system"><p class="eyebrow">02 / Editable SVG + real interaction</p><h2>Keep the decision at the center.</h2>
<svg viewBox="0 0 1100 370" role="group" aria-label="Six evidence sources feed one decision"><g aria-hidden="true"><path class="edge" d="M250 65L550 185M550 65V185M850 65L550 185M250 305L550 185M550 305V185M850 305L550 185"/></g>
<rect class="hub" x="435" y="145" width="230" height="80"/><text class="hub-text" x="550" y="185">Decision</text>
<!-- NODES -->
</svg><p class="detail" id="detail" aria-live="polite">Select a source to reveal its role. Tab + Enter or Space also works.</p></section>
<section id="sequence"><p class="eyebrow">03 / Anime.js timeline</p><h2>Reveal dependency—not decoration.</h2><div class="stages"><div class="stage" data-motion><strong>Observe</strong><p>Make the signal visible.</p></div><div class="stage" data-motion><strong>Interpret</strong><p>Connect evidence and context.</p></div><div class="stage" data-motion><strong>Act</strong><p>Name the next reversible step.</p></div></div><p class="rule muted">A short, bounded sequence. No loops. No hidden final content.</p></section>
<section id="stagger"><p class="eyebrow">04 / Restrained stagger</p><h2>Make order perceptible.</h2><div class="dots" aria-hidden="true"><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i></div><p class="big">The pattern is original. The technique is portable.</p><p class="muted">Reduced motion, print and revisit states show the complete result.</p></section>
<section id="decision"><p class="eyebrow">05 / Delivery gate</p><h2 class="ending">Test the exact file.<br>Inspect <span class="accent">every slide.</span></h2><p class="big">Offline. Readable. Keyboard-operable.<br>Then publish the immutable artifact.</p><p class="rule muted">← → Navigate · Esc Overview · F Fullscreen · Tab Focus</p></section>
</div></div><script>/* REVEAL_JS */</script><script>/* ANIME_JS */</script><script>/* APP_JS */</script>
<!-- LICENSES -->
</body></html>
+9
View File
@@ -0,0 +1,9 @@
The MIT License
Copyright (c) 2025 Julian Garnier
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
/**
* Anime.js - UMD minified bundle
* @version v4.5.0
* @license MIT
* @copyright 2026 - Julian Garnier
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).anime={})}(this,function(t){"use strict";const e="undefined"!=typeof window,s=e?window:null,i=e?document:null,r={replace:0,none:1,blend:2},n=Symbol(),o=Symbol(),a=Symbol(),l=Symbol(),h=Symbol(),d=1e-11,c=1e12,u=1e3,p="",m=[],f=(()=>{const t=new Map;return t.set("x","translateX"),t.set("y","translateY"),t.set("z","translateZ"),t})(),g=["perspective","translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","scaleZ","skew","skewX","skewY"],y=g.reduce((t,e)=>({...t,[e]:e+"("}),{}),_=()=>{},b=/\)\s*[-.\d]/,v=/(^#([\da-f]{3}){1,2}$)|(^#([\da-f]{4}){1,2}$)/i,T=/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i,x=/rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i,S=/hsl\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*\)/i,w=/hsla\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i,$=/[-+]?\d*\.?\d+(?:e[-+]?\d)?/gi,C=/^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i,E=/([a-z])([A-Z])/g,k=/(\*=|\+=|-=)/,N=/var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/,D={id:null,keyframes:null,playbackEase:null,playbackRate:1,frameRate:240,loop:0,reversed:!1,alternate:!1,autoplay:!0,persist:!1,duration:u,delay:0,loopDelay:0,ease:"out(2)",composition:r.replace,modifier:t=>t,onBegin:_,onBeforeUpdate:_,onUpdate:_,onLoop:_,onPause:_,onComplete:_,onRender:_},A={current:null,root:i},I={defaults:D,precision:4,timeScale:1,tickThreshold:200,editor:null},R={version:"4.5.0",engine:null};e&&(s.AnimeJS||(s.AnimeJS=[]),s.AnimeJS.push(R));const L=t=>t.replace(E,"$1-$2").toLowerCase(),B=(t,e)=>0===t.indexOf(e),P=Date.now,F=Array.isArray,M=t=>t&&t.constructor===Object,V=t=>"number"==typeof t&&!isNaN(t),O=t=>"string"==typeof t,z=t=>"function"==typeof t,H=t=>void 0===t,X=t=>H(t)||null===t,Y=t=>e&&t instanceof SVGElement,W=t=>v.test(t),U=t=>B(t,"rgb"),q=t=>B(t,"hsl"),j=t=>!I.defaults.hasOwnProperty(t),G=["opacity","rotate","overflow","color"],Z=t=>O(t)?parseFloat(t):t,Q=Math.pow,J=Math.sqrt,K=Math.sin,tt=Math.cos,et=Math.abs,st=Math.exp,it=Math.ceil,rt=Math.floor,nt=Math.asin,ot=Math.max,at=Math.atan2,lt=Math.PI,ht=Math.round,dt=(t,e,s)=>t<e?e:t>s?s:t,ct=(t,e)=>{if(e<0)return t;if(!e)return ht(t);const s=10**e;return ht(t*s)/s},ut=(t,e)=>F(e)?e.reduce((e,s)=>et(s-t)<et(e-t)?s:e):e?ht(t/e)*e:t,pt=(t,e,s)=>1===s?e:0===s?t:t+(e-t)*s,mt=t=>t===1/0?c:t===-1/0?-c:t,ft=t=>t<=d?d:mt(ct(t,11)),gt=t=>F(t)?[...t]:t,yt=(t,e)=>{const s={...t};for(let i in e){const r=t[i];s[i]=H(r)?e[i]:r}return s},_t=(t,e,s,i="_prev",r="_next")=>{let n=t._head,o=r;for(s&&(n=t._tail,o=i);n;){const t=n[o];e(n),n=t}},bt=(t,e,s="_prev",i="_next")=>{const r=e[s],n=e[i];r?r[i]=n:t._head=n,n?n[s]=r:t._tail=r,e[s]=null,e[i]=null},vt=(t,e,s,i="_prev",r="_next")=>{let n=t._tail;for(;n&&s&&s(n,e);)n=n[i];const o=n?n[r]:t._head;n?n[r]=e:t._head=e,o?o[i]=e:t._tail=e,e[i]=n,e[r]=o},Tt=t=>{let e=p;for(let s=0,i=g.length;s<i;s++){const i=g[s],r=t[i];if(void 0!==r){if("translateX"===i){const i=t.translateY;if(void 0!==i){const n=t.translateZ;void 0!==n?(e+=`translate3d(${r},${i},${n}) `,s+=2):(e+=`translate(${r},${i}) `,s+=1);continue}}if("scaleX"===i&&void 0===t.scale){const i=t.scaleY;if(void 0!==i){const n=t.scaleZ;void 0!==n?(e+=`scale3d(${r},${i},${n}) `,s+=2):(e+=`scale(${r},${i}) `,s+=1);continue}}e+=`${y[i]}${r}) `}"rotateZ"===i&&void 0!==t.rotate3d&&(e+=`rotate3d(${t.rotate3d}) `)}return void 0!==t.matrix&&(e+=`matrix(${t.matrix}) `),void 0!==t.matrix3d&&(e+=`matrix3d(${t.matrix3d}) `),e},xt=[];function St(t,e){if(!t)return null;const s=xt.length;t:for(let i=0;i<s;i++){const s=xt[i];if(s.detect&&!s.detect(t))continue;const r=s.targetAdapters;for(let s=0,i=r.length;s<i;s++){const i=r[s];if(i.detect(t)){const s=i.props[e];if(s&&(!s.gate||s.gate(t)))return s;break t}}}for(let i=0;i<s;i++){const s=xt[i];if(s.detect&&!s.detect(t))continue;const r=s.propertyResolvers;for(let s=0,i=r.length;s<i;s++){const i=r[s](t,e);if(i)return i}}return null}const wt=(t,e,s)=>(s<0&&(s+=1),s>1&&(s-=1),s<1/6?t+6*(e-t)*s:s<.5?e:s<2/3?t+(e-t)*(2/3-s)*6:t),$t=(t,e)=>H(t)?e:t,Ct=(t,e)=>{const s=t.match(N),i=e[o]?e:document.documentElement;let r=getComputedStyle(i)?.getPropertyValue(s[1]);return r&&r.trim()!==p||!s[2]||(r=s[2].trim()),r||0},Et=(t,e,s,i,r,n)=>{if(z(t)){if(!r){const r=t(e,s,i,n);return isNaN(+r)?r||0:+r}const o=()=>{const r=t(e,s,i,n);return isNaN(+r)?r||0:+r};return r.func=o,o()}if(O(t)&&B(t,"var(")){if(!r)return Ct(t,e);const s=()=>Ct(t,e);return r.func=s,s()}return t},kt=(t,e)=>t[o]?t[a]&&((t,e)=>{if(G.includes(e))return!1;if(t.getAttribute(e)||e in t){if("scale"===e){const e=t.parentNode;return e&&"filter"===e.tagName}return!0}})(t,e)?1:g.includes(e)||f.get(e)?3:B(e,"--")?4:e in t.style?2:e in t?0:1:0,Nt=(t,e,s)=>{const i=t.style[e];i&&s&&(s[e]=i);const r=i||getComputedStyle(t[Line truncated
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2011-2026 Hakim El Hattab, http://hakim.se, and reveal.js contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+1
View File
@@ -0,0 +1 @@
.reveal .stretch,.reveal .r-stretch{max-width:none;max-height:none}.reveal pre.stretch code,.reveal pre.r-stretch code{box-sizing:border-box;height:100%;max-height:100%}.reveal .r-fit-text{white-space:nowrap;display:inline-block}.reveal .r-stack{grid-template-rows:100%;display:grid}.reveal .r-stack>*{grid-area:1/1;margin:auto}.reveal .r-vstack,.reveal .r-hstack{display:flex}.reveal .r-vstack img,.reveal .r-vstack video,.reveal .r-hstack img,.reveal .r-hstack video{object-fit:contain;min-width:0;min-height:0}.reveal .r-vstack{flex-direction:column;justify-content:center;align-items:center}.reveal .r-hstack{flex-direction:row;justify-content:center;align-items:center}.reveal .items-stretch{align-items:stretch}.reveal .items-start{align-items:flex-start}.reveal .items-center{align-items:center}.reveal .items-end{align-items:flex-end}.reveal .justify-between{justify-content:space-between}.reveal .justify-around{justify-content:space-around}.reveal .justify-start{justify-content:flex-start}.reveal .justify-center{justify-content:center}.reveal .justify-end{justify-content:flex-end}html.reveal-full-page{width:100%;height:100vh;height:calc(var(--vh,1vh) * 100);height:100dvh;overflow:hidden}.reveal-viewport{color:#000;--r-controls-spacing:12px;--r-overlay-header-height:40px;--r-overlay-margin:0px;--r-overlay-padding:6px;--r-overlay-gap:5px;background-color:#fff;height:100%;margin:0;line-height:1;position:relative;overflow:hidden}@media screen and (width<=1024px),(height<=768px){.reveal-viewport{--r-overlay-header-height:26px}}.reveal-viewport:fullscreen{width:100%!important;height:100%!important;top:0!important;left:0!important;transform:none!important}.reveal .fragment{transition:all .2s}.reveal .fragment:not(.custom){opacity:0;visibility:hidden;will-change:opacity}.reveal .fragment.visible{opacity:1;visibility:inherit}.reveal .fragment.disabled{transition:none}.reveal .fragment.grow{opacity:1;visibility:inherit}.reveal .fragment.grow.visible{transform:scale(1.3)}.reveal .fragment.shrink{opacity:1;visibility:inherit}.reveal .fragment.shrink.visible{transform:scale(.7)}.reveal .fragment.zoom-in{transform:scale(.1)}.reveal .fragment.zoom-in.visible{transform:none}.reveal .fragment.fade-out{opacity:1;visibility:inherit}.reveal .fragment.fade-out.visible{opacity:0;visibility:hidden}.reveal .fragment.semi-fade-out{opacity:1;visibility:inherit}.reveal .fragment.semi-fade-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.strike{opacity:1;visibility:inherit}.reveal .fragment.strike.visible{text-decoration:line-through}.reveal .fragment.fade-up{transform:translateY(40px)}.reveal .fragment.fade-up.visible{transform:translate(0)}.reveal .fragment.fade-down{transform:translateY(-40px)}.reveal .fragment.fade-down.visible{transform:translate(0)}.reveal .fragment.fade-right{transform:translate(-40px)}.reveal .fragment.fade-right.visible{transform:translate(0)}.reveal .fragment.fade-left{transform:translate(40px)}.reveal .fragment.fade-left.visible{transform:translate(0)}.reveal .fragment.fade-in-then-out,.reveal .fragment.current-visible{opacity:0;visibility:hidden}.reveal .fragment.fade-in-then-out.current-fragment,.reveal .fragment.current-visible.current-fragment{opacity:1;visibility:inherit}.reveal .fragment.fade-in-then-semi-out{opacity:0;visibility:hidden}.reveal .fragment.fade-in-then-semi-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.fade-in-then-semi-out.current-fragment,.reveal .fragment.highlight-red,.reveal .fragment.highlight-current-red,.reveal .fragment.highlight-green,.reveal .fragment.highlight-current-green,.reveal .fragment.highlight-blue,.reveal .fragment.highlight-current-blue{opacity:1;visibility:inherit}.reveal .fragment.highlight-red.visible{color:#ff2c2d}.reveal .fragment.highlight-green.visible{color:#17ff2e}.reveal .fragment.highlight-blue.visible{color:#1b91ff}.reveal .fragment.highlight-current-red.current-fragment{color:#ff2c2d}.reveal .fragment.highlight-current-green.current-fragment{color:#17ff2e}.reveal .fragment.highlight-current-blue.current-fragment{color:#1b91ff}.reveal:after{content:"";font-style:italic}.reveal iframe{z-index:1}.reveal a{position:relative}@keyframes bounce-right{0%,10%,25%,40%,50%{transform:translate(0)}20%{transform:translate(10px)}30%{transform:translate(-5px)}}@keyframes bounce-left{0%,10%,25%,40%,50%{transform:translate(0)}20%{transform:translate(-10px)}30%{transform:translate(5px)}}@keyframes bounce-down{0%,10%,25%,40%,50%{transform:translateY(0)}20%{transform:translateY(10px)}30%{transform:translateY(-5px)}}.reveal .controls{top:auto;bottom:var(--r-controls-spacing);right:var(--r-controls-spacing);z-index:11;color:#000;pointer-events:none;font-size:10px;display:none;position:absolute;left:auto}.reveal .controls button{cursor:pointer;color:currentColor;z-index:2;pointer-events:auto;font-size:inherit;visibility:hidden;opacity:0;-webkit-appearance:none;-webkit-tap-highlight-color:#0000;background-color:#0000;border:0;outline:0;padding:0;transitioLine truncated
+40
View File
@@ -0,0 +1,40 @@
/*!
* reveal.js
* https://revealjs.com
* MIT licensed
*
* Copyright (C) 2011-2026 Hakim El Hattab, https://hakim.se
*/
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.Reveal=t())})(this,function(){var e=(e,t)=>{for(let n in t)e[n]=t[n];return e},t=(e,t)=>Array.from(e.querySelectorAll(t)),n=(e,t,n)=>{n?e.classList.add(t):e.classList.remove(t)},r=e=>{if(typeof e==`string`){if(e===`null`)return null;if(e===`true`)return!0;if(e===`false`)return!1;if(e.match(/^-?[\d\.]+$/))return parseFloat(e)}return e},i=(e,t)=>{e.style.transform=t},a=(e,t)=>{let n=e.matches||e.matchesSelector||e.msMatchesSelector;return!!(n&&n.call(e,t))},o=(e,t)=>{if(e&&typeof e.closest==`function`)return e.closest(t);for(;e;){if(a(e,t))return e;e=e.parentElement}return null},s=e=>{e=e||document.documentElement;let t=e.requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen;t&&t.apply(e)},c=(e,t,n,r=``)=>{let i=e.querySelectorAll(`.`+n);for(let t=0;t<i.length;t++){let n=i[t];if(n.parentNode===e)return n}let a=document.createElement(t);return a.className=n,a.innerHTML=r,e.appendChild(a),a},l=e=>{let t=document.createElement(`style`);return e&&e.length>0&&t.appendChild(document.createTextNode(e)),document.head.appendChild(t),t},u=()=>{let e={};location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi,t=>{let n=t.split(`=`).shift(),r=t.split(`=`).pop();return n&&r!==void 0&&(e[n]=r),t});for(let t in e){let n=e[t];e[t]=r(unescape(n))}return e.dependencies!==void 0&&delete e.dependencies,e},d=(e,t=0)=>{if(e){var n;let r,i=e.style.height;return e.style.height=`0px`,e.parentElement&&(e.parentElement.style.height=`auto`),r=t-(((n=e.parentElement)==null?void 0:n.offsetHeight)||0),e.style.height=i+`px`,e.parentElement&&e.parentElement.style.removeProperty(`height`),r}return t},f={mp4:`video/mp4`,m4a:`video/mp4`,ogv:`video/ogg`,mpeg:`video/mpeg`,webm:`video/webm`},p=(e=``)=>{let t=e.split(`.`).pop();return t?f[t]:void 0},m=(e=``)=>encodeURI(e).replace(/%5B/g,`[`).replace(/%5D/g,`]`).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),h=navigator.userAgent,g=/(iphone|ipod|ipad|android)/gi.test(h)||navigator.platform===`MacIntel`&&navigator.maxTouchPoints>1;/chrome/i.test(h)&&/edge/i.test(h);var _=/android/gi.test(h),v=function(e){if(e){var t=function(e){return[].slice.call(e)},n=0,r=1,i=2,a=3,o=[],s=null,c=`requestAnimationFrame`in e?function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{sync:!1};e.cancelAnimationFrame(s);var n=function(){return u(o.filter((function(e){return e.dirty&&e.active})))};if(t.sync)return n();s=e.requestAnimationFrame(n)}:function(){},l=function(e){return function(t){o.forEach((function(t){return t.dirty=e})),c(t)}},u=function(e){e.filter((function(e){return!e.styleComputed})).forEach((function(e){e.styleComputed=m(e)})),e.filter(h).forEach(g);var t=e.filter(p);t.forEach(f),t.forEach((function(e){g(e),d(e)})),t.forEach(_)},d=function(e){return e.dirty=n},f=function(e){e.availableWidth=e.element.parentNode.clientWidth,e.currentWidth=e.element.scrollWidth,e.previousFontSize=e.currentFontSize,e.currentFontSize=Math.min(Math.max(e.minSize,e.availableWidth/e.currentWidth*e.previousFontSize),e.maxSize),e.whiteSpace=e.multiLine&&e.currentFontSize===e.minSize?`normal`:`nowrap`},p=function(e){return e.dirty!==i||e.dirty===i&&e.element.parentNode.clientWidth!==e.availableWidth},m=function(t){var n=e.getComputedStyle(t.element,null);return t.currentFontSize=parseFloat(n.getPropertyValue(`font-size`)),t.display=n.getPropertyValue(`display`),t.whiteSpace=n.getPropertyValue(`white-space`),!0},h=function(e){var t=!1;return!e.preStyleTestCompleted&&(/inline-/.test(e.display)||(t=!0,e.display=`inline-block`),e.whiteSpace!==`nowrap`&&(t=!0,e.whiteSpace=`nowrap`),e.preStyleTestCompleted=!0,t)},g=function(e){e.element.style.whiteSpace=e.whiteSpace,e.element.style.display=e.display,e.element.style.fontSize=e.currentFontSize+`px`},_=function(e){e.element.dispatchEvent(new CustomEvent(`fit`,{detail:{oldValue:e.previousFontSize,newValue:e.currentFontSize,scaleFactor:e.currentFontSize/e.previousFontSize}}))},v=function(e,t){return function(n){e.dirty=t,e.active&&c(n)}},y=function(e){return function(){o=o.filter((function(t){return t.element!==e.element})),e.observeMutations&&e.observer.disconnect(),e.element.style.whiteSpace=e.originalStyle.whiteSpace,e.element.style.display=e.originalStyle.display,e.element.style.fontSize=e.originalStyle.fontSize}},b=function(e){return function(){e.active||(e.active=!0,c())}},x=function(e){return function(){return e.active=!1}},S=function(e){e.observeMutations&&(e.observer=new MutationObserver(v(e,r)),e.observer.observe(e.element,e.observeMutations))},ee={minSize:16,maxSize:512,multiLine:!0,observeMutations:`MutationObserver`in e&&{subtree:!0,childList:!0,characterData:!0}},C=null,w=function(){e.clearTimeout(C),C=e.setTimeout(l(i),E.observeWindowDelay)},te=[`resize`,`orientationchange`];return Object.defineProperty(ELine truncated
<span class="slide-number-a">${e}</span>
<span class="slide-number-delimiter">${t}</span>
<span class="slide-number-b">${n}</span>
</a>`:`<a href="${r}">
<span class="slide-number-a">${e}</span>
</a>`}destroy(){this.element.remove()}},re=class{constructor(e){this.Reveal=e,this.onInput=this.onInput.bind(this),this.onBlur=this.onBlur.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}render(){this.element=document.createElement(`div`),this.element.className=`jump-to-slide`,this.jumpInput=document.createElement(`input`),this.jumpInput.type=`text`,this.jumpInput.className=`jump-to-slide-input`,this.jumpInput.placeholder=`Jump to slide`,this.jumpInput.addEventListener(`input`,this.onInput),this.jumpInput.addEventListener(`keydown`,this.onKeyDown),this.jumpInput.addEventListener(`blur`,this.onBlur),this.element.appendChild(this.jumpInput)}show(){this.indicesOnShow=this.Reveal.getIndices(),this.Reveal.getRevealElement().appendChild(this.element),this.jumpInput.focus()}hide(){this.isVisible()&&(this.element.remove(),this.jumpInput.value=``,clearTimeout(this.jumpTimeout),delete this.jumpTimeout)}isVisible(){return!!this.element.parentNode}jump(){clearTimeout(this.jumpTimeout),delete this.jumpTimeout;let e=this.jumpInput.value.trim(``),t;if(/^\d+$/.test(e)){let n=this.Reveal.getConfig().slideNumber;if(n===`c`||n===`c/t`){let n=this.Reveal.getSlides()[parseInt(e,10)-1];n&&(t=this.Reveal.getIndices(n))}}return t||(/^\d+\.\d+$/.test(e)&&(e=e.replace(`.`,`/`)),t=this.Reveal.location.getIndicesFromHash(e,{oneBasedIndex:!0})),!t&&/\S+/i.test(e)&&e.length>1&&(t=this.search(e)),t&&e!==``?(this.Reveal.slide(t.h,t.v,t.f),!0):(this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),!1)}jumpAfter(e){clearTimeout(this.jumpTimeout),this.jumpTimeout=setTimeout(()=>this.jump(),e)}search(e){let t=RegExp(`\\b`+e.trim()+`\\b`,`i`),n=this.Reveal.getSlides().find(e=>t.test(e.innerText));return n?this.Reveal.getIndices(n):null}cancel(){this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),this.hide()}confirm(){this.jump(),this.hide()}destroy(){this.jumpInput.removeEventListener(`input`,this.onInput),this.jumpInput.removeEventListener(`keydown`,this.onKeyDown),this.jumpInput.removeEventListener(`blur`,this.onBlur),this.element.remove()}onKeyDown(e){e.keyCode===13?this.confirm():e.keyCode===27&&(this.cancel(),e.stopImmediatePropagation())}onInput(e){this.jumpAfter(200)}onBlur(){setTimeout(()=>this.hide(),1)}},D=e=>{let t=e.match(/^#([0-9a-f]{3})$/i);if(t&&t[1]){let e=t[1];return{r:parseInt(e.charAt(0),16)*17,g:parseInt(e.charAt(1),16)*17,b:parseInt(e.charAt(2),16)*17}}let n=e.match(/^#([0-9a-f]{6})$/i);if(n&&n[1]){let e=n[1];return{r:parseInt(e.slice(0,2),16),g:parseInt(e.slice(2,4),16),b:parseInt(e.slice(4,6),16)}}let r=e.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);if(r)return{r:parseInt(r[1],10),g:parseInt(r[2],10),b:parseInt(r[3],10)};let i=e.match(/^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i);return i?{r:parseInt(i[1],10),g:parseInt(i[2],10),b:parseInt(i[3],10),a:parseFloat(i[4])}:null},ie=e=>(typeof e==`string`&&(e=D(e)),e?(e.r*299+e.g*587+e.b*114)/1e3:null),ae=class{constructor(e){this.Reveal=e}render(){this.element=document.createElement(`div`),this.element.className=`backgrounds`,this.Reveal.getRevealElement().appendChild(this.element)}create(){this.element.innerHTML=``,this.element.classList.add(`no-transition`),this.Reveal.getHorizontalSlides().forEach(e=>{let n=this.createBackground(e,this.element);t(e,`section`).forEach(e=>{this.createBackground(e,n),n.classList.add(`stack`)})}),this.Reveal.getConfig().parallaxBackgroundImage?(this.element.style.backgroundImage=`url("`+this.Reveal.getConfig().parallaxBackgroundImage+`")`,this.element.style.backgroundSize=this.Reveal.getConfig().parallaxBackgroundSize,this.element.style.backgroundRepeat=this.Reveal.getConfig().parallaxBackgroundRepeat,this.element.style.backgroundPosition=this.Reveal.getConfig().parallaxBackgroundPosition,setTimeout(()=>{this.Reveal.getRevealElement().classList.add(`has-parallax-background`)},1)):(this.element.style.backgroundImage=``,this.Reveal.getRevealElement().classList.remove(`has-parallax-background`))}createBackground(e,t){let n=document.createElement(`div`);n.className=`slide-background `+e.className.replace(/present|past|future/,``);let r=document.createElement(`div`);return r.className=`slide-background-content`,n.appendChild(r),t.appendChild(n),e.slideBackgroundElement=n,e.slideBackgroundContentElement=r,this.sync(e),n}sync(e){let t=e.slideBackgroundElement,n=e.slideBackgroundContentElement,r={background:e.getAttribute(`data-background`),backgroundSize:e.getAttribute(`data-background-size`),backgroundImage:e.getAttribute(`data-background-image`),backgroundVideo:e.getAttribute(`data-background-video`),backgroundIframe:e.getAttribute(`data-background-iframe`),backgroundColor:e.getAttribute(`data-background-color`),backgroundGradient:e.getAttribute(`data-background-gradient`),backgroundRepeat:e.getAttribute(`data-background-repeat`),backgroundPosition:e.getAttribute(`data-background-position`),backgroundTransition:e.getAttribute(`data-back Line truncated
<button class="navigate-right" aria-label="${e?`previous slide`:`next slide`}"><div class="controls-arrow"></div></button>
<button class="navigate-up" aria-label="above slide"><div class="controls-arrow"></div></button>
<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>`,this.Reveal.getRevealElement().appendChild(this.element),this.controlsLeft=t(n,`.navigate-left`),this.controlsRight=t(n,`.navigate-right`),this.controlsUp=t(n,`.navigate-up`),this.controlsDown=t(n,`.navigate-down`),this.controlsPrev=t(n,`.navigate-prev`),this.controlsNext=t(n,`.navigate-next`),this.controlsFullscreen=t(n,`.enter-fullscreen`),this.controlsRightArrow=this.element.querySelector(`.navigate-right`),this.controlsLeftArrow=this.element.querySelector(`.navigate-left`),this.controlsDownArrow=this.element.querySelector(`.navigate-down`)}configure(e,t){let n=e.controls===`speaker`||e.controls===`speaker-only`;this.element.style.display=e.controls&&(!n||this.Reveal.isSpeakerNotes())?`block`:`none`,this.element.setAttribute(`data-controls-layout`,e.controlsLayout),this.element.setAttribute(`data-controls-back-arrows`,e.controlsBackArrows)}bind(){let e=[`touchstart`,`click`];_&&(e=[`touchend`]),e.forEach(e=>{this.controlsLeft.forEach(t=>t.addEventListener(e,this.onNavigateLeftClicked,!1)),this.controlsRight.forEach(t=>t.addEventListener(e,this.onNavigateRightClicked,!1)),this.controlsUp.forEach(t=>t.addEventListener(e,this.onNavigateUpClicked,!1)),this.controlsDown.forEach(t=>t.addEventListener(e,this.onNavigateDownClicked,!1)),this.controlsPrev.forEach(t=>t.addEventListener(e,this.onNavigatePrevClicked,!1)),this.controlsNext.forEach(t=>t.addEventListener(e,this.onNavigateNextClicked,!1)),this.controlsFullscreen.forEach(t=>t.addEventListener(e,this.onEnterFullscreen,!1))})}unbind(){[`touchstart`,`touchend`,`click`].forEach(e=>{this.controlsLeft.forEach(t=>t.removeEventListener(e,this.onNavigateLeftClicked,!1)),this.controlsRight.forEach(t=>t.removeEventListener(e,this.onNavigateRightClicked,!1)),this.controlsUp.forEach(t=>t.removeEventListener(e,this.onNavigateUpClicked,!1)),this.controlsDown.forEach(t=>t.removeEventListener(e,this.onNavigateDownClicked,!1)),this.controlsPrev.forEach(t=>t.removeEventListener(e,this.onNavigatePrevClicked,!1)),this.controlsNext.forEach(t=>t.removeEventListener(e,this.onNavigateNextClicked,!1)),this.controlsFullscreen.forEach(t=>t.removeEventListener(e,this.onEnterFullscreen,!1))})}update(){let e=this.Reveal.availableRoutes();[...this.controlsLeft,...this.controlsRight,...this.controlsUp,...this.controlsDown,...this.controlsPrev,...this.controlsNext].forEach(e=>{e.classList.remove(`enabled`,`fragmented`),e.setAttribute(`disabled`,`disabled`)}),e.left&&this.controlsLeft.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.right&&this.controlsRight.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.up&&this.controlsUp.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.down&&this.controlsDown.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),(e.left||e.up)&&this.controlsPrev.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),(e.right||e.down)&&this.controlsNext.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)});let t=this.Reveal.getCurrentSlide();if(t){let e=this.Reveal.fragments.availableRoutes();e.prev&&this.controlsPrev.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsNext.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)});let n=this.Reveal.isVerticalSlide(t),r=n&&t.parentElement&&t.parentElement.querySelectorAll(`:scope > section`).length>1;n&&r?(e.prev&&this.controlsUp.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsDown.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)})):(e.prev&&this.controlsLeft.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsRight.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}))}if(this.Reveal.getConfig().controlsTutorial){let t=this.Reveal.getIndices();!this.Reveal.hasNavigatedVertically()&&e.down?this.controlsDownArrow.classList.add(`highlight`):(this.controlsDownArrow.classList.remove(`highlight`),this.Reveal.getConfig().rtl?!this.Reveal.hasNavigatedHorizontally()&&e.left&&t.v===0?this.controlsLeftArrow.classList.add(`highlight`):this.controlsLeftArrow.classList.remove(`highlight`):!this.Reveal.hasNavigatedHorizontally()&&e.right&&t.v===0?this.controlsRightArrow.classList.add(`highlight`):this.controlsRightArrow.classList.remove(`highlight`))}}destroy(){this.unbind(),this.element.remove()}onNavigateLeftClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.getConfig().navigationMode===`linear`?this.Reveal.prev():this.Reveal.left()}onNavigateRightClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.getConfig().navigationMode===`linear`?this.Reveal.next():this.Reveal.right()}onNavigateUpClicked(e){e.preventDefault(),this.Reveal.onULine truncated
`+e))});let r=document.querySelector(`head`);r&&r.insertBefore(n,r.lastChild)},ye=class{constructor(e){this.Reveal=e,this.state=`idle`,this.registeredPlugins={},this.asyncDependencies=[]}load(e,t){return this.state=`loading`,e.forEach(this.registerPlugin.bind(this)),new Promise(e=>{let n=[],r=0;if(t.forEach(e=>{(!e.condition||e.condition())&&(e.async?this.asyncDependencies.push(e):n.push(e))}),n.length){r=n.length;let t=t=>{t&&typeof t.callback==`function`&&t.callback(),--r===0&&this.initPlugins().then(e)};n.forEach(e=>{typeof e.id==`string`?(this.registerPlugin(e),t(e)):typeof e.src==`string`?ve(e.src,()=>t(e)):(console.warn(`Unrecognized plugin format`,e),t())})}else this.initPlugins().then(e)})}initPlugins(){return new Promise(e=>{let t=Object.values(this.registeredPlugins),n=t.length;if(n===0)this.loadAsync().then(e);else{let r,i=()=>{--n===0?this.loadAsync().then(e):r()},a=0;r=()=>{let e=t[a++];if(typeof e.init==`function`){let t=e.init(this.Reveal);t&&typeof t.then==`function`?t.then(i):i()}else i()},r()}})}loadAsync(){return this.state=`loaded`,this.asyncDependencies.length&&this.asyncDependencies.forEach(e=>{ve(e.src,e.callback)}),Promise.resolve()}registerPlugin(e){arguments.length===2&&typeof arguments[0]==`string`?(e=arguments[1],e.id=arguments[0]):typeof e==`function`&&(e=e());let t=e.id;typeof t==`string`?this.registeredPlugins[t]===void 0?(this.registeredPlugins[t]=e,this.state===`loaded`&&typeof e.init==`function`&&e.init(this.Reveal)):console.warn(`reveal.js: "`+t+`" plugin has already been registered`):console.warn(`Unrecognized plugin format; can't find plugin.id`,e)}hasPlugin(e){return!!this.registeredPlugins[e]}getPlugin(e){return this.registeredPlugins[e]}getRegisteredPlugins(){return this.registeredPlugins}destroy(){Object.values(this.registeredPlugins).forEach(e=>{typeof e.destroy==`function`&&e.destroy()}),this.registeredPlugins={},this.asyncDependencies=[]}},be=class{constructor(e){this.Reveal=e,this.onSlidesClicked=this.onSlidesClicked.bind(this),this.iframeTriggerSelector=null,this.mediaTriggerSelector=`[data-preview-image], [data-preview-video]`,this.stateProps=[`previewIframe`,`previewImage`,`previewVideo`,`previewFit`],this.state={}}update(){this.iframeTriggerSelector=this.Reveal.getConfig().previewLinks?`a[href]:not([data-preview-link=false]), [data-preview-link]:not(a):not([data-preview-link=false])`:`[data-preview-link]:not([data-preview-link=false])`;let e=this.Reveal.getSlidesElement().querySelectorAll(this.iframeTriggerSelector).length>0,t=this.Reveal.getSlidesElement().querySelectorAll(this.mediaTriggerSelector).length>0;e||t?this.Reveal.getSlidesElement().addEventListener(`click`,this.onSlidesClicked,!1):this.Reveal.getSlidesElement().removeEventListener(`click`,this.onSlidesClicked,!1)}createOverlay(e){this.dom=document.createElement(`div`),this.dom.classList.add(`r-overlay`),this.dom.classList.add(e),this.viewport=document.createElement(`div`),this.viewport.classList.add(`r-overlay-viewport`),this.dom.appendChild(this.viewport),this.Reveal.getRevealElement().appendChild(this.dom)}previewIframe(e){if(typeof e!=`string`)return;let t;try{t=new URL(e,document.baseURI)}catch(e){return}if(t.protocol===`file:`&&window.location.protocol!==`file:`||![`http:`,`https:`,`file:`].includes(t.protocol)&&t.href!==`about:blank`)return;this.close(),this.state={previewIframe:e},this.createOverlay(`r-overlay-preview`),this.dom.dataset.state=`loading`,this.viewport.innerHTML=`<header class="r-overlay-header">
<a class="r-overlay-header-button r-overlay-external" target="_blank" rel="noopener"><span class="icon"></span></a>
<button class="r-overlay-header-button r-overlay-close"><span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content">
<iframe></iframe>
<small class="r-overlay-content-inner">
<span class="r-overlay-error x-frame-error">Unable to load iframe. This is likely due to the site's policy (x-frame-options).</span>
</small>
</div>`;let n=this.dom.querySelector(`iframe`);n.addEventListener(`load`,e=>{this.dom.dataset.state=`loaded`},!1),n.src=t.href,this.dom.querySelector(`.r-overlay-external`).href=t.href,this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1),this.dom.querySelector(`.r-overlay-external`).addEventListener(`click`,e=>{this.close()},!1),this.Reveal.dispatchEvent({type:`previewiframe`,data:{url:e}})}previewMedia(e,t,n){if(t!==`image`&&t!==`video`){console.warn(`Please specify a valid media type to preview (image|video)`);return}this.close(),n=n||`scale-down`,this.createOverlay(`r-overlay-preview`),this.dom.dataset.state=`loading`,this.dom.dataset.previewFit=n,this.viewport.innerHTML=`<header class="r-overlay-header">
<button class="r-overlay-header-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content"></div>`;let r=this.dom.querySelector(`.r-overlay-content`);if(t===`image`){this.state={previewImage:e,previewFit:n};let t=document.createElement(`img`,{});t.src=e,r.appendChild(t),t.addEventListener(`load`,()=>{this.dom.dataset.state=`loaded`},!1),t.addEventListener(`error`,()=>{this.dom.dataset.state=`error`,r.innerHTML=`<span class="r-overlay-error">Unable to load image.</span>`},!1),this.dom.style.cursor=`zoom-out`,this.dom.addEventListener(`click`,e=>{this.close()},!1),this.Reveal.dispatchEvent({type:`previewimage`,data:{url:e}})}else if(t===`video`){this.state={previewVideo:e,previewFit:n};let t=document.createElement(`video`);t.autoplay=this.dom.dataset.previewAutoplay!==`false`,t.controls=this.dom.dataset.previewControls!==`false`,t.loop=this.dom.dataset.previewLoop===`true`,t.muted=this.dom.dataset.previewMuted===`true`,t.playsInline=!0,t.src=e,r.appendChild(t),t.addEventListener(`loadeddata`,()=>{this.dom.dataset.state=`loaded`},!1),t.addEventListener(`error`,()=>{this.dom.dataset.state=`error`,r.innerHTML=`<span class="r-overlay-error">Unable to load video.</span>`},!1),this.Reveal.dispatchEvent({type:`previewvideo`,data:{url:e}})}else throw Error(`Please specify a valid media type to preview`);this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1)}previewImage(e,t){this.previewMedia(e,`image`,t)}previewVideo(e,t){this.previewMedia(e,`video`,t)}toggleHelp(e){typeof e==`boolean`?e?this.showHelp():this.close():this.dom?this.close():this.showHelp()}showHelp(){if(this.Reveal.getConfig().help){this.close(),this.createOverlay(`r-overlay-help`);let e=`<p class="title">Keyboard Shortcuts</p>`,t=this.Reveal.keyboard.getShortcuts(),n=this.Reveal.keyboard.getBindings();e+=`<table><th>KEY</th><th>ACTION</th>`;for(let n in t)e+=`<tr><td>${n}</td><td>${t[n]}</td></tr>`;for(let t in n)n[t].key&&n[t].description&&(e+=`<tr><td>${n[t].key}</td><td>${n[t].description}</td></tr>`);e+=`</table>`,this.viewport.innerHTML=`
<header class="r-overlay-header">
<button class="r-overlay-header-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-content">
<div class="r-overlay-help-content">${e}</div>
</div>
`,this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1),this.Reveal.dispatchEvent({type:`showhelp`})}}isOpen(){return!!this.dom}close(){return this.dom?(this.dom.remove(),this.dom=null,this.state={},this.Reveal.dispatchEvent({type:`closeoverlay`}),!0):!1}getState(){return this.state}setState(e){this.stateProps.every(t=>this.state[t]===e[t])||(e.previewIframe?this.previewIframe(e.previewIframe):e.previewImage?this.previewImage(e.previewImage,e.previewFit):e.previewVideo?this.previewVideo(e.previewVideo,e.previewFit):this.close())}onSlidesClicked(e){let t=e.target,n=t.closest(this.iframeTriggerSelector),r=t.closest(this.mediaTriggerSelector);if(n){if(e.metaKey||e.shiftKey||e.altKey)return;let t=n.getAttribute(`data-preview-link`),r=typeof t==`string`&&t.startsWith(`http`)?t:n.getAttribute(`href`);r&&(this.previewIframe(r),e.preventDefault())}else if(r){if(r.hasAttribute(`data-preview-image`)){let t=r.dataset.previewImage||r.getAttribute(`src`);t&&(this.previewImage(t,r.dataset.previewFit),e.preventDefault())}else if(r.hasAttribute(`data-preview-video`)){let t=r.dataset.previewVideo||r.getAttribute(`src`);if(!t){let e=r.querySelector(`source`);e&&(t=e.getAttribute(`src`))}t&&(this.previewVideo(t,r.dataset.previewFit),e.preventDefault())}}}destroy(){this.close()}},F=40,I=1.03,xe=class{constructor(e){this.Reveal=e,this.touchStartX=0,this.touchStartY=0,this.touchStartCount=0,this.touchCaptured=!1,this.activePointers=new Map,this.onPointerDown=this.onPointerDown.bind(this),this.onPointerMove=this.onPointerMove.bind(this),this.onPointerUp=this.onPointerUp.bind(this),this.onPointerCancel=this.onPointerCancel.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this)}bind(){let e=this.Reveal.getRevealElement();`onpointerdown`in window?(e.addEventListener(`pointerdown`,this.onPointerDown,!1),e.addEventListener(`pointermove`,this.onPointerMove,!1),e.addEventListener(`pointerup`,this.onPointerUp,!1),e.addEventListener(`pointercancel`,this.onPointerCancel,!1)):(e.addEventListener(`touchstart`,this.onTouchStart,!1),e.addEventListener(`touchmove`,this.onTouchMove,!1),e.addEventListener(`touchend`,this.onTouchEnd,!1))}unbind(){let e=this.Reveal.getRevealElement();e.removeEventListener(`pointerdown`,this.onPointerDown,!1),e.removeEventListener(`pointermove`,this.onPointerMove,!1),e.removeEventListener(`pointerup`,this.onPointerUp,!1),e.removeEventListener(`pointercancel`,this.onPointerCancel,!1),e.removeEventListener(`touchstart`,this.onTouchStart,!1),e.removeEventListener(`touchmove`,this.onTouchMove,!1),e.removeEventListener(`touchend`,this.onTouchEnd,!1)}isSwipePrevented(e){if(a(e,`video[controls], audio[controls]`))return!0;for(;e&&typeof e.hasAttribute==`function`;){if(e.hasAttribute(`data-prevent-swipe`))return!0;e=e.parentNode}return!1}isViewportZoomed(){if(!window.visualViewport||typeof window.visualViewport.scale!=`number`)return!1;let e=window.visualViewport.scale;return this.visualViewportBaseScale=this.visualViewportBaseScale===void 0?e:Math.min(this.visualViewportBaseScale,e),e/this.visualViewportBaseScale>I}onTouchStart(e){if(this.touchCaptured=!1,this.isSwipePrevented(e.target))return!0;this.touchStartX=e.touches[0].clientX,this.touchStartY=e.touches[0].clientY,this.touchStartCount=e.touches.length}onTouchMove(e){if(this.isSwipePrevented(e.target))return!0;let t=this.Reveal.getConfig();if(this.touchCaptured)_&&e.preventDefault();else{this.Reveal.onUserInput(e);let n=e.touches[0].clientX,r=e.touches[0].clientY;if(e.touches.length===1&&this.touchStartCount!==2){let i=this.Reveal.availableRoutes({includeFragments:!0}),a=n-this.touchStartX,o=r-this.touchStartY;a>F&&Math.abs(a)>Math.abs(o)?(this.touchCaptured=!0,t.navigationMode===`linear`?t.rtl?this.Reveal.next():this.Reveal.prev():this.Reveal.left()):a<-40&&Math.abs(a)>Math.abs(o)?(this.touchCaptured=!0,t.navigationMode===`linear`?t.rtl?this.Reveal.prev():this.Reveal.next():this.Reveal.right()):o>F&&i.up?(this.touchCaptured=!0,t.navigationMode===`linear`?this.Reveal.prev():this.Reveal.up()):o<-40&&i.down&&(this.touchCaptured=!0,t.navigationMode===`linear`?this.Reveal.next():this.Reveal.down()),t.embedded?(this.touchCaptured||this.Reveal.isVerticalSlide())&&e.preventDefault():e.preventDefault()}}}onTouchEnd(e){this.touchCaptured&&!this.Reveal.slideContent.isAllowedToPlayAudio()&&this.Reveal.startEmbeddedContent(this.Reveal.getCurrentSlide()),this.touchCaptured=!1}getActiveTouches(){return Array.from(this.activePointers.values(),e=>({clientX:e.clientX,clientY:e.clientY}))}onPointerDown(e){e.pointerType===`touch`&&(this.activePointers.set(e.pointerId,e),e.touches=this.getActiveTouches(),this.onTouchStart(e))}onPointerMove(e){e.pointerType===`touch`&&(this.activePointers.set(e.pointerId,e),e.touches=this.getActiveTouches(),this.onTouchMove(e))}onPointerUp(e){e.pointerType===`touch`&&(this.activePointers.delete(e.pointerId), Line truncated
`):null}destroy(){this.element.remove()}},we=class{constructor(e,t){this.diameter=100,this.diameter2=this.diameter/2,this.thickness=6,this.playing=!1,this.progress=0,this.progressOffset=1,this.container=e,this.progressCheck=t,this.canvas=document.createElement(`canvas`),this.canvas.className=`playback`,this.canvas.width=this.diameter,this.canvas.height=this.diameter,this.canvas.style.width=this.diameter2+`px`,this.canvas.style.height=this.diameter2+`px`,this.context=this.canvas.getContext(`2d`),this.container.appendChild(this.canvas),this.render()}setPlaying(e){let t=this.playing;this.playing=e,!t&&this.playing?this.animate():this.render()}animate(){let e=this.progress;this.progress=this.progressCheck(),e>.8&&this.progress<.2&&(this.progressOffset=this.progress),this.render(),this.playing&&requestAnimationFrame(this.animate.bind(this))}render(){let e=this.playing?this.progress:0,t=this.diameter2-this.thickness,n=this.diameter2,r=this.diameter2;this.progressOffset+=(1-this.progressOffset)*.1;let i=-Math.PI/2+Math.PI*2*e,a=-Math.PI/2+this.progressOffset*(Math.PI*2);this.context.save(),this.context.clearRect(0,0,this.diameter,this.diameter),this.context.beginPath(),this.context.arc(n,r,t+4,0,Math.PI*2,!1),this.context.fillStyle=`rgba( 0, 0, 0, 0.4 )`,this.context.fill(),this.context.beginPath(),this.context.arc(n,r,t,0,Math.PI*2,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle=`rgba( 255, 255, 255, 0.2 )`,this.context.stroke(),this.playing&&(this.context.beginPath(),this.context.arc(n,r,t,a,i,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle=`#fff`,this.context.stroke()),this.context.translate(n-14,r-14),this.playing?(this.context.fillStyle=`#fff`,this.context.fillRect(0,0,10,28),this.context.fillRect(18,0,10,28)):(this.context.beginPath(),this.context.translate(4,0),this.context.moveTo(0,0),this.context.lineTo(24,14),this.context.lineTo(0,28),this.context.fillStyle=`#fff`,this.context.fill()),this.context.restore()}on(e,t){this.canvas.addEventListener(e,t,!1)}off(e,t){this.canvas.removeEventListener(e,t,!1)}destroy(){this.playing=!1,this.canvas.parentNode&&this.container.removeChild(this.canvas)}},Te={width:960,height:700,margin:.04,minScale:.2,maxScale:2,controls:!0,controlsTutorial:!0,controlsLayout:`bottom-right`,controlsBackArrows:`faded`,progress:!0,slideNumber:!1,showSlideNumber:`all`,hashOneBasedIndex:!1,hash:!1,respondToHashChanges:!0,jumpToSlide:!0,history:!1,keyboard:!0,keyboardCondition:null,disableLayout:!1,overview:!0,center:!0,touch:!0,loop:!1,rtl:!1,navigationMode:`default`,shuffle:!1,fragments:!0,fragmentInURL:!0,embedded:!1,help:!0,pause:!0,showNotes:!1,showHiddenSlides:!1,autoPlayMedia:null,preloadIframes:null,mouseWheel:!1,previewLinks:!1,viewDistance:3,mobileViewDistance:2,display:`block`,hideInactiveCursor:!0,hideCursorTime:5e3,sortFragmentsOnSync:!0,autoAnimate:!0,autoAnimateMatcher:null,autoAnimateEasing:`ease`,autoAnimateDuration:1,autoAnimateUnmatched:!0,autoAnimateStyles:[`opacity`,`color`,`background-color`,`padding`,`font-size`,`line-height`,`letter-spacing`,`border-width`,`border-color`,`border-radius`,`outline`,`outline-offset`],autoSlide:0,autoSlideStoppable:!0,autoSlideMethod:null,defaultTiming:null,postMessage:!0,postMessageEvents:!1,focusBodyOnPageVisibilityChange:!0,transition:`slide`,transitionSpeed:`default`,backgroundTransition:`fade`,parallaxBackgroundImage:``,parallaxBackgroundSize:``,parallaxBackgroundRepeat:``,parallaxBackgroundPosition:``,parallaxBackgroundHorizontal:null,parallaxBackgroundVertical:null,view:null,scrollLayout:`full`,scrollSnap:`mandatory`,scrollProgress:`auto`,scrollActivationWidth:435,pdfMaxPagesPerSlide:1/0,pdfSeparateFragments:!0,pdfPageHeightOffset:-1,dependencies:[],plugins:[]},Ee=`6.0.2`;function z(a,s){arguments.length<2&&(s=arguments[0],a=document.querySelector(`.reveal`));let l={},f={},p=!1,m=!1,h,_,v,y,b={hasNavigatedHorizontally:!1,hasNavigatedVertically:!1},x=[],S=1,T={layout:``,overview:``},D={},ie=`idle`,O=0,k,se=0,ce=-1,A=!1,j=new ee(l),M=new ne(l),N=new re(l),ve=new oe(l),F=new ae(l),I=new le(l),L=new ue(l),R=new de(l),z=new fe(l),B=new pe(l),V=new me(l),H=new he(l),U=new ge(l),De=new _e(l),W=new ye(l),G=new be(l),K=new Se(l),Oe=new xe(l),q=new Ce(l);function ke(e){if(!a)throw`Unable to find presentation root (<div class="reveal">).`;if(p)throw`Reveal.js has already been initialized.`;if(p=!0,D.wrapper=a,D.slides=a.querySelector(`.slides`),!D.slides)throw`Unable to find slides container (<div class="slides">).`;return f=P(P(P(P(P({},Te),f),s),e),u()),/print-pdf/gi.test(window.location.search)&&(f.view=`print`),Ae(),window.addEventListener(`load`,Y,!1),W.load(f.plugins,f.dependencies).then(je),new Promise(e=>l.on(`ready`,e))}function Ae(){f.embedded===!0?D.viewport=o(a,`.reveal-viewport`)||a:(D.viewport=document.body,document.documentElement.classList.add(`reveal-full-page`)),D.viewport.classList.add(`reveal-viewport`)}function je(){p!==!1&&(m=!0,Ne(),Pe(),Ve(),ze(),Be(),vt(),He(),F.update(!0),Me(),V Line truncated
+93
View File
@@ -0,0 +1,93 @@
Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans) WorkSans-Italic[wght].ttf: Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
BIN
View File
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
{
"file": "presentation-html-demo-0.1.0.html",
"sha256": "c81f4f77250ee50973c97d12d22172d9e2cd6f97a1e699e960b491a0aa1581b0",
"bytes": 362988,
"slides": 6,
"interactive_nodes": 6,
"reveal": "6.0.2",
"anime": "4.5.0"
}
+240
View File
@@ -0,0 +1,240 @@
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>From signal to decision — offline HTML starter</title>
<style>.reveal .stretch,.reveal .r-stretch{max-width:none;max-height:none}.reveal pre.stretch code,.reveal pre.r-stretch code{box-sizing:border-box;height:100%;max-height:100%}.reveal .r-fit-text{white-space:nowrap;display:inline-block}.reveal .r-stack{grid-template-rows:100%;display:grid}.reveal .r-stack>*{grid-area:1/1;margin:auto}.reveal .r-vstack,.reveal .r-hstack{display:flex}.reveal .r-vstack img,.reveal .r-vstack video,.reveal .r-hstack img,.reveal .r-hstack video{object-fit:contain;min-width:0;min-height:0}.reveal .r-vstack{flex-direction:column;justify-content:center;align-items:center}.reveal .r-hstack{flex-direction:row;justify-content:center;align-items:center}.reveal .items-stretch{align-items:stretch}.reveal .items-start{align-items:flex-start}.reveal .items-center{align-items:center}.reveal .items-end{align-items:flex-end}.reveal .justify-between{justify-content:space-between}.reveal .justify-around{justify-content:space-around}.reveal .justify-start{justify-content:flex-start}.reveal .justify-center{justify-content:center}.reveal .justify-end{justify-content:flex-end}html.reveal-full-page{width:100%;height:100vh;height:calc(var(--vh,1vh) * 100);height:100dvh;overflow:hidden}.reveal-viewport{color:#000;--r-controls-spacing:12px;--r-overlay-header-height:40px;--r-overlay-margin:0px;--r-overlay-padding:6px;--r-overlay-gap:5px;background-color:#fff;height:100%;margin:0;line-height:1;position:relative;overflow:hidden}@media screen and (width<=1024px),(height<=768px){.reveal-viewport{--r-overlay-header-height:26px}}.reveal-viewport:fullscreen{width:100%!important;height:100%!important;top:0!important;left:0!important;transform:none!important}.reveal .fragment{transition:all .2s}.reveal .fragment:not(.custom){opacity:0;visibility:hidden;will-change:opacity}.reveal .fragment.visible{opacity:1;visibility:inherit}.reveal .fragment.disabled{transition:none}.reveal .fragment.grow{opacity:1;visibility:inherit}.reveal .fragment.grow.visible{transform:scale(1.3)}.reveal .fragment.shrink{opacity:1;visibility:inherit}.reveal .fragment.shrink.visible{transform:scale(.7)}.reveal .fragment.zoom-in{transform:scale(.1)}.reveal .fragment.zoom-in.visible{transform:none}.reveal .fragment.fade-out{opacity:1;visibility:inherit}.reveal .fragment.fade-out.visible{opacity:0;visibility:hidden}.reveal .fragment.semi-fade-out{opacity:1;visibility:inherit}.reveal .fragment.semi-fade-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.strike{opacity:1;visibility:inherit}.reveal .fragment.strike.visible{text-decoration:line-through}.reveal .fragment.fade-up{transform:translateY(40px)}.reveal .fragment.fade-up.visible{transform:translate(0)}.reveal .fragment.fade-down{transform:translateY(-40px)}.reveal .fragment.fade-down.visible{transform:translate(0)}.reveal .fragment.fade-right{transform:translate(-40px)}.reveal .fragment.fade-right.visible{transform:translate(0)}.reveal .fragment.fade-left{transform:translate(40px)}.reveal .fragment.fade-left.visible{transform:translate(0)}.reveal .fragment.fade-in-then-out,.reveal .fragment.current-visible{opacity:0;visibility:hidden}.reveal .fragment.fade-in-then-out.current-fragment,.reveal .fragment.current-visible.current-fragment{opacity:1;visibility:inherit}.reveal .fragment.fade-in-then-semi-out{opacity:0;visibility:hidden}.reveal .fragment.fade-in-then-semi-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.fade-in-then-semi-out.current-fragment,.reveal .fragment.highlight-red,.reveal .fragment.highlight-current-red,.reveal .fragment.highlight-green,.reveal .fragment.highlight-current-green,.reveal .fragment.highlight-blue,.reveal .fragment.highlight-current-blue{opacity:1;visibility:inherit}.reveal .fragment.highlight-red.visible{color:#ff2c2d}.reveal .fragment.highlight-green.visible{color:#17ff2e}.reveal .fragment.highlight-blue.visible{color:#1b91ff}.reveal .fragment.highlight-current-red.current-fragment{color:#ff2c2d}.reveal .fragment.highlight-current-green.current-fragment{color:#17ff2e}.reveal .fragment.highlight-current-blue.current-fragment{color:#1b91ff}.reveal:after{content:"";font-style:italic}.reveal iframe{z-index:1}.reveal a{position:relative}@keyframes bounce-right{0%,10%,25%,40%,50%{transform:translate(0)}20%{transform:translate(10px)}30%{transform:translate(-5px)}}@keyframes bounce-left{0%,10%,25%,40%,50%{transform:translate(0)}20%{transform:translate(-10px)}30%{transform:translate(5px)}}@keyframes bounce-down{0%,10%,25%,40%,50%{transform:translateY(0)}20%{transform:translateY(10px)}30%{transform:translateY(-5px)}}.reveal .controls{top:auto;bottom:var(--r-controls-spacing);right:var(--r-controls-spacing);z-index:11;color:#000;pointer-events:none;font-size:10px;display:none;position:absolute;left:auto}.reveal .controls button{cursor:pointer;color:currentColor;z-index:2;pointer-events:auto;font-size:inherit;visibility:hidden;opacity:0;-webkit-appearance:none;-webkit-tap-highlight-color:#0000;background-color:#0000;border:0;outline:0;padding:0;trLine truncated
</style><style>
@font-face{font-family:'Work Sans';font-weight:400;font-style:normal;font-display:block;src:url(data:font/woff2;base64,d09GMgABAAAAAE28ABAAAAAAvJAAAE1aAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmobgZoAHI1wBmA/U1RBVEgAhTYRCAqBoWyBgTYLhSwAATYCJAOKVAQgBYRUB498DAcbmqglbJtWzOA8ABjKnsefTDf3yN1SZxItkmYjYtg4AHP7w7P/P+NAjTF8sAM0y1pTeAgFIpJMoUiC6ipTQ2eVRu9RKhOlnJlOnpfSXPNes9uJ1vNcKrMKFxcO6zorzHhVwTLZ9Fr8Fxdcln9amKrDJy4tv/6Jv65v77SpwIlQuOCkxdJFHLOWNsLH0AgCJ/FzhymcoW+3dQ2P5rhVph+4CoxdD1E11qmX5/ns/r99qvrygx5L0aT2yiFkYChH1Ovjac5+3u5GdhNiLCEkIQkhJUETgofgAVpq2AFFNUARrTgVo4VSRY5S1d/PubVUFOq0V7GjogxP0/kvBWrgrSdN0qQRa+wijVzukpMkF7loU9qmYtQUKe4NUqRsbHToDMaQ/Q2ZKR+TmcCGTRT+n//D/3eufe5vLcW6YIbHMGKhEHbwZkF+Z1Cv8jrAw1Qex+qFvtg6h+QKzdtP6czS1U9F//XPF1WK6qkLEjhAh/axLiebRZZkWbjaXeGCDPl7zSX4M3HMabU4waVc/j/+VO8HoeUQy0FhSCZhgVeaTCAT4BcHzTBshWFqsxEoPq40muARxm4/LLIAA4wk0WuAf4GA/ze3Wbo7xFIY5BGqfszyHpkKlTxXRCOZELn/38yfgVCZCU9jVfErAbKhrLHn8MQryi/5rYi/iks2C8pK5pqmQ6YH/r9SZBvuwYfQN6qyoopgvJ8kxoWnnv4mLs+bWQ8orX+F8tKc+QkzIV8Y+7WrHTAUCHLJ/+9Ps/ZdfysjTVBBbxZ19jiA9nIZAGiXqi2qp/ee9PT/0xvJX/KMpB/NaL4niWRIDPGJ7JD+tzfzLdsTIM0sEAclOzAQUgioWgLsqGm2BCwbqrgomq1aqrdeHr7X9Vmq466ukcZRRy5n88IpR+0gIgwD/VYP3C/LwJ8M/MxtlvmDCSMAWEQtYrQhYLxBtPD/33JaZi51IOVjXCTCcWLVsj/9pdVZtyhsShVCtaJjrIpV01mWnZ0PrUskxuaES4JzrZxQ0B0eIz7CIVEW+FzYq98o6XE7U6N+1MUsQjms7euTamFecs8iw+RhRMRtTNN0RAb3Or/MYW7kf9/1SmvLO18hLBJkCEMIQbzy8qd/QGIcmyrtxZVB7eebq7mUD9i3cZGSFF2U+ngNUlqG0+2HizlmLZ7KdwcXGB4A28GwITyIQC+kzwAsppjhCY8dHksweKaHhicxQTgEAxYCoyFkTAmOOeFBY+f4e4RugFggYEgnEbbDTnscgHfGsRechYcAtOMqEBj89BPPO4vmd8iQ/1RxmmiQoyxZtCA3fqfflcnf+nExCSAUBGnUCInHRQoVyMiDCxUpKRqKoq6AQD05AP/RodwCteN/TryxKd++YfcgQX+ih9G1DkXvn6a5IQCvnBzEp4QQEGh1EOjd1xtgnHKwod5uXFgvlxjWfxUaEXihqb9U1UxpCAIUVOKBFAqz9pXNMv41KDwvD/t+GVWlip+6YhZJOkuYN1KxO/YiulN/bmllwwQJ8NH1Bp5VerBt61r7Vmwx85q75tXOuuLlLn3Jhlw5jSpPXOTM859hbtOUqpBLA5KMaFzmHxXWSsW+c8wnAm35dCXroMOqXAgw0/7Vo+6v99+reWKfW+x0jctc6GwbnRlkl+Vh3oVmm2aiU4wxXJO+auilbs2qlVMKi4nKRF8rkGIQURKfZlQRb+IFHqXRWtX42qrLK23dqd93qiet/5Etda+dHWhPN3V9V7Wt89sq9aqUypc5lNL4xtXS0Ab6OmM96nJQC6WNKQ/sAqv48Mtf8YwHuJgoNFljycrdRNBsP+7xZv1lbxrHbvrZRhftrJjHIac9xtCsllWxuXKn00lmGhOJ/OPN+GPgT35CgwIJonFxLkMH0wVUeWfslz8JiB09UupZzUJm7Ot177M/O22UudfUgjXtZ3IjnVkzLrYsCzObRuMVykIRlu5T48da7/UKvYyN7tsVyBaga4kmFbSWlT+ArcQ0ONaBMGibgeHowZAC8HLuB/AAAVEZyzXQQU8cPMoD/cb8OrEApZUUm37YelStJ4ZkSLTErpYgRtZ5AzYPchetPMoo0c3u+KOXCVEo2XXAdfsFaIF+DJ7SXy81jW7sjj+umEigt+93UkL4WRxbkdO3X0hOzoZwxhlnnM0AZGBlb9Bwr3yZRa1rSz0gDe1E6oQvdRj61mFrmsoTTdDQgZ5cCZTcutwAOCid2C6gjWExvt12VCYWgmMBbWqDWhDcCkxZS0xfQ+QFBQG9hdZ0+eUUVi4uwjxJwOU2K5vShSZLAk2jIytqEhLwQcFDHpRvAvKABUuVX4sQfQn0yuDQC3jZQoFiiEoi1Xh4Cm7AkoCS3FZDM91YSxT45iEBd9TaaqC6JO8Dzh0N5AFxz6EVEkd6SJ3I1QAe2BAb+jrD9AolWZ9ksklWOZhDOpXKGCPqVU/4yId4yENNakDRolVctW7YPElgEhUGfyQTYEt/qCLwROkVjgK28+oWM/CX60CsPEzUGQcbO156rRFAQ6l7e4mdbggSG77uL7MARkMwoA+b8g8CTP9ffBQC+ABgAhIEgMYeBw+TcP5TjwZAJCveCAAAokCm1d9DZgWTZIt91rsMclLliOHWZWggSiNIFNTSqUj5V8l2GAZpFkVEhRyFEtPwUCa+ZtULfusmUBguNTR8LPSK1ZpNyAvoaMRau9Ch6o2bBTJ6UpgCAOR8IB5nD3dD/qhOd7WLLcS75aEQ4u1SNJacYcH6BbxyunIc+OeHUj1rNwyUFg95MqyzDM90Gr0NHo+PBAC8SGRdW4mch1gJhcMMXF9Fr242ZvT5ByfJAboMhLvfuK/P3MAhUbM+QmmppcGVkY13I+nJmfOKG3LtAy8o7/oNaNgZSx+EUQ3GniGr6rm5gdw4gJTgyNFtZnUhE1RZR5J1ldQyHgLKAHYOcR4Wlc1LFHpO8n0NiKV/lZWa1vo8okEP/5kPay5hQpxCO4IPaQpkd1rwsKBukQQFErF0oQbAy6TXQ5+UCSQ0UVqrYY4f0hWbnlwJbRGcw5YiKDOr2nVr27C/QTwKVtnwsTa62L6ALT59hlvSaXHa27rzbYEZPehrOMz3ys9b7jsAFPVyBh+ZkOoaStntzBoaNzEPANrXp2EZKYzBQUfcFDtHE/cWoYhNLTer10xz43hji1t3ROs91BmVZ03hwaOQD8WGjtRzYMILui3DhlENjf4CiMSpH6MLwzpzT4BuNtlsaTi7h73uvhBU5WpL8kHlnXGl9/qtFvSXx83YkP4QiXGdvrVsQjIkKAau1gcnHn0L8UhCHg/buzmlbniJ83xHfEFEa+FFvx65QUG9SgDyOr+fk6rJlTORQyCj56VzCQeHTAw3gNP8MJNHD/dnF4tQLUusE0+H1PU0ULu9n5xT/UmvGY4XQTA7klRri321RkINJQbqG0pBHwZouFPUwTpGn9qkfeQUaL7FhkFbMM4NXhMhB9u2vmBtc+PgDLLIYeEjMZ40QZPTxgk9qNUn55ocKlWiVtEzdR90ooMd6jnPhwKAS3j+OPhOmGkRp/JDjtda83XWn0V5chWs/UkRVHpqppJsYAGwxazfGVfKvlMCZxQgh0V1s1F/7ZDfwupgw1iH2H+eQVw07SVGqyNR3GLzzbPa1wnQnEA3BoqebIzIZNIsWGgPsekn9nXkjwWk1DUtzRS4XHq+RnelBoxBqKpRJAYdMg4noiUA8S/6Ga92fEpQ+NH44KjtGXJO2d6gajFEW1H57HTrcF8Z9rO7vFczIRgGsAM0DHHyQeSgKC2cP+3uDcyDwQdRG+yPAMp5aAKk79Ek7PBOUWdxBr8jzjlpkYNM35X+tOvriC9INSIViLXP2duBkrNRNbnSz0M7o/Z60em2Bc4z4mMbPd6caALEw655ySbSvUNPFRBGje13nahXv7tE3P3MExawzw+srn/wS0vG/VX/P2mhgJDAKDwkAZoNMTcinvxoBQhgECSYt0hRfMWJ4y9BggDJkgVKkSJImgImRZaKsVy7Mut1qbHZZo26DWiyyy7z7HHYfGcMWeayy1YYdtNKb0uD9gQR+DxRRPqjQ9uePHIDGeJtR77xtQtmBoSNQCIpUSgYDi6cDR6MgB2MmBjOnhzGkSMVBTWMMzc4dx4wnjzhvHjB6OlxGBiIePPDECAALYgJX7BgYmZmmAiRMPHiYRIlwqTJwZQrFyVPAaRIGaRcOVsVKtixsqJUqkdq0IClUSNKkyYCzVqxzLIQssgiLIstRlliCaRNG5alluJZZhnKcssJrbAGW7t2Eut1srHBBg66dCFttImjzTZj2Worlm22EerWy0afAbZ22UVvt9109tjDy157edpnnwn220/jgAM8HHSQu0MOcXbYYVqnnWZwxhlGQ4Z4u+wytWEj7F13g5ObbrL31kdcn4zDtACBFSGEwC0MYeAajnBwi0AESGMgBshiIiZIYyEWyGIjEhQJIoAiiggndGguySOnzhBv8nzjSwGTDoVg4iAESAOzVIwDAjDGU5hASUlGVcFNMIsquPDG7ZFjeyEVeRomyghxxpRgmGTjShm1lyYbW46x5I7i8uQTK5AQEFi4Lu8IwhlJoJYugFtoYVg0juMgERrQF8hayWYbV7bsym2PwqEIc/W5kfpNYruRBkxil3wrPhhxxw1vDOjkWfHHDy0KXFDAZy5roccLPSqcUOGEBjE8PNCi3tOsuww5eBns9+VhCV2v2a2W6WXZX7uylw8d3r09n5Jr134fshPTd+gh+jBdSxtpLine truncated
:root{--r-background-color:#101e25;--r-main-color:#f4f3ed;--ink:#f4f3ed;--muted:#b8c4c7;--bg:#101e25;--accent:#c5f27a;--line:#57717c}*{box-sizing:border-box}body.reveal-viewport{background:var(--bg);color:var(--ink)}.reveal{font-family:'Work Sans',sans-serif;font-size:28px}.reveal .slides{text-align:left}.reveal .slides>section{height:700px;padding:45px 48px}.eyebrow{color:var(--accent);font-size:20px;letter-spacing:.15em;text-transform:uppercase;margin:0 0 24px}h1{font-size:86px;line-height:1.05;letter-spacing:-.045em;margin:24px 0}h2{font-size:55px;line-height:1.1;margin:0 0 30px;letter-spacing:-.025em}p{line-height:1.5;max-width:1000px}.muted{color:var(--muted)}.big{font-size:38px;max-width:900px}.accent{color:var(--accent)}.rule{border-top:2px solid var(--line);padding-top:20px;margin-top:42px}button{font:inherit;color:var(--ink);background:#20343e;border:2px solid var(--line);padding:10px 16px;cursor:pointer;min-height:44px}button:hover,button[aria-pressed=true]{border-color:var(--accent);color:var(--accent)}button:focus-visible,[role=button]:focus-visible{outline:4px solid var(--accent);outline-offset:5px}.toolbar{position:fixed;right:22px;top:14px;z-index:30;display:flex;gap:12px;font:16px 'Work Sans',sans-serif}.toolbar button{font-size:16px}.reveal .slide-number{font:18px 'Work Sans';background:transparent;color:var(--muted);left:25px;right:auto;bottom:22px}.reveal .controls{color:var(--accent)}svg{width:100%;height:370px;overflow:visible}svg text{font-family:inherit;fill:var(--ink);font-size:24px;text-anchor:middle;dominant-baseline:middle}svg .edge{stroke:var(--line);stroke-width:3;fill:none}svg rect{fill:#20343e;stroke:var(--line);stroke-width:2}svg [role=button]{cursor:pointer}svg [aria-pressed=true] rect{stroke:var(--accent)}svg .hub{fill:var(--accent)}svg .hub-text{fill:var(--bg);font-weight:700}.detail{margin:5px 0;font-size:24px;min-height:40px}.stages{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:60px}.stage{border-top:3px solid var(--accent);padding-top:20px}.stage strong{font-size:36px}.stage p{font-size:25px}.dots{display:flex;gap:20px;margin:65px 0}.dot{width:70px;height:70px;background:var(--accent)}.ending{font-size:70px;line-height:1.12}.reveal .progress{color:var(--accent)}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
@media print{.toolbar,.controls{display:none!important}.reveal .slides section [data-motion]{opacity:1!important;transform:none!important}}
</style></head><body>
<div class="toolbar"><button id="motion" aria-pressed="true">Motion on</button><button id="fullscreen" aria-pressed="false">Enter fullscreen</button></div>
<div class="reveal"><div class="slides">
<section id="signal"><p class="eyebrow">Original example · presentation-html</p><h1>From signal<br>to <span class="accent">decision.</span></h1><p class="big">Use structure to explain the system.<br>Use motion only to explain change.</p><p class="rule muted">Reveal.js 6.0.2 + Anime.js 4.5.0 · All assets embedded</p></section>
<section id="story"><p class="eyebrow">01 / Narrative</p><h2>One question. One visual answer.</h2><div class="stages"><div class="stage" data-motion><strong>Context</strong><p>What changed, and why does it matter?</p></div><div class="stage" data-motion><strong>Mechanism</strong><p>What connects the evidence to the choice?</p></div><div class="stage" data-motion><strong>Decision</strong><p>What should the audience do next?</p></div></div><p class="rule muted">Choose the slide count from the story—not a template quota.</p></section>
<section id="system"><p class="eyebrow">02 / Editable SVG + real interaction</p><h2>Keep the decision at the center.</h2>
<svg viewBox="0 0 1100 370" role="group" aria-label="Six evidence sources feed one decision"><g aria-hidden="true"><path class="edge" d="M250 65L550 185M550 65V185M850 65L550 185M250 305L550 185M550 305V185M850 305L550 185"/></g>
<rect class="hub" x="435" y="145" width="230" height="80"/><text class="hub-text" x="550" y="185">Decision</text>
<g role="button" tabindex="0" aria-label="People" aria-pressed="false" data-node="0" data-detail="People: who owns the decision?"><rect x="140" y="33" width="220" height="64"/><text x="250" y="65">People</text></g>
<g role="button" tabindex="0" aria-label="Process" aria-pressed="false" data-node="1" data-detail="Process: how does work move?"><rect x="440" y="33" width="220" height="64"/><text x="550" y="65">Process</text></g>
<g role="button" tabindex="0" aria-label="Platform" aria-pressed="false" data-node="2" data-detail="Platform: what enables delivery?"><rect x="740" y="33" width="220" height="64"/><text x="850" y="65">Platform</text></g>
<g role="button" tabindex="0" aria-label="Quality" aria-pressed="false" data-node="3" data-detail="Quality: what evidence proves fitness?"><rect x="140" y="273" width="220" height="64"/><text x="250" y="305">Quality</text></g>
<g role="button" tabindex="0" aria-label="Cost" aria-pressed="false" data-node="4" data-detail="Cost: what constraint shapes the choice?"><rect x="440" y="273" width="220" height="64"/><text x="550" y="305">Cost</text></g>
<g role="button" tabindex="0" aria-label="Risk" aria-pressed="false" data-node="5" data-detail="Risk: what could invalidate the plan?"><rect x="740" y="273" width="220" height="64"/><text x="850" y="305">Risk</text></g>
</svg><p class="detail" id="detail" aria-live="polite">Select a source to reveal its role. Tab + Enter or Space also works.</p></section>
<section id="sequence"><p class="eyebrow">03 / Anime.js timeline</p><h2>Reveal dependency—not decoration.</h2><div class="stages"><div class="stage" data-motion><strong>Observe</strong><p>Make the signal visible.</p></div><div class="stage" data-motion><strong>Interpret</strong><p>Connect evidence and context.</p></div><div class="stage" data-motion><strong>Act</strong><p>Name the next reversible step.</p></div></div><p class="rule muted">A short, bounded sequence. No loops. No hidden final content.</p></section>
<section id="stagger"><p class="eyebrow">04 / Restrained stagger</p><h2>Make order perceptible.</h2><div class="dots" aria-hidden="true"><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i><i class="dot" data-motion></i></div><p class="big">The pattern is original. The technique is portable.</p><p class="muted">Reduced motion, print and revisit states show the complete result.</p></section>
<section id="decision"><p class="eyebrow">05 / Delivery gate</p><h2 class="ending">Test the exact file.<br>Inspect <span class="accent">every slide.</span></h2><p class="big">Offline. Readable. Keyboard-operable.<br>Then publish the immutable artifact.</p><p class="rule muted">← → Navigate · Esc Overview · F Fullscreen · Tab Focus</p></section>
</div></div><script>/*!
* reveal.js
* https://revealjs.com
* MIT licensed
*
* Copyright (C) 2011-2026 Hakim El Hattab, https://hakim.se
*/
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.Reveal=t())})(this,function(){var e=(e,t)=>{for(let n in t)e[n]=t[n];return e},t=(e,t)=>Array.from(e.querySelectorAll(t)),n=(e,t,n)=>{n?e.classList.add(t):e.classList.remove(t)},r=e=>{if(typeof e==`string`){if(e===`null`)return null;if(e===`true`)return!0;if(e===`false`)return!1;if(e.match(/^-?[\d\.]+$/))return parseFloat(e)}return e},i=(e,t)=>{e.style.transform=t},a=(e,t)=>{let n=e.matches||e.matchesSelector||e.msMatchesSelector;return!!(n&&n.call(e,t))},o=(e,t)=>{if(e&&typeof e.closest==`function`)return e.closest(t);for(;e;){if(a(e,t))return e;e=e.parentElement}return null},s=e=>{e=e||document.documentElement;let t=e.requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen;t&&t.apply(e)},c=(e,t,n,r=``)=>{let i=e.querySelectorAll(`.`+n);for(let t=0;t<i.length;t++){let n=i[t];if(n.parentNode===e)return n}let a=document.createElement(t);return a.className=n,a.innerHTML=r,e.appendChild(a),a},l=e=>{let t=document.createElement(`style`);return e&&e.length>0&&t.appendChild(document.createTextNode(e)),document.head.appendChild(t),t},u=()=>{let e={};location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi,t=>{let n=t.split(`=`).shift(),r=t.split(`=`).pop();return n&&r!==void 0&&(e[n]=r),t});for(let t in e){let n=e[t];e[t]=r(unescape(n))}return e.dependencies!==void 0&&delete e.dependencies,e},d=(e,t=0)=>{if(e){var n;let r,i=e.style.height;return e.style.height=`0px`,e.parentElement&&(e.parentElement.style.height=`auto`),r=t-(((n=e.parentElement)==null?void 0:n.offsetHeight)||0),e.style.height=i+`px`,e.parentElement&&e.parentElement.style.removeProperty(`height`),r}return t},f={mp4:`video/mp4`,m4a:`video/mp4`,ogv:`video/ogg`,mpeg:`video/mpeg`,webm:`video/webm`},p=(e=``)=>{let t=e.split(`.`).pop();return t?f[t]:void 0},m=(e=``)=>encodeURI(e).replace(/%5B/g,`[`).replace(/%5D/g,`]`).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),h=navigator.userAgent,g=/(iphone|ipod|ipad|android)/gi.test(h)||navigator.platform===`MacIntel`&&navigator.maxTouchPoints>1;/chrome/i.test(h)&&/edge/i.test(h);var _=/android/gi.test(h),v=function(e){if(e){var t=function(e){return[].slice.call(e)},n=0,r=1,i=2,a=3,o=[],s=null,c=`requestAnimationFrame`in e?function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{sync:!1};e.cancelAnimationFrame(s);var n=function(){return u(o.filter((function(e){return e.dirty&&e.active})))};if(t.sync)return n();s=e.requestAnimationFrame(n)}:function(){},l=function(e){return function(t){o.forEach((function(t){return t.dirty=e})),c(t)}},u=function(e){e.filter((function(e){return!e.styleComputed})).forEach((function(e){e.styleComputed=m(e)})),e.filter(h).forEach(g);var t=e.filter(p);t.forEach(f),t.forEach((function(e){g(e),d(e)})),t.forEach(_)},d=function(e){return e.dirty=n},f=function(e){e.availableWidth=e.element.parentNode.clientWidth,e.currentWidth=e.element.scrollWidth,e.previousFontSize=e.currentFontSize,e.currentFontSize=Math.min(Math.max(e.minSize,e.availableWidth/e.currentWidth*e.previousFontSize),e.maxSize),e.whiteSpace=e.multiLine&&e.currentFontSize===e.minSize?`normal`:`nowrap`},p=function(e){return e.dirty!==i||e.dirty===i&&e.element.parentNode.clientWidth!==e.availableWidth},m=function(t){var n=e.getComputedStyle(t.element,null);return t.currentFontSize=parseFloat(n.getPropertyValue(`font-size`)),t.display=n.getPropertyValue(`display`),t.whiteSpace=n.getPropertyValue(`white-space`),!0},h=function(e){var t=!1;return!e.preStyleTestCompleted&&(/inline-/.test(e.display)||(t=!0,e.display=`inline-block`),e.whiteSpace!==`nowrap`&&(t=!0,e.whiteSpace=`nowrap`),e.preStyleTestCompleted=!0,t)},g=function(e){e.element.style.whiteSpace=e.whiteSpace,e.element.style.display=e.display,e.element.style.fontSize=e.currentFontSize+`px`},_=function(e){e.element.dispatchEvent(new CustomEvent(`fit`,{detail:{oldValue:e.previousFontSize,newValue:e.currentFontSize,scaleFactor:e.currentFontSize/e.previousFontSize}}))},v=function(e,t){return function(n){e.dirty=t,e.active&&c(n)}},y=function(e){return function(){o=o.filter((function(t){return t.element!==e.element})),e.observeMutations&&e.observer.disconnect(),e.element.style.whiteSpace=e.originalStyle.whiteSpace,e.element.style.display=e.originalStyle.display,e.element.style.fontSize=e.originalStyle.fontSize}},b=function(e){return function(){e.active||(e.active=!0,c())}},x=function(e){return function(){return e.active=!1}},S=function(e){e.observeMutations&&(e.observer=new MutationObserver(v(e,r)),e.observer.observe(e.element,e.observeMutations))},ee={minSize:16,maxSize:512,multiLine:!0,observeMutations:`MutationObserver`in e&&{subtree:!0,childList:!0,characterData:!0}},C=null,w=function(){e.clearTimeout(C),C=e.setTimeout(l(i),E.observeWindowDelay)},te=[`resize`,`orientationchange`];return Object.defineProperty(ELine truncated
<span class="slide-number-a">${e}</span>
<span class="slide-number-delimiter">${t}</span>
<span class="slide-number-b">${n}</span>
</a>`:`<a href="${r}">
<span class="slide-number-a">${e}</span>
</a>`}destroy(){this.element.remove()}},re=class{constructor(e){this.Reveal=e,this.onInput=this.onInput.bind(this),this.onBlur=this.onBlur.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}render(){this.element=document.createElement(`div`),this.element.className=`jump-to-slide`,this.jumpInput=document.createElement(`input`),this.jumpInput.type=`text`,this.jumpInput.className=`jump-to-slide-input`,this.jumpInput.placeholder=`Jump to slide`,this.jumpInput.addEventListener(`input`,this.onInput),this.jumpInput.addEventListener(`keydown`,this.onKeyDown),this.jumpInput.addEventListener(`blur`,this.onBlur),this.element.appendChild(this.jumpInput)}show(){this.indicesOnShow=this.Reveal.getIndices(),this.Reveal.getRevealElement().appendChild(this.element),this.jumpInput.focus()}hide(){this.isVisible()&&(this.element.remove(),this.jumpInput.value=``,clearTimeout(this.jumpTimeout),delete this.jumpTimeout)}isVisible(){return!!this.element.parentNode}jump(){clearTimeout(this.jumpTimeout),delete this.jumpTimeout;let e=this.jumpInput.value.trim(``),t;if(/^\d+$/.test(e)){let n=this.Reveal.getConfig().slideNumber;if(n===`c`||n===`c/t`){let n=this.Reveal.getSlides()[parseInt(e,10)-1];n&&(t=this.Reveal.getIndices(n))}}return t||(/^\d+\.\d+$/.test(e)&&(e=e.replace(`.`,`/`)),t=this.Reveal.location.getIndicesFromHash(e,{oneBasedIndex:!0})),!t&&/\S+/i.test(e)&&e.length>1&&(t=this.search(e)),t&&e!==``?(this.Reveal.slide(t.h,t.v,t.f),!0):(this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),!1)}jumpAfter(e){clearTimeout(this.jumpTimeout),this.jumpTimeout=setTimeout(()=>this.jump(),e)}search(e){let t=RegExp(`\\b`+e.trim()+`\\b`,`i`),n=this.Reveal.getSlides().find(e=>t.test(e.innerText));return n?this.Reveal.getIndices(n):null}cancel(){this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),this.hide()}confirm(){this.jump(),this.hide()}destroy(){this.jumpInput.removeEventListener(`input`,this.onInput),this.jumpInput.removeEventListener(`keydown`,this.onKeyDown),this.jumpInput.removeEventListener(`blur`,this.onBlur),this.element.remove()}onKeyDown(e){e.keyCode===13?this.confirm():e.keyCode===27&&(this.cancel(),e.stopImmediatePropagation())}onInput(e){this.jumpAfter(200)}onBlur(){setTimeout(()=>this.hide(),1)}},D=e=>{let t=e.match(/^#([0-9a-f]{3})$/i);if(t&&t[1]){let e=t[1];return{r:parseInt(e.charAt(0),16)*17,g:parseInt(e.charAt(1),16)*17,b:parseInt(e.charAt(2),16)*17}}let n=e.match(/^#([0-9a-f]{6})$/i);if(n&&n[1]){let e=n[1];return{r:parseInt(e.slice(0,2),16),g:parseInt(e.slice(2,4),16),b:parseInt(e.slice(4,6),16)}}let r=e.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);if(r)return{r:parseInt(r[1],10),g:parseInt(r[2],10),b:parseInt(r[3],10)};let i=e.match(/^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i);return i?{r:parseInt(i[1],10),g:parseInt(i[2],10),b:parseInt(i[3],10),a:parseFloat(i[4])}:null},ie=e=>(typeof e==`string`&&(e=D(e)),e?(e.r*299+e.g*587+e.b*114)/1e3:null),ae=class{constructor(e){this.Reveal=e}render(){this.element=document.createElement(`div`),this.element.className=`backgrounds`,this.Reveal.getRevealElement().appendChild(this.element)}create(){this.element.innerHTML=``,this.element.classList.add(`no-transition`),this.Reveal.getHorizontalSlides().forEach(e=>{let n=this.createBackground(e,this.element);t(e,`section`).forEach(e=>{this.createBackground(e,n),n.classList.add(`stack`)})}),this.Reveal.getConfig().parallaxBackgroundImage?(this.element.style.backgroundImage=`url("`+this.Reveal.getConfig().parallaxBackgroundImage+`")`,this.element.style.backgroundSize=this.Reveal.getConfig().parallaxBackgroundSize,this.element.style.backgroundRepeat=this.Reveal.getConfig().parallaxBackgroundRepeat,this.element.style.backgroundPosition=this.Reveal.getConfig().parallaxBackgroundPosition,setTimeout(()=>{this.Reveal.getRevealElement().classList.add(`has-parallax-background`)},1)):(this.element.style.backgroundImage=``,this.Reveal.getRevealElement().classList.remove(`has-parallax-background`))}createBackground(e,t){let n=document.createElement(`div`);n.className=`slide-background `+e.className.replace(/present|past|future/,``);let r=document.createElement(`div`);return r.className=`slide-background-content`,n.appendChild(r),t.appendChild(n),e.slideBackgroundElement=n,e.slideBackgroundContentElement=r,this.sync(e),n}sync(e){let t=e.slideBackgroundElement,n=e.slideBackgroundContentElement,r={background:e.getAttribute(`data-background`),backgroundSize:e.getAttribute(`data-background-size`),backgroundImage:e.getAttribute(`data-background-image`),backgroundVideo:e.getAttribute(`data-background-video`),backgroundIframe:e.getAttribute(`data-background-iframe`),backgroundColor:e.getAttribute(`data-background-color`),backgroundGradient:e.getAttribute(`data-background-gradient`),backgroundRepeat:e.getAttribute(`data-background-repeat`),backgroundPosition:e.getAttribute(`data-background-position`),backgroundTransition:e.getAttribute(`data-backLine truncated
<button class="navigate-right" aria-label="${e?`previous slide`:`next slide`}"><div class="controls-arrow"></div></button>
<button class="navigate-up" aria-label="above slide"><div class="controls-arrow"></div></button>
<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>`,this.Reveal.getRevealElement().appendChild(this.element),this.controlsLeft=t(n,`.navigate-left`),this.controlsRight=t(n,`.navigate-right`),this.controlsUp=t(n,`.navigate-up`),this.controlsDown=t(n,`.navigate-down`),this.controlsPrev=t(n,`.navigate-prev`),this.controlsNext=t(n,`.navigate-next`),this.controlsFullscreen=t(n,`.enter-fullscreen`),this.controlsRightArrow=this.element.querySelector(`.navigate-right`),this.controlsLeftArrow=this.element.querySelector(`.navigate-left`),this.controlsDownArrow=this.element.querySelector(`.navigate-down`)}configure(e,t){let n=e.controls===`speaker`||e.controls===`speaker-only`;this.element.style.display=e.controls&&(!n||this.Reveal.isSpeakerNotes())?`block`:`none`,this.element.setAttribute(`data-controls-layout`,e.controlsLayout),this.element.setAttribute(`data-controls-back-arrows`,e.controlsBackArrows)}bind(){let e=[`touchstart`,`click`];_&&(e=[`touchend`]),e.forEach(e=>{this.controlsLeft.forEach(t=>t.addEventListener(e,this.onNavigateLeftClicked,!1)),this.controlsRight.forEach(t=>t.addEventListener(e,this.onNavigateRightClicked,!1)),this.controlsUp.forEach(t=>t.addEventListener(e,this.onNavigateUpClicked,!1)),this.controlsDown.forEach(t=>t.addEventListener(e,this.onNavigateDownClicked,!1)),this.controlsPrev.forEach(t=>t.addEventListener(e,this.onNavigatePrevClicked,!1)),this.controlsNext.forEach(t=>t.addEventListener(e,this.onNavigateNextClicked,!1)),this.controlsFullscreen.forEach(t=>t.addEventListener(e,this.onEnterFullscreen,!1))})}unbind(){[`touchstart`,`touchend`,`click`].forEach(e=>{this.controlsLeft.forEach(t=>t.removeEventListener(e,this.onNavigateLeftClicked,!1)),this.controlsRight.forEach(t=>t.removeEventListener(e,this.onNavigateRightClicked,!1)),this.controlsUp.forEach(t=>t.removeEventListener(e,this.onNavigateUpClicked,!1)),this.controlsDown.forEach(t=>t.removeEventListener(e,this.onNavigateDownClicked,!1)),this.controlsPrev.forEach(t=>t.removeEventListener(e,this.onNavigatePrevClicked,!1)),this.controlsNext.forEach(t=>t.removeEventListener(e,this.onNavigateNextClicked,!1)),this.controlsFullscreen.forEach(t=>t.removeEventListener(e,this.onEnterFullscreen,!1))})}update(){let e=this.Reveal.availableRoutes();[...this.controlsLeft,...this.controlsRight,...this.controlsUp,...this.controlsDown,...this.controlsPrev,...this.controlsNext].forEach(e=>{e.classList.remove(`enabled`,`fragmented`),e.setAttribute(`disabled`,`disabled`)}),e.left&&this.controlsLeft.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.right&&this.controlsRight.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.up&&this.controlsUp.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),e.down&&this.controlsDown.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),(e.left||e.up)&&this.controlsPrev.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)}),(e.right||e.down)&&this.controlsNext.forEach(e=>{e.classList.add(`enabled`),e.removeAttribute(`disabled`)});let t=this.Reveal.getCurrentSlide();if(t){let e=this.Reveal.fragments.availableRoutes();e.prev&&this.controlsPrev.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsNext.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)});let n=this.Reveal.isVerticalSlide(t),r=n&&t.parentElement&&t.parentElement.querySelectorAll(`:scope > section`).length>1;n&&r?(e.prev&&this.controlsUp.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsDown.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)})):(e.prev&&this.controlsLeft.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}),e.next&&this.controlsRight.forEach(e=>{e.classList.add(`fragmented`,`enabled`),e.removeAttribute(`disabled`)}))}if(this.Reveal.getConfig().controlsTutorial){let t=this.Reveal.getIndices();!this.Reveal.hasNavigatedVertically()&&e.down?this.controlsDownArrow.classList.add(`highlight`):(this.controlsDownArrow.classList.remove(`highlight`),this.Reveal.getConfig().rtl?!this.Reveal.hasNavigatedHorizontally()&&e.left&&t.v===0?this.controlsLeftArrow.classList.add(`highlight`):this.controlsLeftArrow.classList.remove(`highlight`):!this.Reveal.hasNavigatedHorizontally()&&e.right&&t.v===0?this.controlsRightArrow.classList.add(`highlight`):this.controlsRightArrow.classList.remove(`highlight`))}}destroy(){this.unbind(),this.element.remove()}onNavigateLeftClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.getConfig().navigationMode===`linear`?this.Reveal.prev():this.Reveal.left()}onNavigateRightClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.getConfig().navigationMode===`linear`?this.Reveal.next():this.Reveal.right()}onNavigateUpClicked(e){e.preventDefault(),this.Reveal.onULine truncated
`+e))});let r=document.querySelector(`head`);r&&r.insertBefore(n,r.lastChild)},ye=class{constructor(e){this.Reveal=e,this.state=`idle`,this.registeredPlugins={},this.asyncDependencies=[]}load(e,t){return this.state=`loading`,e.forEach(this.registerPlugin.bind(this)),new Promise(e=>{let n=[],r=0;if(t.forEach(e=>{(!e.condition||e.condition())&&(e.async?this.asyncDependencies.push(e):n.push(e))}),n.length){r=n.length;let t=t=>{t&&typeof t.callback==`function`&&t.callback(),--r===0&&this.initPlugins().then(e)};n.forEach(e=>{typeof e.id==`string`?(this.registerPlugin(e),t(e)):typeof e.src==`string`?ve(e.src,()=>t(e)):(console.warn(`Unrecognized plugin format`,e),t())})}else this.initPlugins().then(e)})}initPlugins(){return new Promise(e=>{let t=Object.values(this.registeredPlugins),n=t.length;if(n===0)this.loadAsync().then(e);else{let r,i=()=>{--n===0?this.loadAsync().then(e):r()},a=0;r=()=>{let e=t[a++];if(typeof e.init==`function`){let t=e.init(this.Reveal);t&&typeof t.then==`function`?t.then(i):i()}else i()},r()}})}loadAsync(){return this.state=`loaded`,this.asyncDependencies.length&&this.asyncDependencies.forEach(e=>{ve(e.src,e.callback)}),Promise.resolve()}registerPlugin(e){arguments.length===2&&typeof arguments[0]==`string`?(e=arguments[1],e.id=arguments[0]):typeof e==`function`&&(e=e());let t=e.id;typeof t==`string`?this.registeredPlugins[t]===void 0?(this.registeredPlugins[t]=e,this.state===`loaded`&&typeof e.init==`function`&&e.init(this.Reveal)):console.warn(`reveal.js: "`+t+`" plugin has already been registered`):console.warn(`Unrecognized plugin format; can't find plugin.id`,e)}hasPlugin(e){return!!this.registeredPlugins[e]}getPlugin(e){return this.registeredPlugins[e]}getRegisteredPlugins(){return this.registeredPlugins}destroy(){Object.values(this.registeredPlugins).forEach(e=>{typeof e.destroy==`function`&&e.destroy()}),this.registeredPlugins={},this.asyncDependencies=[]}},be=class{constructor(e){this.Reveal=e,this.onSlidesClicked=this.onSlidesClicked.bind(this),this.iframeTriggerSelector=null,this.mediaTriggerSelector=`[data-preview-image], [data-preview-video]`,this.stateProps=[`previewIframe`,`previewImage`,`previewVideo`,`previewFit`],this.state={}}update(){this.iframeTriggerSelector=this.Reveal.getConfig().previewLinks?`a[href]:not([data-preview-link=false]), [data-preview-link]:not(a):not([data-preview-link=false])`:`[data-preview-link]:not([data-preview-link=false])`;let e=this.Reveal.getSlidesElement().querySelectorAll(this.iframeTriggerSelector).length>0,t=this.Reveal.getSlidesElement().querySelectorAll(this.mediaTriggerSelector).length>0;e||t?this.Reveal.getSlidesElement().addEventListener(`click`,this.onSlidesClicked,!1):this.Reveal.getSlidesElement().removeEventListener(`click`,this.onSlidesClicked,!1)}createOverlay(e){this.dom=document.createElement(`div`),this.dom.classList.add(`r-overlay`),this.dom.classList.add(e),this.viewport=document.createElement(`div`),this.viewport.classList.add(`r-overlay-viewport`),this.dom.appendChild(this.viewport),this.Reveal.getRevealElement().appendChild(this.dom)}previewIframe(e){if(typeof e!=`string`)return;let t;try{t=new URL(e,document.baseURI)}catch(e){return}if(t.protocol===`file:`&&window.location.protocol!==`file:`||![`http:`,`https:`,`file:`].includes(t.protocol)&&t.href!==`about:blank`)return;this.close(),this.state={previewIframe:e},this.createOverlay(`r-overlay-preview`),this.dom.dataset.state=`loading`,this.viewport.innerHTML=`<header class="r-overlay-header">
<a class="r-overlay-header-button r-overlay-external" target="_blank" rel="noopener"><span class="icon"></span></a>
<button class="r-overlay-header-button r-overlay-close"><span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content">
<iframe></iframe>
<small class="r-overlay-content-inner">
<span class="r-overlay-error x-frame-error">Unable to load iframe. This is likely due to the site's policy (x-frame-options).</span>
</small>
</div>`;let n=this.dom.querySelector(`iframe`);n.addEventListener(`load`,e=>{this.dom.dataset.state=`loaded`},!1),n.src=t.href,this.dom.querySelector(`.r-overlay-external`).href=t.href,this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1),this.dom.querySelector(`.r-overlay-external`).addEventListener(`click`,e=>{this.close()},!1),this.Reveal.dispatchEvent({type:`previewiframe`,data:{url:e}})}previewMedia(e,t,n){if(t!==`image`&&t!==`video`){console.warn(`Please specify a valid media type to preview (image|video)`);return}this.close(),n=n||`scale-down`,this.createOverlay(`r-overlay-preview`),this.dom.dataset.state=`loading`,this.dom.dataset.previewFit=n,this.viewport.innerHTML=`<header class="r-overlay-header">
<button class="r-overlay-header-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content"></div>`;let r=this.dom.querySelector(`.r-overlay-content`);if(t===`image`){this.state={previewImage:e,previewFit:n};let t=document.createElement(`img`,{});t.src=e,r.appendChild(t),t.addEventListener(`load`,()=>{this.dom.dataset.state=`loaded`},!1),t.addEventListener(`error`,()=>{this.dom.dataset.state=`error`,r.innerHTML=`<span class="r-overlay-error">Unable to load image.</span>`},!1),this.dom.style.cursor=`zoom-out`,this.dom.addEventListener(`click`,e=>{this.close()},!1),this.Reveal.dispatchEvent({type:`previewimage`,data:{url:e}})}else if(t===`video`){this.state={previewVideo:e,previewFit:n};let t=document.createElement(`video`);t.autoplay=this.dom.dataset.previewAutoplay!==`false`,t.controls=this.dom.dataset.previewControls!==`false`,t.loop=this.dom.dataset.previewLoop===`true`,t.muted=this.dom.dataset.previewMuted===`true`,t.playsInline=!0,t.src=e,r.appendChild(t),t.addEventListener(`loadeddata`,()=>{this.dom.dataset.state=`loaded`},!1),t.addEventListener(`error`,()=>{this.dom.dataset.state=`error`,r.innerHTML=`<span class="r-overlay-error">Unable to load video.</span>`},!1),this.Reveal.dispatchEvent({type:`previewvideo`,data:{url:e}})}else throw Error(`Please specify a valid media type to preview`);this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1)}previewImage(e,t){this.previewMedia(e,`image`,t)}previewVideo(e,t){this.previewMedia(e,`video`,t)}toggleHelp(e){typeof e==`boolean`?e?this.showHelp():this.close():this.dom?this.close():this.showHelp()}showHelp(){if(this.Reveal.getConfig().help){this.close(),this.createOverlay(`r-overlay-help`);let e=`<p class="title">Keyboard Shortcuts</p>`,t=this.Reveal.keyboard.getShortcuts(),n=this.Reveal.keyboard.getBindings();e+=`<table><th>KEY</th><th>ACTION</th>`;for(let n in t)e+=`<tr><td>${n}</td><td>${t[n]}</td></tr>`;for(let t in n)n[t].key&&n[t].description&&(e+=`<tr><td>${n[t].key}</td><td>${n[t].description}</td></tr>`);e+=`</table>`,this.viewport.innerHTML=`
<header class="r-overlay-header">
<button class="r-overlay-header-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-content">
<div class="r-overlay-help-content">${e}</div>
</div>
`,this.dom.querySelector(`.r-overlay-close`).addEventListener(`click`,e=>{this.close(),e.preventDefault()},!1),this.Reveal.dispatchEvent({type:`showhelp`})}}isOpen(){return!!this.dom}close(){return this.dom?(this.dom.remove(),this.dom=null,this.state={},this.Reveal.dispatchEvent({type:`closeoverlay`}),!0):!1}getState(){return this.state}setState(e){this.stateProps.every(t=>this.state[t]===e[t])||(e.previewIframe?this.previewIframe(e.previewIframe):e.previewImage?this.previewImage(e.previewImage,e.previewFit):e.previewVideo?this.previewVideo(e.previewVideo,e.previewFit):this.close())}onSlidesClicked(e){let t=e.target,n=t.closest(this.iframeTriggerSelector),r=t.closest(this.mediaTriggerSelector);if(n){if(e.metaKey||e.shiftKey||e.altKey)return;let t=n.getAttribute(`data-preview-link`),r=typeof t==`string`&&t.startsWith(`http`)?t:n.getAttribute(`href`);r&&(this.previewIframe(r),e.preventDefault())}else if(r){if(r.hasAttribute(`data-preview-image`)){let t=r.dataset.previewImage||r.getAttribute(`src`);t&&(this.previewImage(t,r.dataset.previewFit),e.preventDefault())}else if(r.hasAttribute(`data-preview-video`)){let t=r.dataset.previewVideo||r.getAttribute(`src`);if(!t){let e=r.querySelector(`source`);e&&(t=e.getAttribute(`src`))}t&&(this.previewVideo(t,r.dataset.previewFit),e.preventDefault())}}}destroy(){this.close()}},F=40,I=1.03,xe=class{constructor(e){this.Reveal=e,this.touchStartX=0,this.touchStartY=0,this.touchStartCount=0,this.touchCaptured=!1,this.activePointers=new Map,this.onPointerDown=this.onPointerDown.bind(this),this.onPointerMove=this.onPointerMove.bind(this),this.onPointerUp=this.onPointerUp.bind(this),this.onPointerCancel=this.onPointerCancel.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this)}bind(){let e=this.Reveal.getRevealElement();`onpointerdown`in window?(e.addEventListener(`pointerdown`,this.onPointerDown,!1),e.addEventListener(`pointermove`,this.onPointerMove,!1),e.addEventListener(`pointerup`,this.onPointerUp,!1),e.addEventListener(`pointercancel`,this.onPointerCancel,!1)):(e.addEventListener(`touchstart`,this.onTouchStart,!1),e.addEventListener(`touchmove`,this.onTouchMove,!1),e.addEventListener(`touchend`,this.onTouchEnd,!1))}unbind(){let e=this.Reveal.getRevealElement();e.removeEventListener(`pointerdown`,this.onPointerDown,!1),e.removeEventListener(`pointermove`,this.onPointerMove,!1),e.removeEventListener(`pointerup`,this.onPointerUp,!1),e.removeEventListener(`pointercancel`,this.onPointerCancel,!1),e.removeEventListener(`touchstart`,this.onTouchStart,!1),e.removeEventListener(`touchmove`,this.onTouchMove,!1),e.removeEventListener(`touchend`,this.onTouchEnd,!1)}isSwipePrevented(e){if(a(e,`video[controls], audio[controls]`))return!0;for(;e&&typeof e.hasAttribute==`function`;){if(e.hasAttribute(`data-prevent-swipe`))return!0;e=e.parentNode}return!1}isViewportZoomed(){if(!window.visualViewport||typeof window.visualViewport.scale!=`number`)return!1;let e=window.visualViewport.scale;return this.visualViewportBaseScale=this.visualViewportBaseScale===void 0?e:Math.min(this.visualViewportBaseScale,e),e/this.visualViewportBaseScale>I}onTouchStart(e){if(this.touchCaptured=!1,this.isSwipePrevented(e.target))return!0;this.touchStartX=e.touches[0].clientX,this.touchStartY=e.touches[0].clientY,this.touchStartCount=e.touches.length}onTouchMove(e){if(this.isSwipePrevented(e.target))return!0;let t=this.Reveal.getConfig();if(this.touchCaptured)_&&e.preventDefault();else{this.Reveal.onUserInput(e);let n=e.touches[0].clientX,r=e.touches[0].clientY;if(e.touches.length===1&&this.touchStartCount!==2){let i=this.Reveal.availableRoutes({includeFragments:!0}),a=n-this.touchStartX,o=r-this.touchStartY;a>F&&Math.abs(a)>Math.abs(o)?(this.touchCaptured=!0,t.navigationMode===`linear`?t.rtl?this.Reveal.next():this.Reveal.prev():this.Reveal.left()):a<-40&&Math.abs(a)>Math.abs(o)?(this.touchCaptured=!0,t.navigationMode===`linear`?t.rtl?this.Reveal.prev():this.Reveal.next():this.Reveal.right()):o>F&&i.up?(this.touchCaptured=!0,t.navigationMode===`linear`?this.Reveal.prev():this.Reveal.up()):o<-40&&i.down&&(this.touchCaptured=!0,t.navigationMode===`linear`?this.Reveal.next():this.Reveal.down()),t.embedded?(this.touchCaptured||this.Reveal.isVerticalSlide())&&e.preventDefault():e.preventDefault()}}}onTouchEnd(e){this.touchCaptured&&!this.Reveal.slideContent.isAllowedToPlayAudio()&&this.Reveal.startEmbeddedContent(this.Reveal.getCurrentSlide()),this.touchCaptured=!1}getActiveTouches(){return Array.from(this.activePointers.values(),e=>({clientX:e.clientX,clientY:e.clientY}))}onPointerDown(e){e.pointerType===`touch`&&(this.activePointers.set(e.pointerId,e),e.touches=this.getActiveTouches(),this.onTouchStart(e))}onPointerMove(e){e.pointerType===`touch`&&(this.activePointers.set(e.pointerId,e),e.touches=this.getActiveTouches(),this.onTouchMove(e))}onPointerUp(e){e.pointerType===`touch`&&(this.activePointers.delete(e.pointerId),Line truncated
`):null}destroy(){this.element.remove()}},we=class{constructor(e,t){this.diameter=100,this.diameter2=this.diameter/2,this.thickness=6,this.playing=!1,this.progress=0,this.progressOffset=1,this.container=e,this.progressCheck=t,this.canvas=document.createElement(`canvas`),this.canvas.className=`playback`,this.canvas.width=this.diameter,this.canvas.height=this.diameter,this.canvas.style.width=this.diameter2+`px`,this.canvas.style.height=this.diameter2+`px`,this.context=this.canvas.getContext(`2d`),this.container.appendChild(this.canvas),this.render()}setPlaying(e){let t=this.playing;this.playing=e,!t&&this.playing?this.animate():this.render()}animate(){let e=this.progress;this.progress=this.progressCheck(),e>.8&&this.progress<.2&&(this.progressOffset=this.progress),this.render(),this.playing&&requestAnimationFrame(this.animate.bind(this))}render(){let e=this.playing?this.progress:0,t=this.diameter2-this.thickness,n=this.diameter2,r=this.diameter2;this.progressOffset+=(1-this.progressOffset)*.1;let i=-Math.PI/2+Math.PI*2*e,a=-Math.PI/2+this.progressOffset*(Math.PI*2);this.context.save(),this.context.clearRect(0,0,this.diameter,this.diameter),this.context.beginPath(),this.context.arc(n,r,t+4,0,Math.PI*2,!1),this.context.fillStyle=`rgba( 0, 0, 0, 0.4 )`,this.context.fill(),this.context.beginPath(),this.context.arc(n,r,t,0,Math.PI*2,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle=`rgba( 255, 255, 255, 0.2 )`,this.context.stroke(),this.playing&&(this.context.beginPath(),this.context.arc(n,r,t,a,i,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle=`#fff`,this.context.stroke()),this.context.translate(n-14,r-14),this.playing?(this.context.fillStyle=`#fff`,this.context.fillRect(0,0,10,28),this.context.fillRect(18,0,10,28)):(this.context.beginPath(),this.context.translate(4,0),this.context.moveTo(0,0),this.context.lineTo(24,14),this.context.lineTo(0,28),this.context.fillStyle=`#fff`,this.context.fill()),this.context.restore()}on(e,t){this.canvas.addEventListener(e,t,!1)}off(e,t){this.canvas.removeEventListener(e,t,!1)}destroy(){this.playing=!1,this.canvas.parentNode&&this.container.removeChild(this.canvas)}},Te={width:960,height:700,margin:.04,minScale:.2,maxScale:2,controls:!0,controlsTutorial:!0,controlsLayout:`bottom-right`,controlsBackArrows:`faded`,progress:!0,slideNumber:!1,showSlideNumber:`all`,hashOneBasedIndex:!1,hash:!1,respondToHashChanges:!0,jumpToSlide:!0,history:!1,keyboard:!0,keyboardCondition:null,disableLayout:!1,overview:!0,center:!0,touch:!0,loop:!1,rtl:!1,navigationMode:`default`,shuffle:!1,fragments:!0,fragmentInURL:!0,embedded:!1,help:!0,pause:!0,showNotes:!1,showHiddenSlides:!1,autoPlayMedia:null,preloadIframes:null,mouseWheel:!1,previewLinks:!1,viewDistance:3,mobileViewDistance:2,display:`block`,hideInactiveCursor:!0,hideCursorTime:5e3,sortFragmentsOnSync:!0,autoAnimate:!0,autoAnimateMatcher:null,autoAnimateEasing:`ease`,autoAnimateDuration:1,autoAnimateUnmatched:!0,autoAnimateStyles:[`opacity`,`color`,`background-color`,`padding`,`font-size`,`line-height`,`letter-spacing`,`border-width`,`border-color`,`border-radius`,`outline`,`outline-offset`],autoSlide:0,autoSlideStoppable:!0,autoSlideMethod:null,defaultTiming:null,postMessage:!0,postMessageEvents:!1,focusBodyOnPageVisibilityChange:!0,transition:`slide`,transitionSpeed:`default`,backgroundTransition:`fade`,parallaxBackgroundImage:``,parallaxBackgroundSize:``,parallaxBackgroundRepeat:``,parallaxBackgroundPosition:``,parallaxBackgroundHorizontal:null,parallaxBackgroundVertical:null,view:null,scrollLayout:`full`,scrollSnap:`mandatory`,scrollProgress:`auto`,scrollActivationWidth:435,pdfMaxPagesPerSlide:1/0,pdfSeparateFragments:!0,pdfPageHeightOffset:-1,dependencies:[],plugins:[]},Ee=`6.0.2`;function z(a,s){arguments.length<2&&(s=arguments[0],a=document.querySelector(`.reveal`));let l={},f={},p=!1,m=!1,h,_,v,y,b={hasNavigatedHorizontally:!1,hasNavigatedVertically:!1},x=[],S=1,T={layout:``,overview:``},D={},ie=`idle`,O=0,k,se=0,ce=-1,A=!1,j=new ee(l),M=new ne(l),N=new re(l),ve=new oe(l),F=new ae(l),I=new le(l),L=new ue(l),R=new de(l),z=new fe(l),B=new pe(l),V=new me(l),H=new he(l),U=new ge(l),De=new _e(l),W=new ye(l),G=new be(l),K=new Se(l),Oe=new xe(l),q=new Ce(l);function ke(e){if(!a)throw`Unable to find presentation root (<div class="reveal">).`;if(p)throw`Reveal.js has already been initialized.`;if(p=!0,D.wrapper=a,D.slides=a.querySelector(`.slides`),!D.slides)throw`Unable to find slides container (<div class="slides">).`;return f=P(P(P(P(P({},Te),f),s),e),u()),/print-pdf/gi.test(window.location.search)&&(f.view=`print`),Ae(),window.addEventListener(`load`,Y,!1),W.load(f.plugins,f.dependencies).then(je),new Promise(e=>l.on(`ready`,e))}function Ae(){f.embedded===!0?D.viewport=o(a,`.reveal-viewport`)||a:(D.viewport=document.body,document.documentElement.classList.add(`reveal-full-page`)),D.viewport.classList.add(`reveal-viewport`)}function je(){p!==!1&&(m=!0,Ne(),Pe(),Ve(),ze(),Be(),vt(),He(),F.update(!0),Me(),VLine truncated
* Anime.js - UMD minified bundle
* @version v4.5.0
* @license MIT
* @copyright 2026 - Julian Garnier
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).anime={})}(this,function(t){"use strict";const e="undefined"!=typeof window,s=e?window:null,i=e?document:null,r={replace:0,none:1,blend:2},n=Symbol(),o=Symbol(),a=Symbol(),l=Symbol(),h=Symbol(),d=1e-11,c=1e12,u=1e3,p="",m=[],f=(()=>{const t=new Map;return t.set("x","translateX"),t.set("y","translateY"),t.set("z","translateZ"),t})(),g=["perspective","translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","scaleZ","skew","skewX","skewY"],y=g.reduce((t,e)=>({...t,[e]:e+"("}),{}),_=()=>{},b=/\)\s*[-.\d]/,v=/(^#([\da-f]{3}){1,2}$)|(^#([\da-f]{4}){1,2}$)/i,T=/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i,x=/rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i,S=/hsl\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*\)/i,w=/hsla\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i,$=/[-+]?\d*\.?\d+(?:e[-+]?\d)?/gi,C=/^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i,E=/([a-z])([A-Z])/g,k=/(\*=|\+=|-=)/,N=/var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/,D={id:null,keyframes:null,playbackEase:null,playbackRate:1,frameRate:240,loop:0,reversed:!1,alternate:!1,autoplay:!0,persist:!1,duration:u,delay:0,loopDelay:0,ease:"out(2)",composition:r.replace,modifier:t=>t,onBegin:_,onBeforeUpdate:_,onUpdate:_,onLoop:_,onPause:_,onComplete:_,onRender:_},A={current:null,root:i},I={defaults:D,precision:4,timeScale:1,tickThreshold:200,editor:null},R={version:"4.5.0",engine:null};e&&(s.AnimeJS||(s.AnimeJS=[]),s.AnimeJS.push(R));const L=t=>t.replace(E,"$1-$2").toLowerCase(),B=(t,e)=>0===t.indexOf(e),P=Date.now,F=Array.isArray,M=t=>t&&t.constructor===Object,V=t=>"number"==typeof t&&!isNaN(t),O=t=>"string"==typeof t,z=t=>"function"==typeof t,H=t=>void 0===t,X=t=>H(t)||null===t,Y=t=>e&&t instanceof SVGElement,W=t=>v.test(t),U=t=>B(t,"rgb"),q=t=>B(t,"hsl"),j=t=>!I.defaults.hasOwnProperty(t),G=["opacity","rotate","overflow","color"],Z=t=>O(t)?parseFloat(t):t,Q=Math.pow,J=Math.sqrt,K=Math.sin,tt=Math.cos,et=Math.abs,st=Math.exp,it=Math.ceil,rt=Math.floor,nt=Math.asin,ot=Math.max,at=Math.atan2,lt=Math.PI,ht=Math.round,dt=(t,e,s)=>t<e?e:t>s?s:t,ct=(t,e)=>{if(e<0)return t;if(!e)return ht(t);const s=10**e;return ht(t*s)/s},ut=(t,e)=>F(e)?e.reduce((e,s)=>et(s-t)<et(e-t)?s:e):e?ht(t/e)*e:t,pt=(t,e,s)=>1===s?e:0===s?t:t+(e-t)*s,mt=t=>t===1/0?c:t===-1/0?-c:t,ft=t=>t<=d?d:mt(ct(t,11)),gt=t=>F(t)?[...t]:t,yt=(t,e)=>{const s={...t};for(let i in e){const r=t[i];s[i]=H(r)?e[i]:r}return s},_t=(t,e,s,i="_prev",r="_next")=>{let n=t._head,o=r;for(s&&(n=t._tail,o=i);n;){const t=n[o];e(n),n=t}},bt=(t,e,s="_prev",i="_next")=>{const r=e[s],n=e[i];r?r[i]=n:t._head=n,n?n[s]=r:t._tail=r,e[s]=null,e[i]=null},vt=(t,e,s,i="_prev",r="_next")=>{let n=t._tail;for(;n&&s&&s(n,e);)n=n[i];const o=n?n[r]:t._head;n?n[r]=e:t._head=e,o?o[i]=e:t._tail=e,e[i]=n,e[r]=o},Tt=t=>{let e=p;for(let s=0,i=g.length;s<i;s++){const i=g[s],r=t[i];if(void 0!==r){if("translateX"===i){const i=t.translateY;if(void 0!==i){const n=t.translateZ;void 0!==n?(e+=`translate3d(${r},${i},${n}) `,s+=2):(e+=`translate(${r},${i}) `,s+=1);continue}}if("scaleX"===i&&void 0===t.scale){const i=t.scaleY;if(void 0!==i){const n=t.scaleZ;void 0!==n?(e+=`scale3d(${r},${i},${n}) `,s+=2):(e+=`scale(${r},${i}) `,s+=1);continue}}e+=`${y[i]}${r}) `}"rotateZ"===i&&void 0!==t.rotate3d&&(e+=`rotate3d(${t.rotate3d}) `)}return void 0!==t.matrix&&(e+=`matrix(${t.matrix}) `),void 0!==t.matrix3d&&(e+=`matrix3d(${t.matrix3d}) `),e},xt=[];function St(t,e){if(!t)return null;const s=xt.length;t:for(let i=0;i<s;i++){const s=xt[i];if(s.detect&&!s.detect(t))continue;const r=s.targetAdapters;for(let s=0,i=r.length;s<i;s++){const i=r[s];if(i.detect(t)){const s=i.props[e];if(s&&(!s.gate||s.gate(t)))return s;break t}}}for(let i=0;i<s;i++){const s=xt[i];if(s.detect&&!s.detect(t))continue;const r=s.propertyResolvers;for(let s=0,i=r.length;s<i;s++){const i=r[s](t,e);if(i)return i}}return null}const wt=(t,e,s)=>(s<0&&(s+=1),s>1&&(s-=1),s<1/6?t+6*(e-t)*s:s<.5?e:s<2/3?t+(e-t)*(2/3-s)*6:t),$t=(t,e)=>H(t)?e:t,Ct=(t,e)=>{const s=t.match(N),i=e[o]?e:document.documentElement;let r=getComputedStyle(i)?.getPropertyValue(s[1]);return r&&r.trim()!==p||!s[2]||(r=s[2].trim()),r||0},Et=(t,e,s,i,r,n)=>{if(z(t)){if(!r){const r=t(e,s,i,n);return isNaN(+r)?r||0:+r}const o=()=>{const r=t(e,s,i,n);return isNaN(+r)?r||0:+r};return r.func=o,o()}if(O(t)&&B(t,"var(")){if(!r)return Ct(t,e);const s=()=>Ct(t,e);return r.func=s,s()}return t},kt=(t,e)=>t[o]?t[a]&&((t,e)=>{if(G.includes(e))return!1;if(t.getAttribute(e)||e in t){if("scale"===e){const e=t.parentNode;return e&&"filter"===e.tagName}return!0}})(t,e)?1:g.includes(e)||f.get(e)?3:B(e,"--")?4:e in t.style?2:e in t?0:1:0,Nt=(t,e,s)=>{const i=t.style[e];i&&s&&(s[e]=i);const r=i||getComputedStyle(t[Line truncated
</script><script>(() => {
const {animate, createTimeline, stagger} = anime;
const reduced = matchMedia('(prefers-reduced-motion: reduce)');
const printMode = location.search.includes('print-pdf');
const seen = new Set(); let active = []; let motion = !reduced.matches;
const motionButton = document.querySelector('#motion');
function settle() {
active.forEach(a => { a.complete(); }); active = [];
document.querySelectorAll('[data-motion]').forEach(e => {e.style.opacity='1';e.style.transform='none';});
}
function run() {
settle(); const slide=Reveal.getCurrentSlide();
if (!slide || seen.has(slide.id) || !motion || reduced.matches || printMode) return;
seen.add(slide.id); const targets=slide.querySelectorAll('[data-motion]');
if (!targets.length) return;
if(slide.id==='sequence') {
const tl=createTimeline({defaults:{duration:300,ease:'outCubic'}});
targets.forEach((e,i)=>tl.add(e,{opacity:[0,1],translateY:[12,0]},i*180));active.push(tl);
} else active.push(animate(targets,{opacity:[0,1],translateY:[12,0],delay:stagger(70),duration:400,ease:'outCubic'}));
}
function motionLabel(){motionButton.textContent=motion&&!reduced.matches?'Motion on':'Motion off';motionButton.setAttribute('aria-pressed',String(motion&&!reduced.matches));}
motionButton.onclick=()=>{motion=!motion;settle();motionLabel();};
reduced.addEventListener('change',()=>{if(reduced.matches)settle();motionLabel();});motionLabel();
addEventListener('beforeprint',settle);
const fs=document.querySelector('#fullscreen');
async function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await document.documentElement.requestFullscreen();}catch{fs.textContent='Fullscreen unavailable';}}
fs.onclick=fullscreen;
document.addEventListener('fullscreenchange',()=>{fs.textContent=document.fullscreenElement?'Exit fullscreen':'Enter fullscreen';fs.setAttribute('aria-pressed',String(!!document.fullscreenElement));});
document.addEventListener('keydown',e=>{if(e.key.toLowerCase()==='f'&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.target.matches('input,textarea,[contenteditable]')){e.preventDefault();e.stopPropagation();fullscreen();}},true);
document.querySelectorAll('[data-node]').forEach(node=>{
function select(){const was=node.getAttribute('aria-pressed')==='true';document.querySelectorAll('[data-node]').forEach(n=>n.setAttribute('aria-pressed','false'));node.setAttribute('aria-pressed',String(!was));document.querySelector('#detail').textContent=was?'Select a source to reveal its role. Tab + Enter or Space also works.':node.dataset.detail;}
node.addEventListener('click',select);
node.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();e.stopPropagation();select();}});
});
Reveal.initialize({width:1240,height:700,margin:.08,center:false,hash:true,transition:'none',backgroundTransition:'none',slideNumber:'c/t',view:'slide',keyboard:{70:null},autoAnimate:false}).then(()=>{
if(!location.hash){try{const i=Number(localStorage.getItem('presentation-html-demo-slide'));if(Number.isInteger(i)&&i>=0&&i<Reveal.getTotalSlides())Reveal.slide(i);}catch{}}
run(); window.deckReady=true;
});
Reveal.on('slidechanged',()=>{try{localStorage.setItem('presentation-html-demo-slide',String(Reveal.getIndices().h));}catch{}run();});
window.presentationQA={settle,reduced:()=>reduced.matches,active:()=>active.filter(a=>!a.completed).length};
})();
</script>
<!-- THIRD-PARTY NOTICES
Copyright (C) 2011-2026 Hakim El Hattab, http://hakim.se, and reveal.js contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The MIT License
Copyright (c) 2025 Julian Garnier
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans) WorkSans-Italic[wght].ttf: Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
—————————————————————————————-
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
—————————————————————————————-
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting — in part or in whole — any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-->
</body></html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+32
View File
@@ -0,0 +1,32 @@
{
"file": "/opt/data/work/presentation-html-skill/dist/presentation-html-demo-0.1.0.html",
"sha256": "c81f4f77250ee50973c97d12d22172d9e2cd6f97a1e699e960b491a0aa1581b0",
"runs": [
{
"reducedMotion": "no-preference",
"slides": 6,
"testedNodes": 6,
"errors": [],
"networkRequests": [],
"clipping": 0,
"keyboard": true,
"fullscreen": true,
"overview": true,
"reload": true,
"print": true
},
{
"reducedMotion": "reduce",
"slides": 6,
"testedNodes": 6,
"errors": [],
"networkRequests": [],
"clipping": 0,
"keyboard": true,
"fullscreen": true,
"overview": true,
"reload": true,
"print": true
}
]
}
+11
View File
@@ -0,0 +1,11 @@
# Visual self-review — 2026-10-21
Exact final HTML SHA-256: `c81f4f77250ee50973c97d12d22172d9e2cd6f97a1e699e960b491a0aa1581b0`.
Reviewed all six final 1440×900 screenshots individually using native vision, not just a contact sheet. Title, narrative, hub diagram, sequence, stagger and conclusion have readable text, clear hierarchy and no observed overlap or clipping. The hub screenshot deliberately retains a visible keyboard-focus ring on Risk after interaction tests.
Fix-and-reverify cycle: the first render exposed Reveal's `.reveal-viewport` white background / black text overriding a lower-specificity body rule, reducing accent and muted-text contrast. Fixed with `body.reveal-viewport`; added computed-color regression assertions and reran both motion modes. All six corrected screenshots were inspected again. A reload test also caught asynchronous hash publication; it now waits for the expected slide hash before reload.
Automated tests: normal/reduced motion, six slides and all six interactive nodes in each mode; actual Enter/Space/click detail behavior; font/palette/bounds; zero console/page errors and zero HTTP(S) attempts with browser networking disabled; arrows, overview, fullscreen button/F entry and exit, hash reload and print-control hiding.
Limitations: self-review, not independent review. Tested Chromium headless only; no claim of screen-reader, Safari/Firefox/mobile, projector or paginated PDF acceptance. Example scope is six nodes; the historical 46-node production deck was not re-certified by these tests.
+102
View File
@@ -0,0 +1,102 @@
{
"name": "presentation-html-skill",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "presentation-html-skill",
"version": "0.1.0",
"dependencies": {
"@fontsource/work-sans": "5.2.8",
"animejs": "4.5.0",
"reveal.js": "6.0.2"
},
"devDependencies": {
"@playwright/test": "1.64.1"
}
},
"node_modules/@fontsource/work-sans": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-5.2.8.tgz",
"integrity": "sha512-6LaHjVVgts+rnrcqvEkP2+iUB/jw1oDSYsGO0+TltAhnWki9Hnf/UGpgMQh2jcm0GEH8VqCPnq4PpmHLFzxXtQ==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@playwright/test": {
"version": "1.64.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.64.1.tgz",
"integrity": "sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.64.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/animejs": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/animejs/-/animejs-4.5.0.tgz",
"integrity": "sha512-NQimYX+lz8WaXonGS9zVVoviCIAjONeJayxacUditaivYLqyXgGjFKjuyl0aUUhFKm5MriX9ty1TGovNzoJQWA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/juliangarnier"
},
"peerDependencies": {
"@types/three": ">=0.150.0",
"three": ">=0.150.0"
},
"peerDependenciesMeta": {
"@types/three": {
"optional": true
},
"three": {
"optional": true
}
}
},
"node_modules/playwright": {
"version": "1.64.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz",
"integrity": "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.64.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/playwright-core": {
"version": "1.64.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz",
"integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/reveal.js": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-6.0.2.tgz",
"integrity": "sha512-JYIg5D9aoxoaLeb84O+OvAwsKWdIavVgEDScxtYEXFCT6t6TQrhNtSgogcjdCpdLsWglhJn3zyE3fUOwiH5KEg==",
"license": "MIT"
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "presentation-html-skill",
"version": "0.1.0",
"private": true,
"scripts": {"build":"python3 scripts/build.py", "test":"npm run build && node scripts/test.mjs"},
"devDependencies": {"@playwright/test":"1.64.1"},
"dependencies": {"reveal.js":"6.0.2","animejs":"4.5.0","@fontsource/work-sans":"5.2.8"}
}
+23
View File
@@ -0,0 +1,23 @@
# Established examples and lawful reuse catalog
Inspected 2026-10-21. “Established” identifies official showcase material or a widely known talk, not a measured popularity ranking. External pages are inspiration references, not runtime dependencies. **No branded artwork, logo paths, video, photos or talk slides are redistributed.** The bundled deck contains original compositions authored for this package.
| Source / author | Exact inspected source | Lesson | Reuse decision / bundled counterpart |
|---|---|---|---|
| Reveal.js official demo / Hakim El Hattab and contributors | [live demo](https://revealjs.com/demo/), [6.0.2 source](https://github.com/hakimel/reveal.js/blob/6.0.2/demo.html) | Progressive disclosure, strong typographic hierarchy, navigation | Repository code MIT; embedded media rights may differ. Original title, narrative and conclusion in starter slides 1, 2, 6; no copied talk content. |
| Reveal Auto-Animate / Hakim El Hattab | [official documentation and examples](https://revealjs.com/auto-animate/) | Preserve object continuity rather than add spectacle | Linked only; no copied website assets. Starter chooses no slide transition and content-local motion. |
| Anime.js advanced grid staggering / Julian Garnier | [v4.5.0 example](https://github.com/juliangarnier/anime/tree/v4.5.0/examples/advanced-grid-staggering) | Stagger exposes spatial or temporal order | MIT repository code; source inspected, not redistributed. Original six-mark stagger, slide 5. |
| Anime.js SVG line drawing / Julian Garnier | [v4.5.0 example](https://github.com/juliangarnier/anime/tree/v4.5.0/examples/svg-line-drawing) | Keep vector geometry editable; motion can explain connection | MIT code. Original hub/spoke SVG on slide 3; static edges intentionally avoid gratuitous redraw. |
| Anime.js v4 logo animation / Julian Garnier | [v4.5.0 example](https://github.com/juliangarnier/anime/tree/v4.5.0/examples/animejs-v4-logo-animation) | Timeline orchestration produces staged hierarchy | MIT code does not imply trademark endorsement. Logo not copied. Original three-step process timeline, slide 4. |
| Inventing on Principle / Bret Victor | [author link](https://worrydream.com/InventingOnPrinciple/) redirected to [Vimeo](https://vimeo.com/906418692) | Reference candidate for direct manipulation and explaining ideas through working demonstrations | No reuse license established; link only, no video or slide copying. Only redirect/page availability was verified here, not a new full-video review. Not claimed as a Reveal/Anime implementation. |
## Original reusable examples in assets
`assets/starter.html` and `assets/app.js` are MIT original code, not reproductions of showcase layouts:
1. Editorial title / decision framing (slides 1 and 6).
2. Narrative structure and bounded three-stage timeline (slides 2 and 4).
3. Accessible editable six-spoke decision diagram with real details (slide 3; generated labels in `scripts/build.py`).
4. One-shot six-mark stagger with reduced-motion final state (slide 5).
Use the entire source package or extract these patterns with their supporting CSS and lifecycle handlers. Do not extract the visible effect and omit keyboard, motion-reduction or print behavior. Vendored runtime licenses are in `assets/vendor`; original package code is covered by root `LICENSE`.
+23
View File
@@ -0,0 +1,23 @@
# Exact-artifact acceptance contract
Before test execution freeze the delivered file path and hash, expected slide IDs/titles, expected interactive-node inventory and browser scope. The included test is a six-slide/six-node example contract. When adapting it, change those expectations to the real deck; `DECK_PATH` alone does not make the demo-specific assertions a generic validator.
## Automated checks
- Build from source; load that exact `file://` artifact with context offline before navigation.
- Collect all HTTP(S) request attempts (failed attempts also fail), page errors and console errors.
- Wait for ready and local font loading. Verify computed palette as well as font availability.
- Visit every slide, wait for motion to settle, check text bounds and capture each slide separately.
- Enumerate all interactive nodes and test click, Enter and Space; verify state and real visible effect. Production decks with 46 nodes must test all 46, not a sample.
- Verify arrows, overview, visible fullscreen entry/exit and F entry/exit using actual browser fullscreen state.
- Wait for hash publication before reload. Test storage denial if the delivery environment restricts it.
- Repeat in reduced motion; settle on runtime preference change, print and early slide departure.
- Assert controls disappear in print. For PDF requests, export and inspect every PDF page separately; do not infer pagination quality from this check.
## Visual review
Inspect every exact-final screenshot at readable size. Check: clipped text, dense labels, low contrast, ghost transitions, connector endpoints, box-centered labels, visual hierarchy, visible focus, consistent spacing, and narrative continuity. Native text is not proof of legibility. Avoid thumbnail-only review. If requested, deliver each preview separately in order rather than a contact sheet.
## Evidence and limitations
`evidence/test-report.json` binds the exact generated hash to two actual Chromium runs. `evidence/slide-*.png` are final normal-motion screenshots. `evidence/visual-review.md` records self-review and fixes. Headless Chromium success is not Safari/Firefox/mobile, screen-reader or projector proof. An independent reviewer was not available in the delegated environment; do not label self-review independent.
+37
View File
@@ -0,0 +1,37 @@
# Research and API decisions
Research date: **2026-10-21 UTC**. HTTP audit timestamps, redirects, failures and response hashes are retained in `source-audit.json`; package versions are locked in `package-lock.json`. Latest npm metadata and official GitHub releases both reported Reveal.js 6.0.2 and Anime.js 4.5.0 at research time. These are dated observations, not evergreen “latest” promises.
## Official Reveal.js findings
- [Initialization](https://revealjs.com/initialization/): `Reveal.initialize()` resolves when ready. Official documentation explicitly identifies the UMD bundle as suitable for filesystem use; use it for a portable single file.
- [API](https://revealjs.com/api/): navigate using the framework API, not ad hoc visibility hacks. Query current slide and indices for deterministic tests.
- [Keyboard](https://revealjs.com/keyboard/): numeric key bindings can override or disable defaults; preserve accessibility and avoid double fullscreen handling.
- [Auto-Animate](https://revealjs.com/auto-animate/): object continuity across slides can be useful, but do not combine two animation systems on one property. Our starter deliberately disables it and uses bounded Anime motion inside slides.
- [PDF export](https://revealjs.com/pdf-export/): `?print-pdf` invokes dedicated pagination; ordinary print CSS alone is not equivalent to a validated PDF. Test the actual exported PDF if required.
- [Official demo](https://revealjs.com/demo/): nested navigation, fragments and progressive disclosure demonstrate capability; copying every feature creates cognitive load. Adopt only what supports the story.
- [Release 6.0.2](https://github.com/hakimel/reveal.js/releases/tag/6.0.2): exact runtime chosen for this package.
## Official Anime.js findings
The main animejs.com site and several documentation paths timed out or became unreachable from this environment; one guessed migration URL redirected to the homepage. Those were NOT treated as verified documentation. Research continued through the author's official GitHub release, package README, migration wiki and exact-tag example sources.
- [Release v4.5.0](https://github.com/juliangarnier/anime/releases/tag/v4.5.0), source commit `2c9cf8ea00329f6768c7d7902252ed977d75ce42`.
- [Official migration guide](https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4), read directly from the official wiki raw endpoint. A response hash is in `upstream-sources.json`; the wiki URL is mutable, not a claimed commit pin.
- [Official package README](https://github.com/juliangarnier/anime/tree/v4.5.0): named imports (`animate`, `stagger`); this deck uses equivalent properties from the UMD `anime` namespace.
- V3 `anime({targets, easing:'easeOutCubic'})` becomes v4 `animate(targets,{ease:'outCubic'})`.
- V3 `anime.timeline` becomes `createTimeline`; inherited child options go under `defaults`.
- V3 callbacks become `onBegin`, `onUpdate`, `onComplete`; `finished` becomes `.then()`.
- V4 `loop:1` means one repeat (two iterations), unlike v3. Avoid loops in explanatory deck motion.
- V3 path helpers become the `svg` module's `createMotionPath` and `createDrawable`; do not rename blindly without validating geometry.
- V4 `reverse()` plays backwards; it does not just toggle direction. Use lifecycle completion/cancellation deliberately.
## Production choices derived from research
Use Reveal only for slide navigation and sizing; use Anime only for bounded content motion. Content is visible by default and motion is enhancement. No external imagery is needed for the bundled original examples. Each interactive SVG group has a real detail action, accessible name, state and keyboard behavior. Native text remains editable and searchable.
The approved prior channel deck supplied preferences, not reusable proprietary content: editable diagrams, calm motion, Work Sans, visible fullscreen, offline packaging, exhaustive node testing. Neither its fifteen slides nor its 46-node acceptance scope is imposed on future decks. The historical artifact's embedded Anime bundle identifies itself as 3.2.1 despite prior context describing 3.2.2; the new package does not reuse that bundle.
## Failed candidates and limits
`revealjs.com/examples/` and `slides.com/news/introducing-auto-animate` returned 404; neither is cited as a successful source. Browser Use could not start its isolated daemon; local Playwright Chromium was used for real rendering and interaction QA instead. Anime site availability is an explicit research caveat, not hidden by invented page summaries.
+125
View File
@@ -0,0 +1,125 @@
[
{
"url": "https://revealjs.com/",
"final_url": "https://revealjs.com/",
"status": 200,
"bytes": 37980,
"sha256": "13ad6c3f8e5e142f9a1519348cbee2baaded12b19cce712bdf2c75875fa65a3b",
"retrieved": "2026-10-21T20:07:31.107105+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/initialization/",
"final_url": "https://revealjs.com/initialization/",
"status": 200,
"bytes": 32519,
"sha256": "265abafda18126cc6b2fc2fec50ccbbee70f9a459119a5674d438ba1e2ed094d",
"retrieved": "2026-10-21T20:07:31.096642+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/api/",
"final_url": "https://revealjs.com/api/",
"status": 200,
"bytes": 37890,
"sha256": "5e0dc568a85316bc6495a0890703a0afcf1897716089d8341fea83c88755df25",
"retrieved": "2026-10-21T20:07:31.152241+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/keyboard/",
"final_url": "https://revealjs.com/keyboard/",
"status": 200,
"bytes": 26999,
"sha256": "9107ab26a2796c27358b9b48b559d05931ab74c3f2aef34d3d8d707336655dd8",
"retrieved": "2026-10-21T20:07:31.131452+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/auto-animate/",
"final_url": "https://revealjs.com/auto-animate/",
"status": 200,
"bytes": 53371,
"sha256": "52fc9a48a1b41785669fc86f2551a800ad7eabadb841a37487bad3a9f48a614c",
"retrieved": "2026-10-21T20:07:31.128147+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/pdf-export/",
"final_url": "https://revealjs.com/pdf-export/",
"status": 200,
"bytes": 25670,
"sha256": "241557b9553877b9cf09629635385f09cca6ba9dad1aefc38cc8daf04789da59",
"retrieved": "2026-10-21T20:07:31.056258+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/demo/",
"final_url": "https://revealjs.com/demo/",
"status": 200,
"bytes": 16825,
"sha256": "1fcb4a0ad40896d2d55090fd0be8ddca8854db033b0f6b24931f259e63620dff",
"retrieved": "2026-10-21T20:07:30.965910+00:00",
"excerpt": ""
},
{
"url": "https://revealjs.com/examples/",
"error": "HTTP Error 404: Not Found"
},
{
"url": "https://animejs.com/",
"error": "The read operation timed out"
},
{
"url": "https://animejs.com/documentation/animation/",
"error": "The read operation timed out"
},
{
"url": "https://animejs.com/documentation/timeline/",
"error": "The read operation timed out"
},
{
"url": "https://animejs.com/documentation/scope/",
"error": "The read operation timed out"
},
{
"url": "https://animejs.com/documentation/getting-started/migration-guide/",
"final_url": "https://animejs.com/",
"status": 200,
"bytes": 45925,
"sha256": "33347970c72aed36cf6166115fa93a0242fc573f70fc211febd0f4d030d06c48",
"retrieved": "2026-10-21T20:08:46.572624+00:00",
"excerpt": ""
},
{
"url": "https://api.github.com/repos/hakimel/reveal.js/releases/latest",
"final_url": "https://api.github.com/repos/hakimel/reveal.js/releases/latest",
"status": 200,
"bytes": 2727,
"sha256": "3e3ca1de3def1937575fbd675be49a851aea8ab2b85d86a01becb57405f297c9",
"retrieved": "2026-10-21T20:07:31.375546+00:00",
"excerpt": "{\"url\":\"https://api.github.com/repos/hakimel/reveal.js/releases/386414926\",\"assets_url\":\"https://api.github.com/repos/hakimel/reveal.js/releases/386414926/assets\",\"upload_url\":\"https://uploads.github.com/repos/hakimel/reveal.js/releases/386414926/assets{?name,label}\",\"html_url\":\"https://github.com/hakimel/reveal.js/releases/tag/6.0.2\",\"id\":386414926,\"author\":{\"login\":\"hakimel\",\"id\":629429,\"node_id\":\"MDQ6VXNlcjYyOTQyOQ==\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/629429?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/hakimel\",\"html_url\":\"https://github.com/hakimel\",\"followers_url\":\"https://api.github.com/users/hakimel/followers\",\"following_url\":\"https://api.github.com/users/hakimel/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/hakimel/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/hakimel/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/hakimel/subscriptions\",\"organizations_url\":\"https://api.github.com/users/hakimel/orgs\",\"repos_url\":\"https://api.github.com/users/hakimel/repos\",\"events_url\":\"https://api.github.com/users/hakimel/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/hakimel/received_events\",\"type\":\"User\",\"user_view_type\":\"public\",\"site_admin\":false},\"node_id\":\"RE_kwDOABxnUs4XCDlO\",\"tag_name\":\"6.0.2\",\"target_commitish\":\"master\",\"name\":\"6.0.2\",\"draft\":false,\"immutable\":false,\"prerelease\":false,\"created_at\":\"2026-09-10T15:08:21Z\",\"updated_at\":\"2026-09-10T15:41:21Z\",\"pub"
},
{
"url": "https://api.github.com/repos/juliangarnier/anime/releases/latest",
"final_url": "https://api.github.com/repos/juliangarnier/anime/releases/latest",
"status": 200,
"bytes": 5227,
"sha256": "d98aaa46da832ce755bf0d519c497ff34133e818e7c2febddeb85c898f461b94",
"retrieved": "2026-10-21T20:07:31.382151+00:00",
"excerpt": "{\"url\":\"https://api.github.com/repos/juliangarnier/anime/releases/342978131\",\"assets_url\":\"https://api.github.com/repos/juliangarnier/anime/releases/342978131/assets\",\"upload_url\":\"https://uploads.github.com/repos/juliangarnier/anime/releases/342978131/assets{?name,label}\",\"html_url\":\"https://github.com/juliangarnier/anime/releases/tag/v4.5.0\",\"id\":342978131,\"author\":{\"login\":\"juliangarnier\",\"id\":1268691,\"node_id\":\"MDQ6VXNlcjEyNjg2OTE=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/1268691?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/juliangarnier\",\"html_url\":\"https://github.com/juliangarnier\",\"followers_url\":\"https://api.github.com/users/juliangarnier/followers\",\"following_url\":\"https://api.github.com/users/juliangarnier/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/juliangarnier/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/juliangarnier/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/juliangarnier/subscriptions\",\"organizations_url\":\"https://api.github.com/users/juliangarnier/orgs\",\"repos_url\":\"https://api.github.com/users/juliangarnier/repos\",\"events_url\":\"https://api.github.com/users/juliangarnier/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/juliangarnier/received_events\",\"type\":\"User\",\"user_view_type\":\"public\",\"site_admin\":false},\"node_id\":\"RE_kwDOAzUPws4UcW5T\",\"tag_name\":\"v4.5.0\",\"target_commitish\":\"master\",\"name\":\"v4.5.0\",\"draft\":false,\"immutable\":false,\"prereleas"
},
{
"url": "https://slides.com/news/introducing-auto-animate",
"error": "HTTP Error 404: Not Found"
},
{
"url": "https://worrydream.com/InventingOnPrinciple/",
"final_url": "https://vimeo.com/906418692",
"status": 200,
"bytes": 12348,
"sha256": "a293ee7be369e923ff94eaef5561e3bfb576b115c050e7f75fe8554be98bbbae",
"retrieved": "2026-10-21T20:07:32.275437+00:00",
"excerpt": ""
}
]
+53
View File
@@ -0,0 +1,53 @@
[
{
"name": "anime-migration",
"url": "https://raw.githubusercontent.com/wiki/juliangarnier/anime/Migrating-from-v3-to-v4.md",
"bytes": 8213,
"sha256": "9ddc9256b288552fb8df27b11cf78f823daa361dcd0805ec40adaf5311300df4"
},
{
"name": "anime-svg",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/v4.5.0/examples/svg-line-drawing/index.html",
"bytes": 677,
"sha256": "55a40b49c677c04204b7ce7528bf3a37e877d4062f7f9fcd50ad6e4167b8d62b"
},
{
"name": "anime-stagger",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/v4.5.0/examples/advanced-grid-staggering/index.html",
"bytes": 1243,
"sha256": "2fb23417e232712aa5ef85e466bb81a4f73ef35e0c671bba0b659ab28f755d2a"
},
{
"name": "anime-logo",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/v4.5.0/examples/animejs-v4-logo-animation/index.html",
"bytes": 37316,
"sha256": "ed989abfde04ecad6df3a0b2c3e74103ee750b1d82f23278b5aa35b1211a80ce"
},
{
"name": "reveal-demo",
"url": "https://raw.githubusercontent.com/hakimel/reveal.js/6.0.2/demo.html",
"bytes": 22161,
"sha256": "50cecf01b7ea14cf25af6849a67b3be0e69f91b693093059adb06305344b4631"
},
{
"name": "svg-line-drawing-js",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/2c9cf8ea00329f6768c7d7902252ed977d75ce42/examples/svg-line-drawing/index.js",
"bytes": 2353,
"sha256": "3bccbd209bff397b5249444e9a313d9f9e482e565bf767c8eb6d1b5d4302e135",
"retrieved": "2026-10-21T20:27:02.800745+00:00"
},
{
"name": "advanced-grid-staggering-js",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/2c9cf8ea00329f6768c7d7902252ed977d75ce42/examples/advanced-grid-staggering/index.js",
"bytes": 2032,
"sha256": "7fcf5db00732c9dd2946beb46ccb7f0fbf6f10d62504a42e1e033924cad74e5c",
"retrieved": "2026-10-21T20:27:03.439072+00:00"
},
{
"name": "animejs-v4-logo-animation-js",
"url": "https://raw.githubusercontent.com/juliangarnier/anime/2c9cf8ea00329f6768c7d7902252ed977d75ce42/examples/animejs-v4-logo-animation/index.js",
"bytes": 7471,
"sha256": "9e1f69157103ed354222def57ad3dc5a4d6cee606080e0d0064b317a26096191",
"retrieved": "2026-10-21T20:27:03.743445+00:00"
}
]
+30
View File
@@ -0,0 +1,30 @@
"""Build an offline, single-file deck with no package install or network."""
from pathlib import Path
import base64, hashlib, json, re
R=Path(__file__).resolve().parents[1]
def build():
vendor=R/'assets/vendor'; s=(R/'assets/starter.html').read_text()
manifest=R/'vendor-manifest.json'
if manifest.exists():
for entry in json.loads(manifest.read_text()):
assert hashlib.sha256((R/entry['path']).read_bytes()).hexdigest()==entry['sha256'], entry['path']
nodes=[]
labels=['People','Process','Platform','Quality','Cost','Risk']
details=['People: who owns the decision?','Process: how does work move?','Platform: what enables delivery?','Quality: what evidence proves fitness?','Cost: what constraint shapes the choice?','Risk: what could invalidate the plan?']
for i,(label,detail) in enumerate(zip(labels,details)):
x=[250,550,850][i%3];y=65 if i<3 else 305
nodes.append(f'<g role="button" tabindex="0" aria-label="{label}" aria-pressed="false" data-node="{i}" data-detail="{detail}"><rect x="{x-110}" y="{y-32}" width="220" height="64"/><text x="{x}" y="{y}">{label}</text></g>')
s=s.replace('<!-- NODES -->','\n'.join(nodes))
for marker,file in [('REVEAL_CSS','reveal.css'),('REVEAL_JS','reveal.js'),('ANIME_JS','anime.umd.min.js')]:
text=(vendor/file).read_text();text=re.sub(r'//# sourceMappingURL=.*','',text)
s=s.replace('/* '+marker+' */',text)
fonts=''
for file,weight in [('work-sans.woff2',400),('work-sans-bold.woff2',700)]:
fonts+=f"@font-face{{font-family:'Work Sans';font-weight:{weight};font-style:normal;font-display:block;src:url(data:font/woff2;base64,{base64.b64encode((vendor/file).read_bytes()).decode()}) format('woff2');}}"
s=s.replace('/* FONT_CSS */',fonts).replace('/* APP_JS */',(R/'assets/app.js').read_text())
licenses='\n'.join((vendor/f).read_text() for f in ['reveal-LICENSE','anime-LICENSE.md','work-sans-LICENSE'])
s=s.replace('<!-- LICENSES -->','<!-- THIRD-PARTY NOTICES\n'+licenses.replace('--','—')+'\n-->')
out=R/'dist';out.mkdir(exist_ok=True);p=out/'presentation-html-demo-0.1.0.html';p.write_text(s)
report={'file':p.name,'sha256':hashlib.sha256(p.read_bytes()).hexdigest(),'bytes':p.stat().st_size,'slides':6,'interactive_nodes':6,'reveal':'6.0.2','anime':'4.5.0'}
(out/'manifest.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps(report))
if __name__=='__main__':build()
+37
View File
@@ -0,0 +1,37 @@
import { chromium } from '@playwright/test';
import fs from 'node:fs';import path from 'node:path';import {pathToFileURL} from 'node:url';import assert from 'node:assert/strict';import crypto from 'node:crypto';
const root=path.resolve(import.meta.dirname,'..');const file=process.env.DECK_PATH||path.join(root,'dist/presentation-html-demo-0.1.0.html');
const executablePath=process.env.CHROMIUM_EXECUTABLE;
const browser=await chromium.launch({headless:true,...(executablePath?{executablePath}:{})});
const report={file,sha256:crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'),runs:[]};
fs.mkdirSync(path.join(root,'evidence'),{recursive:true});
try {
for(const reducedMotion of ['no-preference','reduce']){
const context=await browser.newContext({viewport:{width:1440,height:900},reducedMotion,offline:true});const page=await context.newPage();const errors=[],requests=[];
page.on('pageerror',e=>errors.push(String(e)));page.on('console',m=>{if(m.type()==='error')errors.push(m.text());});page.on('request',r=>{if(/^https?:/.test(r.url()))requests.push(r.url());});
await page.goto(pathToFileURL(file).href);await page.waitForFunction(()=>window.deckReady);await page.evaluate(()=>document.fonts.ready);
assert.equal(await page.evaluate(()=>document.fonts.check('28px "Work Sans"')),true);
assert.equal(await page.evaluate(()=>getComputedStyle(document.body).backgroundColor),'rgb(16, 30, 37)');
assert.equal(await page.evaluate(()=>getComputedStyle(document.querySelector('.reveal')).color),'rgb(244, 243, 237)');
const slides=await page.locator('.slides>section').count();assert.equal(slides,6);
let testedNodes=0;
for(let i=0;i<slides;i++){
await page.evaluate(i=>Reveal.slide(i),i);await page.waitForTimeout(900);
const clipped=await page.evaluate(()=>{const s=Reveal.getCurrentSlide(),r=s.getBoundingClientRect();return [...s.querySelectorAll('h1,h2,p,strong,svg text,button')].filter(e=>{const b=e.getBoundingClientRect();return b.left<r.left-2||b.right>r.right+2||b.top<r.top-2||b.bottom>r.bottom+2;}).map(e=>e.textContent);});assert.deepEqual(clipped,[],'Clipping slide '+i);
const nodes=page.locator('.present [data-node]');
for(let j=0;j<await nodes.count();j++){
const n=nodes.nth(j);await n.focus();await page.keyboard.press('Enter');assert.equal(await n.getAttribute('aria-pressed'),'true');assert.equal(await page.locator('#detail').textContent(),await n.getAttribute('data-detail'));await page.keyboard.press('Space');assert.equal(await n.getAttribute('aria-pressed'),'false');await n.click();assert.equal(await n.getAttribute('aria-pressed'),'true');await n.click();testedNodes++;
}
if(reducedMotion==='no-preference')await page.screenshot({path:path.join(root,`evidence/slide-${String(i+1).padStart(2,'0')}.png`)});
}
assert.equal(testedNodes,6);await page.evaluate(()=>Reveal.slide(0));await page.keyboard.press('ArrowRight');assert.equal(await page.evaluate(()=>Reveal.getIndices().h),1);await page.keyboard.press('ArrowLeft');assert.equal(await page.evaluate(()=>Reveal.getIndices().h),0);
await page.keyboard.press('Escape');assert.equal(await page.evaluate(()=>Reveal.isOverview()),true);await page.keyboard.press('Escape');
await page.locator('#fullscreen').click();await page.waitForFunction(()=>!!document.fullscreenElement);assert.equal(await page.locator('#fullscreen').getAttribute('aria-pressed'),'true');await page.keyboard.press('f');await page.waitForFunction(()=>!document.fullscreenElement);
await page.keyboard.press('f');await page.waitForFunction(()=>!!document.fullscreenElement);await page.locator('#fullscreen').click();await page.waitForFunction(()=>!document.fullscreenElement);
await page.evaluate(()=>Reveal.slide(3));await page.waitForFunction(()=>location.hash.includes('sequence'));await page.reload();await page.waitForFunction(()=>window.deckReady);assert.equal(await page.evaluate(()=>Reveal.getIndices().h),3);
if(reducedMotion==='reduce'){assert.equal(await page.evaluate(()=>presentationQA.active()),0);assert.equal(await page.locator('#motion').getAttribute('aria-pressed'),'false');}
await page.emulateMedia({media:'print'});await page.evaluate(()=>dispatchEvent(new Event('beforeprint')));assert.equal(await page.evaluate(()=>getComputedStyle(document.querySelector('.toolbar')).display),'none');
assert.deepEqual(errors,[]);assert.deepEqual(requests,[]);report.runs.push({reducedMotion,slides,testedNodes,errors,networkRequests:requests,clipping:0,keyboard:true,fullscreen:true,overview:true,reload:true,print:true});await context.close();
}
fs.writeFileSync(path.join(root,'evidence/test-report.json'),JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(report,null,2));
} finally {await browser.close();}
+74
View File
@@ -0,0 +1,74 @@
[
{
"path": "assets/vendor/anime-LICENSE.md",
"package": "animejs",
"version": "4.5.0",
"license": "MIT",
"sha256": "3f3e835a9952cfc2a6ca836fb95b3257c981f24fa3ef51249055acb09931897b",
"bytes": 1075,
"source": "https://registry.npmjs.org/animejs/4.5.0"
},
{
"path": "assets/vendor/anime.umd.min.js",
"package": "animejs",
"version": "4.5.0",
"license": "MIT",
"sha256": "8d5b3a58a1f64023a04a4cedeef135a2263b18da04b35177ac13438a0bad033b",
"bytes": 118043,
"source": "https://registry.npmjs.org/animejs/4.5.0"
},
{
"path": "assets/vendor/reveal-LICENSE",
"package": "reveal.js",
"version": "6.0.2",
"license": "MIT",
"sha256": "b2883e4b610bfa1b4d8fff84c4d4b825cc7d553cbd9fec4777c04068a2859dc0",
"bytes": 1108,
"source": "https://registry.npmjs.org/reveal.js/6.0.2"
},
{
"path": "assets/vendor/reveal.css",
"package": "reveal.js",
"version": "6.0.2",
"license": "MIT",
"sha256": "615ee850cbbb98a0f688b60ed37b21819a3f54c9f3e6f20e33e63693ab74a0c0",
"bytes": 53963,
"source": "https://registry.npmjs.org/reveal.js/6.0.2"
},
{
"path": "assets/vendor/reveal.js",
"package": "reveal.js",
"version": "6.0.2",
"license": "MIT",
"sha256": "aa1bbbf2617b23a623b23612cb3c5bdb63de512e652bf20fcfa832b045d37844",
"bytes": 118912,
"source": "https://registry.npmjs.org/reveal.js/6.0.2"
},
{
"path": "assets/vendor/work-sans-LICENSE",
"package": "@fontsource/work-sans",
"version": "5.2.8",
"license": "OFL-1.1",
"sha256": "e7c426573d51f3d2e5e92254878c862344ffa0b9272f79a880f9f99010f002c7",
"bytes": 4515,
"source": "https://registry.npmjs.org/@fontsource/work-sans/5.2.8"
},
{
"path": "assets/vendor/work-sans-bold.woff2",
"package": "@fontsource/work-sans",
"version": "5.2.8",
"license": "OFL-1.1",
"sha256": "c1e51267967030c3d8893931aa3b2e087b9caf44332cf7c384f4f4c22da0dadc",
"bytes": 20840,
"source": "https://registry.npmjs.org/@fontsource/work-sans/5.2.8"
},
{
"path": "assets/vendor/work-sans.woff2",
"package": "@fontsource/work-sans",
"version": "5.2.8",
"license": "OFL-1.1",
"sha256": "418b06936717d2e9b6c7a1fb61acb7e2f40030be4b230452f96505230e7b6b41",
"bytes": 19900,
"source": "https://registry.npmjs.org/@fontsource/work-sans/5.2.8"
}
]