@real-music-packages/web-core 0.44.0 → 0.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/scene/score.ts","../../src/scene/math.ts","../../src/scene/caption.ts","../../src/scene/layers/demo.ts","../../src/scene/keyboardGeometry.ts","../../src/scene/keyboardStore.ts","../../src/scene/layers/keyboard.ts","../../src/scene/layers/fretboard.ts","../../src/scene/layers/fallingNotes.ts","../../src/scene/layers/promoCards.ts","../../src/scene/layers/spectrum.ts","../../src/scene/layers/branding.ts","../../src/scene/staffKeyboardRay.ts","../../src/scene/highlight.ts","../../src/scene/layers/staffKeyboardRay.ts","../../src/scene/countingGrid.ts","../../src/scene/layers/countingTrack.ts","../../src/scene/degreeLabels.ts","../../src/scene/layers/degreeLabels.ts","../../src/scene/harmonyTrack.ts","../../src/scene/layers/functionalHarmony.ts","../../src/scene/quizCard.ts","../../src/scene/layers/mcqCard.ts","../../src/scene/circleOfFifths.ts","../../src/scene/layers/circleOfFifths.ts","../../src/scene/pitchContour.ts","../../src/scene/layers/pitchContour.ts","../../src/scene/sectionMinimap.ts","../../src/scene/layers/sectionMinimap.ts","../../src/scene/layers/beatPulse.ts","../../src/scene/layers/chordRibbon.ts","../../src/scene/layers/progressRing.ts","../../src/scene/layers/radialSpectrum.ts","../../src/scene/layers/karaokeCaption.ts","../../src/scene/layers/intervalArcs.ts","../../src/scene/layers/kineticText.ts","../../src/scene/layers/countdown.ts","../../src/scene/layers/waveform.ts","../../src/scene/layers/scaleHighlight.ts","../../src/scene/layers/particleBurst.ts","../../src/scene/layers/tensionGraph.ts","../../src/scene/layers/texture.ts","../../src/scene/layers/statCounter.ts","../../src/scene/layers/endCard.ts","../../src/scene/layers/imageReveal.ts","../../src/scene/layers/compositeRhythm.ts","../../src/scene/layers/handIndicator.ts","../../src/scene/registry.ts","../../src/scene/audioLayers.ts","../../src/scene/camera.ts","../../src/scene/runner.ts","../../src/scene/gate.ts","../../src/scene/notationCamera.ts","../../src/scene/demos/fallingKeyboardDemo.ts","../../src/scene/demos/promoCardsDemo.ts","../../src/scene/demos/extendedDemo.ts"],"sourcesContent":["// scoreFromMusicXML — derive a timed, typed note list from MusicXML, via OSMD's\n// parsed source model. One input (MusicXML) drives engraving + timing + pitch +\n// hands + lyrics, so engraved note, falling note, keyboard key, and sounding note\n// are the same object (the render-components design — see\n// docs/superpowers/specs/2026-06-14-render-components-spec.md).\n//\n// Approach (validated by the S0 spike — spikes/score-from-musicxml/FINDINGS.md):\n// - osmd.load(xml) builds osmd.sheet (the source model). We read ONLY the source\n// model, never the graphical layout, so it runs headless (Node + the fake-2D\n// shim in ./headless) and unchanged in a browser.\n// - sheet.MusicPartManager.getIterator() is OSMD's PLAYBACK iterator: available\n// without render(), walks the score in playback order, and expands\n// repeats/voltas. CurrentEnrolledTimestamp = linear playback clock (whole\n// notes); CurrentAudibleVoiceEntries() = the voice entries that ONSET now.\n// - Whole-note timestamps/durations -> ms via tempo. v1 uses a single constant\n// tempo (see the tempo-map stub below).\n//\n// OSMD-internals gotchas baked in (pin OSMD; the MIDI-oracle test guards drift):\n// - pitch.getHalfTone() returns MIDI - 12 (its octave 0 == scientific octave 1).\n// - pitch.FundamentalNote is a CHROMATIC enum (C=0,D=2,E=4,F=5,G=7,A=9,B=11),\n// not a 0-6 step index.\n// - alter is pitch.AccidentalHalfTones (NOT pitch.Accidental — a different enum).\n// - Ties: only the tie START note appears as audible; NoteTie.Duration is the\n// whole chain's sounding length. Continuations are skipped.\n\n/* OSMD's typings don't expose the source-model internals we read, so the OSMD\n instance and its model are treated as `any` at the boundary. Everything we\n PRODUCE is fully typed (Score / ScoreNote). */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n// ─── Contracts (from the render-components spec) ────────────────────────────\n\nexport interface ScoreNote {\n /** MIDI note number (middle C = 60). */\n pitchMidi: number;\n /** Diatonic letter name: C D E F G A B. */\n step: string;\n /** Chromatic alteration in semitones: -1 flat, +1 sharp, 0 natural, ±2 double. */\n alter: number;\n /** Scientific octave (middle C = C4). */\n octave: number;\n /** Onset on the linear playback clock, in ms (repeats expanded). */\n onsetMs: number;\n /** Sounding duration in ms (tie chains merged). */\n durMs: number;\n /** Staff index within the whole sheet (0-based). */\n staff: number;\n /** Voice id within the part. */\n voice: number;\n /** Performing hand: grand-staff top -> \"R\", bottom -> \"L\" (see hand-inference stub). */\n hand: 'L' | 'R';\n /** Engraved bar this note sits in, 1-based. Repeat passes keep the printed\n * number — a cursor or scrubber points at bars as printed, not as played. */\n measure: number;\n /** Lyric syllable attached to this note, if any. */\n lyric?: string;\n /** Fingering digit attached to this note, if any. */\n fingering?: number;\n /** True for a grace note (acciaccatura/appoggiatura). Onset equals its\n * principal's onset as parsed; `realizeOrnaments` (../ornaments) re-times\n * the run to end there instead. Additive — existing consumers unaffected. */\n isGrace?: boolean;\n /** True when the note carries a trill mark. `realizeOrnaments` (../ornaments)\n * replaces it with the alternation; parsing leaves it as a single note.\n * Additive — existing consumers unaffected. */\n trill?: boolean;\n /** Performance velocity as a fraction of full (1 = full). Only ornament\n * notes set this today (`realizeOrnaments`); absent means \"full\" for every\n * pre-existing note. Additive — no current PlaybackSink reads it yet. */\n velocity?: number;\n}\n\n/**\n * Tempo map. v1 is a single constant tempo (one segment at t=0). The piecewise\n * shape is the documented seam for rubato / multiple `<sound tempo>` — see the\n * tempo-map stub note in scoreFromMusicXML.\n */\nexport interface TempoMap {\n /** Where the tempo came from: the XML's notated tempo, the fallback, or an override. */\n source: 'xml' | 'fallback' | 'override';\n /** Piecewise-constant segments, ordered by onset. v1 always has exactly one (at 0). */\n segments: Array<{ atMs: number; bpm: number }>;\n}\n\nexport interface Score {\n notes: ScoreNote[];\n tempoMap: TempoMap;\n /** End of the last sounding note, in ms. */\n durationMs: number;\n key?: string;\n /** Key signature as a fifths count (-7..7, sharps positive), when known — the\n * source `realizeOrnaments` (../ornaments) reads for diatonic trill neighbors.\n * Additive alongside `key` (the display string); same source, `<fifths>`. */\n keyFifths?: number;\n timeSig?: string;\n title?: string;\n composer?: string;\n}\n\nexport interface ScoreFromMusicXMLOpts {\n /** bpm to use when the XML has no notated tempo (DefaultStartTempoInBpm === 0). Default 100. */\n tempoFallback?: number;\n /** Force this bpm regardless of the XML's notated tempo (per-recipe override). */\n tempoOverride?: number;\n /**\n * Provide the OSMD instance. Defaults to a literal `import('opensheetmusicdisplay')`\n * + a detached div (works in a browser, or in Node after `setupHeadlessDom()`).\n * Inject for tests or non-DOM environments.\n */\n osmdFactory?: () => any;\n}\n\n// A beat is a quarter note = 1/4 whole note. v1 assumes an x/4 meter for the\n// bpm->ms scaling (onsets in whole-note space are exact regardless; only the\n// beat unit affects scaling). Non-x/4 meters are a documented follow-up.\nconst BEATS_PER_WHOLE_NOTE = 4;\nconst MAX_ITERATOR_STEPS = 200_000;\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Parse MusicXML into a timed, typed Score via OSMD's source model.\n *\n * @param xml MusicXML document (uncompressed string; unzip .mxl first).\n * @param opts tempoFallback / tempoOverride / osmdFactory.\n */\nexport async function scoreFromMusicXML(\n xml: string,\n opts: ScoreFromMusicXMLOpts = {},\n): Promise<Score> {\n const osmd = opts.osmdFactory ? opts.osmdFactory() : await defaultOsmd();\n\n // osmd.sheet (the source model) is populated even if the later graphical layout\n // throws (e.g. lyric text metrics under a bare DOM). So we tolerate load errors\n // and only fail if no source model came out.\n let loadError: unknown = null;\n try {\n await osmd.load(xml);\n } catch (e) {\n loadError = e;\n }\n const sheet = osmd.sheet;\n if (!sheet || !sheet.SourceMeasures?.length) {\n throw new Error(\n 'scoreFromMusicXML: OSMD produced no source model' +\n (loadError ? `: ${(loadError as Error).message}` : ''),\n );\n }\n\n const tempoMap = resolveTempo(sheet, opts);\n const wholeNoteToMs = makeWholeNoteToMs(tempoMap.segments[0].bpm);\n\n const it = sheet.MusicPartManager.getIterator();\n const notes: ScoreNote[] = [];\n\n let steps = 0;\n while (!it.EndReached && steps++ < MAX_ITERATOR_STEPS) {\n const enrolled = it.CurrentEnrolledTimestamp?.RealValue ?? 0;\n const onsetMs = wholeNoteToMs(enrolled);\n // MeasureNumber is the printed number (usually index+1, but pickup bars\n // shift it); fall back to the iterator's index when absent.\n const measure =\n it.CurrentMeasure?.MeasureNumber ?? (it.CurrentMeasureIndex ?? 0) + 1;\n // CurrentAudibleVoiceEntries is a METHOD (not a property) — the entries\n // sounding/onsetting at this step.\n const voiceEntries: any[] = it.CurrentAudibleVoiceEntries?.() ?? [];\n\n for (const ve of voiceEntries) {\n const voiceId = ve.ParentVoice?.VoiceId ?? 1;\n const sse = ve.ParentSourceStaffEntry;\n const staffIdx = sse?.ParentStaff?.idInMusicSheet ?? 0;\n const instrument = sse?.ParentStaff?.ParentInstrument;\n const hand = inferHand(instrument, sse?.ParentStaff);\n // OrnamentContainer is per-VOICE-ENTRY (not per-note); GetOrnament === 0\n // is OrnamentEnum.Trill (OSMD's VoiceData/OrnamentContainer.d.ts — not\n // exported from the package's public index, so pinned by value here,\n // same convention as the KeyEnum minor check in readKey below).\n const isTrill = ve.OrnamentContainer?.GetOrnament === 0;\n\n for (const n of ve.Notes ?? []) {\n if (n.isRestFlag || n.IsRest) continue;\n const pitch = n.Pitch;\n if (!pitch) continue;\n\n // Ties: skip continuation notes (the iterator only reports the START as\n // audible anyway, but be defensive), and give the START the whole chain's\n // length via NoteTie.Duration.\n const tie = n.NoteTie;\n if (tie && tie.StartNote && tie.StartNote !== n) continue;\n const wholeNoteLen =\n tie && tie.StartNote === n && tie.Duration\n ? tie.Duration.RealValue\n : n.Length?.RealValue ?? 0;\n\n const halfTone = pitch.getHalfTone?.();\n const pitchMidi = halfTone == null ? NaN : halfTone + 12;\n\n const note: ScoreNote = {\n pitchMidi,\n step: fundamentalToStep(pitch.FundamentalNote),\n alter: pitch.AccidentalHalfTones ?? 0,\n octave: octaveFromMidi(pitchMidi),\n onsetMs: Math.round(onsetMs),\n durMs: Math.round(wholeNoteToMs(wholeNoteLen)),\n staff: staffIdx,\n voice: voiceId,\n hand,\n measure,\n };\n const lyric = extractLyric(ve);\n if (lyric != null) note.lyric = lyric;\n const fingering = extractFingering(n);\n if (fingering != null) note.fingering = fingering;\n if (n.IsGraceNote) note.isGrace = true;\n if (isTrill) note.trill = true;\n notes.push(note);\n }\n }\n it.moveToNext();\n }\n\n notes.sort((a, b) => a.onsetMs - b.onsetMs || a.pitchMidi - b.pitchMidi);\n const durationMs = notes.reduce((mx, n) => Math.max(mx, n.onsetMs + n.durMs), 0);\n\n const keySig = readKeySignature(sheet);\n\n return {\n notes,\n tempoMap,\n durationMs,\n key: keySig.name,\n keyFifths: keySig.fifths,\n timeSig: readTimeSig(sheet),\n title: sheet.TitleString || undefined,\n composer: sheet.ComposerString || undefined,\n };\n}\n\n// ─── Tempo ───────────────────────────────────────────────────────────────────\n\n/**\n * Resolve the tempo for v1.\n *\n * Precedence: tempoOverride > XML notated tempo > tempoFallback (default 100).\n *\n * STUB — piecewise tempo map (rubato / accel. / multiple `<sound tempo>`): v1\n * emits a SINGLE constant segment at t=0. The {@link TempoMap.segments} array is\n * the documented seam: a follow-up reads per-measure tempo instructions from the\n * source model (sourceMeasure.TempoExpressions / TempoInBpm) and converts\n * enrolled-whole-note onsets to ms piecewise. Low priority — most promo clips use\n * one tempo, and the few rubato pieces are flagged by the MIDI-oracle test as\n * corpus issues, not extractor bugs (FINDINGS.md, risk #2).\n */\nfunction resolveTempo(sheet: any, opts: ScoreFromMusicXMLOpts): TempoMap {\n if (opts.tempoOverride && opts.tempoOverride > 0) {\n return { source: 'override', segments: [{ atMs: 0, bpm: opts.tempoOverride }] };\n }\n const xmlTempo = sheet.DefaultStartTempoInBpm;\n if (xmlTempo && xmlTempo > 0) {\n return { source: 'xml', segments: [{ atMs: 0, bpm: xmlTempo }] };\n }\n return { source: 'fallback', segments: [{ atMs: 0, bpm: opts.tempoFallback ?? 100 }] };\n}\n\n/** ms per whole note at a constant bpm (quarter-note beat; see BEATS_PER_WHOLE_NOTE). */\nfunction makeWholeNoteToMs(bpm: number): (wholeNotes: number) => number {\n const msPerBeat = 60000 / bpm;\n return (wholeNotes: number) => wholeNotes * BEATS_PER_WHOLE_NOTE * msPerBeat;\n}\n\n// ─── Hand inference ───────────────────────────────────────────────────────────\n\n/**\n * Infer the performing hand from the grand-staff layout.\n *\n * Grand staff (one instrument with `<staves>2`): top staff -> \"R\", bottom -> \"L\".\n * Verified correct on Für Elise / Twinkle (FINDINGS.md).\n *\n * STUB — SATB / two-`<score-part>` hand inference: when hands are SEPARATE parts\n * (RMT/SATB convention) there is no single grand staff, so we default to \"R\".\n * The documented follow-up (FINDINGS.md, risk #3) infers hand from clef\n * (treble -> R, bass -> L); OSMD doesn't expose clef cleanly post-parse, so that\n * needs either a raw-XML clef read or a per-recipe hint. Not blocking — RSR's\n * pieces use the grand-staff convention, which works today.\n */\nfunction inferHand(instrument: any, staff: any): 'L' | 'R' {\n const staves = instrument?.Staves;\n if (Array.isArray(staves) && staves.length >= 2 && staff) {\n const local = staves.indexOf(staff);\n if (local >= 0) return local === 0 ? 'R' : 'L';\n }\n return 'R';\n}\n\n// ─── Source-model readers ─────────────────────────────────────────────────────\n\n/** OSMD FundamentalNote is a CHROMATIC (pitch-class) enum, not a 0-6 step index. */\nfunction fundamentalToStep(f: number): string {\n return (\n ({ 0: 'C', 2: 'D', 4: 'E', 5: 'F', 7: 'G', 9: 'A', 11: 'B' } as Record<number, string>)[f] ??\n '?'\n );\n}\n\nfunction octaveFromMidi(midi: number): number {\n if (!Number.isFinite(midi)) return 0;\n return Math.floor(midi / 12) - 1;\n}\n\nfunction extractLyric(ve: any): string | undefined {\n const dict = ve.LyricsEntries;\n if (!dict || !dict.size) return undefined;\n const first = [...dict.values()][0];\n const text = first?.Text?.text ?? first?.Text;\n return typeof text === 'string' && text.length ? text : undefined;\n}\n\nfunction extractFingering(note: any): number | undefined {\n // OSMD attaches fingering as a Fingering technical instruction on the note.\n const raw = note?.Fingering?.value ?? note?.Fingering?.Value ?? note?.Fingering;\n const n = typeof raw === 'string' ? parseInt(raw, 10) : typeof raw === 'number' ? raw : NaN;\n return Number.isFinite(n) ? n : undefined;\n}\n\n// Diatonic key names by fifths on the circle, for major and minor modes.\nconst MAJOR_KEYS = ['Cb', 'Gb', 'Db', 'Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#'];\nconst MINOR_KEYS = ['Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#'];\n\n/**\n * Reads the key signature: both the display name (`Score.key`, e.g. \"G major\")\n * and the raw fifths count (`Score.keyFifths`, -7..7) that `realizeOrnaments`\n * uses for diatonic trill neighbors — same source read, additive second value.\n */\nfunction readKeySignature(sheet: any): { name?: string; fifths?: number } {\n // Class names are minified in OSMD builds, so we identify the KeyInstruction by\n // its shape (it carries `keyType` = fifths and `mode`) rather than by name.\n for (const entry of sheet?.SourceMeasures?.[0]?.FirstInstructionsStaffEntries ?? []) {\n for (const ins of entry?.Instructions ?? []) {\n if (typeof ins?.keyType === 'number') {\n const fifths = ins.keyType; // -7..+7\n const idx = fifths + 7;\n if (idx < 0 || idx > 14) return { fifths };\n // OSMD KeyEnum: major=0/1, minor=2 (others rare/modal — treat as major).\n const isMinor = ins.mode === 2;\n const name = (isMinor ? MINOR_KEYS : MAJOR_KEYS)[idx];\n return { name: name ? `${name} ${isMinor ? 'minor' : 'major'}` : undefined, fifths };\n }\n }\n }\n return {};\n}\n\nfunction readTimeSig(sheet: any): string | undefined {\n const ts = sheet?.SourceMeasures?.[0]?.ActiveTimeSignature;\n if (ts && Number.isFinite(ts.Numerator) && Number.isFinite(ts.Denominator)) {\n return `${ts.Numerator}/${ts.Denominator}`;\n }\n return undefined;\n}\n\n// ─── Default OSMD acquisition ────────────────────────────────────────────────\n\n/**\n * Default OSMD instance: literal dynamic import + a detached div. Works in a\n * browser, or in Node after `setupHeadlessDom()` has installed DOM globals.\n *\n * The specifier is a LITERAL so consumers' bundlers (Vite/Rollup) can statically\n * resolve + code-split it — a variable specifier would reach the browser as a\n * bare import and fail at runtime (mirrors promo.ts:renderNotation).\n */\nasync function defaultOsmd(): Promise<any> {\n if (typeof document === 'undefined') {\n throw new Error(\n \"scoreFromMusicXML: no DOM. In Node, import setupHeadlessDom from \" +\n \"'@real-music-packages/web-core/scene/headless' and call it first, \" +\n 'or pass opts.osmdFactory.',\n );\n }\n const mod: any = await import('opensheetmusicdisplay');\n // Browser bundlers expose the named export; Node's CJS interop puts it on default.\n const OpenSheetMusicDisplay = mod.OpenSheetMusicDisplay ?? mod.default?.OpenSheetMusicDisplay;\n return new OpenSheetMusicDisplay(document.createElement('div'), {\n backend: 'svg',\n autoResize: false,\n });\n}\n","// Pure math shared by the scene primitives (camera, highlight, captions,\n// timeline). Kept separate + dependency-free so it is trivially unit-testable.\n\nexport const clamp = (x: number, lo: number, hi: number): number =>\n x < lo ? lo : x > hi ? hi : x;\n\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t;\n\n/** Inverse-lerp: where does `x` sit in [a,b] as 0..1 (clamped, a===b -> 0). */\nexport const invLerp = (a: number, b: number, x: number): number =>\n a === b ? 0 : clamp((x - a) / (b - a), 0, 1);\n\nexport type Easing = (t: number) => number;\n\nexport const linear: Easing = (t) => t;\n/** Smoothstep ease-in-out. */\nexport const easeInOut: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return c * c * (3 - 2 * c);\n};\nexport const easeIn: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return c * c;\n};\nexport const easeOut: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return 1 - (1 - c) * (1 - c);\n};\n\n/**\n * Eased 0..1 progress of a transition at absolute time `tMs`, given the incoming\n * segment's `startMs` and the transition `durationMs`. Returns 0 at/before the\n * start, 1 at/after start+durationMs, eased in between. Pure fn of t. Shared by\n * the runner so the same envelope drives crossfade alpha, push translate, and\n * wipe clip. `durationMs <= 0` → an instant 1 (degenerate hard-cut).\n */\nexport const transitionProgress = (\n tMs: number,\n startMs: number,\n durationMs: number,\n easing: Easing = easeInOut,\n): number => {\n if (durationMs <= 0) return 1;\n return easing(invLerp(startMs, startMs + durationMs, tMs));\n};\n","// Caption / subtitle engine (S1b) — timed burned-in text from a script.\n//\n// Silent-autoplay retention + accessibility: short-form video is watched muted,\n// so explainer clips need on-screen captions. A script is a list of cues with\n// in/out times; at any tMs the engine returns the active cue (one at a time) and\n// draws it inside the safe box near the bottom (above the phone action rail).\n//\n// The selection/visibility logic is pure (testable); drawCaption is the thin\n// renderer. respects safeBox so captions never sit under TikTok/Reels UI.\n\nimport type { SafeBox } from '../video';\nimport type { PromoTheme } from '../video';\nimport { clamp, invLerp } from './math';\n\nexport interface CaptionCue {\n text: string;\n /** Show from this time (ms). */\n inMs: number;\n /** Hide after this time (ms). */\n outMs: number;\n}\n\nexport type CaptionScript = CaptionCue[];\n\n/** The cue active at `tMs` (last one whose window contains t), or null. */\nexport function activeCue(script: CaptionScript, tMs: number): CaptionCue | null {\n let found: CaptionCue | null = null;\n for (const cue of script) {\n if (tMs >= cue.inMs && tMs < cue.outMs) found = cue;\n }\n return found;\n}\n\n/** Opacity 0..1 for a cue at `tMs` (short fade at both edges). */\nexport function cueOpacity(cue: CaptionCue, tMs: number, fadeMs = 150): number {\n if (tMs < cue.inMs || tMs >= cue.outMs) return 0;\n const inA = invLerp(cue.inMs, cue.inMs + fadeMs, tMs);\n const outA = 1 - invLerp(cue.outMs - fadeMs, cue.outMs, tMs);\n return clamp(Math.min(inA, outA), 0, 1);\n}\n\nexport interface CaptionStyle {\n /** Font size in px. Default 44. */\n size?: number;\n /** Pinned vertical position as a fraction of the safe-box height from its\n * top. Default 0.92 (near the bottom of the safe area). */\n yFrac?: number;\n /** Draw a translucent backing pill for legibility. Default true. */\n pill?: boolean;\n}\n\n/**\n * Greedy word-wrap into at most `maxLines` lines for the current ctx.font.\n * (Local copy so caption rendering doesn't depend on video.ts internals.)\n */\nfunction wrap(ctx: CanvasRenderingContext2D, text: string, maxW: number, maxLines: number): string[] {\n const words = text.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let cur = '';\n for (const w of words) {\n const next = cur ? `${cur} ${w}` : w;\n if (ctx.measureText(next).width > maxW && cur) {\n lines.push(cur);\n cur = w;\n if (lines.length === maxLines) break;\n } else {\n cur = next;\n }\n }\n if (cur && lines.length < maxLines) lines.push(cur);\n return lines;\n}\n\n/**\n * Draw the active caption (if any) inside the safe box. Pure function of tMs.\n * No-op when no cue is active. Drawn in VIEWPORT space (call after the camera\n * transform has been reset to identity) so captions stay pinned to the screen.\n */\nexport function drawCaption(\n ctx: CanvasRenderingContext2D,\n script: CaptionScript,\n tMs: number,\n safe: SafeBox,\n theme: PromoTheme,\n style: CaptionStyle = {},\n): void {\n const cue = activeCue(script, tMs);\n if (!cue) return;\n const alpha = cueOpacity(cue, tMs);\n if (alpha <= 0) return;\n\n const size = style.size ?? 44;\n const yFrac = style.yFrac ?? 0.92;\n const baseY = safe.top + safe.h * yFrac;\n\n ctx.save();\n ctx.globalAlpha = alpha;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.font = `bold ${size}px ${theme.fontBody}`;\n const lines = wrap(ctx, cue.text, safe.w * 0.92, 3);\n const lineH = size * 1.2;\n const blockH = lines.length * lineH;\n const top = baseY - blockH;\n\n if (style.pill !== false) {\n let maxW = 0;\n for (const ln of lines) maxW = Math.max(maxW, ctx.measureText(ln).width);\n const padX = size * 0.5;\n const padY = size * 0.3;\n ctx.fillStyle = 'rgba(0,0,0,0.55)';\n const pillW = Math.min(safe.w, maxW + padX * 2);\n ctx.fillRect(safe.cx - pillW / 2, top - padY, pillW, blockH + padY * 2);\n }\n\n ctx.fillStyle = '#ffffff';\n lines.forEach((ln, i) => {\n ctx.fillText(ln, safe.cx, top + lineH * (i + 0.5));\n });\n ctx.restore();\n}\n","// Trivial demo layers (S1b) — `background` and `caption`. They exist so the\n// runner is provably end-to-end (a SceneSpec renders frames through real layers)\n// WITHOUT pulling in the music layers, which are S2/S3. Each ships a\n// LayerFactory with a prop validator so the registry / gate can reject bad specs.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { drawCaption, type CaptionScript } from '../caption';\n\n// ─── background ────────────────────────────────────────────────────────────\n\nexport interface BackgroundProps {\n /** \"paper\" (theme.paper) | \"ink\" (theme.ink) | a literal CSS colour. */\n style?: string;\n}\n\nfunction backgroundLayer(): Layer<BackgroundProps> {\n let fill = '#000000';\n return {\n key: 'background',\n init(ctx, props) {\n const s = props.style ?? 'paper';\n fill = s === 'paper' ? ctx.theme.paper : s === 'ink' ? ctx.theme.ink : s;\n },\n draw(ctx) {\n const c = ctx.ctx2d;\n c.save();\n c.fillStyle = fill;\n c.fillRect(0, 0, ctx.W, ctx.H);\n c.restore();\n },\n };\n}\n\nexport const backgroundFactory: LayerFactory<BackgroundProps> = {\n key: 'background',\n create: backgroundLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['background: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.style != null && typeof p.style !== 'string') errs.push('background.style must be a string');\n return errs;\n },\n};\n\n// ─── caption ───────────────────────────────────────────────────────────────\n\nexport interface CaptionProps {\n /** Timed cues (in/out in ms relative to the clip start). */\n script: CaptionScript;\n size?: number;\n yFrac?: number;\n}\n\nfunction captionLayer(): Layer<CaptionProps> {\n let script: CaptionScript = [];\n let size: number | undefined;\n let yFrac: number | undefined;\n return {\n key: 'caption',\n init(_ctx: RenderCtx, props) {\n script = props.script ?? [];\n size = props.size;\n yFrac = props.yFrac;\n },\n draw(ctx, tMs) {\n drawCaption(ctx.ctx2d, script, tMs, ctx.safeBox, ctx.theme, { size, yFrac });\n },\n };\n}\n\nexport const captionFactory: LayerFactory<CaptionProps> = {\n key: 'caption',\n create: captionLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['caption: props must be an object'];\n const p = props as Record<string, unknown>;\n if (!Array.isArray(p.script)) {\n errs.push('caption.script must be an array of cues');\n } else {\n p.script.forEach((cue, i) => {\n const c = cue as Record<string, unknown>;\n if (typeof c?.text !== 'string') errs.push(`caption.script[${i}].text must be a string`);\n if (typeof c?.inMs !== 'number' || typeof c?.outMs !== 'number')\n errs.push(`caption.script[${i}] needs numeric inMs/outMs`);\n else if (c.outMs <= c.inMs) errs.push(`caption.script[${i}] outMs must be > inMs`);\n });\n }\n if (p.size != null && typeof p.size !== 'number') errs.push('caption.size must be a number');\n if (p.yFrac != null && typeof p.yFrac !== 'number') errs.push('caption.yFrac must be a number');\n return errs;\n },\n};\n","// Keyboard geometry (S3) — the pure, canvas-free layout shared by the `keyboard`\n// and `falling-notes` layers. Both compute (or read) the SAME KeyboardLayout, so\n// a falling note's column x is, by construction, the x of its key on the\n// keyboard (the headline \"two layers agree\" guarantee — proved in the invariant\n// tests).\n//\n// Coordinate space: world pixels (0,0 top-left, the runner's frame space). A\n// layout pins the keyboard to a strip [x, x+w] × [top, top+height]; key x's are\n// derived from an evenly-spaced WHITE-key grid with black keys overlaid at the\n// canonical between-the-whites offsets. Falling notes use `keyCenterX(midi)`.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\n/** Chromatic class is a black key (C#, D#, F#, G#, A#). */\nconst BLACK_PC = new Set([1, 3, 6, 8, 10]);\n\n/** Number of white keys at-or-below a midi note, counting from MIDI 0 (C-1). */\nfunction whiteIndexAtOrBelow(midi: number): number {\n // Whites per octave below the pitch class within the octave.\n const oct = Math.floor(midi / 12);\n const pc = midi - oct * 12;\n // 0-based white index of the white key AT OR BELOW each pitch class. White pcs\n // map to their own index; black pcs map to the white immediately BELOW them\n // (C#→C, D#→D, F#→F, G#→G, A#→A). (Previously this mapped black keys to the\n // white ABOVE, shifting every black key one white-width right — visible as a\n // Bb spilling off the right edge when the range topped out on a B.)\n const WHITES_BELOW = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6]; // C..B\n return oct * 7 + WHITES_BELOW[pc];\n}\n\nexport function isBlackKey(midi: number): boolean {\n return BLACK_PC.has(((midi % 12) + 12) % 12);\n}\n\n/** A resolved keyboard layout: which keys, where, pinned to a world strip. */\nexport interface KeyboardLayout {\n /** Lowest MIDI note drawn (inclusive). */\n lowMidi: number;\n /** Highest MIDI note drawn (inclusive). */\n highMidi: number;\n /** Strip left edge (world px). */\n x: number;\n /** Strip width (world px) — the white-key span. */\n w: number;\n /** Strip top edge (world px). */\n top: number;\n /** Strip height (world px) = white-key height. */\n height: number;\n /** White-key width (world px). */\n whiteW: number;\n /** First (leftmost) white key index, used to offset the white grid to x. */\n firstWhiteIndex: number;\n /** Count of white keys in [lowMidi, highMidi]. */\n whiteCount: number;\n}\n\n/** Standard 88-key piano: A0 (21) .. C8 (108). */\nexport const PIANO_LOW = 21;\nexport const PIANO_HIGH = 108;\n\n/** Snap a midi down to the nearest white key (so the range starts/ends on a white). */\nfunction snapDownToWhite(midi: number): number {\n let m = midi;\n while (isBlackKey(m)) m--;\n return m;\n}\nfunction snapUpToWhite(midi: number): number {\n let m = midi;\n while (isBlackKey(m)) m++;\n return m;\n}\n\nexport interface KeyboardLayoutOpts {\n /** \"88\" = full piano; \"auto\" = derive from a pitch span (padded to whites). */\n range?: '88' | 'auto';\n /** For \"auto\": the [lowMidi, highMidi] span to cover (snapped out to whites,\n * padded by one white each side so edge keys aren't flush). */\n span?: [number, number];\n /** Strip left/width/top/height in world px. */\n x: number;\n w: number;\n top: number;\n height: number;\n}\n\n/** Build a KeyboardLayout. Pure. */\nexport function keyboardLayout(opts: KeyboardLayoutOpts): KeyboardLayout {\n let low: number;\n let high: number;\n if (opts.range === 'auto' && opts.span) {\n low = snapDownToWhite(opts.span[0]);\n high = snapUpToWhite(opts.span[1]);\n // Pad one white key each side for breathing room (and so falling columns at\n // the extremes aren't half-clipped).\n low = snapDownToWhite(low - 1);\n high = snapUpToWhite(high + 1);\n } else {\n low = PIANO_LOW;\n high = PIANO_HIGH;\n }\n const firstWhiteIndex = whiteIndexAtOrBelow(low);\n const lastWhiteIndex = whiteIndexAtOrBelow(high);\n const whiteCount = lastWhiteIndex - firstWhiteIndex + 1;\n const whiteW = opts.w / whiteCount;\n return {\n lowMidi: low,\n highMidi: high,\n x: opts.x,\n w: opts.w,\n top: opts.top,\n height: opts.height,\n whiteW,\n firstWhiteIndex,\n whiteCount,\n };\n}\n\n/** The pitch span [min,max] of a Score's notes (for \"auto\" range). */\nexport function scorePitchSpan(midis: number[]): [number, number] {\n if (!midis.length) return [PIANO_LOW, PIANO_HIGH];\n return [Math.min(...midis), Math.max(...midis)];\n}\n\n/**\n * Resolve a KeyboardLayout from the shared keyboard placement props + the Score's\n * pitch span. The `keyboard` and `falling-notes` layers BOTH call this with the\n * same inputs in standalone mode, so they produce identical geometry even without\n * the store (the store just covers props that differ). Pure.\n *\n * Placement: the strip spans the safe box horizontally; its bottom sits at\n * `bottomY` (world px) with the given `height`.\n */\nexport function resolveKeyboardLayout(args: {\n range: '88' | 'auto';\n pitchMidis: number[];\n left: number;\n width: number;\n bottomY: number;\n height: number;\n}): KeyboardLayout {\n return keyboardLayout({\n range: args.range,\n span: args.range === 'auto' ? scorePitchSpan(args.pitchMidis) : undefined,\n x: args.left,\n w: args.width,\n top: args.bottomY - args.height,\n height: args.height,\n });\n}\n\n/**\n * Center x (world px) of a note's KEY on the keyboard. White keys sit on the\n * even grid; black keys are nudged to sit between their two neighbouring whites\n * (the canonical piano offset), so a falling block lands centered over the right\n * key. This is the SINGLE source both layers use — they agree by construction.\n */\nexport function keyCenterX(layout: KeyboardLayout, midi: number): number {\n const wIdx = whiteIndexAtOrBelow(midi) - layout.firstWhiteIndex;\n const whiteCenter = layout.x + (wIdx + 0.5) * layout.whiteW;\n if (!isBlackKey(midi)) return whiteCenter;\n // A black key sits on the boundary between its lower white and the next white,\n // i.e. half a white-width to the RIGHT of the lower white's center.\n return whiteCenter + layout.whiteW / 2;\n}\n\n/** Width (world px) a falling-note column should use for a pitch. Black keys are\n * drawn narrower (like the physical key); white keys ~ a white-key width. */\nexport function keyColumnWidth(layout: KeyboardLayout, midi: number): number {\n return isBlackKey(midi) ? layout.whiteW * 0.6 : layout.whiteW * 0.9;\n}\n\n/** The drawable rectangle for a key on the keyboard strip (white or black). */\nexport interface KeyRect {\n x: number;\n y: number;\n w: number;\n h: number;\n black: boolean;\n}\n\nexport function keyRect(layout: KeyboardLayout, midi: number): KeyRect {\n const cx = keyCenterX(layout, midi);\n if (isBlackKey(midi)) {\n const w = layout.whiteW * 0.6;\n return { x: cx - w / 2, y: layout.top, w, h: layout.height * 0.62, black: true };\n }\n const w = layout.whiteW;\n return { x: cx - w / 2, y: layout.top, w, h: layout.height, black: false };\n}\n\n/** Whether `midi` is within the layout's drawn range. */\nexport function inRange(layout: KeyboardLayout, midi: number): boolean {\n return midi >= layout.lowMidi && midi <= layout.highMidi;\n}\n\n/** All white-key midis in the layout (low..high), for drawing the bed. */\nexport function whiteKeys(layout: KeyboardLayout): number[] {\n const out: number[] = [];\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) if (!isBlackKey(m)) out.push(m);\n return out;\n}\n\n/** All black-key midis in the layout (low..high), drawn on top of the whites. */\nexport function blackKeys(layout: KeyboardLayout): number[] {\n const out: number[] = [];\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) if (isBlackKey(m)) out.push(m);\n return out;\n}\n\n// ─── Colour ────────────────────────────────────────────────────────────────\n\nexport type ColorBy = 'hand' | 'pitch-class';\n\n/** Right/left hand colours. R = theme accent (the lead colour); L = a cooler\n * contrast. Both passed in so a host theme can override. */\nexport interface HandColors {\n R: string;\n L: string;\n}\n\n/** 12 pitch-class hues (a perceptually-spread wheel, C..B). Stable + readable. */\nconst PC_COLORS = [\n '#e64545', '#e6803a', '#e6c23a', '#9bcf3a', '#3acf6e', '#3acfb0',\n '#3aa6e6', '#3a5fe6', '#7a3ae6', '#b03ae6', '#e63ab0', '#e63a6e',\n];\n\n/**\n * Colour for a note. `hand` reads the Score's accurate hand (NOT a pitch\n * threshold guess); `pitch-class` colours by chroma. Pure.\n */\nexport function noteColor(\n midi: number,\n hand: 'L' | 'R',\n colorBy: ColorBy,\n hands: HandColors,\n): string {\n if (colorBy === 'pitch-class') return PC_COLORS[(((midi % 12) + 12) % 12)];\n return hand === 'L' ? hands.L : hands.R;\n}\n","// Shared keyboard handoff (S3) — lets the `falling-notes` layer read the EXACT\n// KeyboardLayout the `keyboard` layer computed, so falling-note columns land on\n// the same key x's the keyboard draws (the \"two layers agree by construction\"\n// guarantee). Mirrors engravingStore: a per-render WeakMap keyed by the runner's\n// shared audioClock object (one per render) — GC-friendly, no globals.\n//\n// When no keyboard layer is in the scene, falling-notes computes its OWN layout\n// from its own range/span props (standalone mode); the store is just the channel\n// that keeps the paired case in lockstep.\n\nimport type { RenderCtx } from './layer';\nimport type { KeyboardLayout } from './keyboardGeometry';\n\nconst STORE = new WeakMap<object, KeyboardLayout>();\n\nfunction keyFor(ctx: RenderCtx): object {\n return ctx.audioClock;\n}\n\nexport function setKeyboardLayout(ctx: RenderCtx, layout: KeyboardLayout): void {\n STORE.set(keyFor(ctx), layout);\n}\n\nexport function getKeyboardLayout(ctx: RenderCtx): KeyboardLayout | undefined {\n return STORE.get(keyFor(ctx));\n}\n","// `keyboard` layer (S3) — a piano-keyboard strip pinned to the bottom of the\n// safe box. Lights each key while its note SOUNDS (onsetMs ≤ t < onsetMs+durMs),\n// two-coloured by `hand` straight off the Score (accurate, not pitch-threshold-\n// guessed). Pairs with `falling-notes`: it publishes its resolved KeyboardLayout\n// (keyboardStore) so falling-note columns land on the same key x's.\n//\n// Range: \"88\" (full A0..C8) or \"auto\" (derived from the Score's pitch span,\n// snapped + padded to whites). Pure draw fn of t — no wall clock.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n resolveKeyboardLayout,\n keyRect,\n keyCenterX,\n whiteKeys,\n blackKeys,\n noteColor,\n type KeyboardLayout,\n type ColorBy,\n type HandColors,\n} from '../keyboardGeometry';\nimport { setKeyboardLayout } from '../keyboardStore';\n\nexport interface KeyboardProps {\n /** \"88\" = full piano; \"auto\" = fit the Score's pitch span. Default \"auto\". */\n range?: '88' | 'auto';\n /** Colour active keys by performing hand (Score-accurate) or by pitch class.\n * Default \"hand\". */\n colorBy?: ColorBy;\n /** Strip height in world px. Default 220. */\n height?: number;\n /** Strip bottom edge in world px. Default safeBox.bottom. */\n bottomY?: number;\n /** Right/left hand colours. Defaults: R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n /** Realistic 3-D piano styling: white/black key gradients, rounded key fronts,\n * a felt rail + cast shadow at the top, and a soft glow on lit keys. Default\n * false (the flat two-tone look — unchanged for existing consumers). */\n realistic?: boolean;\n}\n\n/** Rounded-rect path with per-corner radii [tl, tr, br, bl] (falls back to a\n * plain rect if `roundRect` is unavailable). */\nfunction roundedPath(\n c: CanvasRenderingContext2D,\n x: number, y: number, w: number, h: number,\n radii: [number, number, number, number],\n): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number[]) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, radii); return; }\n c.rect(x, y, w, h); // graceful fallback\n}\n\n/** Lighten/darken a #rrggbb by `f` (>0 toward white, <0 toward black). */\nfunction shade(hex: string, f: number): string {\n const n = parseInt(hex.slice(1), 16);\n const t = f < 0 ? 0 : 255, p = Math.abs(f);\n const r = Math.round(((n >> 16) & 255) + (t - ((n >> 16) & 255)) * p);\n const g = Math.round(((n >> 8) & 255) + (t - ((n >> 8) & 255)) * p);\n const b = Math.round((n & 255) + (t - (n & 255)) * p);\n return `rgb(${r},${g},${b})`;\n}\n\nfunction keyboardLayer(): Layer<KeyboardProps> {\n let layout: KeyboardLayout | null = null;\n let colorBy: ColorBy = 'hand';\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let realistic = false;\n\n return {\n key: 'keyboard',\n init(ctx, props) {\n colorBy = props.colorBy ?? 'hand';\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n realistic = props.realistic ?? false;\n const sb = ctx.safeBox;\n const height = props.height ?? 220;\n const bottomY = props.bottomY ?? sb.bottom;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n layout = resolveKeyboardLayout({\n range: props.range ?? 'auto',\n pitchMidis: midis,\n left: sb.left,\n width: sb.w,\n bottomY,\n height,\n });\n // Publish so falling-notes uses the IDENTICAL layout (agree by construction).\n setKeyboardLayout(ctx, layout);\n },\n\n draw(ctx, tMs) {\n if (!layout) return;\n const c = ctx.ctx2d;\n const L = layout;\n\n // Which midis are sounding now, and their hand (for the lit colour).\n const lit = new Map<number, 'L' | 'R'>();\n for (const n of ctx.score?.notes ?? []) {\n if (tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs) lit.set(n.pitchMidi, n.hand);\n }\n\n if (realistic) { drawRealisticKeyboard(c, L, lit, colorBy, hands); return; }\n\n c.save();\n // White-key bed.\n for (const m of whiteKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n c.fillStyle = onHand ? noteColor(m, onHand, colorBy, hands) : '#fbfbfb';\n c.fillRect(r.x, r.y, r.w, r.h);\n c.strokeStyle = '#b8b0a4';\n c.lineWidth = 1;\n c.strokeRect(r.x, r.y, r.w, r.h);\n }\n // Black keys on top.\n for (const m of blackKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n c.fillStyle = onHand ? noteColor(m, onHand, colorBy, hands) : '#1a1614';\n c.fillRect(r.x, r.y, r.w, r.h);\n }\n c.restore();\n },\n\n dispose() {\n layout = null;\n },\n };\n}\n\n// Realistic 3-D piano: gradient white/black keys with rounded fronts, a felt\n// rail + cast shadow at the top, and a soft glow on the lit keys.\nfunction drawRealisticKeyboard(\n c: CanvasRenderingContext2D,\n L: KeyboardLayout,\n lit: Map<number, 'L' | 'R'>,\n colorBy: ColorBy,\n hands: HandColors,\n): void {\n {\n const wr = keyRect(L, whiteKeys(L)[0] ?? 0);\n const wRad = Math.max(3, Math.min(8, wr.w * 0.18));\n\n c.save();\n // White keys: vertical gradient, rounded fronts, hairline separators.\n for (const m of whiteKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n const x = r.x + 0.75, w = r.w - 1.5; // tiny gap → visible key separation\n roundedPath(c, x, r.y, w, r.h, [2, 2, wRad, wRad]);\n if (onHand) {\n const base = noteColor(m, onHand, colorBy, hands);\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, shade(base, 0.18));\n g.addColorStop(1, base);\n c.fillStyle = g;\n } else {\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, '#e9e4da'); // shaded near the fallboard\n g.addColorStop(0.12, '#fbfaf7');\n g.addColorStop(1, '#ffffff');\n c.fillStyle = g;\n }\n c.fill();\n c.strokeStyle = '#cdc6ba';\n c.lineWidth = 1;\n c.stroke();\n }\n // Felt rail + cast shadow at the very top (keys recede under the fallboard).\n const railH = Math.max(4, L.height * 0.03);\n c.fillStyle = '#7b2436';\n c.fillRect(L.x, L.top - railH, L.w, railH);\n const sh = c.createLinearGradient(0, L.top, 0, L.top + L.height * 0.14);\n sh.addColorStop(0, 'rgba(0,0,0,0.22)');\n sh.addColorStop(1, 'rgba(0,0,0,0)');\n c.fillStyle = sh;\n c.fillRect(L.x, L.top, L.w, L.height * 0.14);\n\n // Black keys: narrower, shorter, gradient body with a glossy bottom bevel.\n for (const m of blackKeys(L)) {\n const r = keyRect(L, m);\n const bRad = Math.max(2, r.w * 0.22);\n const onHand = lit.get(m);\n roundedPath(c, r.x, r.y, r.w, r.h, [1, 1, bRad, bRad]);\n if (onHand) {\n const base = noteColor(m, onHand, colorBy, hands);\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, shade(base, -0.25));\n g.addColorStop(1, base);\n c.fillStyle = g;\n } else {\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, '#15110e');\n g.addColorStop(0.82, '#2c2622');\n g.addColorStop(1, '#453d36'); // bevel highlight at the front\n c.fillStyle = g;\n }\n c.fill();\n // Thin specular highlight along the front lip.\n c.fillStyle = onHand ? 'rgba(255,255,255,0.28)' : 'rgba(255,255,255,0.10)';\n roundedPath(c, r.x + r.w * 0.18, r.y + r.h - Math.max(2, r.h * 0.06), r.w * 0.64, Math.max(1.5, r.h * 0.035), [1, 1, 1, 1]);\n c.fill();\n }\n\n // Soft glow over keys that are sounding.\n for (const [m, hand] of lit) {\n const r = keyRect(L, m);\n c.save();\n c.shadowColor = noteColor(m, hand, colorBy, hands);\n c.shadowBlur = 22;\n c.globalAlpha = 0.55;\n c.fillStyle = noteColor(m, hand, colorBy, hands);\n c.fillRect(r.x + r.w * 0.2, r.y + r.h * 0.5, r.w * 0.6, r.h * 0.3);\n c.restore();\n }\n c.restore();\n }\n}\n\nexport const keyboardFactory: LayerFactory<KeyboardProps> = {\n key: 'keyboard',\n create: keyboardLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['keyboard: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.range != null && p.range !== '88' && p.range !== 'auto')\n errs.push('keyboard.range must be \"88\" | \"auto\"');\n if (p.colorBy != null && p.colorBy !== 'hand' && p.colorBy !== 'pitch-class')\n errs.push('keyboard.colorBy must be \"hand\" | \"pitch-class\"');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('keyboard.height must be a positive number');\n if (p.bottomY != null && typeof p.bottomY !== 'number')\n errs.push('keyboard.bottomY must be a number');\n if (p.realistic != null && typeof p.realistic !== 'boolean')\n errs.push('keyboard.realistic must be a boolean');\n return errs;\n },\n};\n\n// Re-export keyCenterX so falling-notes' agreement is via the SAME function.\nexport { keyCenterX };\n","// `fretboard` layer — a guitar fretboard pinned in the safe box. Draws strings,\n// frets, nut + inlay markers, and lights each note's fret position while it SOUNDS.\n// Positions come from ONE coherent placement map over the whole excerpt\n// (buildPlacementMap) so lit frets don't jump strings note-to-note. Sibling of\n// `keyboard.ts`; pure draw fn of t.\nimport type { Layer, LayerFactory } from '../layer';\nimport { STRING_COUNT, buildPlacementMap, type FretPosition } from '../../music/fretboard';\n\nexport interface FretboardProps {\n /** Highest fret drawn. Default: auto-fit the excerpt (min 5, max 15), padded. */\n visibleFrets?: number;\n /** Board height in world px. Default 360 — 6 strings need more vertical room\n * than the piano strip (220 cramped them to ~37px each). */\n height?: number;\n /** Board bottom edge in world px. Default safeBox.bottom. */\n bottomY?: number;\n /** Right/left hand dot colours. Defaults R=theme.accent, L=theme.gold. */\n handColors?: { R: string; L: string };\n /** Draw note-name labels inside lit dots. Default false. */\n showLabels?: boolean;\n}\n\nconst INLAY_FRETS = [3, 5, 7, 9, 12];\nconst NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];\n\nfunction fretboardLayer(): Layer<FretboardProps> {\n let placement = new Map<number, FretPosition>();\n let visibleFrets = 12;\n let hands = { R: '#7b2436', L: '#c8a55b' };\n let showLabels = false;\n let rect = { x: 0, y: 0, w: 0, h: 0 };\n\n return {\n key: 'fretboard',\n init(ctx, props) {\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n showLabels = props.showLabels ?? false;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n placement = buildPlacementMap(midis);\n // auto-fit visible frets to the highest used fret (+1 pad), clamped [5,15].\n let maxFret = 0;\n for (const p of placement.values()) maxFret = Math.max(maxFret, p.fret);\n visibleFrets = props.visibleFrets ?? Math.min(15, Math.max(5, maxFret + 1));\n const sb = ctx.safeBox;\n const height = props.height ?? 360;\n const bottomY = props.bottomY ?? sb.bottom;\n rect = { x: sb.left, y: bottomY - height, w: sb.w, h: height };\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const { x, y, w, h } = rect;\n const nutW = Math.max(6, w * 0.012);\n const boardX = x + nutW;\n const boardW = w - nutW;\n const fretW = boardW / visibleFrets; // equal spacing (diagram style)\n const stringGap = h / (STRING_COUNT - 1);\n // string index 0 = low E at the BOTTOM, high E at the top (TAB orientation).\n const stringY = (s: number) => y + h - s * stringGap;\n const fretX = (f: number) => boardX + f * fretW; // fret line positions\n // a note AT fret f is drawn in the middle of the fret space [f-1, f]; open (0) on the nut.\n const dotX = (f: number) => (f === 0 ? x + nutW / 2 : fretX(f) - fretW / 2);\n\n c.save();\n // Board background.\n c.fillStyle = '#f1e7d6';\n c.fillRect(x, y, w, h);\n // Nut.\n c.fillStyle = '#2a2420';\n c.fillRect(x, y, nutW, h);\n // Frets.\n c.strokeStyle = '#b8a98c';\n c.lineWidth = 2;\n for (let f = 1; f <= visibleFrets; f++) {\n c.beginPath(); c.moveTo(fretX(f), y); c.lineTo(fretX(f), y + h); c.stroke();\n }\n // Inlay markers (single dots, double at 12).\n c.fillStyle = 'rgba(60,50,40,0.28)';\n for (const f of INLAY_FRETS) {\n if (f > visibleFrets) continue;\n const cx = dotX(f);\n const r = Math.min(stringGap, fretW) * 0.18;\n if (f === 12) {\n c.beginPath(); c.arc(cx, stringY(STRING_COUNT - 1) + stringGap * 0.5, r, 0, Math.PI * 2); c.fill();\n c.beginPath(); c.arc(cx, stringY(0) - stringGap * 0.5, r, 0, Math.PI * 2); c.fill();\n } else {\n c.beginPath(); c.arc(cx, y + h / 2, r, 0, Math.PI * 2); c.fill();\n }\n }\n // Strings (thicker for low strings).\n c.strokeStyle = '#5c5345';\n for (let s = 0; s < STRING_COUNT; s++) {\n c.lineWidth = 1 + (STRING_COUNT - 1 - s) * 0.5;\n c.beginPath(); c.moveTo(x, stringY(s)); c.lineTo(x + w, stringY(s)); c.stroke();\n }\n\n // Lit notes: which midis sound now → their placement → a dot on that string/fret.\n const dotR = Math.min(stringGap, fretW) * 0.42;\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n const pos = placement.get(n.pitchMidi);\n if (!pos || pos.fret > visibleFrets) continue;\n const cx = dotX(pos.fret);\n const cy = stringY(pos.string);\n c.fillStyle = n.hand === 'L' ? hands.L : hands.R;\n c.beginPath(); c.arc(cx, cy, dotR, 0, Math.PI * 2); c.fill();\n if (showLabels) {\n c.fillStyle = '#fff';\n c.font = `${Math.round(dotR * 1.1)}px sans-serif`;\n c.textAlign = 'center'; c.textBaseline = 'middle';\n c.fillText(NOTE_NAMES[n.pitchMidi % 12], cx, cy);\n }\n }\n c.restore();\n },\n\n dispose() { placement = new Map(); },\n };\n}\n\nexport const fretboardFactory: LayerFactory<FretboardProps> = {\n key: 'fretboard',\n create: fretboardLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['fretboard: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.visibleFrets != null && (typeof p.visibleFrets !== 'number' || p.visibleFrets < 1 || p.visibleFrets > 24))\n errs.push('fretboard.visibleFrets must be a number in 1..24');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('fretboard.height must be a positive number');\n if (p.bottomY != null && typeof p.bottomY !== 'number') errs.push('fretboard.bottomY must be a number');\n if (p.showLabels != null && typeof p.showLabels !== 'boolean') errs.push('fretboard.showLabels must be a boolean');\n return errs;\n },\n};\n","// `falling-notes` layer (S3) — Synthesia-style note blocks falling toward the\n// keyboard's hit-line. The headline new capability.\n//\n// Sync (pure fn of t, audio-clock-driven):\n// - The hit-line is the keyboard's top edge (or the bottom of the fall region\n// when standalone). A note arrives at the hit-line EXACTLY at onsetMs.\n// - leadMs is how long a note is visible falling before it lands. The note\n// travels the full fall height over leadMs, so px/ms = fallHeight / leadMs.\n// `speed` (px/sec) is an alternative spelling; leadMs wins if both given.\n// - A note's block: bottom edge tracks the time-to-onset; its LENGTH ∝ durMs\n// (length px = durMs * px/ms). The block crosses the hit-line at onset and is\n// fully gone once its TOP passes the hit-line, i.e. after onsetMs + durMs.\n// - hitGlow: a brief flash at the hit-line while a note is sounding.\n//\n// Column x: read from the keyboard layer's published KeyboardLayout (keyboardStore)\n// when paired, else compute the IDENTICAL layout from this layer's own props. So a\n// falling block is always centered on its key — the two layers agree by\n// construction (proved in the invariant tests via keyCenterX equality).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n resolveKeyboardLayout,\n keyCenterX,\n keyColumnWidth,\n inRange,\n noteColor,\n type KeyboardLayout,\n type ColorBy,\n type HandColors,\n} from '../keyboardGeometry';\nimport { getKeyboardLayout } from '../keyboardStore';\n\nexport interface FallingNotesProps {\n /** Pair with a `keyboard` layer (read its layout + hit-line). When false the\n * layer is standalone and builds its own layout from the props below.\n * Default true. */\n keyboard?: boolean;\n /** Colour by performing hand (Score-accurate) or by pitch class. Default \"hand\". */\n colorBy?: ColorBy;\n /** Lead time in ms a note is visible before landing. Default 2000. */\n leadMs?: number;\n /** Fall speed in px/sec. Alternative to leadMs (ignored when leadMs is given). */\n speed?: number;\n /** Glow flash at the hit-line while a note sounds. Default true. */\n hitGlow?: boolean;\n /** Glowy/translucent block styling: rounded corners, a vertical gradient that\n * fades toward the top, a soft bloom (shadow) in the note colour, and a bright\n * leading-edge cap. Default false (flat opaque rectangles — unchanged). */\n glow?: boolean;\n /** Standalone-only: keyboard range when `keyboard:false`. Default \"auto\". */\n range?: '88' | 'auto';\n /** Standalone-only: hit-line (world y). Default safeBox.bottom - 220. */\n hitLineY?: number;\n /** Top of the fall region (world y) — notes appear here. Default safeBox.top. */\n topY?: number;\n /** Right/left hand colours. Defaults: R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n}\n\nconst DEFAULT_LEAD_MS = 2000;\n\n/** Parse \"#rrggbb\" → \"rgba(r,g,b,a)\"; passes other colour strings through (no\n * alpha applied) so the glow still draws something sensible. */\nfunction rgba(color: string, a: number): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) {\n const n = parseInt(color.slice(1), 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n }\n return color;\n}\n\n/** Rounded-rect path (falls back to a plain rect if `roundRect` is unavailable). */\nfunction roundedRect(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, r); return; }\n c.rect(x, y, w, h);\n}\n\n/** A glowy/translucent falling block: rounded, a vertical gradient fading toward\n * the top, a soft colour bloom, and a bright leading-edge cap at the bottom. */\nfunction drawGlowBlock(c: CanvasRenderingContext2D, x: number, y: number, w: number, hRaw: number, color: string): void {\n const h = Math.max(2, hRaw);\n const r = Math.min(w * 0.42, 8);\n const bx = x + 1, bw = Math.max(1, w - 2);\n c.save();\n // Outer bloom: a soft, wide colour halo (drawn as a faint pass behind the body).\n c.shadowColor = rgba(color, 1);\n c.shadowBlur = 22;\n c.fillStyle = rgba(color, 0.5);\n roundedRect(c, bx, y, bw, h, r);\n c.fill();\n c.fill(); // second pass deepens the bloom\n c.shadowBlur = 0;\n // Translucent body: a vertical gradient that fades toward the trailing (top) end\n // so the backdrop reads through — glassy, not a solid bar.\n const g = c.createLinearGradient(0, y, 0, y + h);\n g.addColorStop(0, rgba(color, 0.06));\n g.addColorStop(0.5, rgba(color, 0.38));\n g.addColorStop(1, rgba(color, 0.78));\n c.fillStyle = g;\n roundedRect(c, bx, y, bw, h, r);\n c.fill();\n // Glassy center highlight (a soft lighter streak) for a tube-of-light look.\n const hl = c.createLinearGradient(bx, 0, bx + bw, 0);\n hl.addColorStop(0, 'rgba(255,255,255,0)');\n hl.addColorStop(0.5, 'rgba(255,255,255,0.22)');\n hl.addColorStop(1, 'rgba(255,255,255,0)');\n c.fillStyle = hl;\n roundedRect(c, bx + bw * 0.18, y, bw * 0.46, h, Math.min(r, bw * 0.2));\n c.fill();\n // Bright leading-edge cap (where the note strikes the key).\n const capH = Math.min(7, h);\n c.fillStyle = rgba(color, 0.98);\n roundedRect(c, bx, y + h - capH, bw, capH, Math.min(r, capH / 2));\n c.fill();\n c.restore();\n}\n\nfunction fallingNotesLayer(): Layer<FallingNotesProps> {\n let paired = true;\n let colorBy: ColorBy = 'hand';\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let hitGlow = true;\n let glow = false;\n let ownLayout: KeyboardLayout | null = null;\n let topY = 0;\n let leadMsProp: number | undefined;\n let speedProp: number | undefined;\n\n /** The layout to use this frame: the keyboard's published one when paired,\n * else this layer's own. */\n function layoutFor(ctx: RenderCtx): KeyboardLayout | null {\n if (paired) return getKeyboardLayout(ctx) ?? ownLayout;\n return ownLayout;\n }\n\n /** Hit-line y = keyboard top (paired) or the resolved layout top. */\n function hitLineY(layout: KeyboardLayout): number {\n return layout.top;\n }\n\n /** px per ms given the fall height; leadMs wins, else speed (px/sec). */\n function pxPerMs(fallHeight: number): number {\n if (leadMsProp != null) return fallHeight / leadMsProp;\n if (speedProp != null) return speedProp / 1000;\n return fallHeight / DEFAULT_LEAD_MS;\n }\n\n return {\n key: 'falling-notes',\n init(ctx, props) {\n paired = props.keyboard ?? true;\n colorBy = props.colorBy ?? 'hand';\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n hitGlow = props.hitGlow ?? true;\n glow = props.glow ?? false;\n leadMsProp = props.leadMs;\n speedProp = props.speed;\n const sb = ctx.safeBox;\n topY = props.topY ?? sb.top;\n\n if (!paired) {\n // Standalone: build the SAME layout the keyboard layer would, so a later\n // keyboard pairing would line up exactly.\n const hitLine = props.hitLineY ?? sb.bottom - 220;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n ownLayout = resolveKeyboardLayout({\n range: props.range ?? 'auto',\n pitchMidis: midis,\n left: sb.left,\n width: sb.w,\n // top of the keyboard == hit-line; height below it is irrelevant here.\n bottomY: hitLine + 1,\n height: 1,\n });\n }\n },\n\n draw(ctx, tMs) {\n const layout = layoutFor(ctx);\n if (!layout) return;\n const c = ctx.ctx2d;\n const hit = hitLineY(layout);\n const fallH = Math.max(1, hit - topY);\n const v = pxPerMs(fallH); // px per ms\n\n for (const n of ctx.score?.notes ?? []) {\n if (!inRange(layout, n.pitchMidi)) continue;\n // Bottom edge: at t==onset it sits ON the hit-line; before, it's above.\n const bottomY = hit - (n.onsetMs - tMs) * v;\n const lenPx = Math.max(2, n.durMs * v);\n const topEdge = bottomY - lenPx;\n // Cull: not yet entered the top of the region, or fully past the hit-line.\n if (bottomY < topY) continue; // hasn't appeared (before onset - leadMs)\n if (topEdge > hit) continue; // fully below the hit-line (after onset+dur)\n\n const cx = keyCenterX(layout, n.pitchMidi);\n const w = keyColumnWidth(layout, n.pitchMidi);\n const drawTop = Math.max(topY, topEdge);\n const drawBottom = Math.min(hit, bottomY);\n const fill = noteColor(n.pitchMidi, n.hand, colorBy, hands);\n\n if (glow) {\n drawGlowBlock(c, cx - w / 2, drawTop, w, drawBottom - drawTop, fill);\n } else {\n c.save();\n c.fillStyle = fill;\n c.globalAlpha = 0.92;\n c.fillRect(cx - w / 2, drawTop, w, Math.max(1, drawBottom - drawTop));\n c.restore();\n }\n }\n\n // Hit-line glow: flash over keys whose note is sounding now.\n if (hitGlow) {\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(layout, n.pitchMidi)) continue;\n const cx = keyCenterX(layout, n.pitchMidi);\n const w = keyColumnWidth(layout, n.pitchMidi);\n c.save();\n c.globalAlpha = 0.5;\n c.fillStyle = noteColor(n.pitchMidi, n.hand, colorBy, hands);\n c.fillRect(cx - w / 2, hit - 8, w, 8);\n c.restore();\n }\n }\n },\n\n dispose() {\n ownLayout = null;\n },\n };\n}\n\nexport const fallingNotesFactory: LayerFactory<FallingNotesProps> = {\n key: 'falling-notes',\n create: fallingNotesLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['falling-notes: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.keyboard != null && typeof p.keyboard !== 'boolean')\n errs.push('falling-notes.keyboard must be a boolean');\n if (p.colorBy != null && p.colorBy !== 'hand' && p.colorBy !== 'pitch-class')\n errs.push('falling-notes.colorBy must be \"hand\" | \"pitch-class\"');\n if (p.leadMs != null && (typeof p.leadMs !== 'number' || p.leadMs <= 0))\n errs.push('falling-notes.leadMs must be a positive number');\n if (p.speed != null && (typeof p.speed !== 'number' || p.speed <= 0))\n errs.push('falling-notes.speed must be a positive number');\n if (p.hitGlow != null && typeof p.hitGlow !== 'boolean')\n errs.push('falling-notes.hitGlow must be a boolean');\n if (p.glow != null && typeof p.glow !== 'boolean')\n errs.push('falling-notes.glow must be a boolean');\n if (p.range != null && p.range !== '88' && p.range !== 'auto')\n errs.push('falling-notes.range must be \"88\" | \"auto\"');\n return errs;\n },\n};\n","// `hook` / `reveal` / `cta` layers (S4) — thin Layer wrappers around the\n// EXISTING, battle-tested scene builders in src/video.ts (hookScene /\n// revealScene / ctaScene). We do NOT re-implement the drawing: each wrapper\n// builds the original Scene once in init() and, per frame, converts the runner's\n// absolute `tMs` into the Scene's local 0..1 `t01` and calls Scene.draw — so the\n// pixels are equivalent to the current builders BY CONSTRUCTION.\n//\n// These are screen-pinned cards (the runner draws them in identity-transform\n// space — see SCREEN_PINNED_KEYS), matching how RMT/whozart/RSR draw their\n// hook/reveal/cta over the whole frame today.\n//\n// Timing: a Scene maps t01∈[0,1] across its own duration. The runner only calls\n// draw within a segment window, but it does NOT pass the window bounds to draw.\n// So each wrapper takes `startMs` (segment start) + `durationMs` (segment length)\n// props; `t01 = clamp((tMs - startMs)/durationMs, 0, 1)`. A host SceneSpec\n// builder sets these to the segment's `at` (the S4 demo does this); defaults fall\n// back to the original builder's natural durationMs anchored at 0.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n hookScene,\n revealScene,\n ctaScene,\n loadPortrait,\n type Scene,\n type HookSceneOpts,\n type RevealSceneOpts,\n type CtaSceneOpts,\n} from '../../video';\n\n// ─── shared timing helper ────────────────────────────────────────────────────\n\n/** Map absolute tMs to the wrapped Scene's local t01∈[0,1]. */\nfunction localT01(tMs: number, startMs: number, durationMs: number): number {\n if (durationMs <= 0) return 0;\n const t = (tMs - startMs) / durationMs;\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n\n/** Common window props shared by every card wrapper. */\ninterface WindowProps {\n /** Absolute segment start (ms) — t01 is measured from here. Default 0. */\n startMs?: number;\n /** Segment length (ms) over which t01 sweeps 0..1. Default = scene's natural duration. */\n durationMs?: number;\n}\n\nfunction validateWindow(p: Record<string, unknown>, key: string): string[] {\n const errs: string[] = [];\n if (p.startMs != null && (typeof p.startMs !== 'number' || p.startMs < 0))\n errs.push(`${key}.startMs must be a number >= 0`);\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || !(p.durationMs > 0)))\n errs.push(`${key}.durationMs must be a positive number`);\n return errs;\n}\n\n// ─── hook ────────────────────────────────────────────────────────────────────\n\nexport interface HookProps extends WindowProps {\n /** Hook lines (wraps HookSceneOpts.lines). */\n lines: string[];\n /** Optional brand override (wraps HookSceneOpts.brand). */\n brand?: string;\n}\n\nfunction hookLayer(): Layer<HookProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'hook',\n init(ctx: RenderCtx, props) {\n const opts: HookSceneOpts = { lines: props.lines, brand: props.brand };\n scene = hookScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const hookFactory: LayerFactory<HookProps> = {\n key: 'hook',\n create: hookLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['hook: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.lines) || !p.lines.every((l) => typeof l === 'string'))\n errs.push('hook.lines must be an array of strings');\n if (p.brand != null && typeof p.brand !== 'string') errs.push('hook.brand must be a string');\n return [...errs, ...validateWindow(p, 'hook')];\n },\n};\n\n// ─── reveal ──────────────────────────────────────────────────────────────────\n\nexport interface RevealProps extends WindowProps {\n /** Title (wraps RevealSceneOpts.title). */\n title: string;\n /** Subtitle (wraps RevealSceneOpts.subtitle). */\n subtitle: string;\n /** Optional initials override (wraps RevealSceneOpts.initials). */\n initials?: string;\n /** Optional fun fact (wraps RevealSceneOpts.funFact). */\n funFact?: string;\n}\n\nfunction revealLayer(): Layer<RevealProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'reveal',\n init(ctx: RenderCtx, props) {\n const opts: RevealSceneOpts = {\n title: props.title,\n subtitle: props.subtitle,\n initials: props.initials,\n funFact: props.funFact,\n portrait: null, // the `portrait` layer carries the medallion image; reveal\n // here is the initials-fallback look. See portrait.ts.\n };\n scene = revealScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const revealFactory: LayerFactory<RevealProps> = {\n key: 'reveal',\n create: revealLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['reveal: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.title !== 'string') errs.push('reveal.title must be a string');\n if (typeof p.subtitle !== 'string') errs.push('reveal.subtitle must be a string');\n if (p.initials != null && typeof p.initials !== 'string') errs.push('reveal.initials must be a string');\n if (p.funFact != null && typeof p.funFact !== 'string') errs.push('reveal.funFact must be a string');\n return [...errs, ...validateWindow(p, 'reveal')];\n },\n};\n\n// ─── cta ─────────────────────────────────────────────────────────────────────\n\nexport interface CtaProps extends WindowProps {\n /** End-card lines (wraps CtaSceneOpts.lines): [headline, ...accent lines]. */\n lines: string[];\n}\n\nfunction ctaLayer(): Layer<CtaProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'cta',\n init(ctx: RenderCtx, props) {\n const opts: CtaSceneOpts = { lines: props.lines };\n scene = ctaScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const ctaFactory: LayerFactory<CtaProps> = {\n key: 'cta',\n create: ctaLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['cta: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.lines) || !p.lines.every((l) => typeof l === 'string'))\n errs.push('cta.lines must be an array of strings');\n return [...errs, ...validateWindow(p, 'cta')];\n },\n};\n\n// ─── portrait ────────────────────────────────────────────────────────────────\n//\n// The composer-portrait medallion + fun-fact. This is the SAME visual as\n// revealScene with a decoded `portrait` image, so we wrap revealScene exactly —\n// but the portrait layer owns the ASYNC image load (loadPortrait in init), which\n// the plain `reveal` wrapper can't because props are sync. With no/failed image\n// it falls back to revealScene's initials badge (loadPortrait resolves null →\n// identical to `reveal`).\n\nexport interface PortraitProps extends WindowProps {\n /** Composer name → title + initials badge fallback (wraps RevealSceneOpts.title). */\n title: string;\n /** Subtitle line (e.g. dates / era). */\n subtitle: string;\n /** Portrait image URL (Wikimedia etc.); loaded crossOrigin in init. */\n url?: string | null;\n /** Optional initials override. */\n initials?: string;\n /** Optional fun fact (corpus fun_fact). */\n funFact?: string;\n}\n\nfunction portraitLayer(): Layer<PortraitProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'portrait',\n async init(ctx: RenderCtx, props) {\n // ASYNC asset load — the reason this is its own layer. Fail-soft to null\n // (revealScene then draws the initials badge).\n const portrait = await loadPortrait(props.url ?? null);\n const opts: RevealSceneOpts = {\n title: props.title,\n subtitle: props.subtitle,\n initials: props.initials,\n funFact: props.funFact,\n portrait,\n };\n scene = revealScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const portraitFactory: LayerFactory<PortraitProps> = {\n key: 'portrait',\n create: portraitLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['portrait: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.title !== 'string') errs.push('portrait.title must be a string');\n if (typeof p.subtitle !== 'string') errs.push('portrait.subtitle must be a string');\n if (p.url != null && typeof p.url !== 'string') errs.push('portrait.url must be a string or null');\n if (p.initials != null && typeof p.initials !== 'string') errs.push('portrait.initials must be a string');\n if (p.funFact != null && typeof p.funFact !== 'string') errs.push('portrait.funFact must be a string');\n return [...errs, ...validateWindow(p, 'portrait')];\n },\n};\n","// `spectrum` layer (S4) — the audio-reactive mirrored frequency-bar visualizer,\n// reimplemented from whozart's drawSpectrum (whozart/src/lib/promo.ts) as a\n// Layer. web-core CANNOT depend on whozart, so the analyser does not live here:\n// the layer reads its levels through a host-provided input on RenderCtx (a\n// `levels` provider) or a `levelsFn` prop. With NEITHER it uses the SAME synthetic\n// taper-sine fallback whozart uses when no signal is present — which is also the\n// deterministic, pure-fn-of-t path the invariant + golden tests exercise.\n//\n// ── Input contract (how the layer gets its levels) ──────────────────────────\n// The host attaches a normalized-magnitude provider to RenderCtx:\n//\n// ctx.spectrum = {\n// // 0..1 magnitudes per band, OR raw byte-FFT (0..255) the layer log-bins.\n// levels?(tMs, bands): Float32Array | number[] | null\n// byteFreq?(tMs): Uint8Array | null // e.g. AnalyserNode.getByteFrequencyData\n// }\n//\n// In whozart the host wires byteFreq → AnalyserNode (the live master bus). In a\n// deterministic capture/test, the host omits it and the layer animates the\n// fallback. A `levelsFn` PROP overrides ctx (handy for previews / fixtures).\n//\n// The bar look (mirrored about a center line, wine→gold per magnitude, edge taper\n// on the fallback) is faithful to whozart; the colour lerp is inlined (web-core\n// has no lerpHex) and matches whozart's rgb() output.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport type { SafeBox } from '../../video';\n\nconst SPEC_BARS = 44;\n\n/** Host-provided level source attached to RenderCtx by the app/capture harness. */\nexport interface SpectrumInput {\n /** Normalized 0..1 magnitudes for `bands` bars at time tMs (preferred). */\n levels?(tMs: number, bands: number): Float32Array | number[] | null | undefined;\n /** Raw byte-FFT (0..255), log-binned by the layer (AnalyserNode shape). */\n byteFreq?(tMs: number): Uint8Array | null | undefined;\n}\n\n/** Augment RenderCtx with the optional spectrum input (declaration merging). */\ndeclare module '../layer' {\n interface RenderCtx {\n /** Optional audio-reactive level source for the spectrum layer (host-wired). */\n spectrum?: SpectrumInput;\n }\n}\n\nexport interface SpectrumProps {\n /** Number of bars. Default 44 (whozart SPEC_BARS). */\n bars?: number;\n /** Vertical center as a fraction of H. Default 0.46 (whozart). */\n centerFrac?: number;\n /** Max half-height as a fraction of H. Default 0.135 (whozart). */\n maxHeightFrac?: number;\n /** Bar fill at low magnitude (wine). Default theme.accent. */\n colorLow?: string;\n /** Bar fill at high magnitude (gold). Default theme.gold. */\n colorHigh?: string;\n /** Per-frame level provider (overrides ctx.spectrum). Pure fn of t for tests. */\n levelsFn?: (tMs: number, bands: number) => Float32Array | number[] | null;\n}\n\n// rgb-space lerp between two #rrggbb colours — matches whozart lerpHex output.\nfunction lerpHex(a: string, b: string, f: number): string {\n const pa = parseInt(a.slice(1), 16);\n const pb = parseInt(b.slice(1), 16);\n const r = Math.round(((pa >> 16) & 255) + (((pb >> 16) & 255) - ((pa >> 16) & 255)) * f);\n const g = Math.round(((pa >> 8) & 255) + (((pb >> 8) & 255) - ((pa >> 8) & 255)) * f);\n const bl = Math.round((pa & 255) + ((pb & 255) - (pa & 255)) * f);\n return `rgb(${r},${g},${bl})`;\n}\n\n/** Deterministic synthetic magnitudes (whozart's no-signal fallback). Pure fn of\n * (tSec, band) so tests + offline captures are reproducible. */\nfunction syntheticMagnitude(tSec: number, b: number, n: number): number {\n const phase = tSec * 5 + b * 0.45;\n let m = 0.22 + 0.16 * Math.sin(phase) + 0.10 * Math.sin(phase * 1.7 + 1.2);\n m *= 0.5 + 0.5 * Math.sin((b / (n - 1)) * Math.PI); // taper toward the edges\n return m;\n}\n\n/** Log-bin a byte-FFT into `n` normalized magnitudes (whozart's lo=2..hi=440 map). */\nfunction binByteFreq(freq: Uint8Array, n: number): number[] | null {\n let peak = 0;\n for (let i = 0; i < freq.length; i++) if (freq[i] > peak) peak = freq[i];\n if (peak <= 4) return null; // treat as silence → fall back\n const lo = 2, hi = 440;\n const out: number[] = [];\n for (let b = 0; b < n; b++) {\n const f0 = lo * Math.pow(hi / lo, b / n);\n const f1 = lo * Math.pow(hi / lo, (b + 1) / n);\n const i0 = Math.floor(f0);\n const i1 = Math.max(i0 + 1, Math.floor(f1));\n let sum = 0, c = 0;\n for (let i = i0; i < i1 && i < freq.length; i++) { sum += freq[i]; c++; }\n let m = c ? (sum / c) / 255 : 0;\n m = Math.pow(m, 0.78); // lift quieter bands (whozart)\n out.push(m);\n }\n return out;\n}\n\nfunction spectrumLayer(): Layer<SpectrumProps> {\n let n = SPEC_BARS;\n let centerFrac = 0.46;\n let maxHeightFrac = 0.135;\n let colorLow: string | undefined;\n let colorHigh: string | undefined;\n let levelsFn: SpectrumProps['levelsFn'];\n\n /** Resolve normalized 0..1 magnitudes for this frame, with fallback. */\n function magnitudesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = levelsFn?.(tMs, n);\n const src = fromProp ?? resolveFromCtx(ctx, tMs);\n if (src && src.length) {\n const out = new Array(n);\n for (let b = 0; b < n; b++) out[b] = clamp01(src[Math.min(src.length - 1, b)] ?? 0);\n return out;\n }\n // synthetic fallback\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, b) => clamp01(syntheticMagnitude(tSec, b, n)));\n }\n\n function resolveFromCtx(ctx: RenderCtx, tMs: number): number[] | null {\n const sp = ctx.spectrum;\n if (!sp) return null;\n const lv = sp.levels?.(tMs, n);\n if (lv && lv.length) return Array.from(lv as ArrayLike<number>);\n const bf = sp.byteFreq?.(tMs);\n if (bf && bf.length) return binByteFreq(bf, n);\n return null;\n }\n\n return {\n key: 'spectrum',\n init(_ctx: RenderCtx, props) {\n n = props.bars ?? SPEC_BARS;\n centerFrac = props.centerFrac ?? 0.46;\n maxHeightFrac = props.maxHeightFrac ?? 0.135;\n colorLow = props.colorLow;\n colorHigh = props.colorHigh;\n levelsFn = props.levelsFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const sb: SafeBox = ctx.safeBox;\n const cy = H * centerFrac;\n const left = Math.max(W * 0.10, sb.left);\n const right = sb.right;\n const span = right - left;\n const slot = span / n;\n const gap = slot * 0.34;\n const barW = slot - gap;\n const maxH = H * maxHeightFrac;\n const lo = colorLow ?? ctx.theme.accent;\n const hi = colorHigh ?? ctx.theme.gold;\n\n c.save();\n // Faint center baseline (whozart) so a quiet/paused moment still reads.\n c.strokeStyle = lerpHex(ctx.theme.paper, ctx.theme.sepia, 0.28);\n c.lineWidth = 2;\n c.beginPath(); c.moveTo(left, cy); c.lineTo(right, cy); c.stroke();\n\n const mags = magnitudesAt(ctx, tMs);\n const useRound = typeof c.roundRect === 'function';\n for (let b = 0; b < n; b++) {\n const m = mags[b];\n const half = Math.max(barW * 0.5, m * maxH);\n const x0 = left + b * slot + gap / 2;\n c.fillStyle = lerpHex(lo, hi, m);\n if (useRound) {\n const r = Math.min(barW / 2, half);\n c.beginPath(); c.roundRect(x0, cy - half, barW, half * 2, r); c.fill();\n } else {\n c.fillRect(x0, cy - half, barW, half * 2);\n }\n }\n c.restore();\n },\n };\n}\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\nexport const spectrumFactory: LayerFactory<SpectrumProps> = {\n key: 'spectrum',\n create: spectrumLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['spectrum: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.bars != null && (typeof p.bars !== 'number' || p.bars < 2)) errs.push('spectrum.bars must be a number >= 2');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1))\n errs.push('spectrum.centerFrac must be in [0,1]');\n if (p.maxHeightFrac != null && (typeof p.maxHeightFrac !== 'number' || p.maxHeightFrac <= 0))\n errs.push('spectrum.maxHeightFrac must be a positive number');\n if (p.colorLow != null && typeof p.colorLow !== 'string') errs.push('spectrum.colorLow must be a string');\n if (p.colorHigh != null && typeof p.colorHigh !== 'string') errs.push('spectrum.colorHigh must be a string');\n if (p.levelsFn != null && typeof p.levelsFn !== 'function') errs.push('spectrum.levelsFn must be a function');\n return errs;\n },\n};\n","// `branding` + `safe-guides` layers (S4).\n//\n// `branding` — a small persistent brand/logo wordmark anchored inside the safe\n// box (bottom-centred by default), the chrome RMT/RSR/whozart burn into every\n// clip. Plus an OPTIONAL safe-zone debug overlay when `safezone` is set, which\n// delegates to src/video.ts drawSafeGuides (the SAME overlay apps trigger with\n// ?safe=1). Screen-pinned (drawn in identity space by the runner).\n//\n// `safe-guides` — a thin alias that ALWAYS draws drawSafeGuides (debug-only\n// layer you drop into a spec while laying it out). Both reuse the existing\n// helper; neither re-implements the inset math.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { drawSafeGuides } from '../../video';\n\n// ─── branding ──────────────────────────────────────────────────────────────\n\nexport interface BrandingProps {\n /** Wordmark to draw. Default theme.brand. */\n logo?: string;\n /** Optional second line under the wordmark (e.g. a domain) — the brand-card\n * form used for an end-of-clip mark: wordmark + URL, nothing else. Omit\n * for the original single-line chrome mark. */\n sub?: string;\n /** Sub-line font size in px. Default size*0.5. */\n subSize?: number;\n /** Sub-line colour. Default color/theme.sepia at reduced alpha handled by\n * the caller's theme choice (no alpha applied here so callers stay in\n * control of exact tone). */\n subColor?: string;\n /** Gap between wordmark baseline and sub-line baseline, px. Default size*0.85. */\n gap?: number;\n /** Show the safe-zone debug overlay on top (apps' ?safe=1). Default false. */\n safezone?: boolean;\n /** Vertical anchor as a fraction of safeBox height from its TOP. Default 1\n * (bottom edge of the safe box). With `sub` set, this anchors the WORDMARK\n * line and the sub-line is drawn `gap` px below it. */\n yFrac?: number;\n /** Font size in px. Default 34. */\n size?: number;\n /** Text colour. Default theme.sepia. */\n color?: string;\n}\n\nfunction brandingLayer(): Layer<BrandingProps> {\n let logo: string | undefined;\n let sub: string | undefined;\n let subSize = 0;\n let subColor: string | undefined;\n let gap = 0;\n let safezone = false;\n let yFrac = 1;\n let size = 34;\n let color: string | undefined;\n return {\n key: 'branding',\n init(_ctx: RenderCtx, props) {\n logo = props.logo;\n sub = props.sub;\n safezone = props.safezone ?? false;\n yFrac = props.yFrac ?? 1;\n size = props.size ?? 34;\n subSize = props.subSize ?? size * 0.5;\n gap = props.gap ?? size * 0.85;\n color = props.color;\n subColor = props.subColor;\n },\n draw(ctx) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const text = logo ?? ctx.theme.brand;\n c.save();\n c.textAlign = 'center';\n c.font = `italic ${size}px ${ctx.theme.fontBody}`;\n c.fillStyle = color ?? ctx.theme.sepia;\n // Anchor inside the safe box; at yFrac=1 sit the baseline a little above the\n // bottom edge so descenders stay inside. With a sub-line, both lines are\n // shifted up together so the PAIR (not just the wordmark) stays inside.\n const pairShift = sub ? gap / 2 : 0;\n const y = sb.top + sb.h * yFrac - (yFrac >= 1 ? size * 0.3 : 0) - pairShift;\n c.fillText(text, sb.cx, y);\n if (sub) {\n c.font = `${subSize}px ${ctx.theme.fontBody}`;\n c.fillStyle = subColor ?? color ?? ctx.theme.sepia;\n c.fillText(sub, sb.cx, y + gap);\n }\n c.restore();\n if (safezone) drawSafeGuides(c);\n },\n };\n}\n\nexport const brandingFactory: LayerFactory<BrandingProps> = {\n key: 'branding',\n create: brandingLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['branding: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.logo != null && typeof p.logo !== 'string') errs.push('branding.logo must be a string');\n if (p.sub != null && typeof p.sub !== 'string') errs.push('branding.sub must be a string');\n if (p.subSize != null && (typeof p.subSize !== 'number' || p.subSize <= 0)) errs.push('branding.subSize must be a positive number');\n if (p.subColor != null && typeof p.subColor !== 'string') errs.push('branding.subColor must be a string');\n if (p.gap != null && typeof p.gap !== 'number') errs.push('branding.gap must be a number');\n if (p.safezone != null && typeof p.safezone !== 'boolean') errs.push('branding.safezone must be a boolean');\n if (p.yFrac != null && typeof p.yFrac !== 'number') errs.push('branding.yFrac must be a number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0)) errs.push('branding.size must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('branding.color must be a string');\n return errs;\n },\n};\n\n// ─── safe-guides ─────────────────────────────────────────────────────────────\n\nexport type SafeGuidesProps = Record<string, never>;\n\nfunction safeGuidesLayer(): Layer<SafeGuidesProps> {\n return {\n key: 'safe-guides',\n init() {},\n draw(ctx) {\n drawSafeGuides(ctx.ctx2d);\n },\n };\n}\n\nexport const safeGuidesFactory: LayerFactory<SafeGuidesProps> = {\n key: 'safe-guides',\n create: safeGuidesLayer,\n validateProps(props) {\n if (props != null && typeof props !== 'object') return ['safe-guides: props must be an object'];\n return [];\n },\n};\n","// Staff↔keyboard ray geometry (S5) — the pure endpoints of the animated connector\n// from a sounding note's position ON THE STAFF to its KEY on the keyboard. The\n// \"beginner-killer\": it makes the staff↔piano mapping literal.\n//\n// Two geometry sources, both already produced elsewhere (no new layout):\n// - staff end: the notation layer's per-frame follow LAYOUT (engravingStore),\n// swept to the sounding position with the SAME progress math the playhead uses\n// (measureColumnsFromLayout). We anchor at the playhead x and the active\n// measure's vertical centre — the engraving doesn't expose per-NOTE y cleanly\n// headless, so the ray reads \"this point on the staff\" (documented limitation).\n// - keyboard end: the keyboard layer's published KeyboardLayout (keyboardStore)\n// via keyCenterX — exactly the key the keyboard lights.\n//\n// Pure (no canvas) ⇒ unit-testable; the layer just strokes the returned points.\n\nimport { measureColumnsFromLayout, type NotationLayout } from './notationGeometry';\nimport { keyCenterX, type KeyboardLayout } from './keyboardGeometry';\n\nexport interface RayEndpoints {\n /** Staff-end point (screen px). */\n staff: { x: number; y: number };\n /** Keyboard-end point (screen px) = the top-centre of the key. */\n keyboard: { x: number; y: number };\n}\n\n/**\n * The staff-end anchor for an audio progress `t01` (0..1 over the clip), using the\n * playhead sweep across the notation's mapped measure columns. Mirrors\n * notationGeometry.playheadLine's x/y derivation so the ray starts exactly where\n * the playhead is. Returns null when the layout has no measures/systems.\n */\nexport function staffAnchor(layout: NotationLayout, t01: number): { x: number; y: number } | null {\n const tt = Math.max(0, Math.min(1, t01));\n const cols = measureColumnsFromLayout(layout.measures);\n if (cols.length) {\n const pos = tt * cols.length;\n const i = Math.min(cols.length - 1, Math.floor(pos));\n const m = cols[i];\n const startX = Math.min(m.noteStartX, m.x + m.w);\n const x = startX + (pos - i) * (m.x + m.w - startX);\n return { x, y: m.y + m.h / 2 };\n }\n if (layout.systems.length) {\n const pos = tt * layout.systems.length;\n const row = Math.min(layout.systems.length - 1, Math.floor(pos));\n const s = layout.systems[row];\n return { x: s.x + (pos - row) * s.w, y: s.y + s.h / 2 };\n }\n const r = layout.rect;\n return { x: r.dx + tt * r.dw, y: r.dy + r.dh / 2 };\n}\n\n/**\n * Full ray endpoints for a note's pitch + an audio progress, given both layouts.\n * The keyboard end is the top-centre of the note's key. Pure.\n */\nexport function rayEndpoints(\n notation: NotationLayout,\n keyboard: KeyboardLayout,\n pitchMidi: number,\n t01: number,\n): RayEndpoints | null {\n const staff = staffAnchor(notation, t01);\n if (!staff) return null;\n return {\n staff,\n keyboard: { x: keyCenterX(keyboard, pitchMidi), y: keyboard.top },\n };\n}\n\n/**\n * A point along a quadratic Bézier from staff→keyboard, bulged toward the side, at\n * parameter `u` 0..1. Used to draw a gently curved ray (a straight line reads as a\n * cursor; a curve reads as a connector). Pure.\n */\nexport function rayPointAt(ends: RayEndpoints, u: number, bulge = 0.18): { x: number; y: number } {\n const { staff: a, keyboard: b } = ends;\n // Control point: midpoint pushed horizontally by `bulge` of the vertical span.\n const mx = (a.x + b.x) / 2;\n const my = (a.y + b.y) / 2;\n const span = Math.abs(b.y - a.y);\n const cx = mx + bulge * span;\n const cy = my;\n const uu = u < 0 ? 0 : u > 1 ? 1 : u;\n const inv = 1 - uu;\n return {\n x: inv * inv * a.x + 2 * inv * uu * cx + uu * uu * b.x,\n y: inv * inv * a.y + 2 * inv * uu * cy + uu * uu * b.y,\n };\n}\n","// Highlight-region primitive (S1b) — one reusable helper to spotlight a region\n// for a time range. Used by harmony/theory/ear-training/\"play this part\" layers.\n//\n// A region is an x-range band (world coords) shown only while tMs is inside\n// [inMs, outMs], with a short fade so it doesn't pop. The intensity envelope is\n// pure math (testable without a canvas); the draw helper is a thin band/dim\n// renderer over it.\n\nimport { clamp, invLerp } from './math';\n\nexport interface HighlightRegion {\n /** World x of the band's left edge. */\n x: number;\n /** World y of the band's top edge. */\n y: number;\n /** Band width (world px). */\n w: number;\n /** Band height (world px). */\n h: number;\n /** Show from this time (ms). */\n inMs: number;\n /** Hide after this time (ms). */\n outMs: number;\n /** Fade in/out duration (ms). Default 120. */\n fadeMs?: number;\n /** Fill colour (defaults to a translucent accent at draw time). */\n color?: string;\n}\n\n/**\n * Highlight intensity 0..1 at time `tMs`: 0 outside the window, ramping to 1\n * across `fadeMs` at each edge, flat 1 in the middle. Pure.\n */\nexport function highlightIntensity(region: HighlightRegion, tMs: number): number {\n const fade = region.fadeMs ?? 120;\n if (tMs <= region.inMs - fade || tMs >= region.outMs + fade) return 0;\n const rampIn = invLerp(region.inMs - fade, region.inMs, tMs);\n const rampOut = 1 - invLerp(region.outMs, region.outMs + fade, tMs);\n return clamp(Math.min(rampIn, rampOut), 0, 1);\n}\n\n/**\n * Compute the world x-range covering a set of note onsets that fall inside a\n * time window, given a time→x mapping. Returns null if none qualify. Useful for\n * \"spotlight the bar that's sounding now\" without a layer re-deriving geometry.\n */\nexport function noteSetXRange(\n onsetsMs: number[],\n windowMs: [number, number],\n timeToX: (ms: number) => number,\n): { x: number; w: number } | null {\n let lo = Infinity;\n let hi = -Infinity;\n for (const on of onsetsMs) {\n if (on < windowMs[0] || on > windowMs[1]) continue;\n const x = timeToX(on);\n if (x < lo) lo = x;\n if (x > hi) hi = x;\n }\n if (lo === Infinity) return null;\n return { x: lo, w: Math.max(0, hi - lo) };\n}\n\n/**\n * Draw a highlight band for the current time. No-op when intensity is 0, so it\n * is safe to call every frame. Draws in WORLD coords (the camera transform, if\n * any, is already on the context).\n */\nexport function drawHighlight(\n ctx: CanvasRenderingContext2D,\n region: HighlightRegion,\n tMs: number,\n accent: string,\n): void {\n const a = highlightIntensity(region, tMs);\n if (a <= 0) return;\n ctx.save();\n ctx.globalAlpha = a * 0.35;\n ctx.fillStyle = region.color ?? accent;\n ctx.fillRect(region.x, region.y, region.w, region.h);\n ctx.restore();\n}\n","// `staff-keyboard-ray` layer (S5) — an animated connector from a sounding note's\n// position on the STAFF to its KEY on the keyboard, the top beginner-killer\n// feature (makes \"this dot = this key\" literal). Depends on BOTH a notation layer\n// (engravingStore → per-frame staff geometry + playhead position) AND a keyboard\n// layer (keyboardStore → key x's); if either is absent the layer draws nothing.\n//\n// Sync (pure fn of t): the staff end rides the playhead position (same sweep as\n// notationGeometry.playheadLine); a ray is drawn for each note SOUNDING now, from\n// the staff anchor down a gentle curve to the note's key top, with a draw-on\n// progress (the ray \"grows\" toward the key over a short reveal) + fade.\n//\n// Props pick WHICH notation/keyboard layers to link (by their layer keys) — v1\n// reads the single published notation/keyboard layouts from the per-render stores,\n// so the keys are informational/forward-compatible (documented).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getNotationEngraving } from '../engravingStore';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { inRange } from '../keyboardGeometry';\nimport { rayEndpoints, rayPointAt } from '../staffKeyboardRay';\nimport { highlightIntensity } from '../highlight';\n\nexport interface StaffKeyboardRayProps {\n /** Layer key of the notation layer to link (forward-compat; v1 uses the single\n * published engraving). Default \"notation\". */\n notationKey?: string;\n /** Layer key of the keyboard layer to link. Default \"keyboard\". */\n keyboardKey?: string;\n /** Ray reveal/grow + fade duration, ms. Default 180. */\n fadeMs?: number;\n /** Line width, world px. Default 4. */\n width?: number;\n /** Ray colour (default per-note hand colour via the theme accent). */\n color?: string;\n /** Number of segments to approximate the curve. Default 24. */\n segments?: number;\n}\n\nconst DEFAULT_FADE = 180;\nconst DEFAULT_WIDTH = 4;\nconst DEFAULT_SEGMENTS = 24;\n\nfunction staffKeyboardRayLayer(): Layer<StaffKeyboardRayProps> {\n let fadeMs = DEFAULT_FADE;\n let width = DEFAULT_WIDTH;\n let color: string | undefined;\n let segments = DEFAULT_SEGMENTS;\n\n return {\n key: 'staff-keyboard-ray',\n init(_ctx: RenderCtx, props) {\n fadeMs = props.fadeMs ?? DEFAULT_FADE;\n width = props.width ?? DEFAULT_WIDTH;\n color = props.color;\n segments = props.segments ?? DEFAULT_SEGMENTS;\n },\n\n draw(ctx, tMs) {\n const eng = getNotationEngraving(ctx);\n const kbd = getKeyboardLayout(ctx);\n if (!eng || !kbd) return; // needs both a notation and a keyboard layer\n\n // The notation layout this frame (follow window when a scroll-cursor is\n // present, else the base) — the SAME bitmap the notation layer blits.\n const layout = eng.followLayoutAt ? eng.followLayoutAt(ctx, tMs) : eng.base;\n const dur = ctx.score?.durationMs ?? 0;\n const t01 = dur > 0 ? tMs / dur : 0;\n\n const c = ctx.ctx2d;\n const accent = color ?? ctx.theme.accent;\n\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(kbd, n.pitchMidi)) continue;\n\n const ends = rayEndpoints(layout, kbd, n.pitchMidi, t01);\n if (!ends) continue;\n // NaN / off-canvas guard (gate placement-sanity in-layer).\n if (!Number.isFinite(ends.staff.x) || !Number.isFinite(ends.keyboard.x)) continue;\n\n // Grow-on: the ray extends from staff toward the key over `fadeMs` after\n // onset, then holds; fade out near the note's end. Reuse the highlight\n // envelope for the alpha, and a separate grow fraction for the length.\n const alpha = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: n.onsetMs, outMs: n.onsetMs + n.durMs, fadeMs },\n tMs,\n );\n if (alpha <= 0) continue;\n const grow = Math.min(1, fadeMs > 0 ? (tMs - n.onsetMs) / fadeMs : 1);\n\n c.save();\n c.globalAlpha = alpha;\n c.strokeStyle = accent;\n c.lineWidth = width;\n c.beginPath();\n const steps = Math.max(2, segments);\n for (let i = 0; i <= steps; i++) {\n const u = (i / steps) * Math.max(0, grow);\n const p = rayPointAt(ends, u);\n if (i === 0) c.moveTo(p.x, p.y);\n else c.lineTo(p.x, p.y);\n }\n c.stroke();\n // A dot at each end to read as anchored.\n const head = rayPointAt(ends, Math.max(0, grow));\n c.fillStyle = accent;\n c.beginPath();\n c.arc(ends.staff.x, ends.staff.y, width * 1.1, 0, Math.PI * 2);\n c.fill();\n c.beginPath();\n c.arc(head.x, head.y, width * 1.1, 0, Math.PI * 2);\n c.fill();\n c.restore();\n }\n },\n };\n}\n\nexport const staffKeyboardRayFactory: LayerFactory<StaffKeyboardRayProps> = {\n key: 'staff-keyboard-ray',\n create: staffKeyboardRayLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['staff-keyboard-ray: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.notationKey != null && typeof p.notationKey !== 'string')\n errs.push('staff-keyboard-ray.notationKey must be a string');\n if (p.keyboardKey != null && typeof p.keyboardKey !== 'string')\n errs.push('staff-keyboard-ray.keyboardKey must be a string');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('staff-keyboard-ray.fadeMs must be a non-negative number');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0))\n errs.push('staff-keyboard-ray.width must be a positive number');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('staff-keyboard-ray.color must be a string');\n if (p.segments != null && (typeof p.segments !== 'number' || p.segments < 2))\n errs.push('staff-keyboard-ray.segments must be a number ≥ 2');\n return errs;\n },\n};\n","// Counting-grid geometry (S5) — the pure, canvas-free beat/subdivision schedule\n// for the `counting-track` layer (\"1 e & a\"). Derives beat onsets from the\n// Score's tempoMap (v1: single constant bpm) + time signature, and the bouncing-\n// ball vertical position from the phase within the current beat.\n//\n// A \"beat\" is the time-signature denominator's note value (so 6/8 counts in\n// eighths → six beats/bar). Subdivisions split each beat evenly; the canonical\n// syllables are number-on-the-beat then \"e & a\" for 16ths or \"&\" for 8ths.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\nimport type { TempoMap } from './score';\n\nexport interface BeatGridOpts {\n /** Constant bpm of the QUARTER-note beat (tempoMap.segments[0].bpm). */\n bpm: number;\n /** Beats per bar (time-sig numerator). Default 4. */\n beatsPerBar?: number;\n /** Time-sig denominator (note value of one beat). Default 4. */\n beatUnit?: number;\n /** Subdivisions per beat for the syllable row: 1, 2 (\"&\"), or 4 (\"e & a\"). Default 1. */\n subdiv?: 1 | 2 | 4;\n /** Total length to cover, ms. */\n durationMs: number;\n /** Count-in offset: first beat lands at this ms (default 0). */\n startMs?: number;\n}\n\nexport interface BeatTick {\n /** Onset time, ms. */\n tMs: number;\n /** 0-based beat index across the whole clip. */\n index: number;\n /** 1-based beat number within the bar (the big counted number). */\n beatInBar: number;\n /** Subdivision index within the beat (0 = on the beat). */\n sub: number;\n /** The counting syllable (\"1\",\"e\",\"&\",\"a\", \"2\", …). */\n syllable: string;\n /** True on the downbeat (beatInBar === 1, sub === 0). */\n downbeat: boolean;\n}\n\nconst SUB4 = ['', 'e', '&', 'a'];\n\n/** ms between quarter beats; the beat-unit scales it (eighth beat = half a quarter). */\nexport function msPerBeat(bpm: number, beatUnit: number): number {\n // bpm is the quarter-note tempo; one beat-unit note = 4/beatUnit quarters.\n return (60000 / bpm) * (4 / beatUnit);\n}\n\n/** Build the full beat+subdivision schedule across the clip. Pure. */\nexport function beatGrid(opts: BeatGridOpts): BeatTick[] {\n const beatsPerBar = opts.beatsPerBar ?? 4;\n const beatUnit = opts.beatUnit ?? 4;\n const subdiv = opts.subdiv ?? 1;\n const start = opts.startMs ?? 0;\n const mpb = msPerBeat(opts.bpm, beatUnit);\n const subMs = mpb / subdiv;\n const ticks: BeatTick[] = [];\n let beatIndex = 0;\n // A beat is included when its ONSET is within [start, start+durationMs]; once a\n // beat is in, ALL its subdivisions belong to it (so durationMs:0 → one full beat\n // of subdivisions). This may emit subdivisions slightly past durationMs on the\n // final beat — intended (a beat is an atomic unit of counting).\n for (let t = start; t <= start + opts.durationMs + 1e-6; t += mpb) {\n const beatInBar = (beatIndex % beatsPerBar) + 1;\n for (let s = 0; s < subdiv; s++) {\n const tMs = t + s * subMs;\n ticks.push({\n tMs,\n index: beatIndex,\n beatInBar,\n sub: s,\n syllable: s === 0 ? String(beatInBar) : SUB4[(s * (4 / subdiv)) | 0] || '·',\n downbeat: beatInBar === 1 && s === 0,\n });\n }\n beatIndex++;\n }\n return ticks;\n}\n\n/** The TempoMap's first-segment bpm (v1 single-tempo helper). */\nexport function bpmOf(tempoMap: TempoMap | undefined, fallback = 100): number {\n return tempoMap?.segments?.[0]?.bpm ?? fallback;\n}\n\n/**\n * Phase 0..1 within the current beat at time `tMs` (0 = on the beat). Pure.\n * Used by the bouncing ball's vertical position.\n */\nexport function beatPhase(bpm: number, beatUnit: number, tMs: number, startMs = 0): number {\n const mpb = msPerBeat(bpm, beatUnit);\n const rel = tMs - startMs;\n if (rel < 0) return 0;\n const p = (rel % mpb) / mpb;\n return p < 0 ? p + 1 : p;\n}\n\n/**\n * The bouncing ball's vertical offset 0..1 (0 = at the track line / floor, 1 =\n * top of the arc). A parabola that touches 0 exactly ON each beat and peaks at\n * mid-beat — so the ball \"lands\" on the count. Pure.\n */\nexport function ballArc(phase01: number): number {\n // 4·p·(1−p) is a parabola: 0 at p=0 and p=1, 1 at p=0.5.\n const p = phase01 < 0 ? 0 : phase01 > 1 ? 1 : phase01;\n return 4 * p * (1 - p);\n}\n\n/** Horizontal x (world) of the ball at time t, sweeping left→right across the\n * counting track in step with the beats. Pure. */\nexport function ballX(\n ticks: BeatTick[],\n tMs: number,\n xOf: (tick: BeatTick) => number,\n): number {\n if (!ticks.length) return 0;\n // Find the bracketing on-beat ticks (sub===0) and lerp the x between them.\n const beats = ticks.filter((t) => t.sub === 0);\n if (tMs <= beats[0].tMs) return xOf(beats[0]);\n for (let i = 0; i < beats.length - 1; i++) {\n const a = beats[i];\n const b = beats[i + 1];\n if (tMs >= a.tMs && tMs < b.tMs) {\n const f = (tMs - a.tMs) / (b.tMs - a.tMs);\n return xOf(a) + (xOf(b) - xOf(a)) * f;\n }\n }\n return xOf(beats[beats.length - 1]);\n}\n\n/** Parse a \"n/d\" time signature string into [numerator, denominator]. */\nexport function parseTimeSig(ts: string | undefined): [number, number] {\n if (!ts) return [4, 4];\n const m = /^(\\d+)\\s*\\/\\s*(\\d+)$/.exec(ts.trim());\n if (!m) return [4, 4];\n return [parseInt(m[1], 10), parseInt(m[2], 10)];\n}\n","// `counting-track` layer (S5) — a beat/subdivision counting strip (\"1 e & a\")\n// with a ball that bounces ON each beat. Rhythm pedagogy: shows the metric grid\n// the Score is played against, derived from the Score's tempoMap + time signature\n// (no extra data needed). Pure fn of t.\n//\n// Layout: a horizontal track pinned near the bottom of the safe box. The counting\n// syllables are laid out evenly across the safe width as a fixed legend (a full\n// bar of counts); the ball sweeps left→right across that bar in step with the\n// beats, dropping to the track line exactly on each count (ballArc parabola) so\n// the bounce reads as \"land on the beat\".\n//\n// Sync: every position is a pure function of t via beatGrid/beatPhase/ballArc —\n// audio-clock-driven, no wall clock.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n beatPhase,\n ballArc,\n bpmOf,\n parseTimeSig,\n type BeatTick,\n} from '../countingGrid';\n\nexport interface CountingTrackProps {\n /** Subdivisions per beat shown: 1 (just numbers), 2 (\"&\"), 4 (\"e & a\"). Default 1. */\n subdiv?: 1 | 2 | 4;\n /** Override time signature (else Score.timeSig, else 4/4). */\n timeSig?: string;\n /** Override bpm (else Score.tempoMap, else 100). */\n bpm?: number;\n /** Track baseline (world y) the ball lands on. Default safeBox.bottom - 120. */\n trackY?: number;\n /** Bounce height in world px. Default 110. */\n bounce?: number;\n /** Syllable font size, world px. Default 34. */\n size?: number;\n /** Ball radius, world px. Default 22. */\n ballRadius?: number;\n /** Count-in offset, ms (first beat lands here). Default 0. */\n startMs?: number;\n}\n\nconst DEFAULT_BOUNCE = 110;\nconst DEFAULT_SIZE = 34;\nconst DEFAULT_BALL = 22;\n\nfunction countingTrackLayer(): Layer<CountingTrackProps> {\n let subdiv: 1 | 2 | 4 = 1;\n let bpm = 100;\n let beatUnit = 4;\n let beatsPerBar = 4;\n let trackYProp: number | undefined;\n let bounce = DEFAULT_BOUNCE;\n let size = DEFAULT_SIZE;\n let ballRadius = DEFAULT_BALL;\n let startMs = 0;\n // The legend = ONE bar of counts laid out across the safe width.\n let legend: BeatTick[] = [];\n let left = 0;\n let width = 0;\n\n function xOfTick(t: BeatTick): number {\n // Index within the bar (beat + subdivision), evenly across the safe width.\n const idx = (t.beatInBar - 1) * subdiv + t.sub;\n const slots = beatsPerBar * subdiv;\n return left + ((idx + 0.5) / slots) * width;\n }\n\n return {\n key: 'counting-track',\n init(ctx, props) {\n subdiv = props.subdiv ?? 1;\n const [num, den] = parseTimeSig(props.timeSig ?? ctx.score?.timeSig);\n beatsPerBar = num;\n beatUnit = den;\n bpm = props.bpm ?? bpmOf(ctx.score?.tempoMap);\n trackYProp = props.trackY;\n bounce = props.bounce ?? DEFAULT_BOUNCE;\n size = props.size ?? DEFAULT_SIZE;\n ballRadius = props.ballRadius ?? DEFAULT_BALL;\n startMs = props.startMs ?? 0;\n\n const sb = ctx.safeBox;\n left = sb.left;\n width = sb.w;\n // The fixed legend: exactly one bar of ticks (downbeat..end of bar). We\n // build it directly rather than via beatGrid because the legend is metric\n // positions in a bar (timeless), not absolute onsets across the clip.\n legend = [];\n for (let b = 0; b < beatsPerBar; b++) {\n for (let s = 0; s < subdiv; s++) {\n legend.push({\n tMs: 0, index: b, beatInBar: b + 1, sub: s,\n syllable: s === 0 ? String(b + 1) : ['', 'e', '&', 'a'][(s * (4 / subdiv)) | 0] || '·',\n downbeat: b === 0 && s === 0,\n });\n }\n }\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const trackY = trackYProp ?? sb.bottom - 120;\n\n c.save();\n // Track baseline.\n c.strokeStyle = ctx.theme.sepia;\n c.lineWidth = 2;\n c.globalAlpha = 0.5;\n c.beginPath();\n c.moveTo(left, trackY);\n c.lineTo(left + width, trackY);\n c.stroke();\n c.globalAlpha = 1;\n\n // Which legend slot is active now (so we can emphasise the current count).\n const phase = beatPhase(bpm, beatUnit, tMs, startMs);\n const mpb = (60000 / bpm) * (4 / beatUnit);\n const rel = Math.max(0, tMs - startMs);\n const beatIdx = Math.floor(rel / mpb);\n const activeBeatInBar = (beatIdx % beatsPerBar);\n const activeSub = Math.floor((phase * subdiv)) % subdiv;\n const activeSlot = activeBeatInBar * subdiv + activeSub;\n\n // Syllable legend.\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n for (const t of legend) {\n const x = xOfTick(t);\n const slot = (t.beatInBar - 1) * subdiv + t.sub;\n const active = slot === activeSlot;\n c.font = `${t.sub === 0 ? '700' : '400'} ${t.sub === 0 ? size : size * 0.8}px ${ctx.theme.fontBody}`;\n c.fillStyle = active ? ctx.theme.accent : ctx.theme.ink;\n c.globalAlpha = active ? 1 : t.sub === 0 ? 0.85 : 0.55;\n c.fillText(t.syllable, x, trackY + size * 0.95);\n // tick mark on the baseline\n c.globalAlpha = active ? 0.9 : 0.4;\n c.fillRect(x - 1, trackY - (t.sub === 0 ? 10 : 6), 2, t.sub === 0 ? 10 : 6);\n }\n c.globalAlpha = 1;\n\n // Bouncing ball: x sweeps across the bar in step with the beat; y bounces.\n // x: map the within-bar beat position (continuous) to a legend x.\n const beatPosInBar = (beatIdx % beatsPerBar) + phase; // 0..beatsPerBar\n const ballSlot = beatPosInBar * subdiv; // continuous slot\n const slots = beatsPerBar * subdiv;\n const bx = left + (Math.min(slots, ballSlot + 0.5) / slots) * width;\n const arc = ballArc(phase); // 0 on the beat, 1 mid-beat\n const by = trackY - ballRadius - arc * bounce;\n\n c.fillStyle = ctx.theme.accent;\n c.beginPath();\n c.arc(bx, by, ballRadius, 0, Math.PI * 2);\n c.fill();\n // a soft shadow ellipse on the track to anchor the bounce\n c.globalAlpha = 0.2 * (1 - arc);\n c.fillStyle = ctx.theme.ink;\n c.beginPath();\n c.arc(bx, trackY, ballRadius * 0.8, 0, Math.PI * 2);\n c.fill();\n c.restore();\n },\n };\n}\n\nexport const countingTrackFactory: LayerFactory<CountingTrackProps> = {\n key: 'counting-track',\n create: countingTrackLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['counting-track: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.subdiv != null && p.subdiv !== 1 && p.subdiv !== 2 && p.subdiv !== 4)\n errs.push('counting-track.subdiv must be 1 | 2 | 4');\n if (p.timeSig != null && (typeof p.timeSig !== 'string' || !/^\\d+\\s*\\/\\s*\\d+$/.test(p.timeSig)))\n errs.push('counting-track.timeSig must be \"n/d\"');\n if (p.bpm != null && (typeof p.bpm !== 'number' || p.bpm <= 0))\n errs.push('counting-track.bpm must be a positive number');\n if (p.trackY != null && typeof p.trackY !== 'number')\n errs.push('counting-track.trackY must be a number');\n if (p.bounce != null && (typeof p.bounce !== 'number' || p.bounce < 0))\n errs.push('counting-track.bounce must be a non-negative number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0))\n errs.push('counting-track.size must be a positive number');\n if (p.ballRadius != null && (typeof p.ballRadius !== 'number' || p.ballRadius <= 0))\n errs.push('counting-track.ballRadius must be a positive number');\n if (p.startMs != null && typeof p.startMs !== 'number')\n errs.push('counting-track.startMs must be a number');\n return errs;\n },\n};\n","// Scale-degree / solfège label geometry (S5) — the pure, canvas-free mapping from\n// a Score note (step+alter+pitch) and the Score's key to the text that labels it:\n// a scale-degree number (\"1\"..\"7\", chromatic degrees flagged) or movable-do\n// solfège (\"do re mi …\", chromatic syllables \"di ra …\").\n//\n// Correctness principle (spec S5): use the note's NOTATED spelling (step+alter),\n// NOT a pitch-class guess, so A♭ in an E♭-major piece is the 4th degree / \"fa\",\n// never spelled as G♯. The diatonic degree is the letter distance from the tonic\n// letter; the chromatic inflection is whether the note's pitch class is sharpened\n// or flattened relative to the diatonic scale-degree pitch in that key.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\nconst LETTERS = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];\nconst LETTER_PC: Record<string, number> = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };\n\n/** Major-scale semitone offsets for diatonic degrees 1..7 (index 0..6). */\nconst MAJOR_OFFSETS = [0, 2, 4, 5, 7, 9, 11];\n\n/** Movable-do solfège for the seven diatonic degrees (do based on the tonic). */\nconst DIATONIC_SOLFEGE = ['do', 're', 'mi', 'fa', 'sol', 'la', 'ti'];\n/** Raised (sharp) chromatic syllables, keyed by the diatonic degree being raised. */\nconst RAISED_SOLFEGE: Record<number, string> = { 1: 'di', 2: 'ri', 4: 'fi', 5: 'si', 6: 'li' };\n/** Lowered (flat) chromatic syllables, keyed by the diatonic degree being lowered. */\nconst LOWERED_SOLFEGE: Record<number, string> = { 2: 'ra', 3: 'me', 5: 'se', 6: 'le', 7: 'te' };\n\nexport type LabelMode = 'degree' | 'solfege';\n\nexport interface DegreeLabel {\n /** Diatonic degree 1..7 (the letter-distance from the tonic letter). */\n degree: number;\n /** Chromatic inflection in semitones vs the diatonic degree (−1 flat, +1 sharp, 0 natural). */\n alter: number;\n /** The display text for the chosen mode. */\n text: string;\n}\n\n/** Parse a key string (\"Eb major\" / \"C# minor\" / \"F\") into a tonic letter + mode. */\nexport function parseKey(key: string | undefined): { tonicLetter: string; tonicPc: number; minor: boolean } {\n if (!key) return { tonicLetter: 'C', tonicPc: 0, minor: false };\n const m = /^([A-Ga-g])([#b]*)\\s*(major|minor|maj|min|m)?/.exec(key.trim());\n if (!m) return { tonicLetter: 'C', tonicPc: 0, minor: false };\n const letter = m[1].toUpperCase();\n let pc = LETTER_PC[letter] ?? 0;\n for (const ch of m[2]) pc += ch === '#' ? 1 : -1;\n const modeTok = (m[3] ?? '').toLowerCase();\n const minor = modeTok === 'minor' || modeTok === 'min' || modeTok === 'm';\n return { tonicLetter: letter, tonicPc: ((pc % 12) + 12) % 12, minor };\n}\n\n/**\n * Compute the scale-degree label for a note, from its NOTATED spelling.\n *\n * `degree` = the letter distance (1..7) from the tonic letter to the note's\n * letter, which is the enharmonically-correct diatonic degree. `alter` = how the\n * note's actual pitch class differs from that degree's pitch in the key\n * (so D♯ vs D♭ in C give the same degree 2 but +1 / −1). For MINOR keys the\n * reference scale is natural minor (so the lowered 3/6/7 read as natural degrees,\n * matching how movable-do \"la-based minor\" is taught only in MAJOR; here we use\n * do-based, i.e. degrees relative to the major scale of the tonic — see note).\n *\n * Pure.\n */\nexport function degreeLabel(\n step: string,\n alter: number,\n pitchMidi: number,\n key: string | undefined,\n mode: LabelMode,\n): DegreeLabel {\n const { tonicLetter } = parseKey(key);\n const noteLetterIdx = LETTERS.indexOf(step.toUpperCase());\n const tonicLetterIdx = LETTERS.indexOf(tonicLetter);\n // Diatonic degree = letter distance (mod 7), 1-based.\n const degIdx = noteLetterIdx < 0 || tonicLetterIdx < 0\n ? 0\n : ((noteLetterIdx - tonicLetterIdx) % 7 + 7) % 7;\n const degree = degIdx + 1;\n\n // The pitch class this degree would have in the MAJOR scale of the tonic.\n const { tonicPc } = parseKey(key);\n const diatonicPc = (tonicPc + MAJOR_OFFSETS[degIdx]) % 12;\n // Use the NOTATED spelling (letter + alter) for the actual pc — not a pitch\n // guess — so the chromatic inflection is enharmonically correct. We sanity-check\n // against pitchMidi and fall back to it if step is unrecognised.\n const letterPc = LETTER_PC[step.toUpperCase()];\n const actualPc = letterPc != null\n ? ((letterPc + alter) % 12 + 12) % 12\n : ((pitchMidi % 12) + 12) % 12;\n // Signed chromatic inflection in [-2..+2]; prefer the small-magnitude wrap.\n let chrom = actualPc - diatonicPc;\n if (chrom > 6) chrom -= 12;\n if (chrom < -6) chrom += 12;\n\n const text = mode === 'solfege'\n ? solfegeText(degree, chrom)\n : degreeText(degree, chrom);\n return { degree, alter: chrom, text };\n}\n\nfunction degreeText(degree: number, chrom: number): string {\n const acc = chrom > 0 ? '♯'.repeat(chrom) : chrom < 0 ? '♭'.repeat(-chrom) : '';\n return `${acc}${degree}`;\n}\n\nfunction solfegeText(degree: number, chrom: number): string {\n if (chrom === 0) return DIATONIC_SOLFEGE[degree - 1] ?? '?';\n if (chrom > 0) return RAISED_SOLFEGE[degree] ?? `${DIATONIC_SOLFEGE[degree - 1]}♯`;\n return LOWERED_SOLFEGE[degree] ?? `${DIATONIC_SOLFEGE[degree - 1]}♭`;\n}\n","// `degree-labels` layer (S5) — scale-degree numbers or movable-do solfège drawn\n// on the keyboard keys as their notes SOUND. Pedagogy layer: reads the Score's\n// key + each note's NOTATED step/alter (enharmonically correct — A♭ in E♭ major\n// reads as \"4\"/\"fa\", never \"♯3\"), so it teaches function, not pitch-class trivia.\n//\n// Geometry source: the keyboard layer's published KeyboardLayout (keyboardStore),\n// so a label sits exactly over the key the `keyboard` layer lights — by\n// construction, no second layout. Requires a `keyboard` layer in the scene; if\n// none has published a layout this frame the layer draws nothing (documented).\n//\n// Sync: a label shows while its note sounds (onsetMs ≤ t < onsetMs+durMs) with a\n// short fade, riding the highlight-region intensity envelope so it doesn't pop.\n// Pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { keyCenterX, inRange, isBlackKey, type KeyboardLayout } from '../keyboardGeometry';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { degreeLabel, type LabelMode } from '../degreeLabels';\nimport { highlightIntensity } from '../highlight';\n\nexport interface DegreeLabelsProps {\n /** \"degree\" = scale-degree numbers (1..7, ♯/♭ chromatics); \"solfege\" =\n * movable-do syllables (do re mi …, di ra …). Default \"degree\". */\n mode?: LabelMode;\n /** Override the Score's key (e.g. for an excerpt in a different key). */\n key?: string;\n /** Label font size in world px. Default 30. */\n size?: number;\n /** Fade in/out of a label, ms. Default 120. */\n fadeMs?: number;\n /** Text colour. Default theme.ink on a translucent chip. */\n color?: string;\n}\n\nconst DEFAULT_SIZE = 30;\n\nfunction degreeLabelsLayer(): Layer<DegreeLabelsProps> {\n let mode: LabelMode = 'degree';\n let keyOverride: string | undefined;\n let size = DEFAULT_SIZE;\n let fadeMs = 120;\n let color: string | undefined;\n\n return {\n key: 'degree-labels',\n init(_ctx: RenderCtx, props) {\n mode = props.mode ?? 'degree';\n keyOverride = props.key;\n size = props.size ?? DEFAULT_SIZE;\n fadeMs = props.fadeMs ?? 120;\n color = props.color;\n },\n\n draw(ctx, tMs) {\n const layout: KeyboardLayout | undefined = getKeyboardLayout(ctx);\n if (!layout) return; // no keyboard layer in the scene → nothing to anchor to\n const c = ctx.ctx2d;\n const key = keyOverride ?? ctx.score?.key;\n const ink = color ?? ctx.theme.ink;\n\n for (const n of ctx.score?.notes ?? []) {\n if (!inRange(layout, n.pitchMidi)) continue;\n // Reuse the highlight envelope for a clean fade around the sounding window.\n const a = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: n.onsetMs, outMs: n.onsetMs + n.durMs, fadeMs },\n tMs,\n );\n if (a <= 0) continue;\n\n const label = degreeLabel(n.step, n.alter, n.pitchMidi, key, mode);\n const cx = keyCenterX(layout, n.pitchMidi);\n // Sit the chip just above the keyboard; black keys a touch higher so the\n // chip clears the (taller, frontmost) white-key chips below.\n const cy = layout.top - (isBlackKey(n.pitchMidi) ? size * 1.6 : size * 0.6);\n\n c.save();\n c.globalAlpha = a;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${size}px ${ctx.theme.fontBody}`;\n // Pill behind the text for legibility over any background.\n const w = c.measureText(label.text).width;\n const padX = size * 0.35;\n const padY = size * 0.22;\n c.fillStyle = ctx.theme.paper;\n c.globalAlpha = a * 0.92;\n roundRect(c, cx - w / 2 - padX, cy - size / 2 - padY, w + 2 * padX, size + 2 * padY, size * 0.3);\n c.fill();\n c.globalAlpha = a;\n c.fillStyle = ink;\n c.fillText(label.text, cx, cy);\n c.restore();\n }\n },\n };\n}\n\n/** Rounded-rect path (kept local so the layer has no extra deps; falls back to a\n * plain rect when the ctx lacks the helper, e.g. the recording mock). */\nfunction roundRect(\n c: CanvasRenderingContext2D,\n x: number, y: number, w: number, h: number, r: number,\n): void {\n if (typeof (c as unknown as { roundRect?: unknown }).roundRect === 'function') {\n c.beginPath();\n (c as unknown as { roundRect: (x: number, y: number, w: number, h: number, r: number) => void })\n .roundRect(x, y, w, h, r);\n return;\n }\n c.beginPath();\n c.moveTo(x + r, y);\n c.lineTo(x + w - r, y);\n c.lineTo(x + w, y + h);\n c.lineTo(x, y + h);\n c.closePath();\n}\n\nexport const degreeLabelsFactory: LayerFactory<DegreeLabelsProps> = {\n key: 'degree-labels',\n create: degreeLabelsLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['degree-labels: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.mode != null && p.mode !== 'degree' && p.mode !== 'solfege')\n errs.push('degree-labels.mode must be \"degree\" | \"solfege\"');\n if (p.key != null && typeof p.key !== 'string')\n errs.push('degree-labels.key must be a string');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0))\n errs.push('degree-labels.size must be a positive number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('degree-labels.fadeMs must be a non-negative number');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('degree-labels.color must be a string');\n return errs;\n },\n};\n","// Functional-harmony track (S5) — pure helpers for the `functional-harmony` layer.\n//\n// IMPORTANT (spec S5): the Score does NOT carry harmony. This layer takes a CHORD\n// TRACK as a layer prop (a provider) and does NOT fabricate harmony from the\n// notes. The track is a timed list of chord spans, each with a tonal FUNCTION\n// (Tonic / Subdominant / Dominant / other) — supplied by the host (e.g. RET's\n// chords.json or an analysis step). We map function → a stable colour and resolve\n// the active span at time t. Pure (no canvas) ⇒ unit-testable.\n\nexport type HarmonicFunction = 'T' | 'S' | 'D' | 'other';\n\nexport interface ChordSpan {\n /** Span start, ms. */\n startMs: number;\n /** Span end, ms. */\n endMs: number;\n /** Tonal function. */\n fn: HarmonicFunction;\n /** Optional display label (roman numeral / chord symbol), drawn by the layer. */\n label?: string;\n}\n\n/** Default function colours (warm tonic / cool subdominant / tense dominant). */\nexport interface FunctionColors {\n T: string;\n S: string;\n D: string;\n other: string;\n}\n\nexport const DEFAULT_FUNCTION_COLORS: FunctionColors = {\n T: '#3a7d44', // grounded green\n S: '#3a6ea5', // calm blue\n D: '#c2502f', // tense orange-red\n other: '#7a7a7a',\n};\n\n/** The chord span active at time `tMs` (first whose [start,end) contains t), or null. */\nexport function activeChord(track: ChordSpan[], tMs: number): ChordSpan | null {\n for (const s of track) {\n if (tMs >= s.startMs && tMs < s.endMs) return s;\n }\n return null;\n}\n\n/** Colour for a function. Pure. */\nexport function functionColor(fn: HarmonicFunction, colors: FunctionColors): string {\n return colors[fn] ?? colors.other;\n}\n\n/**\n * Validate a chord track (used by the layer's prop validator). Returns []\n * when valid, else human-readable errors. Exported so the gate message is precise.\n */\nexport function validateChordTrack(track: unknown): string[] {\n if (!Array.isArray(track)) return ['chordTrack must be an array of chord spans'];\n const errs: string[] = [];\n track.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.startMs !== 'number' || typeof s?.endMs !== 'number')\n errs.push(`chordTrack[${i}] needs numeric startMs/endMs`);\n else if (s.endMs <= s.startMs) errs.push(`chordTrack[${i}].endMs must be > startMs`);\n if (s?.fn !== 'T' && s?.fn !== 'S' && s?.fn !== 'D' && s?.fn !== 'other')\n errs.push(`chordTrack[${i}].fn must be \"T\"|\"S\"|\"D\"|\"other\"`);\n if (s?.label != null && typeof s.label !== 'string')\n errs.push(`chordTrack[${i}].label must be a string`);\n });\n return errs;\n}\n","// `functional-harmony` layer (S5) — colour the scene by tonal FUNCTION (Tonic /\n// Subdominant / Dominant) over time. Harmony pedagogy: the whole frame breathes\n// T→S→D→T so the listener FEELS the cadential arc.\n//\n// DATA SOURCE (spec S5): the Score carries no harmony, so this layer takes a\n// `chordTrack` prop (timed ChordSpans with a function + optional roman/label),\n// supplied by the host (RET chords.json, an analysis step, …). It does NOT infer\n// harmony from the notes — if no track is given the layer draws nothing and the\n// validator flags it.\n//\n// Render modes (composable):\n// - \"band\": a colour bar across the top of the safe box (always available).\n// - \"keys\": tint the sounding keyboard keys by the active function (needs a\n// keyboard layer's published layout; rides keyboardStore + the highlight prim).\n// - \"wash\": a faint full-frame wash in the function colour.\n// A roman/label is drawn in the band when the active span has one.\n//\n// Pure fn of t (active span resolved by activeChord; intensity by highlightIntensity).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { keyRect, inRange } from '../keyboardGeometry';\nimport { highlightIntensity } from '../highlight';\nimport {\n activeChord,\n functionColor,\n validateChordTrack,\n DEFAULT_FUNCTION_COLORS,\n type ChordSpan,\n type FunctionColors,\n} from '../harmonyTrack';\n\nexport type HarmonyMode = 'band' | 'keys' | 'wash';\n\nexport interface FunctionalHarmonyProps {\n /** REQUIRED: the timed chord spans (function + optional label). Host-supplied. */\n chordTrack: ChordSpan[];\n /** Which renderings to draw. Default [\"band\"]. */\n modes?: HarmonyMode[];\n /** Override the function→colour map. */\n colors?: FunctionColors;\n /** Band height (world px). Default 64. */\n bandHeight?: number;\n /** Band top (world y). Default safeBox.top. */\n bandTop?: number;\n /** Cross-fade between spans, ms. Default 200. */\n fadeMs?: number;\n /** Wash opacity at full intensity. Default 0.12. */\n washAlpha?: number;\n}\n\nconst DEFAULT_BAND_H = 64;\nconst DEFAULT_FADE = 200;\n\nfunction functionalHarmonyLayer(): Layer<FunctionalHarmonyProps> {\n let track: ChordSpan[] = [];\n let modes: HarmonyMode[] = ['band'];\n let colors: FunctionColors = DEFAULT_FUNCTION_COLORS;\n let bandHeight = DEFAULT_BAND_H;\n let bandTopProp: number | undefined;\n let fadeMs = DEFAULT_FADE;\n let washAlpha = 0.12;\n\n return {\n key: 'functional-harmony',\n init(_ctx: RenderCtx, props) {\n track = props.chordTrack ?? [];\n modes = props.modes ?? ['band'];\n colors = props.colors ?? DEFAULT_FUNCTION_COLORS;\n bandHeight = props.bandHeight ?? DEFAULT_BAND_H;\n bandTopProp = props.bandTop;\n fadeMs = props.fadeMs ?? DEFAULT_FADE;\n washAlpha = props.washAlpha ?? 0.12;\n },\n\n draw(ctx, tMs) {\n const span = activeChord(track, tMs);\n if (!span) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const col = functionColor(span.fn, colors);\n // Intensity ramps at the span edges so colours cross-fade, not pop.\n const a = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: span.startMs, outMs: span.endMs, fadeMs },\n tMs,\n );\n if (a <= 0) return;\n\n if (modes.includes('wash')) {\n c.save();\n c.globalAlpha = a * washAlpha;\n c.fillStyle = col;\n c.fillRect(0, 0, ctx.W, ctx.H);\n c.restore();\n }\n\n if (modes.includes('keys')) {\n const layout = getKeyboardLayout(ctx);\n if (layout) {\n c.save();\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(layout, n.pitchMidi)) continue;\n const r = keyRect(layout, n.pitchMidi);\n c.globalAlpha = a * 0.8;\n c.fillStyle = col;\n c.fillRect(r.x, r.y, r.w, r.h);\n }\n c.restore();\n }\n }\n\n if (modes.includes('band')) {\n const top = bandTopProp ?? sb.top;\n c.save();\n c.globalAlpha = a * 0.85;\n c.fillStyle = col;\n c.fillRect(sb.left, top, sb.w, bandHeight);\n if (span.label) {\n c.globalAlpha = a;\n c.fillStyle = '#ffffff';\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${Math.round(bandHeight * 0.5)}px ${ctx.theme.fontDisplay}`;\n c.fillText(span.label, sb.left + sb.w / 2, top + bandHeight / 2);\n }\n c.restore();\n }\n },\n };\n}\n\nexport const functionalHarmonyFactory: LayerFactory<FunctionalHarmonyProps> = {\n key: 'functional-harmony',\n create: functionalHarmonyLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['functional-harmony: props must be an object'];\n const p = props as Record<string, unknown>;\n // chordTrack is REQUIRED (the layer has no data otherwise).\n errs.push(...validateChordTrack(p.chordTrack).map((e) => `functional-harmony.${e}`));\n if (p.modes != null) {\n if (!Array.isArray(p.modes)) errs.push('functional-harmony.modes must be an array');\n else for (const m of p.modes)\n if (m !== 'band' && m !== 'keys' && m !== 'wash')\n errs.push('functional-harmony.modes items must be \"band\"|\"keys\"|\"wash\"');\n }\n if (p.bandHeight != null && (typeof p.bandHeight !== 'number' || p.bandHeight <= 0))\n errs.push('functional-harmony.bandHeight must be a positive number');\n if (p.bandTop != null && typeof p.bandTop !== 'number')\n errs.push('functional-harmony.bandTop must be a number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('functional-harmony.fadeMs must be a non-negative number');\n if (p.washAlpha != null && (typeof p.washAlpha !== 'number' || p.washAlpha < 0 || p.washAlpha > 1))\n errs.push('functional-harmony.washAlpha must be in [0,1]');\n return errs;\n },\n};\n","// MCQ quiz/poll card (S5) — pure helpers for the `mcq-card` layer that unifies\n// RET's \"pick the interval/chord\" quiz with whozart's \"name that piece\".\n//\n// DATA SOURCE (spec S5): the Score carries no quiz, so the card is driven ENTIRELY\n// by a host-supplied `quiz` prop (question text, options, correctIndex, timings).\n// This module does NOT fabricate a quiz — it only resolves the card's PHASE at a\n// time t and validates the prop. Pure (no canvas) ⇒ exhaustively unit-testable.\n//\n// Phases over the card's lifetime (all times absolute ms on the clip clock):\n// - \"question\": options shown, countdown running, nothing revealed yet\n// [askMs, revealMs)\n// - \"reveal\": the correct option is highlighted; wrong picks dimmed\n// [revealMs, endMs)\n// `askMs` defaults to the start of the segment (0); a host that places the card in\n// a [a,b] segment passes askMs=a so the countdown maps to wall-clock correctly.\n\nexport interface QuizOption {\n /** Option text (e.g. \"Major 3rd\", \"Beethoven\"). */\n text: string;\n}\n\nexport interface Quiz {\n /** The prompt (\"Which interval?\" / \"Name this piece\"). */\n question: string;\n /** 2–4 options. */\n options: QuizOption[];\n /** Index into options of the correct answer (0-based). */\n correctIndex: number;\n /** When the card starts asking (absolute ms). Default 0. */\n askMs?: number;\n /** When the correct option is revealed (absolute ms). REQUIRED. */\n revealMs: number;\n /** When the card finishes (absolute ms). REQUIRED. */\n endMs: number;\n /** Optional per-option poll percentages (0..100), shown as bars. Length must\n * match options. Host-supplied (e.g. real community votes); not fabricated. */\n poll?: number[];\n}\n\nexport type QuizPhase = 'before' | 'question' | 'reveal' | 'after';\n\n/** Resolve the card phase at time `tMs`. Pure. */\nexport function quizPhase(quiz: Quiz, tMs: number): QuizPhase {\n const ask = quiz.askMs ?? 0;\n if (tMs < ask) return 'before';\n if (tMs < quiz.revealMs) return 'question';\n if (tMs < quiz.endMs) return 'reveal';\n return 'after';\n}\n\n/**\n * Countdown fraction remaining 0..1 across [askMs, revealMs] (1 at ask, 0 at\n * reveal), clamped outside. Drives the countdown ring/bar. Pure.\n */\nexport function countdownRemaining(quiz: Quiz, tMs: number): number {\n const ask = quiz.askMs ?? 0;\n const span = quiz.revealMs - ask;\n if (span <= 0) return 0;\n const elapsed = (tMs - ask) / span;\n return elapsed <= 0 ? 1 : elapsed >= 1 ? 0 : 1 - elapsed;\n}\n\n/**\n * Integer seconds remaining on the countdown (ceil), for the big number. Pure.\n */\nexport function countdownSeconds(quiz: Quiz, tMs: number): number {\n const ask = quiz.askMs ?? 0;\n const remMs = Math.max(0, quiz.revealMs - Math.max(ask, tMs));\n return Math.ceil(remMs / 1000);\n}\n\n/**\n * Reveal progress 0..1 across a short window after revealMs (for a pop/fade of\n * the correct option). `windowMs` default 350. Pure.\n */\nexport function revealProgress(quiz: Quiz, tMs: number, windowMs = 350): number {\n if (tMs < quiz.revealMs) return 0;\n if (windowMs <= 0) return 1;\n const p = (tMs - quiz.revealMs) / windowMs;\n return p >= 1 ? 1 : p;\n}\n\n/**\n * Validate a Quiz prop (used by the layer's validator + the pre-render gate).\n * Returns [] when valid, else human-readable errors. The card REQUIRES this prop;\n * it has no data otherwise.\n */\nexport function validateQuiz(quiz: unknown): string[] {\n if (quiz == null || typeof quiz !== 'object') return ['quiz must be an object'];\n const q = quiz as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof q.question !== 'string' || q.question.length === 0)\n errs.push('quiz.question must be a non-empty string');\n if (!Array.isArray(q.options) || q.options.length < 2 || q.options.length > 4)\n errs.push('quiz.options must be an array of 2–4 options');\n else {\n q.options.forEach((o, i) => {\n const opt = o as Record<string, unknown>;\n if (opt == null || typeof opt.text !== 'string' || opt.text.length === 0)\n errs.push(`quiz.options[${i}].text must be a non-empty string`);\n });\n }\n const nOpts = Array.isArray(q.options) ? q.options.length : 0;\n if (typeof q.correctIndex !== 'number' || !Number.isInteger(q.correctIndex) ||\n q.correctIndex < 0 || (nOpts > 0 && q.correctIndex >= nOpts))\n errs.push('quiz.correctIndex must be an integer index into options');\n if (typeof q.revealMs !== 'number') errs.push('quiz.revealMs must be a number (ms)');\n if (typeof q.endMs !== 'number') errs.push('quiz.endMs must be a number (ms)');\n if (typeof q.revealMs === 'number' && typeof q.endMs === 'number' && q.endMs <= q.revealMs)\n errs.push('quiz.endMs must be > revealMs');\n if (q.askMs != null && typeof q.askMs !== 'number') errs.push('quiz.askMs must be a number (ms)');\n if (typeof q.askMs === 'number' && typeof q.revealMs === 'number' && q.revealMs <= q.askMs)\n errs.push('quiz.revealMs must be > askMs');\n if (q.poll != null) {\n if (!Array.isArray(q.poll) || (nOpts > 0 && q.poll.length !== nOpts))\n errs.push('quiz.poll, if given, must be an array matching options length');\n else if (!q.poll.every((p) => typeof p === 'number' && p >= 0 && p <= 100))\n errs.push('quiz.poll entries must be numbers in [0,100]');\n }\n return errs;\n}\n","// `mcq-card` layer (S5) — a multiple-choice quiz/poll card: question + 2–4 options\n// + a countdown + a reveal of the correct one + an optional poll bar. The single\n// layer that unifies RET's quiz (\"pick the interval/chord\") with whozart's \"name\n// that piece\".\n//\n// DATA SOURCE (spec S5): the Score has no quiz; this layer is driven ENTIRELY by a\n// host-supplied `quiz` prop (validateQuiz REQUIRES it — the validator fast-fails\n// if it is missing/malformed). The layer does NOT fabricate a quiz.\n//\n// Screen-pinned (registered in SCREEN_PINNED_KEYS): like the other card layers it\n// draws in viewport space over the whole frame, anchored to the safe box. Pure\n// fn of t (phase/countdown/reveal all from quizCard.ts).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n quizPhase,\n countdownRemaining,\n countdownSeconds,\n revealProgress,\n validateQuiz,\n type Quiz,\n} from '../quizCard';\nimport { easeOut } from '../math';\n\nexport interface McqCardProps {\n /** REQUIRED: the host-supplied quiz (question/options/correctIndex/timings). */\n quiz: Quiz;\n /** Accent for the correct reveal (default theme.accent). */\n correctColor?: string;\n /** Show the poll bars (only if quiz.poll is present). Default true. */\n showPoll?: boolean;\n}\n\nconst CARD_RADIUS = 28;\n\nfunction roundRect(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n const rr = Math.min(r, w / 2, h / 2);\n const anyC = c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void };\n if (typeof anyC.roundRect === 'function') {\n c.beginPath();\n anyC.roundRect(x, y, w, h, rr);\n return;\n }\n c.beginPath();\n c.moveTo(x + rr, y);\n c.lineTo(x + w - rr, y);\n c.lineTo(x + w, y + rr);\n c.lineTo(x + w, y + h - rr);\n c.lineTo(x + w - rr, y + h);\n c.lineTo(x + rr, y + h);\n c.lineTo(x, y + h - rr);\n c.lineTo(x, y + rr);\n c.closePath();\n}\n\nfunction mcqCardLayer(): Layer<McqCardProps> {\n let quiz: Quiz = { question: '', options: [], correctIndex: 0, revealMs: 0, endMs: 0 };\n let correctColor: string | undefined;\n let showPoll = true;\n\n return {\n key: 'mcq-card',\n init(_ctx: RenderCtx, props) {\n quiz = props.quiz;\n correctColor = props.correctColor;\n showPoll = props.showPoll ?? true;\n },\n\n draw(ctx, tMs) {\n const phase = quizPhase(quiz, tMs);\n if (phase === 'before' || phase === 'after') return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = correctColor ?? ctx.theme.accent;\n const revealed = phase === 'reveal';\n const revP = easeOut(revealProgress(quiz, tMs));\n\n // Card geometry: a centred panel within the safe box.\n const cardW = sb.w;\n const n = quiz.options.length;\n const optH = Math.min(120, (sb.h * 0.5) / Math.max(1, n));\n const gap = optH * 0.22;\n const headH = optH * 1.3;\n const cardH = headH + n * (optH + gap);\n const cardX = sb.left;\n const cardY = sb.top + (sb.h - cardH) / 2;\n\n c.save();\n\n // Question header.\n c.fillStyle = ctx.theme.ink;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${Math.round(headH * 0.42)}px ${ctx.theme.fontDisplay}`;\n c.fillText(quiz.question, cardX + cardW / 2, cardY + headH * 0.45);\n\n // Countdown (only during the question phase): a ring + seconds on the right.\n if (!revealed) {\n const rem = countdownRemaining(quiz, tMs);\n const secs = countdownSeconds(quiz, tMs);\n const cr = headH * 0.34;\n const ccx = cardX + cardW - cr - 8;\n const ccy = cardY + headH * 0.45;\n c.lineWidth = Math.max(3, cr * 0.16);\n c.strokeStyle = ctx.theme.sepia;\n c.globalAlpha = 0.3;\n c.beginPath();\n c.arc(ccx, ccy, cr, 0, Math.PI * 2);\n c.stroke();\n c.globalAlpha = 1;\n c.strokeStyle = accent;\n c.beginPath();\n c.arc(ccx, ccy, cr, -Math.PI / 2, -Math.PI / 2 + rem * Math.PI * 2);\n c.stroke();\n c.fillStyle = ctx.theme.ink;\n c.font = `700 ${Math.round(cr * 0.9)}px ${ctx.theme.fontBody}`;\n c.fillText(String(secs), ccx, ccy);\n }\n\n // Options.\n let oy = cardY + headH;\n quiz.options.forEach((opt, i) => {\n const isCorrect = i === quiz.correctIndex;\n const x = cardX;\n const y = oy;\n // Background pill. On reveal: correct glows accent; others dim.\n let bg = ctx.theme.paper;\n let alpha = 1;\n if (revealed) {\n if (isCorrect) bg = accent;\n else alpha = 1 - 0.55 * revP;\n }\n c.globalAlpha = alpha;\n c.fillStyle = bg;\n roundRect(c, x, y, cardW, optH, CARD_RADIUS);\n c.fill();\n // border\n c.globalAlpha = alpha;\n c.lineWidth = isCorrect && revealed ? 4 : 2;\n c.strokeStyle = isCorrect && revealed ? accent : ctx.theme.sepia;\n roundRect(c, x, y, cardW, optH, CARD_RADIUS);\n c.stroke();\n\n // Poll bar fill (if available + enabled): grows on reveal.\n if (showPoll && quiz.poll && quiz.poll[i] != null) {\n const pct = quiz.poll[i] / 100;\n c.globalAlpha = alpha * 0.25;\n c.fillStyle = isCorrect ? accent : ctx.theme.sepia;\n roundRect(c, x, y, cardW * pct * revP, optH, CARD_RADIUS);\n c.fill();\n }\n\n // Option label letter (A/B/C/D) + text.\n c.globalAlpha = alpha;\n const label = String.fromCharCode(65 + i);\n c.fillStyle = isCorrect && revealed ? ctx.theme.paper : ctx.theme.ink;\n c.textAlign = 'left';\n c.font = `700 ${Math.round(optH * 0.34)}px ${ctx.theme.fontBody}`;\n c.fillText(`${label}.`, x + optH * 0.4, y + optH / 2);\n c.fillText(opt.text, x + optH * 1.2, y + optH / 2);\n\n // Poll percent on the right when shown.\n if (showPoll && quiz.poll && quiz.poll[i] != null && revealed) {\n c.textAlign = 'right';\n c.font = `600 ${Math.round(optH * 0.3)}px ${ctx.theme.fontBody}`;\n c.fillText(`${Math.round(quiz.poll[i])}%`, x + cardW - optH * 0.4, y + optH / 2);\n }\n\n oy += optH + gap;\n });\n\n c.restore();\n },\n };\n}\n\nexport const mcqCardFactory: LayerFactory<McqCardProps> = {\n key: 'mcq-card',\n create: mcqCardLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['mcq-card: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n // quiz is REQUIRED (the layer has no data otherwise).\n errs.push(...validateQuiz(p.quiz).map((e) => `mcq-card.${e}`));\n if (p.correctColor != null && typeof p.correctColor !== 'string')\n errs.push('mcq-card.correctColor must be a string');\n if (p.showPoll != null && typeof p.showPoll !== 'boolean')\n errs.push('mcq-card.showPoll must be a boolean');\n return errs;\n },\n};\n","// Circle-of-fifths geometry (S5) — pure, canvas-free placement of the 12 keys\n// around a circle, plus which slice is the current key and the angular path to a\n// modulation target. The `circle-of-fifths` layer just strokes/fills what this\n// returns. Pure ⇒ exhaustively unit-testable.\n//\n// Ordering: clockwise by ascending fifths from C at the top (12 o'clock):\n// C, G, D, A, E, B, F#, Db, Ab, Eb, Bb, F. The pitch class of slot i is\n// (7*i) mod 12. We map a key string → its slot via its tonic pitch class.\n\nimport { parseKey } from './degreeLabels';\n\n/** Major-key labels around the circle, clockwise from the top. */\nexport const FIFTHS_MAJOR = ['C', 'G', 'D', 'A', 'E', 'B', 'F♯', 'D♭', 'A♭', 'E♭', 'B♭', 'F'];\n/** Relative-minor labels (inner ring), aligned to the same slots. */\nexport const FIFTHS_MINOR = ['a', 'e', 'b', 'f♯', 'c♯', 'g♯', 'd♯', 'b♭', 'f', 'c', 'g', 'd'];\n\n/** Pitch class at circle slot i (0..11), clockwise fifths from C. */\nexport function slotPc(i: number): number {\n return (7 * i) % 12;\n}\n\n/** The circle slot (0..11) for a tonic pitch class. Pure. */\nexport function pcToSlot(pc: number): number {\n const p = ((pc % 12) + 12) % 12;\n for (let i = 0; i < 12; i++) if (slotPc(i) === p) return i;\n return 0;\n}\n\n/** The circle slot for a key string (\"G major\" / \"Eb\"). Pure. */\nexport function keySlot(key: string | undefined): number {\n return pcToSlot(parseKey(key).tonicPc);\n}\n\n/** Angle (radians, 0 = up/12-o'clock, clockwise +) of circle slot i. Pure. */\nexport function slotAngle(i: number): number {\n return (i / 12) * Math.PI * 2;\n}\n\nexport interface CirclePoint {\n x: number;\n y: number;\n}\n\n/** Cartesian point for a slot on a circle of radius r centred at (cx,cy).\n * 0 rad = straight up, increasing clockwise. Pure. */\nexport function slotPoint(cx: number, cy: number, r: number, i: number): CirclePoint {\n const a = slotAngle(i) - Math.PI / 2; // canvas 0 rad points right; shift so 0=up\n return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) };\n}\n\n/**\n * Animated highlight slot as a fractional position from a source slot to a target\n * slot over progress `t01`, taking the SHORTEST way around the wheel. Returns a\n * fractional slot (e.g. 1.5 = between G and D). Pure.\n */\nexport function animatedSlot(fromSlot: number, toSlot: number, t01: number): number {\n let delta = toSlot - fromSlot;\n // shortest path around the 12-slot ring\n if (delta > 6) delta -= 12;\n if (delta < -6) delta += 12;\n const t = t01 < 0 ? 0 : t01 > 1 ? 1 : t01;\n const s = fromSlot + delta * t;\n return ((s % 12) + 12) % 12;\n}\n\n/** Cartesian point for a FRACTIONAL slot (e.g. mid-modulation) on the circle.\n * Pure. */\nexport function fracSlotPoint(cx: number, cy: number, r: number, frac: number): CirclePoint {\n const a = (frac / 12) * Math.PI * 2 - Math.PI / 2;\n return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) };\n}\n","// `circle-of-fifths` layer (S5) — a circle-of-fifths widget that highlights the\n// current key (from Score.key) with an optional animated sweep to a target key\n// (modulation). Pure geometry (circleOfFifths.ts) + draw; pure fn of t.\n//\n// DATA SOURCE: the current key comes from Score.key (or a `key` prop override).\n// A modulation is host-supplied via `toKey` + a [fromMs,toMs] window — the Score\n// carries no modulation map, so we do NOT infer it. With no toKey the widget just\n// pins the highlight on the current key.\n//\n// Screen-pinned-friendly but drawn in world space by default (it's a HUD widget,\n// not full-frame chrome); a host can place it anywhere via center/radius props.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n FIFTHS_MAJOR,\n FIFTHS_MINOR,\n keySlot,\n slotPoint,\n fracSlotPoint,\n animatedSlot,\n} from '../circleOfFifths';\nimport { easeInOut, invLerp } from '../math';\n\nexport interface CircleOfFifthsProps {\n /** Override the highlighted key (else Score.key, else C). */\n key?: string;\n /** Animate the highlight to this key (modulation target). Host-supplied. */\n toKey?: string;\n /** Modulation start time (ms). Required if toKey is set. */\n fromMs?: number;\n /** Modulation end time (ms). Required if toKey is set. */\n toMs?: number;\n /** Widget centre [x,y] (world). Default centred in the safe box. */\n center?: [number, number];\n /** Outer radius (world px). Default min(safe.w, safe.h) * 0.32. */\n radius?: number;\n /** Show the relative-minor inner ring. Default true. */\n showMinor?: boolean;\n /** Highlight colour (default theme.accent). */\n color?: string;\n}\n\nfunction circleOfFifthsLayer(): Layer<CircleOfFifthsProps> {\n let keyOverride: string | undefined;\n let toKey: string | undefined;\n let fromMs = 0;\n let toMs = 0;\n let centerProp: [number, number] | undefined;\n let radiusProp: number | undefined;\n let showMinor = true;\n let color: string | undefined;\n\n return {\n key: 'circle-of-fifths',\n init(_ctx: RenderCtx, props) {\n keyOverride = props.key;\n toKey = props.toKey;\n fromMs = props.fromMs ?? 0;\n toMs = props.toMs ?? 0;\n centerProp = props.center;\n radiusProp = props.radius;\n showMinor = props.showMinor ?? true;\n color = props.color;\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cx = centerProp?.[0] ?? sb.left + sb.w / 2;\n const cy = centerProp?.[1] ?? sb.top + sb.h / 2;\n const R = radiusProp ?? Math.min(sb.w, sb.h) * 0.32;\n const accent = color ?? ctx.theme.accent;\n const rMajor = R;\n const rMinor = R * 0.66;\n\n const fromSlot = keySlot(keyOverride ?? ctx.score?.key);\n // Animated highlight slot (fractional during a modulation).\n let hiSlot = fromSlot;\n if (toKey) {\n const t01 = easeInOut(invLerp(fromMs, toMs, tMs));\n hiSlot = animatedSlot(fromSlot, keySlot(toKey), t01);\n }\n\n c.save();\n\n // Outer + inner ring circles.\n c.lineWidth = 2;\n c.strokeStyle = ctx.theme.sepia;\n c.globalAlpha = 0.5;\n c.beginPath();\n c.arc(cx, cy, rMajor + R * 0.16, 0, Math.PI * 2);\n c.stroke();\n if (showMinor) {\n c.beginPath();\n c.arc(cx, cy, rMinor - R * 0.16, 0, Math.PI * 2);\n c.stroke();\n }\n c.globalAlpha = 1;\n\n // Highlight wedge behind the current key (a filled dot that rides the slot).\n const hp = fracSlotPoint(cx, cy, rMajor, hiSlot);\n c.fillStyle = accent;\n c.globalAlpha = 0.9;\n c.beginPath();\n c.arc(hp.x, hp.y, R * 0.2, 0, Math.PI * 2);\n c.fill();\n c.globalAlpha = 1;\n\n // Key labels (major outer, minor inner).\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n for (let i = 0; i < 12; i++) {\n const isHi = Math.round(hiSlot) % 12 === i;\n const pm = slotPoint(cx, cy, rMajor, i);\n c.fillStyle = isHi ? ctx.theme.paper : ctx.theme.ink;\n c.font = `700 ${Math.round(R * 0.16)}px ${ctx.theme.fontBody}`;\n c.fillText(FIFTHS_MAJOR[i], pm.x, pm.y);\n if (showMinor) {\n const pn = slotPoint(cx, cy, rMinor, i);\n c.fillStyle = ctx.theme.sepia;\n c.globalAlpha = isHi ? 1 : 0.8;\n c.font = `400 ${Math.round(R * 0.12)}px ${ctx.theme.fontBody}`;\n c.fillText(FIFTHS_MINOR[i], pn.x, pn.y);\n c.globalAlpha = 1;\n }\n }\n\n c.restore();\n },\n };\n}\n\nexport const circleOfFifthsFactory: LayerFactory<CircleOfFifthsProps> = {\n key: 'circle-of-fifths',\n create: circleOfFifthsLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['circle-of-fifths: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.key != null && typeof p.key !== 'string') errs.push('circle-of-fifths.key must be a string');\n if (p.toKey != null) {\n if (typeof p.toKey !== 'string') errs.push('circle-of-fifths.toKey must be a string');\n if (typeof p.fromMs !== 'number' || typeof p.toMs !== 'number')\n errs.push('circle-of-fifths.toKey requires numeric fromMs and toMs');\n else if (p.toMs <= p.fromMs) errs.push('circle-of-fifths.toMs must be > fromMs');\n }\n if (p.center != null && (!Array.isArray(p.center) || p.center.length !== 2 ||\n !p.center.every((n) => typeof n === 'number')))\n errs.push('circle-of-fifths.center must be [x,y]');\n if (p.radius != null && (typeof p.radius !== 'number' || p.radius <= 0))\n errs.push('circle-of-fifths.radius must be a positive number');\n if (p.showMinor != null && typeof p.showMinor !== 'boolean')\n errs.push('circle-of-fifths.showMinor must be a boolean');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('circle-of-fifths.color must be a string');\n return errs;\n },\n};\n","// Pitch-contour geometry (S5) — the pure, canvas-free mapping from a Score's notes\n// to a polyline tracing melodic pitch over time (\"the shape of the melody\"). The\n// `pitch-contour` layer strokes the returned points and rides a moving dot at the\n// current note. Pure ⇒ unit-testable.\n//\n// Mapping: x = time→[plotLeft, plotRight] across [0, durationMs]; y = pitch→\n// [plotBottom, plotTop] across the score's [minPitch, maxPitch] (inverted so high\n// pitch = up). To read as a melodic CONTOUR we take, at each onset, the melody\n// note = the HIGHEST sounding pitch (a soprano-line heuristic; documented). Notes\n// are sorted by onset; ties between equal onsets keep the highest.\n\nimport type { ScoreNote } from './score';\nimport { clamp } from './math';\n\nexport interface ContourPoint {\n /** Onset time, ms. */\n tMs: number;\n /** MIDI pitch of the melody note at that onset. */\n pitchMidi: number;\n}\n\nexport interface ContourPlot {\n left: number;\n right: number;\n top: number;\n bottom: number;\n minPitch: number;\n maxPitch: number;\n durationMs: number;\n}\n\n/**\n * Reduce notes to one melody point per distinct onset = the highest sounding\n * pitch at that onset. Returns points sorted by time. Pure.\n */\nexport function contourPoints(notes: ScoreNote[]): ContourPoint[] {\n const byOnset = new Map<number, number>(); // onsetMs -> max pitch\n for (const n of notes) {\n const cur = byOnset.get(n.onsetMs);\n if (cur == null || n.pitchMidi > cur) byOnset.set(n.onsetMs, n.pitchMidi);\n }\n return [...byOnset.entries()]\n .map(([tMs, pitchMidi]) => ({ tMs, pitchMidi }))\n .sort((a, b) => a.tMs - b.tMs);\n}\n\n/** Pitch range [min,max] of the points (with a 1-semitone pad, min span 2). Pure. */\nexport function pitchRange(points: ContourPoint[]): { min: number; max: number } {\n if (!points.length) return { min: 60, max: 72 };\n let min = Infinity;\n let max = -Infinity;\n for (const p of points) {\n if (p.pitchMidi < min) min = p.pitchMidi;\n if (p.pitchMidi > max) max = p.pitchMidi;\n }\n if (max - min < 2) { min -= 1; max += 1; }\n return { min: min - 1, max: max + 1 };\n}\n\n/** Project a (time,pitch) onto plot pixels. y is inverted (high pitch = up). Pure. */\nexport function projectPoint(plot: ContourPlot, tMs: number, pitchMidi: number): { x: number; y: number } {\n const tx = plot.durationMs > 0 ? clamp(tMs / plot.durationMs, 0, 1) : 0;\n const py = plot.maxPitch > plot.minPitch\n ? clamp((pitchMidi - plot.minPitch) / (plot.maxPitch - plot.minPitch), 0, 1)\n : 0.5;\n return {\n x: plot.left + tx * (plot.right - plot.left),\n y: plot.bottom - py * (plot.bottom - plot.top),\n };\n}\n\n/** The full pixel polyline for the contour. Pure. */\nexport function contourPolyline(points: ContourPoint[], plot: ContourPlot): { x: number; y: number }[] {\n return points.map((p) => projectPoint(plot, p.tMs, p.pitchMidi));\n}\n\n/**\n * The melody pitch sounding at time `tMs`: the pitch of the most recent onset\n * at-or-before t (step interpolation — a melody holds until the next note). Returns\n * null before the first onset. Pure.\n */\nexport function pitchAt(points: ContourPoint[], tMs: number): number | null {\n if (!points.length || tMs < points[0].tMs) return null;\n let cur = points[0].pitchMidi;\n for (const p of points) {\n if (p.tMs <= tMs) cur = p.pitchMidi;\n else break;\n }\n return cur;\n}\n\n/** The moving-dot position at time t (rides the contour at the current pitch). Pure. */\nexport function dotAt(points: ContourPoint[], plot: ContourPlot, tMs: number): { x: number; y: number } | null {\n const pitch = pitchAt(points, tMs);\n if (pitch == null) return null;\n return projectPoint(plot, tMs, pitch);\n}\n","// `pitch-contour` layer (S5) — a continuous curve tracing the melody's pitch over\n// time from Score.notes (the \"shape of the melody\"), synced to the audio clock: a\n// moving dot rides the current note and the line \"draws on\" up to the playhead.\n//\n// DATA SOURCE: Score.notes only (no extra data). The melody line = the highest\n// sounding pitch at each onset (a soprano-line heuristic; see pitchContour.ts).\n//\n// Sync (pure fn of t): the dot's x = current time; the drawn portion of the line\n// reveals up to the playhead, the rest is a faint \"what's coming\" ghost. World\n// space; pin the plot to a band via props (default a band within the safe box).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n contourPoints,\n pitchRange,\n contourPolyline,\n dotAt,\n projectPoint,\n type ContourPoint,\n type ContourPlot,\n} from '../pitchContour';\n\nexport interface PitchContourProps {\n /** Plot band top (world y). Default safeBox.top + safeBox.h * 0.15. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h * 0.35. */\n height?: number;\n /** Line width (world px). Default 5. */\n width?: number;\n /** Line colour (default theme.accent). */\n color?: string;\n /** Show the moving dot at the current pitch. Default true. */\n dot?: boolean;\n /** Dot radius (world px). Default 14. */\n dotRadius?: number;\n}\n\nfunction pitchContourLayer(): Layer<PitchContourProps> {\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let width = 5;\n let color: string | undefined;\n let dot = true;\n let dotRadius = 14;\n let points: ContourPoint[] = [];\n\n return {\n key: 'pitch-contour',\n init(ctx, props) {\n topProp = props.top;\n heightProp = props.height;\n width = props.width ?? 5;\n color = props.color;\n dot = props.dot ?? true;\n dotRadius = props.dotRadius ?? 14;\n points = contourPoints(ctx.score?.notes ?? []);\n },\n\n draw(ctx, tMs) {\n if (points.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = color ?? ctx.theme.accent;\n const top = topProp ?? sb.top + sb.h * 0.15;\n const height = heightProp ?? sb.h * 0.35;\n const range = pitchRange(points);\n const plot: ContourPlot = {\n left: sb.left,\n right: sb.right,\n top,\n bottom: top + height,\n minPitch: range.min,\n maxPitch: range.max,\n durationMs: ctx.score?.durationMs ?? points[points.length - 1].tMs,\n };\n const poly = contourPolyline(points, plot);\n const playX = projectPoint(plot, tMs, range.min).x;\n\n c.save();\n c.lineJoin = 'round';\n c.lineCap = 'round';\n\n // Ghost (full line, faint) — \"what's coming\".\n c.globalAlpha = 0.22;\n c.strokeStyle = ctx.theme.sepia;\n c.lineWidth = width;\n c.beginPath();\n poly.forEach((p, i) => (i === 0 ? c.moveTo(p.x, p.y) : c.lineTo(p.x, p.y)));\n c.stroke();\n\n // Drawn-on portion up to the playhead (accent, solid). We include the segment\n // crossing the playhead by clipping each segment at playX.\n c.globalAlpha = 1;\n c.strokeStyle = accent;\n c.lineWidth = width;\n c.beginPath();\n let started = false;\n for (let i = 0; i < poly.length; i++) {\n const p = poly[i];\n if (p.x <= playX) {\n if (!started) { c.moveTo(p.x, p.y); started = true; }\n else c.lineTo(p.x, p.y);\n } else {\n // partial segment from previous point to the playhead\n if (i > 0) {\n const a = poly[i - 1];\n const f = (playX - a.x) / (p.x - a.x || 1);\n const y = a.y + (p.y - a.y) * f;\n if (!started) { c.moveTo(a.x, a.y); started = true; }\n c.lineTo(playX, y);\n }\n break;\n }\n }\n if (started) c.stroke();\n\n // Moving dot at the current pitch.\n if (dot) {\n const d = dotAt(points, plot, tMs);\n if (d && Number.isFinite(d.x) && Number.isFinite(d.y)) {\n c.globalAlpha = 1;\n c.fillStyle = accent;\n c.beginPath();\n c.arc(d.x, d.y, dotRadius, 0, Math.PI * 2);\n c.fill();\n c.globalAlpha = 0.3;\n c.beginPath();\n c.arc(d.x, d.y, dotRadius * 1.7, 0, Math.PI * 2);\n c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const pitchContourFactory: LayerFactory<PitchContourProps> = {\n key: 'pitch-contour',\n create: pitchContourLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['pitch-contour: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.top != null && typeof p.top !== 'number') errs.push('pitch-contour.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('pitch-contour.height must be a positive number');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0))\n errs.push('pitch-contour.width must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('pitch-contour.color must be a string');\n if (p.dot != null && typeof p.dot !== 'boolean') errs.push('pitch-contour.dot must be a boolean');\n if (p.dotRadius != null && (typeof p.dotRadius !== 'number' || p.dotRadius <= 0))\n errs.push('pitch-contour.dotRadius must be a positive number');\n return errs;\n },\n};\n","// Section-minimap geometry (S5) — pure, canvas-free placement of a thin piece\n// overview (\"you are here\") with optional section bands. The `section-minimap`\n// layer draws the bar + the position dot + labels. Pure ⇒ unit-testable.\n//\n// DATA SOURCE: section spans are host-supplied (Score carries no section analysis):\n// either an explicit `sections` prop ([{startMs,endMs,label}]) OR derived measure\n// spans (a uniform split, given a measure count + total duration). We do NOT infer\n// musical form. With neither, the minimap is a plain progress bar.\n\nimport { clamp } from './math';\n\nexport interface Section {\n /** Section start, ms. */\n startMs: number;\n /** Section end, ms. */\n endMs: number;\n /** Display label (\"Verse\", \"A\", \"Exposition\"). */\n label?: string;\n}\n\n/** Progress 0..1 along the clip at time t. Pure. */\nexport function progress01(durationMs: number, tMs: number): number {\n return durationMs > 0 ? clamp(tMs / durationMs, 0, 1) : 0;\n}\n\n/** x of a time on a [left, left+width] bar across [0, durationMs]. Pure. */\nexport function timeToX(left: number, width: number, durationMs: number, tMs: number): number {\n return left + progress01(durationMs, tMs) * width;\n}\n\n/** The section containing time t (first whose [start,end) holds t), or null. Pure. */\nexport function activeSection(sections: Section[], tMs: number): Section | null {\n for (const s of sections) if (tMs >= s.startMs && tMs < s.endMs) return s;\n return null;\n}\n\n/**\n * Build uniform measure-span sections from a measure count + total duration (a\n * fallback when no explicit sections are given but a measure count is). Each\n * section is one measure, labelled by 1-based measure number. Pure.\n */\nexport function measureSpans(measureCount: number, durationMs: number): Section[] {\n if (measureCount <= 0 || durationMs <= 0) return [];\n const w = durationMs / measureCount;\n const out: Section[] = [];\n for (let i = 0; i < measureCount; i++) {\n out.push({ startMs: i * w, endMs: (i + 1) * w, label: String(i + 1) });\n }\n return out;\n}\n\n/**\n * Validate a sections array (used by the layer validator). Returns [] when valid.\n */\nexport function validateSections(sections: unknown): string[] {\n if (!Array.isArray(sections)) return ['sections must be an array'];\n const errs: string[] = [];\n sections.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.startMs !== 'number' || typeof s?.endMs !== 'number')\n errs.push(`sections[${i}] needs numeric startMs/endMs`);\n else if (s.endMs <= s.startMs) errs.push(`sections[${i}].endMs must be > startMs`);\n if (s?.label != null && typeof s.label !== 'string')\n errs.push(`sections[${i}].label must be a string`);\n });\n return errs;\n}\n","// `section-minimap` layer (S5) — a thin piece-overview minimap with a \"you are\n// here\" position dot + optional section labels. Production/social helper for any\n// long clip (lessons, walkthroughs). Pure fn of t.\n//\n// DATA SOURCE: sections are host-supplied (`sections` prop) OR derived measure\n// spans (`measureCount` → uniform split via sectionMinimap.measureSpans). The\n// Score carries no form analysis, so we do NOT infer sections. With neither, the\n// minimap is a plain progress bar over Score.durationMs.\n//\n// Layout: a thin horizontal bar pinned near the bottom of the safe box; coloured\n// section bands; a dot at the current time; the active section's label above.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n timeToX,\n activeSection,\n measureSpans,\n validateSections,\n type Section,\n} from '../sectionMinimap';\n\nexport interface SectionMinimapProps {\n /** Host-supplied section spans. Takes precedence over measureCount. */\n sections?: Section[];\n /** Fallback: split the clip into this many uniform measure bands. */\n measureCount?: number;\n /** Bar baseline (world y). Default safeBox.bottom - 40. */\n barY?: number;\n /** Bar thickness (world px). Default 10. */\n barHeight?: number;\n /** Show the active section's label above the bar. Default true. */\n showLabel?: boolean;\n /** Accent for the played portion + dot (default theme.accent). */\n color?: string;\n}\n\nfunction sectionMinimapLayer(): Layer<SectionMinimapProps> {\n let sectionsProp: Section[] | undefined;\n let measureCount = 0;\n let barYProp: number | undefined;\n let barHeight = 10;\n let showLabel = true;\n let color: string | undefined;\n let sections: Section[] = [];\n\n return {\n key: 'section-minimap',\n init(ctx, props) {\n sectionsProp = props.sections;\n measureCount = props.measureCount ?? 0;\n barYProp = props.barY;\n barHeight = props.barHeight ?? 10;\n showLabel = props.showLabel ?? true;\n color = props.color;\n const dur = ctx.score?.durationMs ?? 0;\n sections = sectionsProp ?? (measureCount > 0 ? measureSpans(measureCount, dur) : []);\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = color ?? ctx.theme.accent;\n const dur = ctx.score?.durationMs ?? 0;\n if (dur <= 0) return;\n const left = sb.left;\n const width = sb.w;\n const barY = barYProp ?? sb.bottom - 40;\n\n c.save();\n const round = barHeight / 2;\n\n // Base track.\n c.fillStyle = ctx.theme.sepia;\n c.globalAlpha = 0.3;\n drawBar(c, left, barY - round, width, barHeight, round);\n\n // Section bands (alternating tint) + dividers.\n if (sections.length) {\n c.globalAlpha = 0.5;\n sections.forEach((s, i) => {\n const x0 = timeToX(left, width, dur, s.startMs);\n const x1 = timeToX(left, width, dur, s.endMs);\n c.fillStyle = i % 2 === 0 ? ctx.theme.sepia : ctx.theme.ink;\n c.globalAlpha = i % 2 === 0 ? 0.18 : 0.1;\n c.fillRect(x0, barY - round, Math.max(0, x1 - x0), barHeight);\n // divider tick\n c.globalAlpha = 0.4;\n c.fillStyle = ctx.theme.ink;\n c.fillRect(x0 - 1, barY - round - 3, 2, barHeight + 6);\n });\n }\n\n // Played portion up to now.\n const px = timeToX(left, width, dur, tMs);\n c.globalAlpha = 0.85;\n c.fillStyle = accent;\n drawBar(c, left, barY - round, Math.max(0, px - left), barHeight, round);\n\n // \"You are here\" dot.\n c.globalAlpha = 1;\n c.fillStyle = accent;\n c.beginPath();\n c.arc(px, barY, barHeight * 1.6, 0, Math.PI * 2);\n c.fill();\n c.fillStyle = ctx.theme.paper;\n c.beginPath();\n c.arc(px, barY, barHeight * 0.7, 0, Math.PI * 2);\n c.fill();\n\n // Active section label above the bar.\n if (showLabel) {\n const act = activeSection(sections, tMs);\n if (act?.label) {\n c.globalAlpha = 1;\n c.fillStyle = ctx.theme.ink;\n c.textAlign = 'center';\n c.textBaseline = 'bottom';\n c.font = `700 ${Math.round(barHeight * 2.4)}px ${ctx.theme.fontBody}`;\n c.fillText(act.label, px, barY - barHeight * 2.2);\n }\n }\n c.restore();\n },\n };\n}\n\nfunction drawBar(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n const rr = Math.min(r, w / 2, h / 2);\n const anyC = c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void };\n if (typeof anyC.roundRect === 'function') {\n c.beginPath();\n anyC.roundRect(x, y, w, h, rr);\n c.fill();\n return;\n }\n c.fillRect(x, y, w, h);\n}\n\nexport const sectionMinimapFactory: LayerFactory<SectionMinimapProps> = {\n key: 'section-minimap',\n create: sectionMinimapLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['section-minimap: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.sections != null) errs.push(...validateSections(p.sections).map((e) => `section-minimap.${e}`));\n if (p.measureCount != null && (typeof p.measureCount !== 'number' || p.measureCount < 0 ||\n !Number.isInteger(p.measureCount)))\n errs.push('section-minimap.measureCount must be a non-negative integer');\n if (p.barY != null && typeof p.barY !== 'number') errs.push('section-minimap.barY must be a number');\n if (p.barHeight != null && (typeof p.barHeight !== 'number' || p.barHeight <= 0))\n errs.push('section-minimap.barHeight must be a positive number');\n if (p.showLabel != null && typeof p.showLabel !== 'boolean')\n errs.push('section-minimap.showLabel must be a boolean');\n if (p.color != null && typeof p.color !== 'string') errs.push('section-minimap.color must be a string');\n return errs;\n },\n};\n","// `beat-pulse` layer (S6) — an onset-reactive visual \"breath\": the frame blooms\n// and/or its edges glow on every note onset, then decays. The cheapest energy\n// you can add to a music clip and it costs no new data — the onset times come\n// straight from the Score (the same onsets the cursor/falling-notes already\n// use), so any musical scene can drop it in.\n//\n// DATA SOURCE: onset times. Default = distinct onsets of `ctx.score.notes`. A\n// host can override with an `onsetsMs` prop (e.g. a beat grid, or a downbeats-\n// only list for a calmer pulse). With neither, the layer draws nothing.\n//\n// ENVELOPE: pure fn of t. At time t the pulse value is the max over recent\n// onsets of an attack→decay envelope: it snaps to full brightness AT the onset\n// (default `attackMs` 0 = a crisp flash on the beat; raise it for a softer\n// swell) then decays exponentially over `decayMs`. No wall-clock / rAF —\n// deterministic for capture + golden frames.\n//\n// STYLES (composable): \"bloom\" (a soft radial flash from center) and \"vignette\"\n// (the frame edges glow). Capped by `intensity` so they read as a gentle\n// heartbeat, not a strobe. `blend` defaults to \"source-over\" (a tinted bloom in\n// the accent colour, which reads on LIGHT/paper backgrounds — most of our\n// promos); switch to \"lighter\" (additive) on DARK backgrounds. The layer is\n// screen-pinned (drawn after the camera reset) so a panning notation camera\n// doesn't drag the flash around.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { distinctOnsets } from '../notationGeometry';\n\nexport type PulseStyle = 'bloom' | 'vignette';\n\nexport interface BeatPulseProps {\n /** Override onset times (ms). Default = distinct onsets of ctx.score.notes. */\n onsetsMs?: number[];\n /** Which renderings to draw. Default [\"bloom\"]. */\n styles?: PulseStyle[];\n /** Max overlay alpha at a fresh onset. Default 0.42 (subtle). */\n intensity?: number;\n /** Attack ramp to full, ms. Default 0 (snap to full on the onset). */\n attackMs?: number;\n /** Exponential decay time constant, ms. Default 170. */\n decayMs?: number;\n /** Overlay colour. Default theme.accent. */\n color?: string;\n /**\n * Canvas blend for the overlay. Default \"source-over\" (a tinted bloom that\n * reads on LIGHT backgrounds — most of our promos are paper/cream). Use\n * \"lighter\" (additive glow) on DARK backgrounds where it would otherwise just\n * wash to white.\n */\n blend?: 'source-over' | 'lighter';\n}\n\nconst DEFAULTS = {\n styles: ['bloom'] as PulseStyle[],\n intensity: 0.42,\n attackMs: 0,\n decayMs: 170,\n blend: 'source-over' as const,\n};\n\n/** Envelope value (0..1) at t for one onset: fast linear attack then exp decay. */\nfunction envAt(tMs: number, onsetMs: number, attackMs: number, decayMs: number): number {\n const dt = tMs - onsetMs;\n if (dt < 0) return 0;\n if (dt < attackMs) return attackMs <= 0 ? 1 : dt / attackMs;\n return Math.exp(-(dt - attackMs) / decayMs);\n}\n\n/**\n * Pulse value at t = max envelope over the few most recent onsets. Pure. Exported\n * so other layers / tests can read the same heartbeat the visual draws.\n */\nexport function pulseAt(\n onsetsMs: number[], tMs: number, attackMs = DEFAULTS.attackMs, decayMs = DEFAULTS.decayMs,\n): number {\n let p = 0;\n // Onsets are sorted; only the last handful can contribute (decay is short).\n for (let i = onsetsMs.length - 1; i >= 0; i--) {\n const on = onsetsMs[i];\n if (on > tMs) continue;\n const e = envAt(tMs, on, attackMs, decayMs);\n if (e > p) p = e;\n // Older than ~6 decay constants → contributes <0.3%, stop.\n if (tMs - on > attackMs + decayMs * 6) break;\n }\n return p;\n}\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;\n return `rgba(${r},${g},${b},${a})`;\n}\n\nfunction beatPulseLayer(): Layer<BeatPulseProps> {\n let onsets: number[] = [];\n let styles = DEFAULTS.styles;\n let intensity = DEFAULTS.intensity;\n let attackMs = DEFAULTS.attackMs;\n let decayMs = DEFAULTS.decayMs;\n let color: string | undefined;\n let blend: 'source-over' | 'lighter' = DEFAULTS.blend;\n\n return {\n key: 'beat-pulse',\n init(ctx: RenderCtx, props) {\n onsets = props.onsetsMs ?? (ctx.score ? distinctOnsets(ctx.score.notes) : []);\n onsets = [...onsets].sort((a, b) => a - b);\n styles = props.styles ?? DEFAULTS.styles;\n intensity = props.intensity ?? DEFAULTS.intensity;\n attackMs = props.attackMs ?? DEFAULTS.attackMs;\n decayMs = props.decayMs ?? DEFAULTS.decayMs;\n color = props.color;\n blend = props.blend ?? DEFAULTS.blend;\n },\n draw(ctx, tMs) {\n if (!onsets.length) return;\n const p = pulseAt(onsets, tMs, attackMs, decayMs);\n if (p <= 0.004) return;\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const col = color ?? ctx.theme.accent;\n const a = intensity * p;\n c.save();\n c.globalCompositeOperation = blend;\n if (styles.includes('bloom')) {\n const cx = W / 2, cy = H * 0.46;\n const rad = Math.max(W, H) * (0.30 + 0.10 * p);\n const g = c.createRadialGradient(cx, cy, 0, cx, cy, rad);\n g.addColorStop(0, rgba(col, a * 0.55));\n g.addColorStop(0.55, rgba(col, a * 0.18));\n g.addColorStop(1, rgba(col, 0));\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n if (styles.includes('vignette')) {\n const cx = W / 2, cy = H / 2;\n const inner = Math.min(W, H) * 0.42;\n const outer = Math.max(W, H) * 0.72;\n const g = c.createRadialGradient(cx, cy, inner, cx, cy, outer);\n g.addColorStop(0, rgba(col, 0));\n g.addColorStop(1, rgba(col, a * 0.6));\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n c.restore();\n },\n };\n}\n\nexport const beatPulseFactory: LayerFactory<BeatPulseProps> = {\n key: 'beat-pulse',\n create: beatPulseLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['beat-pulse: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.onsetsMs != null && (!Array.isArray(p.onsetsMs) || p.onsetsMs.some((x) => typeof x !== 'number')))\n errs.push('beat-pulse.onsetsMs must be a number[]');\n if (p.styles != null) {\n if (!Array.isArray(p.styles)) errs.push('beat-pulse.styles must be an array');\n else if (p.styles.some((s) => s !== 'bloom' && s !== 'vignette'))\n errs.push('beat-pulse.styles items must be \"bloom\" | \"vignette\"');\n }\n if (p.intensity != null && (typeof p.intensity !== 'number' || p.intensity < 0 || p.intensity > 1))\n errs.push('beat-pulse.intensity must be in [0,1]');\n if (p.attackMs != null && (typeof p.attackMs !== 'number' || p.attackMs < 0))\n errs.push('beat-pulse.attackMs must be a number >= 0');\n if (p.decayMs != null && (typeof p.decayMs !== 'number' || p.decayMs <= 0))\n errs.push('beat-pulse.decayMs must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('beat-pulse.color must be a string');\n if (p.blend != null && p.blend !== 'source-over' && p.blend !== 'lighter')\n errs.push('beat-pulse.blend must be \"source-over\" | \"lighter\"');\n return errs;\n },\n};\n","// `chord-ribbon` layer (S6) — a horizontally-scrolling strip of chord chips with\n// a fixed \"now\" playhead, so the viewer SEES the harmony arrive in time with the\n// sound. Where `functional-harmony` colours the whole frame by the ACTIVE span,\n// this shows the SEQUENCE (past / now / next) sliding past a marker — the\n// teaching primitive that turns a promo into a mini harmonic-analysis reel.\n//\n// DATA SOURCE: the SAME `ChordSpan[]` the functional-harmony layer takes\n// (harmonyTrack.ts) — host-supplied (RET chords.json, an analysis step, …). Each\n// span carries [start,end), a tonal function, and a display `label` (roman\n// numeral and/or chord symbol, e.g. \"V7\" or \"ii–V\"). The layer does NOT infer\n// harmony from the notes; with no track it draws nothing.\n//\n// LAYOUT (pure fn of t): time → x by a fixed px-per-ms scale, with the current\n// time pinned at `playheadFrac` of the safe width. Chips left of the playhead are\n// already played (dimmed); the chip under the playhead is the active chord\n// (emphasised, tinted by function). Screen-pinned (drawn after the camera reset)\n// and clipped to a horizontal band so a panning notation camera never moves it.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n functionColor,\n validateChordTrack,\n DEFAULT_FUNCTION_COLORS,\n type ChordSpan,\n type FunctionColors,\n} from '../harmonyTrack';\n\nexport interface ChordRibbonProps {\n /** REQUIRED: timed chord spans (function + display label). Host-supplied. */\n chordTrack: ChordSpan[];\n /** Horizontal time scale, px per ms. Default 0.06 (60 px/s). */\n pxPerMs?: number;\n /** Where \"now\" sits, as a fraction of the safe width. Default 0.32. */\n playheadFrac?: number;\n /** Chip baseline (world y / screen y, since pinned). Default safeBox.bottom - 260. */\n y?: number;\n /** Chip height (px). Default 84. */\n height?: number;\n /** Tint chips by tonal function. Default true. */\n showFunction?: boolean;\n /** Override the function→colour map. */\n colors?: FunctionColors;\n}\n\nconst DEFAULTS = { pxPerMs: 0.06, playheadFrac: 0.32, height: 84, showFunction: true };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction roundRectPath(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n if (typeof c.roundRect === 'function') { c.beginPath(); c.roundRect(x, y, w, h, r); return; }\n const rr = Math.min(r, w / 2, h / 2);\n c.beginPath();\n c.moveTo(x + rr, y);\n c.arcTo(x + w, y, x + w, y + h, rr);\n c.arcTo(x + w, y + h, x, y + h, rr);\n c.arcTo(x, y + h, x, y, rr);\n c.arcTo(x, y, x + w, y, rr);\n c.closePath();\n}\n\nfunction chordRibbonLayer(): Layer<ChordRibbonProps> {\n let track: ChordSpan[] = [];\n let pxPerMs = DEFAULTS.pxPerMs;\n let playheadFrac = DEFAULTS.playheadFrac;\n let yProp: number | undefined;\n let height = DEFAULTS.height;\n let showFunction = DEFAULTS.showFunction;\n let colors: FunctionColors = DEFAULT_FUNCTION_COLORS;\n\n return {\n key: 'chord-ribbon',\n init(_ctx: RenderCtx, props) {\n track = (props.chordTrack ?? []).slice().sort((a, b) => a.startMs - b.startMs);\n pxPerMs = props.pxPerMs ?? DEFAULTS.pxPerMs;\n playheadFrac = props.playheadFrac ?? DEFAULTS.playheadFrac;\n yProp = props.y;\n height = props.height ?? DEFAULTS.height;\n showFunction = props.showFunction ?? DEFAULTS.showFunction;\n colors = props.colors ?? DEFAULT_FUNCTION_COLORS;\n },\n draw(ctx, tMs) {\n if (!track.length) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const left = sb.left;\n const right = sb.right;\n const span = right - left;\n const playX = left + span * playheadFrac;\n const y = yProp ?? (sb.bottom - 260);\n const h = height;\n const pad = 8;\n\n c.save();\n // Clip to the ribbon band so chips never spill outside the safe width.\n c.beginPath();\n c.rect(left, y - pad, span, h + pad * 2);\n c.clip();\n\n for (const s of track) {\n const x0 = playX + (s.startMs - tMs) * pxPerMs;\n const w = Math.max(36, (s.endMs - s.startMs) * pxPerMs - 6);\n if (x0 + w < left || x0 > right) continue; // off-band\n const active = tMs >= s.startMs && tMs < s.endMs;\n const played = tMs >= s.endMs;\n const fnCol = showFunction ? functionColor(s.fn, colors) : ctx.theme.accent;\n const chipAlpha = active ? 0.95 : played ? 0.28 : 0.55;\n\n roundRectPath(c, x0, y, w, h, 14);\n c.fillStyle = rgba(fnCol, chipAlpha);\n c.fill();\n if (active) {\n c.lineWidth = 3;\n c.strokeStyle = rgba(ctx.theme.paper ?? '#ffffff', 0.9);\n c.stroke();\n }\n\n const label = s.label ?? '';\n if (label) {\n c.fillStyle = rgba(ctx.theme.paper ?? '#ffffff', active ? 1 : 0.85);\n c.font = `${active ? 700 : 600} ${Math.round(h * 0.42)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(label, x0 + w / 2, y + h / 2);\n }\n }\n\n // The fixed \"now\" playhead tick.\n c.strokeStyle = rgba(ctx.theme.gold ?? '#d4af37', 0.95);\n c.lineWidth = 3;\n c.beginPath();\n c.moveTo(playX, y - pad);\n c.lineTo(playX, y + h + pad);\n c.stroke();\n c.restore();\n },\n };\n}\n\nexport const chordRibbonFactory: LayerFactory<ChordRibbonProps> = {\n key: 'chord-ribbon',\n create: chordRibbonLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['chord-ribbon: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs = validateChordTrack(p.chordTrack);\n if (p.pxPerMs != null && (typeof p.pxPerMs !== 'number' || p.pxPerMs <= 0))\n errs.push('chord-ribbon.pxPerMs must be a positive number');\n if (p.playheadFrac != null && (typeof p.playheadFrac !== 'number' || p.playheadFrac < 0 || p.playheadFrac > 1))\n errs.push('chord-ribbon.playheadFrac must be in [0,1]');\n if (p.y != null && typeof p.y !== 'number') errs.push('chord-ribbon.y must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('chord-ribbon.height must be a positive number');\n if (p.showFunction != null && typeof p.showFunction !== 'boolean')\n errs.push('chord-ribbon.showFunction must be a boolean');\n return errs;\n },\n};\n","// `progress-ring` layer (S6) — a small circular progress indicator that fills as\n// the clip plays, with an optional seconds-remaining countdown in the middle. A\n// retention primitive: a visible \"how much is left\" cue measurably keeps viewers\n// to the end of a short. Where `section-minimap` is a bottom progress BAR, this\n// is a compact corner RING — use whichever fits the layout.\n//\n// DATA SOURCE: total length. Default = ctx.score.durationMs; override with\n// `totalMs` (e.g. the full clip duration when there's no score, or to count the\n// excerpt rather than the trailing CTA). Progress = clamp(t / total, 0, 1).\n//\n// Screen-pinned chrome (drawn after the camera reset) so it stays put in the\n// corner regardless of any notation camera pan/zoom. Pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface ProgressRingProps {\n /** Total length, ms. Default ctx.score.durationMs. Required if no score. */\n totalMs?: number;\n /** Ring radius (px). Default 46. */\n radius?: number;\n /** Ring stroke thickness (px). Default 9. */\n thickness?: number;\n /** Centre x (px). Default safeBox.right - radius - 16. */\n cx?: number;\n /** Centre y (px). Default safeBox.top + radius + 16. */\n cy?: number;\n /** Filled-arc colour. Default theme.gold. */\n color?: string;\n /** Unfilled track colour. Default a faint paper tint. */\n trackColor?: string;\n /** Draw seconds-remaining in the centre. Default false. */\n showCountdown?: boolean;\n}\n\nconst DEFAULTS = { radius: 46, thickness: 9, showCountdown: false };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction progressRingLayer(): Layer<ProgressRingProps> {\n let totalMsProp: number | undefined;\n let radius = DEFAULTS.radius;\n let thickness = DEFAULTS.thickness;\n let cxProp: number | undefined;\n let cyProp: number | undefined;\n let color: string | undefined;\n let trackColor: string | undefined;\n let showCountdown = DEFAULTS.showCountdown;\n\n return {\n key: 'progress-ring',\n init(ctx: RenderCtx, props) {\n totalMsProp = props.totalMs ?? ctx.score?.durationMs;\n radius = props.radius ?? DEFAULTS.radius;\n thickness = props.thickness ?? DEFAULTS.thickness;\n cxProp = props.cx;\n cyProp = props.cy;\n color = props.color;\n trackColor = props.trackColor;\n showCountdown = props.showCountdown ?? DEFAULTS.showCountdown;\n },\n draw(ctx, tMs) {\n const total = totalMsProp;\n if (!total || total <= 0) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cx = cxProp ?? (sb.right - radius - 16);\n const cy = cyProp ?? (sb.top + radius + 16);\n const p = Math.min(1, Math.max(0, tMs / total));\n const start = -Math.PI / 2;\n const end = start + p * Math.PI * 2;\n const fill = color ?? ctx.theme.gold;\n const track = trackColor ?? rgba(ctx.theme.paper, 0.22);\n\n c.save();\n c.lineCap = 'round';\n // Track.\n c.beginPath();\n c.arc(cx, cy, radius, 0, Math.PI * 2);\n c.strokeStyle = track;\n c.lineWidth = thickness;\n c.stroke();\n // Filled arc.\n if (p > 0) {\n c.beginPath();\n c.arc(cx, cy, radius, start, end);\n c.strokeStyle = fill;\n c.lineWidth = thickness;\n c.stroke();\n }\n // Countdown text.\n if (showCountdown) {\n const remain = Math.max(0, Math.ceil((total - tMs) / 1000));\n c.fillStyle = ctx.theme.paper;\n c.font = `700 ${Math.round(radius * 0.7)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(String(remain), cx, cy + 1);\n }\n c.restore();\n },\n };\n}\n\nexport const progressRingFactory: LayerFactory<ProgressRingProps> = {\n key: 'progress-ring',\n create: progressRingLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['progress-ring: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.totalMs != null && (typeof p.totalMs !== 'number' || p.totalMs <= 0))\n errs.push('progress-ring.totalMs must be a positive number');\n if (p.radius != null && (typeof p.radius !== 'number' || p.radius <= 0))\n errs.push('progress-ring.radius must be a positive number');\n if (p.thickness != null && (typeof p.thickness !== 'number' || p.thickness <= 0))\n errs.push('progress-ring.thickness must be a positive number');\n if (p.cx != null && typeof p.cx !== 'number') errs.push('progress-ring.cx must be a number');\n if (p.cy != null && typeof p.cy !== 'number') errs.push('progress-ring.cy must be a number');\n if (p.color != null && typeof p.color !== 'string') errs.push('progress-ring.color must be a string');\n if (p.trackColor != null && typeof p.trackColor !== 'string') errs.push('progress-ring.trackColor must be a string');\n if (p.showCountdown != null && typeof p.showCountdown !== 'boolean')\n errs.push('progress-ring.showCountdown must be a boolean');\n return errs;\n },\n};\n","// `radial-spectrum` layer (S6) — the audio-reactive frequency bars wrapped into a\n// RING radiating from a center point, instead of the flat mirrored strip the\n// `spectrum` layer draws. Made for a portrait/album-art hero: the bars pulse\n// outward around the composer photo. Same level-source contract as `spectrum`\n// (host wires ctx.spectrum.byteFreq → AnalyserNode; deterministic synthetic\n// fallback otherwise), so it's a drop-in alternative visual.\n//\n// Pure fn of t (the synthetic fallback is a pure fn of t; with a live analyser\n// the captured frames follow the master bus exactly, like `spectrum`).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\n// ctx.spectrum (SpectrumInput) is augmented onto RenderCtx by spectrum.ts, which\n// is always loaded (registry imports it). We re-use that same provider here.\n\nconst SPEC_BARS = 64;\n\nexport interface RadialSpectrumProps {\n /** Number of bars around the ring. Default 64. */\n bars?: number;\n /** Center x as a fraction of W. Default 0.5. */\n cxFrac?: number;\n /** Center y as a fraction of H. Default 0.42. */\n cyFrac?: number;\n /** Inner radius (bar roots) as a fraction of min(W,H). Default 0.20. */\n innerFrac?: number;\n /** Max bar length as a fraction of min(W,H). Default 0.13. */\n maxLenFrac?: number;\n /** Bar fill at low magnitude. Default theme.accent. */\n colorLow?: string;\n /** Bar fill at high magnitude. Default theme.gold. */\n colorHigh?: string;\n /** Per-frame level provider (overrides ctx.spectrum). Pure fn of t for tests. */\n levelsFn?: (tMs: number, bands: number) => Float32Array | number[] | null;\n}\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n// rgb-space lerp between two #rrggbb colours (mirrors spectrum.ts lerpHex).\nfunction lerpHex(a: string, b: string, f: number): string {\n const pa = parseInt(a.slice(1), 16);\n const pb = parseInt(b.slice(1), 16);\n const r = Math.round(((pa >> 16) & 255) + (((pb >> 16) & 255) - ((pa >> 16) & 255)) * f);\n const g = Math.round(((pa >> 8) & 255) + (((pb >> 8) & 255) - ((pa >> 8) & 255)) * f);\n const bl = Math.round((pa & 255) + ((pb & 255) - (pa & 255)) * f);\n return `rgb(${r},${g},${bl})`;\n}\n\n// Deterministic synthetic magnitudes (mirrors spectrum.ts; pure fn of (tSec,band)).\nfunction syntheticMagnitude(tSec: number, b: number, n: number): number {\n const phase = tSec * 5 + b * 0.45;\n let m = 0.22 + 0.16 * Math.sin(phase) + 0.10 * Math.sin(phase * 1.7 + 1.2);\n m *= 0.5 + 0.5 * Math.sin((b / (n - 1)) * Math.PI);\n return m;\n}\n\n// Log-bin a byte-FFT into n normalized magnitudes (mirrors spectrum.ts).\nfunction binByteFreq(freq: Uint8Array, n: number): number[] | null {\n let peak = 0;\n for (let i = 0; i < freq.length; i++) if (freq[i] > peak) peak = freq[i];\n if (peak <= 4) return null;\n const lo = 2, hi = 440;\n const out: number[] = [];\n for (let b = 0; b < n; b++) {\n const f0 = lo * Math.pow(hi / lo, b / n);\n const f1 = lo * Math.pow(hi / lo, (b + 1) / n);\n const i0 = Math.floor(f0);\n const i1 = Math.max(i0 + 1, Math.floor(f1));\n let sum = 0, cnt = 0;\n for (let i = i0; i < i1 && i < freq.length; i++) { sum += freq[i]; cnt++; }\n let m = cnt ? (sum / cnt) / 255 : 0;\n m = Math.pow(m, 0.78);\n out.push(m);\n }\n return out;\n}\n\nfunction radialSpectrumLayer(): Layer<RadialSpectrumProps> {\n let n = SPEC_BARS;\n let cxFrac = 0.5, cyFrac = 0.42, innerFrac = 0.20, maxLenFrac = 0.13;\n let colorLow: string | undefined, colorHigh: string | undefined;\n let levelsFn: RadialSpectrumProps['levelsFn'];\n\n function magnitudesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = levelsFn?.(tMs, n);\n const src = fromProp ?? resolveFromCtx(ctx, tMs);\n if (src && src.length) {\n const out = new Array(n);\n for (let b = 0; b < n; b++) out[b] = clamp01(src[Math.min(src.length - 1, b)] ?? 0);\n return out;\n }\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, b) => clamp01(syntheticMagnitude(tSec, b, n)));\n }\n\n function resolveFromCtx(ctx: RenderCtx, tMs: number): number[] | null {\n const sp = ctx.spectrum;\n if (!sp) return null;\n const lv = sp.levels?.(tMs, n);\n if (lv && lv.length) return Array.from(lv as ArrayLike<number>);\n const bf = sp.byteFreq?.(tMs);\n if (bf && bf.length) return binByteFreq(bf, n);\n return null;\n }\n\n return {\n key: 'radial-spectrum',\n init(_ctx: RenderCtx, props) {\n n = props.bars ?? SPEC_BARS;\n cxFrac = props.cxFrac ?? 0.5;\n cyFrac = props.cyFrac ?? 0.42;\n innerFrac = props.innerFrac ?? 0.20;\n maxLenFrac = props.maxLenFrac ?? 0.13;\n colorLow = props.colorLow;\n colorHigh = props.colorHigh;\n levelsFn = props.levelsFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const cx = W * cxFrac, cy = H * cyFrac;\n const unit = Math.min(W, H);\n const inner = unit * innerFrac;\n const maxLen = unit * maxLenFrac;\n const lo = colorLow ?? ctx.theme.accent;\n const hi = colorHigh ?? ctx.theme.gold;\n const mags = magnitudesAt(ctx, tMs);\n const barW = Math.max(3, (2 * Math.PI * inner) / n * 0.6);\n\n c.save();\n c.lineCap = 'round';\n for (let b = 0; b < n; b++) {\n const m = mags[b];\n const ang = (b / n) * Math.PI * 2 - Math.PI / 2;\n const len = Math.max(barW * 0.5, m * maxLen);\n const x0 = cx + Math.cos(ang) * inner;\n const y0 = cy + Math.sin(ang) * inner;\n const x1 = cx + Math.cos(ang) * (inner + len);\n const y1 = cy + Math.sin(ang) * (inner + len);\n c.strokeStyle = lerpHex(lo, hi, m);\n c.lineWidth = barW;\n c.beginPath();\n c.moveTo(x0, y0);\n c.lineTo(x1, y1);\n c.stroke();\n }\n c.restore();\n },\n };\n}\n\nexport const radialSpectrumFactory: LayerFactory<RadialSpectrumProps> = {\n key: 'radial-spectrum',\n create: radialSpectrumLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['radial-spectrum: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.bars != null && (typeof p.bars !== 'number' || p.bars < 6)) errs.push('radial-spectrum.bars must be a number >= 6');\n for (const k of ['cxFrac', 'cyFrac', 'innerFrac', 'maxLenFrac'] as const) {\n if (p[k] != null && (typeof p[k] !== 'number' || (p[k] as number) < 0 || (p[k] as number) > 1))\n errs.push(`radial-spectrum.${k} must be in [0,1]`);\n }\n if (p.colorLow != null && typeof p.colorLow !== 'string') errs.push('radial-spectrum.colorLow must be a string');\n if (p.colorHigh != null && typeof p.colorHigh !== 'string') errs.push('radial-spectrum.colorHigh must be a string');\n if (p.levelsFn != null && typeof p.levelsFn !== 'function') errs.push('radial-spectrum.levelsFn must be a function');\n return errs;\n },\n};\n","// `karaoke-caption` layer (S6) — a caption that fills word-by-word in time, the\n// bouncing-ball / lyric-video effect. Where the `caption` layer shows a whole\n// cue at once, this lights each word as it's \"spoken\" so the eye tracks the\n// reading — strong for narrated fun-facts, hooks, and lyric reels (and it keeps\n// viewers reading = watching).\n//\n// DATA: a list of timed words (`words: {text, atMs}[]`); each word turns\n// \"active\" at its atMs and stays lit. Host supplies the timings (TTS word marks,\n// an even split across a narration's duration, or hand-authored). The layer\n// wraps the words to the safe width and centres the block; pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface KaraokeWord {\n /** The word (no surrounding spaces). */\n text: string;\n /** Time this word lights up, ms. */\n atMs: number;\n}\n\nexport interface KaraokeCaptionProps {\n /** REQUIRED: timed words, ascending by atMs. */\n words: KaraokeWord[];\n /** Vertical center as a fraction of H. Default 0.5. */\n centerFrac?: number;\n /** Font px. Default 64. */\n fontPx?: number;\n /** Colour of already-sung words. Default theme.ink. */\n sungColor?: string;\n /** Colour of not-yet-sung words. Default a faint ink. */\n upcomingColor?: string;\n /** Highlight colour of the word that just lit. Default theme.accent. */\n activeColor?: string;\n /** ms a freshly-lit word stays in the active highlight colour. Default 320. */\n activeHoldMs?: number;\n}\n\nconst DEFAULTS = { centerFrac: 0.5, fontPx: 64, activeHoldMs: 320 };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((ch) => ch + ch).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction karaokeCaptionLayer(): Layer<KaraokeCaptionProps> {\n let words: KaraokeWord[] = [];\n let centerFrac = DEFAULTS.centerFrac;\n let fontPx = DEFAULTS.fontPx;\n let sungColor: string | undefined;\n let upcomingColor: string | undefined;\n let activeColor: string | undefined;\n let activeHoldMs = DEFAULTS.activeHoldMs;\n\n return {\n key: 'karaoke-caption',\n init(_ctx: RenderCtx, props) {\n words = (props.words ?? []).slice().sort((a, b) => a.atMs - b.atMs);\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n sungColor = props.sungColor;\n upcomingColor = props.upcomingColor;\n activeColor = props.activeColor;\n activeHoldMs = props.activeHoldMs ?? DEFAULTS.activeHoldMs;\n },\n draw(ctx, tMs) {\n if (!words.length) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n c.save();\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textBaseline = 'middle';\n c.textAlign = 'left';\n\n const spaceW = c.measureText(' ').width;\n const maxW = sb.w;\n // Lay out words into lines (greedy wrap to the safe width).\n const lines: { items: { w: KaraokeWord; width: number }[]; width: number }[] = [];\n let cur: { items: { w: KaraokeWord; width: number }[]; width: number } = { items: [], width: 0 };\n for (const w of words) {\n const ww = c.measureText(w.text).width;\n const add = cur.items.length ? spaceW + ww : ww;\n if (cur.items.length && cur.width + add > maxW) {\n lines.push(cur);\n cur = { items: [], width: 0 };\n }\n cur.items.push({ w, width: ww });\n cur.width += cur.items.length === 1 ? ww : spaceW + ww;\n }\n if (cur.items.length) lines.push(cur);\n\n const lineH = fontPx * 1.3;\n const totalH = lines.length * lineH;\n let y = H_center(ctx, centerFrac) - totalH / 2 + lineH / 2;\n\n const sung = sungColor ?? ctx.theme.ink;\n const upcoming = upcomingColor ?? rgba(ctx.theme.ink, 0.28);\n const active = activeColor ?? ctx.theme.accent;\n\n for (const line of lines) {\n let x = sb.left + (maxW - line.width) / 2; // center the line\n for (const it of line.items) {\n const lit = tMs >= it.w.atMs;\n const fresh = lit && tMs - it.w.atMs < activeHoldMs;\n c.fillStyle = fresh ? active : lit ? sung : upcoming;\n c.fillText(it.w.text, x, y);\n x += it.width + spaceW;\n }\n y += lineH;\n }\n c.restore();\n },\n };\n}\n\nfunction H_center(ctx: RenderCtx, frac: number): number { return ctx.H * frac; }\n\nexport const karaokeCaptionFactory: LayerFactory<KaraokeCaptionProps> = {\n key: 'karaoke-caption',\n create: karaokeCaptionLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['karaoke-caption: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.words)) {\n errs.push('karaoke-caption.words must be an array of {text, atMs}');\n } else {\n p.words.forEach((raw, i) => {\n const w = raw as Record<string, unknown>;\n if (typeof w?.text !== 'string' || typeof w?.atMs !== 'number')\n errs.push(`karaoke-caption.words[${i}] must be {text:string, atMs:number}`);\n });\n }\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1))\n errs.push('karaoke-caption.centerFrac must be in [0,1]');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0))\n errs.push('karaoke-caption.fontPx must be a positive number');\n if (p.activeHoldMs != null && (typeof p.activeHoldMs !== 'number' || p.activeHoldMs < 0))\n errs.push('karaoke-caption.activeHoldMs must be a number >= 0');\n for (const k of ['sungColor', 'upcomingColor', 'activeColor'] as const) {\n if (p[k] != null && typeof p[k] !== 'string') errs.push(`karaoke-caption.${k} must be a string`);\n }\n return errs;\n },\n};\n","// `interval-arcs` layer (S6) — draws a labeled arc between each pair of\n// consecutive melody notes (e.g. \"P5\", \"M3\", \"m2\"), revealing each arc as the\n// second note sounds. The ear-training primitive: it names the melodic intervals\n// the listener is hearing, in time. Pairs naturally with RealEarTrainer content.\n//\n// DATA SOURCE: Score.notes only — the melody = highest sounding pitch per onset\n// (the same soprano-line heuristic as `pitch-contour`, reused via contourPoints).\n// Interval names come from web-core's intervals table (intervalBySemitones).\n//\n// Sync (pure fn of t): an arc for the pair (i-1, i) is drawn once note i has\n// sounded (its onset ≤ t); earlier arcs stay. Laid out left→right by onset time,\n// pitch→y within a band (same projection as pitch-contour).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n contourPoints,\n pitchRange,\n projectPoint,\n type ContourPoint,\n type ContourPlot,\n} from '../pitchContour';\nimport { intervalBySemitones } from '../../intervals';\n\nexport interface IntervalArcsProps {\n /** Plot band top (world y). Default safeBox.top + safeBox.h * 0.18. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h * 0.34. */\n height?: number;\n /** Arc colour for an ascending interval. Default theme.accent. */\n upColor?: string;\n /** Arc colour for a descending interval. Default theme.sepia. */\n downColor?: string;\n /** Draw the interval label on each arc. Default true. */\n showLabels?: boolean;\n /** Label font px. Default 30. */\n fontPx?: number;\n /** Note-dot radius (world px). Default 9. */\n dotRadius?: number;\n}\n\n/** Interval label for a signed semitone delta (reduces compound to within-octave + P8). */\nfunction intervalLabel(deltaSemis: number): string {\n const semi = Math.abs(deltaSemis);\n const within = semi % 12;\n if (semi > 0 && within === 0) return 'P8';\n return intervalBySemitones(within)?.label ?? `${within}`;\n}\n\nfunction intervalArcsLayer(): Layer<IntervalArcsProps> {\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let upColor: string | undefined;\n let downColor: string | undefined;\n let showLabels = true;\n let fontPx = 30;\n let dotRadius = 9;\n let points: ContourPoint[] = [];\n\n return {\n key: 'interval-arcs',\n init(ctx, props) {\n topProp = props.top;\n heightProp = props.height;\n upColor = props.upColor;\n downColor = props.downColor;\n showLabels = props.showLabels ?? true;\n fontPx = props.fontPx ?? 30;\n dotRadius = props.dotRadius ?? 9;\n points = contourPoints(ctx.score?.notes ?? []);\n },\n draw(ctx, tMs) {\n if (points.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const top = topProp ?? sb.top + sb.h * 0.18;\n const height = heightProp ?? sb.h * 0.34;\n const range = pitchRange(points);\n const plot: ContourPlot = {\n left: sb.left,\n right: sb.right,\n top,\n bottom: top + height,\n minPitch: range.min,\n maxPitch: range.max,\n durationMs: ctx.score?.durationMs ?? points[points.length - 1].tMs,\n };\n const up = upColor ?? ctx.theme.accent;\n const down = downColor ?? ctx.theme.sepia;\n\n c.save();\n c.lineCap = 'round';\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'bottom';\n\n // Arcs for each consecutive pair whose 2nd note has sounded.\n for (let i = 1; i < points.length; i++) {\n const b = points[i];\n if (b.tMs > tMs) break; // not yet revealed (points are onset-sorted)\n const a = points[i - 1];\n const pa = projectPoint(plot, a.tMs, a.pitchMidi);\n const pb = projectPoint(plot, b.tMs, b.pitchMidi);\n const delta = b.pitchMidi - a.pitchMidi;\n const ascending = delta >= 0;\n const col = ascending ? up : down;\n // Quadratic arc bowing AWAY from the band center (up for ascending).\n const midX = (pa.x + pb.x) / 2;\n const bow = Math.min(70, Math.max(28, Math.abs(pb.x - pa.x) * 0.4));\n const ctrlY = Math.min(pa.y, pb.y) - bow;\n\n c.strokeStyle = col;\n c.lineWidth = 4;\n c.globalAlpha = 0.9;\n c.beginPath();\n c.moveTo(pa.x, pa.y);\n c.quadraticCurveTo(midX, ctrlY, pb.x, pb.y);\n c.stroke();\n\n if (showLabels && delta !== 0) {\n c.globalAlpha = 1;\n c.fillStyle = col;\n c.fillText(intervalLabel(delta), midX, ctrlY - 2);\n }\n }\n\n // Note dots (only those sounded).\n c.globalAlpha = 1;\n c.fillStyle = ctx.theme.ink;\n for (const p of points) {\n if (p.tMs > tMs) break;\n const pt = projectPoint(plot, p.tMs, p.pitchMidi);\n c.beginPath();\n c.arc(pt.x, pt.y, dotRadius, 0, Math.PI * 2);\n c.fill();\n }\n c.restore();\n },\n };\n}\n\nexport const intervalArcsFactory: LayerFactory<IntervalArcsProps> = {\n key: 'interval-arcs',\n create: intervalArcsLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['interval-arcs: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.top != null && typeof p.top !== 'number') errs.push('interval-arcs.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('interval-arcs.height must be a positive number');\n if (p.upColor != null && typeof p.upColor !== 'string') errs.push('interval-arcs.upColor must be a string');\n if (p.downColor != null && typeof p.downColor !== 'string') errs.push('interval-arcs.downColor must be a string');\n if (p.showLabels != null && typeof p.showLabels !== 'boolean') errs.push('interval-arcs.showLabels must be a boolean');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('interval-arcs.fontPx must be a positive number');\n if (p.dotRadius != null && (typeof p.dotRadius !== 'number' || p.dotRadius <= 0)) errs.push('interval-arcs.dotRadius must be a positive number');\n return errs;\n },\n};\n","// `kinetic-text` layer (S6) — an animated text intro: the words/characters\n// arrive over time instead of all at once. The first ~2s of a short decide\n// whether anyone watches; kinetic typography on the hook is the strongest\n// retention lever we have, and our hook cards are currently static.\n//\n// MODES:\n// - \"typewriter\": characters reveal left→right across `revealMs`, optional\n// blinking cursor.\n// - \"word-pop\": each word fades + scales in, staggered evenly across `revealMs`.\n//\n// DATA: a `text` string (may contain \"\\n\" for explicit line breaks); otherwise\n// it wraps to the safe width. Pure fn of t (cursor blink is a pure fn of t too).\n// Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport type KineticMode = 'typewriter' | 'word-pop';\n\nexport interface KineticTextProps {\n /** REQUIRED: the text. \"\\n\" forces a line break; otherwise wraps to safe width. */\n text: string;\n /** Animation style. Default \"word-pop\". */\n mode?: KineticMode;\n /** When the animation starts (ms). Default 0. */\n startMs?: number;\n /** Time to fully reveal (ms). Default 1400. */\n revealMs?: number;\n /** Font px. Default 88. */\n fontPx?: number;\n /** Text colour. Default theme.ink. */\n color?: string;\n /** Vertical center as a fraction of H. Default 0.42. */\n centerFrac?: number;\n /** Typewriter blinking cursor. Default true (ignored in word-pop). */\n cursor?: boolean;\n}\n\nconst DEFAULTS = { mode: 'word-pop' as KineticMode, startMs: 0, revealMs: 1400, fontPx: 88, centerFrac: 0.42, cursor: true };\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n/** Greedy-wrap a list of words to maxW using the current ctx font. */\nfunction wrap(c: CanvasRenderingContext2D, words: string[], maxW: number): string[][] {\n const lines: string[][] = [];\n let cur: string[] = [];\n let curW = 0;\n const spaceW = c.measureText(' ').width;\n for (const w of words) {\n const ww = c.measureText(w).width;\n const add = cur.length ? spaceW + ww : ww;\n if (cur.length && curW + add > maxW) { lines.push(cur); cur = []; curW = 0; }\n cur.push(w);\n curW += cur.length === 1 ? ww : spaceW + ww;\n }\n if (cur.length) lines.push(cur);\n return lines;\n}\n\nfunction kineticTextLayer(): Layer<KineticTextProps> {\n let text = '';\n let mode = DEFAULTS.mode;\n let startMs = DEFAULTS.startMs;\n let revealMs = DEFAULTS.revealMs;\n let fontPx = DEFAULTS.fontPx;\n let color: string | undefined;\n let centerFrac = DEFAULTS.centerFrac;\n let cursor = DEFAULTS.cursor;\n\n return {\n key: 'kinetic-text',\n init(_ctx: RenderCtx, props) {\n text = props.text ?? '';\n mode = props.mode ?? DEFAULTS.mode;\n startMs = props.startMs ?? DEFAULTS.startMs;\n revealMs = props.revealMs ?? DEFAULTS.revealMs;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n color = props.color;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n cursor = props.cursor ?? DEFAULTS.cursor;\n },\n draw(ctx, tMs) {\n if (!text) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const col = color ?? ctx.theme.ink;\n c.save();\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textBaseline = 'middle';\n\n // Lines: explicit \"\\n\" first, then wrap each to the safe width.\n const rawLines = text.split('\\n');\n const lines: string[][] = [];\n for (const rl of rawLines) lines.push(...wrap(c, rl.split(/\\s+/).filter(Boolean), sb.w));\n\n const lineH = fontPx * 1.25;\n const totalH = lines.length * lineH;\n const top = ctx.H * centerFrac - totalH / 2 + lineH / 2;\n const p = clamp01((tMs - startMs) / Math.max(1, revealMs));\n\n if (mode === 'typewriter') {\n const full = lines.map((l) => l.join(' '));\n const totalChars = full.reduce((n, l) => n + l.length, 0);\n let shown = Math.floor(p * totalChars);\n c.textAlign = 'center';\n c.fillStyle = col;\n let y = top;\n let lastDrawn = { x: 0, y: 0, end: false };\n for (const line of full) {\n const take = Math.max(0, Math.min(line.length, shown));\n const sub = line.slice(0, take);\n if (sub) c.fillText(sub, ctx.W / 2, y);\n shown -= line.length;\n if (take < line.length && take >= 0 && !lastDrawn.end) {\n // cursor sits at the end of the partially-typed line\n lastDrawn = { x: ctx.W / 2 + c.measureText(sub).width / 2, y, end: true };\n }\n y += lineH;\n }\n if (cursor && p < 1 && (Math.floor(tMs / 450) % 2 === 0)) {\n c.fillRect(lastDrawn.x + 6, lastDrawn.y - fontPx * 0.42, 6, fontPx * 0.8);\n }\n } else {\n // word-pop: stagger words across revealMs; each pops over ~280ms.\n const allWords = lines.flat();\n const n = allWords.length || 1;\n const stagger = revealMs / n;\n const POP = 280;\n c.textAlign = 'left';\n const spaceW = c.measureText(' ').width;\n let wi = 0;\n let y = top;\n for (const line of lines) {\n const lineW = line.reduce((w, word, i) => w + c.measureText(word).width + (i ? spaceW : 0), 0);\n let x = sb.left + (sb.w - lineW) / 2;\n for (const word of line) {\n const ww = c.measureText(word).width;\n const wp = clamp01((tMs - startMs - wi * stagger) / POP);\n if (wp > 0) {\n const scale = 0.7 + 0.3 * wp;\n c.save();\n c.globalAlpha = wp;\n c.translate(x + ww / 2, y);\n c.scale(scale, scale);\n c.fillStyle = col;\n c.fillText(word, -ww / 2, 0);\n c.restore();\n }\n x += ww + spaceW;\n wi++;\n }\n y += lineH;\n }\n }\n c.restore();\n },\n };\n}\n\nexport const kineticTextFactory: LayerFactory<KineticTextProps> = {\n key: 'kinetic-text',\n create: kineticTextLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['kinetic-text: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.text !== 'string' || !p.text) errs.push('kinetic-text.text must be a non-empty string');\n if (p.mode != null && p.mode !== 'typewriter' && p.mode !== 'word-pop') errs.push('kinetic-text.mode must be \"typewriter\" | \"word-pop\"');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('kinetic-text.startMs must be a number');\n if (p.revealMs != null && (typeof p.revealMs !== 'number' || p.revealMs <= 0)) errs.push('kinetic-text.revealMs must be a positive number');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('kinetic-text.fontPx must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('kinetic-text.color must be a string');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('kinetic-text.centerFrac must be in [0,1]');\n if (p.cursor != null && typeof p.cursor !== 'boolean') errs.push('kinetic-text.cursor must be a boolean');\n return errs;\n },\n};\n","// `countdown` layer (S6) — a big \"3 · 2 · 1\" tick before a reveal. In a\n// guess-the-answer format (whozart \"name the piece\", RET \"name the leap\", RMT\n// concept checks) a visible countdown into the reveal builds tension and tells\n// the viewer \"answer's coming — stay\" — a small but real retention beat.\n//\n// Pure fn of t: the active number = `from - floor((t-startMs)/perTick)`, shown\n// only inside [startMs, startMs+durationMs). Each tick pops (a quick scale\n// overshoot) and an optional ring drains to show time left in the tick.\n// Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface CountdownProps {\n /** Count from this number down to 1. Default 3. */\n from?: number;\n /** When the countdown starts (ms). Default 0. */\n startMs?: number;\n /** Total countdown window (ms); each number shows durationMs/from. Default 3000. */\n durationMs?: number;\n /** Number colour. Default theme.accent. */\n color?: string;\n /** Number font px. Default 280. */\n fontPx?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Draw a draining ring around the number. Default true. */\n ring?: boolean;\n /** Ring radius in px. Default = fontPx*0.62. Set explicitly to align the ring\n * with another circular element (e.g. whozart's radial ring / portrait). */\n ringRadius?: number;\n}\n\nconst DEFAULTS = { from: 3, startMs: 0, durationMs: 3000, fontPx: 280, centerFrac: 0.46, ring: true };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction countdownLayer(): Layer<CountdownProps> {\n let from = DEFAULTS.from;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let color: string | undefined;\n let fontPx = DEFAULTS.fontPx;\n let centerFrac = DEFAULTS.centerFrac;\n let ring = DEFAULTS.ring;\n let ringRadiusProp: number | undefined;\n\n return {\n key: 'countdown',\n init(_ctx: RenderCtx, props) {\n from = props.from ?? DEFAULTS.from;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n color = props.color;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n ring = props.ring ?? DEFAULTS.ring;\n ringRadiusProp = props.ringRadius;\n },\n draw(ctx, tMs) {\n const elapsed = tMs - startMs;\n if (elapsed < 0 || elapsed >= durationMs) return;\n const perTick = durationMs / from;\n const idx = Math.floor(elapsed / perTick);\n const n = from - idx;\n if (n < 1 || n > from) return;\n const tickElapsed = elapsed - idx * perTick;\n const tickFrac = tickElapsed / perTick; // 0→1 within this tick\n\n const c = ctx.ctx2d;\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n // Pop: quick overshoot at the start of each tick, settling to 1.\n const pop = 1 + 0.28 * Math.exp(-tickElapsed / 120);\n const fade = 1 - Math.max(0, (tickFrac - 0.7) / 0.3) * 0.85; // fade in last 30%\n\n c.save();\n if (ring) {\n const r = ringRadiusProp ?? fontPx * 0.62;\n c.lineWidth = 10;\n c.lineCap = 'round';\n c.strokeStyle = rgba(col, 0.18 * fade);\n c.beginPath(); c.arc(cx, cy, r, 0, Math.PI * 2); c.stroke();\n // Drains clockwise from full to empty across the tick.\n c.strokeStyle = rgba(col, 0.9 * fade);\n c.beginPath();\n c.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + (1 - tickFrac) * Math.PI * 2);\n c.stroke();\n }\n c.globalAlpha = fade;\n c.fillStyle = col;\n c.font = `800 ${Math.round(fontPx * pop)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(String(n), cx, cy + 2);\n c.restore();\n },\n };\n}\n\nexport const countdownFactory: LayerFactory<CountdownProps> = {\n key: 'countdown',\n create: countdownLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['countdown: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.from != null && (typeof p.from !== 'number' || p.from < 1 || !Number.isInteger(p.from))) errs.push('countdown.from must be an integer >= 1');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('countdown.startMs must be a number');\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || p.durationMs <= 0)) errs.push('countdown.durationMs must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('countdown.color must be a string');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('countdown.fontPx must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('countdown.centerFrac must be in [0,1]');\n if (p.ring != null && typeof p.ring !== 'boolean') errs.push('countdown.ring must be a boolean');\n if (p.ringRadius != null && (typeof p.ringRadius !== 'number' || p.ringRadius <= 0)) errs.push('countdown.ringRadius must be a positive number');\n return errs;\n },\n};\n","// `waveform` layer (S6) — a live oscilloscope (time-domain amplitude trace),\n// the classic audio-viz that complements the FFT `spectrum`/`radial-spectrum`.\n// Same host-provided-input + deterministic-fallback pattern as `spectrum`: the\n// host wires `ctx.waveform.byteTime` → AnalyserNode.getByteTimeDomainData; with\n// none, the layer animates a deterministic synthetic wave (pure fn of t → tests\n// + offline captures reproduce).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\n/** Host-provided time-domain sample source (AnalyserNode shape). */\nexport interface WaveformInput {\n /** 0..255 samples centered ~128 (AnalyserNode.getByteTimeDomainData). */\n byteTime?(tMs: number): Uint8Array | null | undefined;\n}\n\ndeclare module '../layer' {\n interface RenderCtx {\n /** Optional time-domain source for the waveform layer (host-wired). */\n waveform?: WaveformInput;\n }\n}\n\nconst SAMPLES = 128;\n\nexport interface WaveformProps {\n /** Number of samples plotted. Default 128. */\n samples?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Max deflection as a fraction of H. Default 0.10. */\n amplitudeFrac?: number;\n /** Line width px. Default 5. */\n lineWidth?: number;\n /** Line colour. Default theme.accent. */\n color?: string;\n /** Per-frame sample provider returning -1..1 (overrides ctx.waveform). */\n samplesFn?: (tMs: number, n: number) => Float32Array | number[] | null;\n}\n\nfunction clampUnit(x: number): number { return x < -1 ? -1 : x > 1 ? 1 : x; }\n\n/** Deterministic synthetic wave: a few detuned sines that drift with t. Pure. */\nfunction syntheticSample(tSec: number, u: number): number {\n const phase = u * Math.PI * 2;\n return 0.55 * Math.sin(phase * 3 + tSec * 6)\n + 0.28 * Math.sin(phase * 5 + tSec * 9 + 1.1)\n + 0.14 * Math.sin(phase * 8 + tSec * 4 + 2.3);\n}\n\nfunction waveformLayer(): Layer<WaveformProps> {\n let n = SAMPLES;\n let centerFrac = 0.46;\n let amplitudeFrac = 0.10;\n let lineWidth = 5;\n let color: string | undefined;\n let samplesFn: WaveformProps['samplesFn'];\n\n function samplesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = samplesFn?.(tMs, n);\n if (fromProp && fromProp.length) {\n return Array.from({ length: n }, (_, i) => clampUnit(fromProp[Math.min(fromProp.length - 1, i)] ?? 0));\n }\n const bt = ctx.waveform?.byteTime?.(tMs);\n if (bt && bt.length) {\n return Array.from({ length: n }, (_, i) => clampUnit((bt[Math.floor((i / n) * bt.length)] - 128) / 128));\n }\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, i) => clampUnit(syntheticSample(tSec, i / (n - 1))));\n }\n\n return {\n key: 'waveform',\n init(_ctx: RenderCtx, props) {\n n = props.samples ?? SAMPLES;\n centerFrac = props.centerFrac ?? 0.46;\n amplitudeFrac = props.amplitudeFrac ?? 0.10;\n lineWidth = props.lineWidth ?? 5;\n color = props.color;\n samplesFn = props.samplesFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const left = sb.left, right = sb.right;\n const cy = ctx.H * centerFrac;\n const amp = ctx.H * amplitudeFrac;\n const s = samplesAt(ctx, tMs);\n c.save();\n c.lineJoin = 'round';\n c.lineCap = 'round';\n c.strokeStyle = color ?? ctx.theme.accent;\n c.lineWidth = lineWidth;\n c.beginPath();\n for (let i = 0; i < n; i++) {\n const x = left + (right - left) * (i / (n - 1));\n const y = cy + s[i] * amp;\n if (i === 0) c.moveTo(x, y); else c.lineTo(x, y);\n }\n c.stroke();\n c.restore();\n },\n };\n}\n\nexport const waveformFactory: LayerFactory<WaveformProps> = {\n key: 'waveform',\n create: waveformLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['waveform: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.samples != null && (typeof p.samples !== 'number' || p.samples < 8)) errs.push('waveform.samples must be a number >= 8');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('waveform.centerFrac must be in [0,1]');\n if (p.amplitudeFrac != null && (typeof p.amplitudeFrac !== 'number' || p.amplitudeFrac <= 0)) errs.push('waveform.amplitudeFrac must be a positive number');\n if (p.lineWidth != null && (typeof p.lineWidth !== 'number' || p.lineWidth <= 0)) errs.push('waveform.lineWidth must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('waveform.color must be a string');\n if (p.samplesFn != null && typeof p.samplesFn !== 'function') errs.push('waveform.samplesFn must be a function');\n return errs;\n },\n};\n","// `scale-highlight` layer (S6) — lights up the notes of a scale/mode on the\n// keyboard and names it (\"D Dorian\"). The teaching primitive for \"what notes are\n// in this key\": it rides the keyboard layer's published layout (keyboardStore)\n// and tints every key whose pitch-class is in the scale, the tonic distinctly.\n//\n// DATA: a `root` (note name or pitch-class) + a `scale`/mode name → a pitch-class\n// set from the interval table below. No Score needed. If a keyboard layer has\n// published a layout it tints the keys; otherwise it just draws the name label.\n// World-space (must be stacked AFTER the keyboard); pure fn of t (static).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { keyRect, inRange } from '../keyboardGeometry';\nimport { noteNameToIndex, pitchClass } from '../../notes';\n\n/** Semitone interval sets (from the root) for the supported scales/modes. */\nexport const SCALE_INTERVALS: Record<string, number[]> = {\n major: [0, 2, 4, 5, 7, 9, 11],\n ionian: [0, 2, 4, 5, 7, 9, 11],\n minor: [0, 2, 3, 5, 7, 8, 10],\n aeolian: [0, 2, 3, 5, 7, 8, 10],\n dorian: [0, 2, 3, 5, 7, 9, 10],\n phrygian: [0, 1, 3, 5, 7, 8, 10],\n lydian: [0, 2, 4, 6, 7, 9, 11],\n mixolydian: [0, 2, 4, 5, 7, 9, 10],\n locrian: [0, 1, 3, 5, 6, 8, 10],\n harmonicMinor: [0, 2, 3, 5, 7, 8, 11],\n melodicMinor: [0, 2, 3, 5, 7, 9, 11],\n pentatonicMajor: [0, 2, 4, 7, 9],\n pentatonicMinor: [0, 3, 5, 7, 10],\n blues: [0, 3, 5, 6, 7, 10],\n chromatic: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],\n};\n\nconst PRETTY: Record<string, string> = {\n major: 'Major', ionian: 'Ionian', minor: 'Minor', aeolian: 'Aeolian', dorian: 'Dorian',\n phrygian: 'Phrygian', lydian: 'Lydian', mixolydian: 'Mixolydian', locrian: 'Locrian',\n harmonicMinor: 'Harmonic Minor', melodicMinor: 'Melodic Minor',\n pentatonicMajor: 'Pentatonic', pentatonicMinor: 'Minor Pentatonic', blues: 'Blues', chromatic: 'Chromatic',\n};\n\nexport interface ScaleHighlightProps {\n /** Tonic — note name ('C','D','Bb'…) or pitch-class number 0..11. Default 'C'. */\n root?: string | number;\n /** Scale/mode key (see SCALE_INTERVALS). Default 'major'. */\n scale?: string;\n /** Colour for in-scale keys. Default theme.accent. */\n scaleColor?: string;\n /** Colour for the tonic key. Default theme.gold. */\n tonicColor?: string;\n /** Tint opacity over the key. Default 0.55. */\n alpha?: number;\n /** Draw the scale name label. Default true. */\n label?: boolean;\n /** Override the label text. Default `${root} ${PrettyScale}`. */\n labelText?: string;\n /** Label vertical position as a fraction of H. Default near the safe-box top.\n * Set this when the host already draws a top title so the name doesn't collide. */\n labelCenterFrac?: number;\n}\n\nfunction rootPc(root: string | number): number {\n if (typeof root === 'number') return ((root % 12) + 12) % 12;\n return noteNameToIndex(root);\n}\n\nfunction scaleHighlightLayer(): Layer<ScaleHighlightProps> {\n let rootProp: string | number = 'C';\n let scaleKey = 'major';\n let scaleColor: string | undefined;\n let tonicColor: string | undefined;\n let alpha = 0.55;\n let label = true;\n let labelTextProp: string | undefined;\n let labelCenterFrac: number | undefined;\n\n return {\n key: 'scale-highlight',\n init(_ctx: RenderCtx, props) {\n rootProp = props.root ?? 'C';\n scaleKey = props.scale ?? 'major';\n scaleColor = props.scaleColor;\n tonicColor = props.tonicColor;\n alpha = props.alpha ?? 0.55;\n label = props.label ?? true;\n labelTextProp = props.labelText;\n labelCenterFrac = props.labelCenterFrac;\n },\n draw(ctx, _tMs) {\n const c = ctx.ctx2d;\n const intervals = SCALE_INTERVALS[scaleKey] ?? SCALE_INTERVALS.major;\n const root = rootPc(rootProp);\n const inScale = new Set(intervals.map((iv) => (root + iv) % 12));\n const sc = scaleColor ?? ctx.theme.accent;\n const tc = tonicColor ?? ctx.theme.gold;\n\n const layout = getKeyboardLayout(ctx);\n if (layout) {\n // Clean look: a solid PIP near the bottom of each in-scale key (not a\n // muddy full-key wash) — reads clearly and coexists with an app's own\n // key colouring. Tonic = bigger pip with a white core.\n c.save();\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) {\n if (!inRange(layout, m)) continue;\n const pc = pitchClass(m);\n if (!inScale.has(pc)) continue;\n const r = keyRect(layout, m);\n const isTonic = pc === root;\n const cx = r.x + r.w / 2;\n const pipR = (r.black ? r.w * 0.5 : r.w * 0.32) * (isTonic ? 1.25 : 1);\n const cy = r.y + r.h - pipR - (r.black ? 8 : 16);\n c.globalAlpha = Math.min(1, alpha + 0.4); // pips read better near-solid\n c.fillStyle = isTonic ? tc : sc;\n c.beginPath(); c.arc(cx, cy, pipR, 0, Math.PI * 2); c.fill();\n if (isTonic) {\n c.fillStyle = '#ffffff';\n c.beginPath(); c.arc(cx, cy, pipR * 0.42, 0, Math.PI * 2); c.fill();\n }\n }\n c.restore();\n }\n\n if (label) {\n const sb = ctx.safeBox;\n const name = typeof rootProp === 'string' ? rootProp : ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'][root];\n const text = labelTextProp ?? `${name} ${PRETTY[scaleKey] ?? scaleKey}`;\n c.save();\n c.fillStyle = ctx.theme.ink;\n c.font = `700 ${Math.round(ctx.H * 0.030)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = labelCenterFrac != null ? 'middle' : 'top';\n const ly = labelCenterFrac != null ? ctx.H * labelCenterFrac : sb.top + sb.h * 0.04;\n c.fillText(text, ctx.W / 2, ly);\n c.restore();\n }\n },\n };\n}\n\nexport const scaleHighlightFactory: LayerFactory<ScaleHighlightProps> = {\n key: 'scale-highlight',\n create: scaleHighlightLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['scale-highlight: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.root != null && typeof p.root !== 'string' && typeof p.root !== 'number') errs.push('scale-highlight.root must be a note name or pitch-class number');\n if (p.scale != null && (typeof p.scale !== 'string' || !(p.scale in SCALE_INTERVALS))) errs.push(`scale-highlight.scale must be one of: ${Object.keys(SCALE_INTERVALS).join(', ')}`);\n if (p.scaleColor != null && typeof p.scaleColor !== 'string') errs.push('scale-highlight.scaleColor must be a string');\n if (p.tonicColor != null && typeof p.tonicColor !== 'string') errs.push('scale-highlight.tonicColor must be a string');\n if (p.alpha != null && (typeof p.alpha !== 'number' || p.alpha < 0 || p.alpha > 1)) errs.push('scale-highlight.alpha must be in [0,1]');\n if (p.label != null && typeof p.label !== 'boolean') errs.push('scale-highlight.label must be a boolean');\n if (p.labelText != null && typeof p.labelText !== 'string') errs.push('scale-highlight.labelText must be a string');\n if (p.labelCenterFrac != null && (typeof p.labelCenterFrac !== 'number' || p.labelCenterFrac < 0 || p.labelCenterFrac > 1)) errs.push('scale-highlight.labelCenterFrac must be in [0,1]');\n return errs;\n },\n};\n","// `particle-burst` layer (S6) — sparks burst from each note as it sounds. Pure\n// energy/celebration; reuses the same onset times everything else does (Score or\n// an `onsetsMs` prop), so any musical scene gets a reactive firework on the beat.\n//\n// Each onset spawns `count` particles with seeded (deterministic) angles +\n// speeds; a particle's position is a closed-form function of its age\n// (t - onsetMs) under constant gravity → pure fn of t (no per-frame state), so\n// captures/golden frames reproduce. If a Score is present the burst origin x is\n// mapped from the note's pitch (low→left, high→right); otherwise it's centered.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { distinctOnsets } from '../notationGeometry';\n\ninterface OnsetSrc { tMs: number; x01: number }\n\nexport interface ParticleBurstProps {\n /** Override onsets. Default = Score onsets (origin x from pitch when available). */\n onsetsMs?: number[];\n /** Particles per burst. Default 16. */\n count?: number;\n /** Particle lifetime, ms. Default 750. */\n lifeMs?: number;\n /** Initial speed (px/s). Default 520. */\n speed?: number;\n /** Gravity (px/s²). Default 900. */\n gravity?: number;\n /** Origin y as a fraction of H (burst source line). Default 0.6. */\n originYFrac?: number;\n /** Particle colour. Default theme.gold. */\n color?: string;\n /** Max particle radius (px). Default 9. */\n size?: number;\n}\n\nconst DEFAULTS = { count: 16, lifeMs: 750, speed: 520, gravity: 900, originYFrac: 0.6, size: 9 };\n\n/** Deterministic unit pseudo-random from two ints (seeded; no Math.random). */\nfunction seeded(a: number, b: number): number {\n const s = Math.sin(a * 127.1 + b * 311.7) * 43758.5453;\n return s - Math.floor(s);\n}\n\nfunction rgba(hex: string, al: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${al})`;\n}\n\nfunction particleBurstLayer(): Layer<ParticleBurstProps> {\n let sources: OnsetSrc[] = [];\n let count = DEFAULTS.count;\n let lifeMs = DEFAULTS.lifeMs;\n let speed = DEFAULTS.speed;\n let gravity = DEFAULTS.gravity;\n let originYFrac = DEFAULTS.originYFrac;\n let color: string | undefined;\n let size = DEFAULTS.size;\n\n return {\n key: 'particle-burst',\n init(ctx: RenderCtx, props) {\n count = props.count ?? DEFAULTS.count;\n lifeMs = props.lifeMs ?? DEFAULTS.lifeMs;\n speed = props.speed ?? DEFAULTS.speed;\n gravity = props.gravity ?? DEFAULTS.gravity;\n originYFrac = props.originYFrac ?? DEFAULTS.originYFrac;\n color = props.color;\n size = props.size ?? DEFAULTS.size;\n\n if (props.onsetsMs) {\n sources = props.onsetsMs.map((tMs) => ({ tMs, x01: 0.5 }));\n } else if (ctx.score?.notes?.length) {\n // origin x from pitch: map [minPitch,maxPitch] → [0.15,0.85].\n const notes = ctx.score.notes;\n let lo = Infinity, hi = -Infinity;\n for (const nn of notes) { if (nn.pitchMidi < lo) lo = nn.pitchMidi; if (nn.pitchMidi > hi) hi = nn.pitchMidi; }\n const span = Math.max(1, hi - lo);\n // first note at each distinct onset drives the x.\n const byOnset = new Map<number, number>();\n for (const nn of notes) if (!byOnset.has(nn.onsetMs)) byOnset.set(nn.onsetMs, nn.pitchMidi);\n sources = distinctOnsets(notes).map((tMs) => ({ tMs, x01: 0.15 + 0.7 * (((byOnset.get(tMs) ?? lo) - lo) / span) }));\n } else {\n sources = [];\n }\n sources.sort((a, b) => a.tMs - b.tMs);\n },\n draw(ctx, tMs) {\n if (!sources.length) return;\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const oy = H * originYFrac;\n const col = color ?? ctx.theme.gold;\n c.save();\n c.globalCompositeOperation = 'lighter';\n for (let si = 0; si < sources.length; si++) {\n const src = sources[si];\n const age = tMs - src.tMs;\n if (age < 0) break; // sorted → nothing later has sounded\n if (age >= lifeMs) continue;\n const ageSec = age / 1000;\n const lifeFrac = age / lifeMs;\n const ox = W * src.x01;\n for (let i = 0; i < count; i++) {\n const a = seeded(si * 31 + i, 7); // angle seed\n const sp = speed * (0.5 + seeded(si, i * 17)); // speed seed\n const ang = -Math.PI / 2 + (a - 0.5) * Math.PI * 1.2; // upward fan\n const vx = Math.cos(ang) * sp;\n const vy = Math.sin(ang) * sp;\n const x = ox + vx * ageSec;\n const y = oy + vy * ageSec + 0.5 * gravity * ageSec * ageSec;\n const alpha = (1 - lifeFrac) * 0.9;\n const r = size * (1 - lifeFrac * 0.6);\n if (r <= 0) continue;\n c.fillStyle = rgba(col, alpha);\n c.beginPath();\n c.arc(x, y, r, 0, Math.PI * 2);\n c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const particleBurstFactory: LayerFactory<ParticleBurstProps> = {\n key: 'particle-burst',\n create: particleBurstLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['particle-burst: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.onsetsMs != null && (!Array.isArray(p.onsetsMs) || p.onsetsMs.some((x) => typeof x !== 'number'))) errs.push('particle-burst.onsetsMs must be a number[]');\n for (const k of ['count', 'lifeMs', 'speed', 'gravity', 'size'] as const) {\n if (p[k] != null && (typeof p[k] !== 'number' || (p[k] as number) <= 0)) errs.push(`particle-burst.${k} must be a positive number`);\n }\n if (p.originYFrac != null && (typeof p.originYFrac !== 'number' || p.originYFrac < 0 || p.originYFrac > 1)) errs.push('particle-burst.originYFrac must be in [0,1]');\n if (p.color != null && typeof p.color !== 'string') errs.push('particle-burst.color must be a string');\n return errs;\n },\n};\n","// `tension-graph` layer (S6) — a curve of musical TENSION over time that \"draws\n// on\" to the playhead, with a moving dot. Teaching primitive: it visualises the\n// pull-and-release a listener feels (stable tonic → restless leading tone →\n// resolution), turning an abstract feeling into a shape.\n//\n// DATA SOURCE (in priority order):\n// 1. `series` prop — host-supplied [{tMs, value 0..1}] (most control).\n// 2. Score + `key` — tension per melody note from scale-degree stability\n// (stable 1/3/5 → low, 2/4/6 → mid, leading-tone 7 → high, chromatic → max).\n// 3. Score only — normalized melodic height as a rough tension proxy.\n// Pure fn of t. World-space band (placed via props, like pitch-contour).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport { contourPoints, pitchRange } from '../pitchContour';\nimport { getScaleDegree, getStability } from '../../scales';\n\nexport interface TensionPoint { tMs: number; value: number }\n\nexport interface TensionGraphProps {\n /** Host-supplied tension series [{tMs, value 0..1}]. Overrides Score derivation. */\n series?: TensionPoint[];\n /** Key (e.g. 'C') → derive tension from scale-degree stability. */\n key?: string;\n /** Plot band top (world y). Default safeBox.top + safeBox.h*0.18. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h*0.30. */\n height?: number;\n /** Line + fill colour. Default theme.accent. */\n color?: string;\n /** Fill the area under the curve. Default true. */\n fill?: boolean;\n /** Line width. Default 5. */\n width?: number;\n /** Moving dot at the current value. Default true. */\n dot?: boolean;\n}\n\nfunction stabilityTension(midi: number, key: string): number {\n const deg = getScaleDegree(midi, key);\n if (deg == null) return 1.0; // chromatic = max tension\n const s = getStability(deg);\n return s === 'stable' ? 0.15 : s === 'lessStable' ? 0.5 : s === 'unstable' ? 0.85 : 0.5;\n}\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction tensionGraphLayer(): Layer<TensionGraphProps> {\n let series: TensionPoint[] = [];\n let durationMs = 1;\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let color: string | undefined;\n let fill = true;\n let width = 5;\n let dot = true;\n\n return {\n key: 'tension-graph',\n init(ctx, props) {\n topProp = props.top; heightProp = props.height; color = props.color;\n fill = props.fill ?? true; width = props.width ?? 5; dot = props.dot ?? true;\n\n if (props.series?.length) {\n series = props.series.slice().sort((a, b) => a.tMs - b.tMs);\n } else if (ctx.score?.notes?.length) {\n const pts = contourPoints(ctx.score.notes);\n if (props.key) {\n series = pts.map((p) => ({ tMs: p.tMs, value: stabilityTension(p.pitchMidi, props.key!) }));\n } else {\n const r = pitchRange(pts);\n const span = Math.max(1, r.max - r.min);\n series = pts.map((p) => ({ tMs: p.tMs, value: (p.pitchMidi - r.min) / span }));\n }\n } else {\n series = [];\n }\n durationMs = ctx.score?.durationMs ?? (series.length ? series[series.length - 1].tMs : 1);\n },\n draw(ctx, tMs) {\n if (series.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const top = topProp ?? sb.top + sb.h * 0.18;\n const height = heightProp ?? sb.h * 0.30;\n const bottom = top + height;\n const col = color ?? ctx.theme.accent;\n const x = (ms: number) => sb.left + (sb.right - sb.left) * Math.min(1, Math.max(0, ms / durationMs));\n const y = (v: number) => bottom - Math.min(1, Math.max(0, v)) * height;\n const playX = x(tMs);\n\n c.save();\n c.lineJoin = 'round'; c.lineCap = 'round';\n\n // Ghost (full curve, faint).\n c.globalAlpha = 0.2; c.strokeStyle = ctx.theme.sepia; c.lineWidth = width;\n c.beginPath();\n series.forEach((p, i) => (i === 0 ? c.moveTo(x(p.tMs), y(p.value)) : c.lineTo(x(p.tMs), y(p.value))));\n c.stroke();\n\n // Revealed portion up to the playhead (+ optional fill).\n const revealed: { x: number; y: number }[] = [];\n for (let i = 0; i < series.length; i++) {\n const px = x(series[i].tMs);\n if (px <= playX) { revealed.push({ x: px, y: y(series[i].value) }); }\n else { // clip last segment at playX\n if (i > 0) {\n const a = series[i - 1], b = series[i];\n const f = (playX - x(a.tMs)) / Math.max(1, x(b.tMs) - x(a.tMs));\n revealed.push({ x: playX, y: y(a.value + (b.value - a.value) * f) });\n }\n break;\n }\n }\n if (revealed.length) {\n c.globalAlpha = 1;\n if (fill) {\n c.fillStyle = rgba(col, 0.22);\n c.beginPath();\n c.moveTo(revealed[0].x, bottom);\n for (const p of revealed) c.lineTo(p.x, p.y);\n c.lineTo(revealed[revealed.length - 1].x, bottom);\n c.closePath(); c.fill();\n }\n c.strokeStyle = col; c.lineWidth = width;\n c.beginPath();\n revealed.forEach((p, i) => (i === 0 ? c.moveTo(p.x, p.y) : c.lineTo(p.x, p.y)));\n c.stroke();\n if (dot) {\n const last = revealed[revealed.length - 1];\n c.fillStyle = col;\n c.beginPath(); c.arc(last.x, last.y, width * 2, 0, Math.PI * 2); c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const tensionGraphFactory: LayerFactory<TensionGraphProps> = {\n key: 'tension-graph',\n create: tensionGraphLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['tension-graph: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.series != null) {\n if (!Array.isArray(p.series)) errs.push('tension-graph.series must be an array of {tMs, value}');\n else p.series.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.tMs !== 'number' || typeof s?.value !== 'number') errs.push(`tension-graph.series[${i}] must be {tMs:number, value:number}`);\n });\n }\n if (p.key != null && typeof p.key !== 'string') errs.push('tension-graph.key must be a string');\n if (p.top != null && typeof p.top !== 'number') errs.push('tension-graph.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0)) errs.push('tension-graph.height must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('tension-graph.color must be a string');\n if (p.fill != null && typeof p.fill !== 'boolean') errs.push('tension-graph.fill must be a boolean');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0)) errs.push('tension-graph.width must be a positive number');\n if (p.dot != null && typeof p.dot !== 'boolean') errs.push('tension-graph.dot must be a boolean');\n return errs;\n },\n};\n","// `texture` layer (S6) — a finishing overlay: film grain + vignette + optional\n// cinematic letterbox bars. One layer that makes any clip look graded. Drawn last\n// (screen-pinned), so it sits over everything.\n//\n// Grain is deterministic per FRAME: the speck pattern is seeded by the frame\n// index (derived from t + ctx.fps), so it shimmers frame-to-frame like real\n// grain yet is reproducible for captures/golden frames (pure fn of t).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface TextureProps {\n /** Grain intensity 0..1 (speck opacity). Default 0.05. 0 disables grain. */\n grain?: number;\n /** Grain speck count per frame. Default 700. */\n grainDensity?: number;\n /** Vignette strength 0..1 (edge darkening). Default 0.35. 0 disables. */\n vignette?: number;\n /** Letterbox bar height as a fraction of H (top+bottom). Default 0 (off). */\n letterboxFrac?: number;\n}\n\nconst DEFAULTS = { grain: 0.05, grainDensity: 700, vignette: 0.35, letterboxFrac: 0 };\n\n/** Tiny deterministic LCG, seeded per frame. */\nfunction lcg(seed: number): () => number {\n let s = (seed | 0) || 1;\n return () => { s = (s * 1664525 + 1013904223) & 0x7fffffff; return s / 0x7fffffff; };\n}\n\nfunction textureLayer(): Layer<TextureProps> {\n let grain = DEFAULTS.grain;\n let grainDensity = DEFAULTS.grainDensity;\n let vignette = DEFAULTS.vignette;\n let letterboxFrac = DEFAULTS.letterboxFrac;\n\n return {\n key: 'texture',\n init(_ctx: RenderCtx, props) {\n grain = props.grain ?? DEFAULTS.grain;\n grainDensity = props.grainDensity ?? DEFAULTS.grainDensity;\n vignette = props.vignette ?? DEFAULTS.vignette;\n letterboxFrac = props.letterboxFrac ?? DEFAULTS.letterboxFrac;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n c.save();\n\n if (vignette > 0) {\n const g = c.createRadialGradient(W / 2, H / 2, Math.min(W, H) * 0.35, W / 2, H / 2, Math.max(W, H) * 0.72);\n g.addColorStop(0, 'rgba(0,0,0,0)');\n g.addColorStop(1, `rgba(0,0,0,${vignette})`);\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n\n if (grain > 0 && grainDensity > 0) {\n const frame = Math.floor((tMs / 1000) * (ctx.fps || 30));\n const rnd = lcg(frame * 2654435761);\n for (let i = 0; i < grainDensity; i++) {\n const x = rnd() * W;\n const y = rnd() * H;\n const dark = rnd() < 0.5;\n c.fillStyle = dark ? `rgba(0,0,0,${grain})` : `rgba(255,255,255,${grain})`;\n c.fillRect(x, y, 2, 2);\n }\n }\n\n if (letterboxFrac > 0) {\n const bh = H * letterboxFrac;\n c.fillStyle = '#000';\n c.fillRect(0, 0, W, bh);\n c.fillRect(0, H - bh, W, bh);\n }\n c.restore();\n },\n };\n}\n\nexport const textureFactory: LayerFactory<TextureProps> = {\n key: 'texture',\n create: textureLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['texture: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.grain != null && (typeof p.grain !== 'number' || p.grain < 0 || p.grain > 1)) errs.push('texture.grain must be in [0,1]');\n if (p.grainDensity != null && (typeof p.grainDensity !== 'number' || p.grainDensity < 0)) errs.push('texture.grainDensity must be a number >= 0');\n if (p.vignette != null && (typeof p.vignette !== 'number' || p.vignette < 0 || p.vignette > 1)) errs.push('texture.vignette must be in [0,1]');\n if (p.letterboxFrac != null && (typeof p.letterboxFrac !== 'number' || p.letterboxFrac < 0 || p.letterboxFrac > 0.45)) errs.push('texture.letterboxFrac must be in [0,0.45]');\n return errs;\n },\n};\n","// `stat-counter` layer (S6) — a big number that counts up to a target with an\n// eased ramp, plus a caption (e.g. \"1788 · Composed\", \"No. 1 · in 30 countries\").\n// A lower-third info beat: the animated count draws the eye and lands a fact.\n//\n// Pure fn of t: shown = round(ease((t-startMs)/durationMs) * value). Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface StatCounterProps {\n /** Target value to count up to. Required. */\n value: number;\n /** When the count starts (ms). Default 0. */\n startMs?: number;\n /** Count duration (ms). Default 1200. */\n durationMs?: number;\n /** Text before the number (e.g. \"No. \"). Default \"\". */\n prefix?: string;\n /** Text after the number (e.g. \"%\", \"+\"). Default \"\". */\n suffix?: string;\n /** Caption under the number. Default \"\". */\n label?: string;\n /** Number font px. Default 220. */\n fontPx?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Number colour. Default theme.accent. */\n color?: string;\n /** Thousands separators on the number. Default true. */\n group?: boolean;\n}\n\nconst DEFAULTS = { startMs: 0, durationMs: 1200, fontPx: 220, centerFrac: 0.46, group: true };\n\nfunction easeOut(t: number): number { return 1 - Math.pow(1 - t, 3); }\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\nfunction statCounterLayer(): Layer<StatCounterProps> {\n let value = 0;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let prefix = '';\n let suffix = '';\n let label = '';\n let fontPx = DEFAULTS.fontPx;\n let centerFrac = DEFAULTS.centerFrac;\n let color: string | undefined;\n let group = DEFAULTS.group;\n\n return {\n key: 'stat-counter',\n init(_ctx: RenderCtx, props) {\n value = props.value ?? 0;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n prefix = props.prefix ?? '';\n suffix = props.suffix ?? '';\n label = props.label ?? '';\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n color = props.color;\n group = props.group ?? DEFAULTS.group;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const p = clamp01((tMs - startMs) / Math.max(1, durationMs));\n const shown = Math.round(easeOut(p) * value);\n const numStr = group ? shown.toLocaleString('en-US') : String(shown);\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n\n c.save();\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillStyle = col;\n c.font = `800 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.fillText(`${prefix}${numStr}${suffix}`, cx, cy);\n if (label) {\n c.fillStyle = ctx.theme.ink;\n c.font = `600 ${Math.round(fontPx * 0.26)}px ${ctx.theme.fontDisplay}`;\n c.fillText(label, cx, cy + fontPx * 0.62);\n }\n c.restore();\n },\n };\n}\n\nexport const statCounterFactory: LayerFactory<StatCounterProps> = {\n key: 'stat-counter',\n create: statCounterLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['stat-counter: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.value !== 'number') errs.push('stat-counter.value must be a number');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('stat-counter.startMs must be a number');\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || p.durationMs <= 0)) errs.push('stat-counter.durationMs must be a positive number');\n for (const k of ['prefix', 'suffix', 'label'] as const) if (p[k] != null && typeof p[k] !== 'string') errs.push(`stat-counter.${k} must be a string`);\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('stat-counter.fontPx must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('stat-counter.centerFrac must be in [0,1]');\n if (p.color != null && typeof p.color !== 'string') errs.push('stat-counter.color must be a string');\n if (p.group != null && typeof p.group !== 'boolean') errs.push('stat-counter.group must be a boolean');\n return errs;\n },\n};\n","// `end-card` layer (S6) — an animated follow/subscribe CTA for the last beat: a\n// CTA line that scales in, an optional handle, and a bouncing chevron pointing at\n// the follow button. The \"what to do next\" cap that converts a watch into a\n// follow. Pure fn of t (the bounce is a sine of t). Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface EndCardProps {\n /** CTA line. Default \"Follow for daily\". */\n text?: string;\n /** Handle / sub-line under the CTA (e.g. \"@whozart\"). Default \"\". */\n handle?: string;\n /** When the card animates in (ms). Default 0. */\n startMs?: number;\n /** Scale-in duration (ms). Default 400. */\n inMs?: number;\n /** CTA colour. Default theme.accent. */\n color?: string;\n /** Vertical center as a fraction of H. Default 0.5. */\n centerFrac?: number;\n /** Font px for the CTA line. Default 72. */\n fontPx?: number;\n /** Draw the bouncing chevron. Default true. */\n arrow?: boolean;\n /** Arrow direction — which way it points / where the follow button is. Default \"up\". */\n arrowDir?: 'up' | 'down';\n}\n\nconst DEFAULTS = { text: 'Follow for daily', startMs: 0, inMs: 400, centerFrac: 0.5, fontPx: 72, arrow: true, arrowDir: 'up' as const };\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\nfunction easeOut(t: number): number { return 1 - Math.pow(1 - t, 3); }\n\nfunction endCardLayer(): Layer<EndCardProps> {\n let text = DEFAULTS.text;\n let handle = '';\n let startMs = DEFAULTS.startMs;\n let inMs = DEFAULTS.inMs;\n let color: string | undefined;\n let centerFrac = DEFAULTS.centerFrac;\n let fontPx = DEFAULTS.fontPx;\n let arrow = DEFAULTS.arrow;\n let arrowDir: 'up' | 'down' = DEFAULTS.arrowDir;\n\n return {\n key: 'end-card',\n init(_ctx: RenderCtx, props) {\n text = props.text ?? DEFAULTS.text;\n handle = props.handle ?? '';\n startMs = props.startMs ?? DEFAULTS.startMs;\n inMs = props.inMs ?? DEFAULTS.inMs;\n color = props.color;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n arrow = props.arrow ?? DEFAULTS.arrow;\n arrowDir = props.arrowDir ?? DEFAULTS.arrowDir;\n },\n draw(ctx, tMs) {\n if (tMs < startMs) return;\n const c = ctx.ctx2d;\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n const p = clamp01((tMs - startMs) / Math.max(1, inMs));\n const scale = 0.7 + 0.3 * easeOut(p);\n const dir = arrowDir === 'down' ? 1 : -1;\n const bounce = Math.sin(tMs / 260) * 14; // gentle bob\n\n c.save();\n c.globalAlpha = p;\n\n // CTA pill text.\n c.translate(cx, cy);\n c.scale(scale, scale);\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillStyle = col;\n c.font = `800 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.fillText(text, 0, 0);\n if (handle) {\n c.fillStyle = ctx.theme.ink;\n c.font = `600 ${Math.round(fontPx * 0.5)}px ${ctx.theme.fontDisplay}`;\n c.fillText(handle, 0, fontPx * 0.9);\n }\n c.restore();\n\n // Bouncing chevron pointing toward the follow button.\n if (arrow) {\n const ax = cx;\n const ay = (arrowDir === 'down' ? cy + fontPx * 1.6 : cy - fontPx * 1.6) + bounce * dir;\n const s = fontPx * 0.4;\n c.save();\n c.globalAlpha = p;\n c.strokeStyle = col;\n c.lineWidth = 10;\n c.lineCap = 'round';\n c.lineJoin = 'round';\n c.beginPath();\n if (arrowDir === 'down') { c.moveTo(ax - s, ay - s * 0.6); c.lineTo(ax, ay + s * 0.6); c.lineTo(ax + s, ay - s * 0.6); }\n else { c.moveTo(ax - s, ay + s * 0.6); c.lineTo(ax, ay - s * 0.6); c.lineTo(ax + s, ay + s * 0.6); }\n c.stroke();\n c.restore();\n }\n },\n };\n}\n\nexport const endCardFactory: LayerFactory<EndCardProps> = {\n key: 'end-card',\n create: endCardLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['end-card: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n for (const k of ['text', 'handle', 'color'] as const) if (p[k] != null && typeof p[k] !== 'string') errs.push(`end-card.${k} must be a string`);\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('end-card.startMs must be a number');\n if (p.inMs != null && (typeof p.inMs !== 'number' || p.inMs <= 0)) errs.push('end-card.inMs must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('end-card.centerFrac must be in [0,1]');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('end-card.fontPx must be a positive number');\n if (p.arrow != null && typeof p.arrow !== 'boolean') errs.push('end-card.arrow must be a boolean');\n if (p.arrowDir != null && p.arrowDir !== 'up' && p.arrowDir !== 'down') errs.push('end-card.arrowDir must be \"up\" | \"down\"');\n return errs;\n },\n};\n","// `image-reveal` layer — a scramble-and-reveal animation for \"Name That Painting\"\n// guessing videos. At p=0 the image is fully obscured; at p=1 it is clean.\n// Three modes: pixelate (blocky downscale→upscale), blur (CSS filter with a\n// progressive-downscale fallback), shuffle (tile grid with deterministic offsets).\n//\n// The image is fit (preserve aspect, letterbox) into ctx.safeBox. All modes are\n// pure functions of tMs — no wall-clock / Math.random per frame.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport type ImageRevealMode = 'pixelate' | 'shuffle' | 'blur';\n\n/**\n * Reveal pacing curve applied to linear progress before it drives the effect.\n * - 'linear' : constant rate (default — unchanged behavior).\n * - 'easeOut' : fast at first, SLOWER near the end (quadratic).\n * - 'easeOutStrong' : like easeOut but a more DRAMATIC, drawn-out final reveal (cubic).\n * - 'easeInOut' : slow start (stays obscured), then a slow settle at the end.\n */\nexport type RevealEasing = 'linear' | 'easeOut' | 'easeOutStrong' | 'easeInOut';\n\n/** Map linear progress p∈[0,1] through the chosen easing curve. Exported for tests. */\nexport function applyEasing(p: number, easing: RevealEasing): number {\n if (easing === 'easeOut') return 1 - (1 - p) * (1 - p); // quadratic ease-out\n if (easing === 'easeOutStrong') return 1 - (1 - p) * (1 - p) * (1 - p); // cubic ease-out (more dramatic end)\n if (easing === 'easeInOut') return p * p * (3 - 2 * p); // smoothstep\n return p; // linear\n}\n\nexport interface ImageRevealProps {\n /** The image to reveal. Any CanvasImageSource (HTMLImageElement, OffscreenCanvas, …). */\n image: CanvasImageSource;\n /** Reveal mode. Default 'pixelate'. */\n mode?: ImageRevealMode;\n /** When the reveal starts (ms). Default 0. */\n startMs?: number;\n /** How long the full reveal takes (ms). Default 6000. */\n durationMs?: number;\n /**\n * Difficulty scalar 0..1.\n * - pixelate: controls maximum block size (0→small, 1→large; default 0.5 ≈ 1/8 width).\n * - shuffle: controls maximum tile offset fraction (default 0.5).\n * - blur: controls maximum blur radius in px (0→mild, 1→extreme; default 0.5 ≈ 40px).\n */\n difficulty?: number;\n /** Pacing curve for the reveal. Default 'linear'. See RevealEasing. */\n easing?: RevealEasing;\n}\n\nconst DEFAULTS = {\n mode: 'pixelate' as ImageRevealMode,\n startMs: 0,\n durationMs: 6000,\n difficulty: 0.5,\n easing: 'linear' as RevealEasing,\n};\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n// ─── Letterbox fit helper ─────────────────────────────────────────────────────\n\ninterface FitRect { dx: number; dy: number; dw: number; dh: number }\n\n/** Fit the image (iW×iH) into the box (bw×bh at bx,by), preserving aspect ratio.\n * Returns the destination rect to pass to drawImage. */\nfunction fitRect(iW: number, iH: number, bx: number, by: number, bw: number, bh: number): FitRect {\n const scale = Math.min(bw / iW, bh / iH);\n const dw = iW * scale;\n const dh = iH * scale;\n return { dx: bx + (bw - dw) / 2, dy: by + (bh - dh) / 2, dw, dh };\n}\n\n// ─── Pixelate mode ───────────────────────────────────────────────────────────\n\n/** Returns the block size (integer) for a given progress p and max block size. */\nfunction blockSizeAt(p: number, maxBlock: number): number {\n // Linearly interpolate from maxBlock→1 as p goes 0→1.\n // Use Math.max(1,…) so we never try to draw 0-pixel blocks.\n return Math.max(1, Math.round(maxBlock * (1 - p) + 1 * p));\n}\n\n// ─── Shuffle mode ────────────────────────────────────────────────────────────\n\n/** Tiny deterministic integer hash seeded by the tile index. Returns a value in\n * [0, 1). Used to derive stable per-tile offsets across frames. */\nfunction tileHash(i: number): number {\n // Xorshift-based — fast, deterministic, no Math.random per frame.\n let x = (i * 2654435761) >>> 0;\n x ^= x << 13;\n x ^= x >>> 17;\n x ^= x << 5;\n return (x >>> 0) / 0xffffffff;\n}\n\n/** Signed [-1,1) hash for a tile seeded by index + salt. */\nfunction tileHashSigned(i: number, salt: number): number {\n return tileHash((i * 1013904223 + salt * 22695477) | 0) * 2 - 1;\n}\n\n// ─── Layer factory function ───────────────────────────────────────────────────\n\nfunction imageRevealLayer(): Layer<ImageRevealProps> {\n let mode: ImageRevealMode = DEFAULTS.mode;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let difficulty = DEFAULTS.difficulty;\n let easing: RevealEasing = DEFAULTS.easing;\n let image: CanvasImageSource | null = null;\n\n // Offscreen canvas for pixelate mode (reused across frames, resized when needed).\n let offscreen: OffscreenCanvas | HTMLCanvasElement | null = null;\n let offCtx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null = null;\n\n // Cached image natural dimensions (read once in init).\n let imgW = 0;\n let imgH = 0;\n\n function ensureOffscreen(w: number, h: number): OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null {\n const needNew = !offscreen || offscreen.width !== w || offscreen.height !== h;\n if (needNew) {\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(w, h);\n offCtx = offscreen.getContext('2d') as OffscreenCanvasRenderingContext2D;\n } else {\n // Fallback for Node/test env that lacks OffscreenCanvas.\n const el = typeof document !== 'undefined'\n ? document.createElement('canvas')\n : null;\n if (!el) { offscreen = null; offCtx = null; return null; }\n el.width = w;\n el.height = h;\n offscreen = el;\n offCtx = el.getContext('2d');\n }\n }\n return offCtx;\n }\n\n return {\n key: 'image-reveal',\n\n init(_ctx: RenderCtx, props: ImageRevealProps) {\n mode = props.mode ?? DEFAULTS.mode;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n difficulty = props.difficulty ?? DEFAULTS.difficulty;\n easing = props.easing ?? DEFAULTS.easing;\n image = props.image;\n\n // Read image dimensions. CanvasImageSource covers HTMLImageElement\n // (naturalWidth/naturalHeight), OffscreenCanvas / HTMLCanvasElement\n // (width/height), ImageBitmap (width/height), and VideoFrame (displayWidth).\n // Cast through unknown first to avoid TS overlap errors with VideoFrame.\n const src = props.image as unknown as Record<string, unknown>;\n imgW =\n (src['naturalWidth'] as number | undefined) ??\n (src['displayWidth'] as number | undefined) ??\n (src['width'] as number | undefined) ??\n 0;\n imgH =\n (src['naturalHeight'] as number | undefined) ??\n (src['displayHeight'] as number | undefined) ??\n (src['height'] as number | undefined) ??\n 0;\n },\n\n draw(ctx: RenderCtx, tMs: number) {\n if (!image || imgW === 0 || imgH === 0) return;\n\n const raw = clamp01((tMs - startMs) / (durationMs > 0 ? durationMs : 1));\n const p = applyEasing(raw, easing);\n const sb = ctx.safeBox;\n const fit = fitRect(imgW, imgH, sb.left, sb.top, sb.w, sb.h);\n\n const c = ctx.ctx2d;\n c.save();\n\n // Clip to the destination rect so bleed from scaled images stays inside.\n c.beginPath();\n c.rect(fit.dx, fit.dy, fit.dw, fit.dh);\n c.clip();\n\n if (mode === 'pixelate') {\n drawPixelate(c, image, fit, p, difficulty, imgW, imgH, ensureOffscreen);\n } else if (mode === 'blur') {\n drawBlur(c, image, fit, p, difficulty, imgW, imgH, ensureOffscreen);\n } else {\n // shuffle\n drawShuffle(c, image, fit, p, difficulty, imgW, imgH);\n }\n\n c.restore();\n },\n\n dispose() {\n image = null;\n offscreen = null;\n offCtx = null;\n },\n };\n}\n\n// ─── Mode draw functions (module-level to keep the layer closure lean) ────────\n\nfunction drawPixelate(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n getOffCtx: (w: number, h: number) => OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null,\n): void {\n // Maximum block size scales with difficulty (0→1/32 width, 1→1/4 width).\n const maxBlock = Math.max(2, Math.round(fit.dw * (0.03125 + difficulty * 0.21875)));\n const blockSize = blockSizeAt(p, maxBlock);\n\n if (blockSize <= 1) {\n // p ≈ 1: draw the clean image directly.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n return;\n }\n\n // Downscale to blockSize resolution, then upscale with smoothing disabled.\n const smallW = Math.max(1, Math.ceil(fit.dw / blockSize));\n const smallH = Math.max(1, Math.ceil(fit.dh / blockSize));\n const oc = getOffCtx(smallW, smallH);\n if (!oc) {\n // Fallback: just draw the image clean if we can't get an offscreen ctx.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n return;\n }\n\n // Draw the source image scaled DOWN to the tiny offscreen.\n oc.clearRect(0, 0, smallW, smallH);\n oc.drawImage(image, 0, 0, imgW, imgH, 0, 0, smallW, smallH);\n\n // Draw the tiny offscreen BACK up to the destination, pixelated.\n const prev = c.imageSmoothingEnabled;\n c.imageSmoothingEnabled = false;\n const off = oc.canvas as CanvasImageSource;\n c.drawImage(off, 0, 0, smallW, smallH, fit.dx, fit.dy, fit.dw, fit.dh);\n c.imageSmoothingEnabled = prev;\n}\n\nfunction drawBlur(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n getOffCtx: (w: number, h: number) => OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null,\n): void {\n // Max blur in px: difficulty 0→8px, 0.5→40px, 1→80px.\n const maxBlur = 8 + difficulty * 72;\n const blurPx = maxBlur * (1 - p);\n\n // Try CSS filter blur (available in all modern browsers + headless Chrome).\n // The 'filter' property exists on CanvasRenderingContext2D in the spec but\n // may not be supported everywhere (e.g. old Safari, some test stubs).\n const cAny = c as unknown as Record<string, unknown>;\n const hasFilter = 'filter' in c && cAny['filter'] !== undefined;\n\n if (hasFilter && blurPx > 0) {\n const prev = cAny['filter'] as string;\n cAny['filter'] = `blur(${blurPx.toFixed(1)}px)`;\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n cAny['filter'] = prev ?? 'none';\n } else if (blurPx > 0) {\n // Progressive-downscale fallback: repeatedly halve the image down and up\n // to simulate blur without CSS filter. Each halving pass adds ~1 stage of\n // approximated blur (trilinear mipmap style).\n const passes = Math.min(6, Math.round(blurPx / 8));\n if (passes > 0) {\n let w = Math.max(1, Math.round(fit.dw / Math.pow(2, passes)));\n let h = Math.max(1, Math.round(fit.dh / Math.pow(2, passes)));\n const oc = getOffCtx(w, h);\n if (oc) {\n oc.clearRect(0, 0, w, h);\n oc.drawImage(image, 0, 0, imgW, imgH, 0, 0, w, h);\n // Upscale back to dest.\n c.drawImage(oc.canvas as CanvasImageSource, 0, 0, w, h, fit.dx, fit.dy, fit.dw, fit.dh);\n } else {\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n } else {\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n } else {\n // blurPx === 0: clean image.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n}\n\nfunction drawShuffle(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n): void {\n // Grid dimensions: ~8 cols × 14 rows (roughly square tiles at 9:16).\n const cols = 8;\n const rows = 14;\n const tileW = fit.dw / cols;\n const tileH = fit.dh / rows;\n const srcTileW = imgW / cols;\n const srcTileH = imgH / rows;\n\n // Maximum offset: tiles can slide up to (difficulty × 1.5 × tileSize) from home.\n const maxOffX = tileW * difficulty * 1.5;\n const maxOffY = tileH * difficulty * 1.5;\n\n // Eased position: at p=0 full offset, at p=1 at home (offset=0).\n // Use ease-out so tiles snap home toward the end.\n const ease = 1 - (1 - p) * (1 - p); // easeOut\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const i = row * cols + col;\n\n // Stable per-tile displacement seeded by tile index (deterministic across frames).\n const offX = tileHashSigned(i, 0) * maxOffX * (1 - ease);\n const offY = tileHashSigned(i, 1) * maxOffY * (1 - ease);\n\n // Source tile from the image.\n const sx = col * srcTileW;\n const sy = row * srcTileH;\n\n // Destination tile home position + offset.\n const dxHome = fit.dx + col * tileW;\n const dyHome = fit.dy + row * tileH;\n const dx = dxHome + offX;\n const dy = dyHome + offY;\n\n c.drawImage(\n image,\n sx, sy, srcTileW, srcTileH,\n dx, dy, tileW, tileH,\n );\n }\n }\n}\n\n// ─── Factory & validation ─────────────────────────────────────────────────────\n\nconst VALID_MODES: ImageRevealMode[] = ['pixelate', 'shuffle', 'blur'];\nconst VALID_EASINGS: RevealEasing[] = ['linear', 'easeOut', 'easeOutStrong', 'easeInOut'];\n\nexport const imageRevealFactory: LayerFactory<ImageRevealProps> = {\n key: 'image-reveal',\n create: imageRevealLayer,\n validateProps(props: unknown): string[] {\n if (props == null || typeof props !== 'object') return ['image-reveal: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n\n if (p['image'] == null) errs.push('image-reveal.image is required');\n\n if (p['mode'] != null && !VALID_MODES.includes(p['mode'] as ImageRevealMode))\n errs.push(`image-reveal.mode must be one of: ${VALID_MODES.join(', ')}`);\n\n if (p['startMs'] != null && typeof p['startMs'] !== 'number')\n errs.push('image-reveal.startMs must be a number');\n\n if (p['durationMs'] != null && (typeof p['durationMs'] !== 'number' || (p['durationMs'] as number) <= 0))\n errs.push('image-reveal.durationMs must be a positive number');\n\n if (p['difficulty'] != null && (typeof p['difficulty'] !== 'number' || (p['difficulty'] as number) < 0 || (p['difficulty'] as number) > 1))\n errs.push('image-reveal.difficulty must be a number in [0, 1]');\n\n if (p['easing'] != null && !VALID_EASINGS.includes(p['easing'] as RevealEasing))\n errs.push(`image-reveal.easing must be one of: ${VALID_EASINGS.join(', ')}`);\n\n return errs;\n },\n};\n","// `composite-rhythm` layer (E7M-300) — the ONE new visual the \"Hand\n// Independence\" series needs: tick marks for each hand's onsets plus the\n// MERGED attack-point line underneath, all read off the SAME Score that drives\n// the keyboard/falling-notes/audio. This is the shot that carries the series'\n// thesis (\"one plan, two outputs\") — it must be legible at 1080×1920 on a\n// phone: clear ticks, an unmistakable composite mark, no clutter.\n//\n// Three rows, top to bottom: optional reference `grids` (faint, illustrate a\n// notated subdivision like \"3 against 2\"), the LH tick row, the bold MERGED\n// row (a filled dot per composite attack, split teal/gold when both hands\n// land within `mergeWindowMs` of each other — literally \"two outputs, one\n// attack\"), and the RH tick row. A thin spine connects consecutive merged\n// marks so the composite reads as ONE line, not a scatter of dots.\n//\n// No invented \"drift\"/\"stagger\" animation here — if a script wants two hands\n// visibly out of sync, that's baked into the SCORE's own onset timing (real\n// data), and this layer (and the keyboard) just draw it truthfully. Pure fn of\n// tMs; reads ctx.score.notes (onsetMs + hand). Draws in world space (the route\n// runs an identity camera, so world == screen).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport type { HandColors } from '../keyboardGeometry';\n\nexport interface CompositeRhythmProps {\n /** Time window mapped across the row width. Default: [min onset, score.durationMs]. */\n windowMs?: [number, number];\n /** Vertical center of the MERGED row (world px). Default safeBox.top + 0.16*safeBox.h. */\n y?: number;\n /** Gap between the LH / merged / RH rows (world px). Default 40. */\n rowGap?: number;\n /** Tick mark half-height for the LH/RH rows (world px). Default 16. */\n tickH?: number;\n /** Merged-mark radius (world px). Default 12. */\n mergeR?: number;\n /** Reference subdivision rows, drawn as faint rows ABOVE the LH row. Two\n * forms per entry: a plain number N divides `windowMs` into N EQUAL ticks\n * (e.g. `3` for a triplet feel, `2` for straight eighths — a 3-against-2\n * bar is `grids: [3, 2]`); an array of positive weights draws ticks at\n * each GROUP's start per those relative durations (e.g. `[3, 3, 2]` for a\n * 3-3-2 clave bar — ticks at fractions 0, 3/8, 6/8, labeled \"3+3+2\") — for\n * asymmetric groupings the rhythm montage needs that an equal-N grid can't\n * represent. Default none. */\n grids?: Array<number | number[]>;\n /** Onsets within this many ms of each other collapse into ONE composite\n * mark. Default 60. */\n mergeWindowMs?: number;\n /** Hand colours (also used for the merged mark's two-tone split). Default\n * theme.accent (R) / theme.gold (L). */\n handColors?: HandColors;\n /** Merged-mark outline / spine colour. Default theme.ink. */\n mergeColor?: string;\n /** Draw the connecting spine through consecutive merged marks. Default true. */\n spine?: boolean;\n /** Sweeping playhead line at the current tMs. Default true. */\n playhead?: boolean;\n /** Progressive left-to-right reveal: a mark at x-fraction `f` across the\n * window only appears once `tMs >= revealStartMs + f*revealDurationMs`.\n * Omit either to show everything always (default). */\n revealStartMs?: number;\n revealDurationMs?: number;\n}\n\ninterface MergedPoint {\n tMs: number;\n hands: Set<'L' | 'R'>;\n}\n\n/** Collapse onsets within `gapMs` of each other into merged attack points. */\nfunction mergeOnsets(notes: { onsetMs: number; hand: 'L' | 'R' }[], gapMs: number): MergedPoint[] {\n const sorted = [...notes].sort((a, b) => a.onsetMs - b.onsetMs);\n const out: MergedPoint[] = [];\n for (const n of sorted) {\n const last = out[out.length - 1];\n if (last && n.onsetMs - last.tMs <= gapMs) {\n last.hands.add(n.hand);\n } else {\n out.push({ tMs: n.onsetMs, hands: new Set([n.hand]) });\n }\n }\n return out;\n}\n\nfunction compositeRhythmLayer(): Layer<CompositeRhythmProps> {\n let win: [number, number] = [0, 1000];\n let yCenter = 0;\n let rowGap = 40;\n let tickH = 16;\n let mergeR = 12;\n let grids: Array<number | number[]> = [];\n let mergeWindowMs = 60;\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let mergeColor = '#1a1614';\n let spine = true;\n let playhead = true;\n let revealStartMs: number | undefined;\n let revealDurationMs: number | undefined;\n let merged: MergedPoint[] = [];\n let lhTicks: number[] = [];\n let rhTicks: number[] = [];\n\n return {\n key: 'composite-rhythm',\n init(ctx, props) {\n rowGap = props.rowGap ?? 40;\n tickH = props.tickH ?? 16;\n mergeR = props.mergeR ?? 12;\n grids = props.grids ?? [];\n mergeWindowMs = props.mergeWindowMs ?? 60;\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n mergeColor = props.mergeColor ?? ctx.theme.ink;\n spine = props.spine ?? true;\n playhead = props.playhead ?? true;\n revealStartMs = props.revealStartMs;\n revealDurationMs = props.revealDurationMs;\n\n const notes = ctx.score?.notes ?? [];\n const onsets = notes.map((n) => n.onsetMs);\n const w0 = props.windowMs?.[0] ?? (onsets.length ? Math.min(...onsets) : 0);\n const w1 = props.windowMs?.[1] ?? (ctx.score?.durationMs ?? Math.max(1000, ...onsets, 0));\n win = [w0, Math.max(w0 + 1, w1)];\n\n merged = mergeOnsets(notes.map((n) => ({ onsetMs: n.onsetMs, hand: n.hand })), mergeWindowMs);\n lhTicks = notes.filter((n) => n.hand === 'L').map((n) => n.onsetMs);\n rhTicks = notes.filter((n) => n.hand === 'R').map((n) => n.onsetMs);\n\n const sb = ctx.safeBox;\n yCenter = props.y ?? sb.top + sb.h * 0.16;\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const [w0, w1] = win;\n const x = (t: number): number => sb.left + ((t - w0) / (w1 - w0)) * sb.w;\n\n const revealed = (t: number): boolean => {\n if (revealStartMs == null || revealDurationMs == null) return true;\n const f = Math.min(1, Math.max(0, (t - w0) / (w1 - w0)));\n return tMs >= revealStartMs + f * revealDurationMs;\n };\n // Cull anything outside the displayed window — `windowMs` is a real\n // visible SLICE (e.g. one polyrhythm cycle out of a longer score), not\n // just an x-axis scale, so a note before/after it must not draw off the\n // safe box edges.\n const inWin = (t: number): boolean => t >= w0 && t <= w1;\n\n const lhY = yCenter - rowGap;\n const rhY = yCenter + rowGap;\n\n c.save();\n\n // Reference subdivision grids, faint rows stacked above the LH row.\n // A plain number N = N equal-tick starts; a weight array (e.g. [3,3,2])\n // = one tick at each GROUP's start, per those relative durations —\n // draws an asymmetric grouping (3-3-2 and its cousins) truthfully\n // instead of forcing it into an equal-N grid it doesn't fit.\n grids.forEach((entry, gi) => {\n const isGrouping = Array.isArray(entry);\n const weights = isGrouping ? entry.filter((w) => w > 0) : [];\n if (isGrouping && weights.length === 0) return;\n if (!isGrouping && entry <= 0) return;\n const gy = lhY - rowGap * (grids.length - gi);\n c.strokeStyle = 'rgba(26,22,20,0.22)';\n c.lineWidth = 2;\n c.beginPath();\n c.moveTo(sb.left, gy);\n c.lineTo(sb.left + sb.w, gy);\n c.stroke();\n\n const starts: number[] = [];\n if (isGrouping) {\n const total = weights.reduce((a, b) => a + b, 0);\n let acc = 0;\n for (const w of weights) { starts.push(acc / total); acc += w; }\n } else {\n const n = entry as number;\n for (let i = 0; i < n; i++) starts.push(i / n);\n }\n for (const f of starts) {\n const t = w0 + (w1 - w0) * f;\n if (!revealed(t)) continue;\n const gx = x(t);\n c.strokeStyle = 'rgba(26,22,20,0.38)';\n c.lineWidth = 3;\n c.beginPath();\n c.moveTo(gx, gy - tickH * 0.5);\n c.lineTo(gx, gy + tickH * 0.5);\n c.stroke();\n }\n // Small label at the row's left: \"3\" for an equal grid, \"3+3+2\" for a\n // weighted grouping, so the row reads unambiguously either way.\n c.fillStyle = 'rgba(26,22,20,0.55)';\n c.font = '600 22px system-ui, sans-serif';\n c.textAlign = 'right';\n c.textBaseline = 'middle';\n c.fillText(isGrouping ? weights.join('+') : String(entry), sb.left - 10, gy);\n });\n\n // LH / RH tick rows.\n const drawTicks = (ticks: number[], y: number, color: string) => {\n c.fillStyle = color;\n for (const t of ticks) {\n if (!inWin(t) || !revealed(t)) continue;\n const tx = x(t);\n c.fillRect(tx - 3, y - tickH * 0.5, 6, tickH);\n }\n };\n drawTicks(lhTicks, lhY, hands.L);\n drawTicks(rhTicks, rhY, hands.R);\n c.font = '600 22px system-ui, sans-serif';\n c.textAlign = 'right';\n c.textBaseline = 'middle';\n c.fillStyle = hands.L;\n c.fillText('L', sb.left - 10, lhY);\n c.fillStyle = hands.R;\n c.fillText('R', sb.left - 10, rhY);\n\n // Composite (merged) row: the spine first (so marks sit on top), then\n // each attack as a filled circle — two-tone split when both hands\n // landed together, solid hand-colour when only one did.\n const visibleMerged = merged.filter((m) => inWin(m.tMs) && revealed(m.tMs));\n if (spine && visibleMerged.length > 1) {\n c.strokeStyle = mergeColor;\n c.lineWidth = 3;\n c.beginPath();\n visibleMerged.forEach((m, i) => {\n const mx = x(m.tMs);\n if (i === 0) c.moveTo(mx, yCenter); else c.lineTo(mx, yCenter);\n });\n c.stroke();\n }\n for (const m of visibleMerged) {\n const mx = x(m.tMs);\n const both = m.hands.has('L') && m.hands.has('R');\n if (both) {\n c.beginPath();\n c.arc(mx, yCenter, mergeR, -Math.PI / 2, Math.PI / 2);\n c.fillStyle = hands.R;\n c.fill();\n c.beginPath();\n c.arc(mx, yCenter, mergeR, Math.PI / 2, -Math.PI / 2);\n c.fillStyle = hands.L;\n c.fill();\n } else {\n c.beginPath();\n c.arc(mx, yCenter, mergeR, 0, Math.PI * 2);\n c.fillStyle = m.hands.has('R') ? hands.R : hands.L;\n c.fill();\n }\n c.lineWidth = 2;\n c.strokeStyle = mergeColor;\n c.beginPath();\n c.arc(mx, yCenter, mergeR, 0, Math.PI * 2);\n c.stroke();\n }\n\n // Sweeping playhead.\n if (playhead && tMs >= w0 && tMs <= w1) {\n const px = x(tMs);\n const topY = grids.length ? lhY - rowGap * grids.length - tickH : lhY - tickH;\n const botY = rhY + tickH;\n c.strokeStyle = mergeColor;\n c.globalAlpha = 0.5;\n c.lineWidth = 2;\n c.beginPath();\n c.moveTo(px, topY);\n c.lineTo(px, botY);\n c.stroke();\n c.globalAlpha = 1;\n }\n\n c.restore();\n },\n\n dispose() {\n merged = [];\n lhTicks = [];\n rhTicks = [];\n },\n };\n}\n\nexport const compositeRhythmFactory: LayerFactory<CompositeRhythmProps> = {\n key: 'composite-rhythm',\n create: compositeRhythmLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['composite-rhythm: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.windowMs != null) {\n if (!Array.isArray(p.windowMs) || p.windowMs.length !== 2 || p.windowMs.some((v) => typeof v !== 'number'))\n errs.push('composite-rhythm.windowMs must be [number, number]');\n }\n if (p.y != null && typeof p.y !== 'number') errs.push('composite-rhythm.y must be a number');\n if (p.rowGap != null && (typeof p.rowGap !== 'number' || p.rowGap <= 0)) errs.push('composite-rhythm.rowGap must be a positive number');\n if (p.tickH != null && (typeof p.tickH !== 'number' || p.tickH <= 0)) errs.push('composite-rhythm.tickH must be a positive number');\n if (p.mergeR != null && (typeof p.mergeR !== 'number' || p.mergeR <= 0)) errs.push('composite-rhythm.mergeR must be a positive number');\n if (p.grids != null) {\n const gridsValid = Array.isArray(p.grids) && p.grids.every((v) => {\n if (typeof v === 'number') return v > 0;\n if (Array.isArray(v)) return v.length > 0 && v.every((w) => typeof w === 'number' && w > 0);\n return false;\n });\n if (!gridsValid) errs.push('composite-rhythm.grids must be an array of positive numbers or arrays of positive-number weights');\n }\n if (p.mergeWindowMs != null && (typeof p.mergeWindowMs !== 'number' || p.mergeWindowMs < 0))\n errs.push('composite-rhythm.mergeWindowMs must be a non-negative number');\n if (p.mergeColor != null && typeof p.mergeColor !== 'string') errs.push('composite-rhythm.mergeColor must be a string');\n if (p.spine != null && typeof p.spine !== 'boolean') errs.push('composite-rhythm.spine must be a boolean');\n if (p.playhead != null && typeof p.playhead !== 'boolean') errs.push('composite-rhythm.playhead must be a boolean');\n if (p.revealStartMs != null && typeof p.revealStartMs !== 'number') errs.push('composite-rhythm.revealStartMs must be a number');\n if (p.revealDurationMs != null && (typeof p.revealDurationMs !== 'number' || p.revealDurationMs <= 0))\n errs.push('composite-rhythm.revealDurationMs must be a positive number');\n return errs;\n },\n};\n","// `hand-indicator` layer (E7M-300 v3) — two stylised hand silhouettes pinned\n// BELOW the keyboard strip: LH under the lower (bass) register, RH under the\n// upper (treble) register. Each hand lights up (a solid fill + a soft glow)\n// the instant that hand ATTACKS a note and fades back to a faint outline over\n// `fadeMs` once it goes quiet — the SAME LH/RH theme tokens the keyboard keys\n// already use (handColors), so the mapping between \"a hand lights up\" and\n// \"that hand plays\" is instantly readable. This is the shot that makes \"one\n// motor plan, two outputs\" legible without reading a word: watch the hands\n// alone and see the interlock.\n//\n// Pure fn of tMs — reads ctx.score.notes (onsetMs/durMs/hand), the SAME\n// source of truth as keyboard.ts and compositeRhythm.ts. Fixed x position per\n// hand (not tied to the actual note's pitch/x), since a script's LH/RH\n// geography can vary (e.g. a wide-leap voicing crosses register) — the fixed\n// left/right split mirrors the series' own convention (LH reads on the left,\n// RH on the right) and matches where a viewer's eye already expects each\n// hand's colour to live.\n//\n// Deliberately simple: a filled \"mitten\" silhouette (palm + 4 rounded finger\n// stubs + a thumb bump, mirrored L/R) drawn with canvas primitives — a\n// graphic, not a photoreal render. The caller (the route composing this\n// layer) owns the vertical geometry (`y`/`height`) so it can keep this band\n// entirely inside the safe box, below the keyboard, without crowding the\n// composite-rhythm band far above it.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport type { HandColors } from '../keyboardGeometry';\n\nexport interface HandIndicatorProps {\n /** Top of the hand band, world px. Required — the caller owns this route's\n * vertical layout; this layer only draws at the given y. */\n y: number;\n /** Band height, world px. Default 60. */\n height?: number;\n /** Hand colours — the SAME tokens the keyboard/composite-rhythm layers use.\n * Default R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n /** How fast a hand fades from lit back to its resting outline after its\n * last active note ends, ms. Default 200 (the \"quickly\" in ~150-250ms). */\n fadeMs?: number;\n /** Icon size, px (roughly the glyph's bounding box). Default height*0.85. */\n size?: number;\n /** LH icon x, fraction of safeBox width from its left edge. Default 0.26\n * (under the lower/bass register). */\n leftXFrac?: number;\n /** RH icon x, fraction of safeBox width from its left edge. Default 0.74\n * (under the upper/treble register). */\n rightXFrac?: number;\n /** Baseline (resting/unlit) outline alpha. Default 0.32. */\n restAlpha?: number;\n}\n\ninterface HandNote {\n onsetMs: number;\n durMs: number;\n}\n\n/** 1 = a note for this hand is sounding right now; ramps 1→0 linearly over\n * `fadeMs` after the most recent note's release; 0 (resting) before the\n * first note or once the fade has fully elapsed. Pure fn of tMs. */\nfunction activeOpacity(notes: HandNote[], tMs: number, fadeMs: number): number {\n let lastEnd = -Infinity;\n for (const n of notes) {\n const end = n.onsetMs + n.durMs;\n if (tMs >= n.onsetMs && tMs < end) return 1;\n if (end <= tMs && end > lastEnd) lastEnd = end;\n }\n if (lastEnd === -Infinity) return 0;\n const dt = tMs - lastEnd;\n if (dt >= fadeMs) return 0;\n return 1 - dt / fadeMs;\n}\n\n/** Rounded-rect path (falls back to a plain rect if roundRect is unavailable). */\nfunction roundedPath(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, r); return; }\n c.rect(x, y, w, h);\n}\n\n/** A simple stylised \"mitten\" hand glyph: a palm + 4 rounded finger stubs + a\n * thumb, centered at (cx, cy), `size` px tall. `mirror` flips the thumb to\n * the opposite side so an LH/RH pair reads as a natural facing pair, both\n * thumbs pointing inward toward the keyboard's center. `mode` either fills\n * (the \"lit\" pass) or strokes (the always-visible resting outline) each\n * sub-shape — a plain silhouette/outline, not a photoreal render. */\nfunction drawHandGlyph(\n c: CanvasRenderingContext2D,\n cx: number, cy: number, size: number,\n mirror: boolean, mode: 'fill' | 'stroke',\n): void {\n const paint = () => { if (mode === 'fill') c.fill(); else c.stroke(); };\n const palmW = size * 0.66, palmH = size * 0.56;\n const px0 = cx - palmW / 2, py0 = cy - palmH * 0.1;\n roundedPath(c, px0, py0, palmW, palmH, palmW * 0.32);\n paint();\n const fingerW = palmW * 0.17;\n const lens = [0.36, 0.46, 0.42, 0.32];\n for (let i = 0; i < 4; i++) {\n const fx = px0 + palmW * (0.12 + i * 0.25) - fingerW / 2;\n const fLen = size * lens[i];\n const fy = py0 - fLen + size * 0.06;\n roundedPath(c, fx, fy, fingerW, fLen, fingerW / 2);\n paint();\n }\n const thumbW = palmW * 0.24, thumbLen = size * 0.3;\n const sign = mirror ? -1 : 1;\n c.save();\n c.translate(cx + sign * palmW * 0.42, py0 + palmH * 0.32);\n c.rotate(sign * 0.55);\n roundedPath(c, -thumbW / 2, 0, thumbW, thumbLen, thumbW / 2);\n paint();\n c.restore();\n}\n\nfunction handIndicatorLayer(): Layer<HandIndicatorProps> {\n let y = 0;\n let height = 60;\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let fadeMs = 200;\n let size = 0;\n let leftXFrac = 0.26;\n let rightXFrac = 0.74;\n let restAlpha = 0.32;\n let lNotes: HandNote[] = [];\n let rNotes: HandNote[] = [];\n\n return {\n key: 'hand-indicator',\n init(ctx, props) {\n y = props.y;\n height = props.height ?? 60;\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n fadeMs = props.fadeMs ?? 200;\n size = props.size ?? height * 0.85;\n leftXFrac = props.leftXFrac ?? 0.26;\n rightXFrac = props.rightXFrac ?? 0.74;\n restAlpha = props.restAlpha ?? 0.32;\n const notes = ctx.score?.notes ?? [];\n lNotes = notes.filter((n) => n.hand === 'L').map((n) => ({ onsetMs: n.onsetMs, durMs: n.durMs }));\n rNotes = notes.filter((n) => n.hand === 'R').map((n) => ({ onsetMs: n.onsetMs, durMs: n.durMs }));\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cy = y + height / 2;\n\n const drawHand = (frac: number, color: string, notes: HandNote[], mirror: boolean) => {\n const cx = sb.left + sb.w * frac;\n const op = activeOpacity(notes, tMs, fadeMs);\n\n // Always-visible resting outline, so the viewer knows a hand lives\n // here even between attacks.\n c.save();\n c.globalAlpha = restAlpha;\n c.strokeStyle = color;\n c.lineWidth = Math.max(1.5, size * 0.035);\n drawHandGlyph(c, cx, cy, size, mirror, 'stroke');\n c.restore();\n\n // Lit fill, opacity == activeOpacity — a solid fill + a soft glow\n // while lit, fading back to the outline over `fadeMs`.\n if (op > 0.01) {\n c.save();\n c.globalAlpha = op;\n c.shadowColor = color;\n c.shadowBlur = 16 * op;\n c.fillStyle = color;\n drawHandGlyph(c, cx, cy, size, mirror, 'fill');\n c.restore();\n }\n };\n\n drawHand(leftXFrac, hands.L, lNotes, true);\n drawHand(rightXFrac, hands.R, rNotes, false);\n },\n\n dispose() {\n lNotes = [];\n rNotes = [];\n },\n };\n}\n\nexport const handIndicatorFactory: LayerFactory<HandIndicatorProps> = {\n key: 'hand-indicator',\n create: handIndicatorLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['hand-indicator: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.y !== 'number') errs.push('hand-indicator.y is required and must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0)) errs.push('hand-indicator.height must be a positive number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs <= 0)) errs.push('hand-indicator.fadeMs must be a positive number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0)) errs.push('hand-indicator.size must be a positive number');\n if (p.leftXFrac != null && typeof p.leftXFrac !== 'number') errs.push('hand-indicator.leftXFrac must be a number');\n if (p.rightXFrac != null && typeof p.rightXFrac !== 'number') errs.push('hand-indicator.rightXFrac must be a number');\n if (p.restAlpha != null && (typeof p.restAlpha !== 'number' || p.restAlpha < 0 || p.restAlpha > 1)) errs.push('hand-indicator.restAlpha must be a number in [0,1]');\n return errs;\n },\n};\n","// Layer registry (S1b) — maps a SceneSpec layer key (\"background\", \"caption\",\n// …) to its LayerFactory. The runner instantiates layers from here, and the\n// pre-render gate uses it to reject a spec that references an unknown layer or\n// passes invalid props (\"unknown layer/prop → fail fast\", spec gate check #5).\n//\n// S2+ register their real music layers (notation, falling-notes, …) by calling\n// registerLayer — they DON'T edit the runner.\n\nimport type { LayerFactory } from './layer';\nimport { backgroundFactory, captionFactory } from './layers/demo';\nimport { notationFactory } from './layers/notation';\nimport { scrollCursorFactory } from './layers/scrollCursor';\nimport { keyboardFactory } from './layers/keyboard';\nimport { fretboardFactory } from './layers/fretboard';\nimport { fallingNotesFactory } from './layers/fallingNotes';\nimport { hookFactory, revealFactory, ctaFactory, portraitFactory } from './layers/promoCards';\nimport { spectrumFactory } from './layers/spectrum';\nimport { brandingFactory, safeGuidesFactory } from './layers/branding';\nimport { staffKeyboardRayFactory } from './layers/staffKeyboardRay';\nimport { countingTrackFactory } from './layers/countingTrack';\nimport { degreeLabelsFactory } from './layers/degreeLabels';\nimport { functionalHarmonyFactory } from './layers/functionalHarmony';\nimport { mcqCardFactory } from './layers/mcqCard';\nimport { circleOfFifthsFactory } from './layers/circleOfFifths';\nimport { pitchContourFactory } from './layers/pitchContour';\nimport { sectionMinimapFactory } from './layers/sectionMinimap';\nimport { beatPulseFactory } from './layers/beatPulse';\nimport { chordRibbonFactory } from './layers/chordRibbon';\nimport { progressRingFactory } from './layers/progressRing';\nimport { radialSpectrumFactory } from './layers/radialSpectrum';\nimport { karaokeCaptionFactory } from './layers/karaokeCaption';\nimport { intervalArcsFactory } from './layers/intervalArcs';\nimport { kineticTextFactory } from './layers/kineticText';\nimport { countdownFactory } from './layers/countdown';\nimport { waveformFactory } from './layers/waveform';\nimport { scaleHighlightFactory } from './layers/scaleHighlight';\nimport { particleBurstFactory } from './layers/particleBurst';\nimport { tensionGraphFactory } from './layers/tensionGraph';\nimport { textureFactory } from './layers/texture';\nimport { statCounterFactory } from './layers/statCounter';\nimport { endCardFactory } from './layers/endCard';\nimport { imageRevealFactory } from './layers/imageReveal';\nimport { compositeRhythmFactory } from './layers/compositeRhythm';\nimport { handIndicatorFactory } from './layers/handIndicator';\n\nconst REGISTRY = new Map<string, LayerFactory<any>>(); // eslint-disable-line @typescript-eslint/no-explicit-any\n\n/** Register (or replace) a layer factory under its key. */\nexport function registerLayer(factory: LayerFactory<any>): void { // eslint-disable-line @typescript-eslint/no-explicit-any\n REGISTRY.set(factory.key, factory);\n}\n\n/** Look up a factory by key, or undefined if not registered. */\nexport function getLayerFactory(key: string): LayerFactory<any> | undefined { // eslint-disable-line @typescript-eslint/no-explicit-any\n return REGISTRY.get(key);\n}\n\n/** All registered layer keys (for diagnostics / gate messages). */\nexport function registeredKeys(): string[] {\n return [...REGISTRY.keys()];\n}\n\n// Built-in demo layers, registered on import so the runner is end-to-end.\nregisterLayer(backgroundFactory);\nregisterLayer(captionFactory);\n\n// S2 — the extracted music layers (notation + scrolling cursor).\nregisterLayer(notationFactory);\nregisterLayer(scrollCursorFactory);\n\n// S3 — falling-notes (Synthesia) + keyboard + fretboard.\nregisterLayer(keyboardFactory);\nregisterLayer(fretboardFactory);\nregisterLayer(fallingNotesFactory);\n\n// S4 — promo cards (hook/reveal/cta/portrait), spectrum, branding/safe-guides.\nregisterLayer(hookFactory);\nregisterLayer(revealFactory);\nregisterLayer(ctaFactory);\nregisterLayer(portraitFactory);\nregisterLayer(spectrumFactory);\nregisterLayer(brandingFactory);\nregisterLayer(safeGuidesFactory);\n\n// S5 — extended catalog (top-priority): staff↔keyboard ray, counting track +\n// bouncing ball, scale-degree/solfège labels, functional-harmony coloring.\nregisterLayer(staffKeyboardRayFactory);\nregisterLayer(countingTrackFactory);\nregisterLayer(degreeLabelsFactory);\nregisterLayer(functionalHarmonyFactory);\n\n// S5 — extended catalog (batch 2): MCQ poll card, circle-of-fifths, pitch-contour\n// line, section markers + minimap.\nregisterLayer(mcqCardFactory);\nregisterLayer(circleOfFifthsFactory);\nregisterLayer(pitchContourFactory);\nregisterLayer(sectionMinimapFactory);\n\n// S6 — music-video energy/teaching/retention primitives: onset-reactive pulse,\n// scrolling chord ribbon, circular progress ring.\nregisterLayer(beatPulseFactory);\nregisterLayer(chordRibbonFactory);\nregisterLayer(progressRingFactory);\n\n// S6 — next wave: radial spectrum, karaoke caption, interval arcs.\nregisterLayer(radialSpectrumFactory);\nregisterLayer(karaokeCaptionFactory);\nregisterLayer(intervalArcsFactory);\n\n// S6 — batch 3: kinetic hook text, countdown, waveform, scale/mode highlighter.\nregisterLayer(kineticTextFactory);\nregisterLayer(countdownFactory);\nregisterLayer(waveformFactory);\nregisterLayer(scaleHighlightFactory);\n\n// S6 — batch 4: particle burst, tension graph, texture finish, stat counter, end card.\nregisterLayer(particleBurstFactory);\nregisterLayer(tensionGraphFactory);\nregisterLayer(textureFactory);\nregisterLayer(statCounterFactory);\nregisterLayer(endCardFactory);\n\n// image-reveal — scramble-and-reveal for \"Name That Painting\" guessing videos.\nregisterLayer(imageRevealFactory);\n\n// composite-rhythm (E7M-300) — tick marks + the merged attack-point timeline,\n// for the \"Hand Independence\" keyboard-highlight shorts series.\nregisterLayer(compositeRhythmFactory);\n\n// hand-indicator (E7M-300 v3) — LH/RH silhouettes beneath the keyboard that\n// light up per-hand-attack, for the same series.\nregisterLayer(handIndicatorFactory);\n","// Audio utility layers (S1b) — count-in, click/metronome, ambient drone bed,\n// and a VO-duck helper. These EXTEND the existing promo sampler/mastering bus\n// (src/promo.ts createPromoSampler) rather than introducing a new audio stack:\n// each one computes a deterministic SCHEDULE (pure → unit-testable) and then\n// applies it to a Tone-like target (a synth/sampler + the audio context clock).\n//\n// \"Audio layers\" are not visual Layers — they have no per-frame draw. They are\n// scheduled ONCE at render start against the audio clock, alongside the Score's\n// note schedule, and the visual layers read the same clock. Keeping the schedule\n// pure lets the gate / tests assert event times without real audio (the spec's\n// \"audio schedule: mock the sampler, assert trigger times == onsets ± tol\").\n\n/* Tone is typed loose (`any`) exactly like promo.ts / audioHelpers.ts. */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** One scheduled audio event, in seconds RELATIVE to the schedule start. */\nexport interface AudioEvent {\n /** Offset from schedule start, seconds. */\n atSec: number;\n /** Pitch (note name or frequency) for tonal hits; omitted for noise clicks. */\n note?: string | number;\n /** Duration, seconds. Default short. */\n durSec?: number;\n /** Linear gain 0..1. Default 1. */\n gain?: number;\n /** Tag for tests/debugging: \"count\" | \"click\" | \"drone\". */\n kind: 'count' | 'click' | 'drone';\n}\n\n// ─── Count-in (3-2-1 + click) ────────────────────────────────────────────────\n\nexport interface CountInOpts {\n /** Beats to count (default 4 — a full bar of 4/4). */\n beats?: number;\n /** Tempo for the count, bpm. */\n bpm: number;\n /** Accent the downbeat (beat 1) with a higher pitch. Default true. */\n accentDownbeat?: boolean;\n}\n\n/**\n * The count-in schedule: one click per beat, starting at t=0, ending exactly on\n * the downbeat of bar 1 (i.e. the music starts at `beats * 60/bpm` seconds).\n * Pure.\n */\nexport function countInSchedule(opts: CountInOpts): AudioEvent[] {\n const beats = opts.beats ?? 4;\n const accent = opts.accentDownbeat ?? true;\n const beatSec = 60 / opts.bpm;\n const events: AudioEvent[] = [];\n for (let i = 0; i < beats; i++) {\n const isDownbeat = accent && i === 0;\n events.push({\n atSec: i * beatSec,\n note: isDownbeat ? 'C6' : 'C5',\n durSec: 0.05,\n gain: isDownbeat ? 1 : 0.7,\n kind: 'count',\n });\n }\n return events;\n}\n\n/** Seconds after t=0 that the music should start, for a given count-in. */\nexport function countInLeadSec(opts: CountInOpts): number {\n return (opts.beats ?? 4) * (60 / opts.bpm);\n}\n\n// ─── Click / metronome track ─────────────────────────────────────────────────\n\nexport interface ClickTrackOpts {\n bpm: number;\n /** Total duration to fill with clicks, seconds. */\n durationSec: number;\n /** Beats per bar, for downbeat accents. Default 4. */\n beatsPerBar?: number;\n /** Start offset, seconds. Default 0. */\n startSec?: number;\n}\n\n/** A click on every beat across `durationSec`, accenting each bar's downbeat. */\nexport function clickTrackSchedule(opts: ClickTrackOpts): AudioEvent[] {\n const beatSec = 60 / opts.bpm;\n const bpb = opts.beatsPerBar ?? 4;\n const start = opts.startSec ?? 0;\n const events: AudioEvent[] = [];\n const n = Math.floor(opts.durationSec / beatSec);\n for (let i = 0; i < n; i++) {\n const isDownbeat = i % bpb === 0;\n events.push({\n atSec: start + i * beatSec,\n note: isDownbeat ? 'C6' : 'C5',\n durSec: 0.03,\n gain: isDownbeat ? 0.6 : 0.4,\n kind: 'click',\n });\n }\n return events;\n}\n\n// ─── Ambient drone / pad bed ─────────────────────────────────────────────────\n\nexport interface DroneOpts {\n /** Root pitch of the pad (e.g. \"C2\"). */\n root: string;\n /** Pad duration, seconds. */\n durationSec: number;\n /** Add a perfect fifth above the root. Default true. */\n fifth?: boolean;\n /** Bed gain 0..1. Default 0.15 (sits well under the melody). */\n gain?: number;\n}\n\n/** A sustained root (+fifth) pad for the whole clip. */\nexport function droneSchedule(opts: DroneOpts): AudioEvent[] {\n const gain = opts.gain ?? 0.15;\n const events: AudioEvent[] = [\n { atSec: 0, note: opts.root, durSec: opts.durationSec, gain, kind: 'drone' },\n ];\n if (opts.fifth ?? true) {\n events.push({ atSec: 0, note: transposeFifth(opts.root), durSec: opts.durationSec, gain, kind: 'drone' });\n }\n return events;\n}\n\n/** Transpose a note name up a perfect fifth (7 semitones), keeping it simple\n * for the small set of roots a drone uses (naturals). Falls back to the input\n * if it can't parse. */\nfunction transposeFifth(note: string): string {\n const m = /^([A-G])(#|b)?(\\d)$/.exec(note);\n if (!m) return note;\n const order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];\n const pcMap: Record<string, number> = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };\n let pc = pcMap[m[1]] + (m[2] === '#' ? 1 : m[2] === 'b' ? -1 : 0);\n let oct = parseInt(m[3], 10);\n pc += 7;\n if (pc >= 12) {\n pc -= 12;\n oct += 1;\n }\n return `${order[pc]}${oct}`;\n}\n\n// ─── VO duck helper ──────────────────────────────────────────────────────────\n\nexport interface DuckWindow {\n /** Voice-over window start, seconds. */\n startSec: number;\n /** Voice-over window end, seconds. */\n endSec: number;\n}\n\n/**\n * Gain envelope for a music/bed bus that ducks under voice-over windows. Returns\n * the linear gain 0..1 at time `tSec`: `floor` inside a window (after a short\n * attack ramp), 1 outside (after a release ramp). Pure — feed it to a Tone\n * Gain's `.gain.value` per frame, or sample it to build a ramp automation.\n */\nexport function duckGainAt(\n windows: DuckWindow[],\n tSec: number,\n floor = 0.25,\n rampSec = 0.2,\n): number {\n for (const w of windows) {\n if (tSec >= w.startSec - rampSec && tSec <= w.endSec + rampSec) {\n // attack into the window, release out of it\n if (tSec < w.startSec) return lerpGain(1, floor, (tSec - (w.startSec - rampSec)) / rampSec);\n if (tSec > w.endSec) return lerpGain(floor, 1, (tSec - w.endSec) / rampSec);\n return floor;\n }\n }\n return 1;\n}\n\nfunction lerpGain(a: number, b: number, t: number): number {\n const k = t < 0 ? 0 : t > 1 ? 1 : t;\n return a + (b - a) * k;\n}\n\n// ─── Apply a schedule to a Tone target ───────────────────────────────────────\n\n/**\n * Trigger a computed schedule on a Tone instrument at `startTime` (audio-clock\n * seconds, e.g. `Tone.now()` or sampler.audioNow()). Thin glue over the existing\n * sampler/synth — no new mastering bus. Tonal events use triggerAttackRelease;\n * the instrument is whatever the caller wires into the promo mastering chain.\n *\n * Returns the number of events fired (handy for tests/gate sanity).\n */\nexport function applySchedule(\n instrument: { triggerAttackRelease: (note: any, dur: any, time?: any, velocity?: any) => void },\n schedule: AudioEvent[],\n startTime: number,\n): number {\n for (const ev of schedule) {\n if (ev.note == null) continue;\n instrument.triggerAttackRelease(\n ev.note,\n ev.durSec ?? 0.05,\n startTime + ev.atSec,\n ev.gain ?? 1,\n );\n }\n return schedule.length;\n}\n","// Camera / viewport director (S1b cross-cutting primitive).\n//\n// Generalizes RSR's followBox/crop/zoom: layers draw in WORLD coordinates\n// (the frame's natural pixel space); the camera maps a world rectangle onto the\n// W×H output viewport so the runner can pan to the active measure, zoom on a\n// chord, or Ken-Burns the whole frame. Nearly every later layer rides this.\n//\n// Pure math (no canvas) so it is exhaustively unit-testable; `applyToContext`\n// is the only canvas touchpoint and is a thin wrapper over setTransform/scale.\n\nimport { clamp, lerp, easeInOut, type Easing } from './math';\n\n/** A rectangle in WORLD coordinates (the layers' natural pixel space). */\nexport interface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** A camera pose: which world point sits at the viewport centre, and the zoom. */\nexport interface CameraState {\n /** World-space centre x. */\n cx: number;\n /** World-space centre y. */\n cy: number;\n /** Zoom factor (>1 = zoomed in). */\n zoom: number;\n}\n\n/** A 2D affine transform `[a,b,c,d,e,f]` (matches CanvasRenderingContext2D.setTransform). */\nexport type Affine = [number, number, number, number, number, number];\n\n/**\n * The world→viewport transform for a camera pose. We use a uniform scale = zoom\n * and translate so (cx,cy) world maps to (W/2,H/2) viewport.\n * world point (x,y) -> ( (x-cx)*zoom + W/2 , (y-cy)*zoom + H/2 ).\n */\nexport function cameraTransform(cam: CameraState, W: number, H: number): Affine {\n const s = cam.zoom;\n return [s, 0, 0, s, W / 2 - cam.cx * s, H / 2 - cam.cy * s];\n}\n\n/** Map a world point through a camera pose to viewport pixels. */\nexport function worldToViewport(\n cam: CameraState,\n W: number,\n H: number,\n x: number,\n y: number,\n): { x: number; y: number } {\n const [a, , , d, e, f] = cameraTransform(cam, W, H);\n return { x: a * x + e, y: d * y + f };\n}\n\n/**\n * The camera pose that frames `rect` to fill the W×H viewport (contain-fit:\n * whole rect visible, uniform zoom). `pad` (0..1) leaves margin around the rect.\n */\nexport function frameRect(rect: Rect, W: number, H: number, pad = 0): CameraState {\n const padded = (1 + Math.max(0, pad) * 2);\n const zoom = Math.min(W / (rect.w * padded), H / (rect.h * padded));\n return { cx: rect.x + rect.w / 2, cy: rect.y + rect.h / 2, zoom };\n}\n\n/** Interpolate between two camera poses (for pan/zoom moves). */\nexport function lerpCamera(a: CameraState, b: CameraState, t01: number, ease: Easing = easeInOut): CameraState {\n const k = ease(clamp(t01, 0, 1));\n return { cx: lerp(a.cx, b.cx, k), cy: lerp(a.cy, b.cy, k), zoom: lerp(a.zoom, b.zoom, k) };\n}\n\n/**\n * A Ken-Burns move: slow drift from `from` to `to` over a segment. `at` is the\n * segment-relative position 0..1. Pure — returns the pose for that t.\n */\nexport function kenBurns(from: CameraState, to: CameraState, at01: number): CameraState {\n return lerpCamera(from, to, at01, easeInOut);\n}\n\n/**\n * Apply a camera pose to a 2D context (sets the transform). Layers drawn after\n * this render in world coords and appear panned/zoomed. Pair with ctx.save()\n * /ctx.restore() in the runner so layers can't leak the transform.\n */\nexport function applyToContext(\n ctx: CanvasRenderingContext2D,\n cam: CameraState,\n W: number,\n H: number,\n): void {\n const m = cameraTransform(cam, W, H);\n ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);\n}\n\n/** The identity (no pan/zoom) camera: world == viewport. */\nexport function identityCamera(W: number, H: number): CameraState {\n return { cx: W / 2, cy: H / 2, zoom: 1 };\n}\n","// SceneSpec + runner (S1b).\n//\n// A SceneSpec is the declarative description a recipe produces: canvas size,\n// theme, how duration is decided, a timeline of segments (each a stack of\n// layers active over a time range), and audio settings. The runner:\n// 1. resolves segment times (incl. \"end\" / \"end-N\" anchored to the clip end),\n// 2. instantiates layers from the registry + init()s them,\n// 3. on each frame, draws every active segment's layers IN ORDER onto ONE\n// canvas, applying the camera transform, then resets transform so screen-\n// pinned chrome (captions/branding) draws in viewport space.\n//\n// Capture is delegated to src/video.ts recordScenes (canvas captureStream +\n// MediaRecorder + Tone audio) — we wrap the whole timeline as a single composite\n// Scene so we REUSE that machinery rather than reinventing it. A separate\n// deterministic `renderFrame` path (no MediaRecorder) backs the tests + golden\n// frames and runs anywhere a 2D context exists.\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { Layer, RenderCtx, AudioClock } from './layer';\nimport type { Score } from './score';\nimport type { PromoTheme } from '../video';\nimport { recordScenes, safeBox, type Scene, type RecordOpts } from '../video';\nimport { getLayerFactory, registeredKeys } from './registry';\nimport { applySchedule, type AudioEvent } from './audioLayers';\nimport {\n identityCamera,\n applyToContext,\n type CameraState,\n} from './camera';\nimport { clamp, easeInOut as defaultEasing, transitionProgress, type Easing } from './math';\n\n// ─── SceneSpec ───────────────────────────────────────────────────────────────\n\n/** A timeline endpoint: a number (ms? no — seconds), \"end\", or \"end-N\" (N s before end). */\nexport type TimeAnchor = number | 'end' | `end-${number}`;\n\nexport interface SpecLayer {\n /** Registry key. */\n k: string;\n /** Props passed to the layer's init(). */\n p?: unknown;\n}\n\n/**\n * Optional per-segment audio. Lets a segment SCHEDULE sound at its own start\n * time during capture — the seam that lets per-scene / sequential-audio apps\n * (RET templates schedule `engine.playNote` in a scene's `onEnter`; whozart's\n * \"listen\" scenes play a phrase when the scene begins) run on the same runner\n * instead of staying bespoke. Everything here is OPTIONAL and the runner stays\n * pure-visual when it's absent (fully backward-compatible).\n *\n * Two complementary forms, both resolved against the CAPTURE clock (not\n * wall-clock) so they fire at the right offset in an offline/deterministic\n * render:\n * - `schedule`: declarative `AudioEvent`s whose `atSec` is relative to the\n * segment's start. Played through the same Tone sampler/mastering bus via the\n * existing `applySchedule` glue (so timing == captureStart + segStart + atSec).\n * - `onEnter(audioClock, ctx)`: an imperative escape hatch fired once at the\n * segment's start time, handed the audio clock anchored to that start (its\n * `nowMs()` returns the segment's absolute start ms) so the callback can do\n * its own `triggerAttackRelease(note, dur, audioClock.nowMs()/1000 + …)`.\n */\nexport interface SegmentAudio {\n /** Declarative events; `atSec` is relative to the segment's start. */\n schedule?: AudioEvent[];\n /** Imperative hook fired once at the segment's start during capture. */\n onEnter?: (audioClock: AudioClock, ctx: SegmentAudioCtx) => void;\n}\n\n/** Context handed to a `SegmentAudio.onEnter` callback. */\nexport interface SegmentAudioCtx {\n /** The Tone instrument wired into the promo mastering chain (has\n * `triggerAttackRelease`). Same target `applySchedule` plays through. */\n instrument: ScheduleTarget;\n /** Absolute capture-clock seconds at which this segment starts (= the audio\n * base time + the segment's resolved start). Add your own offsets to this. */\n startSec: number;\n /** The render Score, if any (timing/pitch source). */\n score?: Score;\n}\n\n/** Minimal Tone-instrument surface the audio scheduler needs. */\nexport interface ScheduleTarget {\n triggerAttackRelease: (note: unknown, dur: unknown, time?: unknown, velocity?: unknown) => void;\n}\n\n/**\n * Optional ENTER transition for a segment — how it comes IN from the PREVIOUS\n * segment (the one immediately before it in the timeline). Purely VISUAL and\n * fully additive: a segment with no `transition` hard-cuts exactly as before\n * (existing specs are byte-for-byte unchanged). The audio timeline / per-segment\n * `audio` hook is unaffected (transitions are a visual envelope only).\n *\n * During the first `durationMs` of the incoming segment, the runner renders BOTH\n * the previous segment (its last drawn state, held) AND the incoming segment,\n * applying an eased envelope (`easeInOut` by default):\n * - `crossfade` — outgoing opacity 1→0, incoming 0→1 (the keystone; always on).\n * - `push` — both slide along `direction`: incoming enters from off-frame\n * while outgoing exits the opposite way (a hard slide, no fade).\n * - `wipe` — incoming is revealed by a moving clip edge along `direction`;\n * the outgoing shows through the un-wiped region (no fade).\n *\n * The overlap does NOT add time — the transition lives INSIDE the incoming\n * segment's own window, so `visualTimelineMs` (and the gate's A/V-duration check)\n * are unchanged. `durationMs` is clamped to the shorter of the two adjoining\n * segment lengths so a transition can't run past either segment.\n */\nexport interface SegmentTransition {\n /** Transition style. Start with `crossfade`. */\n type: 'crossfade' | 'push' | 'wipe';\n /** Length of the enter window in ms (from the incoming segment's start). */\n durationMs: number;\n /**\n * Slide / wipe direction for `push` and `wipe` (ignored by `crossfade`).\n * Names the edge the INCOMING segment comes FROM. Default `left`.\n */\n direction?: 'left' | 'right' | 'up' | 'down';\n /** Eased envelope, 0..1 → 0..1. Defaults to `easeInOut`. */\n easing?: Easing;\n}\n\nexport interface TimelineSegment {\n /** [start, end] in SECONDS; end may be \"end\" / \"end-N\". */\n at: [number, TimeAnchor];\n layers: SpecLayer[];\n /** Optional sound scheduled at this segment's start (see SegmentAudio). */\n audio?: SegmentAudio;\n /**\n * Optional ENTER transition from the PREVIOUS segment (see SegmentTransition).\n * Omit for today's hard cut. Has no effect on the first segment (nothing to\n * transition from) unless `loop` is set on the spec.\n */\n transition?: SegmentTransition;\n}\n\nexport interface SceneSpec {\n /** [width, height] px. */\n size: [number, number];\n /** Theme key (resolved to a PromoTheme by the host) OR an inline theme. */\n theme: string;\n /** \"audio\" = clip length follows the audio; \"fixed\" = use `durationSec`. */\n durationMode: 'audio' | 'fixed';\n /** Required when durationMode === \"fixed\". */\n durationSec?: number;\n timeline: TimelineSegment[];\n audio?: { voicing?: string; [k: string]: unknown };\n /** Capture frame rate. Default 30. */\n fps?: number;\n}\n\n// ─── Timeline resolution ──────────────────────────────────────────────────────\n\n/** Resolve a TimeAnchor to absolute seconds given the clip's total length. */\nexport function resolveAnchor(anchor: TimeAnchor, totalSec: number): number {\n if (typeof anchor === 'number') return anchor;\n if (anchor === 'end') return totalSec;\n const m = /^end-(\\d+(?:\\.\\d+)?)$/.exec(anchor);\n if (m) return totalSec - parseFloat(m[1]);\n throw new Error(`resolveAnchor: bad anchor \"${anchor}\"`);\n}\n\nexport interface ResolvedSegment {\n startMs: number;\n endMs: number;\n layers: SpecLayer[];\n audio?: SegmentAudio;\n transition?: SegmentTransition;\n}\n\n/** Resolve every segment's [start,end] to ms against the total clip length. */\nexport function resolveTimeline(spec: SceneSpec, totalSec: number): ResolvedSegment[] {\n return spec.timeline.map((seg) => {\n const startSec = resolveAnchor(seg.at[0], totalSec);\n const endSec = resolveAnchor(seg.at[1], totalSec);\n return {\n startMs: startSec * 1000,\n endMs: endSec * 1000,\n layers: seg.layers,\n audio: seg.audio,\n transition: seg.transition,\n };\n });\n}\n\n/** The visual timeline length in ms = the latest resolved segment end. */\nexport function visualTimelineMs(resolved: ResolvedSegment[]): number {\n return resolved.reduce((mx, s) => Math.max(mx, s.endMs), 0);\n}\n\n// ─── Built runner ─────────────────────────────────────────────────────────────\n\n/** A layer instance bound to its segment window + resolved props. */\ninterface BoundLayer {\n layer: Layer<any>;\n startMs: number;\n endMs: number;\n /** Index of the owning segment in the resolved timeline (for transitions). */\n segIndex: number;\n /** Whether this layer renders pinned to the screen (caption/branding) — drawn\n * AFTER the camera transform is reset. Heuristic by key for v1. */\n screenPinned: boolean;\n}\n\nconst SCREEN_PINNED_KEYS = new Set([\n 'caption', 'branding', 'cta', 'hook', 'reveal', 'portrait', 'safe-guides',\n 'mcq-card', 'beat-pulse', 'chord-ribbon', 'progress-ring', 'karaoke-caption',\n 'kinetic-text', 'countdown', 'particle-burst', 'texture', 'stat-counter', 'end-card',\n]);\n\nexport interface BuiltScene {\n W: number;\n H: number;\n fps: number;\n durationMs: number;\n /** Draw the whole composite at absolute time `tMs` onto `ctx2d`. Deterministic\n * (pure fn of tMs). Used by tests, golden frames, and the recordScenes wrapper. */\n renderFrame(ctx2d: CanvasRenderingContext2D, tMs: number): void;\n /** Free every layer's resources. */\n dispose(): void;\n /** The resolved timeline (for the gate / diagnostics). */\n resolved: ResolvedSegment[];\n /**\n * Schedule every segment's optional audio against the CAPTURE clock. Call once\n * at capture start, passing the Tone instrument (the promo sampler) and the\n * audio-clock base time in SECONDS (e.g. `sampler.audioNow()` / `Tone.now()`).\n * Each segment's events fire at `baseSec + segStartMs/1000 + atSec`; each\n * `onEnter` fires once with an AudioClock anchored to its segment start.\n * Deterministic w.r.t. the supplied base time (no wall-clock). No-op for specs\n * that declare no segment audio. Returns the count of events scheduled (handy\n * for tests/gate sanity).\n */\n scheduleAudio(instrument: ScheduleTarget, baseSec: number): number;\n}\n\nexport interface BuildSceneOpts {\n spec: SceneSpec;\n theme: PromoTheme;\n /** The parsed Score (timing source). Optional for non-musical specs. */\n score?: Score;\n /** Total clip length in seconds. With durationMode \"audio\" pass the audio\n * length; with \"fixed\" it defaults to spec.durationSec. */\n totalSec: number;\n /** Override the camera pose per absolute tMs (pan/zoom director). Identity by\n * default. */\n camera?: (tMs: number) => CameraState;\n /**\n * Offscreen-buffer factory used ONLY by segment `transition`s (a segment's\n * stack is rendered to a buffer, then composited onto the main canvas with the\n * transition envelope — so the envelope is robust even when inner layers set\n * their own `globalAlpha`). Unused when no segment has a transition.\n *\n * Default: `OffscreenCanvas` if available, else `document.createElement\n * ('canvas')`. Node/headless callers (and the node-canvas golden tests) pass a\n * factory backed by `createCanvas`. If no factory is resolvable AND a\n * transition fires, the runner falls back to a direct `globalAlpha` envelope\n * (crossfade only; degrades gracefully) rather than throwing.\n */\n bufferFactory?: BufferFactory;\n}\n\n/** Makes an offscreen drawing surface for transition compositing. */\nexport type BufferFactory = (\n w: number,\n h: number,\n) => { ctx: CanvasRenderingContext2D; image: CanvasImageSource };\n\n/** Resolve a default buffer factory from the environment (browser/Node). */\nfunction defaultBufferFactory(): BufferFactory | undefined {\n const g = globalThis as any;\n if (typeof g.OffscreenCanvas === 'function') {\n return (w, h) => {\n const c = new g.OffscreenCanvas(w, h);\n return { ctx: c.getContext('2d'), image: c };\n };\n }\n if (typeof g.document !== 'undefined' && g.document?.createElement) {\n return (w, h) => {\n const c = g.document.createElement('canvas');\n c.width = w; c.height = h;\n return { ctx: c.getContext('2d'), image: c };\n };\n }\n return undefined;\n}\n\n/**\n * Instantiate + init every layer in the spec and return a deterministic renderer.\n * Throws if a referenced layer key isn't registered or its props don't validate\n * (so build failures surface before capture — the gate also re-checks).\n */\nexport async function buildScene(opts: BuildSceneOpts): Promise<BuiltScene> {\n const { spec, theme, score } = opts;\n const [W, H] = spec.size;\n const fps = spec.fps ?? 30;\n const resolved = resolveTimeline(spec, opts.totalSec);\n const safe = safeBox(W, H);\n const camera = opts.camera ?? (() => identityCamera(W, H));\n const bufferFactory = opts.bufferFactory ?? defaultBufferFactory();\n const hasTransitions = resolved.some((s) => s.transition && s.transition.durationMs > 0);\n\n const clock: AudioClock = { nowMs: () => 0 };\n const baseCtx: Omit<RenderCtx, 'ctx2d'> = {\n W, H, score, audioClock: clock, theme, safeBox: safe, fps,\n };\n\n const bound: BoundLayer[] = [];\n for (let segIndex = 0; segIndex < resolved.length; segIndex++) {\n const seg = resolved[segIndex];\n for (const sl of seg.layers) {\n const factory = getLayerFactory(sl.k);\n if (!factory) {\n throw new Error(`buildScene: unknown layer \"${sl.k}\" (registered: ${registeredKeys().join(', ')})`);\n }\n const errs = factory.validateProps(sl.p ?? {});\n if (errs.length) {\n throw new Error(`buildScene: invalid props for \"${sl.k}\": ${errs.join('; ')}`);\n }\n const layer = factory.create();\n // init runs against a ctx that has no live ctx2d yet; layers that need to\n // rasterize use init's ctx only for sizing/theme (drawing happens in draw).\n await layer.init({ ...baseCtx, ctx2d: null as any }, sl.p ?? {});\n bound.push({\n layer,\n startMs: seg.startMs,\n endMs: seg.endMs,\n segIndex,\n screenPinned: SCREEN_PINNED_KEYS.has(sl.k),\n });\n }\n }\n\n const durationMs = visualTimelineMs(resolved);\n\n // ── Transition envelope ────────────────────────────────────────────────────\n // A `SegEnv` describes how a single segment's whole layer stack is composited\n // for this frame. `null` means \"draw exactly as a hard cut\" (no extra ctx ops),\n // which is what keeps no-transition specs byte-for-byte identical to before.\n interface SegEnv {\n /** Multiplied into globalAlpha for the segment's draws (crossfade). */\n alpha: number;\n /** Screen-space pixel translate (push). */\n dx: number;\n dy: number;\n /** Optional clip rect in screen space (wipe). Undefined = no clip. */\n clip?: { x: number; y: number; w: number; h: number };\n }\n\n /** Push/wipe travel for a direction: how far off-frame the incoming starts. */\n function dirVec(dir: SegmentTransition['direction']): { x: number; y: number } {\n switch (dir) {\n case 'right': return { x: 1, y: 0 };\n case 'up': return { x: 0, y: -1 };\n case 'down': return { x: 0, y: 1 };\n case 'left':\n default: return { x: -1, y: 0 };\n }\n }\n\n /**\n * For the INCOMING segment `i`, build the (incoming, outgoing) envelopes at the\n * eased progress `p` (0→1 across the enter window). `outgoing` is the previous\n * segment's held state. Both `null` outside a transition window (hard cut).\n */\n function transitionEnvs(\n tr: SegmentTransition,\n p: number,\n ): { incoming: SegEnv; outgoing: SegEnv } {\n if (tr.type === 'push') {\n const v = dirVec(tr.direction);\n // Incoming slides from off-frame (-W·v at p=0) to 0; outgoing slides off\n // the opposite edge (0 at p=0 to +W·v at p=1).\n const span = Math.abs(v.x) ? W : H;\n return {\n incoming: { alpha: 1, dx: -v.x * span * (1 - p), dy: -v.y * span * (1 - p) },\n outgoing: { alpha: 1, dx: v.x * span * p, dy: v.y * span * p },\n };\n }\n if (tr.type === 'wipe') {\n const v = dirVec(tr.direction);\n // Incoming is revealed by a growing clip rect advancing along the edge;\n // outgoing shows through the complementary (un-wiped) region.\n let inClip: SegEnv['clip'];\n let outClip: SegEnv['clip'];\n if (v.x !== 0) {\n const w = W * p;\n if (v.x < 0) { // incoming enters from the LEFT edge\n inClip = { x: 0, y: 0, w, h: H };\n outClip = { x: w, y: 0, w: W - w, h: H };\n } else { // from the RIGHT edge\n inClip = { x: W - w, y: 0, w, h: H };\n outClip = { x: 0, y: 0, w: W - w, h: H };\n }\n } else {\n const h = H * p;\n if (v.y < 0) { // from the TOP\n inClip = { x: 0, y: 0, w: W, h };\n outClip = { x: 0, y: h, w: W, h: H - h };\n } else { // from the BOTTOM\n inClip = { x: 0, y: H - h, w: W, h };\n outClip = { x: 0, y: 0, w: W, h: H - h };\n }\n }\n return {\n incoming: { alpha: 1, dx: 0, dy: 0, clip: inClip },\n outgoing: { alpha: 1, dx: 0, dy: 0, clip: outClip },\n };\n }\n // crossfade (default + keystone)\n return {\n incoming: { alpha: p, dx: 0, dy: 0 },\n outgoing: { alpha: 1 - p, dx: 0, dy: 0 },\n };\n }\n\n // A frame's render plan: which segments draw, at what time, with what envelope.\n interface PlanEntry { segIndex: number; drawTMs: number; env: SegEnv | null }\n\n /** Build the per-segment render plan for absolute time `tMs`. */\n function frameplan(tMs: number): PlanEntry[] {\n const plan: PlanEntry[] = [];\n for (let i = 0; i < resolved.length; i++) {\n const seg = resolved[i];\n // Is this segment in its own active window?\n if (tMs < seg.startMs || tMs >= seg.endMs) continue;\n const tr = seg.transition;\n const prev = i > 0 ? resolved[i - 1] : undefined;\n const enterEnd = tr ? seg.startMs + Math.max(0, tr.durationMs) : seg.startMs;\n const inEnter = tr && tr.durationMs > 0 && tMs < enterEnd && prev;\n if (!inEnter) {\n // Hard cut (env null → byte-identical to the legacy path).\n plan.push({ segIndex: i, drawTMs: tMs, env: null });\n continue;\n }\n const p = transitionProgress(tMs, seg.startMs, tr!.durationMs, tr!.easing ?? defaultEasing);\n const { incoming, outgoing } = transitionEnvs(tr!, p);\n // Outgoing = previous segment held at its last visible instant.\n const heldTMs = clamp(tMs, prev!.startMs, prev!.endMs - 1);\n plan.push({ segIndex: i - 1, drawTMs: heldTMs, env: outgoing });\n plan.push({ segIndex: i, drawTMs: tMs, env: incoming });\n }\n return plan;\n }\n\n /** Draw a segment's FULL stack (world + pinned) onto a target ctx at drawTMs. */\n function drawSegmentInto(\n target: CanvasRenderingContext2D, segIndex: number, drawTMs: number,\n ): void {\n const ctx: RenderCtx = { ...baseCtx, ctx2d: target };\n const cam = camera(drawTMs);\n // World-space pass (camera applied).\n target.save();\n applyToContext(target, cam, W, H);\n for (const b of bound) {\n if (b.segIndex !== segIndex || b.screenPinned) continue;\n if (drawTMs < b.startMs || drawTMs >= b.endMs) continue;\n b.layer.draw(ctx, drawTMs);\n }\n target.restore();\n // Screen-space pass (pinned chrome).\n target.save();\n target.setTransform(1, 0, 0, 1, 0, 0);\n for (const b of bound) {\n if (b.segIndex !== segIndex || !b.screenPinned) continue;\n if (drawTMs < b.startMs || drawTMs >= b.endMs) continue;\n b.layer.draw(ctx, drawTMs);\n }\n target.restore();\n }\n\n /**\n * Composite one plan entry onto the main ctx. For a transition entry the\n * segment's whole stack is rendered to an offscreen buffer first, then blitted\n * with the envelope (alpha for crossfade, translate for push, clip for wipe) —\n * so a single drawImage carries the envelope and inner-layer `globalAlpha`\n * writes can't leak through. Falls back to a direct alpha envelope if no buffer\n * factory is available (crossfade still works; push/wipe degrade to a fade).\n */\n function compositeEntry(\n ctx2d: CanvasRenderingContext2D, e: PlanEntry,\n ): void {\n if (e.env === null) {\n // Hard cut (no transition for this segment this frame).\n drawSegmentInto(ctx2d, e.segIndex, e.drawTMs);\n return;\n }\n const env = e.env;\n if (!bufferFactory) {\n // Graceful fallback: alpha-only envelope directly on the main ctx.\n ctx2d.save();\n ctx2d.globalAlpha = ctx2d.globalAlpha * env.alpha;\n drawSegmentInto(ctx2d, e.segIndex, e.drawTMs);\n ctx2d.restore();\n return;\n }\n const buf = bufferFactory(W, H);\n drawSegmentInto(buf.ctx, e.segIndex, e.drawTMs);\n ctx2d.save();\n ctx2d.setTransform(1, 0, 0, 1, 0, 0);\n if (env.alpha !== 1) ctx2d.globalAlpha = ctx2d.globalAlpha * env.alpha;\n if (env.clip) {\n ctx2d.beginPath();\n ctx2d.rect(env.clip.x, env.clip.y, env.clip.w, env.clip.h);\n ctx2d.clip();\n }\n // Blit the buffer at the (possibly translated) destination.\n ctx2d.drawImage(buf.image, env.dx, env.dy, W, H);\n ctx2d.restore();\n }\n\n function renderFrame(ctx2d: CanvasRenderingContext2D, tMs: number): void {\n clock.nowMs = () => tMs;\n const ctx: RenderCtx = { ...baseCtx, ctx2d };\n const cam = camera(tMs);\n\n // Fast path: when the spec has NO transitions at all, reproduce the legacy\n // single-pass compositing EXACTLY (one world save/restore for all active\n // layers, then one pinned pass). This keeps no-transition specs byte-for-byte\n // identical to before (op stream, goldens, determinism).\n if (!hasTransitions) {\n ctx2d.save();\n applyToContext(ctx2d, cam, W, H);\n for (const b of bound) {\n if (b.screenPinned) continue;\n if (tMs < b.startMs || tMs >= b.endMs) continue;\n b.layer.draw(ctx, tMs);\n }\n ctx2d.restore();\n\n ctx2d.save();\n ctx2d.setTransform(1, 0, 0, 1, 0, 0);\n for (const b of bound) {\n if (!b.screenPinned) continue;\n if (tMs < b.startMs || tMs >= b.endMs) continue;\n b.layer.draw(ctx, tMs);\n }\n ctx2d.restore();\n return;\n }\n\n // Transition-capable path: composite per segment in timeline order so\n // cross-segment z-order is preserved (outgoing under incoming). A segment\n // not mid-transition this frame composites as a plain hard cut (env null).\n const plan = frameplan(tMs);\n for (const e of plan) compositeEntry(ctx2d, e);\n }\n\n function scheduleAudio(instrument: ScheduleTarget, baseSec: number): number {\n let fired = 0;\n for (const seg of resolved) {\n if (!seg.audio) continue;\n const segStartSec = baseSec + seg.startMs / 1000;\n // Declarative events: applySchedule plays each at segStartSec + atSec.\n if (seg.audio.schedule?.length) {\n fired += applySchedule(instrument, seg.audio.schedule, segStartSec);\n }\n // Imperative escape hatch: fire once with a clock anchored at the segment\n // start (nowMs() == seg.startMs) so the callback can offset off it.\n if (seg.audio.onEnter) {\n const segClock: AudioClock = { nowMs: () => seg.startMs };\n seg.audio.onEnter(segClock, { instrument, startSec: segStartSec, score });\n }\n }\n return fired;\n }\n\n return {\n W, H, fps, durationMs, resolved, renderFrame, scheduleAudio,\n dispose() {\n for (const b of bound) b.layer.dispose?.();\n },\n };\n}\n\n// ─── Capture wrapper (reuses recordScenes) ────────────────────────────────────\n\nexport interface RecordSceneSpecOpts {\n built: BuiltScene;\n audioStream: MediaStream;\n background?: string;\n onProgress?: RecordOpts['onProgress'];\n /** Inject the recorder for tests. Defaults to recordScenes. */\n record?: (scenes: Scene[], o: RecordOpts) => Promise<Blob>;\n /**\n * OPTIONAL per-segment audio scheduling. When a spec declares segment audio\n * (SegmentAudio), pass the Tone instrument (the promo sampler) and a clock so\n * the runner can schedule each segment's sound against the capture timeline.\n * `audioNow()` returns the audio-clock seconds at capture start (e.g.\n * `sampler.audioNow()` / `Tone.now()`); `lookaheadSec` (default 0.1) shifts\n * the whole schedule forward a touch so the first event isn't already in the\n * past when the recorder spins up. Omit entirely → pure-visual capture\n * (unchanged behavior).\n */\n segmentAudio?: {\n instrument: ScheduleTarget;\n audioNow: () => number;\n lookaheadSec?: number;\n };\n}\n\n/**\n * Capture a built scene to a Blob by wrapping the whole timeline as a SINGLE\n * composite Scene and handing it to recordScenes — so audio sync, captureStream,\n * and MediaRecorder all come from the existing, working path. recordScenes\n * clears to `background` each frame and gives us a 0..1 t; we expand it back to\n * absolute ms and call renderFrame.\n */\nexport async function recordSceneSpec(opts: RecordSceneSpecOpts): Promise<Blob> {\n const { built } = opts;\n const record = opts.record ?? recordScenes;\n const composite: Scene = {\n durationMs: built.durationMs,\n // Schedule any per-segment audio against the capture clock exactly when the\n // first frame is entered, so it's anchored to the same start the recorder\n // uses. onEnter fires once per Scene (see recordScenes), so this runs once.\n onEnter: opts.segmentAudio\n ? () => {\n const sa = opts.segmentAudio!;\n built.scheduleAudio(sa.instrument, sa.audioNow() + (sa.lookaheadSec ?? 0.1));\n }\n : undefined,\n draw: (ctx, t01) => built.renderFrame(ctx, t01 * built.durationMs),\n };\n return record([composite], {\n audioStream: opts.audioStream,\n width: built.W,\n height: built.H,\n fps: built.fps,\n background: opts.background,\n onProgress: opts.onProgress,\n });\n}\n","// Pre-render invariant GATE (S1b) — the production \"don't ship a broken video\"\n// guard. Runs around a render and FAILS (returns errors / throws) so a broken\n// clip is never upserted into the catalog. Generalizes RSR's verify()/avdiag.\n//\n// Checks (spec: Testing & quality gates §0):\n// A. A/V duration match: |visualTimelineMs − audioMs| ≤ 60ms.\n// B. Placement sanity: required positions resolve (no NaN / out-of-bounds);\n// nothing required drawn outside safeBox when safezone is on.\n// C. Fonts loaded before the first captured frame.\n// D. Spec valid: every layer key registered + props validate.\n// E. Real output: capture produced ≥ expected frames + an audio track.\n//\n// In-code (pure, unit-tested here): A, B, D, and the *math* of E (frame-count /\n// track-count comparison). Environment-coupled probes are exposed as HOOKS the\n// smvp web-capture recipe wires up: font readiness (C, document.fonts) and\n// ffprobe (E, the actual stream/frame inspection of the produced file).\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { SceneSpec, ResolvedSegment } from './runner';\nimport { resolveTimeline, visualTimelineMs } from './runner';\nimport { getLayerFactory } from './registry';\nimport type { SafeBox } from '../video';\n\nexport interface GateError {\n check: 'av-duration' | 'placement' | 'fonts' | 'spec' | 'output';\n message: string;\n}\n\n/** A position a layer reports placing (for the placement-sanity check). */\nexport interface Placement {\n /** Label for diagnostics (e.g. \"note@1200ms\"). */\n label: string;\n x: number;\n y: number;\n /** Whether this placement is REQUIRED to be inside the safe box. */\n mustBeSafe?: boolean;\n}\n\nexport interface OutputProbe {\n /** Frames the capture actually produced. */\n frames: number;\n /** Audio tracks present (ffprobe a:0…). */\n audioTracks: number;\n /** Optional measured duration in ms (ffprobe), for a secondary A/V check. */\n durationMs?: number;\n}\n\nexport interface GateInput {\n spec: SceneSpec;\n /** Audio length in ms (the sounding length the capture targets). */\n audioMs: number;\n /** Total clip length in seconds used to resolve the timeline. */\n totalSec: number;\n /** Frame size for bounds + safe checks. */\n W: number;\n H: number;\n safeBox: SafeBox;\n /** Whether safezone enforcement is on for this clip. */\n safezone: boolean;\n /** Frame rate (for the expected-frame-count math). Default 30. */\n fps?: number;\n /** Required placements a layer reported (optional; pass [] if none). */\n placements?: Placement[];\n /**\n * Font-readiness hook. The smvp recipe passes `() => document.fonts.ready\n * .then(() => true)` (after loadBrandFonts). Default: assume loaded (true) so\n * headless unit runs don't fail on a missing document.\n */\n fontsReady?: () => boolean | Promise<boolean>;\n /**\n * Output probe hook. The smvp recipe runs ffprobe on the produced file and\n * passes the result. Omit to skip the output check (e.g. a pre-capture gate\n * pass that only validates the spec).\n */\n output?: OutputProbe;\n}\n\nconst AV_TOLERANCE_MS = 60;\n\n/**\n * Run the gate. Returns the list of failures ([] = passes). Pure except for the\n * optional async `fontsReady` hook. Call before capture (spec + A/V + placement)\n * and again after capture with `output` set (real-output check).\n */\nexport async function runGate(input: GateInput): Promise<GateError[]> {\n const errors: GateError[] = [];\n\n // D. Spec valid — fail fast on unknown layer / bad props.\n let resolved: ResolvedSegment[] = [];\n for (const seg of input.spec.timeline) {\n for (const sl of seg.layers) {\n const factory = getLayerFactory(sl.k);\n if (!factory) {\n errors.push({ check: 'spec', message: `unknown layer \"${sl.k}\"` });\n continue;\n }\n const errs = factory.validateProps(sl.p ?? {});\n for (const e of errs) errors.push({ check: 'spec', message: e });\n }\n }\n // Resolve the timeline; a malformed anchor surfaces as a spec error too.\n try {\n resolved = resolveTimeline(input.spec, input.totalSec);\n } catch (e) {\n errors.push({ check: 'spec', message: `timeline: ${(e as Error).message}` });\n }\n\n // A. A/V duration match.\n if (resolved.length) {\n const vis = visualTimelineMs(resolved);\n if (!Number.isFinite(vis) || !Number.isFinite(input.audioMs)) {\n errors.push({ check: 'av-duration', message: 'non-finite visual/audio duration' });\n } else if (Math.abs(vis - input.audioMs) > AV_TOLERANCE_MS) {\n errors.push({\n check: 'av-duration',\n message: `|visual ${Math.round(vis)}ms − audio ${Math.round(input.audioMs)}ms| = ${Math.round(\n Math.abs(vis - input.audioMs),\n )}ms > ${AV_TOLERANCE_MS}ms`,\n });\n }\n }\n\n // B. Placement sanity — no NaN, inside frame, and inside safeBox when required.\n for (const p of input.placements ?? []) {\n if (!Number.isFinite(p.x) || !Number.isFinite(p.y)) {\n errors.push({ check: 'placement', message: `${p.label}: NaN/∞ position` });\n continue;\n }\n if (p.x < 0 || p.x > input.W || p.y < 0 || p.y > input.H) {\n errors.push({ check: 'placement', message: `${p.label}: (${p.x},${p.y}) outside ${input.W}×${input.H}` });\n }\n if (input.safezone && p.mustBeSafe) {\n const b = input.safeBox;\n if (p.x < b.left || p.x > b.right || p.y < b.top || p.y > b.bottom) {\n errors.push({ check: 'placement', message: `${p.label}: outside safe box` });\n }\n }\n }\n\n // C. Fonts loaded (hook; default true so headless unit runs pass).\n const ready = input.fontsReady ? await input.fontsReady() : true;\n if (!ready) errors.push({ check: 'fonts', message: 'fonts not loaded before first frame' });\n\n // E. Real output (only when a probe was supplied — typically post-capture).\n if (input.output) {\n const expectedFrames = Math.floor((input.fps ?? 30) * (input.audioMs / 1000) * 0.5); // ≥ half is generous\n if (input.output.frames < expectedFrames) {\n errors.push({\n check: 'output',\n message: `only ${input.output.frames} frames (expected ≥ ${expectedFrames})`,\n });\n }\n if (input.output.audioTracks < 1) {\n errors.push({ check: 'output', message: 'no audio track in output' });\n }\n if (\n input.output.durationMs != null &&\n Math.abs(input.output.durationMs - input.audioMs) > AV_TOLERANCE_MS\n ) {\n errors.push({\n check: 'output',\n message: `output duration ${Math.round(input.output.durationMs)}ms vs audio ${Math.round(\n input.audioMs,\n )}ms exceeds ${AV_TOLERANCE_MS}ms`,\n });\n }\n }\n\n return errors;\n}\n\n/** Convenience: run the gate and throw a single aggregated error on failure. */\nexport async function assertGate(input: GateInput): Promise<void> {\n const errors = await runGate(input);\n if (errors.length) {\n throw new Error(\n `pre-render gate failed (${errors.length}):\\n` +\n errors.map((e) => ` [${e.check}] ${e.message}`).join('\\n'),\n );\n }\n}\n","// Notation camera bridge (S2) — expresses RSR's followBox/crop/scroll as the\n// shared camera primitive (../camera frameRect/lerpCamera), so the scroll-cursor\n// follow window \"rides the camera\" and downstream layers (falling-notes, labels)\n// can pan/zoom off the SAME pose instead of re-deriving RSR's bespoke crop math.\n//\n// Equivalence (proved in tests/scene-camera-equivalence.test.ts): RSR scrolls by\n// re-cropping a canvas `src` window and fitting it to the band. The identical\n// visual move is a camera that frames the follow window (in the base world layout)\n// to fill the band. Given the base layout L (full content -> band) and a per-frame\n// follow box F (canvas coords), the follow window in WORLD/screen coords is the\n// rect L maps F to. frameRect(worldFollowRect, bandW, bandH) is the pose that\n// scrolls/zooms that window to fill the band — the same pixels RSR's re-crop draws.\n\nimport type { Box, RenderedNotation } from '../promo';\nimport { frameRect, type CameraState } from './camera';\nimport { FOLLOW_PAD, type NotationLayout } from './notationGeometry';\n\n/** Map a canvas-space box through a base notation layout into world/screen coords. */\nexport function mapBoxThroughLayout(base: NotationLayout, b: Box): Box {\n const fx = base.rect.dw / base.src.w;\n const fy = base.rect.dh / base.src.h;\n return {\n x: base.rect.dx + (b.x - base.src.x) * fx,\n y: base.rect.dy + (b.y - base.src.y) * fy,\n w: b.w * fx,\n h: b.h * fy,\n };\n}\n\n/** RSR's per-frame `src` crop from a follow box (FOLLOW_PAD expand + clamp) —\n * the exact canvas window RSR's drawNotation blits (+page.svelte:289-298). */\nexport function followSrcBox(rn: RenderedNotation, focusBoxCanvas: Box): Box {\n const px = (focusBoxCanvas.w * (FOLLOW_PAD - 1)) / 2;\n const py = (focusBoxCanvas.h * (FOLLOW_PAD - 1)) / 2;\n const src: Box = {\n x: Math.max(0, focusBoxCanvas.x - px),\n y: Math.max(0, focusBoxCanvas.y - py),\n w: focusBoxCanvas.w + 2 * px,\n h: focusBoxCanvas.h + 2 * py,\n };\n src.w = Math.min(src.w, rn.canvas.width - src.x);\n src.h = Math.min(src.h, rn.canvas.height - src.y);\n return src;\n}\n\n/**\n * The camera pose that scrolls/zooms the base (fixed) notation blit so the follow\n * window fills a `viewW`×`viewH` viewport — the camera-primitive expression of\n * RSR's per-frame re-crop. Composing this pose with the base blit reproduces RSR's\n * `notationLayout({focusBox})` exactly (proven in scene-notation-camera.test.ts).\n *\n * We frame the SAME canvas window RSR crops (FOLLOW_PAD-expanded `src`), mapped\n * into base-world coords, into the viewport. `viewW`/`viewH` are the dest-rect\n * dimensions (so the contain-fit matches RSR's band-fit).\n */\nexport function cameraForFollow(\n rn: RenderedNotation,\n base: NotationLayout,\n focusBoxCanvas: Box,\n viewW: number,\n viewH: number,\n): CameraState {\n const src = followSrcBox(rn, focusBoxCanvas);\n const world = mapBoxThroughLayout(base, src);\n return frameRect(world, viewW, viewH, 0);\n}\n","// Standalone demo (S3) — proves the headline falling-notes + keyboard capability\n// end-to-end through the runner over a REAL Score (parsed from a bundled\n// grand-staff MusicXML, so L/R hands show). Reusable as the basis of an\n// RSR/whozart variant later; no app is wired here.\n//\n// Returns a SceneSpec + the parsed Score so a host (or the golden test) can\n// buildScene() it and render/capture deterministic frames.\n\nimport type { SceneSpec } from '../runner';\nimport type { Score } from '../score';\nimport { scoreFromMusicXML, type ScoreFromMusicXMLOpts } from '../score';\n\nexport interface FallingKeyboardDemoOpts {\n /** Frame size. Default phone-portrait 1080×1920. */\n size?: [number, number];\n /** Theme key for the spec. Default \"rsr\". */\n theme?: string;\n /** falling-notes lead time (ms). Default 2200. */\n leadMs?: number;\n /** Colour mode for both layers. Default \"hand\". */\n colorBy?: 'hand' | 'pitch-class';\n /** Keyboard range. Default \"auto\" (fit the piece's pitch span). */\n range?: '88' | 'auto';\n /** Forwarded to scoreFromMusicXML (tempo fallback/override/osmdFactory). */\n scoreOpts?: ScoreFromMusicXMLOpts;\n}\n\n/** The SceneSpec for the demo (background + falling-notes + keyboard, full clip). */\nexport function fallingKeyboardDemoSpec(opts: FallingKeyboardDemoOpts = {}): SceneSpec {\n const size = opts.size ?? [1080, 1920];\n const leadMs = opts.leadMs ?? 2200;\n const colorBy = opts.colorBy ?? 'hand';\n const range = opts.range ?? 'auto';\n return {\n size,\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'ink' } },\n // keyboard first so it publishes its layout before falling-notes inits;\n // draw order: keyboard bed under the falling blocks would hide them, so\n // we draw falling-notes ON TOP of the keyboard bed (falling listed last).\n { k: 'keyboard', p: { range, colorBy } },\n { k: 'falling-notes', p: { keyboard: true, colorBy, leadMs } },\n ],\n },\n ],\n };\n}\n\n/** Parse the demo Score from MusicXML (the caller supplies the XML string so the\n * bundling/fixture path stays in the host/test, not in src). */\nexport async function fallingKeyboardDemoScore(\n xml: string,\n opts: FallingKeyboardDemoOpts = {},\n): Promise<Score> {\n return scoreFromMusicXML(xml, opts.scoreOpts);\n}\n","// Standalone demo (S4) — proves the wrapped promo-card layers (hook / reveal /\n// portrait / cta) + spectrum + branding COMPOSE through the runner over a\n// timeline, with no app dependency. A host (or the golden test) buildScene()s\n// this and renders deterministic frames.\n//\n// Timeline (totalSec configurable, default 12s):\n// [0, hookSec] background + hook + branding\n// [hookSec, revealSec] background + spectrum + portrait + branding\n// [end-ctaSec, end] background + cta + branding\n//\n// Each card wrapper gets startMs/durationMs matching its segment so its internal\n// t01 sweeps 0..1 over the segment (the wrappers can't read segment bounds from\n// the runner — see promoCards.ts).\n\nimport type { SceneSpec } from '../runner';\n\nexport interface PromoCardsDemoOpts {\n /** Frame size. Default phone-portrait 1080×1920. */\n size?: [number, number];\n /** Theme key. Default \"rsr\". */\n theme?: string;\n /** Total clip length (s). Default 12. */\n totalSec?: number;\n /** Hook segment length (s). Default 2.2. */\n hookSec?: number;\n /** Reveal/portrait segment length (s). Default 4. */\n revealSec?: number;\n /** CTA segment length (s) at the end. Default 3. */\n ctaSec?: number;\n /** Hook lines. */\n hookLines?: string[];\n /** Reveal/portrait title (composer). */\n title?: string;\n /** Reveal/portrait subtitle. */\n subtitle?: string;\n /** Optional portrait URL (loaded crossOrigin in init; null → initials badge). */\n portraitUrl?: string | null;\n /** Optional fun fact under the portrait. */\n funFact?: string;\n /** CTA lines. */\n ctaLines?: string[];\n /** Brand wordmark. Default theme.brand (resolved by the host). */\n brand?: string;\n}\n\n/** Build the demo SceneSpec wiring the S4 wrapper layers across a timeline. */\nexport function promoCardsDemoSpec(opts: PromoCardsDemoOpts = {}): SceneSpec {\n const size = opts.size ?? [1080, 1920];\n const totalSec = opts.totalSec ?? 12;\n const hookSec = opts.hookSec ?? 2.2;\n const revealSec = opts.revealSec ?? 4;\n const ctaSec = opts.ctaSec ?? 3;\n const hookLines = opts.hookLines ?? ['Can you', 'name this?'];\n const title = opts.title ?? 'Claude Debussy';\n const subtitle = opts.subtitle ?? '1862–1918';\n const ctaLines = opts.ctaLines ?? ['Train your ear', 'realeartrainer.com'];\n const brand = opts.brand;\n\n const ms = (s: number) => Math.round(s * 1000);\n const hookStart = 0;\n const revealStart = hookSec;\n const ctaStart = totalSec - ctaSec;\n\n return {\n size,\n theme: opts.theme ?? 'rsr',\n durationMode: 'fixed',\n durationSec: totalSec,\n timeline: [\n // background spans the whole clip.\n { at: [0, 'end'], layers: [{ k: 'background', p: { style: 'paper' } }] },\n // hook card.\n {\n at: [hookStart, hookSec],\n layers: [{ k: 'hook', p: { lines: hookLines, brand, startMs: ms(hookStart), durationMs: ms(hookSec) } }],\n },\n // reveal phase: audio-reactive spectrum + the portrait medallion.\n {\n at: [revealStart, revealStart + revealSec],\n layers: [\n { k: 'spectrum', p: {} },\n {\n k: 'portrait',\n p: {\n title, subtitle, url: opts.portraitUrl ?? null, funFact: opts.funFact,\n startMs: ms(revealStart), durationMs: ms(revealSec),\n },\n },\n ],\n },\n // end-card CTA.\n {\n at: [ctaStart, 'end'],\n layers: [{ k: 'cta', p: { lines: ctaLines, startMs: ms(ctaStart), durationMs: ms(ctaSec) } }],\n },\n // persistent branding across the whole clip.\n { at: [0, 'end'], layers: [{ k: 'branding', p: { logo: brand } }] },\n ],\n audio: { voicing: 'reading' },\n };\n}\n","// Standalone demos (S5) — assemble the four extended-catalog layers into SceneSpecs\n// a host (or the golden test) can buildScene() + render deterministically. No app\n// is wired here; these are the reusable basis of later RSR/RET/whozart variants.\n\nimport type { SceneSpec } from '../runner';\nimport type { ChordSpan } from '../harmonyTrack';\nimport type { LabelMode } from '../degreeLabels';\nimport type { RenderedNotation } from '../../promo';\nimport type { Quiz } from '../quizCard';\nimport type { Section } from '../sectionMinimap';\n\nexport interface ExtendedDemoOpts {\n size?: [number, number];\n theme?: string;\n}\n\n/** Counting track + bouncing ball + scale-degree labels over a keyboard. */\nexport function countingDegreeDemoSpec(\n opts: ExtendedDemoOpts & { subdiv?: 1 | 2 | 4; labelMode?: LabelMode; range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'keyboard', p: { range: opts.range ?? 'auto' } },\n { k: 'degree-labels', p: { mode: opts.labelMode ?? 'degree' } },\n { k: 'counting-track', p: { subdiv: opts.subdiv ?? 2 } },\n ],\n },\n ],\n };\n}\n\n/** Staff↔keyboard ray over a (pre-rendered) notation + keyboard. */\nexport function staffRayDemoSpec(\n rendered: RenderedNotation,\n opts: ExtendedDemoOpts & { range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n // notation in a top band; keyboard a short strip at the very bottom, so\n // the rays span a clear vertical gap between staff and keys.\n { k: 'notation', p: { rendered, bandHeight: (opts.size?.[1] ?? 1920) * 0.28 } },\n { k: 'keyboard', p: { range: opts.range ?? '88', height: (opts.size?.[1] ?? 1920) * 0.16 } },\n { k: 'staff-keyboard-ray', p: {} },\n ],\n },\n ],\n };\n}\n\n/** Functional-harmony coloring (band + keys) over a keyboard, driven by a chord track. */\nexport function harmonyDemoSpec(\n chordTrack: ChordSpan[],\n opts: ExtendedDemoOpts & { range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'ink' } },\n { k: 'keyboard', p: { range: opts.range ?? 'auto' } },\n { k: 'functional-harmony', p: { chordTrack, modes: ['band', 'keys'] } },\n ],\n },\n ],\n };\n}\n\n// ─── S5 batch 2 demos ──────────────────────────────────────────────────────────\n\n/** MCQ poll card over a plain background (RET quiz / whozart name-that-piece). */\nexport function mcqDemoSpec(quiz: Quiz, opts: ExtendedDemoOpts = {}): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'fixed',\n durationSec: (quiz.endMs) / 1000,\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'mcq-card', p: { quiz } },\n ],\n },\n ],\n };\n}\n\n/** Circle-of-fifths widget (optionally animating a modulation). */\nexport function circleOfFifthsDemoSpec(\n opts: ExtendedDemoOpts & { toKey?: string; fromMs?: number; toMs?: number } = {},\n): SceneSpec {\n const p: Record<string, unknown> = {};\n if (opts.toKey) { p.toKey = opts.toKey; p.fromMs = opts.fromMs ?? 0; p.toMs = opts.toMs ?? 2000; }\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'circle-of-fifths', p },\n ],\n },\n ],\n };\n}\n\n/** Pitch-contour line over the melody (Score.notes) + a section minimap. */\nexport function contourMinimapDemoSpec(\n opts: ExtendedDemoOpts & { sections?: Section[]; measureCount?: number } = {},\n): SceneSpec {\n const mini: Record<string, unknown> = {};\n if (opts.sections) mini.sections = opts.sections;\n else if (opts.measureCount) mini.measureCount = opts.measureCount;\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'pitch-contour', p: {} },\n { k: 'section-minimap', p: mini },\n ],\n },\n ],\n };\n}\n"],"mappings":"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","names":["msPerBeat","w","timeToX","DEFAULT_SIZE","DEFAULT_FADE","roundRect","measureCount","measureCount","rgba","DEFAULTS","rgba","DEFAULTS","rgba","SPEC_BARS","clamp01","lerpHex","syntheticMagnitude","binByteFreq","DEFAULTS","rgba","DEFAULTS","clamp01","wrap","DEFAULTS","rgba","DEFAULTS","rgba","rgba","DEFAULTS","DEFAULTS","easeOut","clamp01","DEFAULTS","clamp01","easeOut","DEFAULTS","clamp01","roundedPath"]}
1
+ {"version":3,"sources":["../../src/scene/score.ts","../../src/scene/math.ts","../../src/scene/caption.ts","../../src/scene/layers/demo.ts","../../src/scene/keyboardGeometry.ts","../../src/scene/keyboardStore.ts","../../src/scene/layers/keyboard.ts","../../src/scene/layers/fretboard.ts","../../src/scene/layers/fallingNotes.ts","../../src/scene/layers/promoCards.ts","../../src/scene/layers/spectrum.ts","../../src/scene/layers/branding.ts","../../src/scene/staffKeyboardRay.ts","../../src/scene/highlight.ts","../../src/scene/layers/staffKeyboardRay.ts","../../src/scene/countingGrid.ts","../../src/scene/layers/countingTrack.ts","../../src/scene/degreeLabels.ts","../../src/scene/layers/degreeLabels.ts","../../src/scene/harmonyTrack.ts","../../src/scene/layers/functionalHarmony.ts","../../src/scene/quizCard.ts","../../src/scene/layers/mcqCard.ts","../../src/scene/circleOfFifths.ts","../../src/scene/layers/circleOfFifths.ts","../../src/scene/pitchContour.ts","../../src/scene/layers/pitchContour.ts","../../src/scene/sectionMinimap.ts","../../src/scene/layers/sectionMinimap.ts","../../src/scene/layers/beatPulse.ts","../../src/scene/layers/chordRibbon.ts","../../src/scene/layers/progressRing.ts","../../src/scene/layers/radialSpectrum.ts","../../src/scene/layers/karaokeCaption.ts","../../src/scene/layers/intervalArcs.ts","../../src/scene/layers/kineticText.ts","../../src/scene/layers/countdown.ts","../../src/scene/layers/waveform.ts","../../src/scene/layers/scaleHighlight.ts","../../src/scene/layers/particleBurst.ts","../../src/scene/layers/tensionGraph.ts","../../src/scene/layers/texture.ts","../../src/scene/layers/statCounter.ts","../../src/scene/layers/endCard.ts","../../src/scene/layers/imageReveal.ts","../../src/scene/layers/compositeRhythm.ts","../../src/scene/layers/handIndicator.ts","../../src/scene/registry.ts","../../src/scene/audioLayers.ts","../../src/scene/camera.ts","../../src/scene/runner.ts","../../src/scene/gate.ts","../../src/scene/notationCamera.ts","../../src/scene/demos/fallingKeyboardDemo.ts","../../src/scene/demos/promoCardsDemo.ts","../../src/scene/demos/extendedDemo.ts"],"sourcesContent":["// scoreFromMusicXML — derive a timed, typed note list from MusicXML, via OSMD's\n// parsed source model. One input (MusicXML) drives engraving + timing + pitch +\n// hands + lyrics, so engraved note, falling note, keyboard key, and sounding note\n// are the same object (the render-components design — see\n// docs/superpowers/specs/2026-06-14-render-components-spec.md).\n//\n// Approach (validated by the S0 spike — spikes/score-from-musicxml/FINDINGS.md):\n// - osmd.load(xml) builds osmd.sheet (the source model). We read ONLY the source\n// model, never the graphical layout, so it runs headless (Node + the fake-2D\n// shim in ./headless) and unchanged in a browser.\n// - sheet.MusicPartManager.getIterator() is OSMD's PLAYBACK iterator: available\n// without render(), walks the score in playback order, and expands\n// repeats/voltas. CurrentEnrolledTimestamp = linear playback clock (whole\n// notes); CurrentAudibleVoiceEntries() = the voice entries that ONSET now.\n// - Whole-note timestamps/durations -> ms via tempo. v1 uses a single constant\n// tempo (see the tempo-map stub below).\n//\n// OSMD-internals gotchas baked in (pin OSMD; the MIDI-oracle test guards drift):\n// - pitch.getHalfTone() returns MIDI - 12 (its octave 0 == scientific octave 1).\n// - pitch.FundamentalNote is a CHROMATIC enum (C=0,D=2,E=4,F=5,G=7,A=9,B=11),\n// not a 0-6 step index.\n// - alter is pitch.AccidentalHalfTones (NOT pitch.Accidental — a different enum).\n// - Ties: only the tie START note appears as audible; NoteTie.Duration is the\n// whole chain's sounding length. Continuations are skipped.\n\n/* OSMD's typings don't expose the source-model internals we read, so the OSMD\n instance and its model are treated as `any` at the boundary. Everything we\n PRODUCE is fully typed (Score / ScoreNote). */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n// ─── Contracts (from the render-components spec) ────────────────────────────\n\nexport interface ScoreNote {\n /** MIDI note number (middle C = 60). */\n pitchMidi: number;\n /** Diatonic letter name: C D E F G A B. */\n step: string;\n /** Chromatic alteration in semitones: -1 flat, +1 sharp, 0 natural, ±2 double. */\n alter: number;\n /** Scientific octave (middle C = C4). */\n octave: number;\n /** Onset on the linear playback clock, in ms (repeats expanded). */\n onsetMs: number;\n /** Sounding duration in ms (tie chains merged). */\n durMs: number;\n /** Staff index within the whole sheet (0-based). */\n staff: number;\n /** Voice id within the part. */\n voice: number;\n /** Performing hand: grand-staff top -> \"R\", bottom -> \"L\" (see hand-inference stub). */\n hand: 'L' | 'R';\n /** Engraved bar this note sits in, 1-based. Repeat passes keep the printed\n * number — a cursor or scrubber points at bars as printed, not as played. */\n measure: number;\n /** Lyric syllable attached to this note, if any. */\n lyric?: string;\n /** Fingering digit attached to this note, if any. */\n fingering?: number;\n /** True for a grace note (acciaccatura/appoggiatura). Onset equals its\n * principal's onset as parsed; `realizeOrnaments` (../ornaments) re-times\n * the run to end there instead. Additive — existing consumers unaffected. */\n isGrace?: boolean;\n /** True when the note carries a trill mark. `realizeOrnaments` (../ornaments)\n * replaces it with the alternation; parsing leaves it as a single note.\n * Additive — existing consumers unaffected. */\n trill?: boolean;\n /** Performance velocity as a fraction of full (1 = full). Only ornament\n * notes set this today (`realizeOrnaments`); absent means \"full\" for every\n * pre-existing note. Additive — no current PlaybackSink reads it yet. */\n velocity?: number;\n}\n\n/**\n * Tempo map. v1 is a single constant tempo (one segment at t=0). The piecewise\n * shape is the documented seam for rubato / multiple `<sound tempo>` — see the\n * tempo-map stub note in scoreFromMusicXML.\n */\nexport interface TempoMap {\n /** Where the tempo came from: the XML's notated tempo, the fallback, or an override. */\n source: 'xml' | 'fallback' | 'override';\n /** Piecewise-constant segments, ordered by onset. v1 always has exactly one (at 0). */\n segments: Array<{ atMs: number; bpm: number }>;\n}\n\nexport interface Score {\n notes: ScoreNote[];\n tempoMap: TempoMap;\n /** End of the last sounding note, in ms. */\n durationMs: number;\n key?: string;\n /** Key signature as a fifths count (-7..7, sharps positive), when known — the\n * source `realizeOrnaments` (../ornaments) reads for diatonic trill neighbors.\n * Additive alongside `key` (the display string); same source, `<fifths>`. */\n keyFifths?: number;\n timeSig?: string;\n title?: string;\n composer?: string;\n}\n\nexport interface ScoreFromMusicXMLOpts {\n /** bpm to use when the XML has no notated tempo (DefaultStartTempoInBpm === 0). Default 100. */\n tempoFallback?: number;\n /** Force this bpm regardless of the XML's notated tempo (per-recipe override). */\n tempoOverride?: number;\n /**\n * Provide the OSMD instance. Defaults to a literal `import('opensheetmusicdisplay')`\n * + a detached div (works in a browser, or in Node after `setupHeadlessDom()`).\n * Inject for tests or non-DOM environments.\n */\n osmdFactory?: () => any;\n}\n\n// A beat is a quarter note = 1/4 whole note. v1 assumes an x/4 meter for the\n// bpm->ms scaling (onsets in whole-note space are exact regardless; only the\n// beat unit affects scaling). Non-x/4 meters are a documented follow-up.\nconst BEATS_PER_WHOLE_NOTE = 4;\nconst MAX_ITERATOR_STEPS = 200_000;\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Parse MusicXML into a timed, typed Score via OSMD's source model.\n *\n * @param xml MusicXML document (uncompressed string; unzip .mxl first).\n * @param opts tempoFallback / tempoOverride / osmdFactory.\n */\nexport async function scoreFromMusicXML(\n xml: string,\n opts: ScoreFromMusicXMLOpts = {},\n): Promise<Score> {\n const osmd = opts.osmdFactory ? opts.osmdFactory() : await defaultOsmd();\n\n // osmd.sheet (the source model) is populated even if the later graphical layout\n // throws (e.g. lyric text metrics under a bare DOM). So we tolerate load errors\n // and only fail if no source model came out.\n let loadError: unknown = null;\n try {\n await osmd.load(xml);\n } catch (e) {\n loadError = e;\n }\n const sheet = osmd.sheet;\n if (!sheet || !sheet.SourceMeasures?.length) {\n throw new Error(\n 'scoreFromMusicXML: OSMD produced no source model' +\n (loadError ? `: ${(loadError as Error).message}` : ''),\n );\n }\n\n const tempoMap = resolveTempo(sheet, opts);\n const wholeNoteToMs = makeWholeNoteToMs(tempoMap.segments[0].bpm);\n\n const it = sheet.MusicPartManager.getIterator();\n const notes: ScoreNote[] = [];\n\n let steps = 0;\n while (!it.EndReached && steps++ < MAX_ITERATOR_STEPS) {\n const enrolled = it.CurrentEnrolledTimestamp?.RealValue ?? 0;\n const onsetMs = wholeNoteToMs(enrolled);\n // MeasureNumber is the printed number (usually index+1, but pickup bars\n // shift it); fall back to the iterator's index when absent.\n const measure =\n it.CurrentMeasure?.MeasureNumber ?? (it.CurrentMeasureIndex ?? 0) + 1;\n // CurrentAudibleVoiceEntries is a METHOD (not a property) — the entries\n // sounding/onsetting at this step.\n const voiceEntries: any[] = it.CurrentAudibleVoiceEntries?.() ?? [];\n\n for (const ve of voiceEntries) {\n const voiceId = ve.ParentVoice?.VoiceId ?? 1;\n const sse = ve.ParentSourceStaffEntry;\n const staffIdx = sse?.ParentStaff?.idInMusicSheet ?? 0;\n const instrument = sse?.ParentStaff?.ParentInstrument;\n const hand = inferHand(instrument, sse?.ParentStaff);\n // OrnamentContainer is per-VOICE-ENTRY (not per-note); GetOrnament === 0\n // is OrnamentEnum.Trill (OSMD's VoiceData/OrnamentContainer.d.ts — not\n // exported from the package's public index, so pinned by value here,\n // same convention as the KeyEnum minor check in readKey below).\n const isTrill = ve.OrnamentContainer?.GetOrnament === 0;\n\n for (const n of ve.Notes ?? []) {\n if (n.isRestFlag || n.IsRest) continue;\n const pitch = n.Pitch;\n if (!pitch) continue;\n\n // Ties: skip continuation notes (the iterator only reports the START as\n // audible anyway, but be defensive), and give the START the whole chain's\n // length via NoteTie.Duration.\n const tie = n.NoteTie;\n if (tie && tie.StartNote && tie.StartNote !== n) continue;\n const wholeNoteLen =\n tie && tie.StartNote === n && tie.Duration\n ? tie.Duration.RealValue\n : n.Length?.RealValue ?? 0;\n\n const halfTone = pitch.getHalfTone?.();\n const pitchMidi = halfTone == null ? NaN : halfTone + 12;\n\n const note: ScoreNote = {\n pitchMidi,\n step: fundamentalToStep(pitch.FundamentalNote),\n alter: pitch.AccidentalHalfTones ?? 0,\n octave: octaveFromMidi(pitchMidi),\n onsetMs: Math.round(onsetMs),\n durMs: Math.round(wholeNoteToMs(wholeNoteLen)),\n staff: staffIdx,\n voice: voiceId,\n hand,\n measure,\n };\n const lyric = extractLyric(ve);\n if (lyric != null) note.lyric = lyric;\n const fingering = extractFingering(n);\n if (fingering != null) note.fingering = fingering;\n if (n.IsGraceNote) note.isGrace = true;\n if (isTrill) note.trill = true;\n notes.push(note);\n }\n }\n it.moveToNext();\n }\n\n notes.sort((a, b) => a.onsetMs - b.onsetMs || a.pitchMidi - b.pitchMidi);\n const durationMs = notes.reduce((mx, n) => Math.max(mx, n.onsetMs + n.durMs), 0);\n\n const keySig = readKeySignature(sheet);\n\n return {\n notes,\n tempoMap,\n durationMs,\n key: keySig.name,\n keyFifths: keySig.fifths,\n timeSig: readTimeSig(sheet),\n title: sheet.TitleString || undefined,\n composer: sheet.ComposerString || undefined,\n };\n}\n\n// ─── Tempo ───────────────────────────────────────────────────────────────────\n\n/**\n * Resolve the tempo for v1.\n *\n * Precedence: tempoOverride > XML notated tempo > tempoFallback (default 100).\n *\n * STUB — piecewise tempo map (rubato / accel. / multiple `<sound tempo>`): v1\n * emits a SINGLE constant segment at t=0. The {@link TempoMap.segments} array is\n * the documented seam: a follow-up reads per-measure tempo instructions from the\n * source model (sourceMeasure.TempoExpressions / TempoInBpm) and converts\n * enrolled-whole-note onsets to ms piecewise. Low priority — most promo clips use\n * one tempo, and the few rubato pieces are flagged by the MIDI-oracle test as\n * corpus issues, not extractor bugs (FINDINGS.md, risk #2).\n */\nfunction resolveTempo(sheet: any, opts: ScoreFromMusicXMLOpts): TempoMap {\n if (opts.tempoOverride && opts.tempoOverride > 0) {\n return { source: 'override', segments: [{ atMs: 0, bpm: opts.tempoOverride }] };\n }\n const xmlTempo = sheet.DefaultStartTempoInBpm;\n if (xmlTempo && xmlTempo > 0) {\n return { source: 'xml', segments: [{ atMs: 0, bpm: xmlTempo }] };\n }\n return { source: 'fallback', segments: [{ atMs: 0, bpm: opts.tempoFallback ?? 100 }] };\n}\n\n/** ms per whole note at a constant bpm (quarter-note beat; see BEATS_PER_WHOLE_NOTE). */\nfunction makeWholeNoteToMs(bpm: number): (wholeNotes: number) => number {\n const msPerBeat = 60000 / bpm;\n return (wholeNotes: number) => wholeNotes * BEATS_PER_WHOLE_NOTE * msPerBeat;\n}\n\n// ─── Hand inference ───────────────────────────────────────────────────────────\n\n/**\n * Infer the performing hand from the grand-staff layout.\n *\n * Grand staff (one instrument with `<staves>2`): top staff -> \"R\", bottom -> \"L\".\n * Verified correct on Für Elise / Twinkle (FINDINGS.md).\n *\n * STUB — SATB / two-`<score-part>` hand inference: when hands are SEPARATE parts\n * (RMT/SATB convention) there is no single grand staff, so we default to \"R\".\n * The documented follow-up (FINDINGS.md, risk #3) infers hand from clef\n * (treble -> R, bass -> L); OSMD doesn't expose clef cleanly post-parse, so that\n * needs either a raw-XML clef read or a per-recipe hint. Not blocking — RSR's\n * pieces use the grand-staff convention, which works today.\n */\nfunction inferHand(instrument: any, staff: any): 'L' | 'R' {\n const staves = instrument?.Staves;\n if (Array.isArray(staves) && staves.length >= 2 && staff) {\n const local = staves.indexOf(staff);\n if (local >= 0) return local === 0 ? 'R' : 'L';\n }\n return 'R';\n}\n\n// ─── Source-model readers ─────────────────────────────────────────────────────\n\n/** OSMD FundamentalNote is a CHROMATIC (pitch-class) enum, not a 0-6 step index. */\nfunction fundamentalToStep(f: number): string {\n return (\n ({ 0: 'C', 2: 'D', 4: 'E', 5: 'F', 7: 'G', 9: 'A', 11: 'B' } as Record<number, string>)[f] ??\n '?'\n );\n}\n\nfunction octaveFromMidi(midi: number): number {\n if (!Number.isFinite(midi)) return 0;\n return Math.floor(midi / 12) - 1;\n}\n\nfunction extractLyric(ve: any): string | undefined {\n const dict = ve.LyricsEntries;\n if (!dict || !dict.size) return undefined;\n const first = [...dict.values()][0];\n const text = first?.Text?.text ?? first?.Text;\n return typeof text === 'string' && text.length ? text : undefined;\n}\n\nfunction extractFingering(note: any): number | undefined {\n // OSMD attaches fingering as a Fingering technical instruction on the note.\n const raw = note?.Fingering?.value ?? note?.Fingering?.Value ?? note?.Fingering;\n const n = typeof raw === 'string' ? parseInt(raw, 10) : typeof raw === 'number' ? raw : NaN;\n return Number.isFinite(n) ? n : undefined;\n}\n\n// Diatonic key names by fifths on the circle, for major and minor modes.\nconst MAJOR_KEYS = ['Cb', 'Gb', 'Db', 'Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#'];\nconst MINOR_KEYS = ['Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#'];\n\n/**\n * Reads the key signature: both the display name (`Score.key`, e.g. \"G major\")\n * and the raw fifths count (`Score.keyFifths`, -7..7) that `realizeOrnaments`\n * uses for diatonic trill neighbors — same source read, additive second value.\n */\nfunction readKeySignature(sheet: any): { name?: string; fifths?: number } {\n // Class names are minified in OSMD builds, so we identify the KeyInstruction by\n // its shape (it carries `keyType` = fifths and `mode`) rather than by name.\n for (const entry of sheet?.SourceMeasures?.[0]?.FirstInstructionsStaffEntries ?? []) {\n for (const ins of entry?.Instructions ?? []) {\n if (typeof ins?.keyType === 'number') {\n const fifths = ins.keyType; // -7..+7\n const idx = fifths + 7;\n if (idx < 0 || idx > 14) return { fifths };\n // OSMD KeyEnum: major=0/1, minor=2 (others rare/modal — treat as major).\n const isMinor = ins.mode === 2;\n const name = (isMinor ? MINOR_KEYS : MAJOR_KEYS)[idx];\n return { name: name ? `${name} ${isMinor ? 'minor' : 'major'}` : undefined, fifths };\n }\n }\n }\n return {};\n}\n\nfunction readTimeSig(sheet: any): string | undefined {\n const ts = sheet?.SourceMeasures?.[0]?.ActiveTimeSignature;\n if (ts && Number.isFinite(ts.Numerator) && Number.isFinite(ts.Denominator)) {\n return `${ts.Numerator}/${ts.Denominator}`;\n }\n return undefined;\n}\n\n// ─── Default OSMD acquisition ────────────────────────────────────────────────\n\n/**\n * Default OSMD instance: literal dynamic import + a detached div. Works in a\n * browser, or in Node after `setupHeadlessDom()` has installed DOM globals.\n *\n * The specifier is a LITERAL so consumers' bundlers (Vite/Rollup) can statically\n * resolve + code-split it — a variable specifier would reach the browser as a\n * bare import and fail at runtime (mirrors promo.ts:renderNotation).\n */\nasync function defaultOsmd(): Promise<any> {\n if (typeof document === 'undefined') {\n throw new Error(\n \"scoreFromMusicXML: no DOM. In Node, import setupHeadlessDom from \" +\n \"'@real-music-packages/web-core/scene/headless' and call it first, \" +\n 'or pass opts.osmdFactory.',\n );\n }\n const mod: any = await import('opensheetmusicdisplay');\n // Browser bundlers expose the named export; Node's CJS interop puts it on default.\n const OpenSheetMusicDisplay = mod.OpenSheetMusicDisplay ?? mod.default?.OpenSheetMusicDisplay;\n return new OpenSheetMusicDisplay(document.createElement('div'), {\n backend: 'svg',\n autoResize: false,\n });\n}\n","// Pure math shared by the scene primitives (camera, highlight, captions,\n// timeline). Kept separate + dependency-free so it is trivially unit-testable.\n\nexport const clamp = (x: number, lo: number, hi: number): number =>\n x < lo ? lo : x > hi ? hi : x;\n\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t;\n\n/** Inverse-lerp: where does `x` sit in [a,b] as 0..1 (clamped, a===b -> 0). */\nexport const invLerp = (a: number, b: number, x: number): number =>\n a === b ? 0 : clamp((x - a) / (b - a), 0, 1);\n\nexport type Easing = (t: number) => number;\n\nexport const linear: Easing = (t) => t;\n/** Smoothstep ease-in-out. */\nexport const easeInOut: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return c * c * (3 - 2 * c);\n};\nexport const easeIn: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return c * c;\n};\nexport const easeOut: Easing = (t) => {\n const c = clamp(t, 0, 1);\n return 1 - (1 - c) * (1 - c);\n};\n\n/**\n * Eased 0..1 progress of a transition at absolute time `tMs`, given the incoming\n * segment's `startMs` and the transition `durationMs`. Returns 0 at/before the\n * start, 1 at/after start+durationMs, eased in between. Pure fn of t. Shared by\n * the runner so the same envelope drives crossfade alpha, push translate, and\n * wipe clip. `durationMs <= 0` → an instant 1 (degenerate hard-cut).\n */\nexport const transitionProgress = (\n tMs: number,\n startMs: number,\n durationMs: number,\n easing: Easing = easeInOut,\n): number => {\n if (durationMs <= 0) return 1;\n return easing(invLerp(startMs, startMs + durationMs, tMs));\n};\n","// Caption / subtitle engine (S1b) — timed burned-in text from a script.\n//\n// Silent-autoplay retention + accessibility: short-form video is watched muted,\n// so explainer clips need on-screen captions. A script is a list of cues with\n// in/out times; at any tMs the engine returns the active cue (one at a time) and\n// draws it inside the safe box near the bottom (above the phone action rail).\n//\n// The selection/visibility logic is pure (testable); drawCaption is the thin\n// renderer. respects safeBox so captions never sit under TikTok/Reels UI.\n\nimport type { SafeBox } from '../video';\nimport type { PromoTheme } from '../video';\nimport { clamp, invLerp } from './math';\n\nexport interface CaptionCue {\n text: string;\n /** Show from this time (ms). */\n inMs: number;\n /** Hide after this time (ms). */\n outMs: number;\n}\n\nexport type CaptionScript = CaptionCue[];\n\n/** The cue active at `tMs` (last one whose window contains t), or null. */\nexport function activeCue(script: CaptionScript, tMs: number): CaptionCue | null {\n let found: CaptionCue | null = null;\n for (const cue of script) {\n if (tMs >= cue.inMs && tMs < cue.outMs) found = cue;\n }\n return found;\n}\n\n/** Opacity 0..1 for a cue at `tMs` (short fade at both edges). */\nexport function cueOpacity(cue: CaptionCue, tMs: number, fadeMs = 150): number {\n if (tMs < cue.inMs || tMs >= cue.outMs) return 0;\n const inA = invLerp(cue.inMs, cue.inMs + fadeMs, tMs);\n const outA = 1 - invLerp(cue.outMs - fadeMs, cue.outMs, tMs);\n return clamp(Math.min(inA, outA), 0, 1);\n}\n\nexport interface CaptionStyle {\n /** Font size in px. Default 44. */\n size?: number;\n /** Pinned vertical position as a fraction of the safe-box height from its\n * top. Default 0.92 (near the bottom of the safe area). */\n yFrac?: number;\n /** Draw a translucent backing pill for legibility. Default true. */\n pill?: boolean;\n}\n\n/**\n * Greedy word-wrap into at most `maxLines` lines for the current ctx.font.\n * (Local copy so caption rendering doesn't depend on video.ts internals.)\n */\nfunction wrap(ctx: CanvasRenderingContext2D, text: string, maxW: number, maxLines: number): string[] {\n const words = text.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let cur = '';\n for (const w of words) {\n const next = cur ? `${cur} ${w}` : w;\n if (ctx.measureText(next).width > maxW && cur) {\n lines.push(cur);\n cur = w;\n if (lines.length === maxLines) break;\n } else {\n cur = next;\n }\n }\n if (cur && lines.length < maxLines) lines.push(cur);\n return lines;\n}\n\n/**\n * Draw the active caption (if any) inside the safe box. Pure function of tMs.\n * No-op when no cue is active. Drawn in VIEWPORT space (call after the camera\n * transform has been reset to identity) so captions stay pinned to the screen.\n */\nexport function drawCaption(\n ctx: CanvasRenderingContext2D,\n script: CaptionScript,\n tMs: number,\n safe: SafeBox,\n theme: PromoTheme,\n style: CaptionStyle = {},\n): void {\n const cue = activeCue(script, tMs);\n if (!cue) return;\n const alpha = cueOpacity(cue, tMs);\n if (alpha <= 0) return;\n\n const size = style.size ?? 44;\n const yFrac = style.yFrac ?? 0.92;\n const baseY = safe.top + safe.h * yFrac;\n\n ctx.save();\n ctx.globalAlpha = alpha;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.font = `bold ${size}px ${theme.fontBody}`;\n const lines = wrap(ctx, cue.text, safe.w * 0.92, 3);\n const lineH = size * 1.2;\n const blockH = lines.length * lineH;\n const top = baseY - blockH;\n\n if (style.pill !== false) {\n let maxW = 0;\n for (const ln of lines) maxW = Math.max(maxW, ctx.measureText(ln).width);\n const padX = size * 0.5;\n const padY = size * 0.3;\n ctx.fillStyle = 'rgba(0,0,0,0.55)';\n const pillW = Math.min(safe.w, maxW + padX * 2);\n ctx.fillRect(safe.cx - pillW / 2, top - padY, pillW, blockH + padY * 2);\n }\n\n ctx.fillStyle = '#ffffff';\n lines.forEach((ln, i) => {\n ctx.fillText(ln, safe.cx, top + lineH * (i + 0.5));\n });\n ctx.restore();\n}\n","// Trivial demo layers (S1b) — `background` and `caption`. They exist so the\n// runner is provably end-to-end (a SceneSpec renders frames through real layers)\n// WITHOUT pulling in the music layers, which are S2/S3. Each ships a\n// LayerFactory with a prop validator so the registry / gate can reject bad specs.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { drawCaption, type CaptionScript } from '../caption';\n\n// ─── background ────────────────────────────────────────────────────────────\n\nexport interface BackgroundProps {\n /** \"paper\" (theme.paper) | \"ink\" (theme.ink) | a literal CSS colour. */\n style?: string;\n}\n\nfunction backgroundLayer(): Layer<BackgroundProps> {\n let fill = '#000000';\n return {\n key: 'background',\n init(ctx, props) {\n const s = props.style ?? 'paper';\n fill = s === 'paper' ? ctx.theme.paper : s === 'ink' ? ctx.theme.ink : s;\n },\n draw(ctx) {\n const c = ctx.ctx2d;\n c.save();\n c.fillStyle = fill;\n c.fillRect(0, 0, ctx.W, ctx.H);\n c.restore();\n },\n };\n}\n\nexport const backgroundFactory: LayerFactory<BackgroundProps> = {\n key: 'background',\n create: backgroundLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['background: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.style != null && typeof p.style !== 'string') errs.push('background.style must be a string');\n return errs;\n },\n};\n\n// ─── caption ───────────────────────────────────────────────────────────────\n\nexport interface CaptionProps {\n /** Timed cues (in/out in ms relative to the clip start). */\n script: CaptionScript;\n size?: number;\n yFrac?: number;\n}\n\nfunction captionLayer(): Layer<CaptionProps> {\n let script: CaptionScript = [];\n let size: number | undefined;\n let yFrac: number | undefined;\n return {\n key: 'caption',\n init(_ctx: RenderCtx, props) {\n script = props.script ?? [];\n size = props.size;\n yFrac = props.yFrac;\n },\n draw(ctx, tMs) {\n drawCaption(ctx.ctx2d, script, tMs, ctx.safeBox, ctx.theme, { size, yFrac });\n },\n };\n}\n\nexport const captionFactory: LayerFactory<CaptionProps> = {\n key: 'caption',\n create: captionLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['caption: props must be an object'];\n const p = props as Record<string, unknown>;\n if (!Array.isArray(p.script)) {\n errs.push('caption.script must be an array of cues');\n } else {\n p.script.forEach((cue, i) => {\n const c = cue as Record<string, unknown>;\n if (typeof c?.text !== 'string') errs.push(`caption.script[${i}].text must be a string`);\n if (typeof c?.inMs !== 'number' || typeof c?.outMs !== 'number')\n errs.push(`caption.script[${i}] needs numeric inMs/outMs`);\n else if (c.outMs <= c.inMs) errs.push(`caption.script[${i}] outMs must be > inMs`);\n });\n }\n if (p.size != null && typeof p.size !== 'number') errs.push('caption.size must be a number');\n if (p.yFrac != null && typeof p.yFrac !== 'number') errs.push('caption.yFrac must be a number');\n return errs;\n },\n};\n","// Keyboard geometry (S3) — the pure, canvas-free layout shared by the `keyboard`\n// and `falling-notes` layers. Both compute (or read) the SAME KeyboardLayout, so\n// a falling note's column x is, by construction, the x of its key on the\n// keyboard (the headline \"two layers agree\" guarantee — proved in the invariant\n// tests).\n//\n// Coordinate space: world pixels (0,0 top-left, the runner's frame space). A\n// layout pins the keyboard to a strip [x, x+w] × [top, top+height]; key x's are\n// derived from an evenly-spaced WHITE-key grid with black keys overlaid at the\n// canonical between-the-whites offsets. Falling notes use `keyCenterX(midi)`.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\n/** Chromatic class is a black key (C#, D#, F#, G#, A#). */\nconst BLACK_PC = new Set([1, 3, 6, 8, 10]);\n\n/** Number of white keys at-or-below a midi note, counting from MIDI 0 (C-1). */\nfunction whiteIndexAtOrBelow(midi: number): number {\n // Whites per octave below the pitch class within the octave.\n const oct = Math.floor(midi / 12);\n const pc = midi - oct * 12;\n // 0-based white index of the white key AT OR BELOW each pitch class. White pcs\n // map to their own index; black pcs map to the white immediately BELOW them\n // (C#→C, D#→D, F#→F, G#→G, A#→A). (Previously this mapped black keys to the\n // white ABOVE, shifting every black key one white-width right — visible as a\n // Bb spilling off the right edge when the range topped out on a B.)\n const WHITES_BELOW = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6]; // C..B\n return oct * 7 + WHITES_BELOW[pc];\n}\n\nexport function isBlackKey(midi: number): boolean {\n return BLACK_PC.has(((midi % 12) + 12) % 12);\n}\n\n/** A resolved keyboard layout: which keys, where, pinned to a world strip. */\nexport interface KeyboardLayout {\n /** Lowest MIDI note drawn (inclusive). */\n lowMidi: number;\n /** Highest MIDI note drawn (inclusive). */\n highMidi: number;\n /** Strip left edge (world px). */\n x: number;\n /** Strip width (world px) — the white-key span. */\n w: number;\n /** Strip top edge (world px). */\n top: number;\n /** Strip height (world px) = white-key height. */\n height: number;\n /** White-key width (world px). */\n whiteW: number;\n /** First (leftmost) white key index, used to offset the white grid to x. */\n firstWhiteIndex: number;\n /** Count of white keys in [lowMidi, highMidi]. */\n whiteCount: number;\n}\n\n/** Standard 88-key piano: A0 (21) .. C8 (108). */\nexport const PIANO_LOW = 21;\nexport const PIANO_HIGH = 108;\n\n/** Snap a midi down to the nearest white key (so the range starts/ends on a white). */\nfunction snapDownToWhite(midi: number): number {\n let m = midi;\n while (isBlackKey(m)) m--;\n return m;\n}\nfunction snapUpToWhite(midi: number): number {\n let m = midi;\n while (isBlackKey(m)) m++;\n return m;\n}\n\nexport interface KeyboardLayoutOpts {\n /** \"88\" = full piano; \"auto\" = derive from a pitch span (padded to whites). */\n range?: '88' | 'auto';\n /** For \"auto\": the [lowMidi, highMidi] span to cover (snapped out to whites,\n * padded by one white each side so edge keys aren't flush). */\n span?: [number, number];\n /** Strip left/width/top/height in world px. */\n x: number;\n w: number;\n top: number;\n height: number;\n}\n\n/** Build a KeyboardLayout. Pure. */\nexport function keyboardLayout(opts: KeyboardLayoutOpts): KeyboardLayout {\n let low: number;\n let high: number;\n if (opts.range === 'auto' && opts.span) {\n low = snapDownToWhite(opts.span[0]);\n high = snapUpToWhite(opts.span[1]);\n // Pad one white key each side for breathing room (and so falling columns at\n // the extremes aren't half-clipped).\n low = snapDownToWhite(low - 1);\n high = snapUpToWhite(high + 1);\n } else {\n low = PIANO_LOW;\n high = PIANO_HIGH;\n }\n const firstWhiteIndex = whiteIndexAtOrBelow(low);\n const lastWhiteIndex = whiteIndexAtOrBelow(high);\n const whiteCount = lastWhiteIndex - firstWhiteIndex + 1;\n const whiteW = opts.w / whiteCount;\n return {\n lowMidi: low,\n highMidi: high,\n x: opts.x,\n w: opts.w,\n top: opts.top,\n height: opts.height,\n whiteW,\n firstWhiteIndex,\n whiteCount,\n };\n}\n\n/** The pitch span [min,max] of a Score's notes (for \"auto\" range). */\nexport function scorePitchSpan(midis: number[]): [number, number] {\n if (!midis.length) return [PIANO_LOW, PIANO_HIGH];\n return [Math.min(...midis), Math.max(...midis)];\n}\n\n/**\n * Resolve a KeyboardLayout from the shared keyboard placement props + the Score's\n * pitch span. The `keyboard` and `falling-notes` layers BOTH call this with the\n * same inputs in standalone mode, so they produce identical geometry even without\n * the store (the store just covers props that differ). Pure.\n *\n * Placement: the strip spans the safe box horizontally; its bottom sits at\n * `bottomY` (world px) with the given `height`.\n */\nexport function resolveKeyboardLayout(args: {\n range: '88' | 'auto';\n pitchMidis: number[];\n left: number;\n width: number;\n bottomY: number;\n height: number;\n}): KeyboardLayout {\n return keyboardLayout({\n range: args.range,\n span: args.range === 'auto' ? scorePitchSpan(args.pitchMidis) : undefined,\n x: args.left,\n w: args.width,\n top: args.bottomY - args.height,\n height: args.height,\n });\n}\n\n/**\n * Center x (world px) of a note's KEY on the keyboard. White keys sit on the\n * even grid; black keys are nudged to sit between their two neighbouring whites\n * (the canonical piano offset), so a falling block lands centered over the right\n * key. This is the SINGLE source both layers use — they agree by construction.\n */\nexport function keyCenterX(layout: KeyboardLayout, midi: number): number {\n const wIdx = whiteIndexAtOrBelow(midi) - layout.firstWhiteIndex;\n const whiteCenter = layout.x + (wIdx + 0.5) * layout.whiteW;\n if (!isBlackKey(midi)) return whiteCenter;\n // A black key sits on the boundary between its lower white and the next white,\n // i.e. half a white-width to the RIGHT of the lower white's center.\n return whiteCenter + layout.whiteW / 2;\n}\n\n/** Width (world px) a falling-note column should use for a pitch. Black keys are\n * drawn narrower (like the physical key); white keys ~ a white-key width. */\nexport function keyColumnWidth(layout: KeyboardLayout, midi: number): number {\n return isBlackKey(midi) ? layout.whiteW * 0.6 : layout.whiteW * 0.9;\n}\n\n/** The drawable rectangle for a key on the keyboard strip (white or black). */\nexport interface KeyRect {\n x: number;\n y: number;\n w: number;\n h: number;\n black: boolean;\n}\n\nexport function keyRect(layout: KeyboardLayout, midi: number): KeyRect {\n const cx = keyCenterX(layout, midi);\n if (isBlackKey(midi)) {\n const w = layout.whiteW * 0.6;\n return { x: cx - w / 2, y: layout.top, w, h: layout.height * 0.62, black: true };\n }\n const w = layout.whiteW;\n return { x: cx - w / 2, y: layout.top, w, h: layout.height, black: false };\n}\n\n/** Whether `midi` is within the layout's drawn range. */\nexport function inRange(layout: KeyboardLayout, midi: number): boolean {\n return midi >= layout.lowMidi && midi <= layout.highMidi;\n}\n\n/** All white-key midis in the layout (low..high), for drawing the bed. */\nexport function whiteKeys(layout: KeyboardLayout): number[] {\n const out: number[] = [];\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) if (!isBlackKey(m)) out.push(m);\n return out;\n}\n\n/** All black-key midis in the layout (low..high), drawn on top of the whites. */\nexport function blackKeys(layout: KeyboardLayout): number[] {\n const out: number[] = [];\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) if (isBlackKey(m)) out.push(m);\n return out;\n}\n\n// ─── Colour ────────────────────────────────────────────────────────────────\n\nexport type ColorBy = 'hand' | 'pitch-class';\n\n/** Right/left hand colours. R = theme accent (the lead colour); L = a cooler\n * contrast. Both passed in so a host theme can override. */\nexport interface HandColors {\n R: string;\n L: string;\n}\n\n/** 12 pitch-class hues (a perceptually-spread wheel, C..B). Stable + readable. */\nconst PC_COLORS = [\n '#e64545', '#e6803a', '#e6c23a', '#9bcf3a', '#3acf6e', '#3acfb0',\n '#3aa6e6', '#3a5fe6', '#7a3ae6', '#b03ae6', '#e63ab0', '#e63a6e',\n];\n\n/**\n * Colour for a note. `hand` reads the Score's accurate hand (NOT a pitch\n * threshold guess); `pitch-class` colours by chroma. Pure.\n */\nexport function noteColor(\n midi: number,\n hand: 'L' | 'R',\n colorBy: ColorBy,\n hands: HandColors,\n): string {\n if (colorBy === 'pitch-class') return PC_COLORS[(((midi % 12) + 12) % 12)];\n return hand === 'L' ? hands.L : hands.R;\n}\n","// Shared keyboard handoff (S3) — lets the `falling-notes` layer read the EXACT\n// KeyboardLayout the `keyboard` layer computed, so falling-note columns land on\n// the same key x's the keyboard draws (the \"two layers agree by construction\"\n// guarantee). Mirrors engravingStore: a per-render WeakMap keyed by the runner's\n// shared audioClock object (one per render) — GC-friendly, no globals.\n//\n// When no keyboard layer is in the scene, falling-notes computes its OWN layout\n// from its own range/span props (standalone mode); the store is just the channel\n// that keeps the paired case in lockstep.\n\nimport type { RenderCtx } from './layer';\nimport type { KeyboardLayout } from './keyboardGeometry';\n\nconst STORE = new WeakMap<object, KeyboardLayout>();\n\nfunction keyFor(ctx: RenderCtx): object {\n return ctx.audioClock;\n}\n\nexport function setKeyboardLayout(ctx: RenderCtx, layout: KeyboardLayout): void {\n STORE.set(keyFor(ctx), layout);\n}\n\nexport function getKeyboardLayout(ctx: RenderCtx): KeyboardLayout | undefined {\n return STORE.get(keyFor(ctx));\n}\n","// `keyboard` layer (S3) — a piano-keyboard strip pinned to the bottom of the\n// safe box. Lights each key while its note SOUNDS (onsetMs ≤ t < onsetMs+durMs),\n// two-coloured by `hand` straight off the Score (accurate, not pitch-threshold-\n// guessed). Pairs with `falling-notes`: it publishes its resolved KeyboardLayout\n// (keyboardStore) so falling-note columns land on the same key x's.\n//\n// Range: \"88\" (full A0..C8) or \"auto\" (derived from the Score's pitch span,\n// snapped + padded to whites). Pure draw fn of t — no wall clock.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n resolveKeyboardLayout,\n keyRect,\n keyCenterX,\n whiteKeys,\n blackKeys,\n noteColor,\n type KeyboardLayout,\n type ColorBy,\n type HandColors,\n} from '../keyboardGeometry';\nimport { setKeyboardLayout } from '../keyboardStore';\n\nexport interface KeyboardProps {\n /** \"88\" = full piano; \"auto\" = fit the Score's pitch span. Default \"auto\". */\n range?: '88' | 'auto';\n /** Colour active keys by performing hand (Score-accurate) or by pitch class.\n * Default \"hand\". */\n colorBy?: ColorBy;\n /** Strip height in world px. Default 220. */\n height?: number;\n /** Strip bottom edge in world px. Default safeBox.bottom. */\n bottomY?: number;\n /** Right/left hand colours. Defaults: R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n /** Realistic 3-D piano styling: white/black key gradients, rounded key fronts,\n * a felt rail + cast shadow at the top, and a soft glow on lit keys. Default\n * false (the flat two-tone look — unchanged for existing consumers). */\n realistic?: boolean;\n}\n\n/** Rounded-rect path with per-corner radii [tl, tr, br, bl] (falls back to a\n * plain rect if `roundRect` is unavailable). */\nfunction roundedPath(\n c: CanvasRenderingContext2D,\n x: number, y: number, w: number, h: number,\n radii: [number, number, number, number],\n): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number[]) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, radii); return; }\n c.rect(x, y, w, h); // graceful fallback\n}\n\n/** Lighten/darken a #rrggbb by `f` (>0 toward white, <0 toward black). */\nfunction shade(hex: string, f: number): string {\n const n = parseInt(hex.slice(1), 16);\n const t = f < 0 ? 0 : 255, p = Math.abs(f);\n const r = Math.round(((n >> 16) & 255) + (t - ((n >> 16) & 255)) * p);\n const g = Math.round(((n >> 8) & 255) + (t - ((n >> 8) & 255)) * p);\n const b = Math.round((n & 255) + (t - (n & 255)) * p);\n return `rgb(${r},${g},${b})`;\n}\n\nfunction keyboardLayer(): Layer<KeyboardProps> {\n let layout: KeyboardLayout | null = null;\n let colorBy: ColorBy = 'hand';\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let realistic = false;\n\n return {\n key: 'keyboard',\n init(ctx, props) {\n colorBy = props.colorBy ?? 'hand';\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n realistic = props.realistic ?? false;\n const sb = ctx.safeBox;\n const height = props.height ?? 220;\n const bottomY = props.bottomY ?? sb.bottom;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n layout = resolveKeyboardLayout({\n range: props.range ?? 'auto',\n pitchMidis: midis,\n left: sb.left,\n width: sb.w,\n bottomY,\n height,\n });\n // Publish so falling-notes uses the IDENTICAL layout (agree by construction).\n setKeyboardLayout(ctx, layout);\n },\n\n draw(ctx, tMs) {\n if (!layout) return;\n const c = ctx.ctx2d;\n const L = layout;\n\n // Which midis are sounding now, and their hand (for the lit colour).\n const lit = new Map<number, 'L' | 'R'>();\n for (const n of ctx.score?.notes ?? []) {\n if (tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs) lit.set(n.pitchMidi, n.hand);\n }\n\n if (realistic) { drawRealisticKeyboard(c, L, lit, colorBy, hands); return; }\n\n c.save();\n // White-key bed.\n for (const m of whiteKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n c.fillStyle = onHand ? noteColor(m, onHand, colorBy, hands) : '#fbfbfb';\n c.fillRect(r.x, r.y, r.w, r.h);\n c.strokeStyle = '#b8b0a4';\n c.lineWidth = 1;\n c.strokeRect(r.x, r.y, r.w, r.h);\n }\n // Black keys on top.\n for (const m of blackKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n c.fillStyle = onHand ? noteColor(m, onHand, colorBy, hands) : '#1a1614';\n c.fillRect(r.x, r.y, r.w, r.h);\n }\n c.restore();\n },\n\n dispose() {\n layout = null;\n },\n };\n}\n\n// Realistic 3-D piano: gradient white/black keys with rounded fronts, a felt\n// rail + cast shadow at the top, and a soft glow on the lit keys.\nfunction drawRealisticKeyboard(\n c: CanvasRenderingContext2D,\n L: KeyboardLayout,\n lit: Map<number, 'L' | 'R'>,\n colorBy: ColorBy,\n hands: HandColors,\n): void {\n {\n const wr = keyRect(L, whiteKeys(L)[0] ?? 0);\n const wRad = Math.max(3, Math.min(8, wr.w * 0.18));\n\n c.save();\n // White keys: vertical gradient, rounded fronts, hairline separators.\n for (const m of whiteKeys(L)) {\n const r = keyRect(L, m);\n const onHand = lit.get(m);\n const x = r.x + 0.75, w = r.w - 1.5; // tiny gap → visible key separation\n roundedPath(c, x, r.y, w, r.h, [2, 2, wRad, wRad]);\n if (onHand) {\n const base = noteColor(m, onHand, colorBy, hands);\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, shade(base, 0.18));\n g.addColorStop(1, base);\n c.fillStyle = g;\n } else {\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, '#e9e4da'); // shaded near the fallboard\n g.addColorStop(0.12, '#fbfaf7');\n g.addColorStop(1, '#ffffff');\n c.fillStyle = g;\n }\n c.fill();\n c.strokeStyle = '#cdc6ba';\n c.lineWidth = 1;\n c.stroke();\n }\n // Felt rail + cast shadow at the very top (keys recede under the fallboard).\n const railH = Math.max(4, L.height * 0.03);\n c.fillStyle = '#7b2436';\n c.fillRect(L.x, L.top - railH, L.w, railH);\n const sh = c.createLinearGradient(0, L.top, 0, L.top + L.height * 0.14);\n sh.addColorStop(0, 'rgba(0,0,0,0.22)');\n sh.addColorStop(1, 'rgba(0,0,0,0)');\n c.fillStyle = sh;\n c.fillRect(L.x, L.top, L.w, L.height * 0.14);\n\n // Black keys: narrower, shorter, gradient body with a glossy bottom bevel.\n for (const m of blackKeys(L)) {\n const r = keyRect(L, m);\n const bRad = Math.max(2, r.w * 0.22);\n const onHand = lit.get(m);\n roundedPath(c, r.x, r.y, r.w, r.h, [1, 1, bRad, bRad]);\n if (onHand) {\n const base = noteColor(m, onHand, colorBy, hands);\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, shade(base, -0.25));\n g.addColorStop(1, base);\n c.fillStyle = g;\n } else {\n const g = c.createLinearGradient(0, r.y, 0, r.y + r.h);\n g.addColorStop(0, '#15110e');\n g.addColorStop(0.82, '#2c2622');\n g.addColorStop(1, '#453d36'); // bevel highlight at the front\n c.fillStyle = g;\n }\n c.fill();\n // Thin specular highlight along the front lip.\n c.fillStyle = onHand ? 'rgba(255,255,255,0.28)' : 'rgba(255,255,255,0.10)';\n roundedPath(c, r.x + r.w * 0.18, r.y + r.h - Math.max(2, r.h * 0.06), r.w * 0.64, Math.max(1.5, r.h * 0.035), [1, 1, 1, 1]);\n c.fill();\n }\n\n // Soft glow over keys that are sounding.\n for (const [m, hand] of lit) {\n const r = keyRect(L, m);\n c.save();\n c.shadowColor = noteColor(m, hand, colorBy, hands);\n c.shadowBlur = 22;\n c.globalAlpha = 0.55;\n c.fillStyle = noteColor(m, hand, colorBy, hands);\n c.fillRect(r.x + r.w * 0.2, r.y + r.h * 0.5, r.w * 0.6, r.h * 0.3);\n c.restore();\n }\n c.restore();\n }\n}\n\nexport const keyboardFactory: LayerFactory<KeyboardProps> = {\n key: 'keyboard',\n create: keyboardLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['keyboard: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.range != null && p.range !== '88' && p.range !== 'auto')\n errs.push('keyboard.range must be \"88\" | \"auto\"');\n if (p.colorBy != null && p.colorBy !== 'hand' && p.colorBy !== 'pitch-class')\n errs.push('keyboard.colorBy must be \"hand\" | \"pitch-class\"');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('keyboard.height must be a positive number');\n if (p.bottomY != null && typeof p.bottomY !== 'number')\n errs.push('keyboard.bottomY must be a number');\n if (p.realistic != null && typeof p.realistic !== 'boolean')\n errs.push('keyboard.realistic must be a boolean');\n return errs;\n },\n};\n\n// Re-export keyCenterX so falling-notes' agreement is via the SAME function.\nexport { keyCenterX };\n","// `fretboard` layer — a guitar fretboard pinned in the safe box. Draws strings,\n// frets, nut + inlay markers, and lights each note's fret position while it SOUNDS.\n// Positions come from ONE coherent placement map over the whole excerpt\n// (buildPlacementMap) so lit frets don't jump strings note-to-note. Sibling of\n// `keyboard.ts`; pure draw fn of t.\nimport type { Layer, LayerFactory } from '../layer';\nimport { STRING_COUNT, buildPlacementMap, type FretPosition } from '../../music/fretboard';\n\nexport interface FretboardProps {\n /** Highest fret drawn. Default: auto-fit the excerpt (min 5, max 15), padded. */\n visibleFrets?: number;\n /** Board height in world px. Default 360 — 6 strings need more vertical room\n * than the piano strip (220 cramped them to ~37px each). */\n height?: number;\n /** Board bottom edge in world px. Default safeBox.bottom. */\n bottomY?: number;\n /** Right/left hand dot colours. Defaults R=theme.accent, L=theme.gold. */\n handColors?: { R: string; L: string };\n /** Draw note-name labels inside lit dots. Default false. */\n showLabels?: boolean;\n}\n\nconst INLAY_FRETS = [3, 5, 7, 9, 12];\nconst NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];\n\nfunction fretboardLayer(): Layer<FretboardProps> {\n let placement = new Map<number, FretPosition>();\n let visibleFrets = 12;\n let hands = { R: '#7b2436', L: '#c8a55b' };\n let showLabels = false;\n let rect = { x: 0, y: 0, w: 0, h: 0 };\n\n return {\n key: 'fretboard',\n init(ctx, props) {\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n showLabels = props.showLabels ?? false;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n placement = buildPlacementMap(midis);\n // auto-fit visible frets to the highest used fret (+1 pad), clamped [5,15].\n let maxFret = 0;\n for (const p of placement.values()) maxFret = Math.max(maxFret, p.fret);\n visibleFrets = props.visibleFrets ?? Math.min(15, Math.max(5, maxFret + 1));\n const sb = ctx.safeBox;\n const height = props.height ?? 360;\n const bottomY = props.bottomY ?? sb.bottom;\n rect = { x: sb.left, y: bottomY - height, w: sb.w, h: height };\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const { x, y, w, h } = rect;\n const nutW = Math.max(6, w * 0.012);\n const boardX = x + nutW;\n const boardW = w - nutW;\n const fretW = boardW / visibleFrets; // equal spacing (diagram style)\n const stringGap = h / (STRING_COUNT - 1);\n // string index 0 = low E at the BOTTOM, high E at the top (TAB orientation).\n const stringY = (s: number) => y + h - s * stringGap;\n const fretX = (f: number) => boardX + f * fretW; // fret line positions\n // a note AT fret f is drawn in the middle of the fret space [f-1, f]; open (0) on the nut.\n const dotX = (f: number) => (f === 0 ? x + nutW / 2 : fretX(f) - fretW / 2);\n\n c.save();\n // Board background.\n c.fillStyle = '#f1e7d6';\n c.fillRect(x, y, w, h);\n // Nut.\n c.fillStyle = '#2a2420';\n c.fillRect(x, y, nutW, h);\n // Frets.\n c.strokeStyle = '#b8a98c';\n c.lineWidth = 2;\n for (let f = 1; f <= visibleFrets; f++) {\n c.beginPath(); c.moveTo(fretX(f), y); c.lineTo(fretX(f), y + h); c.stroke();\n }\n // Inlay markers (single dots, double at 12).\n c.fillStyle = 'rgba(60,50,40,0.28)';\n for (const f of INLAY_FRETS) {\n if (f > visibleFrets) continue;\n const cx = dotX(f);\n const r = Math.min(stringGap, fretW) * 0.18;\n if (f === 12) {\n c.beginPath(); c.arc(cx, stringY(STRING_COUNT - 1) + stringGap * 0.5, r, 0, Math.PI * 2); c.fill();\n c.beginPath(); c.arc(cx, stringY(0) - stringGap * 0.5, r, 0, Math.PI * 2); c.fill();\n } else {\n c.beginPath(); c.arc(cx, y + h / 2, r, 0, Math.PI * 2); c.fill();\n }\n }\n // Strings (thicker for low strings).\n c.strokeStyle = '#5c5345';\n for (let s = 0; s < STRING_COUNT; s++) {\n c.lineWidth = 1 + (STRING_COUNT - 1 - s) * 0.5;\n c.beginPath(); c.moveTo(x, stringY(s)); c.lineTo(x + w, stringY(s)); c.stroke();\n }\n\n // Lit notes: which midis sound now → their placement → a dot on that string/fret.\n const dotR = Math.min(stringGap, fretW) * 0.42;\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n const pos = placement.get(n.pitchMidi);\n if (!pos || pos.fret > visibleFrets) continue;\n const cx = dotX(pos.fret);\n const cy = stringY(pos.string);\n c.fillStyle = n.hand === 'L' ? hands.L : hands.R;\n c.beginPath(); c.arc(cx, cy, dotR, 0, Math.PI * 2); c.fill();\n if (showLabels) {\n c.fillStyle = '#fff';\n c.font = `${Math.round(dotR * 1.1)}px sans-serif`;\n c.textAlign = 'center'; c.textBaseline = 'middle';\n c.fillText(NOTE_NAMES[n.pitchMidi % 12], cx, cy);\n }\n }\n c.restore();\n },\n\n dispose() { placement = new Map(); },\n };\n}\n\nexport const fretboardFactory: LayerFactory<FretboardProps> = {\n key: 'fretboard',\n create: fretboardLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['fretboard: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.visibleFrets != null && (typeof p.visibleFrets !== 'number' || p.visibleFrets < 1 || p.visibleFrets > 24))\n errs.push('fretboard.visibleFrets must be a number in 1..24');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('fretboard.height must be a positive number');\n if (p.bottomY != null && typeof p.bottomY !== 'number') errs.push('fretboard.bottomY must be a number');\n if (p.showLabels != null && typeof p.showLabels !== 'boolean') errs.push('fretboard.showLabels must be a boolean');\n return errs;\n },\n};\n","// `falling-notes` layer (S3) — Synthesia-style note blocks falling toward the\n// keyboard's hit-line. The headline new capability.\n//\n// Sync (pure fn of t, audio-clock-driven):\n// - The hit-line is the keyboard's top edge (or the bottom of the fall region\n// when standalone). A note arrives at the hit-line EXACTLY at onsetMs.\n// - leadMs is how long a note is visible falling before it lands. The note\n// travels the full fall height over leadMs, so px/ms = fallHeight / leadMs.\n// `speed` (px/sec) is an alternative spelling; leadMs wins if both given.\n// - A note's block: bottom edge tracks the time-to-onset; its LENGTH ∝ durMs\n// (length px = durMs * px/ms). The block crosses the hit-line at onset and is\n// fully gone once its TOP passes the hit-line, i.e. after onsetMs + durMs.\n// - hitGlow: a brief flash at the hit-line while a note is sounding.\n//\n// Column x: read from the keyboard layer's published KeyboardLayout (keyboardStore)\n// when paired, else compute the IDENTICAL layout from this layer's own props. So a\n// falling block is always centered on its key — the two layers agree by\n// construction (proved in the invariant tests via keyCenterX equality).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n resolveKeyboardLayout,\n keyCenterX,\n keyColumnWidth,\n inRange,\n noteColor,\n type KeyboardLayout,\n type ColorBy,\n type HandColors,\n} from '../keyboardGeometry';\nimport { getKeyboardLayout } from '../keyboardStore';\n\nexport interface FallingNotesProps {\n /** Pair with a `keyboard` layer (read its layout + hit-line). When false the\n * layer is standalone and builds its own layout from the props below.\n * Default true. */\n keyboard?: boolean;\n /** Colour by performing hand (Score-accurate) or by pitch class. Default \"hand\". */\n colorBy?: ColorBy;\n /** Lead time in ms a note is visible before landing. Default 2000. */\n leadMs?: number;\n /** Fall speed in px/sec. Alternative to leadMs (ignored when leadMs is given). */\n speed?: number;\n /** Glow flash at the hit-line while a note sounds. Default true. */\n hitGlow?: boolean;\n /** Glowy/translucent block styling: rounded corners, a vertical gradient that\n * fades toward the top, a soft bloom (shadow) in the note colour, and a bright\n * leading-edge cap. Default false (flat opaque rectangles — unchanged). */\n glow?: boolean;\n /** Standalone-only: keyboard range when `keyboard:false`. Default \"auto\". */\n range?: '88' | 'auto';\n /** Standalone-only: hit-line (world y). Default safeBox.bottom - 220. */\n hitLineY?: number;\n /** Top of the fall region (world y) — notes appear here. Default safeBox.top. */\n topY?: number;\n /** Right/left hand colours. Defaults: R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n}\n\nconst DEFAULT_LEAD_MS = 2000;\n\n/** Parse \"#rrggbb\" → \"rgba(r,g,b,a)\"; passes other colour strings through (no\n * alpha applied) so the glow still draws something sensible. */\nfunction rgba(color: string, a: number): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) {\n const n = parseInt(color.slice(1), 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n }\n return color;\n}\n\n/** Rounded-rect path (falls back to a plain rect if `roundRect` is unavailable). */\nfunction roundedRect(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, r); return; }\n c.rect(x, y, w, h);\n}\n\n/** A glowy/translucent falling block: rounded, a vertical gradient fading toward\n * the top, a soft colour bloom, and a bright leading-edge cap at the bottom. */\nfunction drawGlowBlock(c: CanvasRenderingContext2D, x: number, y: number, w: number, hRaw: number, color: string): void {\n const h = Math.max(2, hRaw);\n const r = Math.min(w * 0.42, 8);\n const bx = x + 1, bw = Math.max(1, w - 2);\n c.save();\n // Outer bloom: a soft, wide colour halo (drawn as a faint pass behind the body).\n c.shadowColor = rgba(color, 1);\n c.shadowBlur = 22;\n c.fillStyle = rgba(color, 0.5);\n roundedRect(c, bx, y, bw, h, r);\n c.fill();\n c.fill(); // second pass deepens the bloom\n c.shadowBlur = 0;\n // Translucent body: a vertical gradient that fades toward the trailing (top) end\n // so the backdrop reads through — glassy, not a solid bar.\n const g = c.createLinearGradient(0, y, 0, y + h);\n g.addColorStop(0, rgba(color, 0.06));\n g.addColorStop(0.5, rgba(color, 0.38));\n g.addColorStop(1, rgba(color, 0.78));\n c.fillStyle = g;\n roundedRect(c, bx, y, bw, h, r);\n c.fill();\n // Glassy center highlight (a soft lighter streak) for a tube-of-light look.\n const hl = c.createLinearGradient(bx, 0, bx + bw, 0);\n hl.addColorStop(0, 'rgba(255,255,255,0)');\n hl.addColorStop(0.5, 'rgba(255,255,255,0.22)');\n hl.addColorStop(1, 'rgba(255,255,255,0)');\n c.fillStyle = hl;\n roundedRect(c, bx + bw * 0.18, y, bw * 0.46, h, Math.min(r, bw * 0.2));\n c.fill();\n // Bright leading-edge cap (where the note strikes the key).\n const capH = Math.min(7, h);\n c.fillStyle = rgba(color, 0.98);\n roundedRect(c, bx, y + h - capH, bw, capH, Math.min(r, capH / 2));\n c.fill();\n c.restore();\n}\n\nfunction fallingNotesLayer(): Layer<FallingNotesProps> {\n let paired = true;\n let colorBy: ColorBy = 'hand';\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let hitGlow = true;\n let glow = false;\n let ownLayout: KeyboardLayout | null = null;\n let topY = 0;\n let leadMsProp: number | undefined;\n let speedProp: number | undefined;\n\n /** The layout to use this frame: the keyboard's published one when paired,\n * else this layer's own. */\n function layoutFor(ctx: RenderCtx): KeyboardLayout | null {\n if (paired) return getKeyboardLayout(ctx) ?? ownLayout;\n return ownLayout;\n }\n\n /** Hit-line y = keyboard top (paired) or the resolved layout top. */\n function hitLineY(layout: KeyboardLayout): number {\n return layout.top;\n }\n\n /** px per ms given the fall height; leadMs wins, else speed (px/sec). */\n function pxPerMs(fallHeight: number): number {\n if (leadMsProp != null) return fallHeight / leadMsProp;\n if (speedProp != null) return speedProp / 1000;\n return fallHeight / DEFAULT_LEAD_MS;\n }\n\n return {\n key: 'falling-notes',\n init(ctx, props) {\n paired = props.keyboard ?? true;\n colorBy = props.colorBy ?? 'hand';\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n hitGlow = props.hitGlow ?? true;\n glow = props.glow ?? false;\n leadMsProp = props.leadMs;\n speedProp = props.speed;\n const sb = ctx.safeBox;\n topY = props.topY ?? sb.top;\n\n if (!paired) {\n // Standalone: build the SAME layout the keyboard layer would, so a later\n // keyboard pairing would line up exactly.\n const hitLine = props.hitLineY ?? sb.bottom - 220;\n const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);\n ownLayout = resolveKeyboardLayout({\n range: props.range ?? 'auto',\n pitchMidis: midis,\n left: sb.left,\n width: sb.w,\n // top of the keyboard == hit-line; height below it is irrelevant here.\n bottomY: hitLine + 1,\n height: 1,\n });\n }\n },\n\n draw(ctx, tMs) {\n const layout = layoutFor(ctx);\n if (!layout) return;\n const c = ctx.ctx2d;\n const hit = hitLineY(layout);\n const fallH = Math.max(1, hit - topY);\n const v = pxPerMs(fallH); // px per ms\n\n for (const n of ctx.score?.notes ?? []) {\n if (!inRange(layout, n.pitchMidi)) continue;\n // Bottom edge: at t==onset it sits ON the hit-line; before, it's above.\n const bottomY = hit - (n.onsetMs - tMs) * v;\n const lenPx = Math.max(2, n.durMs * v);\n const topEdge = bottomY - lenPx;\n // Cull: not yet entered the top of the region, or fully past the hit-line.\n if (bottomY < topY) continue; // hasn't appeared (before onset - leadMs)\n if (topEdge > hit) continue; // fully below the hit-line (after onset+dur)\n\n const cx = keyCenterX(layout, n.pitchMidi);\n const w = keyColumnWidth(layout, n.pitchMidi);\n const drawTop = Math.max(topY, topEdge);\n const drawBottom = Math.min(hit, bottomY);\n const fill = noteColor(n.pitchMidi, n.hand, colorBy, hands);\n\n if (glow) {\n drawGlowBlock(c, cx - w / 2, drawTop, w, drawBottom - drawTop, fill);\n } else {\n c.save();\n c.fillStyle = fill;\n c.globalAlpha = 0.92;\n c.fillRect(cx - w / 2, drawTop, w, Math.max(1, drawBottom - drawTop));\n c.restore();\n }\n }\n\n // Hit-line glow: flash over keys whose note is sounding now.\n if (hitGlow) {\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(layout, n.pitchMidi)) continue;\n const cx = keyCenterX(layout, n.pitchMidi);\n const w = keyColumnWidth(layout, n.pitchMidi);\n c.save();\n c.globalAlpha = 0.5;\n c.fillStyle = noteColor(n.pitchMidi, n.hand, colorBy, hands);\n c.fillRect(cx - w / 2, hit - 8, w, 8);\n c.restore();\n }\n }\n },\n\n dispose() {\n ownLayout = null;\n },\n };\n}\n\nexport const fallingNotesFactory: LayerFactory<FallingNotesProps> = {\n key: 'falling-notes',\n create: fallingNotesLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['falling-notes: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.keyboard != null && typeof p.keyboard !== 'boolean')\n errs.push('falling-notes.keyboard must be a boolean');\n if (p.colorBy != null && p.colorBy !== 'hand' && p.colorBy !== 'pitch-class')\n errs.push('falling-notes.colorBy must be \"hand\" | \"pitch-class\"');\n if (p.leadMs != null && (typeof p.leadMs !== 'number' || p.leadMs <= 0))\n errs.push('falling-notes.leadMs must be a positive number');\n if (p.speed != null && (typeof p.speed !== 'number' || p.speed <= 0))\n errs.push('falling-notes.speed must be a positive number');\n if (p.hitGlow != null && typeof p.hitGlow !== 'boolean')\n errs.push('falling-notes.hitGlow must be a boolean');\n if (p.glow != null && typeof p.glow !== 'boolean')\n errs.push('falling-notes.glow must be a boolean');\n if (p.range != null && p.range !== '88' && p.range !== 'auto')\n errs.push('falling-notes.range must be \"88\" | \"auto\"');\n return errs;\n },\n};\n","// `hook` / `reveal` / `cta` layers (S4) — thin Layer wrappers around the\n// EXISTING, battle-tested scene builders in src/video.ts (hookScene /\n// revealScene / ctaScene). We do NOT re-implement the drawing: each wrapper\n// builds the original Scene once in init() and, per frame, converts the runner's\n// absolute `tMs` into the Scene's local 0..1 `t01` and calls Scene.draw — so the\n// pixels are equivalent to the current builders BY CONSTRUCTION.\n//\n// These are screen-pinned cards (the runner draws them in identity-transform\n// space — see SCREEN_PINNED_KEYS), matching how RMT/whozart/RSR draw their\n// hook/reveal/cta over the whole frame today.\n//\n// Timing: a Scene maps t01∈[0,1] across its own duration. The runner only calls\n// draw within a segment window, but it does NOT pass the window bounds to draw.\n// So each wrapper takes `startMs` (segment start) + `durationMs` (segment length)\n// props; `t01 = clamp((tMs - startMs)/durationMs, 0, 1)`. A host SceneSpec\n// builder sets these to the segment's `at` (the S4 demo does this); defaults fall\n// back to the original builder's natural durationMs anchored at 0.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n hookScene,\n revealScene,\n ctaScene,\n loadPortrait,\n type Scene,\n type HookSceneOpts,\n type RevealSceneOpts,\n type CtaSceneOpts,\n} from '../../video';\n\n// ─── shared timing helper ────────────────────────────────────────────────────\n\n/** Map absolute tMs to the wrapped Scene's local t01∈[0,1]. */\nfunction localT01(tMs: number, startMs: number, durationMs: number): number {\n if (durationMs <= 0) return 0;\n const t = (tMs - startMs) / durationMs;\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n\n/** Common window props shared by every card wrapper. */\ninterface WindowProps {\n /** Absolute segment start (ms) — t01 is measured from here. Default 0. */\n startMs?: number;\n /** Segment length (ms) over which t01 sweeps 0..1. Default = scene's natural duration. */\n durationMs?: number;\n}\n\nfunction validateWindow(p: Record<string, unknown>, key: string): string[] {\n const errs: string[] = [];\n if (p.startMs != null && (typeof p.startMs !== 'number' || p.startMs < 0))\n errs.push(`${key}.startMs must be a number >= 0`);\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || !(p.durationMs > 0)))\n errs.push(`${key}.durationMs must be a positive number`);\n return errs;\n}\n\n// ─── hook ────────────────────────────────────────────────────────────────────\n\nexport interface HookProps extends WindowProps {\n /** Hook lines (wraps HookSceneOpts.lines). */\n lines: string[];\n /** Optional brand override (wraps HookSceneOpts.brand). */\n brand?: string;\n}\n\nfunction hookLayer(): Layer<HookProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'hook',\n init(ctx: RenderCtx, props) {\n const opts: HookSceneOpts = { lines: props.lines, brand: props.brand };\n scene = hookScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const hookFactory: LayerFactory<HookProps> = {\n key: 'hook',\n create: hookLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['hook: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.lines) || !p.lines.every((l) => typeof l === 'string'))\n errs.push('hook.lines must be an array of strings');\n if (p.brand != null && typeof p.brand !== 'string') errs.push('hook.brand must be a string');\n return [...errs, ...validateWindow(p, 'hook')];\n },\n};\n\n// ─── reveal ──────────────────────────────────────────────────────────────────\n\nexport interface RevealProps extends WindowProps {\n /** Title (wraps RevealSceneOpts.title). */\n title: string;\n /** Subtitle (wraps RevealSceneOpts.subtitle). */\n subtitle: string;\n /** Optional initials override (wraps RevealSceneOpts.initials). */\n initials?: string;\n /** Optional fun fact (wraps RevealSceneOpts.funFact). */\n funFact?: string;\n}\n\nfunction revealLayer(): Layer<RevealProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'reveal',\n init(ctx: RenderCtx, props) {\n const opts: RevealSceneOpts = {\n title: props.title,\n subtitle: props.subtitle,\n initials: props.initials,\n funFact: props.funFact,\n portrait: null, // the `portrait` layer carries the medallion image; reveal\n // here is the initials-fallback look. See portrait.ts.\n };\n scene = revealScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const revealFactory: LayerFactory<RevealProps> = {\n key: 'reveal',\n create: revealLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['reveal: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.title !== 'string') errs.push('reveal.title must be a string');\n if (typeof p.subtitle !== 'string') errs.push('reveal.subtitle must be a string');\n if (p.initials != null && typeof p.initials !== 'string') errs.push('reveal.initials must be a string');\n if (p.funFact != null && typeof p.funFact !== 'string') errs.push('reveal.funFact must be a string');\n return [...errs, ...validateWindow(p, 'reveal')];\n },\n};\n\n// ─── cta ─────────────────────────────────────────────────────────────────────\n\nexport interface CtaProps extends WindowProps {\n /** End-card lines (wraps CtaSceneOpts.lines): [headline, ...accent lines]. */\n lines: string[];\n}\n\nfunction ctaLayer(): Layer<CtaProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'cta',\n init(ctx: RenderCtx, props) {\n const opts: CtaSceneOpts = { lines: props.lines };\n scene = ctaScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const ctaFactory: LayerFactory<CtaProps> = {\n key: 'cta',\n create: ctaLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['cta: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.lines) || !p.lines.every((l) => typeof l === 'string'))\n errs.push('cta.lines must be an array of strings');\n return [...errs, ...validateWindow(p, 'cta')];\n },\n};\n\n// ─── portrait ────────────────────────────────────────────────────────────────\n//\n// The composer-portrait medallion + fun-fact. This is the SAME visual as\n// revealScene with a decoded `portrait` image, so we wrap revealScene exactly —\n// but the portrait layer owns the ASYNC image load (loadPortrait in init), which\n// the plain `reveal` wrapper can't because props are sync. With no/failed image\n// it falls back to revealScene's initials badge (loadPortrait resolves null →\n// identical to `reveal`).\n\nexport interface PortraitProps extends WindowProps {\n /** Composer name → title + initials badge fallback (wraps RevealSceneOpts.title). */\n title: string;\n /** Subtitle line (e.g. dates / era). */\n subtitle: string;\n /** Portrait image URL (Wikimedia etc.); loaded crossOrigin in init. */\n url?: string | null;\n /** Optional initials override. */\n initials?: string;\n /** Optional fun fact (corpus fun_fact). */\n funFact?: string;\n}\n\nfunction portraitLayer(): Layer<PortraitProps> {\n let scene: Scene | null = null;\n let startMs = 0;\n let durationMs = 0;\n return {\n key: 'portrait',\n async init(ctx: RenderCtx, props) {\n // ASYNC asset load — the reason this is its own layer. Fail-soft to null\n // (revealScene then draws the initials badge).\n const portrait = await loadPortrait(props.url ?? null);\n const opts: RevealSceneOpts = {\n title: props.title,\n subtitle: props.subtitle,\n initials: props.initials,\n funFact: props.funFact,\n portrait,\n };\n scene = revealScene(ctx.theme, opts);\n startMs = props.startMs ?? 0;\n durationMs = props.durationMs ?? scene.durationMs;\n },\n draw(ctx, tMs) {\n if (!scene) return;\n scene.draw(ctx.ctx2d, localT01(tMs, startMs, durationMs));\n },\n };\n}\n\nexport const portraitFactory: LayerFactory<PortraitProps> = {\n key: 'portrait',\n create: portraitLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['portrait: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.title !== 'string') errs.push('portrait.title must be a string');\n if (typeof p.subtitle !== 'string') errs.push('portrait.subtitle must be a string');\n if (p.url != null && typeof p.url !== 'string') errs.push('portrait.url must be a string or null');\n if (p.initials != null && typeof p.initials !== 'string') errs.push('portrait.initials must be a string');\n if (p.funFact != null && typeof p.funFact !== 'string') errs.push('portrait.funFact must be a string');\n return [...errs, ...validateWindow(p, 'portrait')];\n },\n};\n","// `spectrum` layer (S4) — the audio-reactive mirrored frequency-bar visualizer,\n// reimplemented from whozart's drawSpectrum (whozart/src/lib/promo.ts) as a\n// Layer. web-core CANNOT depend on whozart, so the analyser does not live here:\n// the layer reads its levels through a host-provided input on RenderCtx (a\n// `levels` provider) or a `levelsFn` prop. With NEITHER it uses the SAME synthetic\n// taper-sine fallback whozart uses when no signal is present — which is also the\n// deterministic, pure-fn-of-t path the invariant + golden tests exercise.\n//\n// ── Input contract (how the layer gets its levels) ──────────────────────────\n// The host attaches a normalized-magnitude provider to RenderCtx:\n//\n// ctx.spectrum = {\n// // 0..1 magnitudes per band, OR raw byte-FFT (0..255) the layer log-bins.\n// levels?(tMs, bands): Float32Array | number[] | null\n// byteFreq?(tMs): Uint8Array | null // e.g. AnalyserNode.getByteFrequencyData\n// }\n//\n// In whozart the host wires byteFreq → AnalyserNode (the live master bus). In a\n// deterministic capture/test, the host omits it and the layer animates the\n// fallback. A `levelsFn` PROP overrides ctx (handy for previews / fixtures).\n//\n// The bar look (mirrored about a center line, wine→gold per magnitude, edge taper\n// on the fallback) is faithful to whozart; the colour lerp is inlined (web-core\n// has no lerpHex) and matches whozart's rgb() output.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport type { SafeBox } from '../../video';\n\nconst SPEC_BARS = 44;\n\n/** Host-provided level source attached to RenderCtx by the app/capture harness. */\nexport interface SpectrumInput {\n /** Normalized 0..1 magnitudes for `bands` bars at time tMs (preferred). */\n levels?(tMs: number, bands: number): Float32Array | number[] | null | undefined;\n /** Raw byte-FFT (0..255), log-binned by the layer (AnalyserNode shape). */\n byteFreq?(tMs: number): Uint8Array | null | undefined;\n}\n\n/** Augment RenderCtx with the optional spectrum input (declaration merging). */\ndeclare module '../layer' {\n interface RenderCtx {\n /** Optional audio-reactive level source for the spectrum layer (host-wired). */\n spectrum?: SpectrumInput;\n }\n}\n\nexport interface SpectrumProps {\n /** Number of bars. Default 44 (whozart SPEC_BARS). */\n bars?: number;\n /** Vertical center as a fraction of H. Default 0.46 (whozart). */\n centerFrac?: number;\n /** Max half-height as a fraction of H. Default 0.135 (whozart). */\n maxHeightFrac?: number;\n /** Bar fill at low magnitude (wine). Default theme.accent. */\n colorLow?: string;\n /** Bar fill at high magnitude (gold). Default theme.gold. */\n colorHigh?: string;\n /** Per-frame level provider (overrides ctx.spectrum). Pure fn of t for tests. */\n levelsFn?: (tMs: number, bands: number) => Float32Array | number[] | null;\n}\n\n// rgb-space lerp between two #rrggbb colours — matches whozart lerpHex output.\nfunction lerpHex(a: string, b: string, f: number): string {\n const pa = parseInt(a.slice(1), 16);\n const pb = parseInt(b.slice(1), 16);\n const r = Math.round(((pa >> 16) & 255) + (((pb >> 16) & 255) - ((pa >> 16) & 255)) * f);\n const g = Math.round(((pa >> 8) & 255) + (((pb >> 8) & 255) - ((pa >> 8) & 255)) * f);\n const bl = Math.round((pa & 255) + ((pb & 255) - (pa & 255)) * f);\n return `rgb(${r},${g},${bl})`;\n}\n\n/** Deterministic synthetic magnitudes (whozart's no-signal fallback). Pure fn of\n * (tSec, band) so tests + offline captures are reproducible. */\nfunction syntheticMagnitude(tSec: number, b: number, n: number): number {\n const phase = tSec * 5 + b * 0.45;\n let m = 0.22 + 0.16 * Math.sin(phase) + 0.10 * Math.sin(phase * 1.7 + 1.2);\n m *= 0.5 + 0.5 * Math.sin((b / (n - 1)) * Math.PI); // taper toward the edges\n return m;\n}\n\n/** Log-bin a byte-FFT into `n` normalized magnitudes (whozart's lo=2..hi=440 map). */\nfunction binByteFreq(freq: Uint8Array, n: number): number[] | null {\n let peak = 0;\n for (let i = 0; i < freq.length; i++) if (freq[i] > peak) peak = freq[i];\n if (peak <= 4) return null; // treat as silence → fall back\n const lo = 2, hi = 440;\n const out: number[] = [];\n for (let b = 0; b < n; b++) {\n const f0 = lo * Math.pow(hi / lo, b / n);\n const f1 = lo * Math.pow(hi / lo, (b + 1) / n);\n const i0 = Math.floor(f0);\n const i1 = Math.max(i0 + 1, Math.floor(f1));\n let sum = 0, c = 0;\n for (let i = i0; i < i1 && i < freq.length; i++) { sum += freq[i]; c++; }\n let m = c ? (sum / c) / 255 : 0;\n m = Math.pow(m, 0.78); // lift quieter bands (whozart)\n out.push(m);\n }\n return out;\n}\n\nfunction spectrumLayer(): Layer<SpectrumProps> {\n let n = SPEC_BARS;\n let centerFrac = 0.46;\n let maxHeightFrac = 0.135;\n let colorLow: string | undefined;\n let colorHigh: string | undefined;\n let levelsFn: SpectrumProps['levelsFn'];\n\n /** Resolve normalized 0..1 magnitudes for this frame, with fallback. */\n function magnitudesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = levelsFn?.(tMs, n);\n const src = fromProp ?? resolveFromCtx(ctx, tMs);\n if (src && src.length) {\n const out = new Array(n);\n for (let b = 0; b < n; b++) out[b] = clamp01(src[Math.min(src.length - 1, b)] ?? 0);\n return out;\n }\n // synthetic fallback\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, b) => clamp01(syntheticMagnitude(tSec, b, n)));\n }\n\n function resolveFromCtx(ctx: RenderCtx, tMs: number): number[] | null {\n const sp = ctx.spectrum;\n if (!sp) return null;\n const lv = sp.levels?.(tMs, n);\n if (lv && lv.length) return Array.from(lv as ArrayLike<number>);\n const bf = sp.byteFreq?.(tMs);\n if (bf && bf.length) return binByteFreq(bf, n);\n return null;\n }\n\n return {\n key: 'spectrum',\n init(_ctx: RenderCtx, props) {\n n = props.bars ?? SPEC_BARS;\n centerFrac = props.centerFrac ?? 0.46;\n maxHeightFrac = props.maxHeightFrac ?? 0.135;\n colorLow = props.colorLow;\n colorHigh = props.colorHigh;\n levelsFn = props.levelsFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const sb: SafeBox = ctx.safeBox;\n const cy = H * centerFrac;\n const left = Math.max(W * 0.10, sb.left);\n const right = sb.right;\n const span = right - left;\n const slot = span / n;\n const gap = slot * 0.34;\n const barW = slot - gap;\n const maxH = H * maxHeightFrac;\n const lo = colorLow ?? ctx.theme.accent;\n const hi = colorHigh ?? ctx.theme.gold;\n\n c.save();\n // Faint center baseline (whozart) so a quiet/paused moment still reads.\n c.strokeStyle = lerpHex(ctx.theme.paper, ctx.theme.sepia, 0.28);\n c.lineWidth = 2;\n c.beginPath(); c.moveTo(left, cy); c.lineTo(right, cy); c.stroke();\n\n const mags = magnitudesAt(ctx, tMs);\n const useRound = typeof c.roundRect === 'function';\n for (let b = 0; b < n; b++) {\n const m = mags[b];\n const half = Math.max(barW * 0.5, m * maxH);\n const x0 = left + b * slot + gap / 2;\n c.fillStyle = lerpHex(lo, hi, m);\n if (useRound) {\n const r = Math.min(barW / 2, half);\n c.beginPath(); c.roundRect(x0, cy - half, barW, half * 2, r); c.fill();\n } else {\n c.fillRect(x0, cy - half, barW, half * 2);\n }\n }\n c.restore();\n },\n };\n}\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\nexport const spectrumFactory: LayerFactory<SpectrumProps> = {\n key: 'spectrum',\n create: spectrumLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['spectrum: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.bars != null && (typeof p.bars !== 'number' || p.bars < 2)) errs.push('spectrum.bars must be a number >= 2');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1))\n errs.push('spectrum.centerFrac must be in [0,1]');\n if (p.maxHeightFrac != null && (typeof p.maxHeightFrac !== 'number' || p.maxHeightFrac <= 0))\n errs.push('spectrum.maxHeightFrac must be a positive number');\n if (p.colorLow != null && typeof p.colorLow !== 'string') errs.push('spectrum.colorLow must be a string');\n if (p.colorHigh != null && typeof p.colorHigh !== 'string') errs.push('spectrum.colorHigh must be a string');\n if (p.levelsFn != null && typeof p.levelsFn !== 'function') errs.push('spectrum.levelsFn must be a function');\n return errs;\n },\n};\n","// `branding` + `safe-guides` layers (S4).\n//\n// `branding` — a small persistent brand/logo wordmark anchored inside the safe\n// box (bottom-centred by default), the chrome RMT/RSR/whozart burn into every\n// clip. Plus an OPTIONAL safe-zone debug overlay when `safezone` is set, which\n// delegates to src/video.ts drawSafeGuides (the SAME overlay apps trigger with\n// ?safe=1). Screen-pinned (drawn in identity space by the runner).\n//\n// `safe-guides` — a thin alias that ALWAYS draws drawSafeGuides (debug-only\n// layer you drop into a spec while laying it out). Both reuse the existing\n// helper; neither re-implements the inset math.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { drawSafeGuides } from '../../video';\n\n// ─── branding ──────────────────────────────────────────────────────────────\n\nexport interface BrandingProps {\n /** Wordmark to draw. Default theme.brand. */\n logo?: string;\n /** Optional second line under the wordmark (e.g. a domain) — the brand-card\n * form used for an end-of-clip mark: wordmark + URL, nothing else. Omit\n * for the original single-line chrome mark. */\n sub?: string;\n /** Sub-line font size in px. Default size*0.5. */\n subSize?: number;\n /** Sub-line colour. Default color/theme.sepia at reduced alpha handled by\n * the caller's theme choice (no alpha applied here so callers stay in\n * control of exact tone). */\n subColor?: string;\n /** Gap between wordmark baseline and sub-line baseline, px. Default size*0.85. */\n gap?: number;\n /** Show the safe-zone debug overlay on top (apps' ?safe=1). Default false. */\n safezone?: boolean;\n /** Vertical anchor as a fraction of safeBox height from its TOP. Default 1\n * (bottom edge of the safe box). With `sub` set, this anchors the WORDMARK\n * line and the sub-line is drawn `gap` px below it. */\n yFrac?: number;\n /** Font size in px. Default 34. */\n size?: number;\n /** Text colour. Default theme.sepia. */\n color?: string;\n}\n\nfunction brandingLayer(): Layer<BrandingProps> {\n let logo: string | undefined;\n let sub: string | undefined;\n let subSize = 0;\n let subColor: string | undefined;\n let gap = 0;\n let safezone = false;\n let yFrac = 1;\n let size = 34;\n let color: string | undefined;\n return {\n key: 'branding',\n init(_ctx: RenderCtx, props) {\n logo = props.logo;\n sub = props.sub;\n safezone = props.safezone ?? false;\n yFrac = props.yFrac ?? 1;\n size = props.size ?? 34;\n subSize = props.subSize ?? size * 0.5;\n gap = props.gap ?? size * 0.85;\n color = props.color;\n subColor = props.subColor;\n },\n draw(ctx) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const text = logo ?? ctx.theme.brand;\n c.save();\n c.textAlign = 'center';\n c.font = `italic ${size}px ${ctx.theme.fontBody}`;\n c.fillStyle = color ?? ctx.theme.sepia;\n // Anchor inside the safe box; at yFrac=1 sit the baseline a little above the\n // bottom edge so descenders stay inside. With a sub-line, both lines are\n // shifted up together so the PAIR (not just the wordmark) stays inside.\n const pairShift = sub ? gap / 2 : 0;\n const y = sb.top + sb.h * yFrac - (yFrac >= 1 ? size * 0.3 : 0) - pairShift;\n c.fillText(text, sb.cx, y);\n if (sub) {\n c.font = `${subSize}px ${ctx.theme.fontBody}`;\n c.fillStyle = subColor ?? color ?? ctx.theme.sepia;\n c.fillText(sub, sb.cx, y + gap);\n }\n c.restore();\n if (safezone) drawSafeGuides(c);\n },\n };\n}\n\nexport const brandingFactory: LayerFactory<BrandingProps> = {\n key: 'branding',\n create: brandingLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['branding: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.logo != null && typeof p.logo !== 'string') errs.push('branding.logo must be a string');\n if (p.sub != null && typeof p.sub !== 'string') errs.push('branding.sub must be a string');\n if (p.subSize != null && (typeof p.subSize !== 'number' || p.subSize <= 0)) errs.push('branding.subSize must be a positive number');\n if (p.subColor != null && typeof p.subColor !== 'string') errs.push('branding.subColor must be a string');\n if (p.gap != null && typeof p.gap !== 'number') errs.push('branding.gap must be a number');\n if (p.safezone != null && typeof p.safezone !== 'boolean') errs.push('branding.safezone must be a boolean');\n if (p.yFrac != null && typeof p.yFrac !== 'number') errs.push('branding.yFrac must be a number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0)) errs.push('branding.size must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('branding.color must be a string');\n return errs;\n },\n};\n\n// ─── safe-guides ─────────────────────────────────────────────────────────────\n\nexport type SafeGuidesProps = Record<string, never>;\n\nfunction safeGuidesLayer(): Layer<SafeGuidesProps> {\n return {\n key: 'safe-guides',\n init() {},\n draw(ctx) {\n drawSafeGuides(ctx.ctx2d);\n },\n };\n}\n\nexport const safeGuidesFactory: LayerFactory<SafeGuidesProps> = {\n key: 'safe-guides',\n create: safeGuidesLayer,\n validateProps(props) {\n if (props != null && typeof props !== 'object') return ['safe-guides: props must be an object'];\n return [];\n },\n};\n","// Staff↔keyboard ray geometry (S5) — the pure endpoints of the animated connector\n// from a sounding note's position ON THE STAFF to its KEY on the keyboard. The\n// \"beginner-killer\": it makes the staff↔piano mapping literal.\n//\n// Two geometry sources, both already produced elsewhere (no new layout):\n// - staff end: the notation layer's per-frame follow LAYOUT (engravingStore),\n// swept to the sounding position with the SAME progress math the playhead uses\n// (measureColumnsFromLayout). We anchor at the playhead x and the active\n// measure's vertical centre — the engraving doesn't expose per-NOTE y cleanly\n// headless, so the ray reads \"this point on the staff\" (documented limitation).\n// - keyboard end: the keyboard layer's published KeyboardLayout (keyboardStore)\n// via keyCenterX — exactly the key the keyboard lights.\n//\n// Pure (no canvas) ⇒ unit-testable; the layer just strokes the returned points.\n\nimport { measureColumnsFromLayout, type NotationLayout } from './notationGeometry';\nimport { keyCenterX, type KeyboardLayout } from './keyboardGeometry';\n\nexport interface RayEndpoints {\n /** Staff-end point (screen px). */\n staff: { x: number; y: number };\n /** Keyboard-end point (screen px) = the top-centre of the key. */\n keyboard: { x: number; y: number };\n}\n\n/**\n * The staff-end anchor for an audio progress `t01` (0..1 over the clip), using the\n * playhead sweep across the notation's mapped measure columns. Mirrors\n * notationGeometry.playheadLine's x/y derivation so the ray starts exactly where\n * the playhead is. Returns null when the layout has no measures/systems.\n */\nexport function staffAnchor(layout: NotationLayout, t01: number): { x: number; y: number } | null {\n const tt = Math.max(0, Math.min(1, t01));\n const cols = measureColumnsFromLayout(layout.measures);\n if (cols.length) {\n const pos = tt * cols.length;\n const i = Math.min(cols.length - 1, Math.floor(pos));\n const m = cols[i];\n const startX = Math.min(m.noteStartX, m.x + m.w);\n const x = startX + (pos - i) * (m.x + m.w - startX);\n return { x, y: m.y + m.h / 2 };\n }\n if (layout.systems.length) {\n const pos = tt * layout.systems.length;\n const row = Math.min(layout.systems.length - 1, Math.floor(pos));\n const s = layout.systems[row];\n return { x: s.x + (pos - row) * s.w, y: s.y + s.h / 2 };\n }\n const r = layout.rect;\n return { x: r.dx + tt * r.dw, y: r.dy + r.dh / 2 };\n}\n\n/**\n * Full ray endpoints for a note's pitch + an audio progress, given both layouts.\n * The keyboard end is the top-centre of the note's key. Pure.\n */\nexport function rayEndpoints(\n notation: NotationLayout,\n keyboard: KeyboardLayout,\n pitchMidi: number,\n t01: number,\n): RayEndpoints | null {\n const staff = staffAnchor(notation, t01);\n if (!staff) return null;\n return {\n staff,\n keyboard: { x: keyCenterX(keyboard, pitchMidi), y: keyboard.top },\n };\n}\n\n/**\n * A point along a quadratic Bézier from staff→keyboard, bulged toward the side, at\n * parameter `u` 0..1. Used to draw a gently curved ray (a straight line reads as a\n * cursor; a curve reads as a connector). Pure.\n */\nexport function rayPointAt(ends: RayEndpoints, u: number, bulge = 0.18): { x: number; y: number } {\n const { staff: a, keyboard: b } = ends;\n // Control point: midpoint pushed horizontally by `bulge` of the vertical span.\n const mx = (a.x + b.x) / 2;\n const my = (a.y + b.y) / 2;\n const span = Math.abs(b.y - a.y);\n const cx = mx + bulge * span;\n const cy = my;\n const uu = u < 0 ? 0 : u > 1 ? 1 : u;\n const inv = 1 - uu;\n return {\n x: inv * inv * a.x + 2 * inv * uu * cx + uu * uu * b.x,\n y: inv * inv * a.y + 2 * inv * uu * cy + uu * uu * b.y,\n };\n}\n","// Highlight-region primitive (S1b) — one reusable helper to spotlight a region\n// for a time range. Used by harmony/theory/ear-training/\"play this part\" layers.\n//\n// A region is an x-range band (world coords) shown only while tMs is inside\n// [inMs, outMs], with a short fade so it doesn't pop. The intensity envelope is\n// pure math (testable without a canvas); the draw helper is a thin band/dim\n// renderer over it.\n\nimport { clamp, invLerp } from './math';\n\nexport interface HighlightRegion {\n /** World x of the band's left edge. */\n x: number;\n /** World y of the band's top edge. */\n y: number;\n /** Band width (world px). */\n w: number;\n /** Band height (world px). */\n h: number;\n /** Show from this time (ms). */\n inMs: number;\n /** Hide after this time (ms). */\n outMs: number;\n /** Fade in/out duration (ms). Default 120. */\n fadeMs?: number;\n /** Fill colour (defaults to a translucent accent at draw time). */\n color?: string;\n}\n\n/**\n * Highlight intensity 0..1 at time `tMs`: 0 outside the window, ramping to 1\n * across `fadeMs` at each edge, flat 1 in the middle. Pure.\n */\nexport function highlightIntensity(region: HighlightRegion, tMs: number): number {\n const fade = region.fadeMs ?? 120;\n if (tMs <= region.inMs - fade || tMs >= region.outMs + fade) return 0;\n const rampIn = invLerp(region.inMs - fade, region.inMs, tMs);\n const rampOut = 1 - invLerp(region.outMs, region.outMs + fade, tMs);\n return clamp(Math.min(rampIn, rampOut), 0, 1);\n}\n\n/**\n * Compute the world x-range covering a set of note onsets that fall inside a\n * time window, given a time→x mapping. Returns null if none qualify. Useful for\n * \"spotlight the bar that's sounding now\" without a layer re-deriving geometry.\n */\nexport function noteSetXRange(\n onsetsMs: number[],\n windowMs: [number, number],\n timeToX: (ms: number) => number,\n): { x: number; w: number } | null {\n let lo = Infinity;\n let hi = -Infinity;\n for (const on of onsetsMs) {\n if (on < windowMs[0] || on > windowMs[1]) continue;\n const x = timeToX(on);\n if (x < lo) lo = x;\n if (x > hi) hi = x;\n }\n if (lo === Infinity) return null;\n return { x: lo, w: Math.max(0, hi - lo) };\n}\n\n/**\n * Draw a highlight band for the current time. No-op when intensity is 0, so it\n * is safe to call every frame. Draws in WORLD coords (the camera transform, if\n * any, is already on the context).\n */\nexport function drawHighlight(\n ctx: CanvasRenderingContext2D,\n region: HighlightRegion,\n tMs: number,\n accent: string,\n): void {\n const a = highlightIntensity(region, tMs);\n if (a <= 0) return;\n ctx.save();\n ctx.globalAlpha = a * 0.35;\n ctx.fillStyle = region.color ?? accent;\n ctx.fillRect(region.x, region.y, region.w, region.h);\n ctx.restore();\n}\n","// `staff-keyboard-ray` layer (S5) — an animated connector from a sounding note's\n// position on the STAFF to its KEY on the keyboard, the top beginner-killer\n// feature (makes \"this dot = this key\" literal). Depends on BOTH a notation layer\n// (engravingStore → per-frame staff geometry + playhead position) AND a keyboard\n// layer (keyboardStore → key x's); if either is absent the layer draws nothing.\n//\n// Sync (pure fn of t): the staff end rides the playhead position (same sweep as\n// notationGeometry.playheadLine); a ray is drawn for each note SOUNDING now, from\n// the staff anchor down a gentle curve to the note's key top, with a draw-on\n// progress (the ray \"grows\" toward the key over a short reveal) + fade.\n//\n// Props pick WHICH notation/keyboard layers to link (by their layer keys) — v1\n// reads the single published notation/keyboard layouts from the per-render stores,\n// so the keys are informational/forward-compatible (documented).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getNotationEngraving } from '../engravingStore';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { inRange } from '../keyboardGeometry';\nimport { rayEndpoints, rayPointAt } from '../staffKeyboardRay';\nimport { highlightIntensity } from '../highlight';\n\nexport interface StaffKeyboardRayProps {\n /** Layer key of the notation layer to link (forward-compat; v1 uses the single\n * published engraving). Default \"notation\". */\n notationKey?: string;\n /** Layer key of the keyboard layer to link. Default \"keyboard\". */\n keyboardKey?: string;\n /** Ray reveal/grow + fade duration, ms. Default 180. */\n fadeMs?: number;\n /** Line width, world px. Default 4. */\n width?: number;\n /** Ray colour (default per-note hand colour via the theme accent). */\n color?: string;\n /** Number of segments to approximate the curve. Default 24. */\n segments?: number;\n}\n\nconst DEFAULT_FADE = 180;\nconst DEFAULT_WIDTH = 4;\nconst DEFAULT_SEGMENTS = 24;\n\nfunction staffKeyboardRayLayer(): Layer<StaffKeyboardRayProps> {\n let fadeMs = DEFAULT_FADE;\n let width = DEFAULT_WIDTH;\n let color: string | undefined;\n let segments = DEFAULT_SEGMENTS;\n\n return {\n key: 'staff-keyboard-ray',\n init(_ctx: RenderCtx, props) {\n fadeMs = props.fadeMs ?? DEFAULT_FADE;\n width = props.width ?? DEFAULT_WIDTH;\n color = props.color;\n segments = props.segments ?? DEFAULT_SEGMENTS;\n },\n\n draw(ctx, tMs) {\n const eng = getNotationEngraving(ctx);\n const kbd = getKeyboardLayout(ctx);\n if (!eng || !kbd) return; // needs both a notation and a keyboard layer\n\n // The notation layout this frame (follow window when a scroll-cursor is\n // present, else the base) — the SAME bitmap the notation layer blits.\n const layout = eng.followLayoutAt ? eng.followLayoutAt(ctx, tMs) : eng.base;\n const dur = ctx.score?.durationMs ?? 0;\n const t01 = dur > 0 ? tMs / dur : 0;\n\n const c = ctx.ctx2d;\n const accent = color ?? ctx.theme.accent;\n\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(kbd, n.pitchMidi)) continue;\n\n const ends = rayEndpoints(layout, kbd, n.pitchMidi, t01);\n if (!ends) continue;\n // NaN / off-canvas guard (gate placement-sanity in-layer).\n if (!Number.isFinite(ends.staff.x) || !Number.isFinite(ends.keyboard.x)) continue;\n\n // Grow-on: the ray extends from staff toward the key over `fadeMs` after\n // onset, then holds; fade out near the note's end. Reuse the highlight\n // envelope for the alpha, and a separate grow fraction for the length.\n const alpha = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: n.onsetMs, outMs: n.onsetMs + n.durMs, fadeMs },\n tMs,\n );\n if (alpha <= 0) continue;\n const grow = Math.min(1, fadeMs > 0 ? (tMs - n.onsetMs) / fadeMs : 1);\n\n c.save();\n c.globalAlpha = alpha;\n c.strokeStyle = accent;\n c.lineWidth = width;\n c.beginPath();\n const steps = Math.max(2, segments);\n for (let i = 0; i <= steps; i++) {\n const u = (i / steps) * Math.max(0, grow);\n const p = rayPointAt(ends, u);\n if (i === 0) c.moveTo(p.x, p.y);\n else c.lineTo(p.x, p.y);\n }\n c.stroke();\n // A dot at each end to read as anchored.\n const head = rayPointAt(ends, Math.max(0, grow));\n c.fillStyle = accent;\n c.beginPath();\n c.arc(ends.staff.x, ends.staff.y, width * 1.1, 0, Math.PI * 2);\n c.fill();\n c.beginPath();\n c.arc(head.x, head.y, width * 1.1, 0, Math.PI * 2);\n c.fill();\n c.restore();\n }\n },\n };\n}\n\nexport const staffKeyboardRayFactory: LayerFactory<StaffKeyboardRayProps> = {\n key: 'staff-keyboard-ray',\n create: staffKeyboardRayLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['staff-keyboard-ray: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.notationKey != null && typeof p.notationKey !== 'string')\n errs.push('staff-keyboard-ray.notationKey must be a string');\n if (p.keyboardKey != null && typeof p.keyboardKey !== 'string')\n errs.push('staff-keyboard-ray.keyboardKey must be a string');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('staff-keyboard-ray.fadeMs must be a non-negative number');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0))\n errs.push('staff-keyboard-ray.width must be a positive number');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('staff-keyboard-ray.color must be a string');\n if (p.segments != null && (typeof p.segments !== 'number' || p.segments < 2))\n errs.push('staff-keyboard-ray.segments must be a number ≥ 2');\n return errs;\n },\n};\n","// Counting-grid geometry (S5) — the pure, canvas-free beat/subdivision schedule\n// for the `counting-track` layer (\"1 e & a\"). Derives beat onsets from the\n// Score's tempoMap (v1: single constant bpm) + time signature, and the bouncing-\n// ball vertical position from the phase within the current beat.\n//\n// A \"beat\" is the time-signature denominator's note value (so 6/8 counts in\n// eighths → six beats/bar). Subdivisions split each beat evenly; the canonical\n// syllables are number-on-the-beat then \"e & a\" for 16ths or \"&\" for 8ths.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\nimport type { TempoMap } from './score';\n\nexport interface BeatGridOpts {\n /** Constant bpm of the QUARTER-note beat (tempoMap.segments[0].bpm). */\n bpm: number;\n /** Beats per bar (time-sig numerator). Default 4. */\n beatsPerBar?: number;\n /** Time-sig denominator (note value of one beat). Default 4. */\n beatUnit?: number;\n /** Subdivisions per beat for the syllable row: 1, 2 (\"&\"), or 4 (\"e & a\"). Default 1. */\n subdiv?: 1 | 2 | 4;\n /** Total length to cover, ms. */\n durationMs: number;\n /** Count-in offset: first beat lands at this ms (default 0). */\n startMs?: number;\n}\n\nexport interface BeatTick {\n /** Onset time, ms. */\n tMs: number;\n /** 0-based beat index across the whole clip. */\n index: number;\n /** 1-based beat number within the bar (the big counted number). */\n beatInBar: number;\n /** Subdivision index within the beat (0 = on the beat). */\n sub: number;\n /** The counting syllable (\"1\",\"e\",\"&\",\"a\", \"2\", …). */\n syllable: string;\n /** True on the downbeat (beatInBar === 1, sub === 0). */\n downbeat: boolean;\n}\n\nconst SUB4 = ['', 'e', '&', 'a'];\n\n/** ms between quarter beats; the beat-unit scales it (eighth beat = half a quarter). */\nexport function msPerBeat(bpm: number, beatUnit: number): number {\n // bpm is the quarter-note tempo; one beat-unit note = 4/beatUnit quarters.\n return (60000 / bpm) * (4 / beatUnit);\n}\n\n/** Build the full beat+subdivision schedule across the clip. Pure. */\nexport function beatGrid(opts: BeatGridOpts): BeatTick[] {\n const beatsPerBar = opts.beatsPerBar ?? 4;\n const beatUnit = opts.beatUnit ?? 4;\n const subdiv = opts.subdiv ?? 1;\n const start = opts.startMs ?? 0;\n const mpb = msPerBeat(opts.bpm, beatUnit);\n const subMs = mpb / subdiv;\n const ticks: BeatTick[] = [];\n let beatIndex = 0;\n // A beat is included when its ONSET is within [start, start+durationMs]; once a\n // beat is in, ALL its subdivisions belong to it (so durationMs:0 → one full beat\n // of subdivisions). This may emit subdivisions slightly past durationMs on the\n // final beat — intended (a beat is an atomic unit of counting).\n for (let t = start; t <= start + opts.durationMs + 1e-6; t += mpb) {\n const beatInBar = (beatIndex % beatsPerBar) + 1;\n for (let s = 0; s < subdiv; s++) {\n const tMs = t + s * subMs;\n ticks.push({\n tMs,\n index: beatIndex,\n beatInBar,\n sub: s,\n syllable: s === 0 ? String(beatInBar) : SUB4[(s * (4 / subdiv)) | 0] || '·',\n downbeat: beatInBar === 1 && s === 0,\n });\n }\n beatIndex++;\n }\n return ticks;\n}\n\n/** The TempoMap's first-segment bpm (v1 single-tempo helper). */\nexport function bpmOf(tempoMap: TempoMap | undefined, fallback = 100): number {\n return tempoMap?.segments?.[0]?.bpm ?? fallback;\n}\n\n/**\n * Phase 0..1 within the current beat at time `tMs` (0 = on the beat). Pure.\n * Used by the bouncing ball's vertical position.\n */\nexport function beatPhase(bpm: number, beatUnit: number, tMs: number, startMs = 0): number {\n const mpb = msPerBeat(bpm, beatUnit);\n const rel = tMs - startMs;\n if (rel < 0) return 0;\n const p = (rel % mpb) / mpb;\n return p < 0 ? p + 1 : p;\n}\n\n/**\n * The bouncing ball's vertical offset 0..1 (0 = at the track line / floor, 1 =\n * top of the arc). A parabola that touches 0 exactly ON each beat and peaks at\n * mid-beat — so the ball \"lands\" on the count. Pure.\n */\nexport function ballArc(phase01: number): number {\n // 4·p·(1−p) is a parabola: 0 at p=0 and p=1, 1 at p=0.5.\n const p = phase01 < 0 ? 0 : phase01 > 1 ? 1 : phase01;\n return 4 * p * (1 - p);\n}\n\n/** Horizontal x (world) of the ball at time t, sweeping left→right across the\n * counting track in step with the beats. Pure. */\nexport function ballX(\n ticks: BeatTick[],\n tMs: number,\n xOf: (tick: BeatTick) => number,\n): number {\n if (!ticks.length) return 0;\n // Find the bracketing on-beat ticks (sub===0) and lerp the x between them.\n const beats = ticks.filter((t) => t.sub === 0);\n if (tMs <= beats[0].tMs) return xOf(beats[0]);\n for (let i = 0; i < beats.length - 1; i++) {\n const a = beats[i];\n const b = beats[i + 1];\n if (tMs >= a.tMs && tMs < b.tMs) {\n const f = (tMs - a.tMs) / (b.tMs - a.tMs);\n return xOf(a) + (xOf(b) - xOf(a)) * f;\n }\n }\n return xOf(beats[beats.length - 1]);\n}\n\n/** Parse a \"n/d\" time signature string into [numerator, denominator]. */\nexport function parseTimeSig(ts: string | undefined): [number, number] {\n if (!ts) return [4, 4];\n const m = /^(\\d+)\\s*\\/\\s*(\\d+)$/.exec(ts.trim());\n if (!m) return [4, 4];\n return [parseInt(m[1], 10), parseInt(m[2], 10)];\n}\n","// `counting-track` layer (S5) — a beat/subdivision counting strip (\"1 e & a\")\n// with a ball that bounces ON each beat. Rhythm pedagogy: shows the metric grid\n// the Score is played against, derived from the Score's tempoMap + time signature\n// (no extra data needed). Pure fn of t.\n//\n// Layout: a horizontal track pinned near the bottom of the safe box. The counting\n// syllables are laid out evenly across the safe width as a fixed legend (a full\n// bar of counts); the ball sweeps left→right across that bar in step with the\n// beats, dropping to the track line exactly on each count (ballArc parabola) so\n// the bounce reads as \"land on the beat\".\n//\n// Sync: every position is a pure function of t via beatGrid/beatPhase/ballArc —\n// audio-clock-driven, no wall clock.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n beatPhase,\n ballArc,\n bpmOf,\n parseTimeSig,\n type BeatTick,\n} from '../countingGrid';\n\nexport interface CountingTrackProps {\n /** Subdivisions per beat shown: 1 (just numbers), 2 (\"&\"), 4 (\"e & a\"). Default 1. */\n subdiv?: 1 | 2 | 4;\n /** Override time signature (else Score.timeSig, else 4/4). */\n timeSig?: string;\n /** Override bpm (else Score.tempoMap, else 100). */\n bpm?: number;\n /** Track baseline (world y) the ball lands on. Default safeBox.bottom - 120. */\n trackY?: number;\n /** Bounce height in world px. Default 110. */\n bounce?: number;\n /** Syllable font size, world px. Default 34. */\n size?: number;\n /** Ball radius, world px. Default 22. */\n ballRadius?: number;\n /** Count-in offset, ms (first beat lands here). Default 0. */\n startMs?: number;\n}\n\nconst DEFAULT_BOUNCE = 110;\nconst DEFAULT_SIZE = 34;\nconst DEFAULT_BALL = 22;\n\nfunction countingTrackLayer(): Layer<CountingTrackProps> {\n let subdiv: 1 | 2 | 4 = 1;\n let bpm = 100;\n let beatUnit = 4;\n let beatsPerBar = 4;\n let trackYProp: number | undefined;\n let bounce = DEFAULT_BOUNCE;\n let size = DEFAULT_SIZE;\n let ballRadius = DEFAULT_BALL;\n let startMs = 0;\n // The legend = ONE bar of counts laid out across the safe width.\n let legend: BeatTick[] = [];\n let left = 0;\n let width = 0;\n\n function xOfTick(t: BeatTick): number {\n // Index within the bar (beat + subdivision), evenly across the safe width.\n const idx = (t.beatInBar - 1) * subdiv + t.sub;\n const slots = beatsPerBar * subdiv;\n return left + ((idx + 0.5) / slots) * width;\n }\n\n return {\n key: 'counting-track',\n init(ctx, props) {\n subdiv = props.subdiv ?? 1;\n const [num, den] = parseTimeSig(props.timeSig ?? ctx.score?.timeSig);\n beatsPerBar = num;\n beatUnit = den;\n bpm = props.bpm ?? bpmOf(ctx.score?.tempoMap);\n trackYProp = props.trackY;\n bounce = props.bounce ?? DEFAULT_BOUNCE;\n size = props.size ?? DEFAULT_SIZE;\n ballRadius = props.ballRadius ?? DEFAULT_BALL;\n startMs = props.startMs ?? 0;\n\n const sb = ctx.safeBox;\n left = sb.left;\n width = sb.w;\n // The fixed legend: exactly one bar of ticks (downbeat..end of bar). We\n // build it directly rather than via beatGrid because the legend is metric\n // positions in a bar (timeless), not absolute onsets across the clip.\n legend = [];\n for (let b = 0; b < beatsPerBar; b++) {\n for (let s = 0; s < subdiv; s++) {\n legend.push({\n tMs: 0, index: b, beatInBar: b + 1, sub: s,\n syllable: s === 0 ? String(b + 1) : ['', 'e', '&', 'a'][(s * (4 / subdiv)) | 0] || '·',\n downbeat: b === 0 && s === 0,\n });\n }\n }\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const trackY = trackYProp ?? sb.bottom - 120;\n\n c.save();\n // Track baseline.\n c.strokeStyle = ctx.theme.sepia;\n c.lineWidth = 2;\n c.globalAlpha = 0.5;\n c.beginPath();\n c.moveTo(left, trackY);\n c.lineTo(left + width, trackY);\n c.stroke();\n c.globalAlpha = 1;\n\n // Which legend slot is active now (so we can emphasise the current count).\n const phase = beatPhase(bpm, beatUnit, tMs, startMs);\n const mpb = (60000 / bpm) * (4 / beatUnit);\n const rel = Math.max(0, tMs - startMs);\n const beatIdx = Math.floor(rel / mpb);\n const activeBeatInBar = (beatIdx % beatsPerBar);\n const activeSub = Math.floor((phase * subdiv)) % subdiv;\n const activeSlot = activeBeatInBar * subdiv + activeSub;\n\n // Syllable legend.\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n for (const t of legend) {\n const x = xOfTick(t);\n const slot = (t.beatInBar - 1) * subdiv + t.sub;\n const active = slot === activeSlot;\n c.font = `${t.sub === 0 ? '700' : '400'} ${t.sub === 0 ? size : size * 0.8}px ${ctx.theme.fontBody}`;\n c.fillStyle = active ? ctx.theme.accent : ctx.theme.ink;\n c.globalAlpha = active ? 1 : t.sub === 0 ? 0.85 : 0.55;\n c.fillText(t.syllable, x, trackY + size * 0.95);\n // tick mark on the baseline\n c.globalAlpha = active ? 0.9 : 0.4;\n c.fillRect(x - 1, trackY - (t.sub === 0 ? 10 : 6), 2, t.sub === 0 ? 10 : 6);\n }\n c.globalAlpha = 1;\n\n // Bouncing ball: x sweeps across the bar in step with the beat; y bounces.\n // x: map the within-bar beat position (continuous) to a legend x.\n const beatPosInBar = (beatIdx % beatsPerBar) + phase; // 0..beatsPerBar\n const ballSlot = beatPosInBar * subdiv; // continuous slot\n const slots = beatsPerBar * subdiv;\n const bx = left + (Math.min(slots, ballSlot + 0.5) / slots) * width;\n const arc = ballArc(phase); // 0 on the beat, 1 mid-beat\n const by = trackY - ballRadius - arc * bounce;\n\n c.fillStyle = ctx.theme.accent;\n c.beginPath();\n c.arc(bx, by, ballRadius, 0, Math.PI * 2);\n c.fill();\n // a soft shadow ellipse on the track to anchor the bounce\n c.globalAlpha = 0.2 * (1 - arc);\n c.fillStyle = ctx.theme.ink;\n c.beginPath();\n c.arc(bx, trackY, ballRadius * 0.8, 0, Math.PI * 2);\n c.fill();\n c.restore();\n },\n };\n}\n\nexport const countingTrackFactory: LayerFactory<CountingTrackProps> = {\n key: 'counting-track',\n create: countingTrackLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['counting-track: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.subdiv != null && p.subdiv !== 1 && p.subdiv !== 2 && p.subdiv !== 4)\n errs.push('counting-track.subdiv must be 1 | 2 | 4');\n if (p.timeSig != null && (typeof p.timeSig !== 'string' || !/^\\d+\\s*\\/\\s*\\d+$/.test(p.timeSig)))\n errs.push('counting-track.timeSig must be \"n/d\"');\n if (p.bpm != null && (typeof p.bpm !== 'number' || p.bpm <= 0))\n errs.push('counting-track.bpm must be a positive number');\n if (p.trackY != null && typeof p.trackY !== 'number')\n errs.push('counting-track.trackY must be a number');\n if (p.bounce != null && (typeof p.bounce !== 'number' || p.bounce < 0))\n errs.push('counting-track.bounce must be a non-negative number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0))\n errs.push('counting-track.size must be a positive number');\n if (p.ballRadius != null && (typeof p.ballRadius !== 'number' || p.ballRadius <= 0))\n errs.push('counting-track.ballRadius must be a positive number');\n if (p.startMs != null && typeof p.startMs !== 'number')\n errs.push('counting-track.startMs must be a number');\n return errs;\n },\n};\n","// Scale-degree / solfège label geometry (S5) — the pure, canvas-free mapping from\n// a Score note (step+alter+pitch) and the Score's key to the text that labels it:\n// a scale-degree number (\"1\"..\"7\", chromatic degrees flagged) or movable-do\n// solfège (\"do re mi …\", chromatic syllables \"di ra …\").\n//\n// Correctness principle (spec S5): use the note's NOTATED spelling (step+alter),\n// NOT a pitch-class guess, so A♭ in an E♭-major piece is the 4th degree / \"fa\",\n// never spelled as G♯. The diatonic degree is the letter distance from the tonic\n// letter; the chromatic inflection is whether the note's pitch class is sharpened\n// or flattened relative to the diatonic scale-degree pitch in that key.\n//\n// Pure (no canvas, no DOM) ⇒ exhaustively unit-testable and deterministic.\n\nconst LETTERS = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];\nconst LETTER_PC: Record<string, number> = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };\n\n/** Major-scale semitone offsets for diatonic degrees 1..7 (index 0..6). */\nconst MAJOR_OFFSETS = [0, 2, 4, 5, 7, 9, 11];\n\n/** Movable-do solfège for the seven diatonic degrees (do based on the tonic). */\nconst DIATONIC_SOLFEGE = ['do', 're', 'mi', 'fa', 'sol', 'la', 'ti'];\n/** Raised (sharp) chromatic syllables, keyed by the diatonic degree being raised. */\nconst RAISED_SOLFEGE: Record<number, string> = { 1: 'di', 2: 'ri', 4: 'fi', 5: 'si', 6: 'li' };\n/** Lowered (flat) chromatic syllables, keyed by the diatonic degree being lowered. */\nconst LOWERED_SOLFEGE: Record<number, string> = { 2: 'ra', 3: 'me', 5: 'se', 6: 'le', 7: 'te' };\n\nexport type LabelMode = 'degree' | 'solfege';\n\nexport interface DegreeLabel {\n /** Diatonic degree 1..7 (the letter-distance from the tonic letter). */\n degree: number;\n /** Chromatic inflection in semitones vs the diatonic degree (−1 flat, +1 sharp, 0 natural). */\n alter: number;\n /** The display text for the chosen mode. */\n text: string;\n}\n\n/** Parse a key string (\"Eb major\" / \"C# minor\" / \"F\") into a tonic letter + mode. */\nexport function parseKey(key: string | undefined): { tonicLetter: string; tonicPc: number; minor: boolean } {\n if (!key) return { tonicLetter: 'C', tonicPc: 0, minor: false };\n const m = /^([A-Ga-g])([#b]*)\\s*(major|minor|maj|min|m)?/.exec(key.trim());\n if (!m) return { tonicLetter: 'C', tonicPc: 0, minor: false };\n const letter = m[1].toUpperCase();\n let pc = LETTER_PC[letter] ?? 0;\n for (const ch of m[2]) pc += ch === '#' ? 1 : -1;\n const modeTok = (m[3] ?? '').toLowerCase();\n const minor = modeTok === 'minor' || modeTok === 'min' || modeTok === 'm';\n return { tonicLetter: letter, tonicPc: ((pc % 12) + 12) % 12, minor };\n}\n\n/**\n * Compute the scale-degree label for a note, from its NOTATED spelling.\n *\n * `degree` = the letter distance (1..7) from the tonic letter to the note's\n * letter, which is the enharmonically-correct diatonic degree. `alter` = how the\n * note's actual pitch class differs from that degree's pitch in the key\n * (so D♯ vs D♭ in C give the same degree 2 but +1 / −1). For MINOR keys the\n * reference scale is natural minor (so the lowered 3/6/7 read as natural degrees,\n * matching how movable-do \"la-based minor\" is taught only in MAJOR; here we use\n * do-based, i.e. degrees relative to the major scale of the tonic — see note).\n *\n * Pure.\n */\nexport function degreeLabel(\n step: string,\n alter: number,\n pitchMidi: number,\n key: string | undefined,\n mode: LabelMode,\n): DegreeLabel {\n const { tonicLetter } = parseKey(key);\n const noteLetterIdx = LETTERS.indexOf(step.toUpperCase());\n const tonicLetterIdx = LETTERS.indexOf(tonicLetter);\n // Diatonic degree = letter distance (mod 7), 1-based.\n const degIdx = noteLetterIdx < 0 || tonicLetterIdx < 0\n ? 0\n : ((noteLetterIdx - tonicLetterIdx) % 7 + 7) % 7;\n const degree = degIdx + 1;\n\n // The pitch class this degree would have in the MAJOR scale of the tonic.\n const { tonicPc } = parseKey(key);\n const diatonicPc = (tonicPc + MAJOR_OFFSETS[degIdx]) % 12;\n // Use the NOTATED spelling (letter + alter) for the actual pc — not a pitch\n // guess — so the chromatic inflection is enharmonically correct. We sanity-check\n // against pitchMidi and fall back to it if step is unrecognised.\n const letterPc = LETTER_PC[step.toUpperCase()];\n const actualPc = letterPc != null\n ? ((letterPc + alter) % 12 + 12) % 12\n : ((pitchMidi % 12) + 12) % 12;\n // Signed chromatic inflection in [-2..+2]; prefer the small-magnitude wrap.\n let chrom = actualPc - diatonicPc;\n if (chrom > 6) chrom -= 12;\n if (chrom < -6) chrom += 12;\n\n const text = mode === 'solfege'\n ? solfegeText(degree, chrom)\n : degreeText(degree, chrom);\n return { degree, alter: chrom, text };\n}\n\nfunction degreeText(degree: number, chrom: number): string {\n const acc = chrom > 0 ? '♯'.repeat(chrom) : chrom < 0 ? '♭'.repeat(-chrom) : '';\n return `${acc}${degree}`;\n}\n\nfunction solfegeText(degree: number, chrom: number): string {\n if (chrom === 0) return DIATONIC_SOLFEGE[degree - 1] ?? '?';\n if (chrom > 0) return RAISED_SOLFEGE[degree] ?? `${DIATONIC_SOLFEGE[degree - 1]}♯`;\n return LOWERED_SOLFEGE[degree] ?? `${DIATONIC_SOLFEGE[degree - 1]}♭`;\n}\n","// `degree-labels` layer (S5) — scale-degree numbers or movable-do solfège drawn\n// on the keyboard keys as their notes SOUND. Pedagogy layer: reads the Score's\n// key + each note's NOTATED step/alter (enharmonically correct — A♭ in E♭ major\n// reads as \"4\"/\"fa\", never \"♯3\"), so it teaches function, not pitch-class trivia.\n//\n// Geometry source: the keyboard layer's published KeyboardLayout (keyboardStore),\n// so a label sits exactly over the key the `keyboard` layer lights — by\n// construction, no second layout. Requires a `keyboard` layer in the scene; if\n// none has published a layout this frame the layer draws nothing (documented).\n//\n// Sync: a label shows while its note sounds (onsetMs ≤ t < onsetMs+durMs) with a\n// short fade, riding the highlight-region intensity envelope so it doesn't pop.\n// Pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { keyCenterX, inRange, isBlackKey, type KeyboardLayout } from '../keyboardGeometry';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { degreeLabel, type LabelMode } from '../degreeLabels';\nimport { highlightIntensity } from '../highlight';\n\nexport interface DegreeLabelsProps {\n /** \"degree\" = scale-degree numbers (1..7, ♯/♭ chromatics); \"solfege\" =\n * movable-do syllables (do re mi …, di ra …). Default \"degree\". */\n mode?: LabelMode;\n /** Override the Score's key (e.g. for an excerpt in a different key). */\n key?: string;\n /** Label font size in world px. Default 30. */\n size?: number;\n /** Fade in/out of a label, ms. Default 120. */\n fadeMs?: number;\n /** Text colour. Default theme.ink on a translucent chip. */\n color?: string;\n}\n\nconst DEFAULT_SIZE = 30;\n\nfunction degreeLabelsLayer(): Layer<DegreeLabelsProps> {\n let mode: LabelMode = 'degree';\n let keyOverride: string | undefined;\n let size = DEFAULT_SIZE;\n let fadeMs = 120;\n let color: string | undefined;\n\n return {\n key: 'degree-labels',\n init(_ctx: RenderCtx, props) {\n mode = props.mode ?? 'degree';\n keyOverride = props.key;\n size = props.size ?? DEFAULT_SIZE;\n fadeMs = props.fadeMs ?? 120;\n color = props.color;\n },\n\n draw(ctx, tMs) {\n const layout: KeyboardLayout | undefined = getKeyboardLayout(ctx);\n if (!layout) return; // no keyboard layer in the scene → nothing to anchor to\n const c = ctx.ctx2d;\n const key = keyOverride ?? ctx.score?.key;\n const ink = color ?? ctx.theme.ink;\n\n for (const n of ctx.score?.notes ?? []) {\n if (!inRange(layout, n.pitchMidi)) continue;\n // Reuse the highlight envelope for a clean fade around the sounding window.\n const a = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: n.onsetMs, outMs: n.onsetMs + n.durMs, fadeMs },\n tMs,\n );\n if (a <= 0) continue;\n\n const label = degreeLabel(n.step, n.alter, n.pitchMidi, key, mode);\n const cx = keyCenterX(layout, n.pitchMidi);\n // Sit the chip just above the keyboard; black keys a touch higher so the\n // chip clears the (taller, frontmost) white-key chips below.\n const cy = layout.top - (isBlackKey(n.pitchMidi) ? size * 1.6 : size * 0.6);\n\n c.save();\n c.globalAlpha = a;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${size}px ${ctx.theme.fontBody}`;\n // Pill behind the text for legibility over any background.\n const w = c.measureText(label.text).width;\n const padX = size * 0.35;\n const padY = size * 0.22;\n c.fillStyle = ctx.theme.paper;\n c.globalAlpha = a * 0.92;\n roundRect(c, cx - w / 2 - padX, cy - size / 2 - padY, w + 2 * padX, size + 2 * padY, size * 0.3);\n c.fill();\n c.globalAlpha = a;\n c.fillStyle = ink;\n c.fillText(label.text, cx, cy);\n c.restore();\n }\n },\n };\n}\n\n/** Rounded-rect path (kept local so the layer has no extra deps; falls back to a\n * plain rect when the ctx lacks the helper, e.g. the recording mock). */\nfunction roundRect(\n c: CanvasRenderingContext2D,\n x: number, y: number, w: number, h: number, r: number,\n): void {\n if (typeof (c as unknown as { roundRect?: unknown }).roundRect === 'function') {\n c.beginPath();\n (c as unknown as { roundRect: (x: number, y: number, w: number, h: number, r: number) => void })\n .roundRect(x, y, w, h, r);\n return;\n }\n c.beginPath();\n c.moveTo(x + r, y);\n c.lineTo(x + w - r, y);\n c.lineTo(x + w, y + h);\n c.lineTo(x, y + h);\n c.closePath();\n}\n\nexport const degreeLabelsFactory: LayerFactory<DegreeLabelsProps> = {\n key: 'degree-labels',\n create: degreeLabelsLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['degree-labels: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.mode != null && p.mode !== 'degree' && p.mode !== 'solfege')\n errs.push('degree-labels.mode must be \"degree\" | \"solfege\"');\n if (p.key != null && typeof p.key !== 'string')\n errs.push('degree-labels.key must be a string');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0))\n errs.push('degree-labels.size must be a positive number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('degree-labels.fadeMs must be a non-negative number');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('degree-labels.color must be a string');\n return errs;\n },\n};\n","// Functional-harmony track (S5) — pure helpers for the `functional-harmony` layer.\n//\n// IMPORTANT (spec S5): the Score does NOT carry harmony. This layer takes a CHORD\n// TRACK as a layer prop (a provider) and does NOT fabricate harmony from the\n// notes. The track is a timed list of chord spans, each with a tonal FUNCTION\n// (Tonic / Subdominant / Dominant / other) — supplied by the host (e.g. RET's\n// chords.json or an analysis step). We map function → a stable colour and resolve\n// the active span at time t. Pure (no canvas) ⇒ unit-testable.\n\nexport type HarmonicFunction = 'T' | 'S' | 'D' | 'other';\n\nexport interface ChordSpan {\n /** Span start, ms. */\n startMs: number;\n /** Span end, ms. */\n endMs: number;\n /** Tonal function. */\n fn: HarmonicFunction;\n /** Optional display label (roman numeral / chord symbol), drawn by the layer. */\n label?: string;\n}\n\n/** Default function colours (warm tonic / cool subdominant / tense dominant). */\nexport interface FunctionColors {\n T: string;\n S: string;\n D: string;\n other: string;\n}\n\nexport const DEFAULT_FUNCTION_COLORS: FunctionColors = {\n T: '#3a7d44', // grounded green\n S: '#3a6ea5', // calm blue\n D: '#c2502f', // tense orange-red\n other: '#7a7a7a',\n};\n\n/** The chord span active at time `tMs` (first whose [start,end) contains t), or null. */\nexport function activeChord(track: ChordSpan[], tMs: number): ChordSpan | null {\n for (const s of track) {\n if (tMs >= s.startMs && tMs < s.endMs) return s;\n }\n return null;\n}\n\n/** Colour for a function. Pure. */\nexport function functionColor(fn: HarmonicFunction, colors: FunctionColors): string {\n return colors[fn] ?? colors.other;\n}\n\n/**\n * Validate a chord track (used by the layer's prop validator). Returns []\n * when valid, else human-readable errors. Exported so the gate message is precise.\n */\nexport function validateChordTrack(track: unknown): string[] {\n if (!Array.isArray(track)) return ['chordTrack must be an array of chord spans'];\n const errs: string[] = [];\n track.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.startMs !== 'number' || typeof s?.endMs !== 'number')\n errs.push(`chordTrack[${i}] needs numeric startMs/endMs`);\n else if (s.endMs <= s.startMs) errs.push(`chordTrack[${i}].endMs must be > startMs`);\n if (s?.fn !== 'T' && s?.fn !== 'S' && s?.fn !== 'D' && s?.fn !== 'other')\n errs.push(`chordTrack[${i}].fn must be \"T\"|\"S\"|\"D\"|\"other\"`);\n if (s?.label != null && typeof s.label !== 'string')\n errs.push(`chordTrack[${i}].label must be a string`);\n });\n return errs;\n}\n","// `functional-harmony` layer (S5) — colour the scene by tonal FUNCTION (Tonic /\n// Subdominant / Dominant) over time. Harmony pedagogy: the whole frame breathes\n// T→S→D→T so the listener FEELS the cadential arc.\n//\n// DATA SOURCE (spec S5): the Score carries no harmony, so this layer takes a\n// `chordTrack` prop (timed ChordSpans with a function + optional roman/label),\n// supplied by the host (RET chords.json, an analysis step, …). It does NOT infer\n// harmony from the notes — if no track is given the layer draws nothing and the\n// validator flags it.\n//\n// Render modes (composable):\n// - \"band\": a colour bar across the top of the safe box (always available).\n// - \"keys\": tint the sounding keyboard keys by the active function (needs a\n// keyboard layer's published layout; rides keyboardStore + the highlight prim).\n// - \"wash\": a faint full-frame wash in the function colour.\n// A roman/label is drawn in the band when the active span has one.\n//\n// Pure fn of t (active span resolved by activeChord; intensity by highlightIntensity).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { keyRect, inRange } from '../keyboardGeometry';\nimport { highlightIntensity } from '../highlight';\nimport {\n activeChord,\n functionColor,\n validateChordTrack,\n DEFAULT_FUNCTION_COLORS,\n type ChordSpan,\n type FunctionColors,\n} from '../harmonyTrack';\n\nexport type HarmonyMode = 'band' | 'keys' | 'wash';\n\nexport interface FunctionalHarmonyProps {\n /** REQUIRED: the timed chord spans (function + optional label). Host-supplied. */\n chordTrack: ChordSpan[];\n /** Which renderings to draw. Default [\"band\"]. */\n modes?: HarmonyMode[];\n /** Override the function→colour map. */\n colors?: FunctionColors;\n /** Band height (world px). Default 64. */\n bandHeight?: number;\n /** Band top (world y). Default safeBox.top. */\n bandTop?: number;\n /** Cross-fade between spans, ms. Default 200. */\n fadeMs?: number;\n /** Wash opacity at full intensity. Default 0.12. */\n washAlpha?: number;\n}\n\nconst DEFAULT_BAND_H = 64;\nconst DEFAULT_FADE = 200;\n\nfunction functionalHarmonyLayer(): Layer<FunctionalHarmonyProps> {\n let track: ChordSpan[] = [];\n let modes: HarmonyMode[] = ['band'];\n let colors: FunctionColors = DEFAULT_FUNCTION_COLORS;\n let bandHeight = DEFAULT_BAND_H;\n let bandTopProp: number | undefined;\n let fadeMs = DEFAULT_FADE;\n let washAlpha = 0.12;\n\n return {\n key: 'functional-harmony',\n init(_ctx: RenderCtx, props) {\n track = props.chordTrack ?? [];\n modes = props.modes ?? ['band'];\n colors = props.colors ?? DEFAULT_FUNCTION_COLORS;\n bandHeight = props.bandHeight ?? DEFAULT_BAND_H;\n bandTopProp = props.bandTop;\n fadeMs = props.fadeMs ?? DEFAULT_FADE;\n washAlpha = props.washAlpha ?? 0.12;\n },\n\n draw(ctx, tMs) {\n const span = activeChord(track, tMs);\n if (!span) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const col = functionColor(span.fn, colors);\n // Intensity ramps at the span edges so colours cross-fade, not pop.\n const a = highlightIntensity(\n { x: 0, y: 0, w: 0, h: 0, inMs: span.startMs, outMs: span.endMs, fadeMs },\n tMs,\n );\n if (a <= 0) return;\n\n if (modes.includes('wash')) {\n c.save();\n c.globalAlpha = a * washAlpha;\n c.fillStyle = col;\n c.fillRect(0, 0, ctx.W, ctx.H);\n c.restore();\n }\n\n if (modes.includes('keys')) {\n const layout = getKeyboardLayout(ctx);\n if (layout) {\n c.save();\n for (const n of ctx.score?.notes ?? []) {\n if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;\n if (!inRange(layout, n.pitchMidi)) continue;\n const r = keyRect(layout, n.pitchMidi);\n c.globalAlpha = a * 0.8;\n c.fillStyle = col;\n c.fillRect(r.x, r.y, r.w, r.h);\n }\n c.restore();\n }\n }\n\n if (modes.includes('band')) {\n const top = bandTopProp ?? sb.top;\n c.save();\n c.globalAlpha = a * 0.85;\n c.fillStyle = col;\n c.fillRect(sb.left, top, sb.w, bandHeight);\n if (span.label) {\n c.globalAlpha = a;\n c.fillStyle = '#ffffff';\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${Math.round(bandHeight * 0.5)}px ${ctx.theme.fontDisplay}`;\n c.fillText(span.label, sb.left + sb.w / 2, top + bandHeight / 2);\n }\n c.restore();\n }\n },\n };\n}\n\nexport const functionalHarmonyFactory: LayerFactory<FunctionalHarmonyProps> = {\n key: 'functional-harmony',\n create: functionalHarmonyLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['functional-harmony: props must be an object'];\n const p = props as Record<string, unknown>;\n // chordTrack is REQUIRED (the layer has no data otherwise).\n errs.push(...validateChordTrack(p.chordTrack).map((e) => `functional-harmony.${e}`));\n if (p.modes != null) {\n if (!Array.isArray(p.modes)) errs.push('functional-harmony.modes must be an array');\n else for (const m of p.modes)\n if (m !== 'band' && m !== 'keys' && m !== 'wash')\n errs.push('functional-harmony.modes items must be \"band\"|\"keys\"|\"wash\"');\n }\n if (p.bandHeight != null && (typeof p.bandHeight !== 'number' || p.bandHeight <= 0))\n errs.push('functional-harmony.bandHeight must be a positive number');\n if (p.bandTop != null && typeof p.bandTop !== 'number')\n errs.push('functional-harmony.bandTop must be a number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs < 0))\n errs.push('functional-harmony.fadeMs must be a non-negative number');\n if (p.washAlpha != null && (typeof p.washAlpha !== 'number' || p.washAlpha < 0 || p.washAlpha > 1))\n errs.push('functional-harmony.washAlpha must be in [0,1]');\n return errs;\n },\n};\n","// MCQ quiz/poll card (S5) — pure helpers for the `mcq-card` layer that unifies\n// RET's \"pick the interval/chord\" quiz with whozart's \"name that piece\".\n//\n// DATA SOURCE (spec S5): the Score carries no quiz, so the card is driven ENTIRELY\n// by a host-supplied `quiz` prop (question text, options, correctIndex, timings).\n// This module does NOT fabricate a quiz — it only resolves the card's PHASE at a\n// time t and validates the prop. Pure (no canvas) ⇒ exhaustively unit-testable.\n//\n// Phases over the card's lifetime (all times absolute ms on the clip clock):\n// - \"question\": options shown, countdown running, nothing revealed yet\n// [askMs, revealMs)\n// - \"reveal\": the correct option is highlighted; wrong picks dimmed\n// [revealMs, endMs)\n// `askMs` defaults to the start of the segment (0); a host that places the card in\n// a [a,b] segment passes askMs=a so the countdown maps to wall-clock correctly.\n\nexport interface QuizOption {\n /** Option text (e.g. \"Major 3rd\", \"Beethoven\"). */\n text: string;\n}\n\nexport interface Quiz {\n /** The prompt (\"Which interval?\" / \"Name this piece\"). */\n question: string;\n /** 2–4 options. */\n options: QuizOption[];\n /** Index into options of the correct answer (0-based). */\n correctIndex: number;\n /** When the card starts asking (absolute ms). Default 0. */\n askMs?: number;\n /** When the correct option is revealed (absolute ms). REQUIRED. */\n revealMs: number;\n /** When the card finishes (absolute ms). REQUIRED. */\n endMs: number;\n /** Optional per-option poll percentages (0..100), shown as bars. Length must\n * match options. Host-supplied (e.g. real community votes); not fabricated. */\n poll?: number[];\n}\n\nexport type QuizPhase = 'before' | 'question' | 'reveal' | 'after';\n\n/** Resolve the card phase at time `tMs`. Pure. */\nexport function quizPhase(quiz: Quiz, tMs: number): QuizPhase {\n const ask = quiz.askMs ?? 0;\n if (tMs < ask) return 'before';\n if (tMs < quiz.revealMs) return 'question';\n if (tMs < quiz.endMs) return 'reveal';\n return 'after';\n}\n\n/**\n * Countdown fraction remaining 0..1 across [askMs, revealMs] (1 at ask, 0 at\n * reveal), clamped outside. Drives the countdown ring/bar. Pure.\n */\nexport function countdownRemaining(quiz: Quiz, tMs: number): number {\n const ask = quiz.askMs ?? 0;\n const span = quiz.revealMs - ask;\n if (span <= 0) return 0;\n const elapsed = (tMs - ask) / span;\n return elapsed <= 0 ? 1 : elapsed >= 1 ? 0 : 1 - elapsed;\n}\n\n/**\n * Integer seconds remaining on the countdown (ceil), for the big number. Pure.\n */\nexport function countdownSeconds(quiz: Quiz, tMs: number): number {\n const ask = quiz.askMs ?? 0;\n const remMs = Math.max(0, quiz.revealMs - Math.max(ask, tMs));\n return Math.ceil(remMs / 1000);\n}\n\n/**\n * Reveal progress 0..1 across a short window after revealMs (for a pop/fade of\n * the correct option). `windowMs` default 350. Pure.\n */\nexport function revealProgress(quiz: Quiz, tMs: number, windowMs = 350): number {\n if (tMs < quiz.revealMs) return 0;\n if (windowMs <= 0) return 1;\n const p = (tMs - quiz.revealMs) / windowMs;\n return p >= 1 ? 1 : p;\n}\n\n/**\n * Validate a Quiz prop (used by the layer's validator + the pre-render gate).\n * Returns [] when valid, else human-readable errors. The card REQUIRES this prop;\n * it has no data otherwise.\n */\nexport function validateQuiz(quiz: unknown): string[] {\n if (quiz == null || typeof quiz !== 'object') return ['quiz must be an object'];\n const q = quiz as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof q.question !== 'string' || q.question.length === 0)\n errs.push('quiz.question must be a non-empty string');\n if (!Array.isArray(q.options) || q.options.length < 2 || q.options.length > 4)\n errs.push('quiz.options must be an array of 2–4 options');\n else {\n q.options.forEach((o, i) => {\n const opt = o as Record<string, unknown>;\n if (opt == null || typeof opt.text !== 'string' || opt.text.length === 0)\n errs.push(`quiz.options[${i}].text must be a non-empty string`);\n });\n }\n const nOpts = Array.isArray(q.options) ? q.options.length : 0;\n if (typeof q.correctIndex !== 'number' || !Number.isInteger(q.correctIndex) ||\n q.correctIndex < 0 || (nOpts > 0 && q.correctIndex >= nOpts))\n errs.push('quiz.correctIndex must be an integer index into options');\n if (typeof q.revealMs !== 'number') errs.push('quiz.revealMs must be a number (ms)');\n if (typeof q.endMs !== 'number') errs.push('quiz.endMs must be a number (ms)');\n if (typeof q.revealMs === 'number' && typeof q.endMs === 'number' && q.endMs <= q.revealMs)\n errs.push('quiz.endMs must be > revealMs');\n if (q.askMs != null && typeof q.askMs !== 'number') errs.push('quiz.askMs must be a number (ms)');\n if (typeof q.askMs === 'number' && typeof q.revealMs === 'number' && q.revealMs <= q.askMs)\n errs.push('quiz.revealMs must be > askMs');\n if (q.poll != null) {\n if (!Array.isArray(q.poll) || (nOpts > 0 && q.poll.length !== nOpts))\n errs.push('quiz.poll, if given, must be an array matching options length');\n else if (!q.poll.every((p) => typeof p === 'number' && p >= 0 && p <= 100))\n errs.push('quiz.poll entries must be numbers in [0,100]');\n }\n return errs;\n}\n","// `mcq-card` layer (S5) — a multiple-choice quiz/poll card: question + 2–4 options\n// + a countdown + a reveal of the correct one + an optional poll bar. The single\n// layer that unifies RET's quiz (\"pick the interval/chord\") with whozart's \"name\n// that piece\".\n//\n// DATA SOURCE (spec S5): the Score has no quiz; this layer is driven ENTIRELY by a\n// host-supplied `quiz` prop (validateQuiz REQUIRES it — the validator fast-fails\n// if it is missing/malformed). The layer does NOT fabricate a quiz.\n//\n// Screen-pinned (registered in SCREEN_PINNED_KEYS): like the other card layers it\n// draws in viewport space over the whole frame, anchored to the safe box. Pure\n// fn of t (phase/countdown/reveal all from quizCard.ts).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n quizPhase,\n countdownRemaining,\n countdownSeconds,\n revealProgress,\n validateQuiz,\n type Quiz,\n} from '../quizCard';\nimport { easeOut } from '../math';\n\nexport interface McqCardProps {\n /** REQUIRED: the host-supplied quiz (question/options/correctIndex/timings). */\n quiz: Quiz;\n /** Accent for the correct reveal (default theme.accent). */\n correctColor?: string;\n /** Show the poll bars (only if quiz.poll is present). Default true. */\n showPoll?: boolean;\n}\n\nconst CARD_RADIUS = 28;\n\nfunction roundRect(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n const rr = Math.min(r, w / 2, h / 2);\n const anyC = c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void };\n if (typeof anyC.roundRect === 'function') {\n c.beginPath();\n anyC.roundRect(x, y, w, h, rr);\n return;\n }\n c.beginPath();\n c.moveTo(x + rr, y);\n c.lineTo(x + w - rr, y);\n c.lineTo(x + w, y + rr);\n c.lineTo(x + w, y + h - rr);\n c.lineTo(x + w - rr, y + h);\n c.lineTo(x + rr, y + h);\n c.lineTo(x, y + h - rr);\n c.lineTo(x, y + rr);\n c.closePath();\n}\n\nfunction mcqCardLayer(): Layer<McqCardProps> {\n let quiz: Quiz = { question: '', options: [], correctIndex: 0, revealMs: 0, endMs: 0 };\n let correctColor: string | undefined;\n let showPoll = true;\n\n return {\n key: 'mcq-card',\n init(_ctx: RenderCtx, props) {\n quiz = props.quiz;\n correctColor = props.correctColor;\n showPoll = props.showPoll ?? true;\n },\n\n draw(ctx, tMs) {\n const phase = quizPhase(quiz, tMs);\n if (phase === 'before' || phase === 'after') return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = correctColor ?? ctx.theme.accent;\n const revealed = phase === 'reveal';\n const revP = easeOut(revealProgress(quiz, tMs));\n\n // Card geometry: a centred panel within the safe box.\n const cardW = sb.w;\n const n = quiz.options.length;\n const optH = Math.min(120, (sb.h * 0.5) / Math.max(1, n));\n const gap = optH * 0.22;\n const headH = optH * 1.3;\n const cardH = headH + n * (optH + gap);\n const cardX = sb.left;\n const cardY = sb.top + (sb.h - cardH) / 2;\n\n c.save();\n\n // Question header.\n c.fillStyle = ctx.theme.ink;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.font = `700 ${Math.round(headH * 0.42)}px ${ctx.theme.fontDisplay}`;\n c.fillText(quiz.question, cardX + cardW / 2, cardY + headH * 0.45);\n\n // Countdown (only during the question phase): a ring + seconds on the right.\n if (!revealed) {\n const rem = countdownRemaining(quiz, tMs);\n const secs = countdownSeconds(quiz, tMs);\n const cr = headH * 0.34;\n const ccx = cardX + cardW - cr - 8;\n const ccy = cardY + headH * 0.45;\n c.lineWidth = Math.max(3, cr * 0.16);\n c.strokeStyle = ctx.theme.sepia;\n c.globalAlpha = 0.3;\n c.beginPath();\n c.arc(ccx, ccy, cr, 0, Math.PI * 2);\n c.stroke();\n c.globalAlpha = 1;\n c.strokeStyle = accent;\n c.beginPath();\n c.arc(ccx, ccy, cr, -Math.PI / 2, -Math.PI / 2 + rem * Math.PI * 2);\n c.stroke();\n c.fillStyle = ctx.theme.ink;\n c.font = `700 ${Math.round(cr * 0.9)}px ${ctx.theme.fontBody}`;\n c.fillText(String(secs), ccx, ccy);\n }\n\n // Options.\n let oy = cardY + headH;\n quiz.options.forEach((opt, i) => {\n const isCorrect = i === quiz.correctIndex;\n const x = cardX;\n const y = oy;\n // Background pill. On reveal: correct glows accent; others dim.\n let bg = ctx.theme.paper;\n let alpha = 1;\n if (revealed) {\n if (isCorrect) bg = accent;\n else alpha = 1 - 0.55 * revP;\n }\n c.globalAlpha = alpha;\n c.fillStyle = bg;\n roundRect(c, x, y, cardW, optH, CARD_RADIUS);\n c.fill();\n // border\n c.globalAlpha = alpha;\n c.lineWidth = isCorrect && revealed ? 4 : 2;\n c.strokeStyle = isCorrect && revealed ? accent : ctx.theme.sepia;\n roundRect(c, x, y, cardW, optH, CARD_RADIUS);\n c.stroke();\n\n // Poll bar fill (if available + enabled): grows on reveal.\n if (showPoll && quiz.poll && quiz.poll[i] != null) {\n const pct = quiz.poll[i] / 100;\n c.globalAlpha = alpha * 0.25;\n c.fillStyle = isCorrect ? accent : ctx.theme.sepia;\n roundRect(c, x, y, cardW * pct * revP, optH, CARD_RADIUS);\n c.fill();\n }\n\n // Option label letter (A/B/C/D) + text.\n c.globalAlpha = alpha;\n const label = String.fromCharCode(65 + i);\n c.fillStyle = isCorrect && revealed ? ctx.theme.paper : ctx.theme.ink;\n c.textAlign = 'left';\n c.font = `700 ${Math.round(optH * 0.34)}px ${ctx.theme.fontBody}`;\n c.fillText(`${label}.`, x + optH * 0.4, y + optH / 2);\n c.fillText(opt.text, x + optH * 1.2, y + optH / 2);\n\n // Poll percent on the right when shown.\n if (showPoll && quiz.poll && quiz.poll[i] != null && revealed) {\n c.textAlign = 'right';\n c.font = `600 ${Math.round(optH * 0.3)}px ${ctx.theme.fontBody}`;\n c.fillText(`${Math.round(quiz.poll[i])}%`, x + cardW - optH * 0.4, y + optH / 2);\n }\n\n oy += optH + gap;\n });\n\n c.restore();\n },\n };\n}\n\nexport const mcqCardFactory: LayerFactory<McqCardProps> = {\n key: 'mcq-card',\n create: mcqCardLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['mcq-card: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n // quiz is REQUIRED (the layer has no data otherwise).\n errs.push(...validateQuiz(p.quiz).map((e) => `mcq-card.${e}`));\n if (p.correctColor != null && typeof p.correctColor !== 'string')\n errs.push('mcq-card.correctColor must be a string');\n if (p.showPoll != null && typeof p.showPoll !== 'boolean')\n errs.push('mcq-card.showPoll must be a boolean');\n return errs;\n },\n};\n","// Circle-of-fifths geometry (S5) — pure, canvas-free placement of the 12 keys\n// around a circle, plus which slice is the current key and the angular path to a\n// modulation target. The `circle-of-fifths` layer just strokes/fills what this\n// returns. Pure ⇒ exhaustively unit-testable.\n//\n// Ordering: clockwise by ascending fifths from C at the top (12 o'clock):\n// C, G, D, A, E, B, F#, Db, Ab, Eb, Bb, F. The pitch class of slot i is\n// (7*i) mod 12. We map a key string → its slot via its tonic pitch class.\n\nimport { parseKey } from './degreeLabels';\n\n/** Major-key labels around the circle, clockwise from the top. */\nexport const FIFTHS_MAJOR = ['C', 'G', 'D', 'A', 'E', 'B', 'F♯', 'D♭', 'A♭', 'E♭', 'B♭', 'F'];\n/** Relative-minor labels (inner ring), aligned to the same slots. */\nexport const FIFTHS_MINOR = ['a', 'e', 'b', 'f♯', 'c♯', 'g♯', 'd♯', 'b♭', 'f', 'c', 'g', 'd'];\n\n/** Pitch class at circle slot i (0..11), clockwise fifths from C. */\nexport function slotPc(i: number): number {\n return (7 * i) % 12;\n}\n\n/** The circle slot (0..11) for a tonic pitch class. Pure. */\nexport function pcToSlot(pc: number): number {\n const p = ((pc % 12) + 12) % 12;\n for (let i = 0; i < 12; i++) if (slotPc(i) === p) return i;\n return 0;\n}\n\n/** The circle slot for a key string (\"G major\" / \"Eb\"). Pure. */\nexport function keySlot(key: string | undefined): number {\n return pcToSlot(parseKey(key).tonicPc);\n}\n\n/** Angle (radians, 0 = up/12-o'clock, clockwise +) of circle slot i. Pure. */\nexport function slotAngle(i: number): number {\n return (i / 12) * Math.PI * 2;\n}\n\nexport interface CirclePoint {\n x: number;\n y: number;\n}\n\n/** Cartesian point for a slot on a circle of radius r centred at (cx,cy).\n * 0 rad = straight up, increasing clockwise. Pure. */\nexport function slotPoint(cx: number, cy: number, r: number, i: number): CirclePoint {\n const a = slotAngle(i) - Math.PI / 2; // canvas 0 rad points right; shift so 0=up\n return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) };\n}\n\n/**\n * Animated highlight slot as a fractional position from a source slot to a target\n * slot over progress `t01`, taking the SHORTEST way around the wheel. Returns a\n * fractional slot (e.g. 1.5 = between G and D). Pure.\n */\nexport function animatedSlot(fromSlot: number, toSlot: number, t01: number): number {\n let delta = toSlot - fromSlot;\n // shortest path around the 12-slot ring\n if (delta > 6) delta -= 12;\n if (delta < -6) delta += 12;\n const t = t01 < 0 ? 0 : t01 > 1 ? 1 : t01;\n const s = fromSlot + delta * t;\n return ((s % 12) + 12) % 12;\n}\n\n/** Cartesian point for a FRACTIONAL slot (e.g. mid-modulation) on the circle.\n * Pure. */\nexport function fracSlotPoint(cx: number, cy: number, r: number, frac: number): CirclePoint {\n const a = (frac / 12) * Math.PI * 2 - Math.PI / 2;\n return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) };\n}\n","// `circle-of-fifths` layer (S5) — a circle-of-fifths widget that highlights the\n// current key (from Score.key) with an optional animated sweep to a target key\n// (modulation). Pure geometry (circleOfFifths.ts) + draw; pure fn of t.\n//\n// DATA SOURCE: the current key comes from Score.key (or a `key` prop override).\n// A modulation is host-supplied via `toKey` + a [fromMs,toMs] window — the Score\n// carries no modulation map, so we do NOT infer it. With no toKey the widget just\n// pins the highlight on the current key.\n//\n// Screen-pinned-friendly but drawn in world space by default (it's a HUD widget,\n// not full-frame chrome); a host can place it anywhere via center/radius props.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n FIFTHS_MAJOR,\n FIFTHS_MINOR,\n keySlot,\n slotPoint,\n fracSlotPoint,\n animatedSlot,\n} from '../circleOfFifths';\nimport { easeInOut, invLerp } from '../math';\n\nexport interface CircleOfFifthsProps {\n /** Override the highlighted key (else Score.key, else C). */\n key?: string;\n /** Animate the highlight to this key (modulation target). Host-supplied. */\n toKey?: string;\n /** Modulation start time (ms). Required if toKey is set. */\n fromMs?: number;\n /** Modulation end time (ms). Required if toKey is set. */\n toMs?: number;\n /** Widget centre [x,y] (world). Default centred in the safe box. */\n center?: [number, number];\n /** Outer radius (world px). Default min(safe.w, safe.h) * 0.32. */\n radius?: number;\n /** Show the relative-minor inner ring. Default true. */\n showMinor?: boolean;\n /** Highlight colour (default theme.accent). */\n color?: string;\n}\n\nfunction circleOfFifthsLayer(): Layer<CircleOfFifthsProps> {\n let keyOverride: string | undefined;\n let toKey: string | undefined;\n let fromMs = 0;\n let toMs = 0;\n let centerProp: [number, number] | undefined;\n let radiusProp: number | undefined;\n let showMinor = true;\n let color: string | undefined;\n\n return {\n key: 'circle-of-fifths',\n init(_ctx: RenderCtx, props) {\n keyOverride = props.key;\n toKey = props.toKey;\n fromMs = props.fromMs ?? 0;\n toMs = props.toMs ?? 0;\n centerProp = props.center;\n radiusProp = props.radius;\n showMinor = props.showMinor ?? true;\n color = props.color;\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cx = centerProp?.[0] ?? sb.left + sb.w / 2;\n const cy = centerProp?.[1] ?? sb.top + sb.h / 2;\n const R = radiusProp ?? Math.min(sb.w, sb.h) * 0.32;\n const accent = color ?? ctx.theme.accent;\n const rMajor = R;\n const rMinor = R * 0.66;\n\n const fromSlot = keySlot(keyOverride ?? ctx.score?.key);\n // Animated highlight slot (fractional during a modulation).\n let hiSlot = fromSlot;\n if (toKey) {\n const t01 = easeInOut(invLerp(fromMs, toMs, tMs));\n hiSlot = animatedSlot(fromSlot, keySlot(toKey), t01);\n }\n\n c.save();\n\n // Outer + inner ring circles.\n c.lineWidth = 2;\n c.strokeStyle = ctx.theme.sepia;\n c.globalAlpha = 0.5;\n c.beginPath();\n c.arc(cx, cy, rMajor + R * 0.16, 0, Math.PI * 2);\n c.stroke();\n if (showMinor) {\n c.beginPath();\n c.arc(cx, cy, rMinor - R * 0.16, 0, Math.PI * 2);\n c.stroke();\n }\n c.globalAlpha = 1;\n\n // Highlight wedge behind the current key (a filled dot that rides the slot).\n const hp = fracSlotPoint(cx, cy, rMajor, hiSlot);\n c.fillStyle = accent;\n c.globalAlpha = 0.9;\n c.beginPath();\n c.arc(hp.x, hp.y, R * 0.2, 0, Math.PI * 2);\n c.fill();\n c.globalAlpha = 1;\n\n // Key labels (major outer, minor inner).\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n for (let i = 0; i < 12; i++) {\n const isHi = Math.round(hiSlot) % 12 === i;\n const pm = slotPoint(cx, cy, rMajor, i);\n c.fillStyle = isHi ? ctx.theme.paper : ctx.theme.ink;\n c.font = `700 ${Math.round(R * 0.16)}px ${ctx.theme.fontBody}`;\n c.fillText(FIFTHS_MAJOR[i], pm.x, pm.y);\n if (showMinor) {\n const pn = slotPoint(cx, cy, rMinor, i);\n c.fillStyle = ctx.theme.sepia;\n c.globalAlpha = isHi ? 1 : 0.8;\n c.font = `400 ${Math.round(R * 0.12)}px ${ctx.theme.fontBody}`;\n c.fillText(FIFTHS_MINOR[i], pn.x, pn.y);\n c.globalAlpha = 1;\n }\n }\n\n c.restore();\n },\n };\n}\n\nexport const circleOfFifthsFactory: LayerFactory<CircleOfFifthsProps> = {\n key: 'circle-of-fifths',\n create: circleOfFifthsLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['circle-of-fifths: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.key != null && typeof p.key !== 'string') errs.push('circle-of-fifths.key must be a string');\n if (p.toKey != null) {\n if (typeof p.toKey !== 'string') errs.push('circle-of-fifths.toKey must be a string');\n if (typeof p.fromMs !== 'number' || typeof p.toMs !== 'number')\n errs.push('circle-of-fifths.toKey requires numeric fromMs and toMs');\n else if (p.toMs <= p.fromMs) errs.push('circle-of-fifths.toMs must be > fromMs');\n }\n if (p.center != null && (!Array.isArray(p.center) || p.center.length !== 2 ||\n !p.center.every((n) => typeof n === 'number')))\n errs.push('circle-of-fifths.center must be [x,y]');\n if (p.radius != null && (typeof p.radius !== 'number' || p.radius <= 0))\n errs.push('circle-of-fifths.radius must be a positive number');\n if (p.showMinor != null && typeof p.showMinor !== 'boolean')\n errs.push('circle-of-fifths.showMinor must be a boolean');\n if (p.color != null && typeof p.color !== 'string')\n errs.push('circle-of-fifths.color must be a string');\n return errs;\n },\n};\n","// Pitch-contour geometry (S5) — the pure, canvas-free mapping from a Score's notes\n// to a polyline tracing melodic pitch over time (\"the shape of the melody\"). The\n// `pitch-contour` layer strokes the returned points and rides a moving dot at the\n// current note. Pure ⇒ unit-testable.\n//\n// Mapping: x = time→[plotLeft, plotRight] across [0, durationMs]; y = pitch→\n// [plotBottom, plotTop] across the score's [minPitch, maxPitch] (inverted so high\n// pitch = up). To read as a melodic CONTOUR we take, at each onset, the melody\n// note = the HIGHEST sounding pitch (a soprano-line heuristic; documented). Notes\n// are sorted by onset; ties between equal onsets keep the highest.\n\nimport type { ScoreNote } from './score';\nimport { clamp } from './math';\n\nexport interface ContourPoint {\n /** Onset time, ms. */\n tMs: number;\n /** MIDI pitch of the melody note at that onset. */\n pitchMidi: number;\n}\n\nexport interface ContourPlot {\n left: number;\n right: number;\n top: number;\n bottom: number;\n minPitch: number;\n maxPitch: number;\n durationMs: number;\n}\n\n/**\n * Reduce notes to one melody point per distinct onset = the highest sounding\n * pitch at that onset. Returns points sorted by time. Pure.\n */\nexport function contourPoints(notes: ScoreNote[]): ContourPoint[] {\n const byOnset = new Map<number, number>(); // onsetMs -> max pitch\n for (const n of notes) {\n const cur = byOnset.get(n.onsetMs);\n if (cur == null || n.pitchMidi > cur) byOnset.set(n.onsetMs, n.pitchMidi);\n }\n return [...byOnset.entries()]\n .map(([tMs, pitchMidi]) => ({ tMs, pitchMidi }))\n .sort((a, b) => a.tMs - b.tMs);\n}\n\n/** Pitch range [min,max] of the points (with a 1-semitone pad, min span 2). Pure. */\nexport function pitchRange(points: ContourPoint[]): { min: number; max: number } {\n if (!points.length) return { min: 60, max: 72 };\n let min = Infinity;\n let max = -Infinity;\n for (const p of points) {\n if (p.pitchMidi < min) min = p.pitchMidi;\n if (p.pitchMidi > max) max = p.pitchMidi;\n }\n if (max - min < 2) { min -= 1; max += 1; }\n return { min: min - 1, max: max + 1 };\n}\n\n/** Project a (time,pitch) onto plot pixels. y is inverted (high pitch = up). Pure. */\nexport function projectPoint(plot: ContourPlot, tMs: number, pitchMidi: number): { x: number; y: number } {\n const tx = plot.durationMs > 0 ? clamp(tMs / plot.durationMs, 0, 1) : 0;\n const py = plot.maxPitch > plot.minPitch\n ? clamp((pitchMidi - plot.minPitch) / (plot.maxPitch - plot.minPitch), 0, 1)\n : 0.5;\n return {\n x: plot.left + tx * (plot.right - plot.left),\n y: plot.bottom - py * (plot.bottom - plot.top),\n };\n}\n\n/** The full pixel polyline for the contour. Pure. */\nexport function contourPolyline(points: ContourPoint[], plot: ContourPlot): { x: number; y: number }[] {\n return points.map((p) => projectPoint(plot, p.tMs, p.pitchMidi));\n}\n\n/**\n * The melody pitch sounding at time `tMs`: the pitch of the most recent onset\n * at-or-before t (step interpolation — a melody holds until the next note). Returns\n * null before the first onset. Pure.\n */\nexport function pitchAt(points: ContourPoint[], tMs: number): number | null {\n if (!points.length || tMs < points[0].tMs) return null;\n let cur = points[0].pitchMidi;\n for (const p of points) {\n if (p.tMs <= tMs) cur = p.pitchMidi;\n else break;\n }\n return cur;\n}\n\n/** The moving-dot position at time t (rides the contour at the current pitch). Pure. */\nexport function dotAt(points: ContourPoint[], plot: ContourPlot, tMs: number): { x: number; y: number } | null {\n const pitch = pitchAt(points, tMs);\n if (pitch == null) return null;\n return projectPoint(plot, tMs, pitch);\n}\n","// `pitch-contour` layer (S5) — a continuous curve tracing the melody's pitch over\n// time from Score.notes (the \"shape of the melody\"), synced to the audio clock: a\n// moving dot rides the current note and the line \"draws on\" up to the playhead.\n//\n// DATA SOURCE: Score.notes only (no extra data). The melody line = the highest\n// sounding pitch at each onset (a soprano-line heuristic; see pitchContour.ts).\n//\n// Sync (pure fn of t): the dot's x = current time; the drawn portion of the line\n// reveals up to the playhead, the rest is a faint \"what's coming\" ghost. World\n// space; pin the plot to a band via props (default a band within the safe box).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n contourPoints,\n pitchRange,\n contourPolyline,\n dotAt,\n projectPoint,\n type ContourPoint,\n type ContourPlot,\n} from '../pitchContour';\n\nexport interface PitchContourProps {\n /** Plot band top (world y). Default safeBox.top + safeBox.h * 0.15. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h * 0.35. */\n height?: number;\n /** Line width (world px). Default 5. */\n width?: number;\n /** Line colour (default theme.accent). */\n color?: string;\n /** Show the moving dot at the current pitch. Default true. */\n dot?: boolean;\n /** Dot radius (world px). Default 14. */\n dotRadius?: number;\n}\n\nfunction pitchContourLayer(): Layer<PitchContourProps> {\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let width = 5;\n let color: string | undefined;\n let dot = true;\n let dotRadius = 14;\n let points: ContourPoint[] = [];\n\n return {\n key: 'pitch-contour',\n init(ctx, props) {\n topProp = props.top;\n heightProp = props.height;\n width = props.width ?? 5;\n color = props.color;\n dot = props.dot ?? true;\n dotRadius = props.dotRadius ?? 14;\n points = contourPoints(ctx.score?.notes ?? []);\n },\n\n draw(ctx, tMs) {\n if (points.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = color ?? ctx.theme.accent;\n const top = topProp ?? sb.top + sb.h * 0.15;\n const height = heightProp ?? sb.h * 0.35;\n const range = pitchRange(points);\n const plot: ContourPlot = {\n left: sb.left,\n right: sb.right,\n top,\n bottom: top + height,\n minPitch: range.min,\n maxPitch: range.max,\n durationMs: ctx.score?.durationMs ?? points[points.length - 1].tMs,\n };\n const poly = contourPolyline(points, plot);\n const playX = projectPoint(plot, tMs, range.min).x;\n\n c.save();\n c.lineJoin = 'round';\n c.lineCap = 'round';\n\n // Ghost (full line, faint) — \"what's coming\".\n c.globalAlpha = 0.22;\n c.strokeStyle = ctx.theme.sepia;\n c.lineWidth = width;\n c.beginPath();\n poly.forEach((p, i) => (i === 0 ? c.moveTo(p.x, p.y) : c.lineTo(p.x, p.y)));\n c.stroke();\n\n // Drawn-on portion up to the playhead (accent, solid). We include the segment\n // crossing the playhead by clipping each segment at playX.\n c.globalAlpha = 1;\n c.strokeStyle = accent;\n c.lineWidth = width;\n c.beginPath();\n let started = false;\n for (let i = 0; i < poly.length; i++) {\n const p = poly[i];\n if (p.x <= playX) {\n if (!started) { c.moveTo(p.x, p.y); started = true; }\n else c.lineTo(p.x, p.y);\n } else {\n // partial segment from previous point to the playhead\n if (i > 0) {\n const a = poly[i - 1];\n const f = (playX - a.x) / (p.x - a.x || 1);\n const y = a.y + (p.y - a.y) * f;\n if (!started) { c.moveTo(a.x, a.y); started = true; }\n c.lineTo(playX, y);\n }\n break;\n }\n }\n if (started) c.stroke();\n\n // Moving dot at the current pitch.\n if (dot) {\n const d = dotAt(points, plot, tMs);\n if (d && Number.isFinite(d.x) && Number.isFinite(d.y)) {\n c.globalAlpha = 1;\n c.fillStyle = accent;\n c.beginPath();\n c.arc(d.x, d.y, dotRadius, 0, Math.PI * 2);\n c.fill();\n c.globalAlpha = 0.3;\n c.beginPath();\n c.arc(d.x, d.y, dotRadius * 1.7, 0, Math.PI * 2);\n c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const pitchContourFactory: LayerFactory<PitchContourProps> = {\n key: 'pitch-contour',\n create: pitchContourLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['pitch-contour: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.top != null && typeof p.top !== 'number') errs.push('pitch-contour.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('pitch-contour.height must be a positive number');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0))\n errs.push('pitch-contour.width must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('pitch-contour.color must be a string');\n if (p.dot != null && typeof p.dot !== 'boolean') errs.push('pitch-contour.dot must be a boolean');\n if (p.dotRadius != null && (typeof p.dotRadius !== 'number' || p.dotRadius <= 0))\n errs.push('pitch-contour.dotRadius must be a positive number');\n return errs;\n },\n};\n","// Section-minimap geometry (S5) — pure, canvas-free placement of a thin piece\n// overview (\"you are here\") with optional section bands. The `section-minimap`\n// layer draws the bar + the position dot + labels. Pure ⇒ unit-testable.\n//\n// DATA SOURCE: section spans are host-supplied (Score carries no section analysis):\n// either an explicit `sections` prop ([{startMs,endMs,label}]) OR derived measure\n// spans (a uniform split, given a measure count + total duration). We do NOT infer\n// musical form. With neither, the minimap is a plain progress bar.\n\nimport { clamp } from './math';\n\nexport interface Section {\n /** Section start, ms. */\n startMs: number;\n /** Section end, ms. */\n endMs: number;\n /** Display label (\"Verse\", \"A\", \"Exposition\"). */\n label?: string;\n}\n\n/** Progress 0..1 along the clip at time t. Pure. */\nexport function progress01(durationMs: number, tMs: number): number {\n return durationMs > 0 ? clamp(tMs / durationMs, 0, 1) : 0;\n}\n\n/** x of a time on a [left, left+width] bar across [0, durationMs]. Pure. */\nexport function timeToX(left: number, width: number, durationMs: number, tMs: number): number {\n return left + progress01(durationMs, tMs) * width;\n}\n\n/** The section containing time t (first whose [start,end) holds t), or null. Pure. */\nexport function activeSection(sections: Section[], tMs: number): Section | null {\n for (const s of sections) if (tMs >= s.startMs && tMs < s.endMs) return s;\n return null;\n}\n\n/**\n * Build uniform measure-span sections from a measure count + total duration (a\n * fallback when no explicit sections are given but a measure count is). Each\n * section is one measure, labelled by 1-based measure number. Pure.\n */\nexport function measureSpans(measureCount: number, durationMs: number): Section[] {\n if (measureCount <= 0 || durationMs <= 0) return [];\n const w = durationMs / measureCount;\n const out: Section[] = [];\n for (let i = 0; i < measureCount; i++) {\n out.push({ startMs: i * w, endMs: (i + 1) * w, label: String(i + 1) });\n }\n return out;\n}\n\n/**\n * Validate a sections array (used by the layer validator). Returns [] when valid.\n */\nexport function validateSections(sections: unknown): string[] {\n if (!Array.isArray(sections)) return ['sections must be an array'];\n const errs: string[] = [];\n sections.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.startMs !== 'number' || typeof s?.endMs !== 'number')\n errs.push(`sections[${i}] needs numeric startMs/endMs`);\n else if (s.endMs <= s.startMs) errs.push(`sections[${i}].endMs must be > startMs`);\n if (s?.label != null && typeof s.label !== 'string')\n errs.push(`sections[${i}].label must be a string`);\n });\n return errs;\n}\n","// `section-minimap` layer (S5) — a thin piece-overview minimap with a \"you are\n// here\" position dot + optional section labels. Production/social helper for any\n// long clip (lessons, walkthroughs). Pure fn of t.\n//\n// DATA SOURCE: sections are host-supplied (`sections` prop) OR derived measure\n// spans (`measureCount` → uniform split via sectionMinimap.measureSpans). The\n// Score carries no form analysis, so we do NOT infer sections. With neither, the\n// minimap is a plain progress bar over Score.durationMs.\n//\n// Layout: a thin horizontal bar pinned near the bottom of the safe box; coloured\n// section bands; a dot at the current time; the active section's label above.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n timeToX,\n activeSection,\n measureSpans,\n validateSections,\n type Section,\n} from '../sectionMinimap';\n\nexport interface SectionMinimapProps {\n /** Host-supplied section spans. Takes precedence over measureCount. */\n sections?: Section[];\n /** Fallback: split the clip into this many uniform measure bands. */\n measureCount?: number;\n /** Bar baseline (world y). Default safeBox.bottom - 40. */\n barY?: number;\n /** Bar thickness (world px). Default 10. */\n barHeight?: number;\n /** Show the active section's label above the bar. Default true. */\n showLabel?: boolean;\n /** Accent for the played portion + dot (default theme.accent). */\n color?: string;\n}\n\nfunction sectionMinimapLayer(): Layer<SectionMinimapProps> {\n let sectionsProp: Section[] | undefined;\n let measureCount = 0;\n let barYProp: number | undefined;\n let barHeight = 10;\n let showLabel = true;\n let color: string | undefined;\n let sections: Section[] = [];\n\n return {\n key: 'section-minimap',\n init(ctx, props) {\n sectionsProp = props.sections;\n measureCount = props.measureCount ?? 0;\n barYProp = props.barY;\n barHeight = props.barHeight ?? 10;\n showLabel = props.showLabel ?? true;\n color = props.color;\n const dur = ctx.score?.durationMs ?? 0;\n sections = sectionsProp ?? (measureCount > 0 ? measureSpans(measureCount, dur) : []);\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const accent = color ?? ctx.theme.accent;\n const dur = ctx.score?.durationMs ?? 0;\n if (dur <= 0) return;\n const left = sb.left;\n const width = sb.w;\n const barY = barYProp ?? sb.bottom - 40;\n\n c.save();\n const round = barHeight / 2;\n\n // Base track.\n c.fillStyle = ctx.theme.sepia;\n c.globalAlpha = 0.3;\n drawBar(c, left, barY - round, width, barHeight, round);\n\n // Section bands (alternating tint) + dividers.\n if (sections.length) {\n c.globalAlpha = 0.5;\n sections.forEach((s, i) => {\n const x0 = timeToX(left, width, dur, s.startMs);\n const x1 = timeToX(left, width, dur, s.endMs);\n c.fillStyle = i % 2 === 0 ? ctx.theme.sepia : ctx.theme.ink;\n c.globalAlpha = i % 2 === 0 ? 0.18 : 0.1;\n c.fillRect(x0, barY - round, Math.max(0, x1 - x0), barHeight);\n // divider tick\n c.globalAlpha = 0.4;\n c.fillStyle = ctx.theme.ink;\n c.fillRect(x0 - 1, barY - round - 3, 2, barHeight + 6);\n });\n }\n\n // Played portion up to now.\n const px = timeToX(left, width, dur, tMs);\n c.globalAlpha = 0.85;\n c.fillStyle = accent;\n drawBar(c, left, barY - round, Math.max(0, px - left), barHeight, round);\n\n // \"You are here\" dot.\n c.globalAlpha = 1;\n c.fillStyle = accent;\n c.beginPath();\n c.arc(px, barY, barHeight * 1.6, 0, Math.PI * 2);\n c.fill();\n c.fillStyle = ctx.theme.paper;\n c.beginPath();\n c.arc(px, barY, barHeight * 0.7, 0, Math.PI * 2);\n c.fill();\n\n // Active section label above the bar.\n if (showLabel) {\n const act = activeSection(sections, tMs);\n if (act?.label) {\n c.globalAlpha = 1;\n c.fillStyle = ctx.theme.ink;\n c.textAlign = 'center';\n c.textBaseline = 'bottom';\n c.font = `700 ${Math.round(barHeight * 2.4)}px ${ctx.theme.fontBody}`;\n c.fillText(act.label, px, barY - barHeight * 2.2);\n }\n }\n c.restore();\n },\n };\n}\n\nfunction drawBar(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n const rr = Math.min(r, w / 2, h / 2);\n const anyC = c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void };\n if (typeof anyC.roundRect === 'function') {\n c.beginPath();\n anyC.roundRect(x, y, w, h, rr);\n c.fill();\n return;\n }\n c.fillRect(x, y, w, h);\n}\n\nexport const sectionMinimapFactory: LayerFactory<SectionMinimapProps> = {\n key: 'section-minimap',\n create: sectionMinimapLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['section-minimap: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.sections != null) errs.push(...validateSections(p.sections).map((e) => `section-minimap.${e}`));\n if (p.measureCount != null && (typeof p.measureCount !== 'number' || p.measureCount < 0 ||\n !Number.isInteger(p.measureCount)))\n errs.push('section-minimap.measureCount must be a non-negative integer');\n if (p.barY != null && typeof p.barY !== 'number') errs.push('section-minimap.barY must be a number');\n if (p.barHeight != null && (typeof p.barHeight !== 'number' || p.barHeight <= 0))\n errs.push('section-minimap.barHeight must be a positive number');\n if (p.showLabel != null && typeof p.showLabel !== 'boolean')\n errs.push('section-minimap.showLabel must be a boolean');\n if (p.color != null && typeof p.color !== 'string') errs.push('section-minimap.color must be a string');\n return errs;\n },\n};\n","// `beat-pulse` layer (S6) — an onset-reactive visual \"breath\": the frame blooms\n// and/or its edges glow on every note onset, then decays. The cheapest energy\n// you can add to a music clip and it costs no new data — the onset times come\n// straight from the Score (the same onsets the cursor/falling-notes already\n// use), so any musical scene can drop it in.\n//\n// DATA SOURCE: onset times. Default = distinct onsets of `ctx.score.notes`. A\n// host can override with an `onsetsMs` prop (e.g. a beat grid, or a downbeats-\n// only list for a calmer pulse). With neither, the layer draws nothing.\n//\n// ENVELOPE: pure fn of t. At time t the pulse value is the max over recent\n// onsets of an attack→decay envelope: it snaps to full brightness AT the onset\n// (default `attackMs` 0 = a crisp flash on the beat; raise it for a softer\n// swell) then decays exponentially over `decayMs`. No wall-clock / rAF —\n// deterministic for capture + golden frames.\n//\n// STYLES (composable): \"bloom\" (a soft radial flash from center) and \"vignette\"\n// (the frame edges glow). Capped by `intensity` so they read as a gentle\n// heartbeat, not a strobe. `blend` defaults to \"source-over\" (a tinted bloom in\n// the accent colour, which reads on LIGHT/paper backgrounds — most of our\n// promos); switch to \"lighter\" (additive) on DARK backgrounds. The layer is\n// screen-pinned (drawn after the camera reset) so a panning notation camera\n// doesn't drag the flash around.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { distinctOnsets } from '../notationGeometry';\n\nexport type PulseStyle = 'bloom' | 'vignette';\n\nexport interface BeatPulseProps {\n /** Override onset times (ms). Default = distinct onsets of ctx.score.notes. */\n onsetsMs?: number[];\n /** Which renderings to draw. Default [\"bloom\"]. */\n styles?: PulseStyle[];\n /** Max overlay alpha at a fresh onset. Default 0.42 (subtle). */\n intensity?: number;\n /** Attack ramp to full, ms. Default 0 (snap to full on the onset). */\n attackMs?: number;\n /** Exponential decay time constant, ms. Default 170. */\n decayMs?: number;\n /** Overlay colour. Default theme.accent. */\n color?: string;\n /**\n * Canvas blend for the overlay. Default \"source-over\" (a tinted bloom that\n * reads on LIGHT backgrounds — most of our promos are paper/cream). Use\n * \"lighter\" (additive glow) on DARK backgrounds where it would otherwise just\n * wash to white.\n */\n blend?: 'source-over' | 'lighter';\n}\n\nconst DEFAULTS = {\n styles: ['bloom'] as PulseStyle[],\n intensity: 0.42,\n attackMs: 0,\n decayMs: 170,\n blend: 'source-over' as const,\n};\n\n/** Envelope value (0..1) at t for one onset: fast linear attack then exp decay. */\nfunction envAt(tMs: number, onsetMs: number, attackMs: number, decayMs: number): number {\n const dt = tMs - onsetMs;\n if (dt < 0) return 0;\n if (dt < attackMs) return attackMs <= 0 ? 1 : dt / attackMs;\n return Math.exp(-(dt - attackMs) / decayMs);\n}\n\n/**\n * Pulse value at t = max envelope over the few most recent onsets. Pure. Exported\n * so other layers / tests can read the same heartbeat the visual draws.\n */\nexport function pulseAt(\n onsetsMs: number[], tMs: number, attackMs = DEFAULTS.attackMs, decayMs = DEFAULTS.decayMs,\n): number {\n let p = 0;\n // Onsets are sorted; only the last handful can contribute (decay is short).\n for (let i = onsetsMs.length - 1; i >= 0; i--) {\n const on = onsetsMs[i];\n if (on > tMs) continue;\n const e = envAt(tMs, on, attackMs, decayMs);\n if (e > p) p = e;\n // Older than ~6 decay constants → contributes <0.3%, stop.\n if (tMs - on > attackMs + decayMs * 6) break;\n }\n return p;\n}\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;\n return `rgba(${r},${g},${b},${a})`;\n}\n\nfunction beatPulseLayer(): Layer<BeatPulseProps> {\n let onsets: number[] = [];\n let styles = DEFAULTS.styles;\n let intensity = DEFAULTS.intensity;\n let attackMs = DEFAULTS.attackMs;\n let decayMs = DEFAULTS.decayMs;\n let color: string | undefined;\n let blend: 'source-over' | 'lighter' = DEFAULTS.blend;\n\n return {\n key: 'beat-pulse',\n init(ctx: RenderCtx, props) {\n onsets = props.onsetsMs ?? (ctx.score ? distinctOnsets(ctx.score.notes) : []);\n onsets = [...onsets].sort((a, b) => a - b);\n styles = props.styles ?? DEFAULTS.styles;\n intensity = props.intensity ?? DEFAULTS.intensity;\n attackMs = props.attackMs ?? DEFAULTS.attackMs;\n decayMs = props.decayMs ?? DEFAULTS.decayMs;\n color = props.color;\n blend = props.blend ?? DEFAULTS.blend;\n },\n draw(ctx, tMs) {\n if (!onsets.length) return;\n const p = pulseAt(onsets, tMs, attackMs, decayMs);\n if (p <= 0.004) return;\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const col = color ?? ctx.theme.accent;\n const a = intensity * p;\n c.save();\n c.globalCompositeOperation = blend;\n if (styles.includes('bloom')) {\n const cx = W / 2, cy = H * 0.46;\n const rad = Math.max(W, H) * (0.30 + 0.10 * p);\n const g = c.createRadialGradient(cx, cy, 0, cx, cy, rad);\n g.addColorStop(0, rgba(col, a * 0.55));\n g.addColorStop(0.55, rgba(col, a * 0.18));\n g.addColorStop(1, rgba(col, 0));\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n if (styles.includes('vignette')) {\n const cx = W / 2, cy = H / 2;\n const inner = Math.min(W, H) * 0.42;\n const outer = Math.max(W, H) * 0.72;\n const g = c.createRadialGradient(cx, cy, inner, cx, cy, outer);\n g.addColorStop(0, rgba(col, 0));\n g.addColorStop(1, rgba(col, a * 0.6));\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n c.restore();\n },\n };\n}\n\nexport const beatPulseFactory: LayerFactory<BeatPulseProps> = {\n key: 'beat-pulse',\n create: beatPulseLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['beat-pulse: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.onsetsMs != null && (!Array.isArray(p.onsetsMs) || p.onsetsMs.some((x) => typeof x !== 'number')))\n errs.push('beat-pulse.onsetsMs must be a number[]');\n if (p.styles != null) {\n if (!Array.isArray(p.styles)) errs.push('beat-pulse.styles must be an array');\n else if (p.styles.some((s) => s !== 'bloom' && s !== 'vignette'))\n errs.push('beat-pulse.styles items must be \"bloom\" | \"vignette\"');\n }\n if (p.intensity != null && (typeof p.intensity !== 'number' || p.intensity < 0 || p.intensity > 1))\n errs.push('beat-pulse.intensity must be in [0,1]');\n if (p.attackMs != null && (typeof p.attackMs !== 'number' || p.attackMs < 0))\n errs.push('beat-pulse.attackMs must be a number >= 0');\n if (p.decayMs != null && (typeof p.decayMs !== 'number' || p.decayMs <= 0))\n errs.push('beat-pulse.decayMs must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('beat-pulse.color must be a string');\n if (p.blend != null && p.blend !== 'source-over' && p.blend !== 'lighter')\n errs.push('beat-pulse.blend must be \"source-over\" | \"lighter\"');\n return errs;\n },\n};\n","// `chord-ribbon` layer (S6) — a horizontally-scrolling strip of chord chips with\n// a fixed \"now\" playhead, so the viewer SEES the harmony arrive in time with the\n// sound. Where `functional-harmony` colours the whole frame by the ACTIVE span,\n// this shows the SEQUENCE (past / now / next) sliding past a marker — the\n// teaching primitive that turns a promo into a mini harmonic-analysis reel.\n//\n// DATA SOURCE: the SAME `ChordSpan[]` the functional-harmony layer takes\n// (harmonyTrack.ts) — host-supplied (RET chords.json, an analysis step, …). Each\n// span carries [start,end), a tonal function, and a display `label` (roman\n// numeral and/or chord symbol, e.g. \"V7\" or \"ii–V\"). The layer does NOT infer\n// harmony from the notes; with no track it draws nothing.\n//\n// LAYOUT (pure fn of t): time → x by a fixed px-per-ms scale, with the current\n// time pinned at `playheadFrac` of the safe width. Chips left of the playhead are\n// already played (dimmed); the chip under the playhead is the active chord\n// (emphasised, tinted by function). Screen-pinned (drawn after the camera reset)\n// and clipped to a horizontal band so a panning notation camera never moves it.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport {\n functionColor,\n validateChordTrack,\n DEFAULT_FUNCTION_COLORS,\n type ChordSpan,\n type FunctionColors,\n} from '../harmonyTrack';\n\nexport interface ChordRibbonProps {\n /** REQUIRED: timed chord spans (function + display label). Host-supplied. */\n chordTrack: ChordSpan[];\n /** Horizontal time scale, px per ms. Default 0.06 (60 px/s). */\n pxPerMs?: number;\n /** Where \"now\" sits, as a fraction of the safe width. Default 0.32. */\n playheadFrac?: number;\n /** Chip baseline (world y / screen y, since pinned). Default safeBox.bottom - 260. */\n y?: number;\n /** Chip height (px). Default 84. */\n height?: number;\n /** Tint chips by tonal function. Default true. */\n showFunction?: boolean;\n /** Override the function→colour map. */\n colors?: FunctionColors;\n}\n\nconst DEFAULTS = { pxPerMs: 0.06, playheadFrac: 0.32, height: 84, showFunction: true };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction roundRectPath(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n if (typeof c.roundRect === 'function') { c.beginPath(); c.roundRect(x, y, w, h, r); return; }\n const rr = Math.min(r, w / 2, h / 2);\n c.beginPath();\n c.moveTo(x + rr, y);\n c.arcTo(x + w, y, x + w, y + h, rr);\n c.arcTo(x + w, y + h, x, y + h, rr);\n c.arcTo(x, y + h, x, y, rr);\n c.arcTo(x, y, x + w, y, rr);\n c.closePath();\n}\n\nfunction chordRibbonLayer(): Layer<ChordRibbonProps> {\n let track: ChordSpan[] = [];\n let pxPerMs = DEFAULTS.pxPerMs;\n let playheadFrac = DEFAULTS.playheadFrac;\n let yProp: number | undefined;\n let height = DEFAULTS.height;\n let showFunction = DEFAULTS.showFunction;\n let colors: FunctionColors = DEFAULT_FUNCTION_COLORS;\n\n return {\n key: 'chord-ribbon',\n init(_ctx: RenderCtx, props) {\n track = (props.chordTrack ?? []).slice().sort((a, b) => a.startMs - b.startMs);\n pxPerMs = props.pxPerMs ?? DEFAULTS.pxPerMs;\n playheadFrac = props.playheadFrac ?? DEFAULTS.playheadFrac;\n yProp = props.y;\n height = props.height ?? DEFAULTS.height;\n showFunction = props.showFunction ?? DEFAULTS.showFunction;\n colors = props.colors ?? DEFAULT_FUNCTION_COLORS;\n },\n draw(ctx, tMs) {\n if (!track.length) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const left = sb.left;\n const right = sb.right;\n const span = right - left;\n const playX = left + span * playheadFrac;\n const y = yProp ?? (sb.bottom - 260);\n const h = height;\n const pad = 8;\n\n c.save();\n // Clip to the ribbon band so chips never spill outside the safe width.\n c.beginPath();\n c.rect(left, y - pad, span, h + pad * 2);\n c.clip();\n\n for (const s of track) {\n const x0 = playX + (s.startMs - tMs) * pxPerMs;\n const w = Math.max(36, (s.endMs - s.startMs) * pxPerMs - 6);\n if (x0 + w < left || x0 > right) continue; // off-band\n const active = tMs >= s.startMs && tMs < s.endMs;\n const played = tMs >= s.endMs;\n const fnCol = showFunction ? functionColor(s.fn, colors) : ctx.theme.accent;\n const chipAlpha = active ? 0.95 : played ? 0.28 : 0.55;\n\n roundRectPath(c, x0, y, w, h, 14);\n c.fillStyle = rgba(fnCol, chipAlpha);\n c.fill();\n if (active) {\n c.lineWidth = 3;\n c.strokeStyle = rgba(ctx.theme.paper ?? '#ffffff', 0.9);\n c.stroke();\n }\n\n const label = s.label ?? '';\n if (label) {\n c.fillStyle = rgba(ctx.theme.paper ?? '#ffffff', active ? 1 : 0.85);\n c.font = `${active ? 700 : 600} ${Math.round(h * 0.42)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(label, x0 + w / 2, y + h / 2);\n }\n }\n\n // The fixed \"now\" playhead tick.\n c.strokeStyle = rgba(ctx.theme.gold ?? '#d4af37', 0.95);\n c.lineWidth = 3;\n c.beginPath();\n c.moveTo(playX, y - pad);\n c.lineTo(playX, y + h + pad);\n c.stroke();\n c.restore();\n },\n };\n}\n\nexport const chordRibbonFactory: LayerFactory<ChordRibbonProps> = {\n key: 'chord-ribbon',\n create: chordRibbonLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['chord-ribbon: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs = validateChordTrack(p.chordTrack);\n if (p.pxPerMs != null && (typeof p.pxPerMs !== 'number' || p.pxPerMs <= 0))\n errs.push('chord-ribbon.pxPerMs must be a positive number');\n if (p.playheadFrac != null && (typeof p.playheadFrac !== 'number' || p.playheadFrac < 0 || p.playheadFrac > 1))\n errs.push('chord-ribbon.playheadFrac must be in [0,1]');\n if (p.y != null && typeof p.y !== 'number') errs.push('chord-ribbon.y must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('chord-ribbon.height must be a positive number');\n if (p.showFunction != null && typeof p.showFunction !== 'boolean')\n errs.push('chord-ribbon.showFunction must be a boolean');\n return errs;\n },\n};\n","// `progress-ring` layer (S6) — a small circular progress indicator that fills as\n// the clip plays, with an optional seconds-remaining countdown in the middle. A\n// retention primitive: a visible \"how much is left\" cue measurably keeps viewers\n// to the end of a short. Where `section-minimap` is a bottom progress BAR, this\n// is a compact corner RING — use whichever fits the layout.\n//\n// DATA SOURCE: total length. Default = ctx.score.durationMs; override with\n// `totalMs` (e.g. the full clip duration when there's no score, or to count the\n// excerpt rather than the trailing CTA). Progress = clamp(t / total, 0, 1).\n//\n// Screen-pinned chrome (drawn after the camera reset) so it stays put in the\n// corner regardless of any notation camera pan/zoom. Pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface ProgressRingProps {\n /** Total length, ms. Default ctx.score.durationMs. Required if no score. */\n totalMs?: number;\n /** Ring radius (px). Default 46. */\n radius?: number;\n /** Ring stroke thickness (px). Default 9. */\n thickness?: number;\n /** Centre x (px). Default safeBox.right - radius - 16. */\n cx?: number;\n /** Centre y (px). Default safeBox.top + radius + 16. */\n cy?: number;\n /** Filled-arc colour. Default theme.gold. */\n color?: string;\n /** Unfilled track colour. Default a faint paper tint. */\n trackColor?: string;\n /** Draw seconds-remaining in the centre. Default false. */\n showCountdown?: boolean;\n}\n\nconst DEFAULTS = { radius: 46, thickness: 9, showCountdown: false };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((c) => c + c).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction progressRingLayer(): Layer<ProgressRingProps> {\n let totalMsProp: number | undefined;\n let radius = DEFAULTS.radius;\n let thickness = DEFAULTS.thickness;\n let cxProp: number | undefined;\n let cyProp: number | undefined;\n let color: string | undefined;\n let trackColor: string | undefined;\n let showCountdown = DEFAULTS.showCountdown;\n\n return {\n key: 'progress-ring',\n init(ctx: RenderCtx, props) {\n totalMsProp = props.totalMs ?? ctx.score?.durationMs;\n radius = props.radius ?? DEFAULTS.radius;\n thickness = props.thickness ?? DEFAULTS.thickness;\n cxProp = props.cx;\n cyProp = props.cy;\n color = props.color;\n trackColor = props.trackColor;\n showCountdown = props.showCountdown ?? DEFAULTS.showCountdown;\n },\n draw(ctx, tMs) {\n const total = totalMsProp;\n if (!total || total <= 0) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cx = cxProp ?? (sb.right - radius - 16);\n const cy = cyProp ?? (sb.top + radius + 16);\n const p = Math.min(1, Math.max(0, tMs / total));\n const start = -Math.PI / 2;\n const end = start + p * Math.PI * 2;\n const fill = color ?? ctx.theme.gold;\n const track = trackColor ?? rgba(ctx.theme.paper, 0.22);\n\n c.save();\n c.lineCap = 'round';\n // Track.\n c.beginPath();\n c.arc(cx, cy, radius, 0, Math.PI * 2);\n c.strokeStyle = track;\n c.lineWidth = thickness;\n c.stroke();\n // Filled arc.\n if (p > 0) {\n c.beginPath();\n c.arc(cx, cy, radius, start, end);\n c.strokeStyle = fill;\n c.lineWidth = thickness;\n c.stroke();\n }\n // Countdown text.\n if (showCountdown) {\n const remain = Math.max(0, Math.ceil((total - tMs) / 1000));\n c.fillStyle = ctx.theme.paper;\n c.font = `700 ${Math.round(radius * 0.7)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(String(remain), cx, cy + 1);\n }\n c.restore();\n },\n };\n}\n\nexport const progressRingFactory: LayerFactory<ProgressRingProps> = {\n key: 'progress-ring',\n create: progressRingLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['progress-ring: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.totalMs != null && (typeof p.totalMs !== 'number' || p.totalMs <= 0))\n errs.push('progress-ring.totalMs must be a positive number');\n if (p.radius != null && (typeof p.radius !== 'number' || p.radius <= 0))\n errs.push('progress-ring.radius must be a positive number');\n if (p.thickness != null && (typeof p.thickness !== 'number' || p.thickness <= 0))\n errs.push('progress-ring.thickness must be a positive number');\n if (p.cx != null && typeof p.cx !== 'number') errs.push('progress-ring.cx must be a number');\n if (p.cy != null && typeof p.cy !== 'number') errs.push('progress-ring.cy must be a number');\n if (p.color != null && typeof p.color !== 'string') errs.push('progress-ring.color must be a string');\n if (p.trackColor != null && typeof p.trackColor !== 'string') errs.push('progress-ring.trackColor must be a string');\n if (p.showCountdown != null && typeof p.showCountdown !== 'boolean')\n errs.push('progress-ring.showCountdown must be a boolean');\n return errs;\n },\n};\n","// `radial-spectrum` layer (S6) — the audio-reactive frequency bars wrapped into a\n// RING radiating from a center point, instead of the flat mirrored strip the\n// `spectrum` layer draws. Made for a portrait/album-art hero: the bars pulse\n// outward around the composer photo. Same level-source contract as `spectrum`\n// (host wires ctx.spectrum.byteFreq → AnalyserNode; deterministic synthetic\n// fallback otherwise), so it's a drop-in alternative visual.\n//\n// Pure fn of t (the synthetic fallback is a pure fn of t; with a live analyser\n// the captured frames follow the master bus exactly, like `spectrum`).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\n// ctx.spectrum (SpectrumInput) is augmented onto RenderCtx by spectrum.ts, which\n// is always loaded (registry imports it). We re-use that same provider here.\n\nconst SPEC_BARS = 64;\n\nexport interface RadialSpectrumProps {\n /** Number of bars around the ring. Default 64. */\n bars?: number;\n /** Center x as a fraction of W. Default 0.5. */\n cxFrac?: number;\n /** Center y as a fraction of H. Default 0.42. */\n cyFrac?: number;\n /** Inner radius (bar roots) as a fraction of min(W,H). Default 0.20. */\n innerFrac?: number;\n /** Max bar length as a fraction of min(W,H). Default 0.13. */\n maxLenFrac?: number;\n /** Bar fill at low magnitude. Default theme.accent. */\n colorLow?: string;\n /** Bar fill at high magnitude. Default theme.gold. */\n colorHigh?: string;\n /** Per-frame level provider (overrides ctx.spectrum). Pure fn of t for tests. */\n levelsFn?: (tMs: number, bands: number) => Float32Array | number[] | null;\n}\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n// rgb-space lerp between two #rrggbb colours (mirrors spectrum.ts lerpHex).\nfunction lerpHex(a: string, b: string, f: number): string {\n const pa = parseInt(a.slice(1), 16);\n const pb = parseInt(b.slice(1), 16);\n const r = Math.round(((pa >> 16) & 255) + (((pb >> 16) & 255) - ((pa >> 16) & 255)) * f);\n const g = Math.round(((pa >> 8) & 255) + (((pb >> 8) & 255) - ((pa >> 8) & 255)) * f);\n const bl = Math.round((pa & 255) + ((pb & 255) - (pa & 255)) * f);\n return `rgb(${r},${g},${bl})`;\n}\n\n// Deterministic synthetic magnitudes (mirrors spectrum.ts; pure fn of (tSec,band)).\nfunction syntheticMagnitude(tSec: number, b: number, n: number): number {\n const phase = tSec * 5 + b * 0.45;\n let m = 0.22 + 0.16 * Math.sin(phase) + 0.10 * Math.sin(phase * 1.7 + 1.2);\n m *= 0.5 + 0.5 * Math.sin((b / (n - 1)) * Math.PI);\n return m;\n}\n\n// Log-bin a byte-FFT into n normalized magnitudes (mirrors spectrum.ts).\nfunction binByteFreq(freq: Uint8Array, n: number): number[] | null {\n let peak = 0;\n for (let i = 0; i < freq.length; i++) if (freq[i] > peak) peak = freq[i];\n if (peak <= 4) return null;\n const lo = 2, hi = 440;\n const out: number[] = [];\n for (let b = 0; b < n; b++) {\n const f0 = lo * Math.pow(hi / lo, b / n);\n const f1 = lo * Math.pow(hi / lo, (b + 1) / n);\n const i0 = Math.floor(f0);\n const i1 = Math.max(i0 + 1, Math.floor(f1));\n let sum = 0, cnt = 0;\n for (let i = i0; i < i1 && i < freq.length; i++) { sum += freq[i]; cnt++; }\n let m = cnt ? (sum / cnt) / 255 : 0;\n m = Math.pow(m, 0.78);\n out.push(m);\n }\n return out;\n}\n\nfunction radialSpectrumLayer(): Layer<RadialSpectrumProps> {\n let n = SPEC_BARS;\n let cxFrac = 0.5, cyFrac = 0.42, innerFrac = 0.20, maxLenFrac = 0.13;\n let colorLow: string | undefined, colorHigh: string | undefined;\n let levelsFn: RadialSpectrumProps['levelsFn'];\n\n function magnitudesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = levelsFn?.(tMs, n);\n const src = fromProp ?? resolveFromCtx(ctx, tMs);\n if (src && src.length) {\n const out = new Array(n);\n for (let b = 0; b < n; b++) out[b] = clamp01(src[Math.min(src.length - 1, b)] ?? 0);\n return out;\n }\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, b) => clamp01(syntheticMagnitude(tSec, b, n)));\n }\n\n function resolveFromCtx(ctx: RenderCtx, tMs: number): number[] | null {\n const sp = ctx.spectrum;\n if (!sp) return null;\n const lv = sp.levels?.(tMs, n);\n if (lv && lv.length) return Array.from(lv as ArrayLike<number>);\n const bf = sp.byteFreq?.(tMs);\n if (bf && bf.length) return binByteFreq(bf, n);\n return null;\n }\n\n return {\n key: 'radial-spectrum',\n init(_ctx: RenderCtx, props) {\n n = props.bars ?? SPEC_BARS;\n cxFrac = props.cxFrac ?? 0.5;\n cyFrac = props.cyFrac ?? 0.42;\n innerFrac = props.innerFrac ?? 0.20;\n maxLenFrac = props.maxLenFrac ?? 0.13;\n colorLow = props.colorLow;\n colorHigh = props.colorHigh;\n levelsFn = props.levelsFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const cx = W * cxFrac, cy = H * cyFrac;\n const unit = Math.min(W, H);\n const inner = unit * innerFrac;\n const maxLen = unit * maxLenFrac;\n const lo = colorLow ?? ctx.theme.accent;\n const hi = colorHigh ?? ctx.theme.gold;\n const mags = magnitudesAt(ctx, tMs);\n const barW = Math.max(3, (2 * Math.PI * inner) / n * 0.6);\n\n c.save();\n c.lineCap = 'round';\n for (let b = 0; b < n; b++) {\n const m = mags[b];\n const ang = (b / n) * Math.PI * 2 - Math.PI / 2;\n const len = Math.max(barW * 0.5, m * maxLen);\n const x0 = cx + Math.cos(ang) * inner;\n const y0 = cy + Math.sin(ang) * inner;\n const x1 = cx + Math.cos(ang) * (inner + len);\n const y1 = cy + Math.sin(ang) * (inner + len);\n c.strokeStyle = lerpHex(lo, hi, m);\n c.lineWidth = barW;\n c.beginPath();\n c.moveTo(x0, y0);\n c.lineTo(x1, y1);\n c.stroke();\n }\n c.restore();\n },\n };\n}\n\nexport const radialSpectrumFactory: LayerFactory<RadialSpectrumProps> = {\n key: 'radial-spectrum',\n create: radialSpectrumLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['radial-spectrum: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.bars != null && (typeof p.bars !== 'number' || p.bars < 6)) errs.push('radial-spectrum.bars must be a number >= 6');\n for (const k of ['cxFrac', 'cyFrac', 'innerFrac', 'maxLenFrac'] as const) {\n if (p[k] != null && (typeof p[k] !== 'number' || (p[k] as number) < 0 || (p[k] as number) > 1))\n errs.push(`radial-spectrum.${k} must be in [0,1]`);\n }\n if (p.colorLow != null && typeof p.colorLow !== 'string') errs.push('radial-spectrum.colorLow must be a string');\n if (p.colorHigh != null && typeof p.colorHigh !== 'string') errs.push('radial-spectrum.colorHigh must be a string');\n if (p.levelsFn != null && typeof p.levelsFn !== 'function') errs.push('radial-spectrum.levelsFn must be a function');\n return errs;\n },\n};\n","// `karaoke-caption` layer (S6) — a caption that fills word-by-word in time, the\n// bouncing-ball / lyric-video effect. Where the `caption` layer shows a whole\n// cue at once, this lights each word as it's \"spoken\" so the eye tracks the\n// reading — strong for narrated fun-facts, hooks, and lyric reels (and it keeps\n// viewers reading = watching).\n//\n// DATA: a list of timed words (`words: {text, atMs}[]`); each word turns\n// \"active\" at its atMs and stays lit. Host supplies the timings (TTS word marks,\n// an even split across a narration's duration, or hand-authored). The layer\n// wraps the words to the safe width and centres the block; pure fn of t.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface KaraokeWord {\n /** The word (no surrounding spaces). */\n text: string;\n /** Time this word lights up, ms. */\n atMs: number;\n}\n\nexport interface KaraokeCaptionProps {\n /** REQUIRED: timed words, ascending by atMs. */\n words: KaraokeWord[];\n /** Vertical center as a fraction of H. Default 0.5. */\n centerFrac?: number;\n /** Font px. Default 64. */\n fontPx?: number;\n /** Colour of already-sung words. Default theme.ink. */\n sungColor?: string;\n /** Colour of not-yet-sung words. Default a faint ink. */\n upcomingColor?: string;\n /** Highlight colour of the word that just lit. Default theme.accent. */\n activeColor?: string;\n /** ms a freshly-lit word stays in the active highlight colour. Default 320. */\n activeHoldMs?: number;\n}\n\nconst DEFAULTS = { centerFrac: 0.5, fontPx: 64, activeHoldMs: 320 };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3\n ? parseInt(h.split('').map((ch) => ch + ch).join(''), 16)\n : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction karaokeCaptionLayer(): Layer<KaraokeCaptionProps> {\n let words: KaraokeWord[] = [];\n let centerFrac = DEFAULTS.centerFrac;\n let fontPx = DEFAULTS.fontPx;\n let sungColor: string | undefined;\n let upcomingColor: string | undefined;\n let activeColor: string | undefined;\n let activeHoldMs = DEFAULTS.activeHoldMs;\n\n return {\n key: 'karaoke-caption',\n init(_ctx: RenderCtx, props) {\n words = (props.words ?? []).slice().sort((a, b) => a.atMs - b.atMs);\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n sungColor = props.sungColor;\n upcomingColor = props.upcomingColor;\n activeColor = props.activeColor;\n activeHoldMs = props.activeHoldMs ?? DEFAULTS.activeHoldMs;\n },\n draw(ctx, tMs) {\n if (!words.length) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n c.save();\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textBaseline = 'middle';\n c.textAlign = 'left';\n\n const spaceW = c.measureText(' ').width;\n const maxW = sb.w;\n // Lay out words into lines (greedy wrap to the safe width).\n const lines: { items: { w: KaraokeWord; width: number }[]; width: number }[] = [];\n let cur: { items: { w: KaraokeWord; width: number }[]; width: number } = { items: [], width: 0 };\n for (const w of words) {\n const ww = c.measureText(w.text).width;\n const add = cur.items.length ? spaceW + ww : ww;\n if (cur.items.length && cur.width + add > maxW) {\n lines.push(cur);\n cur = { items: [], width: 0 };\n }\n cur.items.push({ w, width: ww });\n cur.width += cur.items.length === 1 ? ww : spaceW + ww;\n }\n if (cur.items.length) lines.push(cur);\n\n const lineH = fontPx * 1.3;\n const totalH = lines.length * lineH;\n let y = H_center(ctx, centerFrac) - totalH / 2 + lineH / 2;\n\n const sung = sungColor ?? ctx.theme.ink;\n const upcoming = upcomingColor ?? rgba(ctx.theme.ink, 0.28);\n const active = activeColor ?? ctx.theme.accent;\n\n for (const line of lines) {\n let x = sb.left + (maxW - line.width) / 2; // center the line\n for (const it of line.items) {\n const lit = tMs >= it.w.atMs;\n const fresh = lit && tMs - it.w.atMs < activeHoldMs;\n c.fillStyle = fresh ? active : lit ? sung : upcoming;\n c.fillText(it.w.text, x, y);\n x += it.width + spaceW;\n }\n y += lineH;\n }\n c.restore();\n },\n };\n}\n\nfunction H_center(ctx: RenderCtx, frac: number): number { return ctx.H * frac; }\n\nexport const karaokeCaptionFactory: LayerFactory<KaraokeCaptionProps> = {\n key: 'karaoke-caption',\n create: karaokeCaptionLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['karaoke-caption: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (!Array.isArray(p.words)) {\n errs.push('karaoke-caption.words must be an array of {text, atMs}');\n } else {\n p.words.forEach((raw, i) => {\n const w = raw as Record<string, unknown>;\n if (typeof w?.text !== 'string' || typeof w?.atMs !== 'number')\n errs.push(`karaoke-caption.words[${i}] must be {text:string, atMs:number}`);\n });\n }\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1))\n errs.push('karaoke-caption.centerFrac must be in [0,1]');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0))\n errs.push('karaoke-caption.fontPx must be a positive number');\n if (p.activeHoldMs != null && (typeof p.activeHoldMs !== 'number' || p.activeHoldMs < 0))\n errs.push('karaoke-caption.activeHoldMs must be a number >= 0');\n for (const k of ['sungColor', 'upcomingColor', 'activeColor'] as const) {\n if (p[k] != null && typeof p[k] !== 'string') errs.push(`karaoke-caption.${k} must be a string`);\n }\n return errs;\n },\n};\n","// `interval-arcs` layer (S6) — draws a labeled arc between each pair of\n// consecutive melody notes (e.g. \"P5\", \"M3\", \"m2\"), revealing each arc as the\n// second note sounds. The ear-training primitive: it names the melodic intervals\n// the listener is hearing, in time. Pairs naturally with RealEarTrainer content.\n//\n// DATA SOURCE: Score.notes only — the melody = highest sounding pitch per onset\n// (the same soprano-line heuristic as `pitch-contour`, reused via contourPoints).\n// Interval names come from web-core's intervals table (intervalBySemitones).\n//\n// Sync (pure fn of t): an arc for the pair (i-1, i) is drawn once note i has\n// sounded (its onset ≤ t); earlier arcs stay. Laid out left→right by onset time,\n// pitch→y within a band (same projection as pitch-contour).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport {\n contourPoints,\n pitchRange,\n projectPoint,\n type ContourPoint,\n type ContourPlot,\n} from '../pitchContour';\nimport { intervalBySemitones } from '../../intervals';\n\nexport interface IntervalArcsProps {\n /** Plot band top (world y). Default safeBox.top + safeBox.h * 0.18. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h * 0.34. */\n height?: number;\n /** Arc colour for an ascending interval. Default theme.accent. */\n upColor?: string;\n /** Arc colour for a descending interval. Default theme.sepia. */\n downColor?: string;\n /** Draw the interval label on each arc. Default true. */\n showLabels?: boolean;\n /** Label font px. Default 30. */\n fontPx?: number;\n /** Note-dot radius (world px). Default 9. */\n dotRadius?: number;\n}\n\n/** Interval label for a signed semitone delta (reduces compound to within-octave + P8). */\nfunction intervalLabel(deltaSemis: number): string {\n const semi = Math.abs(deltaSemis);\n const within = semi % 12;\n if (semi > 0 && within === 0) return 'P8';\n return intervalBySemitones(within)?.label ?? `${within}`;\n}\n\nfunction intervalArcsLayer(): Layer<IntervalArcsProps> {\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let upColor: string | undefined;\n let downColor: string | undefined;\n let showLabels = true;\n let fontPx = 30;\n let dotRadius = 9;\n let points: ContourPoint[] = [];\n\n return {\n key: 'interval-arcs',\n init(ctx, props) {\n topProp = props.top;\n heightProp = props.height;\n upColor = props.upColor;\n downColor = props.downColor;\n showLabels = props.showLabels ?? true;\n fontPx = props.fontPx ?? 30;\n dotRadius = props.dotRadius ?? 9;\n points = contourPoints(ctx.score?.notes ?? []);\n },\n draw(ctx, tMs) {\n if (points.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const top = topProp ?? sb.top + sb.h * 0.18;\n const height = heightProp ?? sb.h * 0.34;\n const range = pitchRange(points);\n const plot: ContourPlot = {\n left: sb.left,\n right: sb.right,\n top,\n bottom: top + height,\n minPitch: range.min,\n maxPitch: range.max,\n durationMs: ctx.score?.durationMs ?? points[points.length - 1].tMs,\n };\n const up = upColor ?? ctx.theme.accent;\n const down = downColor ?? ctx.theme.sepia;\n\n c.save();\n c.lineCap = 'round';\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'bottom';\n\n // Arcs for each consecutive pair whose 2nd note has sounded.\n for (let i = 1; i < points.length; i++) {\n const b = points[i];\n if (b.tMs > tMs) break; // not yet revealed (points are onset-sorted)\n const a = points[i - 1];\n const pa = projectPoint(plot, a.tMs, a.pitchMidi);\n const pb = projectPoint(plot, b.tMs, b.pitchMidi);\n const delta = b.pitchMidi - a.pitchMidi;\n const ascending = delta >= 0;\n const col = ascending ? up : down;\n // Quadratic arc bowing AWAY from the band center (up for ascending).\n const midX = (pa.x + pb.x) / 2;\n const bow = Math.min(70, Math.max(28, Math.abs(pb.x - pa.x) * 0.4));\n const ctrlY = Math.min(pa.y, pb.y) - bow;\n\n c.strokeStyle = col;\n c.lineWidth = 4;\n c.globalAlpha = 0.9;\n c.beginPath();\n c.moveTo(pa.x, pa.y);\n c.quadraticCurveTo(midX, ctrlY, pb.x, pb.y);\n c.stroke();\n\n if (showLabels && delta !== 0) {\n c.globalAlpha = 1;\n c.fillStyle = col;\n c.fillText(intervalLabel(delta), midX, ctrlY - 2);\n }\n }\n\n // Note dots (only those sounded).\n c.globalAlpha = 1;\n c.fillStyle = ctx.theme.ink;\n for (const p of points) {\n if (p.tMs > tMs) break;\n const pt = projectPoint(plot, p.tMs, p.pitchMidi);\n c.beginPath();\n c.arc(pt.x, pt.y, dotRadius, 0, Math.PI * 2);\n c.fill();\n }\n c.restore();\n },\n };\n}\n\nexport const intervalArcsFactory: LayerFactory<IntervalArcsProps> = {\n key: 'interval-arcs',\n create: intervalArcsLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['interval-arcs: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.top != null && typeof p.top !== 'number') errs.push('interval-arcs.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0))\n errs.push('interval-arcs.height must be a positive number');\n if (p.upColor != null && typeof p.upColor !== 'string') errs.push('interval-arcs.upColor must be a string');\n if (p.downColor != null && typeof p.downColor !== 'string') errs.push('interval-arcs.downColor must be a string');\n if (p.showLabels != null && typeof p.showLabels !== 'boolean') errs.push('interval-arcs.showLabels must be a boolean');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('interval-arcs.fontPx must be a positive number');\n if (p.dotRadius != null && (typeof p.dotRadius !== 'number' || p.dotRadius <= 0)) errs.push('interval-arcs.dotRadius must be a positive number');\n return errs;\n },\n};\n","// `kinetic-text` layer (S6) — an animated text intro: the words/characters\n// arrive over time instead of all at once. The first ~2s of a short decide\n// whether anyone watches; kinetic typography on the hook is the strongest\n// retention lever we have, and our hook cards are currently static.\n//\n// MODES:\n// - \"typewriter\": characters reveal left→right across `revealMs`, optional\n// blinking cursor.\n// - \"word-pop\": each word fades + scales in, staggered evenly across `revealMs`.\n//\n// DATA: a `text` string (may contain \"\\n\" for explicit line breaks); otherwise\n// it wraps to the safe width. Pure fn of t (cursor blink is a pure fn of t too).\n// Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport type KineticMode = 'typewriter' | 'word-pop';\n\nexport interface KineticTextProps {\n /** REQUIRED: the text. \"\\n\" forces a line break; otherwise wraps to safe width. */\n text: string;\n /** Animation style. Default \"word-pop\". */\n mode?: KineticMode;\n /** When the animation starts (ms). Default 0. */\n startMs?: number;\n /** Time to fully reveal (ms). Default 1400. */\n revealMs?: number;\n /** Font px. Default 88. */\n fontPx?: number;\n /** Text colour. Default theme.ink. */\n color?: string;\n /** Vertical center as a fraction of H. Default 0.42. */\n centerFrac?: number;\n /** Typewriter blinking cursor. Default true (ignored in word-pop). */\n cursor?: boolean;\n}\n\nconst DEFAULTS = { mode: 'word-pop' as KineticMode, startMs: 0, revealMs: 1400, fontPx: 88, centerFrac: 0.42, cursor: true };\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n/** Greedy-wrap a list of words to maxW using the current ctx font. */\nfunction wrap(c: CanvasRenderingContext2D, words: string[], maxW: number): string[][] {\n const lines: string[][] = [];\n let cur: string[] = [];\n let curW = 0;\n const spaceW = c.measureText(' ').width;\n for (const w of words) {\n const ww = c.measureText(w).width;\n const add = cur.length ? spaceW + ww : ww;\n if (cur.length && curW + add > maxW) { lines.push(cur); cur = []; curW = 0; }\n cur.push(w);\n curW += cur.length === 1 ? ww : spaceW + ww;\n }\n if (cur.length) lines.push(cur);\n return lines;\n}\n\nfunction kineticTextLayer(): Layer<KineticTextProps> {\n let text = '';\n let mode = DEFAULTS.mode;\n let startMs = DEFAULTS.startMs;\n let revealMs = DEFAULTS.revealMs;\n let fontPx = DEFAULTS.fontPx;\n let color: string | undefined;\n let centerFrac = DEFAULTS.centerFrac;\n let cursor = DEFAULTS.cursor;\n\n return {\n key: 'kinetic-text',\n init(_ctx: RenderCtx, props) {\n text = props.text ?? '';\n mode = props.mode ?? DEFAULTS.mode;\n startMs = props.startMs ?? DEFAULTS.startMs;\n revealMs = props.revealMs ?? DEFAULTS.revealMs;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n color = props.color;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n cursor = props.cursor ?? DEFAULTS.cursor;\n },\n draw(ctx, tMs) {\n if (!text) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const col = color ?? ctx.theme.ink;\n c.save();\n c.font = `700 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.textBaseline = 'middle';\n\n // Lines: explicit \"\\n\" first, then wrap each to the safe width.\n const rawLines = text.split('\\n');\n const lines: string[][] = [];\n for (const rl of rawLines) lines.push(...wrap(c, rl.split(/\\s+/).filter(Boolean), sb.w));\n\n const lineH = fontPx * 1.25;\n const totalH = lines.length * lineH;\n const top = ctx.H * centerFrac - totalH / 2 + lineH / 2;\n const p = clamp01((tMs - startMs) / Math.max(1, revealMs));\n\n if (mode === 'typewriter') {\n const full = lines.map((l) => l.join(' '));\n const totalChars = full.reduce((n, l) => n + l.length, 0);\n let shown = Math.floor(p * totalChars);\n c.textAlign = 'center';\n c.fillStyle = col;\n let y = top;\n let lastDrawn = { x: 0, y: 0, end: false };\n for (const line of full) {\n const take = Math.max(0, Math.min(line.length, shown));\n const sub = line.slice(0, take);\n if (sub) c.fillText(sub, ctx.W / 2, y);\n shown -= line.length;\n if (take < line.length && take >= 0 && !lastDrawn.end) {\n // cursor sits at the end of the partially-typed line\n lastDrawn = { x: ctx.W / 2 + c.measureText(sub).width / 2, y, end: true };\n }\n y += lineH;\n }\n if (cursor && p < 1 && (Math.floor(tMs / 450) % 2 === 0)) {\n c.fillRect(lastDrawn.x + 6, lastDrawn.y - fontPx * 0.42, 6, fontPx * 0.8);\n }\n } else {\n // word-pop: stagger words across revealMs; each pops over ~280ms.\n const allWords = lines.flat();\n const n = allWords.length || 1;\n const stagger = revealMs / n;\n const POP = 280;\n c.textAlign = 'left';\n const spaceW = c.measureText(' ').width;\n let wi = 0;\n let y = top;\n for (const line of lines) {\n const lineW = line.reduce((w, word, i) => w + c.measureText(word).width + (i ? spaceW : 0), 0);\n let x = sb.left + (sb.w - lineW) / 2;\n for (const word of line) {\n const ww = c.measureText(word).width;\n const wp = clamp01((tMs - startMs - wi * stagger) / POP);\n if (wp > 0) {\n const scale = 0.7 + 0.3 * wp;\n c.save();\n c.globalAlpha = wp;\n c.translate(x + ww / 2, y);\n c.scale(scale, scale);\n c.fillStyle = col;\n c.fillText(word, -ww / 2, 0);\n c.restore();\n }\n x += ww + spaceW;\n wi++;\n }\n y += lineH;\n }\n }\n c.restore();\n },\n };\n}\n\nexport const kineticTextFactory: LayerFactory<KineticTextProps> = {\n key: 'kinetic-text',\n create: kineticTextLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['kinetic-text: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.text !== 'string' || !p.text) errs.push('kinetic-text.text must be a non-empty string');\n if (p.mode != null && p.mode !== 'typewriter' && p.mode !== 'word-pop') errs.push('kinetic-text.mode must be \"typewriter\" | \"word-pop\"');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('kinetic-text.startMs must be a number');\n if (p.revealMs != null && (typeof p.revealMs !== 'number' || p.revealMs <= 0)) errs.push('kinetic-text.revealMs must be a positive number');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('kinetic-text.fontPx must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('kinetic-text.color must be a string');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('kinetic-text.centerFrac must be in [0,1]');\n if (p.cursor != null && typeof p.cursor !== 'boolean') errs.push('kinetic-text.cursor must be a boolean');\n return errs;\n },\n};\n","// `countdown` layer (S6) — a big \"3 · 2 · 1\" tick before a reveal. In a\n// guess-the-answer format (whozart \"name the piece\", RET \"name the leap\", RMT\n// concept checks) a visible countdown into the reveal builds tension and tells\n// the viewer \"answer's coming — stay\" — a small but real retention beat.\n//\n// Pure fn of t: the active number = `from - floor((t-startMs)/perTick)`, shown\n// only inside [startMs, startMs+durationMs). Each tick pops (a quick scale\n// overshoot) and an optional ring drains to show time left in the tick.\n// Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface CountdownProps {\n /** Count from this number down to 1. Default 3. */\n from?: number;\n /** When the countdown starts (ms). Default 0. */\n startMs?: number;\n /** Total countdown window (ms); each number shows durationMs/from. Default 3000. */\n durationMs?: number;\n /** Number colour. Default theme.accent. */\n color?: string;\n /** Number font px. Default 280. */\n fontPx?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Draw a draining ring around the number. Default true. */\n ring?: boolean;\n /** Ring radius in px. Default = fontPx*0.62. Set explicitly to align the ring\n * with another circular element (e.g. whozart's radial ring / portrait). */\n ringRadius?: number;\n}\n\nconst DEFAULTS = { from: 3, startMs: 0, durationMs: 3000, fontPx: 280, centerFrac: 0.46, ring: true };\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction countdownLayer(): Layer<CountdownProps> {\n let from = DEFAULTS.from;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let color: string | undefined;\n let fontPx = DEFAULTS.fontPx;\n let centerFrac = DEFAULTS.centerFrac;\n let ring = DEFAULTS.ring;\n let ringRadiusProp: number | undefined;\n\n return {\n key: 'countdown',\n init(_ctx: RenderCtx, props) {\n from = props.from ?? DEFAULTS.from;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n color = props.color;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n ring = props.ring ?? DEFAULTS.ring;\n ringRadiusProp = props.ringRadius;\n },\n draw(ctx, tMs) {\n const elapsed = tMs - startMs;\n if (elapsed < 0 || elapsed >= durationMs) return;\n const perTick = durationMs / from;\n const idx = Math.floor(elapsed / perTick);\n const n = from - idx;\n if (n < 1 || n > from) return;\n const tickElapsed = elapsed - idx * perTick;\n const tickFrac = tickElapsed / perTick; // 0→1 within this tick\n\n const c = ctx.ctx2d;\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n // Pop: quick overshoot at the start of each tick, settling to 1.\n const pop = 1 + 0.28 * Math.exp(-tickElapsed / 120);\n const fade = 1 - Math.max(0, (tickFrac - 0.7) / 0.3) * 0.85; // fade in last 30%\n\n c.save();\n if (ring) {\n const r = ringRadiusProp ?? fontPx * 0.62;\n c.lineWidth = 10;\n c.lineCap = 'round';\n c.strokeStyle = rgba(col, 0.18 * fade);\n c.beginPath(); c.arc(cx, cy, r, 0, Math.PI * 2); c.stroke();\n // Drains clockwise from full to empty across the tick.\n c.strokeStyle = rgba(col, 0.9 * fade);\n c.beginPath();\n c.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + (1 - tickFrac) * Math.PI * 2);\n c.stroke();\n }\n c.globalAlpha = fade;\n c.fillStyle = col;\n c.font = `800 ${Math.round(fontPx * pop)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(String(n), cx, cy + 2);\n c.restore();\n },\n };\n}\n\nexport const countdownFactory: LayerFactory<CountdownProps> = {\n key: 'countdown',\n create: countdownLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['countdown: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.from != null && (typeof p.from !== 'number' || p.from < 1 || !Number.isInteger(p.from))) errs.push('countdown.from must be an integer >= 1');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('countdown.startMs must be a number');\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || p.durationMs <= 0)) errs.push('countdown.durationMs must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('countdown.color must be a string');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('countdown.fontPx must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('countdown.centerFrac must be in [0,1]');\n if (p.ring != null && typeof p.ring !== 'boolean') errs.push('countdown.ring must be a boolean');\n if (p.ringRadius != null && (typeof p.ringRadius !== 'number' || p.ringRadius <= 0)) errs.push('countdown.ringRadius must be a positive number');\n return errs;\n },\n};\n","// `waveform` layer (S6) — a live oscilloscope (time-domain amplitude trace),\n// the classic audio-viz that complements the FFT `spectrum`/`radial-spectrum`.\n// Same host-provided-input + deterministic-fallback pattern as `spectrum`: the\n// host wires `ctx.waveform.byteTime` → AnalyserNode.getByteTimeDomainData; with\n// none, the layer animates a deterministic synthetic wave (pure fn of t → tests\n// + offline captures reproduce).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\n/** Host-provided time-domain sample source (AnalyserNode shape). */\nexport interface WaveformInput {\n /** 0..255 samples centered ~128 (AnalyserNode.getByteTimeDomainData). */\n byteTime?(tMs: number): Uint8Array | null | undefined;\n}\n\ndeclare module '../layer' {\n interface RenderCtx {\n /** Optional time-domain source for the waveform layer (host-wired). */\n waveform?: WaveformInput;\n }\n}\n\nconst SAMPLES = 128;\n\nexport interface WaveformProps {\n /** Number of samples plotted. Default 128. */\n samples?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Max deflection as a fraction of H. Default 0.10. */\n amplitudeFrac?: number;\n /** Line width px. Default 5. */\n lineWidth?: number;\n /** Line colour. Default theme.accent. */\n color?: string;\n /** Per-frame sample provider returning -1..1 (overrides ctx.waveform). */\n samplesFn?: (tMs: number, n: number) => Float32Array | number[] | null;\n}\n\nfunction clampUnit(x: number): number { return x < -1 ? -1 : x > 1 ? 1 : x; }\n\n/** Deterministic synthetic wave: a few detuned sines that drift with t. Pure. */\nfunction syntheticSample(tSec: number, u: number): number {\n const phase = u * Math.PI * 2;\n return 0.55 * Math.sin(phase * 3 + tSec * 6)\n + 0.28 * Math.sin(phase * 5 + tSec * 9 + 1.1)\n + 0.14 * Math.sin(phase * 8 + tSec * 4 + 2.3);\n}\n\nfunction waveformLayer(): Layer<WaveformProps> {\n let n = SAMPLES;\n let centerFrac = 0.46;\n let amplitudeFrac = 0.10;\n let lineWidth = 5;\n let color: string | undefined;\n let samplesFn: WaveformProps['samplesFn'];\n\n function samplesAt(ctx: RenderCtx, tMs: number): number[] {\n const fromProp = samplesFn?.(tMs, n);\n if (fromProp && fromProp.length) {\n return Array.from({ length: n }, (_, i) => clampUnit(fromProp[Math.min(fromProp.length - 1, i)] ?? 0));\n }\n const bt = ctx.waveform?.byteTime?.(tMs);\n if (bt && bt.length) {\n return Array.from({ length: n }, (_, i) => clampUnit((bt[Math.floor((i / n) * bt.length)] - 128) / 128));\n }\n const tSec = tMs / 1000;\n return Array.from({ length: n }, (_, i) => clampUnit(syntheticSample(tSec, i / (n - 1))));\n }\n\n return {\n key: 'waveform',\n init(_ctx: RenderCtx, props) {\n n = props.samples ?? SAMPLES;\n centerFrac = props.centerFrac ?? 0.46;\n amplitudeFrac = props.amplitudeFrac ?? 0.10;\n lineWidth = props.lineWidth ?? 5;\n color = props.color;\n samplesFn = props.samplesFn;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const left = sb.left, right = sb.right;\n const cy = ctx.H * centerFrac;\n const amp = ctx.H * amplitudeFrac;\n const s = samplesAt(ctx, tMs);\n c.save();\n c.lineJoin = 'round';\n c.lineCap = 'round';\n c.strokeStyle = color ?? ctx.theme.accent;\n c.lineWidth = lineWidth;\n c.beginPath();\n for (let i = 0; i < n; i++) {\n const x = left + (right - left) * (i / (n - 1));\n const y = cy + s[i] * amp;\n if (i === 0) c.moveTo(x, y); else c.lineTo(x, y);\n }\n c.stroke();\n c.restore();\n },\n };\n}\n\nexport const waveformFactory: LayerFactory<WaveformProps> = {\n key: 'waveform',\n create: waveformLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['waveform: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.samples != null && (typeof p.samples !== 'number' || p.samples < 8)) errs.push('waveform.samples must be a number >= 8');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('waveform.centerFrac must be in [0,1]');\n if (p.amplitudeFrac != null && (typeof p.amplitudeFrac !== 'number' || p.amplitudeFrac <= 0)) errs.push('waveform.amplitudeFrac must be a positive number');\n if (p.lineWidth != null && (typeof p.lineWidth !== 'number' || p.lineWidth <= 0)) errs.push('waveform.lineWidth must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('waveform.color must be a string');\n if (p.samplesFn != null && typeof p.samplesFn !== 'function') errs.push('waveform.samplesFn must be a function');\n return errs;\n },\n};\n","// `scale-highlight` layer (S6) — lights up the notes of a scale/mode on the\n// keyboard and names it (\"D Dorian\"). The teaching primitive for \"what notes are\n// in this key\": it rides the keyboard layer's published layout (keyboardStore)\n// and tints every key whose pitch-class is in the scale, the tonic distinctly.\n//\n// DATA: a `root` (note name or pitch-class) + a `scale`/mode name → a pitch-class\n// set from the interval table below. No Score needed. If a keyboard layer has\n// published a layout it tints the keys; otherwise it just draws the name label.\n// World-space (must be stacked AFTER the keyboard); pure fn of t (static).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { getKeyboardLayout } from '../keyboardStore';\nimport { keyRect, inRange } from '../keyboardGeometry';\nimport { noteNameToIndex, pitchClass } from '../../notes';\n\n/** Semitone interval sets (from the root) for the supported scales/modes. */\nexport const SCALE_INTERVALS: Record<string, number[]> = {\n major: [0, 2, 4, 5, 7, 9, 11],\n ionian: [0, 2, 4, 5, 7, 9, 11],\n minor: [0, 2, 3, 5, 7, 8, 10],\n aeolian: [0, 2, 3, 5, 7, 8, 10],\n dorian: [0, 2, 3, 5, 7, 9, 10],\n phrygian: [0, 1, 3, 5, 7, 8, 10],\n lydian: [0, 2, 4, 6, 7, 9, 11],\n mixolydian: [0, 2, 4, 5, 7, 9, 10],\n locrian: [0, 1, 3, 5, 6, 8, 10],\n harmonicMinor: [0, 2, 3, 5, 7, 8, 11],\n melodicMinor: [0, 2, 3, 5, 7, 9, 11],\n pentatonicMajor: [0, 2, 4, 7, 9],\n pentatonicMinor: [0, 3, 5, 7, 10],\n blues: [0, 3, 5, 6, 7, 10],\n chromatic: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],\n};\n\nconst PRETTY: Record<string, string> = {\n major: 'Major', ionian: 'Ionian', minor: 'Minor', aeolian: 'Aeolian', dorian: 'Dorian',\n phrygian: 'Phrygian', lydian: 'Lydian', mixolydian: 'Mixolydian', locrian: 'Locrian',\n harmonicMinor: 'Harmonic Minor', melodicMinor: 'Melodic Minor',\n pentatonicMajor: 'Pentatonic', pentatonicMinor: 'Minor Pentatonic', blues: 'Blues', chromatic: 'Chromatic',\n};\n\nexport interface ScaleHighlightProps {\n /** Tonic — note name ('C','D','Bb'…) or pitch-class number 0..11. Default 'C'. */\n root?: string | number;\n /** Scale/mode key (see SCALE_INTERVALS). Default 'major'. */\n scale?: string;\n /** Colour for in-scale keys. Default theme.accent. */\n scaleColor?: string;\n /** Colour for the tonic key. Default theme.gold. */\n tonicColor?: string;\n /** Tint opacity over the key. Default 0.55. */\n alpha?: number;\n /** Draw the scale name label. Default true. */\n label?: boolean;\n /** Override the label text. Default `${root} ${PrettyScale}`. */\n labelText?: string;\n /** Label vertical position as a fraction of H. Default near the safe-box top.\n * Set this when the host already draws a top title so the name doesn't collide. */\n labelCenterFrac?: number;\n}\n\nfunction rootPc(root: string | number): number {\n if (typeof root === 'number') return ((root % 12) + 12) % 12;\n return noteNameToIndex(root);\n}\n\nfunction scaleHighlightLayer(): Layer<ScaleHighlightProps> {\n let rootProp: string | number = 'C';\n let scaleKey = 'major';\n let scaleColor: string | undefined;\n let tonicColor: string | undefined;\n let alpha = 0.55;\n let label = true;\n let labelTextProp: string | undefined;\n let labelCenterFrac: number | undefined;\n\n return {\n key: 'scale-highlight',\n init(_ctx: RenderCtx, props) {\n rootProp = props.root ?? 'C';\n scaleKey = props.scale ?? 'major';\n scaleColor = props.scaleColor;\n tonicColor = props.tonicColor;\n alpha = props.alpha ?? 0.55;\n label = props.label ?? true;\n labelTextProp = props.labelText;\n labelCenterFrac = props.labelCenterFrac;\n },\n draw(ctx, _tMs) {\n const c = ctx.ctx2d;\n const intervals = SCALE_INTERVALS[scaleKey] ?? SCALE_INTERVALS.major;\n const root = rootPc(rootProp);\n const inScale = new Set(intervals.map((iv) => (root + iv) % 12));\n const sc = scaleColor ?? ctx.theme.accent;\n const tc = tonicColor ?? ctx.theme.gold;\n\n const layout = getKeyboardLayout(ctx);\n if (layout) {\n // Clean look: a solid PIP near the bottom of each in-scale key (not a\n // muddy full-key wash) — reads clearly and coexists with an app's own\n // key colouring. Tonic = bigger pip with a white core.\n c.save();\n for (let m = layout.lowMidi; m <= layout.highMidi; m++) {\n if (!inRange(layout, m)) continue;\n const pc = pitchClass(m);\n if (!inScale.has(pc)) continue;\n const r = keyRect(layout, m);\n const isTonic = pc === root;\n const cx = r.x + r.w / 2;\n const pipR = (r.black ? r.w * 0.5 : r.w * 0.32) * (isTonic ? 1.25 : 1);\n const cy = r.y + r.h - pipR - (r.black ? 8 : 16);\n c.globalAlpha = Math.min(1, alpha + 0.4); // pips read better near-solid\n c.fillStyle = isTonic ? tc : sc;\n c.beginPath(); c.arc(cx, cy, pipR, 0, Math.PI * 2); c.fill();\n if (isTonic) {\n c.fillStyle = '#ffffff';\n c.beginPath(); c.arc(cx, cy, pipR * 0.42, 0, Math.PI * 2); c.fill();\n }\n }\n c.restore();\n }\n\n if (label) {\n const sb = ctx.safeBox;\n const name = typeof rootProp === 'string' ? rootProp : ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'][root];\n const text = labelTextProp ?? `${name} ${PRETTY[scaleKey] ?? scaleKey}`;\n c.save();\n c.fillStyle = ctx.theme.ink;\n c.font = `700 ${Math.round(ctx.H * 0.030)}px ${ctx.theme.fontDisplay}`;\n c.textAlign = 'center';\n c.textBaseline = labelCenterFrac != null ? 'middle' : 'top';\n const ly = labelCenterFrac != null ? ctx.H * labelCenterFrac : sb.top + sb.h * 0.04;\n c.fillText(text, ctx.W / 2, ly);\n c.restore();\n }\n },\n };\n}\n\nexport const scaleHighlightFactory: LayerFactory<ScaleHighlightProps> = {\n key: 'scale-highlight',\n create: scaleHighlightLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['scale-highlight: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.root != null && typeof p.root !== 'string' && typeof p.root !== 'number') errs.push('scale-highlight.root must be a note name or pitch-class number');\n if (p.scale != null && (typeof p.scale !== 'string' || !(p.scale in SCALE_INTERVALS))) errs.push(`scale-highlight.scale must be one of: ${Object.keys(SCALE_INTERVALS).join(', ')}`);\n if (p.scaleColor != null && typeof p.scaleColor !== 'string') errs.push('scale-highlight.scaleColor must be a string');\n if (p.tonicColor != null && typeof p.tonicColor !== 'string') errs.push('scale-highlight.tonicColor must be a string');\n if (p.alpha != null && (typeof p.alpha !== 'number' || p.alpha < 0 || p.alpha > 1)) errs.push('scale-highlight.alpha must be in [0,1]');\n if (p.label != null && typeof p.label !== 'boolean') errs.push('scale-highlight.label must be a boolean');\n if (p.labelText != null && typeof p.labelText !== 'string') errs.push('scale-highlight.labelText must be a string');\n if (p.labelCenterFrac != null && (typeof p.labelCenterFrac !== 'number' || p.labelCenterFrac < 0 || p.labelCenterFrac > 1)) errs.push('scale-highlight.labelCenterFrac must be in [0,1]');\n return errs;\n },\n};\n","// `particle-burst` layer (S6) — sparks burst from each note as it sounds. Pure\n// energy/celebration; reuses the same onset times everything else does (Score or\n// an `onsetsMs` prop), so any musical scene gets a reactive firework on the beat.\n//\n// Each onset spawns `count` particles with seeded (deterministic) angles +\n// speeds; a particle's position is a closed-form function of its age\n// (t - onsetMs) under constant gravity → pure fn of t (no per-frame state), so\n// captures/golden frames reproduce. If a Score is present the burst origin x is\n// mapped from the note's pitch (low→left, high→right); otherwise it's centered.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\nimport { distinctOnsets } from '../notationGeometry';\n\ninterface OnsetSrc { tMs: number; x01: number }\n\nexport interface ParticleBurstProps {\n /** Override onsets. Default = Score onsets (origin x from pitch when available). */\n onsetsMs?: number[];\n /** Particles per burst. Default 16. */\n count?: number;\n /** Particle lifetime, ms. Default 750. */\n lifeMs?: number;\n /** Initial speed (px/s). Default 520. */\n speed?: number;\n /** Gravity (px/s²). Default 900. */\n gravity?: number;\n /** Origin y as a fraction of H (burst source line). Default 0.6. */\n originYFrac?: number;\n /** Particle colour. Default theme.gold. */\n color?: string;\n /** Max particle radius (px). Default 9. */\n size?: number;\n}\n\nconst DEFAULTS = { count: 16, lifeMs: 750, speed: 520, gravity: 900, originYFrac: 0.6, size: 9 };\n\n/** Deterministic unit pseudo-random from two ints (seeded; no Math.random). */\nfunction seeded(a: number, b: number): number {\n const s = Math.sin(a * 127.1 + b * 311.7) * 43758.5453;\n return s - Math.floor(s);\n}\n\nfunction rgba(hex: string, al: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${al})`;\n}\n\nfunction particleBurstLayer(): Layer<ParticleBurstProps> {\n let sources: OnsetSrc[] = [];\n let count = DEFAULTS.count;\n let lifeMs = DEFAULTS.lifeMs;\n let speed = DEFAULTS.speed;\n let gravity = DEFAULTS.gravity;\n let originYFrac = DEFAULTS.originYFrac;\n let color: string | undefined;\n let size = DEFAULTS.size;\n\n return {\n key: 'particle-burst',\n init(ctx: RenderCtx, props) {\n count = props.count ?? DEFAULTS.count;\n lifeMs = props.lifeMs ?? DEFAULTS.lifeMs;\n speed = props.speed ?? DEFAULTS.speed;\n gravity = props.gravity ?? DEFAULTS.gravity;\n originYFrac = props.originYFrac ?? DEFAULTS.originYFrac;\n color = props.color;\n size = props.size ?? DEFAULTS.size;\n\n if (props.onsetsMs) {\n sources = props.onsetsMs.map((tMs) => ({ tMs, x01: 0.5 }));\n } else if (ctx.score?.notes?.length) {\n // origin x from pitch: map [minPitch,maxPitch] → [0.15,0.85].\n const notes = ctx.score.notes;\n let lo = Infinity, hi = -Infinity;\n for (const nn of notes) { if (nn.pitchMidi < lo) lo = nn.pitchMidi; if (nn.pitchMidi > hi) hi = nn.pitchMidi; }\n const span = Math.max(1, hi - lo);\n // first note at each distinct onset drives the x.\n const byOnset = new Map<number, number>();\n for (const nn of notes) if (!byOnset.has(nn.onsetMs)) byOnset.set(nn.onsetMs, nn.pitchMidi);\n sources = distinctOnsets(notes).map((tMs) => ({ tMs, x01: 0.15 + 0.7 * (((byOnset.get(tMs) ?? lo) - lo) / span) }));\n } else {\n sources = [];\n }\n sources.sort((a, b) => a.tMs - b.tMs);\n },\n draw(ctx, tMs) {\n if (!sources.length) return;\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n const oy = H * originYFrac;\n const col = color ?? ctx.theme.gold;\n c.save();\n c.globalCompositeOperation = 'lighter';\n for (let si = 0; si < sources.length; si++) {\n const src = sources[si];\n const age = tMs - src.tMs;\n if (age < 0) break; // sorted → nothing later has sounded\n if (age >= lifeMs) continue;\n const ageSec = age / 1000;\n const lifeFrac = age / lifeMs;\n const ox = W * src.x01;\n for (let i = 0; i < count; i++) {\n const a = seeded(si * 31 + i, 7); // angle seed\n const sp = speed * (0.5 + seeded(si, i * 17)); // speed seed\n const ang = -Math.PI / 2 + (a - 0.5) * Math.PI * 1.2; // upward fan\n const vx = Math.cos(ang) * sp;\n const vy = Math.sin(ang) * sp;\n const x = ox + vx * ageSec;\n const y = oy + vy * ageSec + 0.5 * gravity * ageSec * ageSec;\n const alpha = (1 - lifeFrac) * 0.9;\n const r = size * (1 - lifeFrac * 0.6);\n if (r <= 0) continue;\n c.fillStyle = rgba(col, alpha);\n c.beginPath();\n c.arc(x, y, r, 0, Math.PI * 2);\n c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const particleBurstFactory: LayerFactory<ParticleBurstProps> = {\n key: 'particle-burst',\n create: particleBurstLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['particle-burst: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.onsetsMs != null && (!Array.isArray(p.onsetsMs) || p.onsetsMs.some((x) => typeof x !== 'number'))) errs.push('particle-burst.onsetsMs must be a number[]');\n for (const k of ['count', 'lifeMs', 'speed', 'gravity', 'size'] as const) {\n if (p[k] != null && (typeof p[k] !== 'number' || (p[k] as number) <= 0)) errs.push(`particle-burst.${k} must be a positive number`);\n }\n if (p.originYFrac != null && (typeof p.originYFrac !== 'number' || p.originYFrac < 0 || p.originYFrac > 1)) errs.push('particle-burst.originYFrac must be in [0,1]');\n if (p.color != null && typeof p.color !== 'string') errs.push('particle-burst.color must be a string');\n return errs;\n },\n};\n","// `tension-graph` layer (S6) — a curve of musical TENSION over time that \"draws\n// on\" to the playhead, with a moving dot. Teaching primitive: it visualises the\n// pull-and-release a listener feels (stable tonic → restless leading tone →\n// resolution), turning an abstract feeling into a shape.\n//\n// DATA SOURCE (in priority order):\n// 1. `series` prop — host-supplied [{tMs, value 0..1}] (most control).\n// 2. Score + `key` — tension per melody note from scale-degree stability\n// (stable 1/3/5 → low, 2/4/6 → mid, leading-tone 7 → high, chromatic → max).\n// 3. Score only — normalized melodic height as a rough tension proxy.\n// Pure fn of t. World-space band (placed via props, like pitch-contour).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport { contourPoints, pitchRange } from '../pitchContour';\nimport { getScaleDegree, getStability } from '../../scales';\n\nexport interface TensionPoint { tMs: number; value: number }\n\nexport interface TensionGraphProps {\n /** Host-supplied tension series [{tMs, value 0..1}]. Overrides Score derivation. */\n series?: TensionPoint[];\n /** Key (e.g. 'C') → derive tension from scale-degree stability. */\n key?: string;\n /** Plot band top (world y). Default safeBox.top + safeBox.h*0.18. */\n top?: number;\n /** Plot band height (world px). Default safeBox.h*0.30. */\n height?: number;\n /** Line + fill colour. Default theme.accent. */\n color?: string;\n /** Fill the area under the curve. Default true. */\n fill?: boolean;\n /** Line width. Default 5. */\n width?: number;\n /** Moving dot at the current value. Default true. */\n dot?: boolean;\n}\n\nfunction stabilityTension(midi: number, key: string): number {\n const deg = getScaleDegree(midi, key);\n if (deg == null) return 1.0; // chromatic = max tension\n const s = getStability(deg);\n return s === 'stable' ? 0.15 : s === 'lessStable' ? 0.5 : s === 'unstable' ? 0.85 : 0.5;\n}\n\nfunction rgba(hex: string, a: number): string {\n const h = hex.replace('#', '');\n const n = h.length === 3 ? parseInt(h.split('').map((c) => c + c).join(''), 16) : parseInt(h, 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`;\n}\n\nfunction tensionGraphLayer(): Layer<TensionGraphProps> {\n let series: TensionPoint[] = [];\n let durationMs = 1;\n let topProp: number | undefined;\n let heightProp: number | undefined;\n let color: string | undefined;\n let fill = true;\n let width = 5;\n let dot = true;\n\n return {\n key: 'tension-graph',\n init(ctx, props) {\n topProp = props.top; heightProp = props.height; color = props.color;\n fill = props.fill ?? true; width = props.width ?? 5; dot = props.dot ?? true;\n\n if (props.series?.length) {\n series = props.series.slice().sort((a, b) => a.tMs - b.tMs);\n } else if (ctx.score?.notes?.length) {\n const pts = contourPoints(ctx.score.notes);\n if (props.key) {\n series = pts.map((p) => ({ tMs: p.tMs, value: stabilityTension(p.pitchMidi, props.key!) }));\n } else {\n const r = pitchRange(pts);\n const span = Math.max(1, r.max - r.min);\n series = pts.map((p) => ({ tMs: p.tMs, value: (p.pitchMidi - r.min) / span }));\n }\n } else {\n series = [];\n }\n durationMs = ctx.score?.durationMs ?? (series.length ? series[series.length - 1].tMs : 1);\n },\n draw(ctx, tMs) {\n if (series.length < 2) return;\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const top = topProp ?? sb.top + sb.h * 0.18;\n const height = heightProp ?? sb.h * 0.30;\n const bottom = top + height;\n const col = color ?? ctx.theme.accent;\n const x = (ms: number) => sb.left + (sb.right - sb.left) * Math.min(1, Math.max(0, ms / durationMs));\n const y = (v: number) => bottom - Math.min(1, Math.max(0, v)) * height;\n const playX = x(tMs);\n\n c.save();\n c.lineJoin = 'round'; c.lineCap = 'round';\n\n // Ghost (full curve, faint).\n c.globalAlpha = 0.2; c.strokeStyle = ctx.theme.sepia; c.lineWidth = width;\n c.beginPath();\n series.forEach((p, i) => (i === 0 ? c.moveTo(x(p.tMs), y(p.value)) : c.lineTo(x(p.tMs), y(p.value))));\n c.stroke();\n\n // Revealed portion up to the playhead (+ optional fill).\n const revealed: { x: number; y: number }[] = [];\n for (let i = 0; i < series.length; i++) {\n const px = x(series[i].tMs);\n if (px <= playX) { revealed.push({ x: px, y: y(series[i].value) }); }\n else { // clip last segment at playX\n if (i > 0) {\n const a = series[i - 1], b = series[i];\n const f = (playX - x(a.tMs)) / Math.max(1, x(b.tMs) - x(a.tMs));\n revealed.push({ x: playX, y: y(a.value + (b.value - a.value) * f) });\n }\n break;\n }\n }\n if (revealed.length) {\n c.globalAlpha = 1;\n if (fill) {\n c.fillStyle = rgba(col, 0.22);\n c.beginPath();\n c.moveTo(revealed[0].x, bottom);\n for (const p of revealed) c.lineTo(p.x, p.y);\n c.lineTo(revealed[revealed.length - 1].x, bottom);\n c.closePath(); c.fill();\n }\n c.strokeStyle = col; c.lineWidth = width;\n c.beginPath();\n revealed.forEach((p, i) => (i === 0 ? c.moveTo(p.x, p.y) : c.lineTo(p.x, p.y)));\n c.stroke();\n if (dot) {\n const last = revealed[revealed.length - 1];\n c.fillStyle = col;\n c.beginPath(); c.arc(last.x, last.y, width * 2, 0, Math.PI * 2); c.fill();\n }\n }\n c.restore();\n },\n };\n}\n\nexport const tensionGraphFactory: LayerFactory<TensionGraphProps> = {\n key: 'tension-graph',\n create: tensionGraphLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['tension-graph: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.series != null) {\n if (!Array.isArray(p.series)) errs.push('tension-graph.series must be an array of {tMs, value}');\n else p.series.forEach((raw, i) => {\n const s = raw as Record<string, unknown>;\n if (typeof s?.tMs !== 'number' || typeof s?.value !== 'number') errs.push(`tension-graph.series[${i}] must be {tMs:number, value:number}`);\n });\n }\n if (p.key != null && typeof p.key !== 'string') errs.push('tension-graph.key must be a string');\n if (p.top != null && typeof p.top !== 'number') errs.push('tension-graph.top must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0)) errs.push('tension-graph.height must be a positive number');\n if (p.color != null && typeof p.color !== 'string') errs.push('tension-graph.color must be a string');\n if (p.fill != null && typeof p.fill !== 'boolean') errs.push('tension-graph.fill must be a boolean');\n if (p.width != null && (typeof p.width !== 'number' || p.width <= 0)) errs.push('tension-graph.width must be a positive number');\n if (p.dot != null && typeof p.dot !== 'boolean') errs.push('tension-graph.dot must be a boolean');\n return errs;\n },\n};\n","// `texture` layer (S6) — a finishing overlay: film grain + vignette + optional\n// cinematic letterbox bars. One layer that makes any clip look graded. Drawn last\n// (screen-pinned), so it sits over everything.\n//\n// Grain is deterministic per FRAME: the speck pattern is seeded by the frame\n// index (derived from t + ctx.fps), so it shimmers frame-to-frame like real\n// grain yet is reproducible for captures/golden frames (pure fn of t).\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface TextureProps {\n /** Grain intensity 0..1 (speck opacity). Default 0.05. 0 disables grain. */\n grain?: number;\n /** Grain speck count per frame. Default 700. */\n grainDensity?: number;\n /** Vignette strength 0..1 (edge darkening). Default 0.35. 0 disables. */\n vignette?: number;\n /** Letterbox bar height as a fraction of H (top+bottom). Default 0 (off). */\n letterboxFrac?: number;\n}\n\nconst DEFAULTS = { grain: 0.05, grainDensity: 700, vignette: 0.35, letterboxFrac: 0 };\n\n/** Tiny deterministic LCG, seeded per frame. */\nfunction lcg(seed: number): () => number {\n let s = (seed | 0) || 1;\n return () => { s = (s * 1664525 + 1013904223) & 0x7fffffff; return s / 0x7fffffff; };\n}\n\nfunction textureLayer(): Layer<TextureProps> {\n let grain = DEFAULTS.grain;\n let grainDensity = DEFAULTS.grainDensity;\n let vignette = DEFAULTS.vignette;\n let letterboxFrac = DEFAULTS.letterboxFrac;\n\n return {\n key: 'texture',\n init(_ctx: RenderCtx, props) {\n grain = props.grain ?? DEFAULTS.grain;\n grainDensity = props.grainDensity ?? DEFAULTS.grainDensity;\n vignette = props.vignette ?? DEFAULTS.vignette;\n letterboxFrac = props.letterboxFrac ?? DEFAULTS.letterboxFrac;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const W = ctx.W, H = ctx.H;\n c.save();\n\n if (vignette > 0) {\n const g = c.createRadialGradient(W / 2, H / 2, Math.min(W, H) * 0.35, W / 2, H / 2, Math.max(W, H) * 0.72);\n g.addColorStop(0, 'rgba(0,0,0,0)');\n g.addColorStop(1, `rgba(0,0,0,${vignette})`);\n c.fillStyle = g;\n c.fillRect(0, 0, W, H);\n }\n\n if (grain > 0 && grainDensity > 0) {\n const frame = Math.floor((tMs / 1000) * (ctx.fps || 30));\n const rnd = lcg(frame * 2654435761);\n for (let i = 0; i < grainDensity; i++) {\n const x = rnd() * W;\n const y = rnd() * H;\n const dark = rnd() < 0.5;\n c.fillStyle = dark ? `rgba(0,0,0,${grain})` : `rgba(255,255,255,${grain})`;\n c.fillRect(x, y, 2, 2);\n }\n }\n\n if (letterboxFrac > 0) {\n const bh = H * letterboxFrac;\n c.fillStyle = '#000';\n c.fillRect(0, 0, W, bh);\n c.fillRect(0, H - bh, W, bh);\n }\n c.restore();\n },\n };\n}\n\nexport const textureFactory: LayerFactory<TextureProps> = {\n key: 'texture',\n create: textureLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['texture: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (p.grain != null && (typeof p.grain !== 'number' || p.grain < 0 || p.grain > 1)) errs.push('texture.grain must be in [0,1]');\n if (p.grainDensity != null && (typeof p.grainDensity !== 'number' || p.grainDensity < 0)) errs.push('texture.grainDensity must be a number >= 0');\n if (p.vignette != null && (typeof p.vignette !== 'number' || p.vignette < 0 || p.vignette > 1)) errs.push('texture.vignette must be in [0,1]');\n if (p.letterboxFrac != null && (typeof p.letterboxFrac !== 'number' || p.letterboxFrac < 0 || p.letterboxFrac > 0.45)) errs.push('texture.letterboxFrac must be in [0,0.45]');\n return errs;\n },\n};\n","// `stat-counter` layer (S6) — a big number that counts up to a target with an\n// eased ramp, plus a caption (e.g. \"1788 · Composed\", \"No. 1 · in 30 countries\").\n// A lower-third info beat: the animated count draws the eye and lands a fact.\n//\n// Pure fn of t: shown = round(ease((t-startMs)/durationMs) * value). Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface StatCounterProps {\n /** Target value to count up to. Required. */\n value: number;\n /** When the count starts (ms). Default 0. */\n startMs?: number;\n /** Count duration (ms). Default 1200. */\n durationMs?: number;\n /** Text before the number (e.g. \"No. \"). Default \"\". */\n prefix?: string;\n /** Text after the number (e.g. \"%\", \"+\"). Default \"\". */\n suffix?: string;\n /** Caption under the number. Default \"\". */\n label?: string;\n /** Number font px. Default 220. */\n fontPx?: number;\n /** Vertical center as a fraction of H. Default 0.46. */\n centerFrac?: number;\n /** Number colour. Default theme.accent. */\n color?: string;\n /** Thousands separators on the number. Default true. */\n group?: boolean;\n}\n\nconst DEFAULTS = { startMs: 0, durationMs: 1200, fontPx: 220, centerFrac: 0.46, group: true };\n\nfunction easeOut(t: number): number { return 1 - Math.pow(1 - t, 3); }\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\nfunction statCounterLayer(): Layer<StatCounterProps> {\n let value = 0;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let prefix = '';\n let suffix = '';\n let label = '';\n let fontPx = DEFAULTS.fontPx;\n let centerFrac = DEFAULTS.centerFrac;\n let color: string | undefined;\n let group = DEFAULTS.group;\n\n return {\n key: 'stat-counter',\n init(_ctx: RenderCtx, props) {\n value = props.value ?? 0;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n prefix = props.prefix ?? '';\n suffix = props.suffix ?? '';\n label = props.label ?? '';\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n color = props.color;\n group = props.group ?? DEFAULTS.group;\n },\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const p = clamp01((tMs - startMs) / Math.max(1, durationMs));\n const shown = Math.round(easeOut(p) * value);\n const numStr = group ? shown.toLocaleString('en-US') : String(shown);\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n\n c.save();\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillStyle = col;\n c.font = `800 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.fillText(`${prefix}${numStr}${suffix}`, cx, cy);\n if (label) {\n c.fillStyle = ctx.theme.ink;\n c.font = `600 ${Math.round(fontPx * 0.26)}px ${ctx.theme.fontDisplay}`;\n c.fillText(label, cx, cy + fontPx * 0.62);\n }\n c.restore();\n },\n };\n}\n\nexport const statCounterFactory: LayerFactory<StatCounterProps> = {\n key: 'stat-counter',\n create: statCounterLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['stat-counter: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.value !== 'number') errs.push('stat-counter.value must be a number');\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('stat-counter.startMs must be a number');\n if (p.durationMs != null && (typeof p.durationMs !== 'number' || p.durationMs <= 0)) errs.push('stat-counter.durationMs must be a positive number');\n for (const k of ['prefix', 'suffix', 'label'] as const) if (p[k] != null && typeof p[k] !== 'string') errs.push(`stat-counter.${k} must be a string`);\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('stat-counter.fontPx must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('stat-counter.centerFrac must be in [0,1]');\n if (p.color != null && typeof p.color !== 'string') errs.push('stat-counter.color must be a string');\n if (p.group != null && typeof p.group !== 'boolean') errs.push('stat-counter.group must be a boolean');\n return errs;\n },\n};\n","// `end-card` layer (S6) — an animated follow/subscribe CTA for the last beat: a\n// CTA line that scales in, an optional handle, and a bouncing chevron pointing at\n// the follow button. The \"what to do next\" cap that converts a watch into a\n// follow. Pure fn of t (the bounce is a sine of t). Screen-pinned.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport interface EndCardProps {\n /** CTA line. Default \"Follow for daily\". */\n text?: string;\n /** Handle / sub-line under the CTA (e.g. \"@whozart\"). Default \"\". */\n handle?: string;\n /** When the card animates in (ms). Default 0. */\n startMs?: number;\n /** Scale-in duration (ms). Default 400. */\n inMs?: number;\n /** CTA colour. Default theme.accent. */\n color?: string;\n /** Vertical center as a fraction of H. Default 0.5. */\n centerFrac?: number;\n /** Font px for the CTA line. Default 72. */\n fontPx?: number;\n /** Draw the bouncing chevron. Default true. */\n arrow?: boolean;\n /** Arrow direction — which way it points / where the follow button is. Default \"up\". */\n arrowDir?: 'up' | 'down';\n}\n\nconst DEFAULTS = { text: 'Follow for daily', startMs: 0, inMs: 400, centerFrac: 0.5, fontPx: 72, arrow: true, arrowDir: 'up' as const };\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\nfunction easeOut(t: number): number { return 1 - Math.pow(1 - t, 3); }\n\nfunction endCardLayer(): Layer<EndCardProps> {\n let text = DEFAULTS.text;\n let handle = '';\n let startMs = DEFAULTS.startMs;\n let inMs = DEFAULTS.inMs;\n let color: string | undefined;\n let centerFrac = DEFAULTS.centerFrac;\n let fontPx = DEFAULTS.fontPx;\n let arrow = DEFAULTS.arrow;\n let arrowDir: 'up' | 'down' = DEFAULTS.arrowDir;\n\n return {\n key: 'end-card',\n init(_ctx: RenderCtx, props) {\n text = props.text ?? DEFAULTS.text;\n handle = props.handle ?? '';\n startMs = props.startMs ?? DEFAULTS.startMs;\n inMs = props.inMs ?? DEFAULTS.inMs;\n color = props.color;\n centerFrac = props.centerFrac ?? DEFAULTS.centerFrac;\n fontPx = props.fontPx ?? DEFAULTS.fontPx;\n arrow = props.arrow ?? DEFAULTS.arrow;\n arrowDir = props.arrowDir ?? DEFAULTS.arrowDir;\n },\n draw(ctx, tMs) {\n if (tMs < startMs) return;\n const c = ctx.ctx2d;\n const cx = ctx.W / 2;\n const cy = ctx.H * centerFrac;\n const col = color ?? ctx.theme.accent;\n const p = clamp01((tMs - startMs) / Math.max(1, inMs));\n const scale = 0.7 + 0.3 * easeOut(p);\n const dir = arrowDir === 'down' ? 1 : -1;\n const bounce = Math.sin(tMs / 260) * 14; // gentle bob\n\n c.save();\n c.globalAlpha = p;\n\n // CTA pill text.\n c.translate(cx, cy);\n c.scale(scale, scale);\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillStyle = col;\n c.font = `800 ${fontPx}px ${ctx.theme.fontDisplay}`;\n c.fillText(text, 0, 0);\n if (handle) {\n c.fillStyle = ctx.theme.ink;\n c.font = `600 ${Math.round(fontPx * 0.5)}px ${ctx.theme.fontDisplay}`;\n c.fillText(handle, 0, fontPx * 0.9);\n }\n c.restore();\n\n // Bouncing chevron pointing toward the follow button.\n if (arrow) {\n const ax = cx;\n const ay = (arrowDir === 'down' ? cy + fontPx * 1.6 : cy - fontPx * 1.6) + bounce * dir;\n const s = fontPx * 0.4;\n c.save();\n c.globalAlpha = p;\n c.strokeStyle = col;\n c.lineWidth = 10;\n c.lineCap = 'round';\n c.lineJoin = 'round';\n c.beginPath();\n if (arrowDir === 'down') { c.moveTo(ax - s, ay - s * 0.6); c.lineTo(ax, ay + s * 0.6); c.lineTo(ax + s, ay - s * 0.6); }\n else { c.moveTo(ax - s, ay + s * 0.6); c.lineTo(ax, ay - s * 0.6); c.lineTo(ax + s, ay + s * 0.6); }\n c.stroke();\n c.restore();\n }\n },\n };\n}\n\nexport const endCardFactory: LayerFactory<EndCardProps> = {\n key: 'end-card',\n create: endCardLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['end-card: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n for (const k of ['text', 'handle', 'color'] as const) if (p[k] != null && typeof p[k] !== 'string') errs.push(`end-card.${k} must be a string`);\n if (p.startMs != null && typeof p.startMs !== 'number') errs.push('end-card.startMs must be a number');\n if (p.inMs != null && (typeof p.inMs !== 'number' || p.inMs <= 0)) errs.push('end-card.inMs must be a positive number');\n if (p.centerFrac != null && (typeof p.centerFrac !== 'number' || p.centerFrac < 0 || p.centerFrac > 1)) errs.push('end-card.centerFrac must be in [0,1]');\n if (p.fontPx != null && (typeof p.fontPx !== 'number' || p.fontPx <= 0)) errs.push('end-card.fontPx must be a positive number');\n if (p.arrow != null && typeof p.arrow !== 'boolean') errs.push('end-card.arrow must be a boolean');\n if (p.arrowDir != null && p.arrowDir !== 'up' && p.arrowDir !== 'down') errs.push('end-card.arrowDir must be \"up\" | \"down\"');\n return errs;\n },\n};\n","// `image-reveal` layer — a scramble-and-reveal animation for \"Name That Painting\"\n// guessing videos. At p=0 the image is fully obscured; at p=1 it is clean.\n// Three modes: pixelate (blocky downscale→upscale), blur (CSS filter with a\n// progressive-downscale fallback), shuffle (tile grid with deterministic offsets).\n//\n// The image is fit (preserve aspect, letterbox) into ctx.safeBox. All modes are\n// pure functions of tMs — no wall-clock / Math.random per frame.\n\nimport type { Layer, LayerFactory, RenderCtx } from '../layer';\n\nexport type ImageRevealMode = 'pixelate' | 'shuffle' | 'blur';\n\n/**\n * Reveal pacing curve applied to linear progress before it drives the effect.\n * - 'linear' : constant rate (default — unchanged behavior).\n * - 'easeOut' : fast at first, SLOWER near the end (quadratic).\n * - 'easeOutStrong' : like easeOut but a more DRAMATIC, drawn-out final reveal (cubic).\n * - 'easeInOut' : slow start (stays obscured), then a slow settle at the end.\n */\nexport type RevealEasing = 'linear' | 'easeOut' | 'easeOutStrong' | 'easeInOut';\n\n/** Map linear progress p∈[0,1] through the chosen easing curve. Exported for tests. */\nexport function applyEasing(p: number, easing: RevealEasing): number {\n if (easing === 'easeOut') return 1 - (1 - p) * (1 - p); // quadratic ease-out\n if (easing === 'easeOutStrong') return 1 - (1 - p) * (1 - p) * (1 - p); // cubic ease-out (more dramatic end)\n if (easing === 'easeInOut') return p * p * (3 - 2 * p); // smoothstep\n return p; // linear\n}\n\nexport interface ImageRevealProps {\n /** The image to reveal. Any CanvasImageSource (HTMLImageElement, OffscreenCanvas, …). */\n image: CanvasImageSource;\n /** Reveal mode. Default 'pixelate'. */\n mode?: ImageRevealMode;\n /** When the reveal starts (ms). Default 0. */\n startMs?: number;\n /** How long the full reveal takes (ms). Default 6000. */\n durationMs?: number;\n /**\n * Difficulty scalar 0..1.\n * - pixelate: controls maximum block size (0→small, 1→large; default 0.5 ≈ 1/8 width).\n * - shuffle: controls maximum tile offset fraction (default 0.5).\n * - blur: controls maximum blur radius in px (0→mild, 1→extreme; default 0.5 ≈ 40px).\n */\n difficulty?: number;\n /** Pacing curve for the reveal. Default 'linear'. See RevealEasing. */\n easing?: RevealEasing;\n}\n\nconst DEFAULTS = {\n mode: 'pixelate' as ImageRevealMode,\n startMs: 0,\n durationMs: 6000,\n difficulty: 0.5,\n easing: 'linear' as RevealEasing,\n};\n\nfunction clamp01(x: number): number { return x < 0 ? 0 : x > 1 ? 1 : x; }\n\n// ─── Letterbox fit helper ─────────────────────────────────────────────────────\n\ninterface FitRect { dx: number; dy: number; dw: number; dh: number }\n\n/** Fit the image (iW×iH) into the box (bw×bh at bx,by), preserving aspect ratio.\n * Returns the destination rect to pass to drawImage. */\nfunction fitRect(iW: number, iH: number, bx: number, by: number, bw: number, bh: number): FitRect {\n const scale = Math.min(bw / iW, bh / iH);\n const dw = iW * scale;\n const dh = iH * scale;\n return { dx: bx + (bw - dw) / 2, dy: by + (bh - dh) / 2, dw, dh };\n}\n\n// ─── Pixelate mode ───────────────────────────────────────────────────────────\n\n/** Returns the block size (integer) for a given progress p and max block size. */\nfunction blockSizeAt(p: number, maxBlock: number): number {\n // Linearly interpolate from maxBlock→1 as p goes 0→1.\n // Use Math.max(1,…) so we never try to draw 0-pixel blocks.\n return Math.max(1, Math.round(maxBlock * (1 - p) + 1 * p));\n}\n\n// ─── Shuffle mode ────────────────────────────────────────────────────────────\n\n/** Tiny deterministic integer hash seeded by the tile index. Returns a value in\n * [0, 1). Used to derive stable per-tile offsets across frames. */\nfunction tileHash(i: number): number {\n // Xorshift-based — fast, deterministic, no Math.random per frame.\n let x = (i * 2654435761) >>> 0;\n x ^= x << 13;\n x ^= x >>> 17;\n x ^= x << 5;\n return (x >>> 0) / 0xffffffff;\n}\n\n/** Signed [-1,1) hash for a tile seeded by index + salt. */\nfunction tileHashSigned(i: number, salt: number): number {\n return tileHash((i * 1013904223 + salt * 22695477) | 0) * 2 - 1;\n}\n\n// ─── Layer factory function ───────────────────────────────────────────────────\n\nfunction imageRevealLayer(): Layer<ImageRevealProps> {\n let mode: ImageRevealMode = DEFAULTS.mode;\n let startMs = DEFAULTS.startMs;\n let durationMs = DEFAULTS.durationMs;\n let difficulty = DEFAULTS.difficulty;\n let easing: RevealEasing = DEFAULTS.easing;\n let image: CanvasImageSource | null = null;\n\n // Offscreen canvas for pixelate mode (reused across frames, resized when needed).\n let offscreen: OffscreenCanvas | HTMLCanvasElement | null = null;\n let offCtx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null = null;\n\n // Cached image natural dimensions (read once in init).\n let imgW = 0;\n let imgH = 0;\n\n function ensureOffscreen(w: number, h: number): OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null {\n const needNew = !offscreen || offscreen.width !== w || offscreen.height !== h;\n if (needNew) {\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(w, h);\n offCtx = offscreen.getContext('2d') as OffscreenCanvasRenderingContext2D;\n } else {\n // Fallback for Node/test env that lacks OffscreenCanvas.\n const el = typeof document !== 'undefined'\n ? document.createElement('canvas')\n : null;\n if (!el) { offscreen = null; offCtx = null; return null; }\n el.width = w;\n el.height = h;\n offscreen = el;\n offCtx = el.getContext('2d');\n }\n }\n return offCtx;\n }\n\n return {\n key: 'image-reveal',\n\n init(_ctx: RenderCtx, props: ImageRevealProps) {\n mode = props.mode ?? DEFAULTS.mode;\n startMs = props.startMs ?? DEFAULTS.startMs;\n durationMs = props.durationMs ?? DEFAULTS.durationMs;\n difficulty = props.difficulty ?? DEFAULTS.difficulty;\n easing = props.easing ?? DEFAULTS.easing;\n image = props.image;\n\n // Read image dimensions. CanvasImageSource covers HTMLImageElement\n // (naturalWidth/naturalHeight), OffscreenCanvas / HTMLCanvasElement\n // (width/height), ImageBitmap (width/height), and VideoFrame (displayWidth).\n // Cast through unknown first to avoid TS overlap errors with VideoFrame.\n const src = props.image as unknown as Record<string, unknown>;\n imgW =\n (src['naturalWidth'] as number | undefined) ??\n (src['displayWidth'] as number | undefined) ??\n (src['width'] as number | undefined) ??\n 0;\n imgH =\n (src['naturalHeight'] as number | undefined) ??\n (src['displayHeight'] as number | undefined) ??\n (src['height'] as number | undefined) ??\n 0;\n },\n\n draw(ctx: RenderCtx, tMs: number) {\n if (!image || imgW === 0 || imgH === 0) return;\n\n const raw = clamp01((tMs - startMs) / (durationMs > 0 ? durationMs : 1));\n const p = applyEasing(raw, easing);\n const sb = ctx.safeBox;\n const fit = fitRect(imgW, imgH, sb.left, sb.top, sb.w, sb.h);\n\n const c = ctx.ctx2d;\n c.save();\n\n // Clip to the destination rect so bleed from scaled images stays inside.\n c.beginPath();\n c.rect(fit.dx, fit.dy, fit.dw, fit.dh);\n c.clip();\n\n if (mode === 'pixelate') {\n drawPixelate(c, image, fit, p, difficulty, imgW, imgH, ensureOffscreen);\n } else if (mode === 'blur') {\n drawBlur(c, image, fit, p, difficulty, imgW, imgH, ensureOffscreen);\n } else {\n // shuffle\n drawShuffle(c, image, fit, p, difficulty, imgW, imgH);\n }\n\n c.restore();\n },\n\n dispose() {\n image = null;\n offscreen = null;\n offCtx = null;\n },\n };\n}\n\n// ─── Mode draw functions (module-level to keep the layer closure lean) ────────\n\nfunction drawPixelate(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n getOffCtx: (w: number, h: number) => OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null,\n): void {\n // Maximum block size scales with difficulty (0→1/32 width, 1→1/4 width).\n const maxBlock = Math.max(2, Math.round(fit.dw * (0.03125 + difficulty * 0.21875)));\n const blockSize = blockSizeAt(p, maxBlock);\n\n if (blockSize <= 1) {\n // p ≈ 1: draw the clean image directly.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n return;\n }\n\n // Downscale to blockSize resolution, then upscale with smoothing disabled.\n const smallW = Math.max(1, Math.ceil(fit.dw / blockSize));\n const smallH = Math.max(1, Math.ceil(fit.dh / blockSize));\n const oc = getOffCtx(smallW, smallH);\n if (!oc) {\n // Fallback: just draw the image clean if we can't get an offscreen ctx.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n return;\n }\n\n // Draw the source image scaled DOWN to the tiny offscreen.\n oc.clearRect(0, 0, smallW, smallH);\n oc.drawImage(image, 0, 0, imgW, imgH, 0, 0, smallW, smallH);\n\n // Draw the tiny offscreen BACK up to the destination, pixelated.\n const prev = c.imageSmoothingEnabled;\n c.imageSmoothingEnabled = false;\n const off = oc.canvas as CanvasImageSource;\n c.drawImage(off, 0, 0, smallW, smallH, fit.dx, fit.dy, fit.dw, fit.dh);\n c.imageSmoothingEnabled = prev;\n}\n\nfunction drawBlur(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n getOffCtx: (w: number, h: number) => OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null,\n): void {\n // Max blur in px: difficulty 0→8px, 0.5→40px, 1→80px.\n const maxBlur = 8 + difficulty * 72;\n const blurPx = maxBlur * (1 - p);\n\n // Try CSS filter blur (available in all modern browsers + headless Chrome).\n // The 'filter' property exists on CanvasRenderingContext2D in the spec but\n // may not be supported everywhere (e.g. old Safari, some test stubs).\n const cAny = c as unknown as Record<string, unknown>;\n const hasFilter = 'filter' in c && cAny['filter'] !== undefined;\n\n if (hasFilter && blurPx > 0) {\n const prev = cAny['filter'] as string;\n cAny['filter'] = `blur(${blurPx.toFixed(1)}px)`;\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n cAny['filter'] = prev ?? 'none';\n } else if (blurPx > 0) {\n // Progressive-downscale fallback: repeatedly halve the image down and up\n // to simulate blur without CSS filter. Each halving pass adds ~1 stage of\n // approximated blur (trilinear mipmap style).\n const passes = Math.min(6, Math.round(blurPx / 8));\n if (passes > 0) {\n let w = Math.max(1, Math.round(fit.dw / Math.pow(2, passes)));\n let h = Math.max(1, Math.round(fit.dh / Math.pow(2, passes)));\n const oc = getOffCtx(w, h);\n if (oc) {\n oc.clearRect(0, 0, w, h);\n oc.drawImage(image, 0, 0, imgW, imgH, 0, 0, w, h);\n // Upscale back to dest.\n c.drawImage(oc.canvas as CanvasImageSource, 0, 0, w, h, fit.dx, fit.dy, fit.dw, fit.dh);\n } else {\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n } else {\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n } else {\n // blurPx === 0: clean image.\n c.drawImage(image, 0, 0, imgW, imgH, fit.dx, fit.dy, fit.dw, fit.dh);\n }\n}\n\nfunction drawShuffle(\n c: CanvasRenderingContext2D,\n image: CanvasImageSource,\n fit: FitRect,\n p: number,\n difficulty: number,\n imgW: number,\n imgH: number,\n): void {\n // Grid dimensions: ~8 cols × 14 rows (roughly square tiles at 9:16).\n const cols = 8;\n const rows = 14;\n const tileW = fit.dw / cols;\n const tileH = fit.dh / rows;\n const srcTileW = imgW / cols;\n const srcTileH = imgH / rows;\n\n // Maximum offset: tiles can slide up to (difficulty × 1.5 × tileSize) from home.\n const maxOffX = tileW * difficulty * 1.5;\n const maxOffY = tileH * difficulty * 1.5;\n\n // Eased position: at p=0 full offset, at p=1 at home (offset=0).\n // Use ease-out so tiles snap home toward the end.\n const ease = 1 - (1 - p) * (1 - p); // easeOut\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const i = row * cols + col;\n\n // Stable per-tile displacement seeded by tile index (deterministic across frames).\n const offX = tileHashSigned(i, 0) * maxOffX * (1 - ease);\n const offY = tileHashSigned(i, 1) * maxOffY * (1 - ease);\n\n // Source tile from the image.\n const sx = col * srcTileW;\n const sy = row * srcTileH;\n\n // Destination tile home position + offset.\n const dxHome = fit.dx + col * tileW;\n const dyHome = fit.dy + row * tileH;\n const dx = dxHome + offX;\n const dy = dyHome + offY;\n\n c.drawImage(\n image,\n sx, sy, srcTileW, srcTileH,\n dx, dy, tileW, tileH,\n );\n }\n }\n}\n\n// ─── Factory & validation ─────────────────────────────────────────────────────\n\nconst VALID_MODES: ImageRevealMode[] = ['pixelate', 'shuffle', 'blur'];\nconst VALID_EASINGS: RevealEasing[] = ['linear', 'easeOut', 'easeOutStrong', 'easeInOut'];\n\nexport const imageRevealFactory: LayerFactory<ImageRevealProps> = {\n key: 'image-reveal',\n create: imageRevealLayer,\n validateProps(props: unknown): string[] {\n if (props == null || typeof props !== 'object') return ['image-reveal: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n\n if (p['image'] == null) errs.push('image-reveal.image is required');\n\n if (p['mode'] != null && !VALID_MODES.includes(p['mode'] as ImageRevealMode))\n errs.push(`image-reveal.mode must be one of: ${VALID_MODES.join(', ')}`);\n\n if (p['startMs'] != null && typeof p['startMs'] !== 'number')\n errs.push('image-reveal.startMs must be a number');\n\n if (p['durationMs'] != null && (typeof p['durationMs'] !== 'number' || (p['durationMs'] as number) <= 0))\n errs.push('image-reveal.durationMs must be a positive number');\n\n if (p['difficulty'] != null && (typeof p['difficulty'] !== 'number' || (p['difficulty'] as number) < 0 || (p['difficulty'] as number) > 1))\n errs.push('image-reveal.difficulty must be a number in [0, 1]');\n\n if (p['easing'] != null && !VALID_EASINGS.includes(p['easing'] as RevealEasing))\n errs.push(`image-reveal.easing must be one of: ${VALID_EASINGS.join(', ')}`);\n\n return errs;\n },\n};\n","// `composite-rhythm` layer (E7M-300) — the ONE new visual the \"Hand\n// Independence\" series needs: tick marks for each hand's onsets plus the\n// MERGED attack-point line underneath, all read off the SAME Score that drives\n// the keyboard/falling-notes/audio. This is the shot that carries the series'\n// thesis (\"one plan, two outputs\") — it must be legible at 1080×1920 on a\n// phone: clear ticks, an unmistakable composite mark, no clutter.\n//\n// Three rows, top to bottom: optional reference `grids` (faint, illustrate a\n// notated subdivision like \"3 against 2\"), the LH tick row, the bold MERGED\n// row (a filled dot per composite attack, split teal/gold when both hands\n// land within `mergeWindowMs` of each other — literally \"two outputs, one\n// attack\"), and the RH tick row. A thin spine connects consecutive merged\n// marks so the composite reads as ONE line, not a scatter of dots.\n//\n// No invented \"drift\"/\"stagger\" animation here — if a script wants two hands\n// visibly out of sync, that's baked into the SCORE's own onset timing (real\n// data), and this layer (and the keyboard) just draw it truthfully. Pure fn of\n// tMs; reads ctx.score.notes (onsetMs + hand). Draws in world space (the route\n// runs an identity camera, so world == screen).\n\nimport type { Layer, LayerFactory } from '../layer';\nimport type { HandColors } from '../keyboardGeometry';\n\nexport interface CompositeRhythmProps {\n /** Time window mapped across the row width. Default: [min onset, score.durationMs]. */\n windowMs?: [number, number];\n /** Vertical center of the MERGED row (world px). Default safeBox.top + 0.16*safeBox.h. */\n y?: number;\n /** Gap between the LH / merged / RH rows (world px). Default 40. */\n rowGap?: number;\n /** Tick mark half-height for the LH/RH rows (world px). Default 16. */\n tickH?: number;\n /** Merged-mark radius (world px). Default 12. */\n mergeR?: number;\n /** Reference subdivision rows, drawn as faint rows ABOVE the LH row. Two\n * forms per entry: a plain number N divides `windowMs` into N EQUAL ticks\n * (e.g. `3` for a triplet feel, `2` for straight eighths — a 3-against-2\n * bar is `grids: [3, 2]`); an array of positive weights draws ticks at\n * each GROUP's start per those relative durations (e.g. `[3, 3, 2]` for a\n * 3-3-2 clave bar — ticks at fractions 0, 3/8, 6/8, labeled \"3+3+2\") — for\n * asymmetric groupings the rhythm montage needs that an equal-N grid can't\n * represent. Default none. */\n grids?: Array<number | number[]>;\n /** Onsets within this many ms of each other collapse into ONE composite\n * mark. Default 60. */\n mergeWindowMs?: number;\n /** Hand colours (also used for the merged mark's two-tone split). Default\n * theme.accent (R) / theme.gold (L). */\n handColors?: HandColors;\n /** Merged-mark outline / spine colour. Default theme.ink. */\n mergeColor?: string;\n /** Draw the connecting spine through consecutive merged marks. Default true. */\n spine?: boolean;\n /** Sweeping playhead line at the current tMs. Default true. */\n playhead?: boolean;\n /** Progressive left-to-right reveal: a mark at x-fraction `f` across the\n * window only appears once `tMs >= revealStartMs + f*revealDurationMs`.\n * Omit either to show everything always (default). */\n revealStartMs?: number;\n revealDurationMs?: number;\n}\n\ninterface MergedPoint {\n tMs: number;\n hands: Set<'L' | 'R'>;\n}\n\n/** Collapse onsets within `gapMs` of each other into merged attack points. */\nfunction mergeOnsets(notes: { onsetMs: number; hand: 'L' | 'R' }[], gapMs: number): MergedPoint[] {\n const sorted = [...notes].sort((a, b) => a.onsetMs - b.onsetMs);\n const out: MergedPoint[] = [];\n for (const n of sorted) {\n const last = out[out.length - 1];\n if (last && n.onsetMs - last.tMs <= gapMs) {\n last.hands.add(n.hand);\n } else {\n out.push({ tMs: n.onsetMs, hands: new Set([n.hand]) });\n }\n }\n return out;\n}\n\nfunction compositeRhythmLayer(): Layer<CompositeRhythmProps> {\n let win: [number, number] = [0, 1000];\n let yCenter = 0;\n let rowGap = 40;\n let tickH = 16;\n let mergeR = 12;\n let grids: Array<number | number[]> = [];\n let mergeWindowMs = 60;\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let mergeColor = '#1a1614';\n let spine = true;\n let playhead = true;\n let revealStartMs: number | undefined;\n let revealDurationMs: number | undefined;\n let merged: MergedPoint[] = [];\n let lhTicks: number[] = [];\n let rhTicks: number[] = [];\n\n return {\n key: 'composite-rhythm',\n init(ctx, props) {\n rowGap = props.rowGap ?? 40;\n tickH = props.tickH ?? 16;\n mergeR = props.mergeR ?? 12;\n grids = props.grids ?? [];\n mergeWindowMs = props.mergeWindowMs ?? 60;\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n mergeColor = props.mergeColor ?? ctx.theme.ink;\n spine = props.spine ?? true;\n playhead = props.playhead ?? true;\n revealStartMs = props.revealStartMs;\n revealDurationMs = props.revealDurationMs;\n\n const notes = ctx.score?.notes ?? [];\n const onsets = notes.map((n) => n.onsetMs);\n const w0 = props.windowMs?.[0] ?? (onsets.length ? Math.min(...onsets) : 0);\n const w1 = props.windowMs?.[1] ?? (ctx.score?.durationMs ?? Math.max(1000, ...onsets, 0));\n win = [w0, Math.max(w0 + 1, w1)];\n\n merged = mergeOnsets(notes.map((n) => ({ onsetMs: n.onsetMs, hand: n.hand })), mergeWindowMs);\n lhTicks = notes.filter((n) => n.hand === 'L').map((n) => n.onsetMs);\n rhTicks = notes.filter((n) => n.hand === 'R').map((n) => n.onsetMs);\n\n const sb = ctx.safeBox;\n yCenter = props.y ?? sb.top + sb.h * 0.16;\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const [w0, w1] = win;\n const x = (t: number): number => sb.left + ((t - w0) / (w1 - w0)) * sb.w;\n\n const revealed = (t: number): boolean => {\n if (revealStartMs == null || revealDurationMs == null) return true;\n const f = Math.min(1, Math.max(0, (t - w0) / (w1 - w0)));\n return tMs >= revealStartMs + f * revealDurationMs;\n };\n // Cull anything outside the displayed window — `windowMs` is a real\n // visible SLICE (e.g. one polyrhythm cycle out of a longer score), not\n // just an x-axis scale, so a note before/after it must not draw off the\n // safe box edges.\n const inWin = (t: number): boolean => t >= w0 && t <= w1;\n\n const lhY = yCenter - rowGap;\n const rhY = yCenter + rowGap;\n\n c.save();\n\n // Reference subdivision grids, faint rows stacked above the LH row.\n // A plain number N = N equal-tick starts; a weight array (e.g. [3,3,2])\n // = one tick at each GROUP's start, per those relative durations —\n // draws an asymmetric grouping (3-3-2 and its cousins) truthfully\n // instead of forcing it into an equal-N grid it doesn't fit.\n grids.forEach((entry, gi) => {\n const isGrouping = Array.isArray(entry);\n const weights = isGrouping ? entry.filter((w) => w > 0) : [];\n if (isGrouping && weights.length === 0) return;\n if (!isGrouping && entry <= 0) return;\n const gy = lhY - rowGap * (grids.length - gi);\n c.strokeStyle = 'rgba(26,22,20,0.22)';\n c.lineWidth = 2;\n c.beginPath();\n c.moveTo(sb.left, gy);\n c.lineTo(sb.left + sb.w, gy);\n c.stroke();\n\n const starts: number[] = [];\n if (isGrouping) {\n const total = weights.reduce((a, b) => a + b, 0);\n let acc = 0;\n for (const w of weights) { starts.push(acc / total); acc += w; }\n } else {\n const n = entry as number;\n for (let i = 0; i < n; i++) starts.push(i / n);\n }\n for (const f of starts) {\n const t = w0 + (w1 - w0) * f;\n if (!revealed(t)) continue;\n const gx = x(t);\n c.strokeStyle = 'rgba(26,22,20,0.38)';\n c.lineWidth = 3;\n c.beginPath();\n c.moveTo(gx, gy - tickH * 0.5);\n c.lineTo(gx, gy + tickH * 0.5);\n c.stroke();\n }\n // Small label at the row's left: \"3\" for an equal grid, \"3+3+2\" for a\n // weighted grouping, so the row reads unambiguously either way.\n c.fillStyle = 'rgba(26,22,20,0.55)';\n c.font = '600 22px system-ui, sans-serif';\n c.textAlign = 'right';\n c.textBaseline = 'middle';\n c.fillText(isGrouping ? weights.join('+') : String(entry), sb.left - 10, gy);\n });\n\n // LH / RH tick rows.\n const drawTicks = (ticks: number[], y: number, color: string) => {\n c.fillStyle = color;\n for (const t of ticks) {\n if (!inWin(t) || !revealed(t)) continue;\n const tx = x(t);\n c.fillRect(tx - 3, y - tickH * 0.5, 6, tickH);\n }\n };\n drawTicks(lhTicks, lhY, hands.L);\n drawTicks(rhTicks, rhY, hands.R);\n c.font = '600 22px system-ui, sans-serif';\n c.textAlign = 'right';\n c.textBaseline = 'middle';\n c.fillStyle = hands.L;\n c.fillText('L', sb.left - 10, lhY);\n c.fillStyle = hands.R;\n c.fillText('R', sb.left - 10, rhY);\n\n // Composite (merged) row: the spine first (so marks sit on top), then\n // each attack as a filled circle — two-tone split when both hands\n // landed together, solid hand-colour when only one did.\n const visibleMerged = merged.filter((m) => inWin(m.tMs) && revealed(m.tMs));\n if (spine && visibleMerged.length > 1) {\n c.strokeStyle = mergeColor;\n c.lineWidth = 3;\n c.beginPath();\n visibleMerged.forEach((m, i) => {\n const mx = x(m.tMs);\n if (i === 0) c.moveTo(mx, yCenter); else c.lineTo(mx, yCenter);\n });\n c.stroke();\n }\n for (const m of visibleMerged) {\n const mx = x(m.tMs);\n const both = m.hands.has('L') && m.hands.has('R');\n if (both) {\n c.beginPath();\n c.arc(mx, yCenter, mergeR, -Math.PI / 2, Math.PI / 2);\n c.fillStyle = hands.R;\n c.fill();\n c.beginPath();\n c.arc(mx, yCenter, mergeR, Math.PI / 2, -Math.PI / 2);\n c.fillStyle = hands.L;\n c.fill();\n } else {\n c.beginPath();\n c.arc(mx, yCenter, mergeR, 0, Math.PI * 2);\n c.fillStyle = m.hands.has('R') ? hands.R : hands.L;\n c.fill();\n }\n c.lineWidth = 2;\n c.strokeStyle = mergeColor;\n c.beginPath();\n c.arc(mx, yCenter, mergeR, 0, Math.PI * 2);\n c.stroke();\n }\n\n // Sweeping playhead.\n if (playhead && tMs >= w0 && tMs <= w1) {\n const px = x(tMs);\n const topY = grids.length ? lhY - rowGap * grids.length - tickH : lhY - tickH;\n const botY = rhY + tickH;\n c.strokeStyle = mergeColor;\n c.globalAlpha = 0.5;\n c.lineWidth = 2;\n c.beginPath();\n c.moveTo(px, topY);\n c.lineTo(px, botY);\n c.stroke();\n c.globalAlpha = 1;\n }\n\n c.restore();\n },\n\n dispose() {\n merged = [];\n lhTicks = [];\n rhTicks = [];\n },\n };\n}\n\nexport const compositeRhythmFactory: LayerFactory<CompositeRhythmProps> = {\n key: 'composite-rhythm',\n create: compositeRhythmLayer,\n validateProps(props) {\n const errs: string[] = [];\n if (props == null || typeof props !== 'object') return ['composite-rhythm: props must be an object'];\n const p = props as Record<string, unknown>;\n if (p.windowMs != null) {\n if (!Array.isArray(p.windowMs) || p.windowMs.length !== 2 || p.windowMs.some((v) => typeof v !== 'number'))\n errs.push('composite-rhythm.windowMs must be [number, number]');\n }\n if (p.y != null && typeof p.y !== 'number') errs.push('composite-rhythm.y must be a number');\n if (p.rowGap != null && (typeof p.rowGap !== 'number' || p.rowGap <= 0)) errs.push('composite-rhythm.rowGap must be a positive number');\n if (p.tickH != null && (typeof p.tickH !== 'number' || p.tickH <= 0)) errs.push('composite-rhythm.tickH must be a positive number');\n if (p.mergeR != null && (typeof p.mergeR !== 'number' || p.mergeR <= 0)) errs.push('composite-rhythm.mergeR must be a positive number');\n if (p.grids != null) {\n const gridsValid = Array.isArray(p.grids) && p.grids.every((v) => {\n if (typeof v === 'number') return v > 0;\n if (Array.isArray(v)) return v.length > 0 && v.every((w) => typeof w === 'number' && w > 0);\n return false;\n });\n if (!gridsValid) errs.push('composite-rhythm.grids must be an array of positive numbers or arrays of positive-number weights');\n }\n if (p.mergeWindowMs != null && (typeof p.mergeWindowMs !== 'number' || p.mergeWindowMs < 0))\n errs.push('composite-rhythm.mergeWindowMs must be a non-negative number');\n if (p.mergeColor != null && typeof p.mergeColor !== 'string') errs.push('composite-rhythm.mergeColor must be a string');\n if (p.spine != null && typeof p.spine !== 'boolean') errs.push('composite-rhythm.spine must be a boolean');\n if (p.playhead != null && typeof p.playhead !== 'boolean') errs.push('composite-rhythm.playhead must be a boolean');\n if (p.revealStartMs != null && typeof p.revealStartMs !== 'number') errs.push('composite-rhythm.revealStartMs must be a number');\n if (p.revealDurationMs != null && (typeof p.revealDurationMs !== 'number' || p.revealDurationMs <= 0))\n errs.push('composite-rhythm.revealDurationMs must be a positive number');\n return errs;\n },\n};\n","// `hand-indicator` layer (E7M-300 v3) — two stylised hand silhouettes pinned\n// BELOW the keyboard strip: LH under the lower (bass) register, RH under the\n// upper (treble) register. Each hand lights up (a solid fill + a soft glow)\n// the instant that hand ATTACKS a note and fades back to a faint outline over\n// `fadeMs` once it goes quiet — the SAME LH/RH theme tokens the keyboard keys\n// already use (handColors), so the mapping between \"a hand lights up\" and\n// \"that hand plays\" is instantly readable. This is the shot that makes \"one\n// motor plan, two outputs\" legible without reading a word: watch the hands\n// alone and see the interlock.\n//\n// Pure fn of tMs — reads ctx.score.notes (onsetMs/durMs/hand), the SAME\n// source of truth as keyboard.ts and compositeRhythm.ts. Fixed x position per\n// hand (not tied to the actual note's pitch/x), since a script's LH/RH\n// geography can vary (e.g. a wide-leap voicing crosses register) — the fixed\n// left/right split mirrors the series' own convention (LH reads on the left,\n// RH on the right) and matches where a viewer's eye already expects each\n// hand's colour to live.\n//\n// Deliberately simple: a filled \"mitten\" silhouette (palm + 4 rounded finger\n// stubs + a thumb bump, mirrored L/R) drawn with canvas primitives — a\n// graphic, not a photoreal render. The caller (the route composing this\n// layer) owns the vertical geometry (`y`/`height`) so it can keep this band\n// entirely inside the safe box, below the keyboard, without crowding the\n// composite-rhythm band far above it.\n\nimport type { Layer, LayerFactory } from '../layer';\nimport type { HandColors } from '../keyboardGeometry';\n\nexport interface HandIndicatorProps {\n /** Top of the hand band, world px. Required — the caller owns this route's\n * vertical layout; this layer only draws at the given y. */\n y: number;\n /** Band height, world px. Default 60. */\n height?: number;\n /** Hand colours — the SAME tokens the keyboard/composite-rhythm layers use.\n * Default R = theme.accent, L = theme.gold. */\n handColors?: HandColors;\n /** How fast a hand fades from lit back to its resting outline after its\n * last active note ends, ms. Default 200 (the \"quickly\" in ~150-250ms). */\n fadeMs?: number;\n /** Icon size, px (roughly the glyph's bounding box). Default height*0.78. */\n size?: number;\n /** LH icon x, fraction of safeBox width from its left edge. Default 0.26\n * (under the lower/bass register). */\n leftXFrac?: number;\n /** RH icon x, fraction of safeBox width from its left edge. Default 0.74\n * (under the upper/treble register). */\n rightXFrac?: number;\n /** Baseline (resting/unlit) outline alpha. Default 0.32. */\n restAlpha?: number;\n}\n\ninterface HandNote {\n onsetMs: number;\n durMs: number;\n}\n\n/** 1 = a note for this hand is sounding right now; ramps 1→0 linearly over\n * `fadeMs` after the most recent note's release; 0 (resting) before the\n * first note or once the fade has fully elapsed. Pure fn of tMs. */\nfunction activeOpacity(notes: HandNote[], tMs: number, fadeMs: number): number {\n let lastEnd = -Infinity;\n for (const n of notes) {\n const end = n.onsetMs + n.durMs;\n if (tMs >= n.onsetMs && tMs < end) return 1;\n if (end <= tMs && end > lastEnd) lastEnd = end;\n }\n if (lastEnd === -Infinity) return 0;\n const dt = tMs - lastEnd;\n if (dt >= fadeMs) return 0;\n return 1 - dt / fadeMs;\n}\n\n/** Rounded-rect path (falls back to a plain rect if roundRect is unavailable). */\nfunction roundedPath(c: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void {\n c.beginPath();\n const rr = (c as unknown as { roundRect?: (x: number, y: number, w: number, h: number, r: number) => void }).roundRect;\n if (typeof rr === 'function') { rr.call(c, x, y, w, h, r); return; }\n c.rect(x, y, w, h);\n}\n\n/** A simple stylised \"mitten\" hand glyph: a palm + 4 rounded finger stubs + a\n * thumb, centered at (cx, cy), `size` px tall. `mirror` flips the thumb to\n * the opposite side so an LH/RH pair reads as a natural facing pair, both\n * thumbs pointing inward toward the keyboard's center. `mode` either fills\n * (the \"lit\" pass) or strokes (the always-visible resting outline) each\n * sub-shape — a plain silhouette/outline, not a photoreal render. */\nfunction drawHandGlyph(\n c: CanvasRenderingContext2D,\n cx: number, cy: number, size: number,\n mirror: boolean, mode: 'fill' | 'stroke',\n): void {\n const paint = () => { if (mode === 'fill') c.fill(); else c.stroke(); };\n const palmW = size * 0.66, palmH = size * 0.56;\n const px0 = cx - palmW / 2, py0 = cy - palmH * 0.1;\n roundedPath(c, px0, py0, palmW, palmH, palmW * 0.32);\n paint();\n const fingerW = palmW * 0.17;\n const lens = [0.36, 0.46, 0.42, 0.32];\n for (let i = 0; i < 4; i++) {\n const fx = px0 + palmW * (0.12 + i * 0.25) - fingerW / 2;\n const fLen = size * lens[i];\n const fy = py0 - fLen + size * 0.06;\n roundedPath(c, fx, fy, fingerW, fLen, fingerW / 2);\n paint();\n }\n const thumbW = palmW * 0.24, thumbLen = size * 0.3;\n const sign = mirror ? -1 : 1;\n c.save();\n c.translate(cx + sign * palmW * 0.42, py0 + palmH * 0.32);\n c.rotate(sign * 0.55);\n roundedPath(c, -thumbW / 2, 0, thumbW, thumbLen, thumbW / 2);\n paint();\n c.restore();\n}\n\nfunction handIndicatorLayer(): Layer<HandIndicatorProps> {\n let y = 0;\n let height = 60;\n let hands: HandColors = { R: '#7b2436', L: '#c8a55b' };\n let fadeMs = 200;\n let size = 0;\n let leftXFrac = 0.26;\n let rightXFrac = 0.74;\n let restAlpha = 0.32;\n let lNotes: HandNote[] = [];\n let rNotes: HandNote[] = [];\n\n return {\n key: 'hand-indicator',\n init(ctx, props) {\n y = props.y;\n height = props.height ?? 60;\n hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };\n fadeMs = props.fadeMs ?? 200;\n size = props.size ?? height * 0.78; // leaves real clearance to the band's own top/bottom edges (no glow to buffer, but still real ink)\n leftXFrac = props.leftXFrac ?? 0.26;\n rightXFrac = props.rightXFrac ?? 0.74;\n restAlpha = props.restAlpha ?? 0.32;\n const notes = ctx.score?.notes ?? [];\n lNotes = notes.filter((n) => n.hand === 'L').map((n) => ({ onsetMs: n.onsetMs, durMs: n.durMs }));\n rNotes = notes.filter((n) => n.hand === 'R').map((n) => ({ onsetMs: n.onsetMs, durMs: n.durMs }));\n },\n\n draw(ctx, tMs) {\n const c = ctx.ctx2d;\n const sb = ctx.safeBox;\n const cy = y + height / 2;\n\n const drawHand = (frac: number, color: string, notes: HandNote[], mirror: boolean) => {\n const cx = sb.left + sb.w * frac;\n const op = activeOpacity(notes, tMs, fadeMs);\n\n // Always-visible resting outline, so the viewer knows a hand lives\n // here even between attacks.\n c.save();\n c.globalAlpha = restAlpha;\n c.strokeStyle = color;\n c.lineWidth = Math.max(1.5, size * 0.035);\n drawHandGlyph(c, cx, cy, size, mirror, 'stroke');\n c.restore();\n\n // Lit fill, opacity == activeOpacity, fading back to the resting\n // outline over `fadeMs`. Deliberately NO shadow/glow blur: this band\n // sits in a tight, gate-checked gap right under the keyboard (see\n // stave's layout.ts HAND_GAP_PX) — a blur halo would risk bleeding\n // into that gap exactly the way falling-notes' glow needed a much\n // bigger buffer elsewhere. A crisp alpha fade reads just as clearly\n // as \"lighting up\" without that risk.\n if (op > 0.01) {\n c.save();\n c.globalAlpha = op;\n c.fillStyle = color;\n drawHandGlyph(c, cx, cy, size, mirror, 'fill');\n c.restore();\n }\n };\n\n drawHand(leftXFrac, hands.L, lNotes, true);\n drawHand(rightXFrac, hands.R, rNotes, false);\n },\n\n dispose() {\n lNotes = [];\n rNotes = [];\n },\n };\n}\n\nexport const handIndicatorFactory: LayerFactory<HandIndicatorProps> = {\n key: 'hand-indicator',\n create: handIndicatorLayer,\n validateProps(props) {\n if (props == null || typeof props !== 'object') return ['hand-indicator: props must be an object'];\n const p = props as Record<string, unknown>;\n const errs: string[] = [];\n if (typeof p.y !== 'number') errs.push('hand-indicator.y is required and must be a number');\n if (p.height != null && (typeof p.height !== 'number' || p.height <= 0)) errs.push('hand-indicator.height must be a positive number');\n if (p.fadeMs != null && (typeof p.fadeMs !== 'number' || p.fadeMs <= 0)) errs.push('hand-indicator.fadeMs must be a positive number');\n if (p.size != null && (typeof p.size !== 'number' || p.size <= 0)) errs.push('hand-indicator.size must be a positive number');\n if (p.leftXFrac != null && typeof p.leftXFrac !== 'number') errs.push('hand-indicator.leftXFrac must be a number');\n if (p.rightXFrac != null && typeof p.rightXFrac !== 'number') errs.push('hand-indicator.rightXFrac must be a number');\n if (p.restAlpha != null && (typeof p.restAlpha !== 'number' || p.restAlpha < 0 || p.restAlpha > 1)) errs.push('hand-indicator.restAlpha must be a number in [0,1]');\n return errs;\n },\n};\n","// Layer registry (S1b) — maps a SceneSpec layer key (\"background\", \"caption\",\n// …) to its LayerFactory. The runner instantiates layers from here, and the\n// pre-render gate uses it to reject a spec that references an unknown layer or\n// passes invalid props (\"unknown layer/prop → fail fast\", spec gate check #5).\n//\n// S2+ register their real music layers (notation, falling-notes, …) by calling\n// registerLayer — they DON'T edit the runner.\n\nimport type { LayerFactory } from './layer';\nimport { backgroundFactory, captionFactory } from './layers/demo';\nimport { notationFactory } from './layers/notation';\nimport { scrollCursorFactory } from './layers/scrollCursor';\nimport { keyboardFactory } from './layers/keyboard';\nimport { fretboardFactory } from './layers/fretboard';\nimport { fallingNotesFactory } from './layers/fallingNotes';\nimport { hookFactory, revealFactory, ctaFactory, portraitFactory } from './layers/promoCards';\nimport { spectrumFactory } from './layers/spectrum';\nimport { brandingFactory, safeGuidesFactory } from './layers/branding';\nimport { staffKeyboardRayFactory } from './layers/staffKeyboardRay';\nimport { countingTrackFactory } from './layers/countingTrack';\nimport { degreeLabelsFactory } from './layers/degreeLabels';\nimport { functionalHarmonyFactory } from './layers/functionalHarmony';\nimport { mcqCardFactory } from './layers/mcqCard';\nimport { circleOfFifthsFactory } from './layers/circleOfFifths';\nimport { pitchContourFactory } from './layers/pitchContour';\nimport { sectionMinimapFactory } from './layers/sectionMinimap';\nimport { beatPulseFactory } from './layers/beatPulse';\nimport { chordRibbonFactory } from './layers/chordRibbon';\nimport { progressRingFactory } from './layers/progressRing';\nimport { radialSpectrumFactory } from './layers/radialSpectrum';\nimport { karaokeCaptionFactory } from './layers/karaokeCaption';\nimport { intervalArcsFactory } from './layers/intervalArcs';\nimport { kineticTextFactory } from './layers/kineticText';\nimport { countdownFactory } from './layers/countdown';\nimport { waveformFactory } from './layers/waveform';\nimport { scaleHighlightFactory } from './layers/scaleHighlight';\nimport { particleBurstFactory } from './layers/particleBurst';\nimport { tensionGraphFactory } from './layers/tensionGraph';\nimport { textureFactory } from './layers/texture';\nimport { statCounterFactory } from './layers/statCounter';\nimport { endCardFactory } from './layers/endCard';\nimport { imageRevealFactory } from './layers/imageReveal';\nimport { compositeRhythmFactory } from './layers/compositeRhythm';\nimport { handIndicatorFactory } from './layers/handIndicator';\n\nconst REGISTRY = new Map<string, LayerFactory<any>>(); // eslint-disable-line @typescript-eslint/no-explicit-any\n\n/** Register (or replace) a layer factory under its key. */\nexport function registerLayer(factory: LayerFactory<any>): void { // eslint-disable-line @typescript-eslint/no-explicit-any\n REGISTRY.set(factory.key, factory);\n}\n\n/** Look up a factory by key, or undefined if not registered. */\nexport function getLayerFactory(key: string): LayerFactory<any> | undefined { // eslint-disable-line @typescript-eslint/no-explicit-any\n return REGISTRY.get(key);\n}\n\n/** All registered layer keys (for diagnostics / gate messages). */\nexport function registeredKeys(): string[] {\n return [...REGISTRY.keys()];\n}\n\n// Built-in demo layers, registered on import so the runner is end-to-end.\nregisterLayer(backgroundFactory);\nregisterLayer(captionFactory);\n\n// S2 — the extracted music layers (notation + scrolling cursor).\nregisterLayer(notationFactory);\nregisterLayer(scrollCursorFactory);\n\n// S3 — falling-notes (Synthesia) + keyboard + fretboard.\nregisterLayer(keyboardFactory);\nregisterLayer(fretboardFactory);\nregisterLayer(fallingNotesFactory);\n\n// S4 — promo cards (hook/reveal/cta/portrait), spectrum, branding/safe-guides.\nregisterLayer(hookFactory);\nregisterLayer(revealFactory);\nregisterLayer(ctaFactory);\nregisterLayer(portraitFactory);\nregisterLayer(spectrumFactory);\nregisterLayer(brandingFactory);\nregisterLayer(safeGuidesFactory);\n\n// S5 — extended catalog (top-priority): staff↔keyboard ray, counting track +\n// bouncing ball, scale-degree/solfège labels, functional-harmony coloring.\nregisterLayer(staffKeyboardRayFactory);\nregisterLayer(countingTrackFactory);\nregisterLayer(degreeLabelsFactory);\nregisterLayer(functionalHarmonyFactory);\n\n// S5 — extended catalog (batch 2): MCQ poll card, circle-of-fifths, pitch-contour\n// line, section markers + minimap.\nregisterLayer(mcqCardFactory);\nregisterLayer(circleOfFifthsFactory);\nregisterLayer(pitchContourFactory);\nregisterLayer(sectionMinimapFactory);\n\n// S6 — music-video energy/teaching/retention primitives: onset-reactive pulse,\n// scrolling chord ribbon, circular progress ring.\nregisterLayer(beatPulseFactory);\nregisterLayer(chordRibbonFactory);\nregisterLayer(progressRingFactory);\n\n// S6 — next wave: radial spectrum, karaoke caption, interval arcs.\nregisterLayer(radialSpectrumFactory);\nregisterLayer(karaokeCaptionFactory);\nregisterLayer(intervalArcsFactory);\n\n// S6 — batch 3: kinetic hook text, countdown, waveform, scale/mode highlighter.\nregisterLayer(kineticTextFactory);\nregisterLayer(countdownFactory);\nregisterLayer(waveformFactory);\nregisterLayer(scaleHighlightFactory);\n\n// S6 — batch 4: particle burst, tension graph, texture finish, stat counter, end card.\nregisterLayer(particleBurstFactory);\nregisterLayer(tensionGraphFactory);\nregisterLayer(textureFactory);\nregisterLayer(statCounterFactory);\nregisterLayer(endCardFactory);\n\n// image-reveal — scramble-and-reveal for \"Name That Painting\" guessing videos.\nregisterLayer(imageRevealFactory);\n\n// composite-rhythm (E7M-300) — tick marks + the merged attack-point timeline,\n// for the \"Hand Independence\" keyboard-highlight shorts series.\nregisterLayer(compositeRhythmFactory);\n\n// hand-indicator (E7M-300 v3) — LH/RH silhouettes beneath the keyboard that\n// light up per-hand-attack, for the same series.\nregisterLayer(handIndicatorFactory);\n","// Audio utility layers (S1b) — count-in, click/metronome, ambient drone bed,\n// and a VO-duck helper. These EXTEND the existing promo sampler/mastering bus\n// (src/promo.ts createPromoSampler) rather than introducing a new audio stack:\n// each one computes a deterministic SCHEDULE (pure → unit-testable) and then\n// applies it to a Tone-like target (a synth/sampler + the audio context clock).\n//\n// \"Audio layers\" are not visual Layers — they have no per-frame draw. They are\n// scheduled ONCE at render start against the audio clock, alongside the Score's\n// note schedule, and the visual layers read the same clock. Keeping the schedule\n// pure lets the gate / tests assert event times without real audio (the spec's\n// \"audio schedule: mock the sampler, assert trigger times == onsets ± tol\").\n\n/* Tone is typed loose (`any`) exactly like promo.ts / audioHelpers.ts. */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** One scheduled audio event, in seconds RELATIVE to the schedule start. */\nexport interface AudioEvent {\n /** Offset from schedule start, seconds. */\n atSec: number;\n /** Pitch (note name or frequency) for tonal hits; omitted for noise clicks. */\n note?: string | number;\n /** Duration, seconds. Default short. */\n durSec?: number;\n /** Linear gain 0..1. Default 1. */\n gain?: number;\n /** Tag for tests/debugging: \"count\" | \"click\" | \"drone\". */\n kind: 'count' | 'click' | 'drone';\n}\n\n// ─── Count-in (3-2-1 + click) ────────────────────────────────────────────────\n\nexport interface CountInOpts {\n /** Beats to count (default 4 — a full bar of 4/4). */\n beats?: number;\n /** Tempo for the count, bpm. */\n bpm: number;\n /** Accent the downbeat (beat 1) with a higher pitch. Default true. */\n accentDownbeat?: boolean;\n}\n\n/**\n * The count-in schedule: one click per beat, starting at t=0, ending exactly on\n * the downbeat of bar 1 (i.e. the music starts at `beats * 60/bpm` seconds).\n * Pure.\n */\nexport function countInSchedule(opts: CountInOpts): AudioEvent[] {\n const beats = opts.beats ?? 4;\n const accent = opts.accentDownbeat ?? true;\n const beatSec = 60 / opts.bpm;\n const events: AudioEvent[] = [];\n for (let i = 0; i < beats; i++) {\n const isDownbeat = accent && i === 0;\n events.push({\n atSec: i * beatSec,\n note: isDownbeat ? 'C6' : 'C5',\n durSec: 0.05,\n gain: isDownbeat ? 1 : 0.7,\n kind: 'count',\n });\n }\n return events;\n}\n\n/** Seconds after t=0 that the music should start, for a given count-in. */\nexport function countInLeadSec(opts: CountInOpts): number {\n return (opts.beats ?? 4) * (60 / opts.bpm);\n}\n\n// ─── Click / metronome track ─────────────────────────────────────────────────\n\nexport interface ClickTrackOpts {\n bpm: number;\n /** Total duration to fill with clicks, seconds. */\n durationSec: number;\n /** Beats per bar, for downbeat accents. Default 4. */\n beatsPerBar?: number;\n /** Start offset, seconds. Default 0. */\n startSec?: number;\n}\n\n/** A click on every beat across `durationSec`, accenting each bar's downbeat. */\nexport function clickTrackSchedule(opts: ClickTrackOpts): AudioEvent[] {\n const beatSec = 60 / opts.bpm;\n const bpb = opts.beatsPerBar ?? 4;\n const start = opts.startSec ?? 0;\n const events: AudioEvent[] = [];\n const n = Math.floor(opts.durationSec / beatSec);\n for (let i = 0; i < n; i++) {\n const isDownbeat = i % bpb === 0;\n events.push({\n atSec: start + i * beatSec,\n note: isDownbeat ? 'C6' : 'C5',\n durSec: 0.03,\n gain: isDownbeat ? 0.6 : 0.4,\n kind: 'click',\n });\n }\n return events;\n}\n\n// ─── Ambient drone / pad bed ─────────────────────────────────────────────────\n\nexport interface DroneOpts {\n /** Root pitch of the pad (e.g. \"C2\"). */\n root: string;\n /** Pad duration, seconds. */\n durationSec: number;\n /** Add a perfect fifth above the root. Default true. */\n fifth?: boolean;\n /** Bed gain 0..1. Default 0.15 (sits well under the melody). */\n gain?: number;\n}\n\n/** A sustained root (+fifth) pad for the whole clip. */\nexport function droneSchedule(opts: DroneOpts): AudioEvent[] {\n const gain = opts.gain ?? 0.15;\n const events: AudioEvent[] = [\n { atSec: 0, note: opts.root, durSec: opts.durationSec, gain, kind: 'drone' },\n ];\n if (opts.fifth ?? true) {\n events.push({ atSec: 0, note: transposeFifth(opts.root), durSec: opts.durationSec, gain, kind: 'drone' });\n }\n return events;\n}\n\n/** Transpose a note name up a perfect fifth (7 semitones), keeping it simple\n * for the small set of roots a drone uses (naturals). Falls back to the input\n * if it can't parse. */\nfunction transposeFifth(note: string): string {\n const m = /^([A-G])(#|b)?(\\d)$/.exec(note);\n if (!m) return note;\n const order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];\n const pcMap: Record<string, number> = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };\n let pc = pcMap[m[1]] + (m[2] === '#' ? 1 : m[2] === 'b' ? -1 : 0);\n let oct = parseInt(m[3], 10);\n pc += 7;\n if (pc >= 12) {\n pc -= 12;\n oct += 1;\n }\n return `${order[pc]}${oct}`;\n}\n\n// ─── VO duck helper ──────────────────────────────────────────────────────────\n\nexport interface DuckWindow {\n /** Voice-over window start, seconds. */\n startSec: number;\n /** Voice-over window end, seconds. */\n endSec: number;\n}\n\n/**\n * Gain envelope for a music/bed bus that ducks under voice-over windows. Returns\n * the linear gain 0..1 at time `tSec`: `floor` inside a window (after a short\n * attack ramp), 1 outside (after a release ramp). Pure — feed it to a Tone\n * Gain's `.gain.value` per frame, or sample it to build a ramp automation.\n */\nexport function duckGainAt(\n windows: DuckWindow[],\n tSec: number,\n floor = 0.25,\n rampSec = 0.2,\n): number {\n for (const w of windows) {\n if (tSec >= w.startSec - rampSec && tSec <= w.endSec + rampSec) {\n // attack into the window, release out of it\n if (tSec < w.startSec) return lerpGain(1, floor, (tSec - (w.startSec - rampSec)) / rampSec);\n if (tSec > w.endSec) return lerpGain(floor, 1, (tSec - w.endSec) / rampSec);\n return floor;\n }\n }\n return 1;\n}\n\nfunction lerpGain(a: number, b: number, t: number): number {\n const k = t < 0 ? 0 : t > 1 ? 1 : t;\n return a + (b - a) * k;\n}\n\n// ─── Apply a schedule to a Tone target ───────────────────────────────────────\n\n/**\n * Trigger a computed schedule on a Tone instrument at `startTime` (audio-clock\n * seconds, e.g. `Tone.now()` or sampler.audioNow()). Thin glue over the existing\n * sampler/synth — no new mastering bus. Tonal events use triggerAttackRelease;\n * the instrument is whatever the caller wires into the promo mastering chain.\n *\n * Returns the number of events fired (handy for tests/gate sanity).\n */\nexport function applySchedule(\n instrument: { triggerAttackRelease: (note: any, dur: any, time?: any, velocity?: any) => void },\n schedule: AudioEvent[],\n startTime: number,\n): number {\n for (const ev of schedule) {\n if (ev.note == null) continue;\n instrument.triggerAttackRelease(\n ev.note,\n ev.durSec ?? 0.05,\n startTime + ev.atSec,\n ev.gain ?? 1,\n );\n }\n return schedule.length;\n}\n","// Camera / viewport director (S1b cross-cutting primitive).\n//\n// Generalizes RSR's followBox/crop/zoom: layers draw in WORLD coordinates\n// (the frame's natural pixel space); the camera maps a world rectangle onto the\n// W×H output viewport so the runner can pan to the active measure, zoom on a\n// chord, or Ken-Burns the whole frame. Nearly every later layer rides this.\n//\n// Pure math (no canvas) so it is exhaustively unit-testable; `applyToContext`\n// is the only canvas touchpoint and is a thin wrapper over setTransform/scale.\n\nimport { clamp, lerp, easeInOut, type Easing } from './math';\n\n/** A rectangle in WORLD coordinates (the layers' natural pixel space). */\nexport interface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** A camera pose: which world point sits at the viewport centre, and the zoom. */\nexport interface CameraState {\n /** World-space centre x. */\n cx: number;\n /** World-space centre y. */\n cy: number;\n /** Zoom factor (>1 = zoomed in). */\n zoom: number;\n}\n\n/** A 2D affine transform `[a,b,c,d,e,f]` (matches CanvasRenderingContext2D.setTransform). */\nexport type Affine = [number, number, number, number, number, number];\n\n/**\n * The world→viewport transform for a camera pose. We use a uniform scale = zoom\n * and translate so (cx,cy) world maps to (W/2,H/2) viewport.\n * world point (x,y) -> ( (x-cx)*zoom + W/2 , (y-cy)*zoom + H/2 ).\n */\nexport function cameraTransform(cam: CameraState, W: number, H: number): Affine {\n const s = cam.zoom;\n return [s, 0, 0, s, W / 2 - cam.cx * s, H / 2 - cam.cy * s];\n}\n\n/** Map a world point through a camera pose to viewport pixels. */\nexport function worldToViewport(\n cam: CameraState,\n W: number,\n H: number,\n x: number,\n y: number,\n): { x: number; y: number } {\n const [a, , , d, e, f] = cameraTransform(cam, W, H);\n return { x: a * x + e, y: d * y + f };\n}\n\n/**\n * The camera pose that frames `rect` to fill the W×H viewport (contain-fit:\n * whole rect visible, uniform zoom). `pad` (0..1) leaves margin around the rect.\n */\nexport function frameRect(rect: Rect, W: number, H: number, pad = 0): CameraState {\n const padded = (1 + Math.max(0, pad) * 2);\n const zoom = Math.min(W / (rect.w * padded), H / (rect.h * padded));\n return { cx: rect.x + rect.w / 2, cy: rect.y + rect.h / 2, zoom };\n}\n\n/** Interpolate between two camera poses (for pan/zoom moves). */\nexport function lerpCamera(a: CameraState, b: CameraState, t01: number, ease: Easing = easeInOut): CameraState {\n const k = ease(clamp(t01, 0, 1));\n return { cx: lerp(a.cx, b.cx, k), cy: lerp(a.cy, b.cy, k), zoom: lerp(a.zoom, b.zoom, k) };\n}\n\n/**\n * A Ken-Burns move: slow drift from `from` to `to` over a segment. `at` is the\n * segment-relative position 0..1. Pure — returns the pose for that t.\n */\nexport function kenBurns(from: CameraState, to: CameraState, at01: number): CameraState {\n return lerpCamera(from, to, at01, easeInOut);\n}\n\n/**\n * Apply a camera pose to a 2D context (sets the transform). Layers drawn after\n * this render in world coords and appear panned/zoomed. Pair with ctx.save()\n * /ctx.restore() in the runner so layers can't leak the transform.\n */\nexport function applyToContext(\n ctx: CanvasRenderingContext2D,\n cam: CameraState,\n W: number,\n H: number,\n): void {\n const m = cameraTransform(cam, W, H);\n ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);\n}\n\n/** The identity (no pan/zoom) camera: world == viewport. */\nexport function identityCamera(W: number, H: number): CameraState {\n return { cx: W / 2, cy: H / 2, zoom: 1 };\n}\n","// SceneSpec + runner (S1b).\n//\n// A SceneSpec is the declarative description a recipe produces: canvas size,\n// theme, how duration is decided, a timeline of segments (each a stack of\n// layers active over a time range), and audio settings. The runner:\n// 1. resolves segment times (incl. \"end\" / \"end-N\" anchored to the clip end),\n// 2. instantiates layers from the registry + init()s them,\n// 3. on each frame, draws every active segment's layers IN ORDER onto ONE\n// canvas, applying the camera transform, then resets transform so screen-\n// pinned chrome (captions/branding) draws in viewport space.\n//\n// Capture is delegated to src/video.ts recordScenes (canvas captureStream +\n// MediaRecorder + Tone audio) — we wrap the whole timeline as a single composite\n// Scene so we REUSE that machinery rather than reinventing it. A separate\n// deterministic `renderFrame` path (no MediaRecorder) backs the tests + golden\n// frames and runs anywhere a 2D context exists.\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { Layer, RenderCtx, AudioClock } from './layer';\nimport type { Score } from './score';\nimport type { PromoTheme } from '../video';\nimport { recordScenes, safeBox, type Scene, type RecordOpts } from '../video';\nimport { getLayerFactory, registeredKeys } from './registry';\nimport { applySchedule, type AudioEvent } from './audioLayers';\nimport {\n identityCamera,\n applyToContext,\n type CameraState,\n} from './camera';\nimport { clamp, easeInOut as defaultEasing, transitionProgress, type Easing } from './math';\n\n// ─── SceneSpec ───────────────────────────────────────────────────────────────\n\n/** A timeline endpoint: a number (ms? no — seconds), \"end\", or \"end-N\" (N s before end). */\nexport type TimeAnchor = number | 'end' | `end-${number}`;\n\nexport interface SpecLayer {\n /** Registry key. */\n k: string;\n /** Props passed to the layer's init(). */\n p?: unknown;\n}\n\n/**\n * Optional per-segment audio. Lets a segment SCHEDULE sound at its own start\n * time during capture — the seam that lets per-scene / sequential-audio apps\n * (RET templates schedule `engine.playNote` in a scene's `onEnter`; whozart's\n * \"listen\" scenes play a phrase when the scene begins) run on the same runner\n * instead of staying bespoke. Everything here is OPTIONAL and the runner stays\n * pure-visual when it's absent (fully backward-compatible).\n *\n * Two complementary forms, both resolved against the CAPTURE clock (not\n * wall-clock) so they fire at the right offset in an offline/deterministic\n * render:\n * - `schedule`: declarative `AudioEvent`s whose `atSec` is relative to the\n * segment's start. Played through the same Tone sampler/mastering bus via the\n * existing `applySchedule` glue (so timing == captureStart + segStart + atSec).\n * - `onEnter(audioClock, ctx)`: an imperative escape hatch fired once at the\n * segment's start time, handed the audio clock anchored to that start (its\n * `nowMs()` returns the segment's absolute start ms) so the callback can do\n * its own `triggerAttackRelease(note, dur, audioClock.nowMs()/1000 + …)`.\n */\nexport interface SegmentAudio {\n /** Declarative events; `atSec` is relative to the segment's start. */\n schedule?: AudioEvent[];\n /** Imperative hook fired once at the segment's start during capture. */\n onEnter?: (audioClock: AudioClock, ctx: SegmentAudioCtx) => void;\n}\n\n/** Context handed to a `SegmentAudio.onEnter` callback. */\nexport interface SegmentAudioCtx {\n /** The Tone instrument wired into the promo mastering chain (has\n * `triggerAttackRelease`). Same target `applySchedule` plays through. */\n instrument: ScheduleTarget;\n /** Absolute capture-clock seconds at which this segment starts (= the audio\n * base time + the segment's resolved start). Add your own offsets to this. */\n startSec: number;\n /** The render Score, if any (timing/pitch source). */\n score?: Score;\n}\n\n/** Minimal Tone-instrument surface the audio scheduler needs. */\nexport interface ScheduleTarget {\n triggerAttackRelease: (note: unknown, dur: unknown, time?: unknown, velocity?: unknown) => void;\n}\n\n/**\n * Optional ENTER transition for a segment — how it comes IN from the PREVIOUS\n * segment (the one immediately before it in the timeline). Purely VISUAL and\n * fully additive: a segment with no `transition` hard-cuts exactly as before\n * (existing specs are byte-for-byte unchanged). The audio timeline / per-segment\n * `audio` hook is unaffected (transitions are a visual envelope only).\n *\n * During the first `durationMs` of the incoming segment, the runner renders BOTH\n * the previous segment (its last drawn state, held) AND the incoming segment,\n * applying an eased envelope (`easeInOut` by default):\n * - `crossfade` — outgoing opacity 1→0, incoming 0→1 (the keystone; always on).\n * - `push` — both slide along `direction`: incoming enters from off-frame\n * while outgoing exits the opposite way (a hard slide, no fade).\n * - `wipe` — incoming is revealed by a moving clip edge along `direction`;\n * the outgoing shows through the un-wiped region (no fade).\n *\n * The overlap does NOT add time — the transition lives INSIDE the incoming\n * segment's own window, so `visualTimelineMs` (and the gate's A/V-duration check)\n * are unchanged. `durationMs` is clamped to the shorter of the two adjoining\n * segment lengths so a transition can't run past either segment.\n */\nexport interface SegmentTransition {\n /** Transition style. Start with `crossfade`. */\n type: 'crossfade' | 'push' | 'wipe';\n /** Length of the enter window in ms (from the incoming segment's start). */\n durationMs: number;\n /**\n * Slide / wipe direction for `push` and `wipe` (ignored by `crossfade`).\n * Names the edge the INCOMING segment comes FROM. Default `left`.\n */\n direction?: 'left' | 'right' | 'up' | 'down';\n /** Eased envelope, 0..1 → 0..1. Defaults to `easeInOut`. */\n easing?: Easing;\n}\n\nexport interface TimelineSegment {\n /** [start, end] in SECONDS; end may be \"end\" / \"end-N\". */\n at: [number, TimeAnchor];\n layers: SpecLayer[];\n /** Optional sound scheduled at this segment's start (see SegmentAudio). */\n audio?: SegmentAudio;\n /**\n * Optional ENTER transition from the PREVIOUS segment (see SegmentTransition).\n * Omit for today's hard cut. Has no effect on the first segment (nothing to\n * transition from) unless `loop` is set on the spec.\n */\n transition?: SegmentTransition;\n}\n\nexport interface SceneSpec {\n /** [width, height] px. */\n size: [number, number];\n /** Theme key (resolved to a PromoTheme by the host) OR an inline theme. */\n theme: string;\n /** \"audio\" = clip length follows the audio; \"fixed\" = use `durationSec`. */\n durationMode: 'audio' | 'fixed';\n /** Required when durationMode === \"fixed\". */\n durationSec?: number;\n timeline: TimelineSegment[];\n audio?: { voicing?: string; [k: string]: unknown };\n /** Capture frame rate. Default 30. */\n fps?: number;\n}\n\n// ─── Timeline resolution ──────────────────────────────────────────────────────\n\n/** Resolve a TimeAnchor to absolute seconds given the clip's total length. */\nexport function resolveAnchor(anchor: TimeAnchor, totalSec: number): number {\n if (typeof anchor === 'number') return anchor;\n if (anchor === 'end') return totalSec;\n const m = /^end-(\\d+(?:\\.\\d+)?)$/.exec(anchor);\n if (m) return totalSec - parseFloat(m[1]);\n throw new Error(`resolveAnchor: bad anchor \"${anchor}\"`);\n}\n\nexport interface ResolvedSegment {\n startMs: number;\n endMs: number;\n layers: SpecLayer[];\n audio?: SegmentAudio;\n transition?: SegmentTransition;\n}\n\n/** Resolve every segment's [start,end] to ms against the total clip length. */\nexport function resolveTimeline(spec: SceneSpec, totalSec: number): ResolvedSegment[] {\n return spec.timeline.map((seg) => {\n const startSec = resolveAnchor(seg.at[0], totalSec);\n const endSec = resolveAnchor(seg.at[1], totalSec);\n return {\n startMs: startSec * 1000,\n endMs: endSec * 1000,\n layers: seg.layers,\n audio: seg.audio,\n transition: seg.transition,\n };\n });\n}\n\n/** The visual timeline length in ms = the latest resolved segment end. */\nexport function visualTimelineMs(resolved: ResolvedSegment[]): number {\n return resolved.reduce((mx, s) => Math.max(mx, s.endMs), 0);\n}\n\n// ─── Built runner ─────────────────────────────────────────────────────────────\n\n/** A layer instance bound to its segment window + resolved props. */\ninterface BoundLayer {\n layer: Layer<any>;\n startMs: number;\n endMs: number;\n /** Index of the owning segment in the resolved timeline (for transitions). */\n segIndex: number;\n /** Whether this layer renders pinned to the screen (caption/branding) — drawn\n * AFTER the camera transform is reset. Heuristic by key for v1. */\n screenPinned: boolean;\n}\n\nconst SCREEN_PINNED_KEYS = new Set([\n 'caption', 'branding', 'cta', 'hook', 'reveal', 'portrait', 'safe-guides',\n 'mcq-card', 'beat-pulse', 'chord-ribbon', 'progress-ring', 'karaoke-caption',\n 'kinetic-text', 'countdown', 'particle-burst', 'texture', 'stat-counter', 'end-card',\n]);\n\nexport interface BuiltScene {\n W: number;\n H: number;\n fps: number;\n durationMs: number;\n /** Draw the whole composite at absolute time `tMs` onto `ctx2d`. Deterministic\n * (pure fn of tMs). Used by tests, golden frames, and the recordScenes wrapper. */\n renderFrame(ctx2d: CanvasRenderingContext2D, tMs: number): void;\n /** Free every layer's resources. */\n dispose(): void;\n /** The resolved timeline (for the gate / diagnostics). */\n resolved: ResolvedSegment[];\n /**\n * Schedule every segment's optional audio against the CAPTURE clock. Call once\n * at capture start, passing the Tone instrument (the promo sampler) and the\n * audio-clock base time in SECONDS (e.g. `sampler.audioNow()` / `Tone.now()`).\n * Each segment's events fire at `baseSec + segStartMs/1000 + atSec`; each\n * `onEnter` fires once with an AudioClock anchored to its segment start.\n * Deterministic w.r.t. the supplied base time (no wall-clock). No-op for specs\n * that declare no segment audio. Returns the count of events scheduled (handy\n * for tests/gate sanity).\n */\n scheduleAudio(instrument: ScheduleTarget, baseSec: number): number;\n}\n\nexport interface BuildSceneOpts {\n spec: SceneSpec;\n theme: PromoTheme;\n /** The parsed Score (timing source). Optional for non-musical specs. */\n score?: Score;\n /** Total clip length in seconds. With durationMode \"audio\" pass the audio\n * length; with \"fixed\" it defaults to spec.durationSec. */\n totalSec: number;\n /** Override the camera pose per absolute tMs (pan/zoom director). Identity by\n * default. */\n camera?: (tMs: number) => CameraState;\n /**\n * Offscreen-buffer factory used ONLY by segment `transition`s (a segment's\n * stack is rendered to a buffer, then composited onto the main canvas with the\n * transition envelope — so the envelope is robust even when inner layers set\n * their own `globalAlpha`). Unused when no segment has a transition.\n *\n * Default: `OffscreenCanvas` if available, else `document.createElement\n * ('canvas')`. Node/headless callers (and the node-canvas golden tests) pass a\n * factory backed by `createCanvas`. If no factory is resolvable AND a\n * transition fires, the runner falls back to a direct `globalAlpha` envelope\n * (crossfade only; degrades gracefully) rather than throwing.\n */\n bufferFactory?: BufferFactory;\n}\n\n/** Makes an offscreen drawing surface for transition compositing. */\nexport type BufferFactory = (\n w: number,\n h: number,\n) => { ctx: CanvasRenderingContext2D; image: CanvasImageSource };\n\n/** Resolve a default buffer factory from the environment (browser/Node). */\nfunction defaultBufferFactory(): BufferFactory | undefined {\n const g = globalThis as any;\n if (typeof g.OffscreenCanvas === 'function') {\n return (w, h) => {\n const c = new g.OffscreenCanvas(w, h);\n return { ctx: c.getContext('2d'), image: c };\n };\n }\n if (typeof g.document !== 'undefined' && g.document?.createElement) {\n return (w, h) => {\n const c = g.document.createElement('canvas');\n c.width = w; c.height = h;\n return { ctx: c.getContext('2d'), image: c };\n };\n }\n return undefined;\n}\n\n/**\n * Instantiate + init every layer in the spec and return a deterministic renderer.\n * Throws if a referenced layer key isn't registered or its props don't validate\n * (so build failures surface before capture — the gate also re-checks).\n */\nexport async function buildScene(opts: BuildSceneOpts): Promise<BuiltScene> {\n const { spec, theme, score } = opts;\n const [W, H] = spec.size;\n const fps = spec.fps ?? 30;\n const resolved = resolveTimeline(spec, opts.totalSec);\n const safe = safeBox(W, H);\n const camera = opts.camera ?? (() => identityCamera(W, H));\n const bufferFactory = opts.bufferFactory ?? defaultBufferFactory();\n const hasTransitions = resolved.some((s) => s.transition && s.transition.durationMs > 0);\n\n const clock: AudioClock = { nowMs: () => 0 };\n const baseCtx: Omit<RenderCtx, 'ctx2d'> = {\n W, H, score, audioClock: clock, theme, safeBox: safe, fps,\n };\n\n const bound: BoundLayer[] = [];\n for (let segIndex = 0; segIndex < resolved.length; segIndex++) {\n const seg = resolved[segIndex];\n for (const sl of seg.layers) {\n const factory = getLayerFactory(sl.k);\n if (!factory) {\n throw new Error(`buildScene: unknown layer \"${sl.k}\" (registered: ${registeredKeys().join(', ')})`);\n }\n const errs = factory.validateProps(sl.p ?? {});\n if (errs.length) {\n throw new Error(`buildScene: invalid props for \"${sl.k}\": ${errs.join('; ')}`);\n }\n const layer = factory.create();\n // init runs against a ctx that has no live ctx2d yet; layers that need to\n // rasterize use init's ctx only for sizing/theme (drawing happens in draw).\n await layer.init({ ...baseCtx, ctx2d: null as any }, sl.p ?? {});\n bound.push({\n layer,\n startMs: seg.startMs,\n endMs: seg.endMs,\n segIndex,\n screenPinned: SCREEN_PINNED_KEYS.has(sl.k),\n });\n }\n }\n\n const durationMs = visualTimelineMs(resolved);\n\n // ── Transition envelope ────────────────────────────────────────────────────\n // A `SegEnv` describes how a single segment's whole layer stack is composited\n // for this frame. `null` means \"draw exactly as a hard cut\" (no extra ctx ops),\n // which is what keeps no-transition specs byte-for-byte identical to before.\n interface SegEnv {\n /** Multiplied into globalAlpha for the segment's draws (crossfade). */\n alpha: number;\n /** Screen-space pixel translate (push). */\n dx: number;\n dy: number;\n /** Optional clip rect in screen space (wipe). Undefined = no clip. */\n clip?: { x: number; y: number; w: number; h: number };\n }\n\n /** Push/wipe travel for a direction: how far off-frame the incoming starts. */\n function dirVec(dir: SegmentTransition['direction']): { x: number; y: number } {\n switch (dir) {\n case 'right': return { x: 1, y: 0 };\n case 'up': return { x: 0, y: -1 };\n case 'down': return { x: 0, y: 1 };\n case 'left':\n default: return { x: -1, y: 0 };\n }\n }\n\n /**\n * For the INCOMING segment `i`, build the (incoming, outgoing) envelopes at the\n * eased progress `p` (0→1 across the enter window). `outgoing` is the previous\n * segment's held state. Both `null` outside a transition window (hard cut).\n */\n function transitionEnvs(\n tr: SegmentTransition,\n p: number,\n ): { incoming: SegEnv; outgoing: SegEnv } {\n if (tr.type === 'push') {\n const v = dirVec(tr.direction);\n // Incoming slides from off-frame (-W·v at p=0) to 0; outgoing slides off\n // the opposite edge (0 at p=0 to +W·v at p=1).\n const span = Math.abs(v.x) ? W : H;\n return {\n incoming: { alpha: 1, dx: -v.x * span * (1 - p), dy: -v.y * span * (1 - p) },\n outgoing: { alpha: 1, dx: v.x * span * p, dy: v.y * span * p },\n };\n }\n if (tr.type === 'wipe') {\n const v = dirVec(tr.direction);\n // Incoming is revealed by a growing clip rect advancing along the edge;\n // outgoing shows through the complementary (un-wiped) region.\n let inClip: SegEnv['clip'];\n let outClip: SegEnv['clip'];\n if (v.x !== 0) {\n const w = W * p;\n if (v.x < 0) { // incoming enters from the LEFT edge\n inClip = { x: 0, y: 0, w, h: H };\n outClip = { x: w, y: 0, w: W - w, h: H };\n } else { // from the RIGHT edge\n inClip = { x: W - w, y: 0, w, h: H };\n outClip = { x: 0, y: 0, w: W - w, h: H };\n }\n } else {\n const h = H * p;\n if (v.y < 0) { // from the TOP\n inClip = { x: 0, y: 0, w: W, h };\n outClip = { x: 0, y: h, w: W, h: H - h };\n } else { // from the BOTTOM\n inClip = { x: 0, y: H - h, w: W, h };\n outClip = { x: 0, y: 0, w: W, h: H - h };\n }\n }\n return {\n incoming: { alpha: 1, dx: 0, dy: 0, clip: inClip },\n outgoing: { alpha: 1, dx: 0, dy: 0, clip: outClip },\n };\n }\n // crossfade (default + keystone)\n return {\n incoming: { alpha: p, dx: 0, dy: 0 },\n outgoing: { alpha: 1 - p, dx: 0, dy: 0 },\n };\n }\n\n // A frame's render plan: which segments draw, at what time, with what envelope.\n interface PlanEntry { segIndex: number; drawTMs: number; env: SegEnv | null }\n\n /** Build the per-segment render plan for absolute time `tMs`. */\n function frameplan(tMs: number): PlanEntry[] {\n const plan: PlanEntry[] = [];\n for (let i = 0; i < resolved.length; i++) {\n const seg = resolved[i];\n // Is this segment in its own active window?\n if (tMs < seg.startMs || tMs >= seg.endMs) continue;\n const tr = seg.transition;\n const prev = i > 0 ? resolved[i - 1] : undefined;\n const enterEnd = tr ? seg.startMs + Math.max(0, tr.durationMs) : seg.startMs;\n const inEnter = tr && tr.durationMs > 0 && tMs < enterEnd && prev;\n if (!inEnter) {\n // Hard cut (env null → byte-identical to the legacy path).\n plan.push({ segIndex: i, drawTMs: tMs, env: null });\n continue;\n }\n const p = transitionProgress(tMs, seg.startMs, tr!.durationMs, tr!.easing ?? defaultEasing);\n const { incoming, outgoing } = transitionEnvs(tr!, p);\n // Outgoing = previous segment held at its last visible instant.\n const heldTMs = clamp(tMs, prev!.startMs, prev!.endMs - 1);\n plan.push({ segIndex: i - 1, drawTMs: heldTMs, env: outgoing });\n plan.push({ segIndex: i, drawTMs: tMs, env: incoming });\n }\n return plan;\n }\n\n /** Draw a segment's FULL stack (world + pinned) onto a target ctx at drawTMs. */\n function drawSegmentInto(\n target: CanvasRenderingContext2D, segIndex: number, drawTMs: number,\n ): void {\n const ctx: RenderCtx = { ...baseCtx, ctx2d: target };\n const cam = camera(drawTMs);\n // World-space pass (camera applied).\n target.save();\n applyToContext(target, cam, W, H);\n for (const b of bound) {\n if (b.segIndex !== segIndex || b.screenPinned) continue;\n if (drawTMs < b.startMs || drawTMs >= b.endMs) continue;\n b.layer.draw(ctx, drawTMs);\n }\n target.restore();\n // Screen-space pass (pinned chrome).\n target.save();\n target.setTransform(1, 0, 0, 1, 0, 0);\n for (const b of bound) {\n if (b.segIndex !== segIndex || !b.screenPinned) continue;\n if (drawTMs < b.startMs || drawTMs >= b.endMs) continue;\n b.layer.draw(ctx, drawTMs);\n }\n target.restore();\n }\n\n /**\n * Composite one plan entry onto the main ctx. For a transition entry the\n * segment's whole stack is rendered to an offscreen buffer first, then blitted\n * with the envelope (alpha for crossfade, translate for push, clip for wipe) —\n * so a single drawImage carries the envelope and inner-layer `globalAlpha`\n * writes can't leak through. Falls back to a direct alpha envelope if no buffer\n * factory is available (crossfade still works; push/wipe degrade to a fade).\n */\n function compositeEntry(\n ctx2d: CanvasRenderingContext2D, e: PlanEntry,\n ): void {\n if (e.env === null) {\n // Hard cut (no transition for this segment this frame).\n drawSegmentInto(ctx2d, e.segIndex, e.drawTMs);\n return;\n }\n const env = e.env;\n if (!bufferFactory) {\n // Graceful fallback: alpha-only envelope directly on the main ctx.\n ctx2d.save();\n ctx2d.globalAlpha = ctx2d.globalAlpha * env.alpha;\n drawSegmentInto(ctx2d, e.segIndex, e.drawTMs);\n ctx2d.restore();\n return;\n }\n const buf = bufferFactory(W, H);\n drawSegmentInto(buf.ctx, e.segIndex, e.drawTMs);\n ctx2d.save();\n ctx2d.setTransform(1, 0, 0, 1, 0, 0);\n if (env.alpha !== 1) ctx2d.globalAlpha = ctx2d.globalAlpha * env.alpha;\n if (env.clip) {\n ctx2d.beginPath();\n ctx2d.rect(env.clip.x, env.clip.y, env.clip.w, env.clip.h);\n ctx2d.clip();\n }\n // Blit the buffer at the (possibly translated) destination.\n ctx2d.drawImage(buf.image, env.dx, env.dy, W, H);\n ctx2d.restore();\n }\n\n function renderFrame(ctx2d: CanvasRenderingContext2D, tMs: number): void {\n clock.nowMs = () => tMs;\n const ctx: RenderCtx = { ...baseCtx, ctx2d };\n const cam = camera(tMs);\n\n // Fast path: when the spec has NO transitions at all, reproduce the legacy\n // single-pass compositing EXACTLY (one world save/restore for all active\n // layers, then one pinned pass). This keeps no-transition specs byte-for-byte\n // identical to before (op stream, goldens, determinism).\n if (!hasTransitions) {\n ctx2d.save();\n applyToContext(ctx2d, cam, W, H);\n for (const b of bound) {\n if (b.screenPinned) continue;\n if (tMs < b.startMs || tMs >= b.endMs) continue;\n b.layer.draw(ctx, tMs);\n }\n ctx2d.restore();\n\n ctx2d.save();\n ctx2d.setTransform(1, 0, 0, 1, 0, 0);\n for (const b of bound) {\n if (!b.screenPinned) continue;\n if (tMs < b.startMs || tMs >= b.endMs) continue;\n b.layer.draw(ctx, tMs);\n }\n ctx2d.restore();\n return;\n }\n\n // Transition-capable path: composite per segment in timeline order so\n // cross-segment z-order is preserved (outgoing under incoming). A segment\n // not mid-transition this frame composites as a plain hard cut (env null).\n const plan = frameplan(tMs);\n for (const e of plan) compositeEntry(ctx2d, e);\n }\n\n function scheduleAudio(instrument: ScheduleTarget, baseSec: number): number {\n let fired = 0;\n for (const seg of resolved) {\n if (!seg.audio) continue;\n const segStartSec = baseSec + seg.startMs / 1000;\n // Declarative events: applySchedule plays each at segStartSec + atSec.\n if (seg.audio.schedule?.length) {\n fired += applySchedule(instrument, seg.audio.schedule, segStartSec);\n }\n // Imperative escape hatch: fire once with a clock anchored at the segment\n // start (nowMs() == seg.startMs) so the callback can offset off it.\n if (seg.audio.onEnter) {\n const segClock: AudioClock = { nowMs: () => seg.startMs };\n seg.audio.onEnter(segClock, { instrument, startSec: segStartSec, score });\n }\n }\n return fired;\n }\n\n return {\n W, H, fps, durationMs, resolved, renderFrame, scheduleAudio,\n dispose() {\n for (const b of bound) b.layer.dispose?.();\n },\n };\n}\n\n// ─── Capture wrapper (reuses recordScenes) ────────────────────────────────────\n\nexport interface RecordSceneSpecOpts {\n built: BuiltScene;\n audioStream: MediaStream;\n background?: string;\n onProgress?: RecordOpts['onProgress'];\n /** Inject the recorder for tests. Defaults to recordScenes. */\n record?: (scenes: Scene[], o: RecordOpts) => Promise<Blob>;\n /**\n * OPTIONAL per-segment audio scheduling. When a spec declares segment audio\n * (SegmentAudio), pass the Tone instrument (the promo sampler) and a clock so\n * the runner can schedule each segment's sound against the capture timeline.\n * `audioNow()` returns the audio-clock seconds at capture start (e.g.\n * `sampler.audioNow()` / `Tone.now()`); `lookaheadSec` (default 0.1) shifts\n * the whole schedule forward a touch so the first event isn't already in the\n * past when the recorder spins up. Omit entirely → pure-visual capture\n * (unchanged behavior).\n */\n segmentAudio?: {\n instrument: ScheduleTarget;\n audioNow: () => number;\n lookaheadSec?: number;\n };\n /** Passed straight through to recordScenes — see RecordOpts.splitAudioTrack. */\n splitAudioTrack?: RecordOpts['splitAudioTrack'];\n /** Passed straight through to recordScenes — see RecordOpts.onAudioBlob. */\n onAudioBlob?: RecordOpts['onAudioBlob'];\n}\n\n/**\n * Capture a built scene to a Blob by wrapping the whole timeline as a SINGLE\n * composite Scene and handing it to recordScenes — so audio sync, captureStream,\n * and MediaRecorder all come from the existing, working path. recordScenes\n * clears to `background` each frame and gives us a 0..1 t; we expand it back to\n * absolute ms and call renderFrame.\n */\nexport async function recordSceneSpec(opts: RecordSceneSpecOpts): Promise<Blob> {\n const { built } = opts;\n const record = opts.record ?? recordScenes;\n const composite: Scene = {\n durationMs: built.durationMs,\n // Schedule any per-segment audio against the capture clock exactly when the\n // first frame is entered, so it's anchored to the same start the recorder\n // uses. onEnter fires once per Scene (see recordScenes), so this runs once.\n onEnter: opts.segmentAudio\n ? () => {\n const sa = opts.segmentAudio!;\n built.scheduleAudio(sa.instrument, sa.audioNow() + (sa.lookaheadSec ?? 0.1));\n }\n : undefined,\n draw: (ctx, t01) => built.renderFrame(ctx, t01 * built.durationMs),\n };\n return record([composite], {\n audioStream: opts.audioStream,\n width: built.W,\n height: built.H,\n fps: built.fps,\n background: opts.background,\n onProgress: opts.onProgress,\n splitAudioTrack: opts.splitAudioTrack,\n onAudioBlob: opts.onAudioBlob,\n });\n}\n","// Pre-render invariant GATE (S1b) — the production \"don't ship a broken video\"\n// guard. Runs around a render and FAILS (returns errors / throws) so a broken\n// clip is never upserted into the catalog. Generalizes RSR's verify()/avdiag.\n//\n// Checks (spec: Testing & quality gates §0):\n// A. A/V duration match: |visualTimelineMs − audioMs| ≤ 60ms.\n// B. Placement sanity: required positions resolve (no NaN / out-of-bounds);\n// nothing required drawn outside safeBox when safezone is on.\n// C. Fonts loaded before the first captured frame.\n// D. Spec valid: every layer key registered + props validate.\n// E. Real output: capture produced ≥ expected frames + an audio track.\n//\n// In-code (pure, unit-tested here): A, B, D, and the *math* of E (frame-count /\n// track-count comparison). Environment-coupled probes are exposed as HOOKS the\n// smvp web-capture recipe wires up: font readiness (C, document.fonts) and\n// ffprobe (E, the actual stream/frame inspection of the produced file).\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { SceneSpec, ResolvedSegment } from './runner';\nimport { resolveTimeline, visualTimelineMs } from './runner';\nimport { getLayerFactory } from './registry';\nimport type { SafeBox } from '../video';\n\nexport interface GateError {\n check: 'av-duration' | 'placement' | 'fonts' | 'spec' | 'output';\n message: string;\n}\n\n/** A position a layer reports placing (for the placement-sanity check). */\nexport interface Placement {\n /** Label for diagnostics (e.g. \"note@1200ms\"). */\n label: string;\n x: number;\n y: number;\n /** Whether this placement is REQUIRED to be inside the safe box. */\n mustBeSafe?: boolean;\n}\n\nexport interface OutputProbe {\n /** Frames the capture actually produced. */\n frames: number;\n /** Audio tracks present (ffprobe a:0…). */\n audioTracks: number;\n /** Optional measured duration in ms (ffprobe), for a secondary A/V check. */\n durationMs?: number;\n}\n\nexport interface GateInput {\n spec: SceneSpec;\n /** Audio length in ms (the sounding length the capture targets). */\n audioMs: number;\n /** Total clip length in seconds used to resolve the timeline. */\n totalSec: number;\n /** Frame size for bounds + safe checks. */\n W: number;\n H: number;\n safeBox: SafeBox;\n /** Whether safezone enforcement is on for this clip. */\n safezone: boolean;\n /** Frame rate (for the expected-frame-count math). Default 30. */\n fps?: number;\n /** Required placements a layer reported (optional; pass [] if none). */\n placements?: Placement[];\n /**\n * Font-readiness hook. The smvp recipe passes `() => document.fonts.ready\n * .then(() => true)` (after loadBrandFonts). Default: assume loaded (true) so\n * headless unit runs don't fail on a missing document.\n */\n fontsReady?: () => boolean | Promise<boolean>;\n /**\n * Output probe hook. The smvp recipe runs ffprobe on the produced file and\n * passes the result. Omit to skip the output check (e.g. a pre-capture gate\n * pass that only validates the spec).\n */\n output?: OutputProbe;\n}\n\nconst AV_TOLERANCE_MS = 60;\n\n/**\n * Run the gate. Returns the list of failures ([] = passes). Pure except for the\n * optional async `fontsReady` hook. Call before capture (spec + A/V + placement)\n * and again after capture with `output` set (real-output check).\n */\nexport async function runGate(input: GateInput): Promise<GateError[]> {\n const errors: GateError[] = [];\n\n // D. Spec valid — fail fast on unknown layer / bad props.\n let resolved: ResolvedSegment[] = [];\n for (const seg of input.spec.timeline) {\n for (const sl of seg.layers) {\n const factory = getLayerFactory(sl.k);\n if (!factory) {\n errors.push({ check: 'spec', message: `unknown layer \"${sl.k}\"` });\n continue;\n }\n const errs = factory.validateProps(sl.p ?? {});\n for (const e of errs) errors.push({ check: 'spec', message: e });\n }\n }\n // Resolve the timeline; a malformed anchor surfaces as a spec error too.\n try {\n resolved = resolveTimeline(input.spec, input.totalSec);\n } catch (e) {\n errors.push({ check: 'spec', message: `timeline: ${(e as Error).message}` });\n }\n\n // A. A/V duration match.\n if (resolved.length) {\n const vis = visualTimelineMs(resolved);\n if (!Number.isFinite(vis) || !Number.isFinite(input.audioMs)) {\n errors.push({ check: 'av-duration', message: 'non-finite visual/audio duration' });\n } else if (Math.abs(vis - input.audioMs) > AV_TOLERANCE_MS) {\n errors.push({\n check: 'av-duration',\n message: `|visual ${Math.round(vis)}ms − audio ${Math.round(input.audioMs)}ms| = ${Math.round(\n Math.abs(vis - input.audioMs),\n )}ms > ${AV_TOLERANCE_MS}ms`,\n });\n }\n }\n\n // B. Placement sanity — no NaN, inside frame, and inside safeBox when required.\n for (const p of input.placements ?? []) {\n if (!Number.isFinite(p.x) || !Number.isFinite(p.y)) {\n errors.push({ check: 'placement', message: `${p.label}: NaN/∞ position` });\n continue;\n }\n if (p.x < 0 || p.x > input.W || p.y < 0 || p.y > input.H) {\n errors.push({ check: 'placement', message: `${p.label}: (${p.x},${p.y}) outside ${input.W}×${input.H}` });\n }\n if (input.safezone && p.mustBeSafe) {\n const b = input.safeBox;\n if (p.x < b.left || p.x > b.right || p.y < b.top || p.y > b.bottom) {\n errors.push({ check: 'placement', message: `${p.label}: outside safe box` });\n }\n }\n }\n\n // C. Fonts loaded (hook; default true so headless unit runs pass).\n const ready = input.fontsReady ? await input.fontsReady() : true;\n if (!ready) errors.push({ check: 'fonts', message: 'fonts not loaded before first frame' });\n\n // E. Real output (only when a probe was supplied — typically post-capture).\n if (input.output) {\n const expectedFrames = Math.floor((input.fps ?? 30) * (input.audioMs / 1000) * 0.5); // ≥ half is generous\n if (input.output.frames < expectedFrames) {\n errors.push({\n check: 'output',\n message: `only ${input.output.frames} frames (expected ≥ ${expectedFrames})`,\n });\n }\n if (input.output.audioTracks < 1) {\n errors.push({ check: 'output', message: 'no audio track in output' });\n }\n if (\n input.output.durationMs != null &&\n Math.abs(input.output.durationMs - input.audioMs) > AV_TOLERANCE_MS\n ) {\n errors.push({\n check: 'output',\n message: `output duration ${Math.round(input.output.durationMs)}ms vs audio ${Math.round(\n input.audioMs,\n )}ms exceeds ${AV_TOLERANCE_MS}ms`,\n });\n }\n }\n\n return errors;\n}\n\n/** Convenience: run the gate and throw a single aggregated error on failure. */\nexport async function assertGate(input: GateInput): Promise<void> {\n const errors = await runGate(input);\n if (errors.length) {\n throw new Error(\n `pre-render gate failed (${errors.length}):\\n` +\n errors.map((e) => ` [${e.check}] ${e.message}`).join('\\n'),\n );\n }\n}\n","// Notation camera bridge (S2) — expresses RSR's followBox/crop/scroll as the\n// shared camera primitive (../camera frameRect/lerpCamera), so the scroll-cursor\n// follow window \"rides the camera\" and downstream layers (falling-notes, labels)\n// can pan/zoom off the SAME pose instead of re-deriving RSR's bespoke crop math.\n//\n// Equivalence (proved in tests/scene-camera-equivalence.test.ts): RSR scrolls by\n// re-cropping a canvas `src` window and fitting it to the band. The identical\n// visual move is a camera that frames the follow window (in the base world layout)\n// to fill the band. Given the base layout L (full content -> band) and a per-frame\n// follow box F (canvas coords), the follow window in WORLD/screen coords is the\n// rect L maps F to. frameRect(worldFollowRect, bandW, bandH) is the pose that\n// scrolls/zooms that window to fill the band — the same pixels RSR's re-crop draws.\n\nimport type { Box, RenderedNotation } from '../promo';\nimport { frameRect, type CameraState } from './camera';\nimport { FOLLOW_PAD, type NotationLayout } from './notationGeometry';\n\n/** Map a canvas-space box through a base notation layout into world/screen coords. */\nexport function mapBoxThroughLayout(base: NotationLayout, b: Box): Box {\n const fx = base.rect.dw / base.src.w;\n const fy = base.rect.dh / base.src.h;\n return {\n x: base.rect.dx + (b.x - base.src.x) * fx,\n y: base.rect.dy + (b.y - base.src.y) * fy,\n w: b.w * fx,\n h: b.h * fy,\n };\n}\n\n/** RSR's per-frame `src` crop from a follow box (FOLLOW_PAD expand + clamp) —\n * the exact canvas window RSR's drawNotation blits (+page.svelte:289-298). */\nexport function followSrcBox(rn: RenderedNotation, focusBoxCanvas: Box): Box {\n const px = (focusBoxCanvas.w * (FOLLOW_PAD - 1)) / 2;\n const py = (focusBoxCanvas.h * (FOLLOW_PAD - 1)) / 2;\n const src: Box = {\n x: Math.max(0, focusBoxCanvas.x - px),\n y: Math.max(0, focusBoxCanvas.y - py),\n w: focusBoxCanvas.w + 2 * px,\n h: focusBoxCanvas.h + 2 * py,\n };\n src.w = Math.min(src.w, rn.canvas.width - src.x);\n src.h = Math.min(src.h, rn.canvas.height - src.y);\n return src;\n}\n\n/**\n * The camera pose that scrolls/zooms the base (fixed) notation blit so the follow\n * window fills a `viewW`×`viewH` viewport — the camera-primitive expression of\n * RSR's per-frame re-crop. Composing this pose with the base blit reproduces RSR's\n * `notationLayout({focusBox})` exactly (proven in scene-notation-camera.test.ts).\n *\n * We frame the SAME canvas window RSR crops (FOLLOW_PAD-expanded `src`), mapped\n * into base-world coords, into the viewport. `viewW`/`viewH` are the dest-rect\n * dimensions (so the contain-fit matches RSR's band-fit).\n */\nexport function cameraForFollow(\n rn: RenderedNotation,\n base: NotationLayout,\n focusBoxCanvas: Box,\n viewW: number,\n viewH: number,\n): CameraState {\n const src = followSrcBox(rn, focusBoxCanvas);\n const world = mapBoxThroughLayout(base, src);\n return frameRect(world, viewW, viewH, 0);\n}\n","// Standalone demo (S3) — proves the headline falling-notes + keyboard capability\n// end-to-end through the runner over a REAL Score (parsed from a bundled\n// grand-staff MusicXML, so L/R hands show). Reusable as the basis of an\n// RSR/whozart variant later; no app is wired here.\n//\n// Returns a SceneSpec + the parsed Score so a host (or the golden test) can\n// buildScene() it and render/capture deterministic frames.\n\nimport type { SceneSpec } from '../runner';\nimport type { Score } from '../score';\nimport { scoreFromMusicXML, type ScoreFromMusicXMLOpts } from '../score';\n\nexport interface FallingKeyboardDemoOpts {\n /** Frame size. Default phone-portrait 1080×1920. */\n size?: [number, number];\n /** Theme key for the spec. Default \"rsr\". */\n theme?: string;\n /** falling-notes lead time (ms). Default 2200. */\n leadMs?: number;\n /** Colour mode for both layers. Default \"hand\". */\n colorBy?: 'hand' | 'pitch-class';\n /** Keyboard range. Default \"auto\" (fit the piece's pitch span). */\n range?: '88' | 'auto';\n /** Forwarded to scoreFromMusicXML (tempo fallback/override/osmdFactory). */\n scoreOpts?: ScoreFromMusicXMLOpts;\n}\n\n/** The SceneSpec for the demo (background + falling-notes + keyboard, full clip). */\nexport function fallingKeyboardDemoSpec(opts: FallingKeyboardDemoOpts = {}): SceneSpec {\n const size = opts.size ?? [1080, 1920];\n const leadMs = opts.leadMs ?? 2200;\n const colorBy = opts.colorBy ?? 'hand';\n const range = opts.range ?? 'auto';\n return {\n size,\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'ink' } },\n // keyboard first so it publishes its layout before falling-notes inits;\n // draw order: keyboard bed under the falling blocks would hide them, so\n // we draw falling-notes ON TOP of the keyboard bed (falling listed last).\n { k: 'keyboard', p: { range, colorBy } },\n { k: 'falling-notes', p: { keyboard: true, colorBy, leadMs } },\n ],\n },\n ],\n };\n}\n\n/** Parse the demo Score from MusicXML (the caller supplies the XML string so the\n * bundling/fixture path stays in the host/test, not in src). */\nexport async function fallingKeyboardDemoScore(\n xml: string,\n opts: FallingKeyboardDemoOpts = {},\n): Promise<Score> {\n return scoreFromMusicXML(xml, opts.scoreOpts);\n}\n","// Standalone demo (S4) — proves the wrapped promo-card layers (hook / reveal /\n// portrait / cta) + spectrum + branding COMPOSE through the runner over a\n// timeline, with no app dependency. A host (or the golden test) buildScene()s\n// this and renders deterministic frames.\n//\n// Timeline (totalSec configurable, default 12s):\n// [0, hookSec] background + hook + branding\n// [hookSec, revealSec] background + spectrum + portrait + branding\n// [end-ctaSec, end] background + cta + branding\n//\n// Each card wrapper gets startMs/durationMs matching its segment so its internal\n// t01 sweeps 0..1 over the segment (the wrappers can't read segment bounds from\n// the runner — see promoCards.ts).\n\nimport type { SceneSpec } from '../runner';\n\nexport interface PromoCardsDemoOpts {\n /** Frame size. Default phone-portrait 1080×1920. */\n size?: [number, number];\n /** Theme key. Default \"rsr\". */\n theme?: string;\n /** Total clip length (s). Default 12. */\n totalSec?: number;\n /** Hook segment length (s). Default 2.2. */\n hookSec?: number;\n /** Reveal/portrait segment length (s). Default 4. */\n revealSec?: number;\n /** CTA segment length (s) at the end. Default 3. */\n ctaSec?: number;\n /** Hook lines. */\n hookLines?: string[];\n /** Reveal/portrait title (composer). */\n title?: string;\n /** Reveal/portrait subtitle. */\n subtitle?: string;\n /** Optional portrait URL (loaded crossOrigin in init; null → initials badge). */\n portraitUrl?: string | null;\n /** Optional fun fact under the portrait. */\n funFact?: string;\n /** CTA lines. */\n ctaLines?: string[];\n /** Brand wordmark. Default theme.brand (resolved by the host). */\n brand?: string;\n}\n\n/** Build the demo SceneSpec wiring the S4 wrapper layers across a timeline. */\nexport function promoCardsDemoSpec(opts: PromoCardsDemoOpts = {}): SceneSpec {\n const size = opts.size ?? [1080, 1920];\n const totalSec = opts.totalSec ?? 12;\n const hookSec = opts.hookSec ?? 2.2;\n const revealSec = opts.revealSec ?? 4;\n const ctaSec = opts.ctaSec ?? 3;\n const hookLines = opts.hookLines ?? ['Can you', 'name this?'];\n const title = opts.title ?? 'Claude Debussy';\n const subtitle = opts.subtitle ?? '1862–1918';\n const ctaLines = opts.ctaLines ?? ['Train your ear', 'realeartrainer.com'];\n const brand = opts.brand;\n\n const ms = (s: number) => Math.round(s * 1000);\n const hookStart = 0;\n const revealStart = hookSec;\n const ctaStart = totalSec - ctaSec;\n\n return {\n size,\n theme: opts.theme ?? 'rsr',\n durationMode: 'fixed',\n durationSec: totalSec,\n timeline: [\n // background spans the whole clip.\n { at: [0, 'end'], layers: [{ k: 'background', p: { style: 'paper' } }] },\n // hook card.\n {\n at: [hookStart, hookSec],\n layers: [{ k: 'hook', p: { lines: hookLines, brand, startMs: ms(hookStart), durationMs: ms(hookSec) } }],\n },\n // reveal phase: audio-reactive spectrum + the portrait medallion.\n {\n at: [revealStart, revealStart + revealSec],\n layers: [\n { k: 'spectrum', p: {} },\n {\n k: 'portrait',\n p: {\n title, subtitle, url: opts.portraitUrl ?? null, funFact: opts.funFact,\n startMs: ms(revealStart), durationMs: ms(revealSec),\n },\n },\n ],\n },\n // end-card CTA.\n {\n at: [ctaStart, 'end'],\n layers: [{ k: 'cta', p: { lines: ctaLines, startMs: ms(ctaStart), durationMs: ms(ctaSec) } }],\n },\n // persistent branding across the whole clip.\n { at: [0, 'end'], layers: [{ k: 'branding', p: { logo: brand } }] },\n ],\n audio: { voicing: 'reading' },\n };\n}\n","// Standalone demos (S5) — assemble the four extended-catalog layers into SceneSpecs\n// a host (or the golden test) can buildScene() + render deterministically. No app\n// is wired here; these are the reusable basis of later RSR/RET/whozart variants.\n\nimport type { SceneSpec } from '../runner';\nimport type { ChordSpan } from '../harmonyTrack';\nimport type { LabelMode } from '../degreeLabels';\nimport type { RenderedNotation } from '../../promo';\nimport type { Quiz } from '../quizCard';\nimport type { Section } from '../sectionMinimap';\n\nexport interface ExtendedDemoOpts {\n size?: [number, number];\n theme?: string;\n}\n\n/** Counting track + bouncing ball + scale-degree labels over a keyboard. */\nexport function countingDegreeDemoSpec(\n opts: ExtendedDemoOpts & { subdiv?: 1 | 2 | 4; labelMode?: LabelMode; range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'keyboard', p: { range: opts.range ?? 'auto' } },\n { k: 'degree-labels', p: { mode: opts.labelMode ?? 'degree' } },\n { k: 'counting-track', p: { subdiv: opts.subdiv ?? 2 } },\n ],\n },\n ],\n };\n}\n\n/** Staff↔keyboard ray over a (pre-rendered) notation + keyboard. */\nexport function staffRayDemoSpec(\n rendered: RenderedNotation,\n opts: ExtendedDemoOpts & { range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n // notation in a top band; keyboard a short strip at the very bottom, so\n // the rays span a clear vertical gap between staff and keys.\n { k: 'notation', p: { rendered, bandHeight: (opts.size?.[1] ?? 1920) * 0.28 } },\n { k: 'keyboard', p: { range: opts.range ?? '88', height: (opts.size?.[1] ?? 1920) * 0.16 } },\n { k: 'staff-keyboard-ray', p: {} },\n ],\n },\n ],\n };\n}\n\n/** Functional-harmony coloring (band + keys) over a keyboard, driven by a chord track. */\nexport function harmonyDemoSpec(\n chordTrack: ChordSpan[],\n opts: ExtendedDemoOpts & { range?: '88' | 'auto' } = {},\n): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'ink' } },\n { k: 'keyboard', p: { range: opts.range ?? 'auto' } },\n { k: 'functional-harmony', p: { chordTrack, modes: ['band', 'keys'] } },\n ],\n },\n ],\n };\n}\n\n// ─── S5 batch 2 demos ──────────────────────────────────────────────────────────\n\n/** MCQ poll card over a plain background (RET quiz / whozart name-that-piece). */\nexport function mcqDemoSpec(quiz: Quiz, opts: ExtendedDemoOpts = {}): SceneSpec {\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'fixed',\n durationSec: (quiz.endMs) / 1000,\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'mcq-card', p: { quiz } },\n ],\n },\n ],\n };\n}\n\n/** Circle-of-fifths widget (optionally animating a modulation). */\nexport function circleOfFifthsDemoSpec(\n opts: ExtendedDemoOpts & { toKey?: string; fromMs?: number; toMs?: number } = {},\n): SceneSpec {\n const p: Record<string, unknown> = {};\n if (opts.toKey) { p.toKey = opts.toKey; p.fromMs = opts.fromMs ?? 0; p.toMs = opts.toMs ?? 2000; }\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'circle-of-fifths', p },\n ],\n },\n ],\n };\n}\n\n/** Pitch-contour line over the melody (Score.notes) + a section minimap. */\nexport function contourMinimapDemoSpec(\n opts: ExtendedDemoOpts & { sections?: Section[]; measureCount?: number } = {},\n): SceneSpec {\n const mini: Record<string, unknown> = {};\n if (opts.sections) mini.sections = opts.sections;\n else if (opts.measureCount) mini.measureCount = opts.measureCount;\n return {\n size: opts.size ?? [1080, 1920],\n theme: opts.theme ?? 'rsr',\n durationMode: 'audio',\n timeline: [\n {\n at: [0, 'end'],\n layers: [\n { k: 'background', p: { style: 'paper' } },\n { k: 'pitch-contour', p: {} },\n { k: 'section-minimap', p: mini },\n ],\n },\n ],\n };\n}\n"],"mappings":"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","names":["msPerBeat","w","timeToX","DEFAULT_SIZE","DEFAULT_FADE","roundRect","measureCount","measureCount","rgba","DEFAULTS","rgba","DEFAULTS","rgba","SPEC_BARS","clamp01","lerpHex","syntheticMagnitude","binByteFreq","DEFAULTS","rgba","DEFAULTS","clamp01","wrap","DEFAULTS","rgba","DEFAULTS","rgba","rgba","DEFAULTS","DEFAULTS","easeOut","clamp01","DEFAULTS","clamp01","easeOut","DEFAULTS","clamp01","roundedPath"]}