diff --git a/docs/site-map.md b/docs/site-map.md
index 56d819af81..e5d0ac73d8 100644
--- a/docs/site-map.md
+++ b/docs/site-map.md
@@ -1013,6 +1013,10 @@ This file is generated by `npm run docs:update` (or `npm run sitemap:update` dir
- `/mockups/favourites-review-console` - Route discovered from app directory Source: `src/app/mockups/favourites-review-console/page.tsx`.
- `/mockups/favourites-set-board` - Route discovered from app directory Source: `src/app/mockups/favourites-set-board/page.tsx`.
- `/mockups/favourites-set-navigator` - Route discovered from app directory Source: `src/app/mockups/favourites-set-navigator/page.tsx`.
+- `/mockups/guide-wireframes` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/page.tsx`.
+- `/mockups/guide-wireframes/help-hub` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/help-hub/page.tsx`.
+- `/mockups/guide-wireframes/tip-atlas` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/tip-atlas/page.tsx`.
+- `/mockups/guide-wireframes/walkthrough` - Route discovered from app directory Source: `src/app/mockups/guide-wireframes/walkthrough/page.tsx`.
- `/mockups/medication-prescribing` - Route discovered from app directory Source: `src/app/mockups/medication-prescribing/page.tsx`.
- `/mockups/mode-dropdown` - Route discovered from app directory Source: `src/app/mockups/mode-dropdown/page.tsx`.
- `/mockups/phone-inpage-navigation` - Route discovered from app directory Source: `src/app/mockups/phone-inpage-navigation/page.tsx`.
diff --git a/src/app/mockups/guide-wireframes/help-hub/page.tsx b/src/app/mockups/guide-wireframes/help-hub/page.tsx
new file mode 100644
index 0000000000..5f6a316d93
--- /dev/null
+++ b/src/app/mockups/guide-wireframes/help-hub/page.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from "next";
+
+import { GuideHelpHubMockup } from "@/components/guide-wireframe-mockups";
+
+export const metadata: Metadata = {
+ title: "Guide Help Hub Wireframe - Clinical KB",
+ description: "Option B wireframe: fullscreen TOC help hub with nested citation demo sheet.",
+};
+
+export default function GuideHelpHubMockupRoute() {
+ return ;
+}
diff --git a/src/app/mockups/guide-wireframes/page.tsx b/src/app/mockups/guide-wireframes/page.tsx
new file mode 100644
index 0000000000..c5c9247e9b
--- /dev/null
+++ b/src/app/mockups/guide-wireframes/page.tsx
@@ -0,0 +1,13 @@
+import type { Metadata } from "next";
+
+import { GuideWireframesIndex } from "@/components/guide-wireframe-mockups";
+
+export const metadata: Metadata = {
+ title: "Guide Wireframes - Clinical KB",
+ description:
+ "Three design-scratch wireframe options for the Clinical KB in-app guide, with phone and desktop Sheet states.",
+};
+
+export default function GuideWireframesIndexRoute() {
+ return ;
+}
diff --git a/src/app/mockups/guide-wireframes/tip-atlas/page.tsx b/src/app/mockups/guide-wireframes/tip-atlas/page.tsx
new file mode 100644
index 0000000000..a75f8774df
--- /dev/null
+++ b/src/app/mockups/guide-wireframes/tip-atlas/page.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from "next";
+
+import { GuideTipAtlasMockup } from "@/components/guide-wireframe-mockups";
+
+export const metadata: Metadata = {
+ title: "Guide Tip Atlas Wireframe - Clinical KB",
+ description: "Option A wireframe: interactive tip-atlas guide with annotated deep-dive sheets.",
+};
+
+export default function GuideTipAtlasMockupRoute() {
+ return ;
+}
diff --git a/src/app/mockups/guide-wireframes/walkthrough/page.tsx b/src/app/mockups/guide-wireframes/walkthrough/page.tsx
new file mode 100644
index 0000000000..2b7c964012
--- /dev/null
+++ b/src/app/mockups/guide-wireframes/walkthrough/page.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from "next";
+
+import { GuideWalkthroughMockup } from "@/components/guide-wireframe-mockups";
+
+export const metadata: Metadata = {
+ title: "Guide Walkthrough Wireframe - Clinical KB",
+ description: "Option C wireframe: first-run walkthrough sheet with skip confirm and done state.",
+};
+
+export default function GuideWalkthroughMockupRoute() {
+ return ;
+}
diff --git a/src/app/mockups/mockups-layout-client.tsx b/src/app/mockups/mockups-layout-client.tsx
index 5d7065da62..f8195578d4 100644
--- a/src/app/mockups/mockups-layout-client.tsx
+++ b/src/app/mockups/mockups-layout-client.tsx
@@ -39,6 +39,9 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) {
// These studies render their own top bar and composer inside each device
// frame. Suppress shared chrome so it cannot be mistaken for the concept.
const isTherapyNavigationMockup = pathname.startsWith("/mockups/therapy-navigation-");
+ // Guide wireframes paint Answer chrome + Sheet stacks inside device frames;
+ // the shared composer would read as a second, real search bar over the study.
+ const isGuideWireframesMockup = pathname.startsWith("/mockups/guide-wireframes");
// The calculators search page owns its own search input (top on desktop, docked
// at the bottom on phones), so the shared universal composer is suppressed here
// to avoid a second, floating search bar.
@@ -74,6 +77,7 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) {
!isPhoneInPageNavigationMockup &&
!isSearchBandDirectionsMockup &&
!isTherapyNavigationMockup &&
+ !isGuideWireframesMockup &&
!isWarningConsolidationMockup &&
!isWarningLineMockup &&
!isAnswerHomeProposalMockup
@@ -85,6 +89,7 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) {
!isSearchBandDirectionsMockup &&
!isPhoneInPageNavigationMockup &&
!isTherapyNavigationMockup &&
+ !isGuideWireframesMockup &&
!isWarningConsolidationMockup &&
!isWarningLineMockup &&
!isAnswerHomeProposalMockup
diff --git a/src/components/guide-wireframe-mockups.tsx b/src/components/guide-wireframe-mockups.tsx
new file mode 100644
index 0000000000..d810c02451
--- /dev/null
+++ b/src/components/guide-wireframe-mockups.tsx
@@ -0,0 +1,1451 @@
+"use client";
+
+import Link from "next/link";
+import {
+ ArrowLeft,
+ ArrowRight,
+ BookOpen,
+ Check,
+ ChevronRight,
+ Copy,
+ FileSearch,
+ Filter,
+ Layers,
+ Menu,
+ Quote,
+ Search,
+ Settings,
+ ShieldCheck,
+ Sparkles,
+ UploadCloud,
+ X,
+ type LucideIcon,
+} from "lucide-react";
+import { useId, useState, type ReactNode } from "react";
+
+import { cn, textMuted } from "@/components/ui-primitives";
+
+/* -------------------------------------------------------------------------- */
+/* Content — grounded in live GuideDialog copy, expanded for wireframes */
+/* -------------------------------------------------------------------------- */
+
+export const guideDirections = [
+ {
+ slug: "tip-atlas",
+ letter: "A",
+ name: "Tip Atlas",
+ tagline: "Tappable tip cards that open annotated deep-dives.",
+ href: "/mockups/guide-wireframes/tip-atlas",
+ },
+ {
+ slug: "help-hub",
+ letter: "B",
+ name: "Help Hub",
+ tagline: "Fullscreen TOC help with feature demos beside each topic.",
+ href: "/mockups/guide-wireframes/help-hub",
+ },
+ {
+ slug: "walkthrough",
+ letter: "C",
+ name: "Walkthrough",
+ tagline: "First-run step sheet with progress and a sticky footer.",
+ href: "/mockups/guide-wireframes/walkthrough",
+ },
+] as const;
+
+export type GuideDirectionSlug = (typeof guideDirections)[number]["slug"];
+
+type TopicId = "ask" | "citations" | "scope" | "evidence" | "upload" | "copy";
+
+const topics: Array<{
+ id: TopicId;
+ title: string;
+ body: string;
+ detail: string;
+ icon: LucideIcon;
+ productCue: string;
+}> = [
+ {
+ id: "ask",
+ title: "Ask and verify",
+ body: "Ask a focused guideline question, then verify linked citations and source passages before use.",
+ detail:
+ "Keep questions specific (drug + indication + setting). Treat the generated answer as a pointer into sources — not a final clinical note.",
+ icon: Search,
+ productCue: "Answer composer → cited response → open Top source",
+ },
+ {
+ id: "citations",
+ title: "Top source and citations",
+ body: "Use Top source, citation chips, and source cards to open the relevant document page and check the retrieved evidence.",
+ detail:
+ "Citation chips jump to the retrieved passage. Source cards expose publisher, page, and governance status so you can judge currency before acting.",
+ icon: BookOpen,
+ productCue: "Top source chip · citation chips · source cards",
+ },
+ {
+ id: "scope",
+ title: "Document scope",
+ body: "Use document scope controls when a question should search only selected guidelines rather than every indexed source.",
+ detail:
+ "Scope is for bounded questions (one protocol set, one service). Leave scope open for exploratory guideline search across the library.",
+ icon: Filter,
+ productCue: "Scope control → selected guidelines → ask again",
+ },
+ {
+ id: "evidence",
+ title: "Quotes, images, sources",
+ body: "Bottom nav jumps to quotes, diagrams, and source passages. Empty sections had no citations.",
+ detail:
+ "Empty evidence tabs mean nothing was retrieved for that modality — not a failed load. Prefer the populated tab before widening the question.",
+ icon: Quote,
+ productCue: "Phone bottom nav: Quotes · Images · Sources",
+ },
+ {
+ id: "upload",
+ title: "Upload and indexing",
+ body: "Real uploads require Supabase, OpenAI setup, the database schema, and the worker. Demo mode is synthetic only.",
+ detail:
+ "Demo mode never writes to your live corpus. Live indexing runs through the worker (OCR + captions + embeddings) before a document becomes searchable.",
+ icon: UploadCloud,
+ productCue: "Documents drawer → upload → indexing job status",
+ },
+ {
+ id: "copy",
+ title: "Copying text",
+ body: "Copied drafts are not final clinical notes. Keep the provenance footer and verify source material before using copied text.",
+ detail:
+ "The provenance footer preserves what was cited. Strip it only after you have verified the primary PDF passage yourself.",
+ icon: Copy,
+ productCue: "Copy answer → provenance footer retained",
+ },
+];
+
+const walkthroughSteps = [
+ {
+ id: "ask",
+ title: "Ask a focused question",
+ body: "Name the drug, indication, and care setting. Vague prompts return broader, harder-to-verify answers.",
+ cue: "Type in the Answer composer",
+ },
+ {
+ id: "verify",
+ title: "Verify before you trust",
+ body: "Open Top source and at least one citation chip. Confirm the passage matches the claim.",
+ cue: "Tap Top source · open a citation",
+ },
+ {
+ id: "scope",
+ title: "Narrow with scope when needed",
+ body: "If the question belongs to one protocol set, limit document scope first, then re-ask.",
+ cue: "Open document scope controls",
+ },
+ {
+ id: "evidence",
+ title: "Use evidence tabs",
+ body: "Quotes, images, and sources jump to retrieved modalities. Empty means none cited.",
+ cue: "Phone bottom nav evidence jumps",
+ },
+ {
+ id: "copy",
+ title: "Copy with provenance",
+ body: "Copied text stays a draft. Keep the provenance footer until the PDF is checked.",
+ cue: "Copy control + footer",
+ },
+] as const;
+
+const focusRing =
+ "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]";
+
+/* -------------------------------------------------------------------------- */
+/* Shared chrome */
+/* -------------------------------------------------------------------------- */
+
+function DirectionSwitcher({ current }: { current?: GuideDirectionSlug | "index" }) {
+ return (
+
+ {guideDirections.map((direction) => {
+ const active = direction.slug === current;
+ return (
+
+
+ Option {direction.letter}
+
+ {direction.name}
+ {direction.tagline}
+
+ );
+ })}
+
+ );
+}
+
+function MockupShell({
+ current,
+ letter,
+ name,
+ headline,
+ intro,
+ children,
+}: {
+ current: GuideDirectionSlug | "index";
+ letter: string;
+ name: string;
+ headline: string;
+ intro: string;
+ children: ReactNode;
+}) {
+ return (
+
+
+
+
+ Clinical KB guide · Option {letter} · {name}
+
+
+ {headline}
+
+ {intro}
+
+ Design scratch only — grounded in live{" "}
+ GuideDialog +{" "}
+ Sheet patterns. Not production.
+
+
+
+ {children}
+
+
+ );
+}
+
+function DevicePair({
+ title,
+ body,
+ desktop,
+ phone,
+}: {
+ title: string;
+ body: string;
+ desktop: ReactNode;
+ phone: ReactNode;
+}) {
+ return (
+
+
+
+
+ Desktop + phone
+
+
+
+
+ );
+}
+
+function DesktopFrame({ children }: { children: ReactNode }) {
+ return (
+
+
+
+
+
+
+ Desktop · centered Sheet
+
+
+
+ {children}
+
+
+ );
+}
+
+function PhoneFrame({ children }: { children: ReactNode }) {
+ return (
+
+
+
+ 19:16
+
+ ●●
+
+ {children}
+
+
+ );
+}
+
+function AppChrome({ device, dimmed = false }: { device: "desktop" | "phone"; dimmed?: boolean }) {
+ return (
+
+
+
+
+
+
+
+
+ Answer
+
+
+
+
+
+ );
+}
+
+function Scrim({ onClick }: { onClick?: () => void }) {
+ return (
+
+ );
+}
+
+function SheetHeader({
+ title,
+ description,
+ onClose,
+ closeLabel = "Close",
+ leading,
+}: {
+ title: string;
+ description?: string;
+ onClose: () => void;
+ closeLabel?: string;
+ leading?: ReactNode;
+}) {
+ return (
+
+ {leading}
+
+
+ {title}
+
+ {description ?
{description}
: null}
+
+
+
+
+
+ );
+}
+
+function TopicIcon({ icon: Icon }: { icon: LucideIcon }) {
+ return (
+
+
+
+ );
+}
+
+function NoteGrid({ items }: { items: Array<{ title: string; body: string }> }) {
+ return (
+
+ {items.map((item) => (
+
+ {item.title}
+ {item.body}
+
+ ))}
+
+ );
+}
+
+/* -------------------------------------------------------------------------- */
+/* Index */
+/* -------------------------------------------------------------------------- */
+
+export function GuideWireframesIndex() {
+ return (
+
+
+ Current product baseline
+
+ Responsive Sheet : bottom sheet on phone
+ (drag grip, safe-area), centered dialog from sm:. Six static tip cards, no links. Entry: Settings
+ Help & About, plus a dashboard Guide trigger when drawers are open.
+
+
+ {topics.map((topic) => (
+
+
+
+ {topic.title}
+ {topic.body}
+
+
+ ))}
+
+
+
+
+
+
+ Open a variation
+
+ Each option is interactive inside the device frames — tap cards, TOC rows, and walkthrough controls.
+
+
+ {guideDirections.map((direction) => (
+
+
+ Option {direction.letter}
+
+
{direction.name}
+
{direction.tagline}
+
+ Open wireframe
+
+
+
+ ))}
+
+
+
+ );
+}
+
+/* -------------------------------------------------------------------------- */
+/* Option A — Tip Atlas */
+/* -------------------------------------------------------------------------- */
+
+type AtlasLayer = "closed" | "guide" | "topic" | "entry";
+
+export function GuideTipAtlasMockup() {
+ const [desktopLayer, setDesktopLayer] = useState("guide");
+ const [phoneLayer, setPhoneLayer] = useState("guide");
+ const [desktopTopic, setDesktopTopic] = useState("ask");
+ const [phoneTopic, setPhoneTopic] = useState("ask");
+
+ return (
+
+
+
+
+
+
+ setDesktopLayer("guide")}
+ onCloseGuide={() => setDesktopLayer("entry")}
+ onOpenTopic={(id) => {
+ setDesktopTopic(id);
+ setDesktopLayer("topic");
+ }}
+ onCloseTopic={() => setDesktopLayer("guide")}
+ />
+ }
+ phone={
+ setPhoneLayer("guide")}
+ onCloseGuide={() => setPhoneLayer("entry")}
+ onOpenTopic={(id) => {
+ setPhoneTopic(id);
+ setPhoneLayer("topic");
+ }}
+ onCloseTopic={() => setPhoneLayer("guide")}
+ />
+ }
+ />
+
+
+
+ );
+}
+
+function FrameControl({
+ label,
+ value,
+ onChange,
+ options,
+}: {
+ label: string;
+ value: T;
+ onChange: (next: T) => void;
+ options: Array<{ id: T; label: string }>;
+}) {
+ return (
+
+
+ {label}
+
+ {options.map((option) => {
+ const active = option.id === value;
+ return (
+ onChange(option.id)}
+ className={cn(
+ "min-h-12 rounded-lg px-3 text-xs font-semibold transition",
+ focusRing,
+ active
+ ? "bg-[color:var(--command)] text-[color:var(--clinical-accent-contrast)]"
+ : "bg-[color:var(--surface-subtle)] text-[color:var(--text)] hover:bg-[color:var(--surface-inset)]",
+ )}
+ >
+ {option.label}
+
+ );
+ })}
+
+ );
+}
+
+function AtlasDevice({
+ device,
+ layer,
+ topicId,
+ onOpenGuide,
+ onCloseGuide,
+ onOpenTopic,
+ onCloseTopic,
+}: {
+ device: "desktop" | "phone";
+ layer: AtlasLayer;
+ topicId: TopicId;
+ onOpenGuide: () => void;
+ onCloseGuide: () => void;
+ onOpenTopic: (id: TopicId) => void;
+ onCloseTopic: () => void;
+}) {
+ const topic = topics.find((item) => item.id === topicId) ?? topics[0];
+ const showOverlay = layer === "guide" || layer === "topic" || layer === "entry";
+
+ return (
+
+
+
+
+
Answer canvas (dimmed)
+
+ Guide overlays sit above the composer — no second search bar.
+
+
+
+
+ {showOverlay ?
: null}
+
+ {layer === "entry" ? (
+
+ {device === "phone" ? (
+
+
+
+ ) : null}
+
+
+
+ Help & About
+
+
+
Clinical Knowledge Base
+
+ Source-linked clinical reference. Verify primary guidance before acting.
+
+
+
+ Guide & help
+
+
+
+
+ ) : null}
+
+ {layer === "guide" || layer === "topic" ? (
+
+ {device === "phone" ? (
+
+
+
+ ) : null}
+
+
+
+ {topics.map((item) => (
+
onOpenTopic(item.id)}
+ className={cn(
+ "rounded-xl border border-[color:var(--border)] bg-[color:var(--surface-lux)] p-3 text-left shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)]",
+ focusRing,
+ topicId === item.id && layer === "topic" ? "border-[color:var(--clinical-accent-border)]" : null,
+ )}
+ >
+
+
+
+
+
{item.title}
+
+
+
{item.body}
+
+
+
+ ))}
+
+
+
+ ) : null}
+
+ {layer === "topic" ? (
+
+ {device === "phone" ? (
+
+
+
+ ) : null}
+
+
+
+ }
+ />
+
+
+
+
+ In the product
+
+
{topic.productCue}
+
+
+
+
+ ) : null}
+
+ );
+}
+
+function ProductCuePreview({ topicId }: { topicId: TopicId }) {
+ if (topicId === "citations") {
+ return (
+
+
Top source
+
+
+ Clozapine monitoring protocol · p.12
+
+
+
+ [1]
+
+
+ [2]
+
+
+
+ );
+ }
+ if (topicId === "scope") {
+ return (
+
+
Document scope
+
+
+ 3 selected
+
+
+ All documents
+
+
+
+ );
+ }
+ if (topicId === "evidence") {
+ return (
+
+
+ {["Quotes", "Images", "Sources"].map((label) => (
+
+ {label}
+
+ ))}
+
+
+ );
+ }
+ if (topicId === "copy") {
+ return (
+
+
Draft answer text…
+
+ Provenance: sources [1][2] · verify primary PDF before clinical use
+
+
+ );
+ }
+ return (
+
+
+
+ Ask about lithium monitoring in shared care…
+
+
+ );
+}
+
+/* -------------------------------------------------------------------------- */
+/* Option B — Help Hub */
+/* -------------------------------------------------------------------------- */
+
+type HubSection = TopicId | "safety" | "demo";
+
+const hubNav: Array<{ id: HubSection; label: string; icon: LucideIcon }> = [
+ { id: "ask", label: "Asking well", icon: Search },
+ { id: "citations", label: "Sources", icon: BookOpen },
+ { id: "scope", label: "Scope", icon: Filter },
+ { id: "evidence", label: "Evidence", icon: Layers },
+ { id: "upload", label: "Upload", icon: UploadCloud },
+ { id: "copy", label: "Copy safety", icon: Copy },
+ { id: "safety", label: "Safety", icon: ShieldCheck },
+ { id: "demo", label: "Demo vs live", icon: Sparkles },
+];
+
+export function GuideHelpHubMockup() {
+ const [desktopSection, setDesktopSection] = useState("ask");
+ const [phoneSection, setPhoneSection] = useState("ask");
+ const [desktopDemoOpen, setDesktopDemoOpen] = useState(false);
+ const [phoneDemoOpen, setPhoneDemoOpen] = useState(false);
+
+ return (
+
+ setDesktopDemoOpen(true)}
+ onDemoClose={() => setDesktopDemoOpen(false)}
+ />
+ }
+ phone={
+ setPhoneDemoOpen(true)}
+ onDemoClose={() => setPhoneDemoOpen(false)}
+ />
+ }
+ />
+
+
+
+ );
+}
+
+function HelpHubDevice({
+ device,
+ section,
+ onSection,
+ demoOpen,
+ onDemoOpen,
+ onDemoClose,
+}: {
+ device: "desktop" | "phone";
+ section: HubSection;
+ onSection: (id: HubSection) => void;
+ demoOpen: boolean;
+ onDemoOpen: () => void;
+ onDemoClose: () => void;
+}) {
+ return (
+
+
+
+
+
undefined}
+ closeLabel="Close help"
+ leading={
+ device === "phone" ? (
+
+
+
+ ) : null
+ }
+ />
+
+ {device === "desktop" ? (
+
+ {hubNav.map((item) => {
+ const active = item.id === section;
+ const Icon = item.icon;
+ return (
+ onSection(item.id)}
+ className={cn(
+ "mb-1 flex min-h-12 w-full items-center gap-2 rounded-lg px-2 text-left text-xs font-semibold",
+ focusRing,
+ active
+ ? "bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
+ : "text-[color:var(--text)] hover:bg-[color:var(--surface)]",
+ )}
+ >
+
+ {item.label}
+
+ );
+ })}
+
+ ) : (
+
+
+ {hubNav.map((item) => {
+ const active = item.id === section;
+ return (
+ onSection(item.id)}
+ className={cn(
+ "min-h-12 rounded-lg px-3 text-xs font-semibold",
+ focusRing,
+ active
+ ? "bg-[color:var(--command)] text-[color:var(--clinical-accent-contrast)]"
+ : "bg-[color:var(--surface-subtle)] text-[color:var(--text)]",
+ )}
+ >
+ {item.label}
+
+ );
+ })}
+
+
+ )}
+
+
+
+
+
+
+
+ {demoOpen ? (
+ <>
+
+
+ {device === "phone" ? (
+
+
+
+ ) : null}
+
+
+
+ Open the Top source chip, skim the passage, then confirm the claim before using the answer.
+
+
+
+ Tap Top source to open the PDF page.
+ Match the cited sentence to the passage.
+ Only then copy or act on the answer.
+
+
+ Got it
+
+
+
+ >
+ ) : null}
+
+ );
+}
+
+function HubChapter({ section, onDemoOpen }: { section: HubSection; onDemoOpen: () => void }) {
+ if (section === "safety") {
+ return (
+
+ Safety posture
+
+ This is a clinical reference prototype, not validated decision support. Answers must remain verifiable against
+ linked sources. When generation fails quality gates, the product degrades to source-only citations — that is
+ expected, not an error.
+
+
+ Never treat a fluent answer as a substitute for the primary guideline PDF.
+
+
+ );
+ }
+ if (section === "demo") {
+ return (
+
+ Demo vs live
+
+ Demo mode uses a synthetic corpus. Live mode needs Supabase, OpenAI, the schema, and the ingestion worker.
+ Uploads in demo never reach your private library.
+
+
+ );
+ }
+ const topic = topics.find((item) => item.id === section);
+ if (!topic) return null;
+ return (
+
+
+
+
+
{topic.title}
+
{topic.detail}
+
+
+
+ {topic.id === "citations" ? (
+
+ Try this: verify a citation
+
+
+ ) : null}
+
+ );
+}
+
+/* -------------------------------------------------------------------------- */
+/* Option C — Walkthrough */
+/* -------------------------------------------------------------------------- */
+
+type WalkLayer = "tour" | "skip" | "done";
+
+export function GuideWalkthroughMockup() {
+ const [desktopStep, setDesktopStep] = useState(0);
+ const [phoneStep, setPhoneStep] = useState(0);
+ const [desktopLayer, setDesktopLayer] = useState("tour");
+ const [phoneLayer, setPhoneLayer] = useState("tour");
+ const labelId = useId();
+
+ return (
+
+
+ }
+ phone={
+
+ }
+ />
+
+
+
+ );
+}
+
+function WalkDevice({
+ device,
+ step,
+ layer,
+ labelId,
+ onStep,
+ onLayer,
+}: {
+ device: "desktop" | "phone";
+ step: number;
+ layer: WalkLayer;
+ labelId: string;
+ onStep: (step: number) => void;
+ onLayer: (layer: WalkLayer) => void;
+}) {
+ const current = walkthroughSteps[Math.min(step, walkthroughSteps.length - 1)];
+ const progress = ((step + 1) / walkthroughSteps.length) * 100;
+ const isLast = step >= walkthroughSteps.length - 1;
+
+ return (
+
+
+
+
+ First-run Answer home behind the tour
+
+
+
+
+
+ {device === "phone" ? (
+
+
+
+ ) : null}
+
+ {layer === "done" ? (
+ <>
+
onLayer("tour")}
+ />
+
+
+
+
+
+ Ask focused questions, open Top source, and keep provenance on copies. Re-open the guide anytime from
+ Settings → Guide & help.
+
+
+ Browse Tip Atlas
+
+
+ Open Help Hub
+
+
{
+ onStep(0);
+ onLayer("tour");
+ }}
+ className={cn(
+ "inline-flex min-h-12 items-center justify-center text-sm font-semibold text-[color:var(--clinical-accent)]",
+ focusRing,
+ )}
+ >
+ Restart walkthrough
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+ Step {step + 1} of {walkthroughSteps.length}
+
+
+ {current.title}
+
+
+
onLayer("skip")}
+ className={cn(
+ "min-h-12 shrink-0 rounded-lg px-3 text-xs font-semibold text-[color:var(--text-muted)] hover:bg-[color:var(--surface-subtle)]",
+ focusRing,
+ )}
+ >
+ Skip
+
+
+
+
+
+
+
{current.body}
+
+
+ Look for
+
+
{current.cue}
+
+
+
+
+
+
onStep(Math.max(0, step - 1))}
+ className={cn(
+ "inline-flex min-h-12 flex-1 items-center justify-center gap-2 rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface)] text-sm font-semibold disabled:opacity-40",
+ focusRing,
+ )}
+ >
+
+ Back
+
+
{
+ if (isLast) onLayer("done");
+ else onStep(step + 1);
+ }}
+ className={cn(
+ "inline-flex min-h-12 flex-[1.4] items-center justify-center gap-2 rounded-lg bg-[color:var(--command)] text-sm font-semibold text-[color:var(--clinical-accent-contrast)]",
+ focusRing,
+ )}
+ >
+ {isLast ? "Finish" : "Next"}
+ {!isLast ? (
+
+ ) : (
+
+ )}
+
+
+ >
+ )}
+
+
+ {layer === "skip" ? (
+ <>
+
onLayer("tour")}
+ className="absolute inset-0 z-30 bg-[color:var(--overlay-backdrop)]"
+ />
+
+
onLayer("tour")} />
+
+
+ You can reopen Guide & help from Settings anytime. Skipping hides these first-run steps.
+
+
+ onLayer("tour")}
+ className={cn(
+ "inline-flex min-h-12 flex-1 items-center justify-center rounded-lg border border-[color:var(--border-lux)] text-sm font-semibold",
+ focusRing,
+ )}
+ >
+ Keep going
+
+ onLayer("done")}
+ className={cn(
+ "inline-flex min-h-12 flex-1 items-center justify-center rounded-lg bg-[color:var(--command)] text-sm font-semibold text-[color:var(--clinical-accent-contrast)]",
+ focusRing,
+ )}
+ >
+ Skip
+
+
+
+
+ >
+ ) : null}
+
+ );
+}