From 94164e368266a9c857ec3c8d3fe51aa9572b501d Mon Sep 17 00:00:00 2001 From: 1500122958 <1500122958@qq.com> Date: Wed, 29 Jul 2026 15:12:04 +0800 Subject: [PATCH] feat(content-cards): add air theme and chart layouts --- skills/video-add-content-cards/SKILL.md | 110 +- .../assets/content-cards-review.html | 316 ++++- .../assets/fonts/Lexend-VariableFont_wght.ttf | Bin 0 -> 174528 bytes .../examples/build-gallery.mjs | 35 +- .../examples/gallery-animated.html | 109 +- .../examples/index-air.html | 1206 +++++++++++++++++ .../examples/index-almanac.html | 423 +++++- .../examples/index-apex.html | 314 ++++- .../examples/index-dotgrid.html | 310 ++++- .../examples/index-editorial.html | 316 ++++- .../examples/index-teal.html | 328 ++++- .../examples/shoot.mjs | 18 +- .../reference/chart-data.md | 122 ++ .../reference/ux-design.md | 28 +- .../scripts/apply_cards_review.py | 60 +- .../scripts/build_cards_plan.py | 160 ++- .../scripts/build_review_page.py | 108 +- 17 files changed, 3872 insertions(+), 91 deletions(-) create mode 100644 skills/video-add-content-cards/assets/fonts/Lexend-VariableFont_wght.ttf create mode 100644 skills/video-add-content-cards/examples/index-air.html create mode 100644 skills/video-add-content-cards/reference/chart-data.md diff --git a/skills/video-add-content-cards/SKILL.md b/skills/video-add-content-cards/SKILL.md index 96e7af73f..487d4ec6f 100644 --- a/skills/video-add-content-cards/SKILL.md +++ b/skills/video-add-content-cards/SKILL.md @@ -1,6 +1,6 @@ --- name: video-add-content-cards -description: Use when an understood video project needs selective transcript-timed titles, lower-thirds, statistics, lists, quotes, chapter cards, or calls to action authored as HyperFrames HTML graphics. +description: Use when an understood video project needs selective transcript-timed titles, lower-thirds, statistics, metric spotlights, comparisons, lists, quotes, chapter cards, or calls to action authored as HyperFrames HTML graphics. --- # Video Add Content Cards @@ -77,7 +77,7 @@ xdg-open skills/video-add-content-cards/examples/gallery-animated.html ``` If the command fails, diagnose and retry it. Do not replace this action with a URI or ask the -user to find and click the file. The gallery compares all five themes and can focus one column +user to find and click the file. The gallery compares all six themes and can focus one column with its native picker: | Theme | Starting character | @@ -87,6 +87,7 @@ with its native picker: | `editorial` | documentary, ink and amber | | `dotgrid` | pixel-mono, technical | | `apex` | high-energy sports, red accent | +| `air` | transparent Lexend typography, no backing plates or scrims | Ask exactly one question at a time in this order: @@ -112,7 +113,11 @@ python skills/video-add-content-cards/scripts/build_cards_plan.py ` --notes "Keep product names verbatim" ``` -The script maps kept semantic moments into program time and omits moments removed by the cut. It preserves source ranges and evidence references, clamps duration to the containing clip, and marks copy/placement/visual treatment as `draft`. +The script maps kept semantic moments into program time and omits moments removed by the cut. It +preserves source ranges and evidence references, clamps duration to the containing clip, and +marks copy/placement/visual treatment as `draft`. A `stat` starts with `metric-spotlight`; a +`list` starts with `side-by-side`; the candidate review can retain the normal layout or switch +a list to `parallel-columns`. The confirmed interview is stored as `brief` in `cards-plan.json`. Repeat `--must-include-type` for multiple values; omit flags that do not apply. Older optional brief fields remain accepted for compatibility but are not part of the normal interview. @@ -139,6 +144,33 @@ Use these mappings as a starting point: `repetition`, `tangent`, and `risk` are editorial evidence, not automatic cards. +Keep the five canonical card types. Store the following optional layouts under +`visual_treatment.layout` rather than creating new top-level card types: + +| Canonical card type | Allowed layout | +|---|---| +| `stat` | `default`, `metric-spotlight`, `bar-chart`, `pie-chart`, `line-chart` | +| `list` | `default`, `side-by-side`, `parallel-columns` | + +Use `metric-spotlight` only when one evidence-backed number, percentage, amount, multiple, +year, or duration deserves emphasis. Put the number between concise upper and lower context, +fit long values without overlap, and keep the number visually centered between those labels. +Use `side-by-side` or `parallel-columns` for two to four short parallel, contrasting, opposing, +or corresponding concepts. Prefer short phrases; use the review to choose the orientation that +fits the available footage and clears the subject. + +Use a chart only when the retained transcript provides a complete, evidence-backed data set. +Read `reference/chart-data.md` before authoring chart data. Manually store the dimension, +metric, unit, period, ordered points, and each point's transcript evidence under `data`; never +infer values from an analyzer summary or combine points from different videos. Use `bar-chart` +for 2-6 comparable categories, `pie-chart` for 2-6 mutually exclusive parts of a proven whole, +and `line-chart` for 3-8 meaningfully ordered points. When required values, labels, a denominator, +time order, or units are missing, use `metric-spotlight`, a normal stat card, or no card. +For chart layouts, compress long explanations into concise supporting copy without changing +their meaning. The chart should primarily communicate the key values and their change or +relationship. Do not enumerate or restate values, labels, periods, or units already visible in +the chart. + Keep all evidence-backed candidates at this stage. Aim for the brief's target card count, but allow a stronger or sparser set when the evidence warrants it. @@ -154,9 +186,13 @@ python skills/video-add-content-cards/scripts/build_review_page.py ` work/content-cards/cards-plan.json ` review/03-content-cards/content-cards-review.html ` --video input/source.mp4 ` - --timeline work/timeline.json + --timeline work/timeline.json ` + --captions-plan work/captions/captions-plan.json ``` +Omit `--captions-plan` when the project has no active captions operation. With an active +captions plan, the review frame shows its actual occupied top, center, or bottom regions. + Immediately open the populated review yourself with the native command for the current OS. Run exactly one: @@ -174,17 +210,22 @@ xdg-open review/03-content-cards/content-cards-review.html ``` If the command fails, diagnose and retry it; do not ask the user to locate the file. The page -shows each source frame, card ID, program time, editable copy, and placement. Every candidate +shows each source frame, card ID, program time, editable copy, visual treatment, and placement. +Chart layouts also show editable structured data and the point evidence references. Review +every value against its displayed evidence before approving it. +Every candidate starts unselected with placement set to `bottom`. Changing placement moves a gray card proxy over the real frame so collision risk is visible before approval. The user selects cards, -edits copy, chooses placement, clicks **Copy summary**, and pastes the visible summary back +edits copy, chooses treatment and placement, clicks **Copy summary**, and pastes the visible +summary back into the conversation. **Present + STOP.** Wait for the pasted selection summary. Do not author HTML from unchecked draft candidates. Convert the summary into `review/03-content-cards/content-cards-review.json`: write one entry for every plan card, mark -summary IDs selected with their chosen copy and placement, and mark all other IDs unselected -with their current draft copy and an empty placement. Then apply the validated review: +summary IDs selected with their chosen copy, visual treatment, and placement, and mark all +other IDs unselected with their current draft copy and an empty placement. Store the review +choice as a string field named `visual_treatment`. Then apply the validated review: ```powershell python skills/video-add-content-cards/scripts/apply_cards_review.py ` @@ -192,15 +233,27 @@ python skills/video-add-content-cards/scripts/apply_cards_review.py ` review/03-content-cards/content-cards-review.json ``` -The apply step rejects unknown, duplicate, or missing card IDs, blank selected copy, and -invalid placement without changing the plan. On success it keeps selected cards and marks +The apply step rejects unknown, duplicate, or missing card IDs, blank selected copy, invalid +placement, and treatments incompatible with the canonical card type without changing the plan. On success it keeps selected cards and marks their copy, placement, visual treatment, and top-level review `approved`. Read the resulting selected count against the brief target before continuing. +For selected chart layouts the apply step also validates the complete structured data and marks +`data.status` approved. Any invalid selected chart rejects the whole review before the plan is +copied. Choosing a non-chart layout removes unused draft chart data. After apply, face and +caption clearance remain pending until the composition is checked against real composited frames. + ### 5. Author one HyperFrames composition Use one `index.html` and a paused GSAP timeline. Each approved card becomes one `.clip` keyed by the plan's `program_start_s` and `duration_s`. Keep motion seek-safe and derive timing from data attributes rather than wall-clock timers. +For chart cues, animate only through that paused timeline at absolute composition times. Bars +reveal from one zero baseline; pie sectors reveal in point order with geometry proportional to +values; line paths draw in X-axis order before their points appear. Use source-relative sizing +and fixed layout boxes so seeking, labels, and dynamic values never cause layout jumps. Suggested +durations are 5-6 seconds for bar and pie and 6-8 seconds for line, always clamped to the retained +clip. + Start from the closest repository example: - `examples/index-almanac.html` @@ -208,19 +261,27 @@ Start from the closest repository example: - `examples/index-dotgrid.html` - `examples/index-editorial.html` - `examples/index-teal.html` +- `examples/index-air.html` -Keep one visual language across all cards. Use edge anchoring, a card or scrim for legibility, source-relative sizing, and enough contrast against the selected grade. Do not cover the speaker or captions. +Keep one visual language across all cards, use source-relative sizing, and maintain enough +contrast against the selected grade. The five opaque-panel themes may use a card or scrim for +legibility. `air` must keep scene and text containers transparent: no solid or translucent +backing rectangles, pills, local color blocks, decorative color bars, or full-frame scrims. +Use white for primary text and light gray for auxiliary small text. It may use Lexend weight, +text shadow, a thin outline, spacing, and alignment, but no decorative rules, dots, or filled +geometric accents. Do not cover the speaker or captions. Pass the source FPS explicitly; preserve fractional rates such as `24000/1001` rather than rounding to 24 or 30. On Windows use `npx.cmd`, a project-local npm cache, and local assets: ```powershell $env:npm_config_cache = "$PWD/work/cache/npm" -npx.cmd hyperframes lint -npx.cmd hyperframes validate +npx.cmd hyperframes check ``` -Do not depend on remote fonts, images, scripts, or styles at render time. +Do not depend on remote fonts, images, scripts, or styles at render time. For `air`, copy +`assets/fonts/Lexend-VariableFont_wght.ttf` into the project composition assets and record its +output path and SHA-256 in `renderer_recipe` like every other runtime asset. The approved plan must preserve the exact style and motion values used by the composition, not prose labels such as `edge-slide` alone. At minimum record panel geometry and padding, @@ -240,6 +301,10 @@ Check: - copy is true to its `evidence_ref`; - source and program times map correctly; - text is legible and fits; +- for chart cues, key values and their change remain the primary visual message, and supporting copy does not restate the plotted data set; +- the approved visual treatment matches the canonical card type; +- `air` has no backing plate, decorative rule, filled geometric accent, or full-frame scrim; +- `air` uses white primary text and light-gray auxiliary text; - faces and captions remain clear; - animation lands on the spoken phrase; - alpha is transparent outside card regions. @@ -251,6 +316,16 @@ Every approved cue must have a matching composited still in `review/03-content-cards/card-stills/`, `face_clearance: "verified"`, and the still path in the plan. Record the review result in `review/03-content-cards/content-cards-summary.md`. +Clearance happens after the user has selected cards and their initial placements. Sample the +cue near its start, middle, and end, plus any frame where the speaker moves materially. Set +`face_clearance: "verified"` only when the face outline, eyes, nose, mouth, and expression area +remain unobstructed. With active captions, use stills containing the real rendered caption +overlay and set `caption_clearance: "verified"`; without active captions use +`caption_clearance: "not-applicable"`. Save the composited still path, decision mode, and a +non-empty rationale. If any sampled frame collides, first move the card, then reduce its area or +label density, then choose another layout; remove the card if no safe result exists. Never hide, +move, rewrite, or reduce the readability of captions to make a card fit. + ### 7. Render the transparent contribution Prefer one short alpha MOV per cue so a few seconds of graphics do not create a full-program @@ -299,3 +374,10 @@ python skills/video-understand/scripts/render_project.py work/render/render-plan ## Combining With Captions Treat captions and cards as separate overlay contributions. Resolve their placement conflict before rendering, then composite them in declared sequence in the shared delivery pass. Keep captions at the bottom and move cards to the top when both would occupy the same safe area. + +When captions and cards are both being added, preserve the human gates in this order. First, +finish the captions preset review and its existing **Present + STOP**. Then run the content-card +candidate review and its **Present + STOP** so the user approves cards and chooses their initial +positions. Only after that selection does the Agent author the combined composition and perform +face/caption clearance. The later composited-still review remains required and presents the +clearance evidence for final approval. diff --git a/skills/video-add-content-cards/assets/content-cards-review.html b/skills/video-add-content-cards/assets/content-cards-review.html index 1b0fcaea3..442cde979 100644 --- a/skills/video-add-content-cards/assets/content-cards-review.html +++ b/skills/video-add-content-cards/assets/content-cards-review.html @@ -60,7 +60,7 @@ form { display: grid; gap: 10px; padding-top: 14px; } .candidate { display: grid; - grid-template-columns: 40px 150px minmax(220px, 1fr) 140px minmax(280px, 360px); + grid-template-columns: 40px 140px minmax(210px, 1fr) 170px 130px minmax(280px, 360px); gap: 14px; align-items: center; min-height: 104px; @@ -75,7 +75,7 @@ .identity strong { color: var(--text); font-size: 15px; } .identity span { overflow-wrap: anywhere; } .field { display: grid; gap: 6px; min-width: 0; color: var(--muted); } - input[type="text"], select { + input[type="text"], input[type="number"], textarea, select { width: 100%; min-height: 40px; border: 1px solid #50575c; @@ -85,7 +85,11 @@ font: inherit; padding: 8px 10px; } - input[type="text"]:focus, select:focus { border-color: var(--accent); outline: 2px solid #265950; } + input[type="text"]:focus, input[type="number"]:focus, textarea:focus, select:focus { + border-color: var(--accent); + outline: 2px solid #265950; + } + textarea { resize: vertical; } .frame-preview { position: relative; width: 100%; @@ -135,13 +139,60 @@ .placement-proxy[data-placement="center"] { top: 35%; left: 21%; width: 58%; height: 30%; opacity: 1; } + .placement-proxy[data-chart="true"][data-placement="top"] { + top: 4%; left: 15%; width: 70%; height: 34%; + } + .placement-proxy[data-chart="true"][data-placement="bottom"] { + bottom: 4%; left: 15%; width: 70%; height: 34%; + } + .placement-proxy[data-chart="true"][data-placement="left"] { + top: 15%; left: 3%; width: 42%; height: 70%; + } + .placement-proxy[data-chart="true"][data-placement="right"] { + top: 15%; right: 3%; width: 42%; height: 70%; + } + .placement-proxy[data-chart="true"][data-placement="center"] { + top: 22%; left: 15%; width: 70%; height: 56%; + } .placement-proxy span { display: -webkit-box; overflow: hidden; overflow-wrap: anywhere; + white-space: pre-line; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } + [data-caption-occupancy] { position: absolute; inset: 0; pointer-events: none; } + .caption-occupancy { + position: absolute; + z-index: 1; + border: 1px dashed rgba(243, 189, 91, 0.95); + background: rgba(243, 189, 91, 0.2); + } + .caption-occupancy[data-region="top"] { top: 6%; left: 16%; width: 68%; height: 24%; } + .caption-occupancy[data-region="center"] { top: 35%; left: 12%; width: 76%; height: 30%; } + .caption-occupancy[data-region="bottom"] { bottom: 6%; left: 12%; width: 76%; height: 24%; } + .chart-editor { + grid-column: 2 / -1; + min-width: 0; + border-top: 1px solid var(--line); + padding-top: 10px; + } + .chart-editor[hidden] { display: none; } + .chart-editor summary { color: var(--text); font-weight: 650; cursor: pointer; } + .chart-fields { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 10px; margin-top: 12px; } + .chart-points { display: grid; gap: 8px; margin-top: 10px; } + .chart-point { + display: grid; + grid-template-columns: minmax(110px, 1fr) minmax(90px, 0.7fr) minmax(180px, 1.6fr) 36px; + gap: 8px; + align-items: end; + } + .point-evidence { font: 12px/1.35 ui-monospace, "Cascadia Code", Consolas, monospace; } + .remove-point { width: 36px; min-height: 40px; padding: 0; font-size: 20px; } + .chart-actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; } + .chart-error { color: var(--warning); overflow-wrap: anywhere; } + button:disabled { cursor: not-allowed; opacity: 0.45; } .summary-box { margin-top: 20px; border: 1px solid var(--line); @@ -173,7 +224,7 @@ overflow-wrap: anywhere; } @media (max-width: 1100px) { - .candidate { grid-template-columns: 40px 150px minmax(220px, 1fr) 140px; } + .candidate { grid-template-columns: 40px 140px minmax(210px, 1fr) 170px 130px; } .frame-preview { grid-column: 2 / -1; width: min(520px, 100%); } } @media (max-width: 780px) { @@ -182,8 +233,12 @@ .toolbar button { width: 100%; } .summary { display: grid; gap: 2px; white-space: normal; } .candidate { grid-template-columns: 36px 1fr; align-items: start; } - .copy-field, .placement-field { grid-column: 2; } + .copy-field, .treatment-field, .placement-field { grid-column: 2; } .frame-preview { grid-column: 1 / -1; } + .chart-editor { grid-column: 1 / -1; } + .chart-fields { grid-template-columns: 1fr 1fr; } + .chart-point { grid-template-columns: 1fr 1fr 36px; } + .chart-point .evidence-field { grid-column: 1 / 3; } } @@ -221,6 +276,18 @@

