diff --git a/.gitignore b/.gitignore index c24a835..237552f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ next-env.d.ts # production /build +/output/ # misc .DS_Store @@ -43,4 +44,4 @@ yarn-error.log* *.tsbuildinfo # idea files -.idea \ No newline at end of file +.idea diff --git a/README.md b/README.md index 3597f38..1ac3cdf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # Medscribe Web -Marketing homepage for [Medscribe](../README.md) — a privacy-first, on-device medical companion for iOS and Android. +Public product website for [Medscribe](../README.md) — a privacy-first, +on-device medical companion in internal beta on iOS and Android. -Built with [create-t3-app](https://create.t3.gg/) (Next.js + TypeScript + Tailwind). Single-page site; no database or API routes. +Built with [create-t3-app](https://create.t3.gg/) (Next.js + TypeScript + +Tailwind). There is no product database or beta-form backend. Uses **bun** exclusively (`bun.lock`). @@ -15,6 +17,26 @@ bun run dev Open [http://localhost:3000](http://localhost:3000). +## Routes + +The first public product slice includes: + +- `/` — consumer-first portable-context story +- `/how-it-works` — inputs, review loop, medication maintenance, and handoff +- `/privacy` — data map, sharing limitations, and evidence boundaries +- `/beta` — expectations and a health-data-free email request + +Named partner print material remains isolated and excluded from search: + +- `/briefs/northwell` +- `/cvs` +- `/walgreens` + +`/executive-summary` and `/landscape` remain retired/internal and are also +excluded from search. `SITE_PASSWORD` still gates the entire site when set; a +future pass should make protection route-specific before the public site is +deployed alongside private briefs. + ### Analytics (optional) Set in `.env` (see `.env.example`): @@ -31,7 +53,10 @@ bun run build bun run start ``` -Screenshots live in `public/screenshots/`. They are wrapped in an iPhone-style frame via [`react-device-mockup`](https://github.com/jung-youngmin/react-device-mockup). +Screenshots live in `public/screenshots/`. The long-form homepage uses +precomposited, transparent iPhone product-bezel assets in +`public/screenshots/pitch/`; supporting pages use the lightweight CSS frame. +Public pages label product imagery as synthetic demo data and beta UI. ### Capturing raw screenshots for the site @@ -39,6 +64,11 @@ Export **in-app content only** from the simulator or device — no simulator chr 1. Run the app on **iPhone 17 Pro** (or 16 Pro) simulator or device, or on an **Android** emulator/device. 2. Capture each screen (⌘S in Simulator saves to Desktop; `adb exec-out screencap -p` on Android). -3. Drop PNGs into `public/screenshots/` and update paths in `src/app/page.tsx` / `src/components/feature-showcase.tsx`. +3. Drop PNGs into `public/screenshots/` and update paths in the relevant route + or `src/components/feature-showcase.tsx`. -The frame dimensions assume tall iPhone screenshots and are controlled in `src/components/phone-mockup.tsx`. Use `captureMode="simulator"` only for old captures that already include device chrome. +The CSS frame dimensions assume tall iPhone screenshots and are controlled in +`src/components/phone-mockup.tsx`. Use `captureMode="simulator"` only for old +captures that already include device chrome. Homepage product images are +composited separately at 810 × 1656 with transparent backgrounds and rendered +through `src/components/product-device.tsx`. diff --git a/WEBSITE_STRATEGY.md b/WEBSITE_STRATEGY.md new file mode 100644 index 0000000..5d96db0 --- /dev/null +++ b/WEBSITE_STRATEGY.md @@ -0,0 +1,1059 @@ +# Medscribe Website Strategy + +**Planning status:** phase-one public slice implemented; later routes and content operations remain planned
+**Product stage:** internal beta on iOS and Android
+**Primary website job:** help a person recognize the care-context problem, understand the product in under two minutes, trust its privacy boundaries, and request beta access
+**Secondary website job:** give health-system and pharmacy contacts a precise, evidence-backed path to evaluate a conversation without turning the consumer site into an enterprise pitch deck + +### Implementation update — August 20, 2026 + +The first coherent public slice is now built: + +- `/` is consumer-first and tells the context-thread story from visit review to + medication maintenance to a Know Me/caregiver handoff. +- `/how-it-works`, `/privacy`, and `/beta` provide the first product, trust, and + access depth without inventing a form backend or collecting health data. +- Shared public navigation, a richer footer, route metadata, reduced-motion + behavior, mobile layouts, an accessible product tab pattern, and a typed + release/claim registry are in place. +- Northwell moved to `/briefs/northwell`; Northwell, CVS, and Walgreens print + output remains intact and named/internal routes are `noindex` and disallowed + in `robots.txt`. +- Sharing copy includes the current beta limitation found in simulator testing: + ciphertext integrity and wrong-recipient rejection work, but import does not + yet require an actively trusted sender and recipient-key metadata is not + authenticated/checked. Revocation only blocks future local exports and cannot + invalidate ciphertext already exported. + +Still planned: dedicated caregivers/emergency pages, generic partner pages, +about/support/legal policy routes, route-specific brief authentication, a +reviewed form backend, richer evidence methodology, structured content-freshness +checks, and new captures showing the latest schedule/freshness UI. + +## 1. The decision + +Build one coherent public site with three deliberately different layers: + +1. **The human story** — a consumer-facing homepage and scenario pages about the record that goes missing between visits and at handoffs. +2. **The product proof** — real, clearly labeled beta screens and short demos showing how a visit, a medication, and a caregiver handoff contribute to one current record. +3. **The trust proof** — a plain-language privacy and evidence center that states exactly what runs locally, what can use a network, what sharing v1 does, what has been evaluated, and what remains unfinished. + +Partner materials should sit beside this public story, not replace it. The existing Northwell, CVS, and Walgreens work is useful sales collateral, but named-partner executive summaries should be private or intentionally shared, excluded from search, and protected from changes to the public design system. + +The richer site should feel like an editorial product story, not a dense feature grid and not a generic “AI healthcare” landing page. Its central narrative is: + +> Care happens in fragments. Medscribe helps the patient carry the context between them. + +The everyday loop is the evidence for the emergency promise: + +```text +visit or document + ↓ +plain-language summary + ↓ +medication and follow-up record + ↓ +daily reminders, questions, and corrections + ↓ +a current Know Me handoff when someone new needs the picture +``` + +This is the story no single isolated screenshot currently tells. + +## 2. What the phase-one baseline gave us + +Before the public slice was built, the Next.js site had more useful material +than its “single-page site” README description suggested: + +- A polished Northwell-specific root page and three-page print summary, now + preserved at `/briefs/northwell`. +- Separate in-progress CVS and Walgreens print-oriented routes. +- A retired but reusable general executive-summary page and competitive-landscape content. +- A strong screenshot library covering onboarding, visits, transcription, summaries, medications, chat, settings, and the emergency record. +- Reusable `PhoneMockup`, `FeatureShowcase`, logo, section-label, card, button, header, footer, and screen-background components. +- A restrained app-aligned teal/slate token set, light/dark preferences, and early entrance-motion utilities. +- Optional Umami analytics and an optional site-wide password gate. +- Print-specific page geometry already tuned for letter-size PDFs. + +The phase-one implementation resolved the missing public information +architecture and separated Northwell from `/`. Partner print artifacts still +share global marketing and print styles, so later layout refactors must remain +narrow and regression-tested. + +### Preserve during implementation + +The current working tree contains uncommitted marketing, styling, PDF, CVS, and Walgreens work. Do not mechanically reorganize it as part of the first website pass. Land or otherwise establish ownership of that work first, then refactor in narrow steps. + +The following behaviors are release constraints: + +- `/cvs` and `/walgreens` must continue to render their intended screen instructions and exact print documents. +- The Northwell print summary must remain exportable even after `/` becomes consumer-facing. +- Letter-size page count, print backgrounds, page breaks, and print color must be regression-tested. +- Public navigation and analytics must not appear in a printed partner document. +- A public-site redesign must not silently publish previously redirected internal landscape or executive-summary content. + +## 3. Audiences and jobs to be done + +### Primary: a patient or family caregiver + +They are usually not shopping for “on-device AI.” They are trying to keep medications, appointments, changed instructions, and family handoffs straight. They need to answer: + +- Is this for a situation like mine? +- What would I actually do with it this week? +- Does it replace MyChart or my clinician? (No.) +- Will I be asked to upload health information or create another cloud account? +- What is built in the beta, and how do I try it? + +The site should lead with recognition and relief, then demonstrate the workflow, then explain the architecture. + +### Secondary: a health-system evaluator + +They need a disciplined description of the boundary between the patient-owned layer and the institutional EHR, plus security details, operational assumptions, integration status, evaluation evidence, and a narrowly defined pilot hypothesis. They should not have to infer those facts from consumer copy. + +### Secondary: a pharmacy or payer-adjacent evaluator + +They need a medication-management narrative, but outcome language must stay hypothetical until measured. “Designed to support refill continuity” is supportable; “moves adherence,” “lowers cost of care,” or a guaranteed impact on Stars/EQuIPP is not yet substantiated. + +### Supporting: clinicians, pharmacists, privacy reviewers, press, and potential collaborators + +They need a quick product explanation and a trustworthy source trail. A clear evidence page is more persuasive than larger performance claims on the homepage. + +### Not a target audience + +Do not position Medscribe as a diagnostic assistant, dosing authority, clinician scribe, EHR replacement, emergency medical device, or universal health-data integration layer. + +## 4. Information architecture + +### Public navigation + +```text +Medscribe +├── / Home: the portable-context story +├── /how-it-works One record, built through everyday use +├── /caregivers Multi-person care and handoffs +├── /emergency The Know Me record and its limits +├── /privacy Data map, local inference, sharing, limitations +├── /evidence Sources, evaluation methods, release status +├── /beta Who the beta is for, expectations, request access +├── /about Founder story and product principles +└── /partners Generic partner overview and contact path + ├── /health-systems EHR-complement and pilot hypothesis + └── /pharmacies Medication-loop and pilot hypothesis +``` + +This is the target architecture, not a requirement to launch nine pages on day one. The first launch can combine `caregivers` and `emergency` into the homepage and combine `privacy` and `evidence` if content ownership is limited. + +### Intentionally shared partner materials + +```text +/briefs/northwell +/briefs/cvs +/briefs/walgreens +``` + +Recommended behavior: + +- Require the existing password gate for named briefs or use unguessable, expiring distribution outside the site. +- Add `noindex, nofollow, noarchive` headers and metadata even when gated. +- Exclude them from sitemap, public nav, site search, and public analytics. +- Keep printable documents visually self-contained. +- Preserve `/cvs` and `/walgreens` as redirects or aliases only after verifying bookmarks and any PDFs already distributed. + +The current proxy is site-wide when `SITE_PASSWORD` is set. A later implementation should support route-specific protection so the public site can remain public while named briefs stay private. That change deserves its own security review; do not improvise it during visual work. + +### Utility and policy routes + +Before a broad beta launch, add: + +- `/privacy-policy` — actual legal/privacy notice, distinct from the architecture explainer. +- `/terms` — beta terms and medical disclaimer reviewed by counsel. +- `/support` — contact, deletion/reset instructions, device-loss guidance, and incident-reporting path. +- `/accessibility` — optional at first, but provide a direct accessibility contact in the footer. + +These pages should not be generated from marketing copy. They require product/security verification and legal review. + +## 5. The richer editorial narrative + +The experience should alternate between human stakes and concrete proof. Avoid starting with a technology taxonomy or a competitive claim. + +### Act I: the missing picture + +A restrained hero introduces a familiar moment: the doctor, pharmacist, sibling, or ER asks what changed, and the complete answer is scattered across memory, portals, labels, and one caregiver's notes. + +Suggested headline territory: + +- **Carry the context when care gets complicated.** +- **Your medical picture should travel with you.** +- **Be known, even when the chart is incomplete.** + +Suggested support line: + +> Medscribe is a private, patient-controlled beta that helps you keep visits, medications, and critical context together on your device, ready to review or share when you choose. + +The hero should show the product, but not as an isolated phone floating beside copy. Use a small sequence of three screens or a layered “record assembling” composition: visit summary, medication card, then Know Me record. + +Primary CTA: **Request beta access**
+Secondary CTA: **See how it works**
+Status label: **Internal beta · iOS and Android** + +### Act II: the record earns its freshness + +Tell one continuous workflow in three chapters: + +1. **After a visit:** type or paste notes, import a portal screenshot, attach a document, or use consent-gated recording; review the generated summary. +2. **Between visits:** keep the medication list, reminders, refills, intake, and questions close to the underlying record. +3. **At a handoff:** open a Know Me view or manually export an encrypted snapshot to a paired caregiver. + +Each chapter should contain: + +- one real product screen; +- one sentence describing the user's action; +- one sentence explaining what is local/deterministic; +- one explicit confirmation or correction moment, so AI output is not presented as automatically authoritative. + +### Act III: why local changes the relationship + +Move from the experience to a plain-language data flow. Explain task by task, not with “everything” absolutes: + +- transcription and supported AI tasks run on the device; +- bundled drug facts and known interaction pairs are read locally; +- sharing v1 is a manual, pairwise encrypted snapshot; +- model installation/update may require a network; +- product behavior and storage protections are still being hardened for broader release. + +Link to the full privacy and evidence pages. + +### Act IV: invite the right beta user + +The close should define a good beta participant: + +- managing several medications or repeated visits; +- coordinating with a family caregiver; +- willing to review generated information and report errors; +- understands this is not emergency care, diagnosis, or dosing guidance. + +The beta form should ask only operational questions. Do not collect diagnoses, medication names, transcripts, or other health information on the website. + +## 6. Page-by-page content and wireframes + +### `/` — Home + +**Goal:** recognition → product understanding → trust → beta request. + +```text +[compact nav: How it works | Caregivers | Privacy | Evidence | Beta] + +[beta eyebrow] +[human headline + two CTAs] [3-screen record composition] +[one-line product boundary] + +[problem vignette: “the context doesn’t travel”] +[visit] — [between visits] — [handoff] + +[scrolling record thread] + capture → review → keep current → share when chosen + +[interactive product proof] + Visits | Medications | Ask | Know Me | Sharing + real screenshot + action + limitation/source note + +[three human situations] + newly diagnosed | long-distance caregiver | unfamiliar care setting + +[privacy proof strip] + local inference | bundled facts | patient-controlled manual sharing + “Read the exact boundaries” → /privacy + +[what the beta is / is not] + +[request access CTA] +[footer: product, trust, company, legal] +``` + +Use `FeatureShowcase` as a starting point for product proof, but update it to an accessible tabs or disclosure pattern, add the Know Me/sharing distinction, and ensure every mobile user can reach the same content without hover. + +### `/how-it-works` + +**Goal:** make the daily-maintenance thesis tangible. + +Sections: + +1. A numbered overview of the four input paths: type/paste, portal screenshot, documents/photos, optional consent-gated audio. +2. A review checkpoint showing that summaries and extracted data are suggestions until confirmed. +3. Medication workflow: label OCR, editing, reminders/refills/intake, bundled facts. +4. Ask workflow: deterministic fact lookup versus locally generated conversational glue; clinician/pharmacist deferral boundary. +5. Record freshness: show the built current/unconfirmed/review-needed Know Me state and the built + schedule source/confirmation/timezone UI. Broader provenance for conditions, allergies, and + visit-derived record changes remains roadmap and must not be mocked as current. +6. Handoff: Know Me view versus paired encrypted export. +7. “What it does not do” block. + +### `/caregivers` + +**Goal:** center the real coordination use case without implying live synchronization. + +Sections: + +- A shift-change story involving two family members. +- What is built: QR identity pairing, safety-number verification, manual encrypted snapshot export/import, local trust and revocation controls. +- What is not built: account-based household, automatic background sync, blind relay, Double Ratchet/forward-secret live channel. +- A walkthrough with synthetic demo data. +- A safety checklist: verify the recipient, review content before export, re-export after material changes, revocation does not erase a snapshot already received. +- Beta CTA specific to caregiver pairs. + +Never use “real-time,” “continuous shared view,” “Signal protocol,” or “live sync” for v1. + +### `/emergency` + +**Goal:** explain the Know Me record's role and limits. + +Sections: + +- The founder's motivating story, told carefully and without implying a guaranteed clinical outcome. +- The structured fields currently built: conditions, allergies, baseline, contacts, medications, + per-medication time-critical flag/note, fixed-time or as-needed schedule, optional user-entered + window, source/confirmation/timezone, and whole-card freshness state. +- A large, readable Know Me screen demonstration. +- “Prepared for a handoff, not a replacement for emergency services or a clinician's verification.” +- State that the app records only timing supplied by the label/pharmacist/prescribing team; it does + not calculate or shift times. Label physical-device migration/notification/DST validation and + broader field provenance accurately. +- Device access constraints: the current app experience is not a lock-screen medical ID unless and until that specific behavior ships and is tested. + +Prefer **handoff-ready** or **Know Me record** over an unqualified **emergency-ready** claim. If “emergency-ready” remains in brand copy, define it immediately and state that no clinical outcome has been validated. + +### `/privacy` + +**Goal:** make the privacy promise falsifiable and useful. + +Lead with a data-flow diagram and a task matrix: + +| Task | Where it happens | Network behavior | User control | +| ------------------------- | -------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------- | +| Visit transcription | Device | No cloud inference | Recording requires consent | +| Summary generation | Device | No cloud inference | User reviews output | +| Medication OCR | Device | No cloud inference | User confirms extracted fields | +| Drug facts/interactions | Bundled data | No runtime lookup for fact answers | Source and coverage disclosed | +| Model installation/update | Device receives model file | Network may be required | Integrity/version status disclosed | +| Caregiver sharing v1 | Pairwise encrypted export | User chooses the transport for ciphertext | Pair, verify, export, revoke trust | +| Local record storage | Device database | No Medscribe cloud account | State exact OS/app protection; do not claim app-level at-rest encryption unless shipped | +| Website analytics | Marketing site only | Optional limited web telemetry | State provider, fields, retention, opt-out | + +Also include: + +- a precise threat-model summary; +- what the design protects against and what it does not; +- lost/unlocked device considerations; +- model-download and update integrity status; +- sharing protocol/version language; +- a dated security changelog or link; +- a security contact and incident-reporting method; +- separate links to the legal privacy policy and beta terms. + +Do not publish the current strongest language (“nothing to breach,” “nothing to subpoena,” or “no BAA to negotiate”) as fact. Local storage can still be exposed on a compromised or unlocked device, exported ciphertext can persist with a recipient, websites have their own telemetry, and BAA applicability is a legal/operational conclusion. + +### `/evidence` + +**Goal:** replace generic AI superlatives with reproducible evidence. + +Content model for each evidence card: + +- capability tested; +- model/build identifier; +- dataset or scenario set and sample count; +- held-out versus training status; +- platform tested (Mac is not iPhone); +- date; +- metric definition; +- result; +- known blind spots; +- link to methodology or a concise downloadable report. + +Suggested sections: + +1. Product status table: built, beta-verified, device-tested, roadmap. +2. Deterministic knowledge sources: openFDA label data, RxNorm terminology, MED-RT/DailyMed classes, and ONC/CredibleMeds interaction inputs, with coverage counts and dataset dates pulled from the release manifest. +3. Model evaluation, clearly separating MLX/Mac, llama.cpp/Mac, simulator, and physical-device results. +4. Human review status for condensed drug information. Do not imply that public source data makes the condensed patient wording FDA-reviewed. +5. Safety boundaries and known failure classes. +6. Release notes and last verification date. + +Do not carry forward the retired executive page's 97% extraction and 100% refusal numbers without the full model version, test-set size, methodology, platform, date, and current-release match. Historical results can appear in a labeled archive. + +### `/beta` + +**Goal:** produce qualified beta conversations without collecting health data. + +Sections: + +- Who it is for. +- What platforms and distribution channels are currently available. +- What participants should expect: onboarding, manual review, feedback, possible resets, no guaranteed continuity. +- What not to submit on the website. +- Current test focus: record freshness, medication-label edge cases, notifications, caregiver export, Know Me comprehension, and physical-device model performance. +- Short form: name, email, iOS/Android, patient/caregiver/both, general coordination challenge in non-clinical categories, willingness to test with a caregiver, consent to be contacted. +- Medical and emergency disclaimer. + +Until a privacy-reviewed form backend exists, retain the current email CTA but rewrite the prefilled body so it does not solicit diagnoses, medications, or other health details. + +### `/about` + +**Goal:** establish origin and principles without making the founder story carry the whole proof burden. + +Sections: + +- Sean O'Connor and the grandfather/caregiver origin. +- The product principles: patient control, local inference, facts before fluent guesses, review before authority. +- Current beta stage and collaborator/contact CTA. +- Corporate identity stated consistently after legal review. + +### `/partners` + +**Goal:** route serious evaluators without turning public pages into named account pitches. + +Use a generic overview with two paths: + +- **Health systems:** patient-held context between and outside institutional encounters; a pilot can test handoff completeness and patient engagement. FHIR import and workflow integration are roadmap unless a scoped prototype is built. +- **Pharmacies:** patient-held medication context, reminders, refill visibility, and pharmacist questions. A pilot can test usability and refill continuity; do not claim measured adherence, cost, Star, EQuIPP, or script-share impact before data exists. + +Every outcome statement should use a three-part pattern: + +```text +Observed problem → product mechanism → measurable pilot hypothesis +``` + +Example: + +> Medication lists become fragmented across prescribers. Medscribe helps a participant review one patient-held list and reminder history. A pilot could measure list completeness, correction frequency, and participant-reported confidence; it has not yet established an effect on adherence or medical spend. + +### `/partners/health-systems` + +Suggested modules: + +- The between-visit and out-of-network gap. +- Exact current workflow without integration. +- Future import-only FHIR direction, explicitly roadmap. +- Security architecture and diligence links. +- Pilot shape: population, duration, responsibilities, opt-in metrics, safety escalation. +- What remains with Epic/MyChart. +- Contact CTA. + +Avoid “feeds context back into Epic” until an actual consented workflow/integration exists. Say “a future partner workflow could return patient-approved context” and label it proposed. + +### `/partners/pharmacies` + +Suggested modules: + +- Cross-prescriber medication picture. +- Reminder, refill, intake, and facts-first question workflow. +- Pharmacist escalation rather than model authority. +- Generic pilot hypothesis and metrics. +- Integration/co-branding possibilities labeled proposed. +- Contact CTA. + +Keep named CVS and Walgreens business logic in their briefs, not this public route. + +## 7. Claim system and guardrails + +Claims need a source of truth just as drug facts do. Phase one introduced a +typed registry in `src/content/product.ts`; continue expanding it before adding +quantitative, security, integration, regulatory, clinical-outcome, or +availability claims. Each claim should carry: + +```ts +type Claim = { + id: string; + publicCopy: string; + status: "built" | "beta-verified" | "proposed" | "roadmap"; + evidence: string; + verifiedOn: string; + owner: string; +}; +``` + +Render status labels where ambiguity is likely and make stale verification dates fail a content check or appear in a review report. + +### Copy guardrail table + +| Topic | Supportable language now | Avoid until verified | +| -------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| Runtime AI | “Visit transcription and supported AI tasks run on the device.” | “Nothing ever leaves the phone.” | +| Network | “No cloud AI inference.” | “No network use” unless every release path is audited and qualified. | +| Storage | “The primary record is stored locally on your device.” | “Encrypted at rest by Medscribe,” “unbreachable,” or equivalent without implemented and tested app-level protection. | +| Sharing | “Manual pairwise encrypted snapshot sharing using X25519 and XChaCha20-Poly1305 primitives.” | “The Signal protocol,” “live sync,” “continuous shared view,” “forward secret.” | +| Patient control | “You choose when to pair and export.” | “Revocation deletes every previously received copy.” | +| Drug facts | “Answers can render bundled fields derived from named public reference datasets.” | “FDA-reviewed answers,” “clinically validated,” or “complete interaction checker.” | +| Interaction coverage | “Known pairs from a defined bundled high-priority list.” | “Catches drug interactions” without a coverage limitation. | +| AI accuracy | Model/build/test/platform/date-specific results. | Unqualified percentages, “medical-grade,” “safe,” or “accurate.” | +| Emergency use | “A Know Me handoff puts selected context in one view.” | “Prevents errors,” “saves lives,” “works from the lock screen,” or “medical device” claims. | +| Integrations | “Portal screenshots can be imported; structured FHIR import is roadmap.” | “Integrates with Epic/MyChart,” “feeds Epic,” or “interoperable” as a current feature. | +| Release stage | “Internal beta on [verified channels].” | “Shipped,” “production-ready,” or broad availability if access remains controlled. | +| Business impact | “A pilot could measure…” | “Moves adherence,” “lowers costs,” “improves ratings,” or “keeps scripts” without evidence. | +| Legal/compliance | Describe architecture and data flow. | “HIPAA compliant,” “no BAA required,” or legal conclusions without counsel and operating context. | + +### Release-content rule + +No quantitative, security, integration, regulatory, clinical-outcome, or availability claim should ship from an ad hoc component string. It must point to a dated evidence or product-status entry and be reviewed when the app release changes. + +## 8. Visual direction + +### Character + +Keep the current app-aligned teal/slate foundation, editorial serif headings, and high legibility. Make it richer through composition, pacing, and genuine product material—not through more gradients, glass effects, or AI imagery. + +Desired qualities: + +- calm and humane rather than sterile; +- precise rather than futuristic; +- domestic and between-visit rather than hospital-centric; +- recognizably a health product without stock doctors, glowing brains, shields, or abstract neural networks. + +### Signature visual: the context thread + +Use a thin, evolving line or ledger motif that connects a visit excerpt, medication change, daily reminder, caregiver note, and Know Me field. On wide screens it can travel vertically through the page. On small screens it becomes a simple numbered sequence. It should demonstrate provenance and continuity, not decorate empty space. + +### Product imagery + +- Keep real screenshots large enough to read. +- Use one primary phone at a time on mobile; avoid tiny multi-phone collages. +- On desktop, layer two or three screens only when their relationship is the point. +- Pair each screenshot with a visible status: **Current beta**, **Illustrated workflow**, or **Roadmap concept**. +- Never place invented medical content in a mockup without a clear synthetic-demo label. + +### Color and typography + +- Preserve the current serif/sans pairing unless performance or licensing review suggests a system fallback. +- Add one warm neutral range for human-story sections; do not add a second saturated brand color. +- Reserve red/amber for actual warnings and status, not decoration. +- Maintain WCAG 2.2 AA contrast in light and dark preferences. +- Avoid low-contrast teal-on-mint for small text and badges. + +### Motion + +Use motion to reveal the record's continuity: + +- gentle crossfade or matched transition between the three lifecycle screens; +- a progress line that advances as chapters enter the viewport; +- screenshot detail callouts that appear after the screen settles; +- optional, user-controlled 30–45 second product demo. + +Constraints: + +- Respect `prefers-reduced-motion`; all meaning must remain present with animation disabled. +- Do not autoplay audio. +- Avoid scroll-jacking, pinned sections longer than one viewport on mobile, parallax text, or looping attention animation. +- Pause media when off-screen and provide visible controls, captions, and a transcript. +- Keep entrance motion under roughly 300–500 ms and never delay primary content for choreography. + +## 9. Responsive and accessible behavior + +Treat accessibility as a component acceptance criterion, not a final audit. + +### Global requirements + +- Semantic landmark structure, one descriptive `h1`, and logical heading hierarchy. +- Keyboard-visible focus that survives light/dark themes. +- Skip link to main content. +- Touch targets at least 44 by 44 CSS pixels where practical. +- No information available only through hover, color, animation, or a device mockup. +- Descriptive alternative text for product screens; decorative frame art gets empty alt text. +- Captions and transcripts for all product video. +- Form labels, errors, status, and success messages announced correctly. +- No health-related prompt or questionnaire should be mistaken for medical advice. + +### Interactive product showcase + +The current button row should evolve into either: + +- a WAI-ARIA tabs pattern with arrow-key behavior and stable panels; or +- a stacked disclosure/accordion on narrow screens and tabs at wider breakpoints. + +Do not auto-cycle tabs. Preserve the currently selected panel through hydration and avoid content layout jumps. + +### Responsive layout + +- **Small phones:** single-column story, full-width copy, one readable screen, sticky CTA only if it does not obscure content. +- **Large phones/tablets:** screenshot beside short proof copy where it remains readable. +- **Desktop:** alternating editorial grid with a maximum readable text measure; avoid stretching the current `max-w-5xl` into a wall of copy. +- **Very wide screens:** constrain the narrative canvas; decorative context line can use the extra gutter. +- **Print:** named briefs use a separate invariant layout and do not inherit responsive public navigation. + +### Quality gates + +Target: + +- WCAG 2.2 AA on critical flows; +- automated axe checks plus manual keyboard and VoiceOver pass; +- no horizontal overflow at 320 px; +- 200% browser zoom without loss of function; +- reduced-motion screenshot test; +- light and dark preference snapshots. + +## 10. Trust, evidence, and provenance content operations + +### One release manifest + +The website should consume or be updated from a small, reviewed release manifest rather than copying status across pages: + +```ts +type ProductRelease = { + version: string; + releasedOn: string; + stage: "internal-beta" | "closed-beta" | "public-beta"; + platforms: Array<"ios" | "android">; + model: { id: string; onDeviceValidated: boolean }; + drugDataset: { records: number; sourceDates: string[]; reviewStatus: string }; + interactions: { pairs: number; coverageNote: string }; + sharingProtocol: string; + knownLimitations: string[]; +}; +``` + +It can start as hand-reviewed TypeScript. Automation can follow after release practice stabilizes. + +### Visible provenance + +On the evidence page, show: + +- dataset names and release dates; +- coverage boundaries; +- whether wording is source text, mechanically normalized, or AI-condensed; +- human-review status; +- last product build used for verification; +- evaluation links and known failure examples summarized without exposing patient data. + +### Content freshness + +- Every trust/evidence page displays **Last verified**. +- Claims older than a defined window (for example, 90 days or one app release) enter a review queue. +- A launch checklist compares website status against app and model release notes. +- Roadmap sections use dates only when there is a real delivery commitment; otherwise use ordered stages. + +## 11. Product demo and media asset plan + +### Existing asset inventory to reuse + +The repository already includes screens for: + +- onboarding and home; +- visits, visit detail, transcript, timeline, and summary; +- appointment capture and summary; +- medication list/detail; +- chat, suggestions, and response; +- emergency record; +- settings. + +Audit each image before reuse for build freshness, real personal data, contrast, status-bar details, and whether the UI shown is still shipped. + +### Required synthetic demo profile + +Create one clearly fictional, consistent profile used across every screenshot and video. Define: + +- a non-real name and avatar; +- 2–3 common conditions; +- 3–4 medications selected for safe demonstration; +- one fictional visit and follow-up; +- one caregiver relationship; +- one time-critical medication with a clearly fictional source-confirmed schedule/window that does + not imply Medscribe supplied dosing advice. + +Have a product/safety reviewer approve the scenario. Never use a founder family member's real medical details as demo data. + +### Shot list + +1. Home with record status and next action. +2. Portal screenshot import → OCR/result review. +3. Visit summary with source/review state. +4. Medication label scan → editable extracted fields. +5. Medication detail with bundled-fact source label. +6. Question answered with deterministic facts and pharmacist deferral. +7. Know Me view with time-critical schedule, source, confirmation, and freshness warning. +8. Caregiver QR pairing and safety number. +9. Manual encrypted export/import confirmation. +10. Settings/privacy boundaries and model status. + +### Media formats + +- **Hero:** optimized AVIF/WebP stills with responsive sizes; do not make video the LCP dependency. +- **Workflow demos:** short muted MP4/WebM loops, each 6–12 seconds, with still fallback and text equivalent. +- **Full demo:** optional 30–45 second user-controlled video, captions, transcript, chapter links. +- **Architecture/provenance diagrams:** semantic SVG or HTML/CSS, not raster text. +- **Open Graph:** 1200×630 image with no patient-like details and a stable product claim. + +### Capture protocol + +Record app version, OS/device, capture date, demo-profile version, and screen route for every asset. Store that metadata next to the asset manifest. Re-capture when a visible flow materially changes. + +## 12. SEO, analytics, and website privacy + +### Search strategy + +Prioritize high-intent, non-diagnostic themes: + +- patient-owned medical record; +- caregiver medication organizer; +- organize medical visits and medications; +- private on-device medical companion; +- medical handoff information for caregivers; +- portable care context across providers. + +Do not chase condition-specific advice queries or publish generated drug/diagnosis SEO pages. That creates a medical-content review burden and confuses the product boundary. + +### Technical SEO + +- Unique title, description, canonical URL, and social image per public route. +- `sitemap.xml` includes public routes only. +- `robots.txt` and `X-Robots-Tag` exclude login, auth, named briefs, print views, and retired internal routes. +- Add organization and software application structured data only for fields that are verifiably true; do not add review ratings or availability claims without evidence. +- Give screenshot images useful dimensions and context; product screenshots do not need to be indexed independently. +- Use internal links around the central “portable context” concept rather than duplicating keyword blocks. + +### Analytics posture + +Umami can remain optional, but “privacy-friendly” is not a complete disclosure. Decide and document: + +- hosted versus self-hosted; +- fields collected; +- IP handling; +- cookie behavior; +- retention; +- processor location; +- whether Do Not Track or Global Privacy Control is honored. + +Recommended public event taxonomy: + +- `beta_cta_clicked` with placement only; +- `how_it_works_viewed`; +- `privacy_detail_opened`; +- `evidence_method_opened`; +- `demo_started`, `demo_completed`; +- `partner_path_selected` with generic audience only. + +Never collect: + +- form free text in analytics; +- medication, condition, or caregiver details; +- email addresses or URL query values; +- named-partner brief paths in third-party analytics; +- product screenshots containing real data. + +Prefer aggregate conversion and page-sequence analysis. Do not add session replay, heatmaps, ad pixels, or cross-site retargeting to a health-related site. + +### Web security baseline + +Before public launch, add and verify: + +- Content Security Policy compatible with the chosen analytics/media hosts; +- HSTS at the hosting layer; +- `Referrer-Policy`, `Permissions-Policy`, and anti-sniffing headers; +- rate limiting and generic errors on auth/contact endpoints; +- route-specific protection for named briefs; +- no secrets or partner names in client analytics payloads; +- dependency and deployed-header review. + +## 13. Concrete implementation guidance for this codebase + +### Route and layout boundaries + +After the existing working changes are settled: + +```text +src/app/ +├── (public)/ +│ ├── layout.tsx +│ ├── page.tsx +│ ├── how-it-works/page.tsx +│ ├── caregivers/page.tsx +│ ├── emergency/page.tsx +│ ├── privacy/page.tsx +│ ├── evidence/page.tsx +│ ├── beta/page.tsx +│ ├── about/page.tsx +│ └── partners/... +├── (briefs)/briefs/ +│ ├── layout.tsx +│ ├── northwell/page.tsx +│ ├── cvs/page.tsx +│ └── walgreens/page.tsx +├── login/page.tsx +└── api/auth/route.ts +``` + +Route-group names do not affect URLs. Use separate layouts so briefs do not mount the public background, navigation, footer, or analytics by accident. + +### Component reuse + +- **`PhoneMockup`:** retain for static product proof; add responsive image sizing and a simpler frame option so not every screen pays for an interactive client dependency. +- **`FeatureShowcase`:** reuse its typed content model and screenshot set; refactor interaction to accessible tabs/disclosures and include status/source annotations. +- **`SiteHeader`:** evolve from a centered “Executive summary” capsule to public navigation, beta CTA, mobile menu, and active-route state. +- **`SiteFooter`:** add product/trust/company/legal columns, beta status, security/accessibility contact, and verified corporate copy. +- **`ScreenBackground`:** keep as a subtle base; create the context-thread visual as a separate progressive-enhancement component. +- **`SectionLabel`, `Card`, `Badge`, `Button`:** preserve the primitives, audit focus and contrast, and introduce variants only when a page need repeats. +- **`lib/beta.ts`:** keep shared stage/CTA copy, but replace broad “everything” statements with a release-manifest-backed boundary. +- **`UmamiAnalytics`:** mount in the public layout after policy is settled; omit from briefs/auth or explicitly filter them. + +### Content architecture + +Move long marketing strings out of route components into reviewed content modules: + +```text +src/content/ +├── product-release.ts +├── claims.ts +├── home.ts +├── privacy.ts +├── evidence.ts +└── partners/ + ├── health-systems.ts + ├── pharmacies.ts + ├── cvs.ts + └── walgreens.ts +``` + +Keep public product truth and account-specific sales hypotheses separate. A partner name should never be interpolated into a generic claim if the underlying business model differs, as the current CVS/Walgreens distinction correctly demonstrates. + +### Print architecture + +The current CVS and Walgreens components duplicate a common print frame and similar product page. That is an appropriate future refactor, but only after pixel baselines exist. + +Recommended sequence: + +1. Render and archive current PDFs and page PNGs as baselines. +2. Extract only the page frame/header/footer to `components/briefs/print-page.tsx`. +3. Move partner-specific claims to typed content modules. +4. Extract shared capability blocks without forcing identical partner payoff logic. +5. Keep print CSS in a dedicated `briefs-print.css` imported only by the briefs layout if Next/Tailwind output remains stable. +6. Compare generated PDFs for page count, clipping, fonts, backgrounds, and URL/footer artifacts. + +Do not rename or reinterpret the existing `northwell-print-*` classes as part of the public homepage work. The names are inelegant but harmless; page stability matters more. + +### Rendering and performance + +- Keep most pages as React Server Components. +- Isolate interactive showcase, media player, and mobile navigation as small client islands. +- Avoid shipping all screenshots or partner content in the root bundle. +- Use route-level metadata and static generation. +- Set explicit image sizes and `sizes`; prioritize only the true hero image. +- Lazy-load below-the-fold video and device-mockup code. +- Subset fonts and verify the two Google font families do not block first render; consider self-hosted or system fallbacks if deployment policy requires no runtime third-party font calls. + +Performance targets on a representative mobile connection: + +- Core Web Vitals in the “good” range; +- LCP under 2.5 seconds at the 75th percentile; +- CLS under 0.1; +- INP under 200 ms; +- initial JavaScript kept low enough that the text narrative works before hydration. + +### Testing + +Add: + +- content/claim registry validation; +- component tests for status labels and route-specific copy; +- accessibility tests for navigation, tabs, form, and video controls; +- Playwright smoke tests for every public route; +- screenshot baselines at mobile/tablet/desktop, light/dark, and reduced motion; +- PDF render tests for Northwell/CVS/Walgreens page counts and clipping; +- sitemap/robots tests proving named briefs are excluded; +- analytics tests proving no form values or partner paths are sent. + +## 14. Phased implementation backlog + +### Phase 0 — Establish product truth and protect existing work + +**Outcome:** the site has an approved factual foundation and print baselines before restructuring. + +Tasks: + +- Resolve and land or explicitly preserve the current uncommitted web changes. +- Render current Northwell, CVS, and Walgreens PDFs and screenshots as baselines. +- Reconcile product claims with the current app/model release. +- Create the typed product-release and claim registries. +- Decide beta distribution wording, company identity, analytics policy, security contact, and named-brief access policy. +- Inventory screenshots and mark keep/recapture/retire. + +Acceptance criteria: + +- Every homepage-level claim has an owner, evidence/status, and verification date. +- Current print artifacts are reproducible with documented commands and match approved baselines. +- No public copy claims app-level at-rest encryption, complete accuracy, FDA review/approval, FHIR/Epic integration, full Signal protocol, clinical efficacy, or production readiness. +- Named briefs have an agreed public/private distribution rule. + +Dependencies: + +- App security/release audit status. +- Current model promotion/device-validation status. +- Human review status for bundled drug-info condensation. +- Founder/legal decision on public company and policy copy. + +### Phase 1 — Public foundation and homepage + +**Outcome:** `/` becomes a strong consumer product narrative while partner print flows remain unchanged. + +Tasks: + +- Introduce public and briefs layouts without moving named URLs yet. +- Build public header/footer/navigation and skip link. +- Implement the editorial homepage, record-thread sequence, accessible product showcase, privacy strip, and beta CTA. +- Replace homepage-specific Northwell rendering with a dedicated brief route while retaining a compatibility path. +- Add route metadata, social image, sitemap, and robots policy. +- Add initial responsive/accessibility/performance tests. + +Acceptance criteria: + +- A first-time visitor can state what Medscribe does, who it is for, what is local, and how to request access after a two-minute moderated review. +- Homepage contains no named-account pitch or proposed integration presented as current. +- All content and CTAs work at 320 px, keyboard-only, VoiceOver, 200% zoom, dark preference, and reduced motion. +- Existing print PDFs remain visually equivalent and retain page counts. +- Mobile LCP is not dependent on video or a large client bundle. + +### Phase 2 — Product and scenario depth + +**Outcome:** users can understand the full workflow without making the homepage encyclopedic. + +Tasks: + +- Build `/how-it-works`, `/caregivers`, and `/emergency`. +- Capture the approved synthetic demo-profile screenshots. +- Add short workflow clips with still fallbacks, captions, and transcripts. +- Build `/beta` with email CTA or a privacy-reviewed minimal form. +- Build `/about`. + +Acceptance criteria: + +- Current versus roadmap behavior is visible on every relevant module. +- Caregiver content accurately describes manual pairwise export and revocation limits. +- Emergency content does not imply lock-screen access, clinical validation, or guaranteed prevention. +- No demo asset contains real personal/health data. +- Beta intake collects no health details and provides stage/safety expectations. + +Dependencies: + +- Synthetic demo profile and content approval. +- Current app build available for consistent captures. +- Decision on beta intake backend and policy. + +### Phase 3 — Trust and evidence center + +**Outcome:** privacy/security and model claims are inspectable rather than ornamental. + +Tasks: + +- Build `/privacy` data map and limitations. +- Build `/evidence` release status, datasets, evaluations, and review state. +- Add last-verified dates and content-freshness checks. +- Publish legal privacy/terms/support pages after review. +- Add security headers and incident/security contact workflow. + +Acceptance criteria: + +- A privacy reviewer can identify every current runtime network path and storage boundary from the site. +- All quantitative results name the model/build, test set, metric, platform, and date. +- Drug data source and human-review status are explicit. +- Legal and architecture explanations are clearly distinct. +- Stale product evidence is detected before deployment. + +Dependencies: + +- Security hardening and threat-model review. +- Physical-device model results. +- Drug-info human review. +- Legal/privacy counsel. + +### Phase 4 — Partner framework + +**Outcome:** generic partner paths and named briefs share accurate primitives without sharing unsupported conclusions. + +Tasks: + +- Build `/partners`, `/partners/health-systems`, and `/partners/pharmacies`. +- Add route-specific protection/noindex for named briefs. +- Migrate or alias Northwell/CVS/Walgreens into the briefs layout. +- After baselines, extract shared print frame and typed account content. +- Replace outcome assertions with measurable pilot hypotheses unless evidence exists. +- Create a one-page security/architecture diligence download tied to the evidence release. + +Acceptance criteria: + +- Public partner pages contain no customer logos or implied partnerships without permission. +- Named briefs are absent from sitemap, public nav, public analytics, and search indexing. +- FHIR/Epic, co-branding, refill routing, and reporting are labeled proposed/roadmap. +- Every pilot page distinguishes product capabilities, partner work, dependencies, and outcome hypotheses. +- All three PDFs pass regression tests. + +Dependencies: + +- Partner-distribution decision. +- Counsel review of outcome/compliance claims. +- Product decision on the primary 90-day commercial motion. + +### Phase 5 — Enrichment and learning loop + +**Outcome:** richer media and analytics improve comprehension without compromising privacy or performance. + +Tasks: + +- Add the optional full product demo and architecture animation. +- Instrument the minimal aggregate event taxonomy. +- Run five patient/caregiver comprehension sessions and three partner-evaluator sessions. +- Test hero/headline and beta-flow comprehension, not medical fear. +- Publish a lightweight release/evidence changelog. +- Tune performance and accessibility from real monitoring. + +Acceptance criteria: + +- No analytics event includes free text, email, medical content, or named-brief paths. +- Demo is user-controlled, captioned, transcribed, reduced-motion safe, and not LCP-critical. +- Research participants can correctly explain the app's limits as well as its value. +- Changes are driven by observed confusion/conversion, not page decoration. + +## 15. Priorities: now, next, later + +### Now + +1. Freeze factual copy into a reviewed claim registry. +2. Capture print baselines and protect named briefs. +3. Replace the Northwell root with the consumer narrative. +4. Reuse the product showcase and screenshot library to tell one continuous record story. +5. State beta, sharing, AI, network, storage, and integration boundaries precisely. + +### Next + +1. Add the workflow, caregiver, emergency, beta, privacy, and evidence pages. +2. Recapture a consistent synthetic profile. +3. Add route-specific partner protection and noindex behavior. +4. Build generic health-system and pharmacy pilot pages. + +### Later + +1. Refactor duplicated brief interiors after visual regression infrastructure exists. +2. Add richer video/motion after the still narrative performs well. +3. Add research/press resources when there is verified beta evidence to publish. +4. Add FHIR or live-sharing material only when those product capabilities actually exist. + +## 16. What not to build yet + +- A condition/drug content farm. +- A public competitor comparison matrix with brittle superiority claims. +- A diagnostic chatbot demo on the marketing site. +- Personalized ROI calculators based on unvalidated adherence or utilization assumptions. +- A portal-login/FHIR simulation that looks like a current integration. +- A live patient-record demo or website account system. +- Heavy 3D device scenes, autoplay hero video, or scroll-jacked storytelling. +- A CMS before content ownership and release verification have become genuine bottlenecks. +- A single white-label template that flattens material differences between a health system, an integrated payer/pharmacy, and a pharmacy without an owned plan. + +## 17. Definition of a successful richer site + +The richer website succeeds if it produces clarity, not merely visual density: + +- A caregiver sees their own weekly coordination problem before reading about AI. +- A patient understands how daily use can keep one record more current. +- A technical reviewer can map each sensitive task to its actual data boundary. +- A partner evaluator can distinguish what is built, what a pilot would test, and what requires integration. +- A beta participant knows that generated output requires review and is not medical advice. +- No visitor leaves believing Medscribe currently has app-level at-rest encryption, universal drug/interactions coverage, FDA review, FHIR/Epic integration, automatic caregiver sync, full Signal protocol, measured clinical outcomes, or production readiness. +- Northwell, CVS, and Walgreens print artifacts continue to work exactly as designed. + +That combination—human recognition, real product proof, and unusually exact limits—is a stronger expression of Medscribe's product thesis than a conventional “private medical AI” landing page. diff --git a/bun.lock b/bun.lock index 0f62f5f..5b4bd63 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", + "puppeteer-core": "^25.4.0", "tailwindcss": "^4.0.15", "typescript": "^5.8.2", "typescript-eslint": "^8.27.0", @@ -221,6 +222,8 @@ "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + "@puppeteer/browsers": ["@puppeteer/browsers@3.0.6", "", { "dependencies": { "modern-tar": "^0.7.6", "yargs": "^18.0.0" }, "peerDependencies": { "proxy-agent": ">=8.0.1", "yauzl": "^2.10.0 || ^3.4.0" }, "optionalPeers": ["proxy-agent", "yauzl"], "bin": { "browsers": "lib/main-cli.js" } }, "sha512-B/gKoqlFkzhvzsI6jo9K1cZz9o5ypviVv/xu8CwA4grZzyVwN+XfkT+tu8T1zrauuEXv6VhS2oGX+6NL95WcKA=="], + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], "@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], @@ -349,6 +352,8 @@ "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -403,10 +408,14 @@ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "chromium-bidi": ["chromium-bidi@17.0.2", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -437,6 +446,8 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "devtools-protocol": ["devtools-protocol@0.0.1653615", "", {}, "sha512-pGVkY3T/qXxAp2nFPodwYqOevk6ncNMSmvL8QfRCx5ZWGd6Vor7AFNmyaA8Zs6uJyP1QAfjuLandCgvSix1BNA=="], + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], "drizzle-kit": ["drizzle-kit@0.31.10", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw=="], @@ -469,6 +480,8 @@ "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], @@ -537,6 +550,10 @@ "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], @@ -705,6 +722,10 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "modern-tar": ["modern-tar@0.7.7", "", {}, "sha512-t9VmxaqrmANnEOBhpSDI6HD192Ge48k8vmWqQQL7hSFEqHEYwZbbsu49+aKLWZeRvFs3j1pMhXOqqF4kPlvjkQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.15", "", { "bin": "bin/nanoid.cjs" }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], @@ -767,6 +788,8 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "puppeteer-core": ["puppeteer-core@25.4.0", "", { "dependencies": { "@puppeteer/browsers": "3.0.6", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1653615", "typed-query-selector": "^2.12.2", "webdriver-bidi-protocol": "0.4.2", "ws": "^8.21.1" } }, "sha512-K1plkLOdeoUnGeT1OvdqF3qxl33v+Ra/uH5VyPEhXdMcpvGiEskHzxxEU3fgpccJpJLIipB/rPUsvkZRWeKqOA=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], @@ -831,6 +854,8 @@ "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + "string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="], + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], @@ -843,6 +868,8 @@ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], @@ -881,6 +908,8 @@ "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "possible-typed-array-names": "^1.1.0", "reflect.getprototypeof": "^1.0.10" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="], + "typed-query-selector": ["typed-query-selector@2.12.2", "", {}, "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript-eslint": ["typescript-eslint@8.62.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.62.0", "@typescript-eslint/parser": "8.62.0", "@typescript-eslint/typescript-estree": "8.62.0", "@typescript-eslint/utils": "8.62.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q=="], @@ -893,6 +922,8 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "webdriver-bidi-protocol": ["webdriver-bidi-protocol@0.4.2", "", {}, "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], @@ -905,6 +936,16 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yargs": ["yargs@18.1.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg=="], + + "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -923,6 +964,8 @@ "@unrs/resolver-binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + "cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -941,6 +984,10 @@ "tsx/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -987,6 +1034,8 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + "cliui/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], @@ -1039,6 +1088,8 @@ "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], } } diff --git a/package.json b/package.json index a3ec546..b019fd1 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", + "puppeteer-core": "^25.4.0", "tailwindcss": "^4.0.15", "typescript": "^5.8.2", "typescript-eslint": "^8.27.0" diff --git a/public/print-wash.svg b/public/print-wash.svg new file mode 100644 index 0000000..330a3f8 --- /dev/null +++ b/public/print-wash.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/screenshots/pitch/ask-black.webp b/public/screenshots/pitch/ask-black.webp new file mode 100644 index 0000000..e05bd40 Binary files /dev/null and b/public/screenshots/pitch/ask-black.webp differ diff --git a/public/screenshots/pitch/home-black.webp b/public/screenshots/pitch/home-black.webp new file mode 100644 index 0000000..1849f29 Binary files /dev/null and b/public/screenshots/pitch/home-black.webp differ diff --git a/public/screenshots/pitch/know-me-natural.webp b/public/screenshots/pitch/know-me-natural.webp new file mode 100644 index 0000000..a081c0f Binary files /dev/null and b/public/screenshots/pitch/know-me-natural.webp differ diff --git a/public/screenshots/pitch/medication-detail-natural.webp b/public/screenshots/pitch/medication-detail-natural.webp new file mode 100644 index 0000000..545189b Binary files /dev/null and b/public/screenshots/pitch/medication-detail-natural.webp differ diff --git a/public/screenshots/pitch/medications-black.webp b/public/screenshots/pitch/medications-black.webp new file mode 100644 index 0000000..60d8c73 Binary files /dev/null and b/public/screenshots/pitch/medications-black.webp differ diff --git a/public/screenshots/pitch/summary-natural.webp b/public/screenshots/pitch/summary-natural.webp new file mode 100644 index 0000000..1024e5d Binary files /dev/null and b/public/screenshots/pitch/summary-natural.webp differ diff --git a/public/screenshots/pitch/transcript-black.webp b/public/screenshots/pitch/transcript-black.webp new file mode 100644 index 0000000..168ad9e Binary files /dev/null and b/public/screenshots/pitch/transcript-black.webp differ diff --git a/public/screenshots/pitch/visits-natural.webp b/public/screenshots/pitch/visits-natural.webp new file mode 100644 index 0000000..320bc59 Binary files /dev/null and b/public/screenshots/pitch/visits-natural.webp differ diff --git a/scripts/capture-homepage.mjs b/scripts/capture-homepage.mjs new file mode 100644 index 0000000..241b4c3 --- /dev/null +++ b/scripts/capture-homepage.mjs @@ -0,0 +1,108 @@ +// Capture the long-form homepage in both system color schemes. +// +// bun scripts/capture-homepage.mjs [baseUrl] [outputDirectory] +// +// Requires a running local server and Google Chrome. If SITE_PASSWORD is set, +// the script uses the same signed access cookie as the login route. +import crypto from "node:crypto"; +import fs from "node:fs/promises"; +import path from "node:path"; +import puppeteer from "puppeteer-core"; + +const baseUrl = process.argv[2] ?? "http://localhost:3000"; +const outputDirectory = path.resolve(process.argv[3] ?? "output"); +const chrome = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; +const password = process.env.SITE_PASSWORD; + +await fs.mkdir(outputDirectory, { recursive: true }); + +const browser = await puppeteer.launch({ + executablePath: chrome, + headless: true, +}); + +async function capture({ name, width, height, colorScheme }) { + const page = await browser.newPage(); + + try { + await page.setViewport({ width, height, deviceScaleFactor: 1 }); + await page.emulateMediaFeatures([ + { name: "prefers-color-scheme", value: colorScheme }, + { name: "prefers-reduced-motion", value: "reduce" }, + ]); + + if (password) { + const value = crypto + .createHmac("sha256", password) + .update("medscribe-site-access-v1") + .digest("base64"); + await browser.setCookie({ + name: "medscribe_site_access", + value, + url: baseUrl, + }); + } + + await page.goto(baseUrl, { + waitUntil: "domcontentloaded", + timeout: 60_000, + }); + if (page.url().includes("/login")) { + throw new Error("Homepage capture was redirected to /login."); + } + + await page.addStyleTag({ + content: + ".pitch-lazy{content-visibility:visible!important}*{animation:none!important;transition:none!important}", + }); + + await page.evaluate(async () => { + await document.fonts.ready; + for (let y = 0; y < document.documentElement.scrollHeight; y += 700) { + window.scrollTo(0, y); + await new Promise((resolve) => setTimeout(resolve, 20)); + } + window.scrollTo(0, 0); + }); + await page.waitForFunction( + () => Array.from(document.images).every((image) => image.complete), + { timeout: 30_000 }, + ); + + const layout = await page.evaluate(() => ({ + clientWidth: document.documentElement.clientWidth, + scrollWidth: document.documentElement.scrollWidth, + scrollHeight: document.documentElement.scrollHeight, + })); + if (layout.scrollWidth > layout.clientWidth) { + throw new Error( + `${name} has horizontal overflow: ${layout.scrollWidth}px > ${layout.clientWidth}px`, + ); + } + + const destination = path.join(outputDirectory, `${name}.png`); + await page.screenshot({ path: destination, fullPage: true }); + console.log(`${name}: ${layout.clientWidth} × ${layout.scrollHeight}`); + } finally { + await page.close(); + } +} + +try { + for (const colorScheme of ["light", "dark"]) { + await capture({ + name: `homepage-${colorScheme}`, + width: 1440, + height: 1000, + colorScheme, + }); + await capture({ + name: `homepage-${colorScheme}-mobile`, + width: 390, + height: 844, + colorScheme, + }); + } +} finally { + await browser.close(); +} diff --git a/scripts/render-pdf.mjs b/scripts/render-pdf.mjs new file mode 100644 index 0000000..dcff70f --- /dev/null +++ b/scripts/render-pdf.mjs @@ -0,0 +1,66 @@ +// Render a marketing route to a print-styled PDF, the same way the browser's +// "Print PDF" button does (print CSS + designed print-only components). +// +// bun scripts/render-pdf.mjs [baseUrl] +// bun scripts/render-pdf.mjs /cvs ../docs/business/CVS_SUMMARY.pdf +// +// Self-authenticates past the SITE_PASSWORD gate by computing the same signed +// access cookie the login route issues (see src/lib/site-auth.ts). Requires a +// running dev/preview server and a local Google Chrome. +import crypto from "node:crypto"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import puppeteer from "puppeteer-core"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const route = process.argv[2] ?? "/cvs"; +const outArg = process.argv[3] ?? "../docs/business/CVS_SUMMARY.pdf"; +const baseUrl = process.argv[4] ?? "http://localhost:3000"; +const outPath = path.resolve(__dirname, "..", outArg); + +const CHROME = + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; +const AUTH_COOKIE_NAME = "medscribe_site_access"; +const ACCESS_PAYLOAD = "medscribe-site-access-v1"; + +const password = process.env.SITE_PASSWORD; +const cookies = []; +if (password) { + const token = crypto + .createHmac("sha256", password) + .update(ACCESS_PAYLOAD) + .digest("base64"); + cookies.push({ + name: AUTH_COOKIE_NAME, + value: token, + url: baseUrl, + }); +} + +const browser = await puppeteer.launch({ + executablePath: CHROME, + headless: true, +}); +try { + const page = await browser.newPage(); + if (cookies.length) await browser.setCookie(...cookies); + const res = await page.goto(`${baseUrl}${route}`, { + waitUntil: "networkidle0", + timeout: 60_000, + }); + const finalUrl = page.url(); + if (finalUrl.includes("/login")) { + throw new Error( + `Redirected to login (${res?.status()}). Check SITE_PASSWORD is set in the env used to run this script.`, + ); + } + await page.pdf({ + path: outPath, + printBackground: true, + preferCSSPageSize: true, + }); + console.log(`Wrote ${outPath}`); +} finally { + await browser.close(); +} diff --git a/src/app/(marketing)/beta/page.tsx b/src/app/(marketing)/beta/page.tsx new file mode 100644 index 0000000..498fe2b --- /dev/null +++ b/src/app/(marketing)/beta/page.tsx @@ -0,0 +1,226 @@ +import type { Metadata } from "next"; +import { + ArrowRight, + Check, + ClipboardCheck, + Mail, + MessageSquareText, + RotateCcw, + Smartphone, + Users, +} from "lucide-react"; +import Link from "next/link"; + +import { SectionLabel } from "~/components/section-label"; +import { Badge } from "~/components/ui/badge"; +import { Button } from "~/components/ui/button"; +import { productRelease } from "~/content/product"; +import { BETA_INVITE_MAILTO } from "~/lib/beta"; + +export const metadata: Metadata = { + title: "Request beta access", + description: + "Learn who the Medscribe internal beta is for, what participants should expect, and how to request access without submitting health information.", +}; + +const expectations = [ + { + icon: ClipboardCheck, + title: "Review is part of testing", + body: "Check generated summaries, extracted medication fields, and the Know Me view before relying on them.", + }, + { + icon: MessageSquareText, + title: "Direct feedback", + body: "Report confusing language, incorrect extraction, notification behavior, and sharing friction.", + }, + { + icon: RotateCcw, + title: "Possible resets", + body: "This is an internal beta. Builds, local data, and setup may need to be reset as the product changes.", + }, +] as const; + +const testFocus = [ + "Whether the record still feels current after several weeks", + "Medication-label edge cases and correction flow", + "Reminder permissions, timing, travel, and daylight-saving behavior", + "Caregiver pairing, encrypted export, and import comprehension", + "Whether the Know Me handoff is readable and appropriately cautious", + "Physical-device model performance and battery or thermal behavior", +] as const; + +export default function BetaPage() { + return ( + <> +
+
+
+
+ {productRelease.stage} + {productRelease.platforms} +
+

