chore: import upstream snapshot with attribution
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:35 +08:00
commit 85453da49f
4031 changed files with 710987 additions and 0 deletions
@@ -0,0 +1,314 @@
---
version: alpha
name: Coral — Frame (video / frame layer)
description: >
Video-first companion to Coral's design.md. The unit is the frame (1920×1080), not the
slide-in-a-deck. Atoms are identical and sacred — the three-surface system (coral fire /
ink black / warm cream), Bebas Neue uppercase tracked + Inter body, the 45° diagonal hatch,
decorative wallpaper numerals, hard color-region splits, zero shadow, zero radius (save
circles). Composition, frame scale, and aspect-ratio behavior are rewritten for the frame.
Motion is out of scope.
unit: the frame — 1920×1080 primary; 9:16 and 1:1 documented
principle: atoms are sacred · composition is free · numbers come from the script
colors:
coral: "#E85D5D"
coral-dark: "#D44A4A"
cream: "#F5F0E8"
cream-dark: "#E8E0D4"
black: "#1A1A1A"
gray: "#6B6B6B"
light-gray: "#B0B0B0"
white: "#FFFFFF"
typography:
# — reading ramp (Inter) —
body: { fontFamily: "Inter", cqw: 1.0, weight: 400, lineHeight: 1.7 }
body-light: { fontFamily: "Inter", cqw: 1.5, weight: 300, lineHeight: 1.5, note: "pull-quote voice" }
section-label: { fontFamily: "Inter", px: 12, weight: 700, tracking: "4px", upper: true }
item-label: { fontFamily: "Inter", px: 11, weight: 700, tracking: "3px", upper: true }
quote-attribution: { fontFamily: "Inter", px: 14, weight: 600, tracking: "3px", upper: true }
quote-role: { fontFamily: "Inter", px: 12, weight: 400, tracking: "1px" }
# — display / hero ramp (Bebas Neue, uppercase, tracked) —
card-title: { fontFamily: "Bebas Neue", cqw: 1.9, weight: 400, lineHeight: 1.1, tracking: "1px", upper: true }
sidebar-value: { fontFamily: "Bebas Neue", cqw: 2.3, weight: 400, lineHeight: 1.0, upper: true }
bar-title: { fontFamily: "Bebas Neue", cqw: 2.3, weight: 400, lineHeight: 1.0, tracking: "2px", upper: true }
card-stat: { fontFamily: "Bebas Neue", cqw: 2.5, weight: 400, lineHeight: 1.0, upper: true }
column-title: { fontFamily: "Bebas Neue", cqw: 3.7, weight: 400, lineHeight: 1.0, tracking: "2px", upper: true }
section-headline:{ fontFamily: "Bebas Neue", cqw: 4.2, weight: 400, lineHeight: 1.0, tracking: "2px", upper: true }
stat-numeral: { fontFamily: "Bebas Neue", cqw: 5.0, weight: 400, lineHeight: 1.0, upper: true }
hero-title: { fontFamily: "Bebas Neue", cqw: 6.5, weight: 400, lineHeight: 0.9, tracking: "4px", upper: true }
jumbo-feature: { fontFamily: "Bebas Neue", cqw: 9.0, weight: 400, lineHeight: 1.0, tracking: "12px", upper: true }
# — decorative —
background-numeral: { fontFamily: "Bebas Neue", cqw: 10.0, weight: 400, color: "rgba(0,0,0,0.12)", note: "wallpaper numeral inside a coral region" }
giant-mark: { fontFamily: "Bebas Neue", cqw: 14.0, weight: 400, color: "rgba(0,0,0,0.35)", note: "decorative quote mark inside a coral region" }
spacing:
pad-x: "5cqw" # standard horizontal frame padding
pad-y: "4cqw"
pad-col: "3cqw"
gap-grid: "1.7cqw"
card-pad: "2cqw"
components:
diagonal-hatch:
backgroundImage: "repeating-linear-gradient(45deg, transparent 0 20px, rgba(0,0,0,.06) 20px 40px)"
placement: "::before overlay on {colors.coral} regions"
description: "Signature 45° hatch (6% ink). Variants 45° 30/60px, 90° vertical 60/62px 10% ink. Texture, never depth."
region-split:
layout: "two/three solid surfaces meeting at a hard edge; ratios 38/62 rows, 40/60 cols, 50/50"
rounded: "0"
description: "The primary layout device — no gradient, no rounded junction; the boundary is the layout."
card:
backgroundColor: "{colors.white}"
borderTop: "0.26cqw solid {colors.coral}"
rounded: "0"
shadow: "none"
typography: "{typography.card-title} + {typography.body}"
description: "5px coral TOP border is the only chrome; holds a card-icon, title, body, coral card-stat."
sidebar-item:
backgroundColor: "{colors.white}"
borderLeft: "0.2cqw solid {colors.coral}"
rounded: "0"
typography: "{typography.sidebar-value} + {typography.section-label}"
description: "4px coral LEFT border is the only chrome."
card-icon:
backgroundColor: "{colors.coral}"
textColor: "{colors.white}"
size: "2.5cqw square"
rounded: "0"
description: "The card mark — one white Bebas glyph centered."
accent-line:
backgroundColor: "{colors.coral}"
size: "4cqw × 0.25cqw (60×4 closing variant)"
rounded: "0"
description: "Sub-headline accent rule."
background-numeral:
typography: "{typography.background-numeral}"
color: "rgba(0,0,0,0.12)"
placement: "behind a {colors.coral} region's title"
description: "The wallpaper-numeral signature (12% ink)."
giant-mark:
typography: "{typography.giant-mark}"
color: "rgba(0,0,0,0.35)"
placement: "inside a {colors.coral} region"
description: "Oversized quote mark / character, half-decorative."
timeline:
line: "0.2cqw solid {colors.black} (gradient-dashed ::after)"
node: "{colors.coral} circle, {colors.cream} halo, 50% radius"
description: "Ink line with coral nodes + cream halos."
info-bar:
backgroundColor: "{colors.cream-dark}"
typography: "{typography.bar-title} + uppercase {typography.section-label}"
rounded: "0"
shadow: "none"
description: "Footer band beneath a feature region — Bebas title left, Inter meta right."
---
# Coral — Frame (video / frame layer)
## Overview
Coral at frame scale is a **bold magazine poster** built from three solid surfaces — coral fire,
ink black, warm cream — that meet at **hard color edges.** The region boundary IS the layout: a
frame splits into a coral plane + a cream plane, or a coral panel + an ink panel, each holding a
self-contained composition. No gradient transitions, no rounded junctions, no drop shadows.
The voice is a two-face hierarchy: **Bebas Neue** — tall condensed caps, always uppercase, always
tracked (112px) — carries every headline, stat, title, and meta figure; **Inter** carries every
body line, label, and attribution across weights 300700. Bebas declares; Inter explains. The
signature atmospherics are the **45° diagonal hatch** (6% ink) over coral regions and the
**oversized wallpaper numeral** (12% ink) behind a region's title.
**Key characteristics at frame scale:**
- **Three surfaces, hard edges** — `{colors.coral}` / `{colors.black}` / `{colors.cream}` as solid regions.
- **Bebas uppercase + tracking** on every display element; **Inter** on every body/label.
- **45° hatch** (6% ink) on coral regions; **wallpaper numerals** (12%) and **giant marks** (35%) behind content.
- **Ink-on-fire** — Bebas on coral is always ink, never white. Eyebrows coral on cream/ink, ink on coral.
- **Flat** — no shadow, no elevation; radius only on circles (nav dots, timeline nodes).
- **Coral as accent AND environment** — 45px coral borders, 48px coral icon squares, and full coral regions.
### Frame Craft Bar
Three eyeball tests gate every frame before any structural check:
- **Squint** — one element dominates at **36× its nearest neighbor**: the `hero-title`/`jumbo-feature` or a wallpaper numeral behind a region's title, never two rival headlines.
- **Silence** — coral/cream/ink regions read **4055% empty**; the **three-column catalog is the one dense exception**. A coral region underfilled gets a wallpaper numeral, never more content.
- **Restraint** — coral fires as **either accent or one full region per frame** (not both at full strength); one giant-mark per quote; ink-on-fire (never white on coral).
- **Reference** — aim at a **sports-magazine cover / Saul Bass travel poster** (solid planes at hard edges, condensed caps as architecture); failure looks like a **soft drop-shadowed card deck**.
## The Frame
- **Primary:** 1920×1080 (16:9). Display sizes authored in **`cqw`** (`px ÷ 1920 × 100 = cqw`).
- **Vertical:** 1080×1920 (9:16). **Square:** 1080×1080 (1:1).
- **Safe area:** `5cqw` (pad-x) standard frame padding; region edges may bleed full-frame.
**The container law (load-bearing).** Every frame ground sets `container-type: size`; ALL
frame-relative units are `cqw`/`cqh` resolved against it — **never `vw`.** A `vw`-sized frame
inflates whenever it isn't full-screen; `cqw` resolves against the frame at any render size.
## Colors
Tokens identical to the source. At frame scale the three surfaces are intermixed by composition —
coral/cream, coral/ink, ink/cream, or single-surface. `{colors.coral}` is both accent (borders,
icon squares, timeline nodes, eyebrows on cream/ink) and environment (full regions). Headlines:
ink on cream/coral, cream on ink — **never gray, never white-on-coral.** Eyebrows: coral on
cream/ink, ink on coral — coral-on-coral does not exist. The only sanctioned gradient is the rare
135° coral-dark→coral feature region; everything else is flat.
## Typography
Two ramps. The **reading ramp** (Inter body 1.0cqw, body-light 1.5cqw, labels in px) carries copy
and eyebrows; the **display/hero ramp** (Bebas, `card-title` 1.9cqw → `jumbo-feature` 9.0cqw, plus
the decorative `background-numeral` 10cqw and `giant-mark` 14cqw) carries every headline and stat.
- **Legibility floor:** any load-bearing line ≥ **1.4cqw**; px labels are chrome only.
- **Fit-to-measure:** size the headline to its line length. Cap the block at **≤ 78cqw**; ≤3 words → `hero-title`/`jumbo-feature`; 46 → `section-headline`; 7+ → `column-title`.
- **Every Bebas element is uppercase with ≥1px tracking** (2px standard, 4px hero, 12px jumbo). **Every Inter label is uppercase, 14px tracked.** No italic, no underline, no sentence-case Bebas.
## Depth & Surface
Flat, with hard color edges. Depth signals only:
- **Hard region boundaries** — the primary structural device.
- **Accent borders** — 5px coral top (cards), 4px coral left (sidebar tiles), 4px ink (timeline).
- **45° hatch** — 6% ink texture on coral regions (no depth).
- **Wallpaper typography** — numerals at 12%, giant marks at 35%, layered behind content.
**Ceiling:** no box-shadow, no elevated card, no soft gradient (save the one 135° coral feature), no rounded rectangle.
## Shapes
- **0 radius** on every rectangle — regions, cards, sidebar tiles, icon squares, info bars, accent lines.
- **50%** on circles only — nav dots (10px), nav arrows (44px), timeline nodes (20px).
## Components
- **region-split** — the layout device; surfaces meet at a hard edge.
- **card** (5px coral top) / **sidebar-item** (4px coral left) / **card-icon** (48px coral square) — the only chrome on each is its single coral border.
- **diagonal-hatch** / **background-numeral** / **giant-mark** — the atmospheric + wallpaper signatures on coral regions.
- **accent-line** — coral sub-headline rule. **timeline** — ink line, coral nodes, cream halos. **info-bar** — cream-dark footer band.
## Frame Treatments
> Recipe per plate: ground · container · composes · focal · chrome · accent · silence · Fixed/Free · density.
> Lean centered where the move allows; vary anchor; one idea per region.
### 1 · Region-Split Cover (identity · move: hard region edge · left)
**Ground** 38/62 split — `{colors.coral}` top band (hatch + wallpaper numeral) over `{colors.cream}`.
**Container** grid rows; brand + meta in the coral band, hero title in the cream field. **Composes**
region-split, diagonal-hatch, background-numeral, hero-title. **Focal** a 2-line `hero-title` in
ink, second line in `{colors.coral}`, left-anchored in the cream field. **Chrome** Bebas brand left
- Bebas meta right in the coral band. **Accent** coral band + coral second line. **Silence** the
cream field ~45% empty. **Fixed** ink-on-fire in band, hatch on coral, hard edge. **Free** title
copy, which line is coral, meta. **Density** sparse.
### 2 · Feature Stat (anchor · move: scale · coral environment · left)
**Ground** full `{colors.coral}` with hatch. **Composes** diagonal-hatch, background-numeral,
section-label, stat headline, body-light. **Focal** a `stat-numeral`/`jumbo-feature` figure or
2-line headline in ink, with a `background-numeral` (12% ink) behind it as wallpaper. **Chrome** an
ink `section-label` eyebrow; an optional Inter-300 support line ≤44cqw. **Accent** the coral ground
IS the environment; ink type, no white. **Silence** ~40% of the coral field empty. **Fixed**
ink-on-coral, hatch present, wallpaper numeral behind. **Free** the figure, headline, support copy.
**Density** sparse.
### 3 · Quote Layout (quote · move: panel split · giant mark)
**Ground** 40/60 split — `{colors.coral}` left panel (hatch + giant-mark) + `{colors.black}` right
panel. **Composes** region-split, giant-mark, body-light, accent-line, quote-attribution.
**Focal** a 23 line pull quote in `{colors.cream}` Inter **weight 300** on the ink panel.
**Chrome** a `giant-mark` (35% ink) on the coral panel; a `60×4` coral accent-line above the
attribution. **Accent** coral panel + coral accent-line. **Silence** the coral panel is mostly the
mark. **Fixed** Inter-300 quote, ink panel, ink-on-coral mark. **Free** quote, attribution, mark
glyph. **Density** sparse.
### 4 · Closing Plate (closer · move: cream field + coral band · centered)
**Ground** `{colors.cream}` field with a bottom `{colors.coral}` band (hatch). **Composes**
section-label, section-headline/hero-title, accent-line, info-bar. **Focal** a 2-line sign-off in
ink, centered, with a coral `accent-line` beneath. **Chrome** an ink eyebrow above; a coral band
footer carrying a Bebas sign-off + year. **Accent** coral band + coral accent-line. **Silence**
~55% empty cream. **Fixed** centered, ink type, one coral band. **Free** sign-off copy, band
contents. **Density** sparse.
### 5 · Three-Column Catalog (catalog · move: density — the dense frame · centered head)
**Ground** `{colors.cream}` (or `{colors.black}`), `pad-x`. **Composes** section-headline, 3× card.
**Focal** a centered `section-headline` over three white `card`s (5px coral top, 48px icon square,
Bebas title, Inter body, coral stat). **Accent** the three coral top borders + icon squares.
**Silence** tight — the density exception. **Fixed** 5px coral top as sole chrome, no shadow/radius.
**Free** the three cards' content. **Density** dense-exception.
### 6 · Timeline (process · move: horizontal rail · left)
**Ground** `{colors.cream}`, `pad-x`. **Composes** section-headline, timeline. **Focal** the ink
timeline-line with 45 coral nodes (cream halos) and Bebas labels. **Accent** coral nodes.
**Silence** moderate. **Fixed** ink line, coral nodes, cream halos. **Free** node count, labels.
**Density** standard.
## Composition Rules
### Do
- Compose as **multi-surface region splits** — coral / ink / cream meeting at hard edges; the boundary is the layout.
- Set every Bebas element **uppercase + tracked** (2px standard, 4px hero, 12px jumbo); every Inter label uppercase, 14px.
- Render eyebrows **coral on cream/ink, ink on coral**; headlines ink on cream/coral, cream on ink.
- Apply the **45° hatch** (6% ink) on coral regions; fill underweight coral regions with a **12% wallpaper numeral**.
- Use the **5px coral top** (cards) / **4px coral left** (tiles) as the only chrome on those elements.
- Lean centered on cover sign-offs and catalog heads; left/panel-split on features and quotes.
### Don't
- Don't render Bebas in sentence case or untracked; don't pair it with a non-Inter body sans.
- Don't add a fourth surface, a drop shadow, an elevation, or a rounded rectangle.
- Don't put white headlines on coral (always ink) or gray headlines anywhere (gray is body/meta).
- Don't soften a region boundary with a gradient (except the rare 135° coral feature).
- Don't fill a coral region with sparse fragments — fully populate it or add a wallpaper numeral / giant mark.
- Don't blow a headline edge-to-edge — step the ramp down for long lines.
## Aspect-Ratio Behavior
| Treatment | 16:9 | 9:16 | 1:1 |
| -------------------- | -------------------------- | ------------------------------------ | -------------------- |
| Region-Split Cover | 38/62 rows, title left | taller coral band, title below | 40/60, title lower |
| Feature Stat | figure left, numeral right | figure top, numeral behind | centered figure |
| Quote Layout | 40/60 coral+ink | stacked: coral mark top, quote below | stacked |
| Closing Plate | cream + bottom coral band | cream + taller band | centered, band below |
| Three-Column Catalog | head over 3-up | head top, 3 stacked | head top, 2+1 |
| Timeline | horizontal rail | vertical rail | compact horizontal |
Safe area holds the `5cqw` padding on the short edge; re-step display per ratio so no load-bearing
line drops below the 1.4cqw floor. Bebas runs ~20% wider in CJK — adjust line breaks per ratio.
## Approved Entities
No real customers, logos, or vendors are defined in the source — render any such mark as a
placeholder. The system supplies surfaces and geometry, not brands.
## Numerals & Claims (hard rule)
Never invent figures, stats, dates, or counts at frame scale. Render slots as `— figure —`,
`{metric}`, `N×`. Real numerals appear only when the script supplies them — the feature stat,
catalog, and timeline especially carry placeholders, not fabricated values. Wallpaper numerals
(01, 02…) are decorative and may be ordinal.
## Pre-Render Self-Audit
- **Squint** — one focal element per region dominates at 35× its neighbor.
- **Silence** — sparse frames 4055% empty; only the catalog runs dense.
- **Surfaces** — two or three of coral/ink/cream, meeting at hard edges; no fourth surface.
- **Type** — Bebas uppercase + tracked, fit-to-measure; ink-on-coral; eyebrow color correct for surface; ≥1.4cqw floor.
- **Depth** — 0 shadow, 0 rounded rectangle; hatch + wallpaper carry texture.
- **Anchor** — centered on sign-offs/heads, panel/left on features/quotes; no 3 consecutive frames share an anchor.
- **Fabrication** — every numeral traces to the script, else placeholder.
## Known Gaps
- **Motion intentionally out of scope.** frame.md specifies composition only; timing and transitions are a later stage. The 0.6s opacity fade in the source is a deck mechanic, not a frame spec.
- **Bebas Neue + Inter via Google Fonts.** CJK pairing (ZCOOL XiaoWei / Yozai) carries over from the source's CJK section; Bebas runs ~20% wider in CJK.
- **9:16 / 1:1 are guidance**, not pixel-locked; verify the legibility floor per ratio.
- The 45° hatch, wallpaper numerals, giant marks, and timeline dash are CSS-only; no external imagery is required.
@@ -0,0 +1,226 @@
<!--
Coral — caption skin (preset-local source).
This is the preset's own lower-third karaoke caption look. product-launch-video's
Step 2 copies it into the project as caption-skin.html; captions.mjs then fills the
three reserved holes, wraps the fragment in a <template>, and writes
compositions/captions.html:
· the empty GROUPS array → the engine's scene-aware word groups
· the 0 duration + 0 width/height → real total + canvas size
· the empty data-brand-tokens style → :root tokens derived from the project's frame.md
Token contract — reference ONLY this fixed vocab (captions.mjs injects it from
frame.md, so the Step-2 brand overlay flows through; each var has a coral literal
fallback for standalone preview):
--cap-ink · --cap-canvas · --cap-accent · --cap-accent-2 · --font-display ·
--font-body · --cap-band-top · --cap-band-height
Hooks (do not rename): .caption-group / .caption-word + states .is-active / .is-spoken,
root data-composition-id="captions", timeline at window.__timelines["captions"].
State changes use gsap.set({className}) (applied on the engine's frame-by-frame seek);
never tl.call() callbacks (seek does not fire them → wrong state on render).
Visual: a flat warm-cream block on a single ink hairline border — coral's hard-edge
geometry, square corners, ZERO radius, ZERO shadow (coral's depth is hard color edges,
never cast). Bebas Neue, uppercase + tracked, ink glyphs. Upcoming words sit in faint
ink; the current word gets a solid coral-fire flat block (the brand's signature
hard-edge fill — a square block, no blur, no radius) with ink reading on top; spoken
words settle to solid ink, fill cleared. Brand/key terms carry a coral hairline
underline (the counter inflection).
-->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<!-- captions.mjs injects the project's :root tokens here -->
<style data-brand-tokens></style>
<style>
#captions-root {
position: absolute;
inset: 0;
pointer-events: none;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
}
/* the keep-out band, from captionBand(H) — frame content stays above it */
.caption-stage {
position: absolute;
left: 0;
right: 0;
top: var(--cap-band-top, 900px);
height: var(--cap-band-height, 180px);
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
/* every group is absolutely stacked over the SAME band — exactly one shown at a
time via opacity. Must be absolute: in-flow groups would lay out side-by-side. */
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
/* THE flat cream block — single ink hairline border, square corners (coral's hard-edge
geometry), ZERO radius, ZERO shadow */
.caption-pill {
max-width: 78%;
padding: 18px 44px 20px;
background: var(--cap-canvas, #f5f0e8);
border: 1px solid var(--cap-ink, #1a1a1a);
border-radius: 0;
}
.caption-line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.1em 0.3em;
font-family: var(--font-display, "Bebas Neue"), sans-serif;
font-weight: 400;
text-transform: uppercase;
font-size: clamp(44px, 4.8vw, 64px);
line-height: 1.04;
letter-spacing: 0.06em;
}
/* upcoming — faint ink, no fill */
.caption-word {
display: inline-block;
padding: 0 0.12em;
color: color-mix(in srgb, var(--cap-ink, #1a1a1a) 40%, var(--cap-canvas, #f5f0e8));
}
/* current word — THE coral-fire flat block (box-shadow spread = a hard square block
w/o layout shift; no blur, no radius), ink reading on top */
.caption-word.is-active {
color: var(--cap-ink, #1a1a1a);
background: var(--cap-accent, #e85d5d);
box-shadow: 0 0 0 0.1em var(--cap-accent, #e85d5d);
}
/* already spoken — solid ink, fill cleared */
.caption-word.is-spoken {
color: var(--cap-ink, #1a1a1a);
background: transparent;
box-shadow: none;
}
/* optional per-word accents (dormant until a word-classer tags them) */
/* brand / key term — a coral hairline underline (coral's only display inflection) */
.caption-word.cap-brand {
text-decoration: underline;
text-decoration-thickness: 0.06em;
text-decoration-color: var(--cap-accent, #e85d5d);
text-underline-offset: 0.12em;
}
/* numerals / data — Inter body voice, slightly recessed */
.caption-word.cap-num {
font-family: var(--font-body, "Inter"), ui-sans-serif, sans-serif;
color: color-mix(in srgb, var(--cap-ink, #1a1a1a) 72%, var(--cap-canvas, #f5f0e8));
}
/* emphasis — a coral-dark hairline underline (the counter accent) */
.caption-word.cap-cta {
text-decoration: underline;
text-decoration-thickness: 2px;
text-decoration-color: var(--cap-accent-2, #d44a4a);
text-underline-offset: 0.1em;
}
@media (max-aspect-ratio: 9/16) {
.caption-pill {
max-width: 92%;
}
.caption-line {
font-size: clamp(38px, 5.8vw, 54px);
}
}
</style>
<div
id="captions-root"
data-composition-id="captions"
data-timeline-locked
data-start="0"
data-duration="0"
data-fps="30"
data-width="0"
data-height="0"
>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="caption-stage"></div>
</div>
</div>
<script>
// captions.mjs injects the engine's scene-aware groups + real duration here.
var GROUPS = [];
var DURATION = 0;
(function () {
var stage = document.getElementById("caption-stage");
// build DOM: one .caption-group per group, .caption-word per word
GROUPS.forEach(function (group, g) {
var groupEl = document.createElement("div");
groupEl.className = "caption-group";
groupEl.id = "caption-group-" + g;
var pill = document.createElement("div");
pill.className = "caption-pill";
var line = document.createElement("div");
line.className = "caption-line";
(group.words || []).forEach(function (w, i) {
var span = document.createElement("span");
span.className = "caption-word";
span.id = "caption-word-" + g + "-" + i;
span.textContent = String(w.text);
line.appendChild(span);
});
pill.appendChild(line);
groupEl.appendChild(pill);
stage.appendChild(groupEl);
});
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, g) {
var groupEl = document.getElementById("caption-group-" + g);
var words = group.words || [];
var next = GROUPS[g + 1];
var isLast = g === GROUPS.length - 1;
var start = Math.max(0, Number(group.start));
var end = isLast ? DURATION : Math.min(Number(next.start), Number(group.end) + 0.3);
if (end <= start) end = start + 0.01;
// group on/off — exactly one group visible at a time
tl.set(groupEl, { opacity: 1 }, start);
tl.set(groupEl, { opacity: 0 }, end);
// 3-state karaoke via className SETS — seek-safe (gsap.set applies on the
// engine's frame-by-frame seek; .call callbacks would NOT fire).
words.forEach(function (w, i) {
var el = document.getElementById("caption-word-" + g + "-" + i);
var at = Math.max(start, Number(w.start));
tl.set(el, { className: "caption-word" }, start); // upcoming (also resets on reverse-seek)
tl.set(el, { className: "caption-word is-active" }, at); // current
tl.fromTo(el, { scale: 0.97 }, { scale: 1, duration: 0.14, ease: "power2.out" }, at);
if (i + 1 < words.length) {
var nextAt = Math.max(start, Number(words[i + 1].start));
tl.set(el, { className: "caption-word is-spoken" }, nextAt); // demote when next activates
}
});
if (words.length) {
var lastEl = document.getElementById("caption-word-" + g + "-" + (words.length - 1));
var lastSpoken = Math.min(end, Number(words[words.length - 1].end) + 0.1);
tl.set(lastEl, { className: "caption-word is-spoken" }, lastSpoken);
}
});
// full-span anchor so the sub-comp timeline spans the whole video
tl.to({}, { duration: DURATION }, 0);
window.__timelines["captions"] = tl;
})();
</script>
File diff suppressed because it is too large Load Diff