octocode-cli 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +129 -28
  2. package/out/chunks/chunk-7476PETK.js +309 -0
  3. package/out/chunks/chunk-CVNNNSMQ.js +26 -0
  4. package/out/chunks/chunk-OQBJTZWK.js +60 -0
  5. package/out/chunks/chunk-UCZCF3BQ.js +9 -0
  6. package/out/chunks/command-help-specs-JZXVSLZ5.js +8 -0
  7. package/out/chunks/commands-XBFPLHSQ.js +8 -0
  8. package/out/chunks/help-P7TCOYAJ.js +10 -0
  9. package/out/chunks/main-help-ULF5PAQY.js +10 -0
  10. package/out/chunks/prompts-5E6VKRX5.js +8 -0
  11. package/out/chunks/spinner-URV2OX6O.js +8 -0
  12. package/out/chunks/tool-command-M6VA7P2F.js +8 -0
  13. package/out/octocode-cli.js +1 -1
  14. package/package.json +5 -3
  15. package/skills/README.md +60 -58
  16. package/skills/agentic-flow-best-practices/SKILL.md +280 -0
  17. package/skills/agentic-flow-best-practices/references/agent-collaboration-patterns.md +75 -0
  18. package/skills/agentic-flow-best-practices/references/pr-review-agent-example.md +47 -0
  19. package/skills/agentic-flow-best-practices/references/resources.md +112 -0
  20. package/skills/octocode-brainstorming/.env.example +11 -0
  21. package/skills/octocode-brainstorming/SKILL.md +262 -0
  22. package/skills/octocode-brainstorming/scripts/tavily-search.mjs +138 -0
  23. package/skills/octocode-chrome-devtools/README.md +541 -0
  24. package/skills/octocode-chrome-devtools/SKILL.md +197 -0
  25. package/skills/octocode-chrome-devtools/agents/openai.yaml +7 -0
  26. package/skills/octocode-chrome-devtools/references/CDP_AGENT_REFERENCE.md +401 -0
  27. package/skills/octocode-chrome-devtools/references/CHROME_FLAGS.md +234 -0
  28. package/skills/octocode-chrome-devtools/references/INTENTS.md +108 -0
  29. package/skills/octocode-chrome-devtools/references/INTENTS_AUTH.md +179 -0
  30. package/skills/octocode-chrome-devtools/references/INTENTS_AUTOMATION.md +214 -0
  31. package/skills/octocode-chrome-devtools/references/INTENTS_DEBUG.md +329 -0
  32. package/skills/octocode-chrome-devtools/references/INTENTS_ENVIRONMENT.md +237 -0
  33. package/skills/octocode-chrome-devtools/references/INTENTS_INSPECT.md +263 -0
  34. package/skills/octocode-chrome-devtools/references/INTENTS_STORAGE_CONSENT.md +214 -0
  35. package/skills/octocode-chrome-devtools/references/RECOVERY.md +39 -0
  36. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS.md +43 -0
  37. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_ASYNC_WORKERS.md +345 -0
  38. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_BROWSER.md +403 -0
  39. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_OBSERVE.md +275 -0
  40. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_SPECIAL.md +18 -0
  41. package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +503 -0
  42. package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +123 -0
  43. package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +81 -0
  44. package/skills/octocode-chrome-devtools/scripts/octocode-chrome-devtools.vpn.example.json +8 -0
  45. package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +362 -0
  46. package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +193 -0
  47. package/skills/octocode-chrome-devtools/scripts/undercover.mjs +226 -0
  48. package/skills/octocode-design/README.md +2 -2
  49. package/skills/octocode-documentation-writer/README.md +1 -1
  50. package/skills/octocode-engineer/README.md +1 -1
  51. package/skills/octocode-engineer/SKILL.md +137 -306
  52. package/skills/octocode-engineer/references/cli-reference.md +13 -0
  53. package/skills/octocode-engineer/references/output-files.md +3 -3
  54. package/skills/octocode-engineer/scripts/run.js +146 -146
  55. package/skills/octocode-engineer/src/pipeline/main.ts +1 -17
  56. package/skills/octocode-engineer/src/pipeline/progress.ts +4 -0
  57. package/skills/octocode-engineer/src/reporting/summary-md.test.ts +48 -0
  58. package/skills/octocode-engineer/src/reporting/summary-md.ts +43 -2
  59. package/skills/octocode-install/SKILL.md +1 -1
  60. package/skills/octocode-pull-request-reviewer/README.md +5 -5
  61. package/skills/octocode-pull-request-reviewer/SKILL.md +1 -1
  62. package/skills/octocode-research/AGENTS.md +1 -1
  63. package/skills/octocode-research/README.md +2 -2
  64. package/skills/octocode-research/docs/ARCHITECTURE.md +1 -1
  65. package/skills/octocode-research/scripts/server.js +184 -239
  66. package/skills/octocode-research/src/routes/github.ts +4 -21
  67. package/skills/octocode-research/src/routes/local.ts +4 -21
  68. package/skills/octocode-research/src/utils/fileContentTransform.ts +44 -0
  69. package/skills/octocode-search-skill/SKILL.md +337 -0
  70. package/skills/octocode-search-skill/references/agent-skills-guide.md +177 -0
  71. package/skills/octocode-search-skill/references/discovery-surfaces.md +162 -0
  72. package/skills/octocode-search-skill/references/fetch-and-create-locally.md +57 -0
  73. package/skills/octocode-search-skill/references/install-reference.md +130 -0
  74. package/skills/octocode-search-skill/references/references-template.md +27 -0
  75. package/skills/octocode-search-skill/references/references.md +62 -0
  76. package/skills/octocode-slides/README.md +307 -0
  77. package/skills/octocode-slides/SKILL.md +410 -0
  78. package/skills/octocode-slides/references/01-brief.md +156 -0
  79. package/skills/octocode-slides/references/02-research.md +149 -0
  80. package/skills/octocode-slides/references/03-outline.md +172 -0
  81. package/skills/octocode-slides/references/04-design.md +301 -0
  82. package/skills/octocode-slides/references/05-implementation.md +213 -0
  83. package/skills/octocode-slides/references/06-review.md +258 -0
  84. package/skills/octocode-slides/references/animation.md +281 -0
  85. package/skills/octocode-slides/references/design-system.md +316 -0
  86. package/skills/octocode-slides/references/html-templates.md +673 -0
  87. package/skills/octocode-slides/references/image-generation.md +448 -0
  88. package/skills/octocode-slides/references/resources.md +840 -0
  89. package/skills/octocode-slides/references/slide-rules.md +541 -0
  90. package/skills/octocode-slides/references/wireframes.md +727 -0
  91. package/skills/octocode-slides/scripts/animation.js +182 -0
  92. package/skills/octocode-slides/scripts/base.css +353 -0
  93. package/skills/octocode-slides/scripts/base.html +655 -0
  94. package/skills/octocode-slides/scripts/generate_image.py +221 -0
  95. package/skills/octocode-slides/scripts/navbridge.js +79 -0
  96. package/skills/octocode-slides/scripts/presenter.js +316 -0
  97. package/skills/octocode-slides/scripts/slide.html +248 -0
  98. package/skills/octocode-stats/SKILL.md +73 -0
  99. package/skills/octocode-stats/assets/template.html +1332 -0
  100. package/skills/octocode-stats/scripts/build_dashboard.mjs +407 -0
  101. package/out/chunks/chunk-LH4AZJPA.js +0 -389
  102. package/out/chunks/command-help-specs-CQ3RBLP6.js +0 -8
  103. package/out/chunks/commands-M3QTWKWE.js +0 -51
  104. package/out/chunks/help-XPXP46ZT.js +0 -10
  105. package/out/chunks/main-help-HXFAFHPG.js +0 -10
  106. package/out/chunks/tool-command-VHFLPIHY.js +0 -8
