1# Peach Website Demo Update Spec2**Target: peachpasswords.com — HeroDemo.astro**3**Extension version: 1.4.12**4**Goal: make the website demo show the real extension as faithfully as possible**56---78## 1. Overview910The live demo at `peachpasswords.com` renders a simulated 320×520px popup shell directly in the browser. It currently shows a subset of the real popup's surfaces and uses some placeholder or outdated data/markup. This document is a complete spec for bringing the demo to parity with the real extension's v1.4.12 state.1112The implementation approach is:131. Copy the compiled CSS from `popup.css` + `vars.css` into the demo as-is (single source of truth).142. Rebuild the demo HTML surfaces to match the real component markup exactly.153. Wire up the demo's click/hover interactivity to show as many surfaces as possible.164. Use realistic sample data that tells a story about Peach's capabilities.1718---1920## 2. CSS strategy (highest priority)2122The demo currently has its own CSS. **Replace it** with a direct copy of the extension's compiled CSS:2324```25src/popup/styles/vars.css → embedded in <style> in HeroDemo.astro26src/popup/styles/popup.css → embedded in <style> in HeroDemo.astro27```2829Do **not** maintain a separate CSS copy. Instead add a build step (or Astro integration) that reads both files from the extension workspace and injects them at build time. This guarantees the demo never drifts from the real product.3031The shell wrapper the website uses should be:3233```html34<div class="popup-root peach-theme" id="hd-root">35 36</div>37```3839Override only one thing at the demo level: scale the popup root down so it fits the marketing page layout:4041```css42#hd-root {43 transform: scale(0.88);44 transform-origin: top center;45 border-radius: 20px;46 overflow: hidden;47 box-shadow: 0 32px 80px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06);48}49```5051---5253## 3. Fonts5455The real extension self-hosts fonts from `public/fonts/`. The website should reference the same files or load them from a CDN. Required faces:5657| Family | Weights used | Role |58|--------|-------------|------|59| Cormorant Garamond | 300 italic, 400 italic | Display headings (lock screen, entry name) |60| IBM Plex Mono | 400 | Metadata, labels, mono values, TOTP code |61| Inter Variable | 300, 400, 500 | UI body text |6263The `@font-face` declarations in `vars.css` already handle this. Copy them verbatim.6465---6667## 4. Demo surface inventory6869Show the following surfaces. Each has a tab/button in the demo chrome that reveals it. The demo should start on **Surface A (unlocked entry detail)** — this is the most impressive screen and what a visitor should see first.7071| ID | Surface | Entry point |72|----|---------|-------------|73| A | Unlocked — Entry detail (GitHub) | Default / "detail" button |74| B | Unlocked — Entry detail (Stripe, with TOTP) | Click "Stripe" in search bar |75| C | Vault list (with sort + tab-match badge) | Click search bar input |76| D | Add new entry | "+" button in header |77| E | Settings | Gear icon in header |78| F | Vault Health | From Settings → "open" |79| G | Lock screen (unlock) | Lock icon in header |80| H | Lock screen (onboarding / new vault) | Toggle inside demo |8182The passkey approval, CJK export/import, sync pairing, and export surfaces can be shown as static screenshots below the interactive demo rather than as interactive surfaces (they are powerful features but too branched for a simple demo).8384---8586## 5. Sample data8788Use this fixed vault for the demo. It demonstrates all entry types and features:8990```js91const DEMO_ENTRIES = [92 {93 id: "demo-1",94 type: "login",95 title: "GitHub",96 url: "https://github.com",97 username: "juni_patel",98 password: "p#9Kx!mQ3rL@vZ",99 totp: null,100 tags: ["work", "dev"],101 breach: 0,102 createdAt: Date.now() - 86400000 * 90,103 modifiedAt: Date.now() - 86400000 * 14,104 },105 {106 id: "demo-2",107 type: "login",108 title: "Stripe",109 url: "https://dashboard.stripe.com",110 username: "[email protected]",111 password: "Tr!p0d$2024#Acme",112 totp: "JBSWY3DPEHPK3PXP", 113 tags: ["work"],114 breach: 0,115 createdAt: Date.now() - 86400000 * 120,116 modifiedAt: Date.now() - 86400000 * 3,117 },118 {119 id: "demo-3",120 type: "login",121 title: "Netflix",122 url: "https://netflix.com",123 username: "[email protected]",124 password: "password123", 125 tags: ["personal"],126 breach: 54231, 127 createdAt: Date.now() - 86400000 * 400,128 modifiedAt: Date.now() - 86400000 * 400,129 },130 {131 id: "demo-4",132 type: "passkey",133 title: "Apple ID",134 rpId: "apple.com",135 userName: "juni_patel",136 userDisplayName: "Juni Patel",137 tags: ["personal"],138 createdAt: Date.now() - 86400000 * 30,139 modifiedAt: Date.now() - 86400000 * 30,140 },141 {142 id: "demo-5",143 type: "note",144 title: "Server SSH keys",145 notes: "prod: ssh -i ~/.ssh/peach_prod [email protected]\nstaging: ssh -i ~/.ssh/peach_stage [email protected]",146 tags: ["dev"],147 createdAt: Date.now() - 86400000 * 60,148 modifiedAt: Date.now() - 86400000 * 7,149 },150 {151 id: "demo-6",152 type: "login",153 title: "Gmail",154 url: "https://mail.google.com",155 username: "[email protected]",156 password: "Gm4!l$ecure#88",157 tags: ["personal"],158 breach: 0,159 canary: { label: "honeypot", webhookUrl: "https://hooks.example.com/canary" }, 160 createdAt: Date.now() - 86400000 * 200,161 modifiedAt: Date.now() - 86400000 * 45,162 }163];164```165166The tab domain for the demo should be simulated as `github.com` — this causes "GitHub" to show the tab-match orange dot in the vault list.167168---169170## 6. Surface A — Entry detail (GitHub, login)171172This is the default surface. HTML structure matches `Entry.tsx` exactly:173174```html175<section id="hd-shell" class="peach-shell peach-theme" data-surface="extension">176 177 <header class="peach-chrome">178 <div class="peach-wordmark-wrap">179 180 <svg class="peach-brand-mark" ...></svg>181 <span class="peach-wordmark-text">peach</span>182 <span class="peach-status-pip unlocked"></span>183 </div>184 <div class="peach-chrome-actions">185 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-btn-settings" aria-label="Settings">186 187 </button>188 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-btn-add" aria-label="New entry">189 190 </button>191 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn peach-lock-btn" id="hd-btn-lock" aria-label="Lock vault">192 193 </button>194 </div>195 </header>196197 198 <div class="peach-shell-body">199 <div class="peach-shell-veil"></div>200 <div class="peach-content-frame" data-content-phase="idle">201202 203 <div class="peach-ghost"></div>204 <div class="peach-stage">205206 207 <div class="peach-entry-name-wrap">208 <button class="peach-entry-name-button">209 <h1 class="peach-entry-name" aria-label="GitHub">210 <span class="peach-entry-name-text">211 212 <span class="peach-entry-name-char" style="--char-index:0">G</span>213 <span class="peach-entry-name-char" style="--char-index:1">i</span>214 <span class="peach-entry-name-char" style="--char-index:2">t</span>215 <span class="peach-entry-name-char" style="--char-index:3">H</span>216 <span class="peach-entry-name-char" style="--char-index:4">u</span>217 <span class="peach-entry-name-char" style="--char-index:5">b</span>218 </span>219 </h1>220 </button>221 </div>222223 224 <div class="peach-domain">225 <div class="peach-domain-row">226 227 <img src="https://icons.duckduckgo.com/ip3/github.com.ico" width="16" height="16" style="border-radius:3px" alt="">228 <span class="peach-domain-value">github.com</span>229 </div>230 231 <span class="peach-tag-chip" style="font-size:10px;padding:2px 8px;">work</span>232 <span class="peach-tag-chip" style="font-size:10px;padding:2px 8px;">dev</span>233 </div>234235 236 <section class="peach-fields">237 238 <div class="peach-field-card">239 <span class="peach-label">username</span>240 <div class="peach-field-value-row">241 <span class="peach-field-value">juni_patel</span>242 <div class="peach-field-actions">243 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn peach-copy-btn" id="hd-copy-username">244 <span class="peach-copy-icon">245 246 </span>247 <span class="peach-copy-sheen"></span>248 </button>249 </div>250 </div>251 </div>252253 254 <div class="peach-field-card">255 <span class="peach-label">password</span>256 <div class="peach-field-value-row">257 <div class="peach-password-value">258 <span class="peach-password-mask">...............</span>259 </div>260 <div class="peach-field-actions">261 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-reveal-password" aria-label="Reveal password">262 263 </button>264 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn peach-copy-btn" id="hd-copy-password">265 <span class="peach-copy-icon"></span>266 <span class="peach-copy-sheen"></span>267 </button>268 </div>269 </div>270 </div>271 </section>272273 </div>274 </div>275276 277 <section class="peach-search-wrap">278 <div class="peach-search-bar">279 <span class="peach-search-icon"></span>280 <input class="peach-search-input" placeholder="search vault..." readonly id="hd-search-input">281 <div class="peach-search-meta">282 <span class="peach-search-counter">1 / 6</span>283 <span class="peach-divider"></span>284 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-prev"></button>285 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-next"></button>286 </div>287 </div>288 </section>289 </div>290</section>291```292293### A — Interactive behaviors294295| Action | Effect |296|--------|--------|297| Click copy (username) | `peach-copy-btn` gets `.is-copied` for 1.5s; drain animation plays |298| Click copy (password) | Same copy animation |299| Click reveal | Toggle `.peach-password-mask` ↔ `.peach-password-text` content |300| Click prev/next arrows | Cycle through `DEMO_ENTRIES`; re-render entry name chars with `--char-index` delay |301| Click search bar | Show Surface C (vault list overlay) |302| Click settings gear | Show Surface E |303| Click "+" | Show Surface D |304| Click lock | Show Surface G |305306---307308## 7. Surface B — Entry detail (Stripe, with TOTP)309310Same structure as A but with:311- Entry name: "Stripe"312- Domain: `dashboard.stripe.com`313- Username: `[email protected]` 314- Tags: `work`315- TOTP band rendered below the password field:316317```html318<section class="peach-totp-band">319 <div class="peach-totp-main">320 <span class="peach-label">2-factor</span>321 <div class="peach-totp-code-row">322 <p class="peach-totp-code">323 <span class="peach-totp-code-text">482 917</span>324 </p>325 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn peach-copy-btn peach-totp-copy-btn">326 <span class="peach-copy-icon"></span>327 <span class="peach-copy-sheen"></span>328 </button>329 </div>330 </div>331 <div class="peach-totp-meta">332 <span class="peach-label">expires</span>333 <div class="peach-totp-countdown" id="hd-totp-remaining">18s</div>334 <svg class="peach-totp-ring" width="44" height="44" viewBox="0 0 44 44">335 <circle cx="22" cy="22" r="17" class="peach-totp-ring-track" />336 <circle cx="22" cy="22" r="17" class="peach-totp-ring-progress"337 style="stroke-dasharray: 106.8; stroke-dashoffset: 35.6;" />338 </svg>339 </div>340 <div class="peach-totp-progress">341 <div class="peach-totp-progress-bar" style="width: 60%;"></div>342 </div>343</section>344```345346Animate the TOTP ring/countdown in the demo: use a JavaScript interval to decrement a fake countdown from 30→0, updating `stroke-dashoffset` and the countdown text. Reset to 30 and generate a new fake 6-digit code when it hits 0. This makes the demo feel alive.347348Surface B is reached by clicking "Stripe" in the vault list (Surface C).349350---351352## 8. Surface C — Vault list353354```html355<div class="peach-overlay-view ov-enter-up" id="hd-view-list" style="display:flex">356 357 <div class="peach-list-top">358 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-list-close">359 360 </button>361 <input class="peach-search-input" placeholder="search vault..." style="font-size:14px" id="hd-list-search">362 <span class="peach-search-counter peach-search-counter-list">6 entries</span>363 </div>364365 366 <div class="peach-sort-bar">367 <button class="peach-sort-btn is-active">smart</button>368 <button class="peach-sort-btn">A–Z</button>369 <button class="peach-sort-btn">Z–A</button>370 <button class="peach-sort-btn">newest</button>371 <span class="peach-sort-domain-hint">github.com</span>372 </div>373374 375 <div class="peach-vault-list">376 377 <button class="peach-entry-row is-active is-tab-match" style="animation-delay:0ms">378 <div class="peach-entry-row-icon">379 <img src="https://icons.duckduckgo.com/ip3/github.com.ico" width="16" height="16" style="border-radius:3px" alt="">380 </div>381 <div class="peach-entry-row-info">382 <span class="peach-entry-row-name">383 GitHub384 <span class="peach-tab-match-badge" title="Matches current tab">●</span>385 </span>386 <span class="peach-entry-row-meta">github.com</span>387 </div>388 </button>389390 391 <button class="peach-entry-row" style="animation-delay:20ms" id="hd-list-stripe">392 <div class="peach-entry-row-icon">393 <img src="https://icons.duckduckgo.com/ip3/dashboard.stripe.com.ico" width="16" height="16" style="border-radius:3px" alt="">394 </div>395 <div class="peach-entry-row-info">396 <span class="peach-entry-row-name">Stripe</span>397 <span class="peach-entry-row-meta">dashboard.stripe.com</span>398 </div>399 </button>400401 402 <button class="peach-entry-row" style="animation-delay:40ms">403 <div class="peach-entry-row-icon is-breach">404 405 <svg width="11" height="12" viewBox="0 0 11 12" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">406 <rect x="2" y="5" width="7" height="5" rx="1.2"/>407 <path d="M3.5 5V3.75A2 2 0 0 1 5.5 1.75A2 2 0 0 1 7.5 3.75V5"/>408 </svg>409 </div>410 <div class="peach-entry-row-info">411 <span class="peach-entry-row-name">Netflix</span>412 <span class="peach-entry-row-meta">netflix.com</span>413 </div>414 <span class="peach-entry-row-dot" aria-label="breach"></span>415 </button>416417 <!-- Apple ID — passkey -->418 <button class="peach-entry-row" style="animation-delay:60ms">419 <div class="peach-entry-row-icon">420 <!-- passkey icon SVG -->421 <svg width="11" height="12" viewBox="0 0 11 12" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">422 <circle cx="4" cy="4.5" r="3"/>423 <path d="M6.5 7l3.5 3.5M8.5 8.5L10 10"/>424 </svg>425 </div>426 <div class="peach-entry-row-info">427 <span class="peach-entry-row-name">428 Apple ID429 <span class="peach-passkey-badge">passkey</span>430 </span>431 <span class="peach-entry-row-meta">apple.com</span>432 </div>433 </button>434435 <!-- Server SSH keys — note -->436 <button class="peach-entry-row" style="animation-delay:80ms">437 <div class="peach-entry-row-icon">438 <!-- note icon SVG -->439 <svg width="11" height="12" viewBox="0 0 11 12" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">440 <rect x="1.5" y="1.5" width="8" height="9" rx="1.5"/>441 <path d="M3.5 4.5h4M3.5 6.5h4M3.5 8.5h2"/>442 </svg>443 </div>444 <div class="peach-entry-row-info">445 <span class="peach-entry-row-name">Server SSH keys</span>446 <span class="peach-entry-row-meta">prod: ssh -i ~/.ssh/p...</span>447 </div>448 </button>449450 <!-- Gmail — canary -->451 <button class="peach-entry-row" style="animation-delay:100ms">452 <div class="peach-entry-row-icon">453 <img src="https://icons.duckduckgo.com/ip3/mail.google.com.ico" width="16" height="16" style="border-radius:3px" alt="">454 </div>455 <div class="peach-entry-row-info">456 <span class="peach-entry-row-name">Gmail</span>457 <span class="peach-entry-row-meta">mail.google.com</span>458 </div>459 <span class="peach-entry-row-canary" title="Canary: honeypot">🪤</span>460 </button>461 </div>462463 <!-- New entry row -->464 <button class="peach-new-entry-row" id="hd-list-add">465 <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round">466 <path d="M5 1v8M1 5h8"/>467 </svg>468 new entry469 </button>470</div>471```472473### C — Interactive behaviors474475| Action | Effect |476|--------|--------|477| Type in search input | Filter rows client-side with JS (simple substring match on title/domain) |478| Click sort button | Toggle `.is-active` between sort buttons; demo doesn't need to re-sort, just show the UI working |479| Click "GitHub" row | Close list, show Surface A |480| Click "Stripe" row | Close list, show Surface B (with TOTP) |481| Click "Netflix" row | Close list, show breach version of entry detail (see §9) |482| Click "new entry" | Show Surface D |483| Click back / Esc | Close list, return to current entry detail |484485---486487## 9. Breach entry detail (Netflix)488489Same structure as Surface A, but with the breach strip rendered:490491```html492493<div class="peach-breach-badge">494 <span>⚠ 54,231 data breach exposures</span>495 <button class="peach-btn peach-btn--sm peach-btn--accent peach-breach-action">change password</button>496</div>497```498499Also the icon shows with `is-breach` class applied.500501---502503## 10. Surface D — Add new entry504505```html506<div class="peach-overlay-view ov-enter-right" id="hd-view-new" style="display:flex">507 <div class="peach-overlay-header">508 <button class="peach-btn peach-btn--subtle peach-btn--sm peach-overlay-action" id="hd-new-cancel">cancel</button>509 <span class="peach-overlay-title">new entry</span>510 <button class="peach-btn peach-btn--accent peach-btn--sm peach-overlay-action" id="hd-new-create" disabled>create</button>511 </div>512513 <div class="peach-new-body">514 515 <div class="peach-type-tabs">516 <button class="peach-type-tab is-active" id="hd-tab-login">login</button>517 <button class="peach-type-tab" id="hd-tab-note">note</button>518 </div>519520 521 <input class="peach-new-name-input" placeholder="entry name" id="hd-new-title">522523 524 <div class="peach-domain peach-new-domain">525 <input class="peach-domain-value peach-new-field-input" placeholder="https://..." id="hd-new-url">526 </div>527528 529 <section class="peach-fields peach-new-fields">530 <div class="peach-field-card">531 <span class="peach-label">username</span>532 <div class="peach-field-value-row">533 <input class="peach-field-value peach-new-field-input" type="text" placeholder="username or email" id="hd-new-username" autocomplete="off">534 </div>535 </div>536537 <div class="peach-field-card">538 <span class="peach-label">password</span>539 <div class="peach-field-value-row">540 <input class="peach-field-value peach-new-field-input" type="password" placeholder="password" id="hd-new-password" autocomplete="new-password">541 542 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-new-generate" title="Generate password">543 544 <svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round">545 <rect x="1.5" y="1.5" width="10" height="10" rx="2"/>546 <circle cx="4.5" cy="4.5" r="1" fill="currentColor" stroke="none"/>547 <circle cx="8.5" cy="8.5" r="1" fill="currentColor" stroke="none"/>548 <circle cx="4.5" cy="8.5" r="1" fill="currentColor" stroke="none"/>549 <circle cx="8.5" cy="4.5" r="1" fill="currentColor" stroke="none"/>550 <circle cx="6.5" cy="6.5" r="1" fill="currentColor" stroke="none"/>551 </svg>552 </button>553 </div>554 <!-- Strength hairline (shown when password has content) -->555 <div class="peach-strength-hairline" id="hd-strength-bar" style="display:none">556 <div class="peach-strength-hairline-track">557 <div class="peach-strength-hairline-fill" id="hd-strength-fill" style="width:0%"></div>558 </div>559 <span class="peach-strength-label" id="hd-strength-label"></span>560 </div>561 </div>562563 <div class="peach-field-card">564 <span class="peach-label">totp secret</span>565 <div class="peach-field-value-row">566 <input class="peach-field-value peach-new-field-input" type="text" placeholder="otpauth:// or base32" id="hd-new-totp" autocomplete="off">567 </div>568 </div>569570 <div class="peach-field-card">571 <span class="peach-label">notes</span>572 <div class="peach-field-value-row">573 <textarea class="peach-new-textarea" rows="3" placeholder="optional notes" id="hd-new-notes"></textarea>574 </div>575 </div>576 </section>577578 <!-- Tags -->579 <div class="peach-field-card" style="margin-top:12px">580 <span class="peach-label">tags</span>581 <div class="peach-tag-input-wrap">582 <input class="peach-tag-input" placeholder="add tags..." id="hd-new-tags">583 </div>584 <div class="peach-tag-suggestions" style="margin-top:4px">585 <button class="peach-tag-suggestion">work</button>586 <button class="peach-tag-suggestion">personal</button>587 <button class="peach-tag-suggestion">dev</button>588 </div>589 </div>590 </div>591</div>592```593594### D — Interactive behaviors595596| Action | Effect |597|--------|--------|598| Click "note" tab | Toggle tab active state; hide url/username/password/totp fields; show note textarea |599| Click "login" tab | Toggle back; restore login fields |600| Type in password field | Show strength bar; update fill width and label text (simple heuristic OK in demo) |601| Click generate button | Fill password field with a random 16-char password; trigger strength bar update |602| All fields filled | Enable "create" button |603| Click "create" | Briefly show "saving…" then return to entry detail with a success animation |604| Click "cancel" | Close overlay, return to entry detail |605606---607608## 11. Surface E — Settings609610```html611<div class="peach-overlay-view ov-enter-right" id="hd-view-settings" style="display:flex">612 <div class="peach-overlay-header">613 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-settings-back"></button>614 <span class="peach-overlay-title">settings</span>615 <div class="peach-overlay-spacer"></div>616 </div>617618 <div class="peach-settings-body">619620 621 <div class="peach-settings-group">622 <p class="peach-settings-label">security</p>623 <div class="peach-settings-row">624 <span>Lock on close</span>625 <button class="peach-toggle is-on"><span class="peach-toggle-knob"></span></button>626 </div>627 <div class="peach-settings-row">628 <span>Breach monitoring</span>629 <button class="peach-toggle is-on"><span class="peach-toggle-knob"></span></button>630 </div>631 <div class="peach-settings-row peach-settings-row--meta">632 <span>Check all passwords</span>633 <button class="peach-btn peach-btn--subtle peach-btn--sm">run</button>634 </div>635 <div class="peach-settings-row peach-settings-row--meta">636 <span>Add canary entry</span>637 <button class="peach-btn peach-btn--subtle peach-btn--sm">open</button>638 </div>639 </div>640641 642 <div class="peach-settings-group">643 <p class="peach-settings-label">duress password</p>644 <div class="peach-settings-row peach-settings-row--top">645 <div class="peach-settings-row-main">646 <span>Duress password</span>647 <span class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--t2)">648 If entered under coercion, shows a decoy empty vault instead of your real one.649 </span>650 </div>651 <div class="peach-duress-status">off</div>652 </div>653 <div class="peach-settings-row peach-settings-row--meta">654 <button class="peach-btn peach-btn--subtle peach-btn--sm">Set up</button>655 </div>656 </div>657658 659 <div class="peach-settings-group">660 <p class="peach-settings-label">passkeys</p>661 <div class="peach-settings-row peach-settings-row--top">662 <div class="peach-settings-row-main">663 <span>Intercept passkey requests</span>664 <span class="peach-settings-value" style="font-size:11px;color:var(--t2)">665 Peach can handle passkey sign-in and creation on supported Chromium browsers.666 </span>667 </div>668 <button class="peach-toggle is-on"><span class="peach-toggle-knob"></span></button>669 </div>670 <div class="peach-settings-row peach-settings-row--meta">671 <span>Status</span>672 <span class="peach-settings-value">Active</span>673 </div>674 </div>675676 677 <div class="peach-settings-group" style="position:relative">678 <p class="peach-settings-label">sync</p>679 <div class="peach-sync-pro-gate">680 <div class="peach-sync-pro-gate-blur">681 <div class="peach-settings-row">682 <span>Status</span>683 <span class="peach-settings-value peach-settings-value--status">684 <span class="peach-sync-status-dot" style="background:var(--peach-sync-off)"></span>685 Offline686 </span>687 </div>688 </div>689 <div class="peach-sync-pro-gate-overlay">690 <span class="peach-sync-pro-badge">Pro</span>691 <span style="font-size:13px;color:rgba(255,255,255,0.7)">Sync across all your devices</span>692 <a href="https://peachpasswords.com/#pricing" target="_blank" rel="noopener noreferrer"693 class="peach-link" style="color:#D96830;font-size:13px">Upgrade to Pro →</a>694 </div>695 </div>696 </div>697698 699 <div class="peach-settings-group">700 <p class="peach-settings-label">vault</p>701 <div class="peach-settings-row peach-settings-row--meta">702 <span>Entries</span>703 <span class="peach-settings-value">6</span>704 </div>705 <div class="peach-settings-row peach-settings-row--meta">706 <span>Theme</span>707 <button class="peach-btn peach-btn--subtle peach-btn--sm" id="hd-settings-theme">light</button>708 </div>709 <div class="peach-settings-row peach-settings-row--meta">710 <span>Vault Health</span>711 <button class="peach-btn peach-btn--subtle peach-btn--sm" id="hd-settings-health">open</button>712 </div>713 </div>714715 716 <div class="peach-settings-footer">717 <span>Peach 1.4.12</span>718 <span class="peach-divider"></span>719 <span>Browser extension</span>720 </div>721722 </div>723</div>724```725726### E — Interactive behaviors727728| Action | Effect |729|--------|--------|730| Toggle "Lock on close" | Toggle `.is-on` class on button |731| Toggle "Breach monitoring" | Toggle `.is-on` |732| Toggle "Intercept passkey requests" | Toggle `.is-on` |733| Click "Theme" | Switch demo between dark/light by toggling `.light` on `#hd-root` |734| Click "Vault Health" → open | Show Surface F |735| Click back | Close settings, return to entry detail |736737---738739## 12. Surface F — Vault Health740741```html742<div class="peach-overlay-view ov-enter-right" id="hd-view-health" style="display:flex">743 <div class="peach-overlay-header">744 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn" id="hd-health-back"></button>745 <span class="peach-overlay-title">vault health</span>746 <div class="peach-overlay-spacer"></div>747 </div>748749 <div class="peach-health-body">750 <div class="peach-health-summary">751 <span class="peach-health-summary-warn">2 entries need attention</span>752 </div>753 <div class="peach-health-check-row">754 <button class="peach-btn peach-btn--accent peach-btn--sm peach-health-check-btn">755 Check all breaches now756 </button>757 </div>758 <div class="peach-health-sections">759760 761 <div class="peach-health-section">762 <button class="peach-health-section-header">763 <span class="peach-health-section-title">Breached</span>764 <span class="peach-health-section-count">1</span>765 <svg width="8" height="5" viewBox="0 0 8 5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round">766 <path d="M1 1l3 3 3-3"/>767 </svg>768 </button>769 <div class="peach-health-section-body">770 <div class="peach-health-row">771 <div class="peach-health-row-info">772 <span class="peach-health-row-name">Netflix</span>773 <span class="peach-health-row-meta" style="color:var(--peach-breach)">54,231 exposures</span>774 </div>775 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-health-fix-btn">Fix</button>776 </div>777 </div>778 </div>779780 <!-- Reused section (0) -->781 <div class="peach-health-section">782 <button class="peach-health-section-header">783 <span class="peach-health-section-title">Reused</span>784 <span class="peach-health-section-count">0</span>785 <svg width="8" height="5" viewBox="0 0 8 5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round">786 <path d="M1 1l3 3 3-3"/>787 </svg>788 </button>789 <div class="peach-health-section-body" style="display:none">790 <div class="peach-list-empty"><span>No reused passwords</span></div>791 </div>792 </div>793794 <!-- Weak section (1 entry — Netflix) -->795 <div class="peach-health-section">796 <button class="peach-health-section-header">797 <span class="peach-health-section-title">Weak</span>798 <span class="peach-health-section-count">1</span>799 <svg width="8" height="5" viewBox="0 0 8 5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round">800 <path d="M1 1l3 3 3-3"/>801 </svg>802 </button>803 <div class="peach-health-section-body">804 <div class="peach-health-row">805 <div class="peach-health-row-info">806 <span class="peach-health-row-name">netflix.com</span>807 <span class="peach-health-row-meta">score: 0 (very weak)</span>808 </div>809 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-health-fix-btn">Fix</button>810 </div>811 </div>812 </div>813814 <!-- Old section (0) -->815 <div class="peach-health-section">816 <button class="peach-health-section-header">817 <span class="peach-health-section-title">Old</span>818 <span class="peach-health-section-count">1</span>819 <svg width="8" height="5" viewBox="0 0 8 5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round">820 <path d="M1 1l3 3 3-3"/>821 </svg>822 </button>823 <div class="peach-health-section-body">824 <div class="peach-health-row">825 <div class="peach-health-row-info">826 <span class="peach-health-row-name">Netflix</span>827 <span class="peach-health-row-meta">last changed: over 1 year ago</span>828 </div>829 <button class="peach-btn peach-btn--ghost peach-btn--sm peach-health-fix-btn">Fix</button>830 </div>831 </div>832 </div>833834 </div>835 </div>836</div>837```838839### F — Interactive behaviors840841| Action | Effect |842|--------|--------|843| Click section header | Collapse/expand that section body (toggle `display:none`) |844| Click "Check all breaches" | Brief "checking…" label then return to "2 entries need attention" |845| Click back | Return to Settings |846847---848849## 13. Surface G — Lock screen (unlock)850851```html852<section class="peach-screen peach-theme lock-screen lock-screen--vault-stage" id="hd-view-lock">853 <header class="peach-screen-header lock-screen-header">854 855 <div class="peach-wordmark-wrap">856 <svg class="peach-brand-mark" ...></svg>857 <span class="peach-wordmark-text">peach</span>858 </div>859 </header>860861 <div class="peach-lock-hero peach-lock-hero--unlock">862 <div class="peach-lock-hero-art" aria-hidden="true">863 864 </div>865 <span class="peach-lock-eyebrow">vault locked</span>866 <h1 class="peach-onboarding-headline peach-lock-headline--unlock">867 unlock<br>your vault.868 </h1>869 <p class="peach-subline peach-subline--lock">870 Everything stays local and encrypted until you unlock.871 </p>872 </div>873874 <section class="peach-lock-panel peach-lock-panel--unlock">875 <label class="peach-label" for="hd-unlock-password">master password</label>876 <input id="hd-unlock-password"877 class="peach-underline-input peach-unlock-input lock-password-input"878 type="password"879 placeholder="master password"880 autocomplete="current-password">881 <button class="peach-btn peach-btn--accent peach-btn--md peach-lock-submit" id="hd-unlock-btn" disabled>882 unlock883 </button>884 <p class="peach-hint">encrypted vault · stored locally</p>885 </section>886</section>887```888889### G — Interactive behaviors890891| Action | Effect |892|--------|--------|893| Type in password field | Enable "unlock" button when field is non-empty |894| Click "unlock" | Show brief "unlocking…" state then transition back to Surface A with spring animation |895| Toggle button inside demo | Show Surface H (onboarding/new vault) instead |896897---898899## 14. Surface H — Lock screen (onboarding / new vault)900901```html902<section class="peach-screen peach-theme lock-screen lock-screen--vault-stage lock-screen--onboarding" id="hd-view-onboarding">903 <header class="peach-screen-header lock-screen-header">904 <div class="peach-wordmark-wrap"></div>905 </header>906907 <div class="peach-lock-hero">908 <div class="peach-lock-hero-art" aria-hidden="true"></div>909 <span class="peach-lock-eyebrow">new vault</span>910 <h1 class="peach-onboarding-headline">911 your secrets,<br>kept close.912 </h1>913 <p class="peach-subline peach-subline--lock">914 Create a master password to encrypt your vault. Or restore from an existing backup.915 </p>916 </div>917918 <section class="peach-lock-panel peach-lock-panel--primary">919 <label class="peach-label" for="hd-create-pw">master password</label>920 <input id="hd-create-pw" class="peach-underline-input peach-unlock-input lock-password-input"921 type="password" placeholder="master password" autocomplete="new-password">922923 <label class="peach-label" for="hd-create-confirm">confirm password</label>924 <input id="hd-create-confirm" class="peach-underline-input peach-unlock-input"925 type="password" placeholder="confirm password" autocomplete="new-password">926927 928 <div class="peach-password-strength" id="hd-create-strength" style="display:none">929 <div class="peach-strength-bar peach-strength-bar--fair">930 <div class="peach-strength-fill"></div>931 </div>932 <span class="peach-strength-label">fair</span>933 </div>934935 <button class="peach-btn peach-btn--accent peach-btn--md peach-lock-submit" id="hd-create-btn" disabled>936 create vault937 </button>938 <p class="peach-hint">If you lose this password, Peach cannot recover your vault.</p>939940 <div class="peach-lock-panel-footer">941 <p class="peach-hint">Already have a backup?</p>942 <div class="peach-onboarding-alt-actions">943 <button class="peach-lock-restore-link">restore from CJK phrase</button>944 <button class="peach-lock-restore-link">import from backup file</button>945 </div>946 </div>947 </section>948</section>949```950951### H — Interactive behaviors952953| Action | Effect |954|--------|--------|955| Type in master password | Show strength bar; animate fill; update label (weak/fair/good/strong) |956| Both fields match, ≥8 chars | Enable "create vault" button |957| Click "create vault" | Show "creating…" then transition to Surface A |958959---960961## 15. JavaScript interactivity sketch962963All demo interactivity lives in a single `<script>` tag at the bottom of `HeroDemo.astro`. Keep it simple — no framework.964965```js966967const surfaces = {968 detail: document.getElementById('hd-shell'),969 list: document.getElementById('hd-view-list'),970 add: document.getElementById('hd-view-new'),971 settings: document.getElementById('hd-view-settings'),972 health: document.getElementById('hd-view-health'),973 lock: document.getElementById('hd-view-lock'),974 onboarding: document.getElementById('hd-view-onboarding'),975};976977let activeSurface = 'detail';978979function show(id) {980 981 Object.values(surfaces).forEach(s => s && (s.style.display = 'none'));982 const el = surfaces[id];983 if (el) {984 el.style.display = 'flex';985 986 el.classList.remove('ov-enter-up', 'ov-enter-right');987 void el.offsetWidth;988 if (id === 'list') el.classList.add('ov-enter-up');989 else if (['add','settings','health'].includes(id)) el.classList.add('ov-enter-right');990 activeSurface = id;991 }992}993994995function animateCopy(btn) {996 btn.classList.add('is-copied');997 setTimeout(() => btn.classList.remove('is-copied'), 1500);998}99910001001let totpInterval = null;1002let totpRemaining = 18;10031004function startTotpCountdown() {1005 clearInterval(totpInterval);1006 totpInterval = setInterval(() => {1007 totpRemaining--;1008 if (totpRemaining <= 0) {1009 totpRemaining = 30;1010 1011 const newCode = String(Math.floor(100000 + Math.random() * 900000));1012 const formatted = newCode.slice(0,3) + ' ' + newCode.slice(3);1013 document.getElementById('hd-totp-code')?.textContent = formatted;1014 }1015 const el = document.getElementById('hd-totp-remaining');1016 if (el) el.textContent = totpRemaining + 's';1017 1018 const circumference = 106.8;1019 const progress = totpRemaining / 30;1020 const offset = circumference * (1 - progress);1021 document.querySelector('.peach-totp-ring-progress')1022 ?.style.setProperty('stroke-dashoffset', offset);1023 document.querySelector('.peach-totp-progress-bar')1024 ?.style.setProperty('width', (progress * 100) + '%');1025 }, 1000);1026}102710281029function getStrength(pw) {1030 if (!pw) return null;1031 let s = 0;1032 if (pw.length >= 8) s++;1033 if (/[A-Z]/.test(pw) && /[a-z]/.test(pw)) s++;1034 if (/\d/.test(pw)) s++;1035 if (/[^A-Za-z0-9]/.test(pw)) s++;1036 const labels = ['weak','fair','fair','good','strong'];1037 const widths = ['20%','40%','55%','75%','100%'];1038 return { label: labels[s], width: widths[s], cls: labels[s] };1039}1040104110421043```10441045---10461047## 16. Feature callouts beneath the demo10481049Below the interactive popup, add a row of feature callout chips that reinforce what was just seen. Each chip links to the relevant section on the page:10501051```html1052<div class="hd-feature-chips">1053 <span class="hd-chip">🔐 AES-256-GCM encryption</span>1054 <span class="hd-chip">🛡 HIBP breach monitoring</span>1055 <span class="hd-chip">🗝 Passkeys (WebAuthn)</span>1056 <span class="hd-chip">🟠 Canary honeypots</span>1057 <span class="hd-chip">🈶 CJK phrase backup</span>1058 <span class="hd-chip">🔄 Peach Sync (Pro)</span>1059 <span class="hd-chip">📊 Vault Health</span>1060 <span class="hd-chip">💡 Duress password</span>1061</div>1062```10631064---10651066## 17. Static screenshot surfaces10671068The following surfaces are too branched to demo interactively but should appear as static annotated screenshots in a "More features" section or carousel on the page:106910701. **Passkey approval** — the pop-up that appears when a site requests a passkey (`save a passkey?` / `sign in with passkey?`)10712. **CJK export** — the 2,000+ character Chinese Ideograph backup phrase output10723. **CJK import** — the phrase validation flow with HIBP check10734. **Sync pairing** — the 6-digit pairing code UI10745. **Autofill in-page** — screenshot of the injected Peach icon on a login field + identity-helper dropdown10756. **Duress password setup** — the settings flow for configuring the decoy vault10761077Produce these screenshots by loading the real built extension in a screenshot-capable Chrome instance.10781079---10801081## 18. Accessibility requirements10821083The demo is focusable and keyboard-navigable:1084- All interactive buttons have `aria-label` attributes1085- Copy confirmation is announced via `aria-live="polite"` region1086- Vault list rows are `<button>` elements (already the case in real code)1087- Password reveal toggle updates `aria-label` to reflect state ("Reveal password" / "Hide password")1088- TOTP countdown is wrapped in `aria-live="polite"` so screen readers can announce refresh10891090---10911092## 19. Sync checklist — keeping demo current10931094After every extension release, verify these items before shipping the website update:10951096- [ ] CSS — re-copy `vars.css` and `popup.css` from extension source1097- [ ] Version number in Surface E footer1098- [ ] Settings group order matches real Settings.tsx (currently: security → duress → passkeys → sync → vault → license)1099- [ ] Sort bar labels match real Vault.tsx (`smart`, `A–Z`, `Z–A`, `newest`)1100- [ ] Strength bar classes used in Surface H match real Lock.tsx (`peach-strength-bar--weak/fair/good/strong`)1101- [ ] Feature chip list reflects any new features added in the release1102- [ ] TOTP code animation ring physics (`stroke-dasharray: 106.8` matches `r=17`, circumference = 2π×17 ≈ 106.8)