--- // PhasePanel — the three-step "Click a phase / see the handoff" block. // // Static shell: the tab buttons and the panel markup are server-rendered. // The `initial` phase's content is shown by default; the other tabs still // carry the `data-phase` hook so the interactive island (task 15) can swap // the panel on click. // // Every `data-phase` value is asserted by `scripts/verify.mjs`. type Localized = { en: string; pt: string }; export type PhaseId = 'plan' | 'build' | 'review'; interface Phase { id: PhaseId; /** Two-letter label rendered in the tab, e.g. "PLAN". */ label: string | Localized; /** Numeric prefix, e.g. "01". */ number: string; title: string | Localized; body: string | Localized; /** Headline + small caption shown above the panel title. */ meta: { deliverable: string | Localized; gate: string | Localized }; /** Code-line evidence shown at the bottom of the panel. */ evidence: string; } interface Props { phases: Phase[]; /** Initial active phase. Defaults to the first entry. */ initial?: PhaseId; } const { phases, initial = phases[0]?.id ?? 'plan' } = Astro.props; const active = phases.find((phase) => phase.id === initial) ?? phases[0]; ---
{ phases.map((phase) => ( )) }
{ typeof active.meta.deliverable === 'string' ? ( active.meta.deliverable ) : ( <> <> {active.meta.deliverable.en} ) } { typeof active.meta.gate === 'string' ? ( active.meta.gate ) : ( <> <> {active.meta.gate.en} ) }

{ typeof active.title === 'string' ? ( active.title ) : ( <> <> {active.title.en} ) }

{ typeof active.body === 'string' ? ( active.body ) : ( <> <> {active.body.en} ) }

{active.evidence}