@@ -0,0 +1,182 @@
1
+ /*
2
+ * animation.js — In-slide step animation engine for Octocode Slides
3
+ *
4
+ * Elements marked with [data-step] are "steps" — components revealed
5
+ * sequentially with → / Space / ↓ and hidden in reverse with ← / ↑.
6
+ *
7
+ * HOW IT WORKS
8
+ * ─────────────
9
+ * Steps are revealed before the slide advances to the next slide, and
10
+ * all steps must be hidden before the slide retreats to the previous one.
11
+ *
12
+ * This is achieved by intercepting keydown events in capture phase, before
13
+ * navbridge.js forwards them to the parent (index.html). When a step is
14
+ * consumed, stopImmediatePropagation() prevents navbridge from seeing the
15
+ * key — so the parent does not advance the slide. Once all steps are
16
+ * exhausted in either direction, the key passes through normally and the
17
+ * parent navigates to the next or previous slide.
18
+ *
19
+ * REQUIRED LOADING ORDER (in slide HTML, before </body>):
20
+ * <script src="../js/animation.js"></script> ← MUST come first
21
+ * <script src="../js/navbridge.js"></script> ← always last
22
+ *
23
+ * USAGE
24
+ * ─────
25
+ * Add data-step="N" to any element (N = 1-based display order):
26
+ *
27
+ * <li data-step="1">First point</li>
28
+ * <li data-step="2">Second point</li>
29
+ * <li data-step="3">Third point</li>
30
+ *
31
+ * If the value is absent or non-numeric, DOM order is used as the fallback.
32
+ * Elements without [data-step] are unaffected and always visible.
33
+ *
34
+ * CUSTOMISING ANIMATIONS
35
+ * ──────────────────────
36
+ * Override the default fade-up in the slide's local <style>:
37
+ *
38
+ * [data-step] { opacity: 0; transform: scale(0.95); transition: ... }
39
+ * [data-step].step-visible { opacity: 1; transform: scale(1); }
40
+ *
41
+ * Add staggered delays using CSS nth-of-type or by setting a custom
42
+ * transition-delay on each step element.
43
+ *
44
+ * OPTIONAL — step indicator dot bar
45
+ * ───────────────────────────────────
46
+ * Add data-step-indicator to the slide root (<div class="slide ...">)
47
+ * to enable a small dot bar that tracks the current step count:
48
+ *
49
+ * <div class="slide slide--content" data-step-indicator>
50
+ *
51
+ * See animation.md for full documentation.
52
+ */
53
+ (function () {
54
+ /* Skip when not embedded in an iframe (e.g. opened standalone for debug). */
55
+ if (window.parent === window) return;
56
+
57
+ /* ── Inject default step CSS ──────────────────────────────────────────── */
58
+ var style = document.createElement('style');
59
+ style.textContent = [
60
+ /* Hidden state — all [data-step] elements start invisible */
61
+ '[data-step]{',
62
+ ' opacity:0;',
63
+ ' transform:translateY(14px);',
64
+ ' transition:opacity 320ms cubic-bezier(.4,0,.2,1),',
65
+ ' transform 320ms cubic-bezier(.4,0,.2,1);',
66
+ ' will-change:opacity,transform;',
67
+ '}',
68
+ /* Visible state — added by showNext() */
69
+ '[data-step].step-visible{',
70
+ ' opacity:1;',
71
+ ' transform:translateY(0);',
72
+ '}',
73
+ /* Step indicator dot bar */
74
+ '.step-indicator{',
75
+ ' display:flex;gap:6px;align-items:center;',
76
+ ' position:absolute;bottom:18px;left:50%;transform:translateX(-50%);',
77
+ ' z-index:20;pointer-events:none;',
78
+ '}',
79
+ '.step-dot{',
80
+ ' width:6px;height:6px;border-radius:50%;',
81
+ ' background:rgba(255,255,255,0.25);',
82
+ ' transition:background 200ms ease,transform 200ms ease;',
83
+ '}',
84
+ '.step-dot.is-done{background:rgba(255,255,255,0.85);transform:scale(1.2);}'
85
+ ].join('');
86
+ document.head.appendChild(style);
87
+
88
+ /* ── State ──────────────────────────────────────────────────────────── */
89
+ var steps = []; /* ordered [data-step] elements */
90
+ var currentStep = 0; /* how many steps are currently visible */
91
+ var indicator = null; /* dot bar element, if enabled */
92
+
93
+ /* ── Collect and sort steps ─────────────────────────────────────────── */
94
+ function collectSteps() {
95
+ var els = document.querySelectorAll('[data-step]');
96
+
97
+ steps = Array.prototype.slice.call(els).sort(function (a, b) {
98
+ var na = parseInt(a.getAttribute('data-step'), 10);
99
+ var nb = parseInt(b.getAttribute('data-step'), 10);
100
+ var aNaN = isNaN(na);
101
+ var bNaN = isNaN(nb);
102
+ /* Both non-numeric: keep DOM order (querySelectorAll is document order) */
103
+ if (aNaN && bNaN) return 0;
104
+ if (aNaN) return 1;
105
+ if (bNaN) return -1;
106
+ return na - nb;
107
+ });
108
+
109
+ if (!steps.length) return;
110
+
111
+ /* Build optional dot indicator */
112
+ var slideRoot = document.querySelector('[data-step-indicator]');
113
+ if (slideRoot) {
114
+ indicator = document.createElement('div');
115
+ indicator.className = 'step-indicator';
116
+ steps.forEach(function () {
117
+ var dot = document.createElement('span');
118
+ dot.className = 'step-dot';
119
+ indicator.appendChild(dot);
120
+ });
121
+ slideRoot.appendChild(indicator);
122
+ }
123
+ }
124
+
125
+ /* ── Show next step ─────────────────────────────────────────────────── */
126
+ function showNext() {
127
+ if (currentStep >= steps.length) return false; /* all shown — pass through */
128
+ steps[currentStep].classList.add('step-visible');
129
+ if (indicator) {
130
+ indicator.children[currentStep].classList.add('is-done');
131
+ }
132
+ currentStep++;
133
+ return true; /* consumed */
134
+ }
135
+
136
+ /* ── Hide last visible step ─────────────────────────────────────────── */
137
+ function hideLast() {
138
+ if (currentStep <= 0) return false; /* none shown — pass through */
139
+ currentStep--;
140
+ steps[currentStep].classList.remove('step-visible');
141
+ if (indicator) {
142
+ indicator.children[currentStep].classList.remove('is-done');
143
+ }
144
+ return true; /* consumed */
145
+ }
146
+
147
+ /* ── Key sets ───────────────────────────────────────────────────────── */
148
+ var FORWARD = { ArrowRight: 1, ArrowDown: 1, ' ': 1 };
149
+ var BACKWARD = { ArrowLeft: 1, ArrowUp: 1 };
150
+
151
+ /* ── Keydown interceptor (capture phase, before navbridge) ──────────── */
152
+ /*
153
+ * Both animation.js and navbridge.js attach to document in capture phase.
154
+ * Because animation.js is loaded (and therefore registered) first, its
155
+ * handler runs before navbridge's. When a step is consumed, calling
156
+ * stopImmediatePropagation() prevents navbridge from seeing the event,
157
+ * so the parent (index.html) does not advance or retreat the slide.
158
+ *
159
+ * When no step is available (all shown going forward, none going back),
160
+ * the key is NOT consumed — it falls through to navbridge, which forwards
161
+ * it to the parent, and normal slide navigation occurs.
162
+ */
163
+ document.addEventListener('keydown', function (e) {
164
+ if (!steps.length) return; /* this slide has no steps — no-op */
165
+
166
+ var consumed = false;
167
+ if (FORWARD[e.key]) consumed = showNext();
168
+ if (BACKWARD[e.key]) consumed = hideLast();
169
+
170
+ if (consumed) {
171
+ e.stopImmediatePropagation();
172
+ e.preventDefault();
173
+ }
174
+ }, true /* capture phase */);
175
+
176
+ /* ── Init ────────────────────────────────────────────────────────────── */
177
+ if (document.readyState === 'loading') {
178
+ document.addEventListener('DOMContentLoaded', collectSteps);
179
+ } else {
180
+ collectSteps();
181
+ }
182
+ })();
@@ -0,0 +1,353 @@
1
+ /* base.css — layout, variables, all slide rules */
2
+
3
+ :root {
4
+ /* --- Colors (defaults; theme.css overrides) --- */
5
+ --bg: #0D1117;
6
+ --surface: #161B22;
7
+ --border: #30363D;
8
+ --accent: #39D353;
9
+ --text: #E6EDF3;
10
+ --muted: #7D8590;
11
+ --code-bg: #0D1117;
12
+ --code-text: #79C0FF;
13
+
14
+ /* --- Fonts (theme.css sets these) --- */
15
+ --font-head: system-ui, sans-serif;
16
+ --font-body: system-ui, sans-serif;
17
+ --font-mono: 'Courier New', monospace;
18
+
19
+ /* --- Type scale (responsive, no px overrides) --- */
20
+ --t-display: clamp(2.75rem, 5.5vw, 4.25rem); /* 44–68px — title/section */
21
+ --t-title: clamp(1.6rem, 3.5vw, 2.5rem); /* 26–40px — slide headings */
22
+ --t-sub: clamp(1.15rem, 2.5vw, 1.65rem); /* 18–26px — descriptions, sub-headings */
23
+ --t-body: clamp(1rem, 1.8vw, 1.2rem); /* 16–19px — bullets, paragraphs */
24
+ --t-small: clamp(0.75rem, 1.2vw, 0.9rem); /* 12–14px — captions, footers */
25
+ --t-code: clamp(0.82rem, 1.4vw, 1rem); /* 13–16px — code blocks */
26
+
27
+ /* --- Spacing (8px grid) --- */
28
+ --sp-1: 0.5rem; --sp-2: 1rem; --sp-3: 1.5rem;
29
+ --sp-4: 2rem; --sp-6: 3rem; --sp-8: 4rem;
30
+
31
+ /* --- Slide canvas --- */
32
+ --pad: var(--sp-8);
33
+
34
+ /* --- Radius --- */
35
+ --r-sm: 4px; --r-md: 8px; --r-lg: 16px;
36
+
37
+ /* --- Motion --- */
38
+ --fast: 150ms; --base: 300ms; --slow: 600ms;
39
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
40
+ }
41
+
42
+ /* Reset */
43
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
44
+
45
+ /* Slide fills its iframe 100%×100% — scaling is on the stage wrapper */
46
+ html { width: 100%; height: 100%; }
47
+ body {
48
+ width: 100%; height: 100%;
49
+ font-family: var(--font-body);
50
+ color: var(--text);
51
+ background: var(--bg);
52
+ line-height: 1.6;
53
+ overflow: hidden; /* slides should never scroll — split content if it overflows */
54
+ }
55
+
56
+ /* ============================================================ */
57
+ /* CANONICAL SLIDE SKELETON */
58
+ /* Every slide uses up to four regions, in this order: */
59
+ /* .slide-logo optional · brand mark, top-right (absolute) */
60
+ /* .slide-header optional · .title + .description */
61
+ /* .slide-content REQUIRED · smart flex body */
62
+ /* .slide-footer optional · source / page / link */
63
+ /* ============================================================ */
64
+
65
+ .slide {
66
+ width: 100%; height: 100%;
67
+ padding: var(--pad);
68
+ display: flex;
69
+ flex-direction: column;
70
+ background: var(--bg);
71
+ position: relative;
72
+ overflow: hidden;
73
+ }
74
+
75
+ /* Region: logo (absolute top-right) */
76
+ .slide-logo {
77
+ position: absolute;
78
+ top: var(--sp-3);
79
+ right: var(--sp-4);
80
+ height: clamp(20px, 2.2vw, 30px);
81
+ display: flex;
82
+ align-items: center;
83
+ z-index: 2;
84
+ opacity: 0.85;
85
+ }
86
+ .slide-logo img,
87
+ .slide-logo svg { height: 100%; width: auto; display: block; }
88
+
89
+ /* Region: header (title + description, stacked) */
90
+ .slide-header {
91
+ flex-shrink: 0;
92
+ margin-bottom: var(--sp-4);
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: var(--sp-2);
96
+ }
97
+
98
+ /* Region: content (smart flex body, fills remaining height) */
99
+ .slide-content {
100
+ flex: 1;
101
+ min-height: 0;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: var(--sp-3);
105
+ }
106
+
107
+ /* Region: footer (source, page number, link, attribution) */
108
+ .slide-footer {
109
+ flex-shrink: 0;
110
+ margin-top: var(--sp-4);
111
+ padding-top: var(--sp-3);
112
+ border-top: 1px solid var(--border);
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: space-between;
116
+ gap: var(--sp-3);
117
+ font-size: var(--t-small);
118
+ color: var(--muted);
119
+ }
120
+ .slide-footer a { color: var(--accent); text-decoration: none; }
121
+ .slide-footer a:hover { text-decoration: underline; }
122
+
123
+ /* Smart flex modifiers for .slide-content (opt-in when default isn't right) */
124
+ .slide-content--center { justify-content: center; align-items: center; text-align: center; }
125
+ .slide-content--middle { justify-content: center; }
126
+ .slide-content--row { flex-direction: row; align-items: stretch; gap: var(--sp-6); }
127
+ .slide-content--grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
128
+ .slide-content--grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); }
129
+
130
+ /* ---------- Typography classes ---------- */
131
+ .display {
132
+ font-family: var(--font-head);
133
+ font-size: var(--t-display);
134
+ font-weight: 700;
135
+ line-height: 1.12;
136
+ letter-spacing: -0.02em;
137
+ color: var(--accent);
138
+ }
139
+ .title {
140
+ font-family: var(--font-head);
141
+ font-size: var(--t-title);
142
+ font-weight: 700;
143
+ line-height: 1.2;
144
+ color: var(--text);
145
+ }
146
+ .description {
147
+ font-family: var(--font-body);
148
+ font-size: var(--t-sub);
149
+ font-weight: 400;
150
+ line-height: 1.4;
151
+ color: var(--muted);
152
+ }
153
+ .subtitle { font-size: var(--t-sub); color: var(--muted); }
154
+ .eyebrow {
155
+ font-size: var(--t-small);
156
+ color: var(--accent);
157
+ font-weight: 600;
158
+ letter-spacing: 0.08em;
159
+ text-transform: uppercase;
160
+ margin-bottom: var(--sp-2);
161
+ }
162
+ .meta { font-size: var(--t-small); color: var(--muted); margin-top: var(--sp-4); }
163
+
164
+ /* ---------- Slide type rules ---------- */
165
+
166
+ /* Centered types: vertically center the entire region stack.
167
+ .slide-content stops being greedy so the stack sizes to its content. */
168
+ .slide--title,
169
+ .slide--section,
170
+ .slide--quote,
171
+ .slide--closing { justify-content: center; }
172
+
173
+ .slide--title .slide-content,
174
+ .slide--section .slide-content,
175
+ .slide--quote .slide-content,
176
+ .slide--closing .slide-content { flex: 0 0 auto; }
177
+
178
+ /* Title and closing also center horizontally */
179
+ .slide--title,
180
+ .slide--closing { text-align: center; align-items: center; }
181
+ .slide--title .slide-header,
182
+ .slide--title .slide-content,
183
+ .slide--closing .slide-header,
184
+ .slide--closing .slide-content { align-items: center; }
185
+ .slide--title .meta { display: flex; justify-content: center; gap: var(--sp-2); }
186
+
187
+ /* Stats and agenda: header sits above; content vertically centered in body */
188
+ .slide--stats .slide-content,
189
+ .slide--agenda .slide-content { justify-content: center; }
190
+
191
+ /* Quote: extra horizontal padding for breathing room */
192
+ .slide--quote { padding: var(--sp-8) calc(var(--sp-8) * 1.5); text-align: center; }
193
+ .slide--quote .slide-content { align-items: center; }
194
+
195
+ /* Section: vertically centered, left-aligned (default text-align) */
196
+ .slide--section .slide-header { gap: var(--sp-3); }
197
+ .section-num { font-size: var(--t-small); color: var(--accent); font-weight: 700; letter-spacing: 0.1em; }
198
+
199
+ /* ---------- Component classes (used inside .slide-content) ---------- */
200
+
201
+ /* agenda */
202
+ .agenda-list { list-style: none; display: flex; flex-direction: column; gap: var(--sp-3); }
203
+ .agenda-list li { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--t-body); }
204
+ .num { font-size: var(--t-small); color: var(--accent); font-weight: 700; min-width: 2.5rem; }
205
+
206
+ /* bullets (content slide) */
207
+ .bullets { list-style: none; display: flex; flex-direction: column; gap: var(--sp-3); }
208
+ .bullets li {
209
+ font-size: var(--t-body);
210
+ padding-left: var(--sp-4);
211
+ position: relative;
212
+ }
213
+ .bullets li::before { content: '→'; position: absolute; left: 0; color: var(--accent); }
214
+
215
+ /* two-column */
216
+ .col-heading { font-size: var(--t-sub); font-weight: 700; color: var(--accent); margin-bottom: var(--sp-3); }
217
+ .col ul { list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--t-body); }
218
+
219
+ /* quote */
220
+ .quote-mark { font-size: 6rem; line-height: 0.8; color: var(--accent); opacity: 0.25; font-family: Georgia, serif; }
221
+ .quote-text { font-size: var(--t-sub); font-style: italic; line-height: 1.5; margin: var(--sp-4) 0; }
222
+ .quote-attr { font-size: var(--t-small); color: var(--muted); }
223
+
224
+ /* code */
225
+ .code-block {
226
+ background: var(--code-bg);
227
+ border: 1px solid var(--border);
228
+ border-radius: var(--r-md);
229
+ padding: var(--sp-4);
230
+ font-family: var(--font-mono);
231
+ font-size: var(--t-code);
232
+ color: var(--code-text);
233
+ line-height: 1.6;
234
+ overflow: hidden;
235
+ flex: 1;
236
+ }
237
+ .code-caption { font-size: var(--t-small); color: var(--muted); margin-top: var(--sp-2); }
238
+
239
+ /* chart */
240
+ .bar-chart { display: flex; flex-direction: column; gap: var(--sp-3); flex: 1; }
241
+ .bar-row { display: flex; align-items: center; gap: var(--sp-3); }
242
+ .bar-label { font-size: var(--t-small); min-width: 8rem; color: var(--muted); }
243
+ .bar { flex: 1; height: 2.25rem; background: var(--surface); border-radius: var(--r-sm); position: relative; overflow: hidden; }
244
+ .bar::after {
245
+ content: '';
246
+ position: absolute; left: 0; top: 0;
247
+ width: var(--pct, 0%); height: 100%;
248
+ background: var(--accent);
249
+ border-radius: var(--r-sm);
250
+ transition: width var(--slow) var(--ease);
251
+ }
252
+ .bar span { position: absolute; right: var(--sp-2); top: 50%; transform: translateY(-50%); font-size: var(--t-small); font-weight: 700; z-index: 1; }
253
+ .chart-insight { font-size: var(--t-body); color: var(--muted); font-style: italic; margin-top: var(--sp-3); }
254
+
255
+ /* timeline */
256
+ .timeline { list-style: none; display: flex; flex-direction: column; gap: 0; position: relative; }
257
+ .timeline::before { content: ''; position: absolute; left: 0.55rem; top: 0; bottom: 0; width: 2px; background: var(--border); }
258
+ .tl-item { display: flex; gap: var(--sp-3); padding-bottom: var(--sp-4); position: relative; }
259
+ .tl-dot { width: 1.1rem; height: 1.1rem; min-width: 1.1rem; border-radius: 50%; background: var(--accent); margin-top: 0.25rem; z-index: 1; }
260
+ .tl-label { font-size: var(--t-small); color: var(--accent); font-weight: 700; display: block; }
261
+
262
+ /* comparison */
263
+ .comparison { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--sp-4); flex: 1; }
264
+ .cmp-divider { width: 1px; background: var(--border); }
265
+ .cmp-col h3 { font-size: var(--t-sub); font-weight: 700; margin-bottom: var(--sp-3); }
266
+ .cmp-before h3 { color: var(--muted); }
267
+ .cmp-after h3 { color: var(--accent); }
268
+ .cmp-col ul { list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--t-body); }
269
+
270
+ /* closing */
271
+ .closing-links { display: flex; gap: var(--sp-4); justify-content: center; margin-top: var(--sp-4); }
272
+ .closing-links a { color: var(--accent); font-size: var(--t-body); text-decoration: none; border-bottom: 1px solid currentColor; padding-bottom: 2px; }
273
+
274
+ /* stats */
275
+ .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--sp-6); padding: var(--sp-4) 0; flex: 1; align-items: center; }
276
+ .stat-item { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); }
277
+ .stat-value { font-family: var(--font-head); font-size: var(--t-display); font-weight: 700; color: var(--accent); line-height: 1; letter-spacing: -0.02em; }
278
+ .stat-label { font-size: var(--t-body); color: var(--muted); text-align: center; }
279
+ .stat-caption { font-size: var(--t-small); color: var(--muted); font-style: italic; margin-top: var(--sp-4); text-align: center; }
280
+
281
+ /* image (full-bleed) */
282
+ .slide--image { padding: 0; overflow: hidden; }
283
+ .slide--image img,
284
+ .slide--image video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
285
+ .image-overlay { position: absolute; inset: 0; background: linear-gradient(to bottom, transparent 40%, rgba(0,0,0,0.72) 100%); }
286
+ .image-caption { position: absolute; bottom: var(--sp-8); left: var(--sp-8); right: var(--sp-8); }
287
+ .slide--image .image-caption .display { color: #fff; text-shadow: 0 2px 12px rgba(0,0,0,0.5); }
288
+ .slide--image .image-caption .description { color: rgba(255,255,255,0.78); margin-top: var(--sp-2); }
289
+
290
+ /* image placeholder — used when user has not yet provided an image file */
291
+ .image-ph {
292
+ flex: 1;
293
+ border: 1.5px dashed var(--border);
294
+ border-radius: var(--r-md);
295
+ display: flex;
296
+ align-items: center;
297
+ justify-content: center;
298
+ background: var(--surface);
299
+ min-height: 180px;
300
+ color: var(--muted);
301
+ }
302
+ .image-ph-inner {
303
+ display: flex;
304
+ flex-direction: column;
305
+ align-items: center;
306
+ gap: var(--sp-2);
307
+ text-align: center;
308
+ padding: var(--sp-4);
309
+ }
310
+ .image-ph-label { font-size: var(--t-small); max-width: 28ch; line-height: 1.4; }
311
+
312
+ /* full-bleed placeholder — for slide--image type when image not yet provided */
313
+ .image-ph-bleed {
314
+ position: absolute;
315
+ inset: 0;
316
+ display: flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ background: var(--surface);
320
+ color: var(--muted);
321
+ font-size: var(--t-small);
322
+ font-family: var(--font-mono);
323
+ letter-spacing: 0.04em;
324
+ border: 2px dashed var(--border);
325
+ }
326
+
327
+ /* Speaker notes — hidden in presentation */
328
+ .speaker-notes { display: none; }
329
+
330
+ /* ---------- Animations ---------- */
331
+ @keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
332
+ @keyframes slideUp { from { opacity: 0; transform: translateY(20px) } to { opacity: 1; transform: none } }
333
+ @keyframes popIn { from { opacity: 0; transform: scale(0.94) } to { opacity: 1; transform: none } }
334
+
335
+ .fade-in { animation: fadeIn var(--base) var(--ease) both; }
336
+ .slide-up { animation: slideUp var(--base) var(--ease) both; }
337
+ .pop-in { animation: popIn var(--fast) var(--ease) both; }
338
+
339
+ .delay-1 { animation-delay: 100ms; }
340
+ .delay-2 { animation-delay: 220ms; }
341
+ .delay-3 { animation-delay: 340ms; }
342
+ .delay-4 { animation-delay: 460ms; }
343
+
344
+ @media (prefers-reduced-motion: reduce) {
345
+ *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
346
+ }
347
+
348
+ /* Print / PDF export (headless Chrome: 1280×720 pages) */
349
+ @media print {
350
+ html, body { width: 1280px; height: 720px; overflow: hidden; }
351
+ .slide { width: 1280px; height: 720px; page-break-after: always; }
352
+ @page { size: 1280px 720px; margin: 0; }
353
+ }