+ Test the care context, not just the demo. +

+

+ We are looking for people managing repeated visits or several + medications, especially family caregiver pairs willing to review + the record over time and tell us where it fails. +

+
+ +
+
+
+

+ Request access by email +

+

+ The draft asks only for your platform, whether you are testing as + a patient, caregiver, or both, and whether you can test with + another person. +

+ +

+ Do not send diagnoses, medication names, visit notes, transcripts, + or other health details. Email is not a medical-support or + emergency channel. +

+
+
+
+ +
+
+ A good fit +

+ The beta needs real maintenance and handoff situations. +

+
+ {[ + { + icon: Smartphone, + title: "Several moving pieces", + body: "You regularly coordinate visits, medication reminders, or follow-ups.", + }, + { + icon: Users, + title: "A caregiver relationship", + body: "You can test a deliberate handoff with a trusted family caregiver.", + }, + { + icon: Check, + title: "Comfort reviewing", + body: "You understand generated information can be wrong and will check it.", + }, + ].map((item) => { + const Icon = item.icon; + + return ( +
+
+ ); + })} +
+
+
+ +
+
+
+
+ What to expect +

+ This is collaborative product testing. +

+

+ Availability is controlled and continuity is not guaranteed. + Medscribe is not emergency care, diagnosis, dosing guidance, or + a replacement for your care team. +