Copy the following summary back to the AI agent.

Text + + + + + +
+
+ + +
+ + + + + + + + + + +
+ +
+
+
+
+
+
MONDAY MORNING MEETING · EP 14
+
HS TOP 200
+
with Herschel Fruean — NZ Schoolboy Rugby
+
+
+
+
+ + +
+
+
+
PART 1
+
The Christchurch Trip
+
+
+
+
+ + +
+
+
+
+
St Bede's vs Christ's
+
His two best teams down south
+
+
+
+
+ + +
+
+
+
0
+
points — a blowout
+
+
+
+ + +
+
+
+
+
HS TOP 200
+
Thanks for watching
+
Monday Morning Meeting Podcast
+
+
+
+ + +
+
+
+
EPISODE 14
+
The Season Ahead
+
+
+
+ + +
+
+
+
+
"Depth wins championships, not one star player."
+
+
+
+
+ + +
+
+
+
+
CONTEXT, NOT CONSTRAINTS
+
He's not fast enough for the wing
+
Instead say
+
He plays a different game — vision over pace
+
+
+
+
+ + +
+
+
+
+
TRY PROMPTING
+
"Rank the top 200 by upside, not just current form."
+
+
+
+
+ + +
+
+
+
+
+
BEFORE
+
Ranked 47th nationally
+
+
+
+
AFTER
+
Top 10 in one season
+
+
+
+
+
+ + +
+
+
+
+
WHAT SCOUTS WATCH
+
01Decision-making under pressure
+
02Work rate off the ball
+
03Composure in contact
+
+
+
+
+ + +
+
+
+
+
HOW TO WATCH
+
/rankingsthe full top 200 list
+
/players 62jump to a player card
+
/comparetwo prospects side by side
+
+
+
+
+ + +
+
+
+
THREE THINGS TO WATCH
+
01The Christchurch derby
+
02A record points margin
+
03Next year's breakout name
+
+
+
+ + +
+
+
+
+
2024 SEASON
+
0%
+
More line breaks
+
Already 37% above last season
+
+
+
+ + +
+
+
+
PLAYER PROFILE
+
Three ways to impact
+
+
+
Raw pace
+
Game vision
+
Composure
+
+
+
+
+ + +
+
+
+
+
PLAYER PROFILE
+
Three ways to impact
+
+
+
Raw pace
+
Game vision
+
Composure
+
+
+
+
+ +
FY2025
Revenue by quarter
USD millions
12
Q1
18
Q2
24
Q3
31
Q4
+
REVENUE MIX
One complete whole
Share of total
Product60%
Services25%
Partners15%
+
JAN–MAY 2025
Monthly revenue trend
USD millions
JanFebMarAprMay
+ + +
+ + diff --git a/skills/video-add-content-cards/examples/index-almanac.html b/skills/video-add-content-cards/examples/index-almanac.html index 05bebf587..a7d1f9b5b 100644 --- a/skills/video-add-content-cards/examples/index-almanac.html +++ b/skills/video-add-content-cards/examples/index-almanac.html @@ -425,16 +425,272 @@ font-size: 5vh; font-weight: 500; } + + /* ── MetricSpotlight: full-frame hero number over the source footage ──── */ + .metric { + position: absolute; + inset: 0; + } + .metric-scrim { + position: absolute; + inset: 0; + background: + radial-gradient(circle at 50% 48%, rgba(20, 18, 14, 0.18) 0 24%, rgba(20, 18, 14, 0.68) 72%), + linear-gradient(180deg, rgba(20, 18, 14, 0.42), rgba(20, 18, 14, 0.18) 40%, rgba(20, 18, 14, 0.68)); + } + .metric-wrap { + position: relative; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + } + .metric .kicker { + color: var(--cream); + font-size: 2.8vh; + font-weight: 500; + letter-spacing: 0.24em; + text-transform: uppercase; + } + .metric .hero-number { + display: flex; + align-items: flex-end; + justify-content: center; + margin: 5.2vh 0 0.2vh; + } + .metric .value { + color: var(--cream); + font-size: 34vh; + font-weight: 500; + letter-spacing: -0.07em; + line-height: 0.95; + text-shadow: 0 1.2vh 4vh rgba(0, 0, 0, 0.28); + } + .metric .unit { + color: var(--coral); + font-size: 9vh; + font-weight: 600; + line-height: 1; + margin: 0 0 2.3vh 1.2vh; + } + .metric .title { + color: var(--cream); + font-size: 5.2vh; + font-weight: 600; + letter-spacing: 0.08em; + line-height: 1; + text-transform: uppercase; + } + .metric .detail { + color: rgba(249, 247, 243, 0.82); + font-size: 3.2vh; + line-height: 1.25; + margin-top: 2.8vh; + } + + /* ── SideBySide: three parallel semantic terms in a vertical rail ────── */ + .side { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: flex-end; + box-sizing: border-box; + padding-right: 8vw; + background: linear-gradient(90deg, transparent 0 42%, rgba(20, 18, 14, 0.16) 58%, rgba(20, 18, 14, 0.54) 100%); + } + .side .card { + width: 31vw; + min-height: 72vh; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + padding: 4.2vh 3.6vh; + border: 0.22vh solid rgba(249, 247, 243, 0.9); + border-left: 0.9vh solid var(--coral); + border-radius: 3vh; + background: rgba(249, 247, 243, 0.95); + } + .side .kicker { + color: var(--coral); + font-size: 2.1vh; + font-weight: 600; + letter-spacing: 0.18em; + text-align: center; + text-transform: uppercase; + } + .side .title-rule { + display: flex; + flex-direction: column; + width: fit-content; + max-width: 100%; + margin: 0 auto; + } + .side .title { + color: var(--ink); + font-size: 4.8vh; + font-weight: 600; + line-height: 1.05; + margin-top: 1vh; + text-align: center; + } + .side .rule { + width: 100%; + height: 0.4vh; + margin: 2.2vh auto 3vh; + background: var(--coral); + } + .side .items { + display: flex; + flex-direction: column; + gap: 1.8vh; + } + .side .item { + color: var(--ink); + padding: 2.1vh 2vh; + border: 0.16vh solid rgba(92, 87, 78, 0.4); + border-radius: 0.8vh; + background: rgba(255, 255, 255, 0.34); + font-size: 3.8vh; + font-weight: 600; + letter-spacing: 0.04em; + line-height: 1; + text-align: center; + text-transform: uppercase; + } + /* ── ParallelColumns: equal-weight semantic terms across a lower rail ── */ + .parallel { + position: absolute; + inset: 0; + box-sizing: border-box; + display: flex; + align-items: flex-end; + justify-content: center; + padding: 0 5vw 7vh; + background: linear-gradient(180deg, transparent 0 54%, rgba(20, 18, 14, 0.52) 100%); + } + .parallel .card { + width: 100%; + box-sizing: border-box; + padding: 3.2vh 3.6vh 3.4vh; + border-top: 0.8vh solid var(--coral); + border-radius: 2.4vh; + background: rgba(249, 247, 243, 0.96); + box-shadow: 0 2vh 5vh rgba(0, 0, 0, 0.28); + } + .parallel .header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 4vw; + } + .parallel .kicker { + color: var(--coral); + font-size: 2.1vh; + font-weight: 600; + letter-spacing: 0.18em; + text-transform: uppercase; + } + .parallel .title { + color: var(--ink); + font-size: 4.2vh; + font-weight: 600; + line-height: 1; + text-align: right; + } + .parallel .items { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.6vh; + margin-top: 2.6vh; + } + .parallel .item { + color: var(--ink); + padding: 2.4vh 2vh; + border: 0.16vh solid rgba(92, 87, 78, 0.4); + border-radius: 0.8vh; + background: rgba(255, 255, 255, 0.4); + font-size: 4vh; + font-weight: 600; + letter-spacing: 0.04em; + line-height: 1; + text-align: center; + text-transform: uppercase; + } + /* ── Charts: structured stat layouts (96–114s) ─────────────────────── */ + .chart-scene { + display: grid; + place-items: center; + padding: 6%; + } + .chart-panel { + --chart-ink: var(--ink); + --chart-muted: var(--muted); + --chart-accent: var(--coral); + --chart-line: rgba(20, 18, 14, 0.34); + width: min(78%, 1220px); + height: min(72%, 720px); + min-height: 420px; + padding: 42px 50px 36px; + overflow: hidden; + border: 1px solid rgba(20, 18, 14, 0.24); + background: var(--cream); + color: var(--chart-ink); + container-type: inline-size; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + gap: 24px; + } + .chart-head { display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 24px; } + .chart-kicker { color: var(--chart-accent); font-size: 22.5px; font-weight: 700; text-transform: uppercase; } + .chart-title { margin-top: 4px; font-size: clamp(37.5px, 6.25cqi, 72.5px); font-weight: 600; line-height: 1; } + .chart-unit { color: var(--chart-muted); font-size: 22.5px; text-align: right; } + .chart-body { min-height: 0; } + .bar-plot { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 22px; height: 100%; } + .bar-item { display: grid; grid-template-rows: minmax(0, 1fr) 28px; gap: 8px; min-width: 0; text-align: center; } + .bar-value { position: absolute; right: 0; bottom: calc(var(--bar-value) * 1% + 8px); left: 0; color: var(--chart-ink); font-size: 31.25px; font-weight: 700; line-height: 40px; } + .bar-track { position: relative; display: flex; align-items: flex-end; justify-content: center; min-height: 0; margin-top: 40px; border-bottom: 2px solid var(--chart-ink); } + .bar-fill { width: min(72%, 118px); height: calc(var(--bar-value) * 1%); transform-origin: 50% 100%; background: var(--chart-accent); } + .bar-label, .line-label { overflow-wrap: anywhere; color: var(--chart-muted); font-size: 21.25px; font-weight: 650; } + .pie-layout { display: grid; grid-template-columns: 360px max-content; align-items: center; justify-content: center; gap: 28px; height: 100%; } + .pie-svg { display: block; width: min(100%, 360px); margin: auto; overflow: visible; } + .pie-sector { transform-box: fill-box; transform-origin: center; stroke: var(--cream); stroke-width: 4; } + .pie-sector:nth-child(1) { fill: var(--chart-accent); } + .pie-sector:nth-child(2) { fill: #d4a779; } + .pie-sector:nth-child(3) { fill: #7e786d; } + .chart-legend { display: grid; width: max-content; max-width: 100%; justify-self: start; gap: 14px; } + .legend-item { display: grid; grid-template-columns: 14px 1fr auto; align-items: center; gap: 12px; min-width: 0; } + .legend-swatch { width: 14px; height: 14px; background: var(--swatch); } + .legend-label { min-width: 0; overflow-wrap: anywhere; font-size: 23.75px; } + .legend-value { font-size: 27.5px; font-weight: 700; } + .line-layout { display: grid; grid-template-columns: minmax(0, 1fr); height: 100%; } + .line-svg { width: 100%; height: 100%; min-height: 250px; overflow: visible; } + .line-grid { stroke: var(--chart-line); stroke-width: 1.5; } + .line-path { fill: none; stroke: var(--chart-accent); stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; } + .line-point { fill: var(--cream); stroke: var(--chart-accent); stroke-width: 6; transform-box: fill-box; transform-origin: center; } + .line-axis-label { fill: var(--chart-muted); font: 650 21.25px var(--serif); text-anchor: middle; } + @container (max-width: 620px) { + .chart-panel { min-height: 500px; padding: 28px 24px; } + .chart-head { grid-template-columns: 1fr; gap: 8px; } + .chart-unit { text-align: left; } + .bar-plot { gap: 8px; } + .pie-layout { grid-template-columns: 1fr; gap: 18px; } + .pie-svg { width: min(100%, 260px); } + .chart-legend { width: 100%; justify-self: stretch; grid-template-columns: 1fr 1fr; gap: 8px 14px; } + } - -
+
@@ -600,6 +856,112 @@
+ +
+
+
+
+
2024 SEASON
+
0%
+
More line breaks
+
Already 37% above last season
+
+
+
+ + +
+
+
+
PLAYER PROFILE
+
+
Three ways to impact
+
+
+
+
Raw pace
+
Game vision
+
Composure
+
+
+
+
+ + +
+
+
+
+
PLAYER PROFILE
+
Three ways to impact
+
+
+
Raw pace
+
Game vision
+
Composure
+
+
+
+
+ + +
+
+
+
FY2025
Revenue by quarter
+
USD millions
+
+
+
12
Q1
+
18
Q2
+
24
Q3
+
31
Q4
+
+
+
+ + +
+
+
+
REVENUE MIX
One complete whole
+
Share of total
+
+
+ + + + + +
+
Product60%
+
Services25%
+
Partners15%
+
+
+
+
+ + +
+
+
+
JAN–MAY 2025
Monthly revenue trend
+
USD millions
+
+
+ + + + + + + JanFebMarAprMay + +
+
+
+