diff --git a/docs/prd/README.md b/docs/prd/README.md index 21c7146..c033507 100644 --- a/docs/prd/README.md +++ b/docs/prd/README.md @@ -42,6 +42,32 @@ aimed at increasing amusement, engagement, and social sharing. 6. **What-If Simulator** — medium effort, highest shareability + return traffic 7. **Pledge to Reduce** — medium effort, longest return-visit tail +## Phase 3 — Virality & Joy Features + +| # | PRD | File | Summary | Shareability | Return-visit pull | Effort | +|---|-----|------|---------|-------------|-------------------|--------| +| 1 | Token Horoscope | `prd-token-horoscope.md` | Daily satirical AI horoscope seeded from UTC date; shareable + refreshes every 24 h | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ (daily) | Low | +| 2 | AI Guilt-O-Meter | `prd-guilt-o-meter.md` | Real-time guilt thermometer filling over 5 min; share your guilt level; unlocks Certified Hypocrite badge | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Low | +| 3 | Token Confessional | `prd-token-confessional.md` | Confess your worst AI sin; receive satirical absolution + shareable card; ephemeral (nothing leaves the browser) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Low | +| 4 | Token Exchange Rate Board | `prd-token-exchange-board.md` | Financial-terminal ticker showing live token-to-absurd-thing exchange rates reusing existing equivalences logic | ⭐⭐⭐⭐ | ⭐⭐ | Low | +| 5 | Doomscroll Bingo | `prd-doomscroll-bingo.md` | Weekly 5×5 bingo card of AI sins; confetti on BINGO; Full House of Doom badge; highly shareable card image | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ (weekly) | Medium | +| 6 | Name That Wasteful Prompt | `prd-name-that-prompt.md` | Daily slider game: guess how many tokens a silly fictional prompt used; Prompt Sommelier badge for 5 plays | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ (daily) | Medium | +| 7 | AI Autopsy Report | `prd-ai-autopsy-report.md` | On-unload satirical autopsy document for session tokens; printable HTML card; shareable as screenshot | ⭐⭐⭐⭐ | ⭐⭐ | Medium | +| 8 | Doom Postcard Generator | `prd-doom-postcard.md` | Canvas-rendered downloadable PNG postcard with chosen greeting, milestone, and live token count | ⭐⭐⭐⭐⭐ | ⭐⭐ | Medium | + +### Phase 3 Implementation Order + +1. **Token Horoscope** — low effort, strongest daily return-visit driver; daily seed + share button +2. **AI Guilt-O-Meter** — low effort, adds emotional persistence throughout the session; badge milestone +3. **Token Confessional** — low effort, highly relatable; shareable absolution card drives organic spread +4. **Token Exchange Rate Board** — low effort, reuses existing equivalences; visual punch for Dashboard +5. **Doomscroll Bingo** — medium effort, proven viral bingo format; weekly rotation drives D7 returns +6. **Name That Wasteful Prompt** — medium effort, daily curiosity-gap game; strong badge retention mechanic +7. **AI Autopsy Report** — medium effort, printable satirical artefact; complements Token Receipt +8. **Doom Postcard Generator** — medium effort, highest absolute shareability (downloadable image) + +--- + ## Shared Design Principles - **No new runtime npm packages** — the site must remain fully static diff --git a/docs/prd/prd-ai-autopsy-report.md b/docs/prd/prd-ai-autopsy-report.md new file mode 100644 index 0000000..f13d4e3 --- /dev/null +++ b/docs/prd/prd-ai-autopsy-report.md @@ -0,0 +1,177 @@ +# PRD: The AI Autopsy Report 🔬 + +## Overview + +At the end of a session — triggered by the `beforeunload` / `pagehide` event, +like the existing Token Receipt modal — the site generates a satirical +"autopsy report" for the tokens consumed during the visit. Styled as a mock +medical-examiner document, it includes absurdist fields like *"Cause of +Death," "Time of Death," "Surviving Dependents,"* and *"Forensic Notes."* +The report is rendered as a printable HTML card and can be saved as a +screenshot or printed by the user. + +--- + +## Problem + +The existing Token Receipt modal shows session stats in a thermal-receipt +format. That feature works well, but it is informational rather than +satirical. The Autopsy Report takes the same session data and re-frames it +through the lens of absurdist medical bureaucracy — a format that is +inherently funnier, more screenshot-worthy, and more socially shareable than +a receipt. + +--- + +## Goals + +- Create the site's most screenshot-ready artefact for social media sharing +- Add a printable leave-behind that users keep and share offline +- Complement (not replace) the existing Token Receipt with a parallel + satirical framing + +--- + +## Non-Goals + +- Replacing the Token Receipt (both can coexist — the Autopsy is an optional + alternative view) +- Accurate medical terminology (the absurdity is the point) +- Canvas/image rendering (HTML + CSS print stylesheet is sufficient) + +--- + +## Feature Description + +### Report Fields + +The report is formatted as a mock official document with the header: + +``` +OFFICE OF THE DIGITAL MEDICAL EXAMINER +AUTOPSY REPORT — DECEASED AI SESSION +CERTIFICATE No. [6-digit random number] +``` + +| Field | Value / Logic | +|---|---| +| **Decedent** | *"Global AI Token Pool (Session Portion)"* | +| **Date of Death** | Current date/time formatted as `DD MMM YYYY HH:MM UTC` | +| **Cause of Death** | Randomly selected from a pool of 20 causes (see below) | +| **Time of Death** | Session end timestamp | +| **Duration of Illness** | `[sessionDurationSeconds]` formatted as *"X minutes, Y seconds"* | +| **Tokens Consumed** | `sessionTokens` formatted with commas | +| **CO₂ Emitted** | `calculateEnvironmentalImpact(sessionTokens).co2Grams` + units | +| **Water Used** | Water equivalence in mL | +| **Energy Consumed** | kWh equivalence, formatted to 4 decimal places | +| **Surviving Dependents** | Randomly selected satirical line (see below) | +| **Forensic Notes** | Randomly selected note from pool of 15 | +| **Pathologist** | *"Dr. A.I. Burnout, MBBS (Digital), FRCGPU"* | +| **Signature** | ASCII-art signature line | + +**Cause of Death pool (20 items):** + +- *"Unnecessary synonym generation (acute)"* +- *"Chronic re-prompting with minor wording changes"* +- *"Compulsive birthday card outsourcing"* +- *"Acute image generation with no clear purpose"* +- *"Self-inflicted philosophical debate at 02:00"* +- *"Repeated 'make it more professional' injections"* +- *"Wikipedia summary avoidance disorder (terminal)"* +- *"Elective lunch decision paralysis"* +- *"Third-party apology text composition"* +- *"Experimental 'explain like I'm 5' overdose"* +- *(10 additional)* + +**Surviving Dependents pool (15 items):** + +- *"3 cached responses that were never read"* +- *"1 email draft that was deleted without sending"* +- *"47 GPU cycles, mourned by their families"* +- *"2 synonyms for 'utilise' that were not utilised"* +- *"The original, un-AI'd version of your text"* +- *(10 additional)* + +**Forensic Notes pool (15 items):** + +- *"No foul play suspected. Victim was a willing participant."* +- *"Deceased showed no signs of having used a search engine."* +- *"Evidence of repeated 'regenerate' button activation."* +- *"Time of arrival on site inconsistent with stated 'quick look'."* +- *(11 additional)* + +### Trigger Mechanism + +Two complementary triggers (same pattern as Token Receipt): + +1. **Passive:** `pagehide` event after ≥ 30 seconds on page — the report modal + opens automatically, giving the user a chance to screenshot before leaving +2. **Active:** A **"🔬 Generate Autopsy Report"** button is always visible in + the Dashboard tab footer (visible after 60 seconds) + +The modal has a **"Print / Save as PDF"** button that calls `window.print()`. +A `@media print` CSS block hides everything except the report card, so the +browser's print-to-PDF produces a clean document. + +### Visual Design + +- White card on a dark overlay (modal backdrop) +- Header in a serif font (or `font-family: Georgia, serif` fallback) +- Field labels in uppercase, values in normal case +- A horizontal divider line between sections +- Faint watermark text: *"CONFIDENTIAL — FOR SATIRICAL PURPOSES ONLY"* +- Certificate number in monospace font, top-right corner + +### Share Button + +> 🔬 I just received my AI Autopsy Report. +> Cause of death: "[cause]" +> Tokens consumed: [N] | Session: [duration] +> Get yours: → [URL] #TokenDeathClock #AIAutopsy + +--- + +## Architecture Notes + +| Layer | Change | +|-------|--------| +| `death-clock-core.js` | Add `AUTOPSY_CAUSES`, `AUTOPSY_DEPENDENTS`, `AUTOPSY_NOTES` constant arrays; add `generateAutopsyReport(sessionData, nowMs)` returning a plain object with all report fields | +| `index.html` | Add `
` (hidden); add trigger button in dashboard footer | +| `src/js/` | New `28-autopsy.js`: `initAutopsy()`, `openAutopsyReport()`, `renderAutopsyReport(data)`, print button handler | +| `styles/` | New rules in `features.css`: `.autopsy-modal`, `.autopsy-card`, `.autopsy-header`, `.autopsy-field`, `@media print` rules | + +All dynamic report fields use `textContent` — the `generateAutopsyReport` +function returns a plain object, and the renderer maps fields to DOM nodes via +`textContent` assignments. + +--- + +## UX / Accessibility + +- Modal follows ARIA modal pattern: `role="dialog"`, `aria-modal="true"`, + `aria-labelledby` pointing to the "AUTOPSY REPORT" heading +- Focus trapped inside modal; `Escape` closes it +- Print button has `aria-label="Print or save the autopsy report as PDF"` +- `prefers-reduced-motion`: modal appears without fade animation +- The `@media print` stylesheet hides the modal overlay/backdrop and renders + only the card for clean PDF output + +--- + +## Success Metrics + +- Autopsy modal open rate +- Print/PDF button click rate +- Share button click rate +- Social posts containing "#AIAutopsy" + +--- + +## Open Questions + +- Should the Autopsy Report and Token Receipt coexist as separate modals, or + should one be a "style" toggle of the other? (Separate modals preferred — + each serves a different tone) +- Should a unique "Certificate Number" be shareable as a URL hash to let users + retrieve "their" report later? (v2 — would require deterministic seeding from + session data) diff --git a/docs/prd/prd-doom-postcard.md b/docs/prd/prd-doom-postcard.md new file mode 100644 index 0000000..f0ed972 --- /dev/null +++ b/docs/prd/prd-doom-postcard.md @@ -0,0 +1,193 @@ +# PRD: Doom Postcard Generator 🖼️ + +## Overview + +A Canvas API-rendered "doom postcard" that the user can customise with a +satirical greeting, today's global token count, and a chosen milestone. The +postcard is rendered entirely client-side (no image libraries, no backend), +and the user can download it as a PNG or copy its share text. Downloadable +images are shared on social media at a significantly higher rate than text +links, making this the site's highest-reach potential feature. + +--- + +## Problem + +All current sharing mechanisms produce text. Text posts are lower-engagement +on social platforms than image posts. A Canvas-rendered image that can be +downloaded and posted directly to Instagram, X, LinkedIn, or Mastodon bypasses +the link-preview problem entirely and keeps the site's branding and message +visible even when the link is stripped. + +--- + +## Goals + +- Create the highest-shareability single feature on the site +- Produce a visually distinctive, screenshot-ready image with zero dependencies +- Keep all rendering entirely client-side (no upload, no cloud render) + +--- + +## Non-Goals + +- Server-side image generation +- GIF or animated export (v2) +- Fonts beyond system/Canvas defaults (v2 — font loading via FontFace API adds + complexity) + +--- + +## Feature Description + +### Postcard Layout (800 × 500 px canvas) + +``` +┌─────────────────────────────────────┬──────────────────┐ +│ │ TOKEN │ +│ [GREETING TEXT] │ DEATHCLOCK │ +│ │ 🌍 │ +│ [STAT LINE 1] │ [QR-like art] │ +│ [STAT LINE 2] │ │ +│ │ nitrocode. │ +│ [Milestone name] reached at: │ github.io/ │ +│ [Milestone token count] │ token- │ +│ │ deathclock │ +│ token-deathclock │ │ +└─────────────────────────────────────┴──────────────────┘ +``` + +- Left panel: dark background (`#0d1117` or theme equivalent), white/green text +- Right panel: slightly lighter dark background, site branding, URL +- A subtle noise/grain texture via `ctx.putImageData` for a vintage postcard + feel +- A thin coloured border matching the chosen milestone's `color` property + +### User Customisation Options + +Presented in a panel above the canvas preview: + +| Option | Type | Choices | +|---|---|---| +| **Greeting** | Dropdown | 10 preset satirical greetings (see below) | +| **Milestone** | Dropdown | All milestones from `MILESTONES` array | +| **Stat to highlight** | Dropdown | CO₂, water, electricity, "tokens today" | +| **Theme** | Toggle | Dark (default) / Light | + +**Preset Greetings (10):** + +1. *"Wish you were here — unfortunately the servers are on fire 🔥"* +2. *"Greetings from the age of automated mediocrity"* +3. *"Having a wonderful time. The data centres disagree."* +4. *"Wish you were here. The glaciers wish they were too."* +5. *"Sending you [N] tokens of love (uninvited)"* +6. *"The machines are thinking about you. Constantly."* +7. *"Don't worry, the AI is handling everything. That's the problem."* +8. *"Postcards from the algorithm: wish you'd typed less"* +9. *"The planet called. It said 'please stop.'"* +10. *"You are here. The tokens have already left."* + +### Canvas Rendering + +The `renderPostcard(ctx, options)` function (in `src/js/`) performs: + +1. Fill backgrounds (left and right panels) +2. Draw border rectangle using `options.milestoneColor` +3. Fill greeting text using `ctx.fillText()` with word-wrap helper +4. Fill stat lines (token count, equivalence) +5. Fill milestone name and token threshold +6. Fill branding text (right panel) +7. Draw a simple ASCII-art decoration in the right panel (a 🌍 emoji rendered + via `ctx.fillText` is sufficient) +8. Apply grain texture: loop over a small tile of `putImageData` with random + ±5 RGB noise, tiled across the canvas + +All text uses `ctx.font` with a system monospace font stack: +`'Courier New', Courier, monospace`. + +### Download + +*"Download Postcard 📥"* button calls: + +```js +const link = document.createElement('a'); +link.download = 'token-deathclock-postcard.png'; +link.href = canvas.toDataURL('image/png'); +link.click(); +``` + +No libraries, no server round-trip. + +### Share Button + +Shown alongside the download button: + +> 🖼️ I generated a doom postcard on the Token Deathclock. +> [Greeting text] → [URL] #TokenDeathClock #AIPostcard + +--- + +## Architecture Notes + +| Layer | Change | +|-------|--------| +| `death-clock-core.js` | No changes needed; postcard renderer uses already-exported `MILESTONES`, `getCurrentTokens()`, `calculateEnvironmentalImpact()` | +| `index.html` | Add `
` with options panel, ``, download button, share button | +| `src/js/` | New `29-postcard.js`: `initPostcard()`, `renderPostcard(ctx, options)`, `downloadPostcard()`, word-wrap helper `wrapText(ctx, text, x, y, maxWidth, lineHeight)` | +| `styles/` | New rules in `features.css`: `.postcard-section`, `.postcard-options`, `.postcard-canvas-wrap` | + +### Word Wrap Helper + +```js +function wrapText(ctx, text, x, y, maxWidth, lineHeight) { + const words = text.split(' '); + let line = ''; + for (const word of words) { + const test = line + (line ? ' ' : '') + word; + if (ctx.measureText(test).width > maxWidth && line) { + ctx.fillText(line, x, y); + line = word; + y += lineHeight; + } else { + line = test; + } + } + if (line) ctx.fillText(line, x, y); +} +``` + +--- + +## UX / Accessibility + +- The `` element has `role="img"` and `aria-label` updated dynamically + to describe the current postcard content (e.g., *"Doom postcard: 'Wish you + were here — unfortunately the servers are on fire' with [N] tokens milestone"*) +- Download button has a clear `aria-label` +- Options panel uses standard `` with `aria-label`, `aria-valuemin`, + `aria-valuemax`, `aria-valuenow`, and an associated `` element +- Reveal card uses `aria-live="assertive"` to announce the result to screen + readers +- All dynamic text uses `textContent`, never `innerHTML` +- `prefers-reduced-motion`: reveal animation is instant (no flip/slide) +- Dark + light theme: prompt card uses existing CSS custom property colour + tokens + +--- + +## Success Metrics + +- `prompt_sommelier` badge earn rate (indicates 5+ return visits) +- `perfect_palate` badge earn rate (indicates engagement quality) +- Share button click rate +- Social posts containing "#NameThatPrompt" + +--- + +## Open Questions + +- Should there be a "Hard Mode" with a wider slider range (0–100,000) and + more complex prompts? (v2) +- Should there be a weekly challenge prompt that persists for 7 days instead + of rotating daily? (v2) diff --git a/docs/prd/prd-token-confessional.md b/docs/prd/prd-token-confessional.md new file mode 100644 index 0000000..0f5bfdb --- /dev/null +++ b/docs/prd/prd-token-confessional.md @@ -0,0 +1,172 @@ +# PRD: The Token Confessional 🙏 + +## Overview + +A modal "confessional booth" where visitors admit their most wasteful AI +prompt sin, either by typing a free-form confession or selecting from a +dropdown of pre-written sins. Upon confessing, the site calculates an estimate +of how many tokens that sin likely consumed, presents the environmental +equivalent, and grants a satirical "absolution" message. The absolution card +is shareable as text. Nothing is stored or transmitted — confessions are +completely ephemeral. + +--- + +## Problem + +The site quantifies global token waste in the abstract, but users do not +naturally connect the large numbers to their own individual behaviour. A +confession mechanic bridges that gap: it asks the user to name a specific +guilty behaviour, making the personal cost concrete and memorable. The +absurdist "absolution" creates an emotional release that is inherently +shareable. + +--- + +## Goals + +- Make the environmental cost of individual AI usage feel personal, not + abstract +- Create a highly shareable "absolution card" moment +- Award a badge that rewards honest self-reflection (and encourages others to + try it) + +--- + +## Non-Goals + +- Storing, transmitting, or aggregating confessions (privacy requirement: + nothing leaves the browser) +- Penalising or shaming users — this is satirical and warm, not punitive +- Requiring a sign-in + +--- + +## Feature Description + +### Confession Modal + +Triggered by a **"🙏 Enter the Confessional"** button in the Dashboard tab +(placed near the Personal Footprint Calculator). + +The modal contains: + +1. **Dropdown of preset sins** (with option to type a custom one): + + | # | Sin | Token estimate | + |---|-----|----------------| + | 1 | "Asked AI to write an email I could have written in 2 minutes" | ~400 tokens | + | 2 | "Ran the same prompt 5+ times hoping for a different answer" | ~3,000 tokens | + | 3 | "Used AI to name my WiFi network" | ~200 tokens | + | 4 | "Generated 20+ images to find one I liked" | ~50,000 tokens | + | 5 | "Asked AI to summarise a video I hadn't watched" | ~1,500 tokens | + | 6 | "Let AI write a birthday card for someone I love" | ~300 tokens | + | 7 | "Had a 2-hour philosophical debate with a chatbot" | ~120,000 tokens | + | 8 | "Used AI to decide what to have for lunch" | ~250 tokens | + | 9 | "Asked AI to 'make it pop'" | ~500 tokens | + | 10 | "Generated code, didn't understand it, shipped it anyway" | ~2,000 tokens | + | 11 | "I have sinned in ways I cannot name" | ~999,999 tokens | + | (custom) | Free-text input (max 200 chars) | ~1,000 tokens (flat estimate) | + +2. **"Confess 🙏"** button + +### Absolution Card + +After confessing, the modal transitions to an absolution screen: + +``` +┌─────────────────────────────────────────┐ +│ 🕯️ ABSOLUTION GRANTED 🕯️ │ +│ │ +│ Your sin: "[confession text]" │ +│ │ +│ Estimated cost: ~[X] tokens │ +│ That's roughly: [equivalence] │ +│ │ +│ "You are forgiven. For penance, │ +│ [satirical penance]." │ +│ │ +│ [Share My Absolution] [Confess Again] │ +└─────────────────────────────────────────┘ +``` + +**Penance messages** (one randomly selected from a pool of 15): + +- *"use a search engine once this week"* +- *"read the actual documentation"* +- *"write one email without AI assistance"* +- *"let a human summarise something for you"* +- *"sit with a question unanswered for five minutes"* +- *"close three browser tabs you opened with AI help"* +- *"use a dictionary"* +- *"think before you type"* +- *"drink a glass of water instead of running another prompt"* + +**Equivalence** is drawn from the existing `sessionEquivalences()` helper in +`death-clock-core.js`, called with the sin's token estimate. + +### Badge + +| Badge ID | Icon | Name | Trigger | +|---|---|---|---| +| `confessed_sinner` | 🙏 | *Confessed Sinner* | First completed confession | +| `repeat_offender` | 😈 | *Repeat Offender* | 3 confessions in a single session | + +### Share Button + +Share text (assembled in JS, nothing from free-text user input is included +in the URL or share text for privacy): + +> 🙏 I just confessed my AI sins at the Token Deathclock. +> My absolution: "[penance message]" +> What's YOUR worst AI sin? → [URL] #TokenDeathClock #AIConfessional + +Note: The actual confession text is **never** included in the share text to +protect user privacy. Only the penance message is shared. + +--- + +## Architecture Notes + +| Layer | Change | +|-------|--------| +| `death-clock-core.js` | Add `CONFESSION_SINS` array (preset sins with token estimates); add `PENANCE_MESSAGES` array; add `getAbsolution(sinTokens, nowMs)` returning `{ equivalence, penance }` | +| `index.html` | Add `
` (hidden by default) with confession form and absolution card; add trigger button | +| `src/js/` | New `24-confessional.js`: `initConfessional()`, `openConfessional()`, `submitConfession()`, `showAbsolution()`, `closeConfessional()` | +| `styles/` | New rules in `features.css`: `.confessional-modal`, `.confessional-backdrop`, `.absolution-card`, `.penance-text` | + +### Security / Privacy + +- Free-text confession input is sanitised via `escHtml()` before any display +- Free-text is **never** included in share text or URLs +- No data leaves the browser; no `fetch()` or `beacon()` calls + +--- + +## UX / Accessibility + +- Modal follows ARIA modal pattern: `role="dialog"`, `aria-modal="true"`, + `aria-labelledby` pointing to heading +- Focus is trapped inside the modal while open; `Escape` closes it +- On close, focus returns to the trigger button +- Absolution card text uses `textContent` throughout; the confession snippet + shown inside the card is escaped via `escHtml()` +- `prefers-reduced-motion`: no fade/slide transitions, modal appears instantly +- Dark + light theme fully supported via CSS custom properties + +--- + +## Success Metrics + +- `confessed_sinner` badge earn rate +- Share button click rate on the absolution card +- `repeat_offender` badge earn rate (indicates high fun / replayability) + +--- + +## Open Questions + +- Should the absolution card be canvas-rendered for easier social-media + screenshot sharing? (v2 — see Doom Postcard Generator PRD) +- Should there be a "global sin tally" shown in aggregate? (Requires a backend + counter — deferred indefinitely given the no-backend constraint) diff --git a/docs/prd/prd-token-exchange-board.md b/docs/prd/prd-token-exchange-board.md new file mode 100644 index 0000000..bf54043 --- /dev/null +++ b/docs/prd/prd-token-exchange-board.md @@ -0,0 +1,131 @@ +# PRD: Token Exchange Rate Board 💹 + +## Overview + +A "stock ticker"-style scrolling board displays live "exchange rates" between +AI tokens and absurd real-world things, updating every few seconds. Formatted +like a financial terminal with green-on-black aesthetic, it shows satirical +equivalences such as *"1 Haiku = 🌮 0.0003 Tacos"* and *"Right now AI is +generating 🥚 [N] eggs-worth of energy every second."* All data is calculated +client-side using the existing equivalences logic; no backend is required. + +--- + +## Problem + +The site already shows environmental equivalences in the "What Could We Have +Done Instead?" strip, but that section presents one item at a time and blends +into the page. The token economy data is genuinely interesting and could be +presented in a much more visually engaging and repeatable way. A financial +terminal aesthetic creates a fun, ironic contrast between the sterile language +of finance and the environmental absurdity of AI token consumption. + +--- + +## Goals + +- Increase time spent on the Dashboard tab through an entertaining live + visual element +- Reuse existing equivalences logic without duplicating logic or adding + dependencies +- Create another "ticker" style visual element that rewards re-reads as the + numbers change in real time + +--- + +## Non-Goals + +- Actual financial data or real market prices +- Backend APIs or WebSocket connections +- User-customisable exchange items + +--- + +## Feature Description + +### Board Layout + +A horizontally scrolling marquee-style strip (or a vertically updating table — +both are acceptable; the scrolling marquee is preferred for visual dynamism). +It cycles through a list of "exchange rate" items, each on a line: + +``` +💹 TOKEN EXCHANGE BOARD — LIVE RATES + + GPT-4 haiku (100 tok) = 🌮 0.0003 tacos + Average ChatGPT reply = ☕ 0.008 cups of coffee + One full novel (90K tok) = 🌍 23 g CO₂ + Right now (this second) = 🥚 [N] eggs-worth of energy + Since you loaded this page = 🚗 [X] metres driven + Tokens since midnight = 💡 [Y] LED-hours +``` + +Numbers that include session or real-time data (eggs, metres, LED-hours) are +recomputed every **5 seconds** using `getCurrentTokens()` and +`calculateEnvironmentalImpact()` from the existing core module. + +### Exchange Rate Items + +| Label | Calculation | Update frequency | +|-------|-------------|-----------------| +| GPT-4 haiku (100 tok) | Static: 100 tokens ≈ energy to heat 3 mL water | Static | +| Average ChatGPT reply (~800 tok) | Static: 800 tok ≈ 0.0008 kWh | Static | +| Full novel (90K tok) | Static: 90K tok ≈ 23 g CO₂ | Static | +| Tokens this second | `TOKENS_PER_SECOND` static | Static | +| Tokens since you arrived | `sessionTokens` | Every 5 s | +| Global tokens today | `getCurrentTokens() - tokensAtUTCMidnight` | Every 5 s | +| Session CO₂ (mg) | `calculateEnvironmentalImpact(sessionTokens).co2Grams * 1000` | Every 5 s | + +### Visual Style + +- Black background panel (dark-mode aware: `var(--bg-terminal)` custom + property, fallback `#0a0a0a`) +- Green monospace text (`var(--color-terminal-green)`, fallback `#00ff41`) +- Amber for the live-updating numbers (`var(--color-terminal-amber)`, + fallback `#ffb300`) +- Header row: *"💹 TOKEN EXCHANGE BOARD — LIVE RATES"* in uppercase +- Each row: left-aligned label, right-aligned value with icon +- Subtle scanline CSS overlay using a `::before` pseudo-element repeating + gradient for the retro terminal look + +--- + +## Architecture Notes + +| Layer | Change | +|-------|--------| +| `death-clock-core.js` | No new functions needed; uses existing `getCurrentTokens()`, `calculateEnvironmentalImpact()`, `TOKENS_PER_SECOND` | +| `index.html` | Add `
` with the board container | +| `src/js/` | New `25-exchange-board.js`: `initExchangeBoard()`, `updateExchangeBoard()` (called every 5 s via `setInterval`); static rates computed once at init, live rates updated on interval | +| `styles/` | New rules in `features.css`: `.exchange-board`, `.exchange-board-header`, `.exchange-row`, `.exchange-value-live`, scanline pseudo-element | + +--- + +## UX / Accessibility + +- The board is a `` with `
` for screen readers +- Live cells have `aria-live="polite"` and `aria-atomic="true"` so screen + readers announce updates without spamming +- The terminal colour scheme meets WCAG AA contrast on its dedicated dark + background +- `prefers-reduced-motion`: horizontal marquee scroll (if used) is replaced + with a static list +- Dark / light theme: light mode shows a soft dark panel to keep the terminal + aesthetic without a jarring full-page brightness shift + +--- + +## Success Metrics + +- Scroll depth into the Dashboard section (proxy: time before scroll-past) +- Return visit rate (secondary — this feature alone is not expected to drive + returns, but reinforces daily check-in habit when combined with Horoscope) + +--- + +## Open Questions + +- Should there be a *"Copy Exchange Rate"* button so users can paste a single + stat into social posts? (Low effort v2 addition) +- Should the board have a fullscreen/expand mode for live events / talks? + (v2) diff --git a/docs/prd/prd-token-horoscope.md b/docs/prd/prd-token-horoscope.md new file mode 100644 index 0000000..36c203b --- /dev/null +++ b/docs/prd/prd-token-horoscope.md @@ -0,0 +1,148 @@ +# PRD: Token Horoscope 🔮 + +## Overview + +Each day a new satirical "AI horoscope" is generated deterministically from +the current UTC date and served to all visitors. The horoscope tells the +visitor which AI "sin" they are astrologically destined to commit today — +complete with a dramatic warning about data-centre consequences. It refreshes +every 24 hours, creating a strong daily return-visit pull with zero backend +infrastructure. + +--- + +## Problem + +The site's existing content is largely static between visits — the counter +changes, but the editorial content does not. Without a reason to come back +*today specifically*, many users visit once and never return. A daily rotating +item that feels personal is one of the cheapest possible levers for driving +consecutive daily visits. + +--- + +## Goals + +- Drive daily return visits (D1, D3, D7 retention) +- Create a highly shareable satirical artefact that changes every day +- Add levity and personality to the top of the Dashboard section + +--- + +## Non-Goals + +- Real astrology or personalisation by birthdate +- Server-generated content +- Localisation by timezone (all horoscopes use UTC date) + +--- + +## Feature Description + +### Horoscope Generation + +A pool of **30 horoscope templates** is embedded in `death-clock-core.js` as a +constant array. Each template is a short satirical paragraph (2–3 sentences) +following the pattern: + +> *"[Astrological sign-like opener]. Today you will [AI sin]. The stars +> (and several thousand GPU cores) [dramatic consequence]. [Satirical advice]."* + +**Sample templates:** + +1. *"Mercury is in retrograde and so is your judgment. Today you will ask AI to + rewrite a perfectly good email four times. The data centres hum approvingly. + Consider a typewriter."* + +2. *"The alignment of Jupiter and your idle fingers portends a reckless + afternoon. You will use AI to summarise a Wikipedia article you could have + read in 90 seconds. Three cooling towers exhale in unison."* + +3. *"Venus rises in your browser history. You will prompt an image generator + for 45 variations of 'a cat wearing sunglasses' before choosing the first + one. The oceans do not forget."* + +4. *"Your lunar node suggests creative avoidance. You will ask AI to write a + birthday card for your own parent. The glaciers note this."* + +5. *"Saturn's gaze falls upon your clipboard. You will run the same prompt + five times with minor wording changes to see if the answer improves. It + will not. The servers will not forget."* + +*(25 additional templates in the constant array — full list in implementation.)* + +### Daily Seed Selection + +```js +// In death-clock-core.js +function getDailyHoroscope(nowMs, templates) { + const day = Math.floor(nowMs / 86400000); // UTC day index + return templates[day % templates.length]; +} +``` + +All visitors on the same UTC day see the same horoscope — which is intentional +(it creates a shared cultural moment and a reason to compare notes). + +### Display + +- A collapsible card near the top of the **Dashboard** tab, labelled + **"🔮 Your Daily AI Horoscope"** with today's UTC date shown as a subtitle +- Default state: expanded on first daily load; collapsed if already viewed + today (tracked via `localStorage` key `tokenDeathclockHoroscopeDate`) +- The card includes the horoscope text and a **"Share My Fate 🔮"** button + +### Share Button + +Share text: + +> 🔮 Today's AI Horoscope: "[horoscope text]" +> Will this be you today? → [URL] #TokenDeathClock #AIHoroscope + +--- + +## Architecture Notes + +| Layer | Change | +|-------|--------| +| `death-clock-core.js` | Add `HOROSCOPE_TEMPLATES` array (30 items); add `getDailyHoroscope(nowMs, templates)` pure function | +| `index.html` | Add `
` with card, text container, and share button | +| `src/js/` | New `23-horoscope.js`: `initHoroscope()` reads date, calls core, renders text via `textContent`, handles collapse state | +| `styles/` | New rules in `features.css`: `.horoscope-card`, `.horoscope-date`, `.horoscope-text` | + +### localStorage Schema + +```json +{ "date": "2026-04-26" } +``` + +Key: `tokenDeathclockHoroscopeDate`. If the stored date matches today's UTC +date, the card starts collapsed. Otherwise it starts expanded and the date is +updated. + +--- + +## UX / Accessibility + +- The horoscope text is set via `textContent`, never `innerHTML` +- The collapsible uses a `
` / `` pattern for native keyboard + and screen-reader support +- `aria-live="polite"` on the text container so screen readers announce the + content on expansion +- Dark + light theme: card uses existing CSS custom property colour tokens + +--- + +## Success Metrics + +- D1 and D7 return-visit rate increase +- Share button click rate +- Social posts containing "#AIHoroscope" + +--- + +## Open Questions + +- Should the horoscope vary by "sign" selected by the user? (v2 — adds 12× + personalisation for minimal extra cost) +- Should there be a "horoscope archive" tab showing the past 7 days? (v2)