Files
open-design/skills/replit-deck/examples/example-holm.html
eff0764f93 Feat: add replit-deck skill with 8 themes from replit.com/slides (#74)
* Feat: add replit-deck skill with 8 themes captured from replit.com/slides

- New deck skill modeled on the Replit Slides landing-page template gallery.
- Eight self-contained themes selectable via <body data-theme=...>: helix,
  holm, vance, bevel, world-dark, world-mint, atlas, bluehouse. Each ships
  a complete visual system — palette, font stack, display weight, accent
  rules — sampled directly from the original PNGs with ImageMagick.
- Seed template.html reuses the proven iframe-safe nav script from
  simple-deck (five-rule scroller/keyboard/focus/scroll-into-view pattern).
- references/: themes.md (when-to-pick + do/don't per theme), layouts.md
  (ten paste-ready <section> blocks cross-referenced to theme fits),
  components.md (shared primitives), checklist.md (P0/P1/P2 + 5-dim
  critique gate).
- examples/: four reference decks showing the most contrasting themes in
  action (helix / holm / atlas / bluehouse). The four remaining themes
  are covered by themes.md + template.html tokens.
- Registers as mode:deck, previewType:html. Discovered by the daemon's
  skill registry on next scan — no other wiring needed.

* Fix: replit-deck review feedback — atlas display-face, theme lock-in, docs gaps

Addresses review feedback on #74:

P2 bugs (Codex):
- SKILL.md: atlas uses serif display (not sans). The Hard Rules list
  contradicted the --font-display token in template.html and would
  mis-generate atlas decks.
- themes.md: remove the world-dark/world-mint 'alternating slides'
  escape hatch that conflicted with the P0 theme-lock gate. Replaced
  with 'still one theme per file' guidance; world-mint is a companion
  standalone deck, not a mid-deck swap. Ripple fix in components.md.

Doc gaps (lefarcen):
- Scope & provenance: eight themes = full replit.com/slides gallery
  at snapshot date 2026-04-29, not a curated subset.
- Maintenance: one-time snapshot, not auto-tracked; drift reporting
  goes through a repo issue.
- Browser support: Chrome 110+ / Safari 16+ / Firefox 115+, explicit
  about color-mix/scroll-snap/text-wrap:balance baseline.
- When-to-use table comparing replit-deck to simple-deck and
  magazine-web-ppt (not guizang-ppt; the registered name is
  magazine-web-ppt with guizang-ppt-skill as upstream).
- Verification section with real curl output against the running
  daemon on localhost:7456.
- themes.md: 'Contributing a new theme' with the exact ImageMagick
  sampling procedure and the 6-token mapping contract.

P3 nits (lefarcen):
- slide_count frontmatter comment: 6 is a board baseline, memos
  usually run 3–4.
- template.html: expanded the 'do not rewrite' comment to name all
  five iframe-nav bugs the script solves.
- SKILL.md workflow Step 5: one-liner pointing to the checklist's
  P0 gates + five-dimension critique.

* Fix: namespace replit-deck persisted slide position by pathname

Codex caught that the nav script claimed 'keyed by pathname' in its
comment but actually used a single global localStorage key
('od-deck-pos'), so two decks served from the same origin would
overwrite each other's saved slide index. Opening a second deck after
a long first one could land the user mid-deck on an unrelated file.

Fix: namespace KEY with location.pathname in template.html and in the
four ship-along example decks. Also corrected the comment to say
localStorage (not sessionStorage) to match the actual implementation.

(Same pattern exists in skills/simple-deck — filed as a separate
concern, not in the scope of this PR.)

---------

Co-authored-by: joey <joey@joeydeMacBook-Air.local>
2026-04-30 11:19:52 +08:00

514 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<!--
OD replit-deck seed.
Single-file horizontal-swipe HTML deck in the style of Replit Slides's
landing-page template gallery (replit.com/slides). One deck picks ONE
theme via `<body data-theme="...">`. All 8 themes ship as tokens below;
never override per-slide.
Themes:
helix Modern minimal · light grey + ink + electric blue
holm Editorial serif · cream + ink + deep chestnut
vance Gallery · black/cream bars + cream serif
bevel Y2K editorial · black + Y2K display type
world-dark Finance dark · deep green + mint + neon yellow
world-mint Finance light · mint + deep green + neon yellow
atlas Museum · black + ivory + vermilion + serif
bluehouse Consumer · deep navy + peach/coral gradient cards
DO NOT rewrite the script at the bottom. It solves five iframe-specific
bugs (real scroller, dual listeners, auto-focus, no scrollIntoView,
position persistence). See `skills/simple-deck` for the same pattern.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Holm — Series A Pre-Read</title>
<style>
/* ─── font stacks (system-only; no external fonts) ─────────────── */
:root {
--font-sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
--font-sans-display: -apple-system, BlinkMacSystemFont, 'Inter Display', 'Inter', 'Segoe UI', system-ui, sans-serif;
--font-serif: 'Iowan Old Style', 'Charter', 'Palatino', Georgia, 'Times New Roman', serif;
--font-serif-display: 'GT Super', 'Tiempos Headline', 'Iowan Old Style', Georgia, serif;
--font-mono: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}
/* ─── theme: helix (slide-1/4/5 — modern minimal) ─────────────── */
body[data-theme="helix"] {
--bg: #fafafa;
--surface: #ffffff;
--fg: #19191c;
--muted: #6e6e73;
--border: #e4e4e7;
--accent: #5889fe;
--accent-soft: color-mix(in oklch, #5889fe 14%, transparent);
--font-display: var(--font-sans-display);
--font-body: var(--font-sans);
--display-weight: 600;
--display-tracking: -0.02em;
}
/* ─── theme: holm (slide-2 — editorial serif memo) ────────────── */
body[data-theme="holm"] {
--bg: #e4dfd7;
--surface: #eee9e0;
--fg: #0f0f0e;
--muted: #7c7e84;
--border: #c7c1b7;
--accent: #52311d;
--accent-soft: color-mix(in oklch, #52311d 14%, transparent);
--font-display: var(--font-serif-display);
--font-body: var(--font-sans);
--display-weight: 500;
--display-tracking: -0.015em;
}
/* ─── theme: vance (slide-3/7 — gallery catalog) ──────────────── */
body[data-theme="vance"] {
--bg: #f1ede2;
--surface: #e7e2d4;
--fg: #171815;
--muted: #6e6b62;
--border: #d6d2c5;
--accent: #171815;
--accent-soft: color-mix(in oklch, #171815 8%, transparent);
--bar: #0a0a0a; /* top/bottom gallery bar */
--bar-fg: #f1ede2;
--font-display: var(--font-serif-display);
--font-body: var(--font-sans);
--display-weight: 400;
--display-tracking: -0.01em;
}
/* ─── theme: bevel (slide-6/13 — Y2K editorial) ───────────────── */
body[data-theme="bevel"] {
--bg: #0d0d0b;
--surface: #18181a;
--fg: #eae6dd;
--muted: #a29e95;
--border: #2a2a28;
--accent: #c8ff00; /* neon outline only — sparingly */
--accent-soft: color-mix(in oklch, #c8ff00 10%, transparent);
--font-display: 'Antonio', 'Bebas Neue', Impact, var(--font-sans-display);
--font-body: var(--font-sans);
--display-weight: 700;
--display-tracking: 0;
}
/* ─── theme: world-dark (slide-8/10 — finance dark) ───────────── */
body[data-theme="world-dark"] {
--bg: #0d3a2b;
--surface: #124736;
--fg: #bcd6cd;
--muted: #789f91;
--border: #1d4c3c;
--accent: #e8f615;
--accent-soft: color-mix(in oklch, #e8f615 18%, transparent);
--font-display: var(--font-sans-display);
--font-body: var(--font-sans);
--display-weight: 500;
--display-tracking: -0.015em;
}
/* ─── theme: world-mint (slide-9 — finance light sibling) ─────── */
body[data-theme="world-mint"] {
--bg: #bcd6cd;
--surface: #c8e0d6;
--fg: #0d3a2b;
--muted: #527567;
--border: #9abbac;
--accent: #e8f615;
--accent-soft: color-mix(in oklch, #e8f615 22%, transparent);
--font-display: var(--font-sans-display);
--font-body: var(--font-sans);
--display-weight: 500;
--display-tracking: -0.015em;
}
/* ─── theme: atlas (slide-11 — museum chapter) ────────────────── */
body[data-theme="atlas"] {
--bg: #111010;
--surface: #1a1918;
--fg: #e7e6e2;
--muted: #827d78;
--border: #2a2826;
--accent: #de3f40;
--accent-soft: color-mix(in oklch, #de3f40 14%, transparent);
--font-display: var(--font-serif-display);
--font-body: var(--font-sans);
--display-weight: 500;
--display-tracking: -0.02em;
}
/* ─── theme: bluehouse (slide-12 — consumer card) ─────────────── */
body[data-theme="bluehouse"] {
--bg: #0b1524;
--surface: #10203a;
--fg: #ffffff;
--muted: #8ea0b8;
--border: #1a2c46;
--accent: #fb675d; /* coral */
--accent-2: #ff8f68; /* peach */
--accent-soft: color-mix(in oklch, #fb675d 18%, transparent);
--card-peach: #e0af99;
--card-lavender: #c7cff0;
--font-display: var(--font-sans-display);
--font-body: var(--font-sans);
--display-weight: 700;
--display-tracking: -0.025em;
}
/* ─── reset / base ────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: 18px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
display: flex;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
}
body::-webkit-scrollbar { display: none; }
p { text-wrap: pretty; margin: 0; }
h1, h2, h3 { text-wrap: balance; margin: 0; font-weight: var(--display-weight); letter-spacing: var(--display-tracking); }
/* ─── slide surface ───────────────────────────────────────────── */
.slide {
flex: 0 0 100vw;
width: 100vw;
height: 100vh;
scroll-snap-align: start;
padding: clamp(48px, 6vw, 96px) clamp(56px, 7vw, 112px);
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
}
.slide.center { align-items: center; justify-content: center; text-align: center; }
/* ─── meta bar (top thin row: brand · meta · page) ────────────── */
.meta-bar {
position: absolute;
top: clamp(32px, 4vw, 56px);
left: clamp(56px, 7vw, 112px);
right: clamp(56px, 7vw, 112px);
display: flex;
justify-content: space-between;
align-items: center;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
body[data-theme="vance"] .meta-bar { color: var(--bar-fg); }
/* ─── typography primitives ──────────────────────────────────── */
.eyebrow {
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.eyebrow.accent { color: var(--accent); }
.h-hero { font-family: var(--font-display); font-size: clamp(56px, 8.5vw, 128px); line-height: 1.02; }
.h-xl { font-family: var(--font-display); font-size: clamp(40px, 5vw, 76px); line-height: 1.08; }
.h-lg { font-family: var(--font-display); font-size: clamp(28px, 3vw, 44px); line-height: 1.14; }
.h-md { font-family: var(--font-display); font-size: clamp(20px, 1.6vw, 24px); line-height: 1.25; }
.lead { font-size: clamp(16px, 1.2vw, 19px); color: var(--muted); max-width: 56ch; }
/* Big numeric display — for kpi rows, used by helix / world / atlas */
.num { font-family: var(--font-display); font-size: clamp(48px, 5.5vw, 84px); line-height: 1; letter-spacing: -0.03em; }
.num-label { font-size: 15px; color: var(--muted); margin-bottom: 8px; }
.num-delta { font-family: var(--font-mono); font-size: 13px; color: var(--accent); margin-top: 8px; letter-spacing: 0.02em; }
/* ─── layout primitives ──────────────────────────────────────── */
.hstack { display: flex; gap: var(--gap, 24px); align-items: flex-start; }
.vstack { display: flex; flex-direction: column; gap: var(--gap, 16px); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(24px, 3vw, 48px); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(32px, 4vw, 64px); align-items: center; }
.grid-6 { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(32px, 4vw, 56px); row-gap: clamp(48px, 5vw, 80px); }
.divider { height: 1px; background: var(--border); margin: clamp(16px, 2vw, 32px) 0; }
/* ─── surface cards ──────────────────────────────────────────── */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: clamp(20px, 2vw, 32px);
}
/* ─── vance gallery bars ─────────────────────────────────────── */
body[data-theme="vance"] .slide { padding-top: 0; padding-bottom: 0; }
body[data-theme="vance"] .vance-top {
background: var(--bar);
color: var(--bar-fg);
padding: clamp(32px, 4vw, 64px) clamp(56px, 7vw, 112px);
margin-left: calc(-1 * clamp(56px, 7vw, 112px));
margin-right: calc(-1 * clamp(56px, 7vw, 112px));
}
/* ─── bevel dashed frames ────────────────────────────────────── */
body[data-theme="bevel"] .bevel-frame {
border: 1px dashed var(--accent);
padding: clamp(16px, 2vw, 28px);
position: relative;
}
body[data-theme="bevel"] .bevel-frame::before,
body[data-theme="bevel"] .bevel-frame::after {
content: "";
position: absolute;
width: 8px; height: 8px;
background: var(--accent);
border-radius: 50%;
}
body[data-theme="bevel"] .bevel-frame::before { top: -4px; left: -4px; }
body[data-theme="bevel"] .bevel-frame::after { bottom: -4px; right: -4px; }
/* ─── world yellow square marker ─────────────────────────────── */
body[data-theme="world-dark"] .world-marker,
body[data-theme="world-mint"] .world-marker {
display: inline-block;
width: 14px; height: 14px;
background: var(--accent);
vertical-align: middle;
}
/* ─── atlas chapter dot ──────────────────────────────────────── */
body[data-theme="atlas"] .atlas-dot {
display: inline-block;
width: 10px; height: 10px;
border-radius: 50%;
background: var(--accent);
margin-right: 12px;
vertical-align: middle;
}
/* ─── bluehouse gradient card ────────────────────────────────── */
body[data-theme="bluehouse"] .bh-card {
border-radius: 24px;
padding: clamp(28px, 3vw, 48px);
aspect-ratio: 4 / 3;
display: flex;
flex-direction: column;
justify-content: space-between;
}
body[data-theme="bluehouse"] .bh-card.peach {
background: var(--card-peach);
color: #1a0e08;
}
body[data-theme="bluehouse"] .bh-card.coral {
background: linear-gradient(135deg, var(--accent-2), var(--accent));
color: #ffffff;
}
body[data-theme="bluehouse"] .bh-card.lavender {
background: linear-gradient(180deg, var(--card-lavender), #8faad8);
color: #0b1524;
}
/* ─── deck chrome (counter, progress, hint) ──────────────────── */
.deck-counter {
position: fixed;
bottom: 24px; right: 32px;
font-family: var(--font-mono);
font-size: 11px;
padding: 6px 12px;
background: color-mix(in oklch, var(--bg) 92%, transparent);
border: 1px solid var(--border);
letter-spacing: 0.08em;
color: var(--muted);
z-index: 10;
}
.deck-hint {
position: fixed;
bottom: 24px; left: 32px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
letter-spacing: 0.04em;
z-index: 10;
}
.deck-progress {
position: fixed;
top: 0; left: 0;
height: 2px;
background: var(--accent);
width: 0;
z-index: 10;
transition: width 0.18s ease;
}
</style>
</head>
<body data-theme="holm">
<!-- example: holm · illustrative content only. Numbers are synthetic. -->
<section class="slide" data-screen-label="01 Cover">
<div class="meta-bar">
<span style="font-family: var(--font-serif-display); font-size: 32px; text-transform: none; letter-spacing: 0; color: var(--accent);">Holm</span><span style="color: var(--muted);">&nbsp;&nbsp;EST. 2024</span>
<span>MEMO 04 &nbsp;/&nbsp; APR 2026</span>
</div>
<div style="margin-top: 30vh; max-width: 58vw;">
<p class="eyebrow" style="color: var(--accent); margin-bottom: 28px;">—&nbsp;&nbsp;SERIES A — CONFIDENTIAL PRE-READ</p>
<h1 class="h-xl" style="max-width: 18ch;">Banking and back-office for the 1.4 million lawyers who were never supposed to be alone.</h1>
<p class="lead" style="margin-top: 28px; max-width: 50ch;">Why solo and small-firm legal is the largest unbundled fintech opportunity left in professional services.</p>
</div>
<div style="position: absolute; bottom: clamp(40px, 5vh, 72px); left: clamp(56px, 7vw, 112px); right: clamp(56px, 7vw, 112px); display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;">
<span><b>NAOMI VELEZ</b> — CO-FOUNDER, CEO &nbsp;·&nbsp; <b>DANIEL LIOR</b> — CO-FOUNDER, CTO</span>
<span>HOLM.LAW</span>
</div>
</section>
<section class="slide" data-screen-label="02 The Problem">
<div class="meta-bar">
<span style="color: var(--accent);">02 — THE PROBLEM &nbsp;<span style="display: inline-block; width: 40px; height: 1px; background: var(--accent); vertical-align: middle;"></span></span>
<span>HOLM · 02 / 03</span>
</div>
<div style="margin-top: 25vh; max-width: 62vw;">
<h2 class="h-xl" style="max-width: 18ch;">The median solo lawyer sends 34% of their billable hours to a bookkeeper, a bank, and a filing service that don't talk to each other.</h2>
</div>
<div style="position: absolute; bottom: clamp(56px, 7vh, 96px); left: clamp(56px, 7vw, 112px); right: clamp(56px, 7vw, 112px); display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(24px, 3vw, 56px); padding-top: clamp(24px, 3vh, 40px); border-top: 1px solid var(--border);">
<div><p class="eyebrow" style="margin-bottom: 8px;">MARKET SIZE</p><div class="num" style="font-size: clamp(36px, 4vw, 56px);">1.4M</div><div style="color: var(--muted); font-size: 13px; margin-top: 4px;">US solo + small-firm lawyers.</div></div>
<div><p class="eyebrow" style="margin-bottom: 8px;">TIME LOST</p><div class="num" style="font-size: clamp(36px, 4vw, 56px); color: var(--accent);">34%</div><div style="color: var(--muted); font-size: 13px; margin-top: 4px;">Billable hours on back-office.</div></div>
<div><p class="eyebrow" style="margin-bottom: 8px;">TAM</p><div class="num" style="font-size: clamp(36px, 4vw, 56px);">$18B</div><div style="color: var(--muted); font-size: 13px; margin-top: 4px;">Annual, US only.</div></div>
</div>
</section>
<section class="slide" data-screen-label="03 The Ask">
<div class="meta-bar">
<span style="color: var(--accent);">04 — THE ASK &nbsp;<span style="display: inline-block; width: 40px; height: 1px; background: var(--accent); vertical-align: middle;"></span></span>
<span>HOLM · 03 / 03</span>
</div>
<div style="margin-top: clamp(40px, 6vh, 80px);">
<h2 class="h-xl" style="max-width: 22ch;">$11.4M Series A, led by Felicis. Closing 6/15.</h2>
<p class="lead" style="margin-top: 20px;">Re-up from First Round and Cowboy. Notable angels: Patrick McKenzie, Olympia Hostler (Quaderno), Marshall Kirkpatrick.</p>
</div>
<div style="margin-top: clamp(32px, 4vh, 48px); display: grid; grid-template-columns: 1.1fr 1fr; gap: clamp(32px, 4vw, 64px); align-items: start;">
<div>
<p class="eyebrow" style="margin-bottom: 20px;">USE OF FUNDS — 24 MONTH PLAN</p>
<div style="display: flex; flex-direction: column; gap: 16px;">
<div style="display: grid; grid-template-columns: 60px 1fr; gap: 24px; align-items: start;">
<span class="num" style="font-size: 28px; color: var(--accent);">52%</span>
<div><div style="font-weight: 600;">Engineering &amp; product</div><div style="color: var(--muted); font-size: 14px; margin-bottom: 6px;">Trust accounting, payroll, multi-state filings.</div><div style="height: 3px; background: var(--border); position: relative;"><div style="position: absolute; left: 0; top: 0; height: 100%; width: 52%; background: var(--accent);"></div></div></div>
</div>
<div style="display: grid; grid-template-columns: 60px 1fr; gap: 24px; align-items: start;">
<span class="num" style="font-size: 28px; color: var(--accent);">28%</span>
<div><div style="font-weight: 600;">Go-to-market</div><div style="color: var(--muted); font-size: 14px; margin-bottom: 6px;">Direct + state bar partnerships in CA, TX, NY.</div><div style="height: 3px; background: var(--border); position: relative;"><div style="position: absolute; left: 0; top: 0; height: 100%; width: 28%; background: var(--accent);"></div></div></div>
</div>
<div style="display: grid; grid-template-columns: 60px 1fr; gap: 24px; align-items: start;">
<span class="num" style="font-size: 28px; color: var(--accent);">12%</span>
<div><div style="font-weight: 600;">Compliance &amp; licensing</div><div style="color: var(--muted); font-size: 14px; margin-bottom: 6px;">MTL, sponsor bank coverage.</div><div style="height: 3px; background: var(--border); position: relative;"><div style="position: absolute; left: 0; top: 0; height: 100%; width: 12%; background: var(--accent);"></div></div></div>
</div>
<div style="display: grid; grid-template-columns: 60px 1fr; gap: 24px; align-items: start;">
<span class="num" style="font-size: 28px; color: var(--accent);">8%</span>
<div><div style="font-weight: 600;">Operations</div><div style="color: var(--muted); font-size: 14px; margin-bottom: 6px;">Brand, finance, ops.</div><div style="height: 3px; background: var(--border); position: relative;"><div style="position: absolute; left: 0; top: 0; height: 100%; width: 8%; background: var(--accent);"></div></div></div>
</div>
</div>
</div>
<div class="card" style="background: var(--surface); border: 1px solid var(--border);">
<p class="eyebrow" style="color: var(--accent); margin-bottom: 20px;">THE TEAM</p>
<div style="display: flex; flex-direction: column; gap: 16px;">
<div style="display: grid; grid-template-columns: 48px 1fr; gap: 16px; align-items: start;">
<div style="width: 48px; height: 48px; border-radius: 50%; background: color-mix(in oklch, var(--accent) 22%, var(--surface)); display: flex; align-items: center; justify-content: center; font-family: var(--font-serif-display); color: var(--accent); font-size: 20px;">N</div>
<div><div style="font-weight: 600;">Naomi Velez — CEO</div><div style="color: var(--muted); font-size: 14px;">Latham &amp; Watkins associate '14–'19. Solo practice 5 yrs. Cornell Law.</div></div>
</div>
<div style="display: grid; grid-template-columns: 48px 1fr; gap: 16px; align-items: start;">
<div style="width: 48px; height: 48px; border-radius: 50%; background: color-mix(in oklch, var(--muted) 30%, var(--surface)); display: flex; align-items: center; justify-content: center; font-family: var(--font-serif-display); color: var(--fg); font-size: 20px;">D</div>
<div><div style="font-weight: 600;">Daniel Lior — CTO</div><div style="color: var(--muted); font-size: 14px;">Ledger infra at Plaid. Built ACH stack at Modern Treasury. Waterloo CS.</div></div>
</div>
</div>
<div style="border-top: 1px solid var(--border); margin-top: 20px; padding-top: 14px;">
<p class="eyebrow" style="margin-bottom: 6px;">DIRECT</p>
<div style="display: flex; justify-content: space-between; font-size: 14px;"><span style="font-weight: 600;">naomi@holm.law</span><span style="color: var(--muted);">+1 (415) 555-0142</span></div>
</div>
</div>
</div>
</section>
<!-- chrome (do not move) -->
<div class="deck-progress" id="deck-progress" aria-hidden></div>
<div class="deck-counter" id="deck-counter">1 / 3</div>
<div class="deck-hint">← / → · scroll · swipe</div>
<script>
/*
Five hard rules for deck nav inside an iframe.
Verified by skills/simple-deck — do not rewrite.
1. Detect the real scroller — body OR documentElement.
2. Listen for scroll on BOTH window and document, capture phase.
3. Listen for keydown on BOTH window and document, capture phase.
4. Auto-focus body so arrow keys work without an upfront click.
5. Never use Element.scrollIntoView — yanks host page.
*/
(function () {
var slides = document.querySelectorAll('.slide');
var counter = document.getElementById('deck-counter');
var progress = document.getElementById('deck-progress');
var KEY = 'od-deck-pos:' + (location.pathname || '/');
var active = 0;
function scroller() {
if (document.body.scrollWidth > document.body.clientWidth + 1) return document.body;
return document.scrollingElement || document.documentElement;
}
function setActive(i) {
active = i;
if (counter) counter.textContent = (i + 1) + ' / ' + slides.length;
if (progress) progress.style.width = (((i + 1) / slides.length) * 100) + '%';
try { localStorage.setItem(KEY, String(i)); } catch (_) {}
}
function go(i) {
var next = Math.max(0, Math.min(slides.length - 1, i));
setActive(next);
scroller().scrollTo({ left: next * window.innerWidth, behavior: 'smooth' });
}
function syncFromScroll() {
var i = Math.round(scroller().scrollLeft / window.innerWidth);
if (i !== active && i >= 0 && i < slides.length) setActive(i);
}
function onKey(e) {
var t = e.target;
if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA')) return;
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') { e.preventDefault(); go(active + 1); }
else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); go(active - 1); }
else if (e.key === 'Home') { e.preventDefault(); go(0); }
else if (e.key === 'End') { e.preventDefault(); go(slides.length - 1); }
}
window.addEventListener('keydown', onKey, true);
document.addEventListener('keydown', onKey, true);
document.addEventListener('scroll', syncFromScroll, { passive: true, capture: true });
window.addEventListener('scroll', syncFromScroll, { passive: true });
document.body.setAttribute('tabindex', '-1');
document.body.style.outline = 'none';
function focusDeck() { try { window.focus(); document.body.focus({ preventScroll: true }); } catch (_) {} }
document.addEventListener('mousedown', focusDeck);
window.addEventListener('load', focusDeck);
focusDeck();
try {
var saved = parseInt(localStorage.getItem(KEY) || '0', 10);
if (!isNaN(saved) && saved >= 0 && saved < slides.length) {
setActive(saved);
scroller().scrollTo({ left: saved * window.innerWidth, behavior: 'instant' });
} else {
setActive(0);
}
} catch (_) { setActive(0); }
})();
</script>
</body>
</html>