+
+
+ {expectations.map((item) => { + const Icon = item.icon; + + return ( +
+
+ ); + })} +
+
+ +
+
+
+

+ Current test focus +

+

+ The questions we are trying to answer +

+
+
    + {testFocus.map((item) => ( +
  • +
  • + ))} +
+
+
+ +
+
+

+ Want the technical boundaries first? +

+

+ See what stays local, how sharing works, and the current + evidence. +

+
+ +
+
+
+ + ); +} diff --git a/src/app/(marketing)/briefs/northwell/page.tsx b/src/app/(marketing)/briefs/northwell/page.tsx new file mode 100644 index 0000000..37c05d0 --- /dev/null +++ b/src/app/(marketing)/briefs/northwell/page.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from "next"; + +import { NorthwellPrintSummary } from "~/components/northwell-print-summary"; +import { PrintButton } from "~/components/print-button"; +import { SectionLabel } from "~/components/section-label"; + +export const metadata: Metadata = { + title: "Northwell executive summary", + robots: { index: false, follow: false, nocache: true }, +}; + +export default function NorthwellBriefPage() { + return ( + <> + +
+ Executive summary · Northwell Health +

+ The health-system executive summary +

+

+ This private, search-excluded route preserves the existing three-page + print document. Use Print PDF (or Cmd+P, then Save as PDF) to export + it. +

+
+ +
+
+ + ); +} diff --git a/src/app/(marketing)/cvs/page.tsx b/src/app/(marketing)/cvs/page.tsx new file mode 100644 index 0000000..b7d6b39 --- /dev/null +++ b/src/app/(marketing)/cvs/page.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from "next"; + +import { CvsPrintSummary } from "~/components/cvs-print-summary"; +import { PrintButton } from "~/components/print-button"; +import { SectionLabel } from "~/components/section-label"; + +export const metadata: Metadata = { + title: "CVS executive summary", + robots: { index: false, follow: false, nocache: true }, +}; + +export default function CvsSummaryPage() { + return ( + <> + +
+ Executive summary · CVS Health +

+ The pharmacy executive summary +

+

+ This page is formatted as a two-page print document: the medication + record that moves adherence, keeps refills, and lowers cost of care. + Use Print PDF (or Cmd+P, then Save as PDF) to export it. +

+
+ +
+
+ + ); +} diff --git a/src/app/(marketing)/executive-summary/page.tsx b/src/app/(marketing)/executive-summary/page.tsx index 20d129c..952395f 100644 --- a/src/app/(marketing)/executive-summary/page.tsx +++ b/src/app/(marketing)/executive-summary/page.tsx @@ -21,6 +21,7 @@ export const metadata: Metadata = { title: "Executive Summary", description: "One-page executive summary for Medscribe, a private medical context app for families managing complex care.", + robots: { index: false, follow: false, nocache: true }, }; const TAGLINE = diff --git a/src/app/(marketing)/how-it-works/page.tsx b/src/app/(marketing)/how-it-works/page.tsx new file mode 100644 index 0000000..3ada5d5 --- /dev/null +++ b/src/app/(marketing)/how-it-works/page.tsx @@ -0,0 +1,268 @@ +import type { Metadata } from "next"; +import { + ArrowRight, + Camera, + Check, + FileText, + Keyboard, + Mic, + ShieldAlert, +} from "lucide-react"; +import Link from "next/link"; + +import { PhoneMockup } from "~/components/phone-mockup"; +import { SectionLabel } from "~/components/section-label"; +import { Badge } from "~/components/ui/badge"; +import { Button } from "~/components/ui/button"; +import { productClaims } from "~/content/product"; + +export const metadata: Metadata = { + title: "How it works", + description: + "See how Medscribe turns visits and medication routines into one local record a person can review and carry.", +}; + +const inputs = [ + { + icon: Keyboard, + title: "Type or paste", + body: "Start from notes you already have and keep the source close to the result.", + }, + { + icon: Camera, + title: "Import a screenshot", + body: "Bring in a portal summary or prescription label, then correct extracted fields.", + }, + { + icon: FileText, + title: "Attach a document", + body: "Use a photo or document as the input to a local review workflow.", + }, + { + icon: Mic, + title: "Record with consent", + body: "Optional visit audio requires consent and is transcribed on the device.", + }, +] as const; + +const boundaries = [ + "It does not diagnose or recommend treatment.", + "It does not calculate, shift, or supply medication timing.", + "It does not replace a pharmacist, prescribing team, or emergency service.", + "It does not automatically synchronize a household in the background.", +] as const; + +export default function HowItWorksPage() { + return ( + <> +
+
+ Current beta workflow +

+ One record, built through everyday use. +

+

+ Medscribe is useful at a handoff only if ordinary moments keep the + picture current. Every generated result has a person in the review + loop. +

+
+
+ +
+
+ Start where the information is +

+ Four ways in. One review checkpoint. +

+
+ {inputs.map((item, index) => { + const Icon = item.icon; + + return ( +
+
+ + + + 0{index + 1} + +
+

+ {item.title} +

+

+ {item.body} +

+
+ ); + })} +
+
+
+
+
+ +
+
+
+
+ Review what changed +

+ A summary is a draft, not a verdict. +

+

+ Supported transcription and summarization run locally. The visit + remains available beside the summary so a person can check + follow-ups and wording. +

+

+ What comes next: a + structured review inbox for accepting, correcting, or dismissing + changes inferred from a visit. +

+
+
+ +

+ Synthetic demo data · beta interface +

+
+
+ +
+
+ +

+ Synthetic demo data · beta interface +

+
+
+ Maintain the medication picture +

+ Facts and schedules carry their boundary with them. +

+

+ {productClaims.medicationFacts.publicCopy} Schedules can record + whether they are fixed-time or as-needed, the times supplied by + the person's source, confirmation, and timezone. +

+

+ Medscribe records timing supplied by a label, pharmacist, or + prescribing team. It does not calculate or shift medication + times. +

+
+
+ +
+
+ Prepare the handoff +

+ Know Me is the view. Sharing is a separate choice. +

+

+ Open the local Know Me screen when someone needs a concise + picture. For a caregiver, pair devices, compare a safety number, + review the record, and manually export an encrypted snapshot. +

+

+ The current import path does not yet require the sender to be in + the recipient's active trust list, so recipients must + verify the sender out of band. Revocation prevents a future + local export; it cannot invalidate already-exported ciphertext + or erase a copy the recipient imported. +

+
+
+ +

+ Synthetic demo data · beta interface +

+
+
+
+
+ +
+
+
+
+
+

+ What Medscribe does not do +

+

+ A useful boundary is part of the product, not fine print after it. +

+
+
    + {boundaries.map((item) => ( +
  • +
  • + ))} +
+
+
+ +
+
+
+

+ See the trust boundary task by task. +

+

+ Read what stays local, what can use a network, and what remains + unfinished. +

+
+ +
+
+ + ); +} diff --git a/src/app/(marketing)/landscape/page.tsx b/src/app/(marketing)/landscape/page.tsx index d6313b6..ace0d8d 100644 --- a/src/app/(marketing)/landscape/page.tsx +++ b/src/app/(marketing)/landscape/page.tsx @@ -14,6 +14,7 @@ export const metadata: Metadata = { title: "Solution Landscape", description: "How the medical-context space is divided today, and the combined job no existing product owns.", + robots: { index: false, follow: false, nocache: true }, }; const INTRO = diff --git a/src/app/(marketing)/layout.tsx b/src/app/(marketing)/layout.tsx index 7b31c64..b7ea926 100644 --- a/src/app/(marketing)/layout.tsx +++ b/src/app/(marketing)/layout.tsx @@ -7,9 +7,15 @@ export default function MarketingLayout({ }: Readonly<{ children: React.ReactNode }>) { return ( <> + + Skip to content + -
{children}
+
{children}
); diff --git a/src/app/(marketing)/page.tsx b/src/app/(marketing)/page.tsx index c0b10a6..a56d3f6 100644 --- a/src/app/(marketing)/page.tsx +++ b/src/app/(marketing)/page.tsx @@ -1,229 +1,522 @@ -import { HeartPulse, Layers, ShieldCheck, Users } from "lucide-react"; - -import { PhoneMockup } from "~/components/phone-mockup"; -import { SectionLabel } from "~/components/section-label"; -import { BETA_INVITE_MAILTO } from "~/lib/beta"; -import { Badge } from "~/components/ui/badge"; -import { Button } from "~/components/ui/button"; +import type { Metadata } from "next"; import { - Card, - CardDescription, - CardHeader, - CardTitle, -} from "~/components/ui/card"; + ArrowRight, + Check, + ContactRound, + KeyRound, + LockKeyhole, + ScanLine, + ShieldCheck, + Smartphone, +} from "lucide-react"; +import Link from "next/link"; -const fit = [ +import { ProductDevice } from "~/components/product-device"; +import { Button } from "~/components/ui/button"; +import { productClaims, productRelease } from "~/content/product"; +import { BETA_INVITE_MAILTO } from "~/lib/beta"; + +export const metadata: Metadata = { + title: "Carry the context when care gets complicated", + description: + "Medscribe is a private, patient-controlled beta that helps keep visits, medications, and critical context together on your device.", +}; + +const visitMoments = [ { - icon: Layers, - title: "Complements Epic, doesn't compete", - body: "Different job. Epic is the record of care inside Northwell. Medscribe is the patient-owned layer beside it, holding what Epic can't reach: the non-Epic care, the between-visit context, the emergency handoff. With the patient's consent, that context flows back into the workflows Northwell already runs, raising the value of the Epic investment rather than competing for it.", + number: "01", + eyebrow: "Visits stay findable", + title: "The visit has a place to land.", + body: "Keep the recording, transcript, and reviewed summary together instead of reconstructing the appointment from memory.", + image: "/screenshots/pitch/visits-natural.webp", + alt: "Medscribe Visits screen in a natural titanium iPhone product bezel", }, { - icon: Users, - title: "Keeps patients active in their own care", - body: "Reminders and refills the app sets for them, dose tracking, and, between visits, questions routed back to their own care team rather than out to a search engine. Low-friction by design, because a tool the patient actually uses is what turns into adherence, engagement, and in-network follow-through, the things Northwell is measured on.", + number: "02", + eyebrow: "Words stay checkable", + title: "Go back to what was actually said.", + body: "Search the local transcript and compare it with the generated summary before anything becomes part of the record you rely on.", + image: "/screenshots/pitch/transcript-black.webp", + alt: "Medscribe visit transcript in a black titanium iPhone product bezel", }, { - icon: HeartPulse, - title: "Patient-centered where it counts", - body: "The emergency handoff, the family caregiver covering a shift, the newly-diagnosed patient who half-remembers the plan. Fewer blank handoffs, safer medication moments, patients who stay engaged instead of going dark.", + number: "03", + eyebrow: "The next step stays visible", + title: "Review the useful part. Keep the uncertainty.", + body: "Medications, follow-ups, diagnoses, and instructions are prepared on-device. Suggestions remain suggestions until a person checks them.", + image: "/screenshots/pitch/summary-natural.webp", + alt: "Medscribe appointment summary in a natural titanium iPhone product bezel", + }, +] as const; + +const proofStats = [ + { + value: productRelease.drugFacts.toLocaleString("en-US"), + label: "bundled medication records", + }, + { + value: productRelease.rxNormTerms.toLocaleString("en-US"), + label: "RxNorm terms for matching", + }, + { + value: productRelease.interactionPairs.toLocaleString("en-US"), + label: "known interaction pairs", + }, +] as const; + +const privacyPrinciples = [ + { + icon: Smartphone, + title: "Your working record stays local", + body: productClaims.localRecord.publicCopy, + }, + { + icon: LockKeyhole, + title: "AI runs on the device", + body: productClaims.localAi.publicCopy, }, { icon: ShieldCheck, - title: "Private by architecture", - body: "The sensitive work (summarizing visits, answering medication questions, building the record) runs entirely on the device, not the cloud. No health data sent to a server for AI processing, no new inbox to breach, no BAA to negotiate.", + title: "Facts do not come from model memory", + body: productClaims.medicationFacts.publicCopy, }, -]; +] as const; + +const betaFit = [ + "Managing several medications or repeated visits", + "Coordinating care with a family caregiver", + "Willing to review generated information and report errors", +] as const; export default function HomePage() { return ( -
- {/* Phone floats right on sm+; prose sections wrap around it like a newspaper column */} -
- -
- -
- {/* Origin */} -
- Executive summary · Northwell Health -

- Be known when care gets complicated + <> +
+
+
+
+ + {productRelease.stage} + + + {productRelease.platforms} + +
+

+ Carry the context.

-
-

- A grandfather with three adult children, each living twenty to - fifty minutes away. None able to be at every appointment. No - shared, current view of his medications, his visits, or what had - changed. -

-

- Then came the emergency. A fall. A hospital that did not - understand his condition. Some of his medications were - time-critical. They were administered incorrectly, his decline was - misread, the wrong path was taken, and he passed six weeks later. - The clinicians were not negligent. The system was blind to who he - was, and no trustworthy picture of him existed to hand over. -

-

- Medscribe is the private, patient-owned record built for that gap: - the whole picture, held by the patient, ready to hand over the - moment the chart in front of you doesn't have it. -

-
-
- On-device · no cloud AI - Emergency-ready - Complements Epic - iOS · Android beta -
-
- - {/* What it is */} -
- What it is -

- A medical record the patient owns and carries -

-

- Current medications, allergies, baseline, visit summaries, and an - emergency “know-me” record, kept current through - everyday use, and ready to hand to a caregiver, a new doctor, or an - ER in seconds. Everything runs on the device. Nothing is uploaded. +

+ Visits, medications, and the critical details between them—kept + together on the device you carry.

-
-

- How the record stays current (live in beta today) -

-
    -
  • - Visit summaries: type - or paste notes, import MyChart and portal after-visit summaries - from a screenshot, or attach photos and documents; optional - consent-gated audio recording for patients who want it. Any path - feeds the same on-device summary. -
  • -
  • - Medication management: - pill-bottle label recognition, reminders, and on-device Q&A - grounded in FDA drug facts -
  • -
  • - Emergency handoff: a - know-me card and encrypted caregiver sharing, patient-controlled -
  • -
-

- Structured MyChart / FHIR export import is on the roadmap: - import-and-own, not write-back. -

+
+ +
-
+

- {/* The gap Epic's own tools can't close */} -
- Where the picture breaks -

- Epic knows what Epic sees -

-

- MyChart is excellent inside the Epic walls. It puts the chart on the - patient's phone, links their Epic portals through MyChart - Central, and lets them share with a caregiver or a provider. What no - institutional record can be is the whole picture: the urgent care, - the community specialist, the facility that isn't on Epic; the - changes between visits; the context a patient keeps themselves. Epic - aggregates Epic. And when it matters most, at an out-of-network ER, - or when the patient can't generate a share code, the record - that's actually there is the one they already carry. +

+
+ +
+
+ +
+

+ Synthetic demo data · beta interface

-
+
+ - {/* What makes it different */} -
- What makes it different -

- No one else owns the whole job -

-

- Existing Electronic Health Record (EHR) systems owns the - institutional view, and every institutional tool stops at its own - walls. Around them, the market is full of single-purpose apps: visit - notetakers, medication trackers, record vaults, emergency-ID cards. - Each owns one slice. None holds a private, patient-owned record that - spans every system, Epic and non-Epic alike, stays current through - daily use, and is ready to hand over in a crisis, all on the - patient's own device. Medscribe is that combination, beside - Epic, not instead of it. +

+
+

+ The missing picture

-
+

+ Care happens in fragments. Your context shouldn't. +

+

+ One instruction lives in a portal. A medication label sits in a + cabinet. The latest change lives in someone's memory. Medscribe + is the patient-held thread between those moments. +

+ +
- {/* Why it fits Northwell: clear float so the card grid spans full width */} -
-
- Why it fits Northwell -

- Built to strengthen the Epic investment, not compete with it +
+
+
+

+ After the visit +

+

+ Turn what happened into something you can check.

+

+ Bring in typed notes, a portal screenshot, a document, or an + optional consent-gated recording. The work happens locally. You + decide what is useful. +

+
+

+ The safety loop:{" "} + review generated information before acting. Medscribe does not + diagnose, prescribe, or replace clinical verification. +

+
-
- {fit.map((item) => { - const Icon = item.icon; + +
    + {visitMoments.map((moment) => ( +
  1. +
    +
    + + {moment.number} + +

    + {moment.eyebrow} +

    +
    +

    + {moment.title} +

    +

    + {moment.body} +

    +
    +
    +
    + +
    +
  2. + ))} +
+
+
+ +
+
+
+
+

+ Between visits +

+

+ Keep the medication picture from going stale. +

+

+ The list, the label, the schedule, and the latest confirmation + belong in the same view—not scattered across paper and memory. +

+
+ +
+
+
+ +
+
+ +
+
+ +
+ {[ + { + title: "Show where a schedule came from", + body: "Schedules can carry their source, confirmation, timezone, and freshness state.", + }, + { + title: "Keep facts deterministic", + body: productClaims.medicationFacts.publicCopy, + }, + { + title: "Make uncertainty visible", + body: "Missing or stale details stay visible so the person or clinician can verify them.", + }, + ].map((item) => ( +
+
+ ))} +
+
+
+
+ +
+
+
+

+ Ask from the record +

+

+ Better questions begin with your context. +

+

+ Ask about a visit or medication without uploading the record to a + remote model. Answers are grounded in local history and bundled + facts, with the boundary kept in view. +

+

+ Informational only. Medication changes still belong with a + clinician or pharmacist. +

+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+

+ At the handoff +

+

+ Bring the picture when the chart is incomplete. +

+

+ Know Me gathers selected medications, allergies, conditions, + baseline, and contacts into one readable snapshot. Freshness + signals show what may need another look. +

+
+
+
+
+
+
+
+
+ +
+
+
+
+
    + {[ + ["1", "You review", "Choose the record to send"], + ["2", "Encrypted copy", "Protected for the recipient key"], + ["3", "They import", "A local, readable snapshot"], + ].map(([number, title, body]) => ( +
  1. + + {number} + +

    + {title} +

    +

    + {body} +

    +
  2. + ))} +
+
+

+ Manual today—not live sync or a household cloud account. + Import-side sender trust enforcement is still being hardened; + revocation cannot recall a copy already exported. +

+
+
+
+ +
+
+
+
+

+ A precise privacy boundary +

+

+ Local changes what you have to trust. +

+

+ The working record and supported AI tasks stay on the device. + Medication facts come from bundled references, not a model trying + to remember medicine. +

+
+ +
+ {proofStats.map((stat) => ( +
+
{stat.label}
+
+ {stat.value} +
+
+ ))} +
+ +
+ {privacyPrinciples.map((principle) => { + const Icon = principle.icon; return ( - - -
- -
- {item.title} - {item.body} -
-
+
+
); })}
-
- {/* Who's behind it + the ask */} -
- - -
- - Who's behind it - - - Sean O'Connor, a Bucknell computer science and engineering - graduate, built Medscribe after watching his own family try to - hold this together for his grandfather, when the record - scattered exactly at the moment it mattered most. It is live in - beta on iOS and Android today. - - - The ask is a short conversation about whether a patient-owned - record that spans the systems Epic can't reach, and feeds - that context back into Northwell's Epic, is worth - exploring together. - -
- +

+ +
+ +
+
+

+ The right beta participant +

+

+ Help build the record people can actually carry. +

+

+ This is an internal beta. Expect close feedback, manual review, + rough edges, and possible resets—not guaranteed continuity. +

+
- - + + + {item} + + ))} + +
+ + +
+

+ Start with your platform and whether you are testing as a patient, + caregiver, or both. Please do not email health details. +

+ + + ); } diff --git a/src/app/(marketing)/privacy/page.tsx b/src/app/(marketing)/privacy/page.tsx new file mode 100644 index 0000000..b947ebe --- /dev/null +++ b/src/app/(marketing)/privacy/page.tsx @@ -0,0 +1,388 @@ +import type { Metadata } from "next"; +import { + AlertTriangle, + ArrowRight, + Check, + Database, + FileCheck2, + KeyRound, + LockKeyhole, + Wifi, +} from "lucide-react"; +import Link from "next/link"; + +import { SectionLabel } from "~/components/section-label"; +import { Badge } from "~/components/ui/badge"; +import { Button } from "~/components/ui/button"; +import { productClaims, productRelease } from "~/content/product"; + +export const metadata: Metadata = { + title: "Privacy & evidence", + description: + "A task-by-task view of Medscribe's local architecture, network behavior, caregiver sharing, evidence, and beta limitations.", +}; + +const dataMap = [ + { + task: "Visit transcription", + location: "Device", + network: "No cloud inference", + control: "Recording requires consent", + }, + { + task: "Summary generation", + location: "Device", + network: "No cloud inference", + control: "The person reviews output", + }, + { + task: "Medication-label OCR", + location: "Device", + network: "No cloud inference", + control: "Extracted fields remain editable", + }, + { + task: "Drug facts and known pairs", + location: "Bundled on device", + network: "No runtime fact lookup", + control: "Sources and coverage are disclosed", + }, + { + task: "Model installation or update", + location: "Downloaded to device", + network: "Network may be required", + control: "Approved URLs and file integrity checks", + }, + { + task: "Caregiver sharing", + location: "Encrypted device-to-device snapshot", + network: "The person chooses ciphertext transport", + control: "Verify out of band; import trust enforcement is incomplete", + }, + { + task: "Primary record storage", + location: "Local app database", + network: "No Medscribe cloud account", + control: "The unlocked device remains a trust boundary", + }, + { + task: "Website analytics", + location: "Marketing site only", + network: "Optional Umami script when configured", + control: "Disabled when no website ID is configured", + }, +] as const; + +const protects = [ + "Runtime disclosure of visits or medications to a cloud AI provider", + "A Medscribe account database becoming the default copy of the record", + "A readable caregiver export being sent to someone who was not paired", +] as const; + +const doesNotProtect = [ + "Information viewed on an unlocked or compromised phone", + "A snapshot that a verified recipient already imported", + "Someone intentionally copying or sharing visible information", + "Every possible drug interaction or model failure", +] as const; + +export default function PrivacyPage() { + return ( + <> +
+
+
+ + Last verified {productRelease.verifiedOn} + + Internal beta +
+

+ Privacy should be specific enough to challenge. +

+

+ “Local” is not a magic word. Here is where each task happens, when a + network can be involved, what the design protects, and what it does + not. +

+
+
+ +
+
+ Data map +

+ Task by task, not “everything” absolutes. +

+ +
+ + + + + {[ + "Task", + "Where it happens", + "Network behavior", + "User control", + ].map((heading) => ( + + ))} + + + + {dataMap.map((row) => ( + + + + + + + ))} + +
+ Medscribe tasks, processing location, network behavior, and user + control +
+ {heading} +
+ {row.task} + + {row.location} + + {row.network} + + {row.control} +
+
+
+
+ +
+
+
+ {[ + { + icon: LockKeyhole, + title: "Supported AI work stays local", + body: productClaims.localAi.publicCopy, + }, + { + icon: Database, + title: "The primary record stays local", + body: `${productClaims.localRecord.publicCopy} The beta relies on the device and operating system's protections; it does not claim a separately encrypted app database.`, + }, + { + icon: Wifi, + title: "Downloads are the network exception", + body: "Installing or updating a model can require a network. Approved model files are checked against an expected SHA-256 digest before use.", + }, + ].map((item) => { + const Icon = item.icon; + + return ( +
+ + +

+ {item.title} +

+

+ {item.body} +

+
+ ); + })} +
+
+
+ +
+
+
+
+
+
+ Caregiver sharing +

+ Verify a person, then export deliberately. +

+

+ New snapshots use authenticated encryption targeted to the + recipient key. Ciphertext integrity and wrong-recipient + rejection are tested. The current import path does not yet + require the sender to be in the recipient's active trust + list, and recipient-key metadata is not authenticated and + checked on import. Verify the sender out of band. The beta does + not claim the Signal protocol or live synchronization. +

+
+
+

+ The practical rule +

+
    + {[ + "Compare the safety number with the intended caregiver.", + "Review the snapshot before every material export.", + "Re-export after important changes; it is not automatic.", + "Treat revocation as a block on future local exports; it cannot invalidate already-exported ciphertext.", + ].map((item, index) => ( +
  1. + + {index + 1} + + {item} +
  2. + ))} +
+
+
+
+
+ +
+
+ Evidence, not superlatives +
+
+

+ What the current release can actually point to. +

+
+

+ Medication language is based on named public reference datasets, + but condensed wording is not FDA-reviewed. Coverage counts + describe the bundle; they do not prove clinical completeness. +

+
+ +
+ {[ + [ + productRelease.drugFacts.toLocaleString(), + "Condensed drug records", + ], + [ + productRelease.rxNormTerms.toLocaleString(), + "RxNorm search terms", + ], + [ + productRelease.interactionPairs.toLocaleString(), + "Known high-priority pairs", + ], + [ + productRelease.flaggedForReview.toLocaleString(), + "Records flagged for review", + ], + ].map(([value, label]) => ( +
+
+ {label} +
+
+ {value} +
+
+ ))} +
+ +
+
+
+
+ +
+
+
+
+
+
    + {protects.map((item) => ( +
  • + {item} +
  • + ))} +
+
+
+
+
+
    + {doesNotProtect.map((item) => ( +
  • + {item} +
  • + ))} +
+
+
+ +
+
+

+ Help test the boundary. +

+

+ The internal beta is focused on correctness, comprehension, and + caregiver handoffs. +

+
+ +
+
+ + ); +} diff --git a/src/app/(marketing)/walgreens/page.tsx b/src/app/(marketing)/walgreens/page.tsx new file mode 100644 index 0000000..1dcc2b6 --- /dev/null +++ b/src/app/(marketing)/walgreens/page.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from "next"; + +import { PrintButton } from "~/components/print-button"; +import { SectionLabel } from "~/components/section-label"; +import { WalgreensPrintSummary } from "~/components/walgreens-print-summary"; + +export const metadata: Metadata = { + title: "Walgreens executive summary", + robots: { index: false, follow: false, nocache: true }, +}; + +export default function WalgreensSummaryPage() { + return ( + <> + +
+ Executive summary · Walgreens +

+ The pharmacy executive summary +

+

+ This page is formatted as a two-page print document: what Medscribe + can do, and how every capability pays off twice, for the patient and + for Walgreens. Use Print PDF (or Cmd+P, then Save as PDF) to export + it. +

+
+ +
+
+ + ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c4283e2..7704a11 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -17,11 +17,12 @@ const playfair = Playfair_Display({ export const metadata: Metadata = { title: { - default: "Medscribe | Be known when care gets complicated", + default: "Medscribe | Carry the context when care gets complicated", template: "Medscribe | %s", }, description: - "A private, emergency-ready medical context app for families managing complex care. Keep visits, medications, baseline, allergies, and caregiver handoffs current on device.", + "A private, patient-controlled beta that helps keep visits, medications, and critical context together on your device.", + applicationName: "Medscribe", icons: { icon: [ { url: "/favicon.ico", sizes: "any" }, @@ -29,12 +30,14 @@ export const metadata: Metadata = { { url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" }, { url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" }, ], - apple: [{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }], + apple: [ + { url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }, + ], }, openGraph: { title: "Medscribe", description: - "Be known when care gets complicated. Private medical context for families managing complex care.", + "Carry the context when care gets complicated. Private, patient-controlled medical context on your device.", type: "website", }, }; @@ -43,7 +46,10 @@ export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( - + {children} diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..82327f4 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,19 @@ +import type { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow: [ + "/api/", + "/briefs/", + "/cvs", + "/executive-summary", + "/landscape", + "/login", + "/walgreens", + ], + }, + }; +} diff --git a/src/components/cvs-print-summary.tsx b/src/components/cvs-print-summary.tsx new file mode 100644 index 0000000..564b309 --- /dev/null +++ b/src/components/cvs-print-summary.tsx @@ -0,0 +1,308 @@ +import Image from "next/image"; +import { + HeartPulse, + Layers, + Mic, + MessageSquare, + ScanLine, + ShieldCheck, +} from "lucide-react"; + +/** + * Pharmacy-specific print summary (CVS-targeted, reusable for other pharmacies). + * Two designed pages, built for the product rather than mirroring the Northwell + * layout: + * Page 1 - what Medscribe can do (capabilities, patient-facing power). + * Page 2 - every capability pays off twice (patient value -> CVS value). + * Reuses the `northwell-print-*` sheet/label classes from globals.css only as + * the page frame; the interior is its own design. + */ + +function PrintPage({ + page, + children, +}: Readonly<{ page: number; children: React.ReactNode }>) { + return ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + + {page === 1 ? ( +
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + + + Medscribe + +
+ + Executive summary · CVS Health + +
+ ) : null} + +
{children}
+ +
+ Page {page} of 2 +
+
+ ); +} + +const capabilities = [ + { + icon: Mic, + title: "Understand a visit", + body: "Type, paste, snap the after-visit summary, or record the conversation with consent. Medscribe transcribes and summarizes it on the phone.", + }, + { + icon: ScanLine, + title: "Read a pill bottle", + body: "Point the camera at a prescription label and the drug, dose, and schedule land in the record automatically.", + }, + { + icon: MessageSquare, + title: "Answer the hard questions", + body: "Ask whether two medications are safe together and get an answer grounded in bundled FDA drug facts and known interaction pairs, not a chatbot's guess.", + }, + { + icon: Layers, + title: "Hold the whole picture", + body: "Medications, allergies, conditions, baseline, and visit history across every provider, in one place the patient owns.", + }, + { + icon: HeartPulse, + title: "Be ready for an emergency", + body: "A patient-controlled Know Me card puts the time-critical facts one tap away for a caregiver or an ER.", + }, + { + icon: ShieldCheck, + title: "Share without exposure", + body: "End-to-end encrypted sharing lets a caregiver help, without a server ever reading the record.", + }, +] as const; + +const payoffs = [ + { + patientTitle: "One picture, everywhere they are treated", + patient: + "Nothing falls through the cracks between a specialist, an urgent care, and the pharmacy.", + cvsTitle: "One central pharmacy", + cvs: "Refills route back to CVS and scattered scripts consolidate, instead of leaking to Amazon, Cost Plus, or mail order. Share of scripts, won.", + }, + { + patientTitle: "Medications that fit their day", + patient: + "Reminders and refill nudges that actually help, so they stay on their medications.", + cvsTitle: "Star Ratings", + cvs: "Diabetes, hypertension, and statin adherence are triple-weighted in Part D Star Ratings: Quality Bonus Payments and retention for Aetna and SilverScript.", + }, + { + patientTitle: "Fewer dangerous surprises", + patient: + "Interactions and duplicate prescriptions caught across every prescriber, not one pharmacy's slice.", + cvsTitle: "Lower cost of care", + cvs: "Fewer adverse events and ER visits: direct medical spend saved for the members shared with Caremark and Aetna.", + }, + { + patientTitle: "Ready at the counter", + patient: + "They arrive with a clean list and their routine questions already answered.", + cvsTitle: "Pharmacist leverage", + cvs: "Less low-value counter volume, more time for the billable MTM and CMR work pharmacists already do.", + }, + { + patientTitle: "A tool that is theirs", + patient: + "An advocate they control, on their own device. Not a sales pitch, and never on a server.", + cvsTitle: "Engagement without the liability", + cvs: "A CVS-branded habit patients actually open, with no PHI on a server: no new breach surface and no BAA to negotiate.", + }, +] as const; + +export function CvsPrintSummary() { + return ( + + ); +} diff --git a/src/components/feature-showcase.tsx b/src/components/feature-showcase.tsx index 7092c9c..8d3cb0a 100644 --- a/src/components/feature-showcase.tsx +++ b/src/components/feature-showcase.tsx @@ -1,120 +1,122 @@ "use client"; +import type { KeyboardEvent } from "react"; import { useState } from "react"; import { CalendarDays, - Camera, Check, ContactRound, - Home, MessageCircle, - Shield, + Pill, } from "lucide-react"; import { PhoneMockup } from "~/components/phone-mockup"; import { cn } from "~/lib/utils"; const features = [ - { - id: "home", - label: "Profile", - icon: Home, - title: "One profile for the person you care for", - description: - "Visits, medications, reminders, and questions live together under the person they belong to.", - points: [ - "Switch between everyone you care for", - "One home for the whole picture", - "Nothing scattered across apps", - ], - image: "/screenshots/home.png", - alt: "Medscribe home dashboard with record visit, search, medications, and ask shortcuts", - }, - { - id: "context", - label: "Context", - icon: ContactRound, - title: "The context behind the handoff", - description: - "Conditions, medication changes, visit summaries, and caregiver notes stay close enough to become useful when time is short.", - points: [ - "Baseline, allergies, and conditions up front", - "Time-critical medications flagged", - "A source trail behind each fact", - ], - image: "/screenshots/emergency-record.png", - alt: "Medscribe emergency record with critical care context", - }, { id: "visits", label: "Visits", icon: CalendarDays, - title: "Visit recordings that refresh the record", + title: "Turn a visit into context you can review", description: - "A visit can become searchable context, so a medication change or follow-up instruction does not depend on memory alone.", + "Type or paste notes, import a portal screenshot or document, or use consent-gated recording. The beta creates a local summary for the person to check.", points: [ - "Consent-gated, on-device recording", - "Searchable transcript and summary", - "Follow-ups captured, not forgotten", + "Supported transcription and summarization run on device", + "The person reviews generated information", + "Structured visit-to-record change review is still roadmap", ], - image: "/screenshots/visit-timeline.png", - alt: "Medscribe visit timeline with appointment cards", + image: "/screenshots/visit-detail.png", + alt: "Synthetic Medscribe beta visit detail showing a visit summary and follow-up information", }, { - id: "meds", - label: "Meds", - icon: Camera, - title: "Medication OCR, reminders, and history", + id: "medications", + label: "Medications", + icon: Pill, + title: "Keep the daily medication picture close", description: - "Prescription labels, reminders, refill alerts, and dose history help the family see what is supposed to happen and what actually happened.", + "Scan a label, correct the extracted fields, set reminders, follow refills, and see a local intake history without asking a language model to invent drug facts.", points: [ - "Scan labels with the camera", - "Reminders and refill alerts", - "Dose history at a glance", + "Label text remains editable before it becomes the record", + "Bundled facts and known pairs use named reference datasets", + "Coverage is intentionally bounded, not a complete interaction check", ], - image: "/screenshots/medications.png", - alt: "Medscribe medications screen with active medications", + image: "/screenshots/medication-detail.png", + alt: "Synthetic Medscribe beta medication detail with schedule and medication information", }, { - id: "ask", - label: "Ask", + id: "questions", + label: "Questions", icon: MessageCircle, - title: "Medication questions grounded in the record", + title: "Facts first, fluent answers second", description: - "Plain-language questions can draw from the person's record and bundled FDA facts, while keeping diagnosis and dosing with clinicians.", + "Medication questions can use the person’s local record and bundled fields. Dosing changes, diagnosis, and treatment decisions stay with a clinician or pharmacist.", points: [ - "Plain-language answers", - "Grounded in the record and FDA facts", - "Defers dosing and diagnosis to clinicians", + "Known facts render from bundled data", + "The local model helps with conversational phrasing", + "High-risk or unsupported questions defer to a professional", ], - image: "/screenshots/chat.png", - alt: "Medscribe Ask chat answering a question about blood pressure medications", + image: "/screenshots/chat-response.png", + alt: "Synthetic Medscribe beta answer with medication context and a safety boundary", }, { - id: "privacy", - label: "Privacy", - icon: Shield, - title: "Private by architecture, not policy", + id: "handoff", + label: "Know Me", + icon: ContactRound, + title: "Prepare one readable handoff", description: - "The sensitive work happens on device, so the family record is not another cloud inbox waiting to be mined.", + "The Know Me view brings selected medications, allergies, conditions, baseline, and contacts together. Current, unconfirmed, and review-needed states make uncertainty visible.", points: [ - "On-device AI, no cloud processing", - "No inbox to breach or mine", - "You control what is ever shared", + "Medication schedules can include source and confirmation", + "The person can show or share the snapshot they reviewed", + "It complements—not replaces—clinical verification", ], - image: "/screenshots/settings.png", - alt: "Medscribe settings showing on-device AI models and privacy notice", + image: "/screenshots/emergency-record.png", + alt: "Synthetic Medscribe beta Know Me handoff with active medications and review state", }, ] as const; +type FeatureId = (typeof features)[number]["id"]; + export function FeatureShowcase() { - const [activeId, setActiveId] = useState<(typeof features)[number]["id"]>("home"); - const active = features.find((f) => f.id === activeId) ?? features[0]; + const [activeId, setActiveId] = useState("visits"); + const active = + features.find((feature) => feature.id === activeId) ?? features[0]; + + const onTabKeyDown = (event: KeyboardEvent) => { + const activeIndex = features.findIndex( + (feature) => feature.id === activeId, + ); + let nextIndex: number | undefined; + + if (event.key === "ArrowRight" || event.key === "ArrowDown") { + nextIndex = (activeIndex + 1) % features.length; + } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") { + nextIndex = (activeIndex - 1 + features.length) % features.length; + } else if (event.key === "Home") { + nextIndex = 0; + } else if (event.key === "End") { + nextIndex = features.length - 1; + } + + if (nextIndex === undefined) return; + + event.preventDefault(); + const nextFeature = features[nextIndex]; + if (!nextFeature) return; + + setActiveId(nextFeature.id); + document.getElementById(`feature-tab-${nextFeature.id}`)?.focus(); + }; return ( -
-
-
+
+
+
{features.map((feature) => { const Icon = feature.icon; const isActive = feature.id === activeId; @@ -122,49 +124,67 @@ export function FeatureShowcase() { return ( ); })}
-
-

+
+

+ Current beta +

+

{active.title}

-

+

{active.description}

-
-
    - {active.points.map((point) => ( -
  • - - - - {point} -
  • - ))} -
+
    + {active.points.map((point) => ( +
  • + + + {point} +
  • + ))} +
+

-
- +
+ +

+ Synthetic demo data · beta interface +

diff --git a/src/components/northwell-print-summary.tsx b/src/components/northwell-print-summary.tsx new file mode 100644 index 0000000..f70e431 --- /dev/null +++ b/src/components/northwell-print-summary.tsx @@ -0,0 +1,381 @@ +import Image from "next/image"; + +function PrintPage({ + page, + children, +}: Readonly<{ page: number; children: React.ReactNode }>) { + return ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + + {page === 1 ? ( +
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + + + Medscribe + +
+ + Executive summary · Northwell Health + +
+ ) : null} + +
{children}
+ +
+ Page {page} of 3 +
+
+ ); +} + +const people = [ + [ + "The student on their own", + "Managing prescriptions and appointments for the first time, away from the parent who used to track it all.", + ], + [ + "The newly diagnosed", + "A new routine, new instructions, and follow-ups that are a blur by the next visit.", + ], + [ + "The long-distance caregiver", + "An adult child coordinating a parent's medications with siblings who cover different days.", + ], + [ + "The emergency", + "An older patient at an out-of-network ER, where no one has met them or knows which medications cannot be delayed.", + ], +] as const; + +const productPillars = [ + [ + "Capture visits", + "Type or paste notes, import a portal screenshot, attach a document, or use consent-gated recording. Summarization stays on device.", + ], + [ + "Keep medications current", + "Pill-bottle label recognition, reminders, refill tracking, dose history, and questions grounded in bundled FDA facts.", + ], + [ + "Be ready to hand off", + "A patient-controlled Know Me record and encrypted caregiver sharing keep critical context ready when the chart is incomplete.", + ], +] as const; + +const northwellFit = [ + [ + "AI patients can trust", + "Visit summarization and medication Q&A run on the phone, while medication facts come from bundled reference data rather than model recall. It is a clear AI posture for patients, privacy teams, and clinicians.", + ], + [ + "A Northwell story worth telling", + "Northwell can lead with a concrete patient benefit: one private, emergency-ready picture that travels across systems. The experience could carry Northwell's brand without pretending to replace MyChart.", + ], + [ + "An everyday touchstone", + "Reminders, refills, dose tracking, questions, and record updates create useful reasons to return between appointments. Each moment can point the patient back to the Northwell care team.", + ], +] as const; + +const recordContents = [ + "Active medications, doses, schedules, and refill status", + "Allergies, conditions, baseline, and the context that changes a handoff", + "Visit summaries, follow-up instructions, and questions for the next visit", + "Emergency contacts and a concise, patient-controlled Know Me record", +] as const; + +export function NorthwellPrintSummary() { + return ( + + ); +} diff --git a/src/components/phone-mockup.tsx b/src/components/phone-mockup.tsx index 42a6767..c6bdce3 100644 --- a/src/components/phone-mockup.tsx +++ b/src/components/phone-mockup.tsx @@ -1,7 +1,4 @@ -"use client"; - import Image from "next/image"; -import { IPhoneMockup } from "react-device-mockup"; import { cn } from "~/lib/utils"; @@ -27,33 +24,26 @@ export function PhoneMockup({ const isSimulatorCapture = captureMode === "simulator"; return ( -
- -
- {alt} -
-
+
+
+ {alt} +
); } diff --git a/src/components/product-device.tsx b/src/components/product-device.tsx new file mode 100644 index 0000000..1709bbe --- /dev/null +++ b/src/components/product-device.tsx @@ -0,0 +1,39 @@ +import Image from "next/image"; + +import { cn } from "~/lib/utils"; + +type ProductDeviceProps = { + src: string; + alt: string; + className?: string; + sizes?: string; + priority?: boolean; +}; + +/** + * A precomposited product capture using the licensed device bezels in + * `public/screenshots/pitch`. Keeping the frame in the source image preserves + * the bezel details without adding layout or client-side work. + */ +export function ProductDevice({ + src, + alt, + className, + sizes = "(min-width: 1024px) 360px, (min-width: 640px) 320px, 72vw", + priority = false, +}: ProductDeviceProps) { + return ( + {alt} + ); +} diff --git a/src/components/section-label.tsx b/src/components/section-label.tsx index 41f1548..60abdf8 100644 --- a/src/components/section-label.tsx +++ b/src/components/section-label.tsx @@ -1,6 +1,19 @@ -export function SectionLabel({ children }: { children: React.ReactNode }) { +import { cn } from "~/lib/utils"; + +export function SectionLabel({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { return ( -

+

{children}

); diff --git a/src/components/site-footer.tsx b/src/components/site-footer.tsx index 4cf9a9f..c948d70 100644 --- a/src/components/site-footer.tsx +++ b/src/components/site-footer.tsx @@ -1,24 +1,69 @@ +import Link from "next/link"; + +import { MedscribeLogo } from "~/components/medscribe-logo"; + +const footerLinks = [ + { href: "/how-it-works", label: "How it works" }, + { href: "/privacy", label: "Privacy & evidence" }, + { href: "/beta", label: "Beta access" }, +] as const; + export function SiteFooter() { return ( -