Files
yuanshuai c91a64fddb
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run
chore(repo): initialize team collaboration repository
2026-07-27 20:40:12 +08:00

598 lines
38 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<!-- @template name="Deck" description="A twenty-one-slide presentation starter: cover, contents, dividers, columns, quadrants, a data table, SVG charts and timeline, bleed imagery, a quote and a close, on the theme's tokens" -->
<helmet>
<link rel="stylesheet" href="_ds/industry-22c874fd-9888-4e99-8234-d60d39b3d719/styles.css" />
<title>Industry — deck</title>
<script src="./ds-base.js"></script>
<script src="./image-slot.js"></script>
<style>
/* Template scaffolding — slide layout and projection-scale sizing only; every
color, font, radius and shadow comes from ../../styles.css. Change one
number here to re-size the whole deck.
THE WHOLE-GRID DECK (review rounds 4-5, replacing the containers-only
reading at Barron's pick): the modular grid the blueprint grammar
implies is drawn in full on every slide — the Grid Systems cover reading
of the same direction. Type registers baseline-ON-rule, display sizes
step up hard (Müller-Brockmann size contrast), photographs become grid
objects, and the system's "+" registration marks keep marking what is
measured. The field is EQUAL-MARGINED — 120px from all four stage
edges — and nearly every element seats on a DRAWN rule (each seat's
comment carries its derivation). */
deck-stage {
--type-display: 288px; /* 140 → 288: the grid variant's size contrast */
--type-title: 96px; /* 76 → 96 */
--type-subtitle: 40px;
--type-body: 34px;
--type-small: 28px;
--type-kicker: 24px;
--pad-x: 120px; /* round 2 (Barron): equal field margins — 120 on all four sides */
--page-foot-note: "Confidential — July 2026"; /* the deck-wide imprint text (page furniture; see the PAGE FURNITURE note below) */
--pad-top: 96px;
--pad-bottom: 120px;
--baseline: 48px; /* grid unit — one default-text line: the 34px Barlow body at its ~1.41
deck leading (a compact sans). The field's bottom rule is the 20th gridline (960); in
this variant the cover/close meta seats a half-step ABOVE it, on the 936 cell-bottom
rule (see .pin-bottom), and 960 itself stays the hard vertical budget: the bleed's
note ends exactly ON it, the reviewed deck's own footer convention. */
}
.slide {
display: flex; flex-direction: column;
isolation: isolate; /* the slide is its own stacking context, so the board's
negative z-index (below) floors it above the slide's OWN background but
beneath every in-flow and positioned child — without isolation a negative
z-index would drop the board behind the slide background entirely, and
with z-index:auto a positioned board paints ABOVE in-flow ink (CSS paint
order), the opposite of the drawn-grid intent */
background: var(--color-bg); color: var(--color-text);
font-family: var(--font-body);
text-wrap: pretty; /* inherited: better breaks, no orphans on wrapped blocks */
padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
/* — the board: the whole modular grid, drawn — */
/* The variant's one big move. The field is equal-margined: 120px from every
stage edge (x 120..1800, y 120..960), so the frame reads symmetric and the
bottom rule IS the deck's established 960 footer/budget line. Columns:
four 384px modules with 48px gutters (pitch 432) — one baseline unit of
gutter, the Grid Systems cover's wider vertical doubling. Rows: seven
groups at pitch 120 (2.5 baseline units), each a 96px cell plus a 24px
caption row — the cover's doubled-line rhythm, every rule top on the
deck's 48/24px baseline lattice. Drawn as
border + four repeating gradients (origin border-box, so offsets count
from the field edge); zero-flow overlay like the reviewed deck's cells —
delete it and no baseline moves. Ink rules on paper; the dividers and
bleeds re-derive the rule color per ground (see below). Four "+" marks
register the field corners; content-anchored crosses stay on the
containers that keep them. */
.board {
--board-rule: color-mix(in srgb, var(--color-divider) 50%, transparent); /* review
round 10 (Barron, pre-ship): the grid fades by half — scoped here so the
chart grids and timeline line (data furniture on the same divider token)
keep their full ink. The table has no rules of its own — the board rules
it — so its ruling fades with the board, a deliberate part of the fade. */
position: absolute; z-index: -1; /* beneath all content — see .slide's isolation
note. The bleed slide overrides this: there the board deliberately lies ON
the photograph, beneath the scrim (see .bleed .board) */
left: var(--pad-x); top: calc(2.5 * var(--baseline));
width: calc(1680px + 1px); height: calc(17.5 * var(--baseline) + 1px);
border: 1px solid var(--board-rule, var(--color-divider));
background-origin: border-box; background-clip: border-box;
pointer-events: none;
background:
repeating-linear-gradient(to bottom, var(--board-rule, var(--color-divider)) 0 1px, transparent 1px 120px),
repeating-linear-gradient(to bottom, transparent 0 96px, var(--board-rule, var(--color-divider)) 96px 97px, transparent 97px 120px),
repeating-linear-gradient(to right, var(--board-rule, var(--color-divider)) 0 1px, transparent 1px 432px),
repeating-linear-gradient(to right, transparent 0 384px, var(--board-rule, var(--color-divider)) 384px 385px, transparent 385px 432px);
}
/* — shared pieces — */
/* Baseline grid: text sits on a 48px rhythm (--baseline) measured from the
slide's top edge. Snapped text blocks are trimmed to their cap and baseline
edges (text-box), so a block's bottom edge IS its last baseline and the next
block's first baseline lands N gridlines down with
margin-top: calc(N * var(--baseline) - 1cap). Compact rhythms (chart axes
and ticks) sit on the 24px half-step. Review the rhythm with ?baselines in
the URL, or press B (script in the helmet) — half-step lines draw
fainter than full ones. In this variant the board's rules are a subset of
the same lattice: row-group tops are full gridlines, cell bottoms are
half-steps, so "baseline on the rule" and "baseline on the grid" are one
statement. */
.kicker {
position: absolute; top: calc(2 * var(--baseline) - 1cap); /* baseline on the reviewed
deck's own kicker line (96) — now half a unit ABOVE the field's 120 top rule,
so the rule and its corner crosses stay clean */
left: var(--pad-x); text-box: trim-both cap alphabetic;
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: var(--type-kicker); letter-spacing: 0.08em; text-transform: uppercase;
color: var(--color-accent);
font-feature-settings: "tnum" 1;
}
.k-right { left: auto; right: var(--pad-x); } /* the cover/close kicker rides the
top rule flush right — the Grid Systems cover's author line */
.slide-title {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: var(--type-title); line-height: calc(2 * var(--baseline)); letter-spacing: 0; margin: 0;
margin-left: -0.052em; /* optical left alignment — measured for Barlow
Condensed SemiBold in the reviewed deck; em-scaled, so it holds at 96px */
text-box: trim-both cap alphabetic;
}
.pin-bottom { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: calc(var(--pad-bottom) + var(--baseline) / 2); } /* baseline
on the 936 caption rule — inside the field, clear of the 960 corner crosses;
the last caption row [936,960] is the imprint's band */
.meta { display: flex; align-items: baseline; gap: 18px; font-size: var(--type-kicker); color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.meta span { text-box: trim-both cap alphabetic; } /* baseline on the 936 caption rule — see .pin-bottom */
/* PAGE FURNITURE — on by default. To remove the page numbers and footer
text deck-wide, add data-no-page-furniture to the deck's <x-import>
mount (a one-line tweak — it renders on the <deck-stage>); to change
the text, edit --page-foot-note in
the deck's token block. The imprint sits where this board documents its
imprint's band: flush left on the axis, baseline ON the 936 caption rule
(the last caption row [936,960] of the field), at the meta row's scale
and 55% ink, the number tabular. Carried by the content slides (hero,
quote, split and the half-bleeds included — on the half-bleeds it
follows the copy half, because small muted type cannot pass the
type-over-photography rule) AND by the dividers: a blueprint sheet
carries its imprint on every ground, so the dividers keep it with the
ink re-derived to the page-corner marks' 55% paper (measured against
the steel ground in the render audit). The cover and close keep their
meta rows on the same rule instead, and the full bleed's photograph
owns its edges. Every slide still counts, so the printed number stays
truthful to position. */
deck-stage { counter-reset: page; }
.slide { counter-increment: page; }
.page-foot {
position: absolute; left: var(--pad-x); bottom: calc(var(--pad-bottom) + var(--baseline) / 2); /* baseline ON the 936 caption rule, like .pin-bottom */
font-size: var(--type-kicker);
color: color-mix(in srgb, var(--color-text) 55%, transparent);
text-box: trim-both cap alphabetic; /* bottom edge = baseline */
}
.page-foot::before { content: counter(page) " · "; font-feature-settings: "tnum" 1; }
.page-foot::after { content: var(--page-foot-note); }
deck-stage[data-no-page-furniture] .page-foot, deck-stage[data-no-page-furniture] .page-head { display: none !important; }
.divider .page-foot { color: color-mix(in srgb, var(--color-bg) 55%, transparent); } /* the imprint survives the steel sheet in the page corners' paper-alpha ink */
.half-bleed:not(.flip) .page-foot { left: 984px; } /* the imprint follows the copy half, seated on column 3's rule — type never sits on the photograph (deck-templates.md law) */
/* — cover and close — */
/* The Grid Systems cover, in industry's inks: the full board behind a
display line seated baseline-ON-rule (row-group 5's top, y 600), with the
description split into two caption columns under it — a short left cell
on the axis, the long cell on column 2 — first baselines ON the 696
caption rule, clear of the display's descenders (~55px below 600). The
kicker rides flush right above the field; the meta sits on the last
caption rule (936), inside the field. Most of the grid stays empty on
purpose. */
.cover { justify-content: flex-start; }
.cover .rise { position: absolute; inset: 0 var(--pad-x); }
.display {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: var(--type-display); line-height: calc(6 * var(--baseline)); letter-spacing: 0; margin: 0;
margin-left: -0.052em; /* optical left alignment, em-scaled — see .slide-title */
margin-top: calc(12.5 * var(--baseline) - 1cap); /* baseline exactly on the group-5 top rule (600) */
text-box: trim-both cap alphabetic;
}
.cover-sub { font-size: var(--type-small); line-height: var(--baseline); margin: 0;
position: absolute; left: 0; max-width: 768px;
top: calc(14.5 * var(--baseline) - 1cap); /* first baseline ON the 696 caption rule —
clear of the 288px display's descenders (~55px below 600) */
text-box: trim-both cap alphabetic; }
.cover-sub-a { left: 0; max-width: 360px; }
.cover-sub-b { left: 432px; max-width: 816px; } /* column 2's left edge (x 552) */
/* — contents — */
/* Contents as the cover's caption rows: no panel, no added rules — the
board IS the ruling. Each entry's baseline sits ON a caption rule
(456 / 576 / 696 / 816), the 24px caption row beneath reading as the
doubled line of the reference cover. Numbers hold the axis; labels start
on column 2 (x 552) — secondary starts in this deck always sit on a
column edge. */
.toc { position: absolute; inset: 0 var(--pad-x); font-size: var(--type-subtitle); }
.toc-row { position: absolute; left: 0; right: 0; }
.toc-row > span { text-box: trim-both cap alphabetic; position: absolute; top: 0; }
.toc-row > .toc-label { left: 432px; }
.toc-num { font-size: var(--type-kicker); color: var(--color-accent); font-feature-settings: "tnum" 1; }
.toc-row { top: calc(9.5 * var(--baseline)); } /* baseline on the 456 caption rule; the rows
re-seat below via nth-child so markup stays the reviewed deck's */
.toc-row:nth-child(2) { top: calc(12 * var(--baseline)); }
.toc-row:nth-child(3) { top: calc(14.5 * var(--baseline)); }
.toc-row:nth-child(4) { top: calc(17 * var(--baseline)); }
.toc-row > span { top: calc(-1cap); } /* each span trims to its cap; the row's
top IS the rule, so the baseline lands on it */
/* — section dividers — */
/* Dividers keep their own ground — the deep steel blueprint sheet — and the
variant draws the board through it in paper-alpha hairlines: the section
break is a fresh sheet of the same grid. Ghost numeral and title seat
baseline-ON-rule like everything else (ghost on 600, title on 816), both
flush on the axis; the "+" page corners keep their registration spots.
Contrast re-derived for the steel ground: paper type 12.6:1, ghost at
~2.2:1 (a drawing showing through the sheet), board rules decorative at
9% paper (halved in round 10's pre-ship fade). */ /* seats: ghost baseline ON the group-5 top rule (600),
title ON the 816 caption rule — see each declaration */
.divider { justify-content: flex-start; background: var(--color-accent-900); color: var(--color-bg); }
.divider .board { --board-rule: color-mix(in srgb, var(--color-bg) 9%, transparent); } /* half of the pre-round-10 18% — the fade keeps the per-ground ratio */
.divider .board > .corner { color: color-mix(in srgb, var(--color-bg) 55%, transparent); }
.divider .rise { position: absolute; inset: 0 var(--pad-x); }
.page-corner {
position: absolute; width: 15px; height: 15px;
color: color-mix(in srgb, var(--color-bg) 55%, transparent);
}
.page-corner::before, .page-corner::after { content: ""; position: absolute; background: currentColor; }
.page-corner::before { left: 7px; top: 0; width: 1px; height: 100%; }
.page-corner::after { top: 7px; left: 0; width: 100%; height: 1px; }
.page-corner.tl { top: 41px; left: 41px; }
.page-corner.tr { top: 41px; right: 41px; }
.page-corner.bl { bottom: 41px; left: 41px; }
.page-corner.br { bottom: 41px; right: 41px; }
/* — registration crosses (the cell-corner grammar, kept) — */
.cell { position: absolute; border: 1px solid var(--color-divider); pointer-events: none; }
.cell > .corner, .board > .corner { position: absolute; width: 15px; height: 15px; color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.cell > .corner::before, .cell > .corner::after,
.board > .corner::before, .board > .corner::after { content: ""; position: absolute; background: currentColor; }
.cell > .corner::before, .board > .corner::before { left: 7px; top: 0; width: 1px; height: 100%; }
.cell > .corner::after, .board > .corner::after { top: 7px; left: 0; width: 100%; height: 1px; }
.cell > .corner.tl, .board > .corner.tl { top: -8px; left: -8px; }
.cell > .corner.tr, .board > .corner.tr { top: -8px; right: -8px; }
.cell > .corner.bl, .board > .corner.bl { bottom: -8px; left: -8px; }
.cell > .corner.br, .board > .corner.br { bottom: -8px; right: -8px; }
/* -8px = the 7px arm offset + the 1px border: absolute offsets resolve from
the PADDING box, inside the border — each cross centers exactly on its
rule intersection, the reviewed deck's convention. */
.ghost-num {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 480px; line-height: 1; letter-spacing: 0;
color: var(--color-accent-700);
position: absolute; top: calc(12.5 * var(--baseline) - 1cap); /* baseline on the
group-5 top rule (600) */
text-box: trim-both cap alphabetic;
margin-left: -0.036em; /* ghosts lead with "0" — optical value from the reviewed deck, em-scaled */
}
.divider .slide-title { position: absolute; top: calc(17 * var(--baseline) - 1cap); } /* baseline on the 816 caption rule */
/* — content slides — */
.content { padding-top: calc(5 * var(--baseline)); /* title baseline on the 5th gridline —
row 2's top rule (240), clear of the pinned kicker row */ }
.content > .slide-title, .content > .chart-head { margin-top: -1cap; } /* cap-trimmed: the baseline, not the cap top, sits on the rule */
.content > .bullets { margin-top: calc(2.5 * var(--baseline) - 1cap); } /* first baseline on
the group-3 top rule (360), one row group below the title's */
.content > figure { margin-top: calc(2 * var(--baseline)); } /* svg top on the 336 caption
rule; the plots' axis line (svg y 360) lands ON the 696 caption rule */
.content > .table { margin-top: calc(0.5 * var(--baseline)); } /* table top at 264: the 96px
header row closes its baseline ON the group-3 top rule (360) */
.content > .chart-caption { margin-top: calc(2 * var(--baseline) - 1cap); }
.bullets { display: flex; flex-direction: column; max-width: 1220px; margin: 0; padding: 0; list-style: none; font-size: var(--type-body); }
.bullets li { position: relative; line-height: var(--baseline); text-box: trim-both cap alphabetic; }
.bullets li + li { margin-top: calc(2.5 * var(--baseline) - 1cap); } /* one thought per
row group: single-line bullets seat on successive group-top rules
(360 / 480 / 600) — the variant's pitch (the reviewed deck: every other
gridline) */
.bullets li::before, .bullets li::after { content: ""; position: absolute; background: var(--color-accent); }
.bullets li::before { left: -37px; top: calc(0.5cap - 8px); width: 2px; height: 16px; }
.bullets li::after { left: -44px; top: calc(0.5cap - 1px); width: 16px; height: 2px; }
/* — column layouts — */
/* Columns are the board's own bays: the two-column setting takes two
modules per column (x 120 and 984, 816px each, the drawn 48px gutter
between), and the three-column setting takes one module apiece (x 120,
552, 984) leaving module 4 DELIBERATELY empty — the Grid Systems cover's
empty-cell statement; on a four-module board three equal full-width
columns cannot seat on drawn rules, so the grid wins and the measure
narrows (a module runs ≈21 characters of condensed head, ≈26 of small
note — notes, not essays). No rules are added: the board is the ruling.
Seats: head baselines ON the group-3 top rule (360), note baselines ON
the 456 caption rule, wrapped lines riding the 48px lattice below. */
.cols { display: grid; column-gap: var(--baseline); margin-top: calc(2.5 * var(--baseline)); /* container top ON the group-3 rule; the cap term lives on the h3, where it resolves in the heading face (the two-family decks' -1cap lesson) */ }
.cols-2 { grid-template-columns: 816px 816px; }
.cols-3 { grid-template-columns: repeat(3, 384px); }
.cols h3 { font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: var(--type-subtitle); line-height: var(--baseline); margin: 0;
margin-top: -1cap; /* baseline ON the group-3 top rule (360); 1cap in the h3's own face */
text-box: trim-both cap alphabetic; }
.cols p { font-size: var(--type-small); line-height: var(--baseline); margin: 0;
margin-top: calc(2 * var(--baseline) - 1cap); /* first baseline ON the 456 caption rule */
text-box: trim-both cap alphabetic; }
/* — quadrants — */
/* The board IS the matrix — no division is drawn because every division
is already there: the vertical axis is the columns 2/3 gutter (center
960), the horizontal axis is the group-5 top rule (600). Cells are
two-module x two-row-group bays: heads seat their baselines ON the band
top rules (360 upper, 600 lower — the lower heads hang from the crossing
rule itself, the way every seated element does), one-liners ON the
caption rules below (456 / 696), wraps on the lattice. The axis pair
seats ON the group-7 top rule (840), left label on the axis, right label
flush right (the cover kicker's k-right precedent), one row group clear
of the 936 imprint. Left column = lower x; the y reading (top row
smaller work) is carried by the cell copy — a y pair has no seat that
doesn't fight the imprint band or the axis, so the horizontal pair does
the labeling (the shared-spec minimum). */
.quad { display: grid; grid-template-columns: 816px 816px; column-gap: var(--baseline);
grid-template-rows: calc(5 * var(--baseline)) calc(5 * var(--baseline));
margin-top: calc(2.5 * var(--baseline)); /* matrix top = the group-3 top rule (360) */ }
.quad h3 { font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: var(--type-subtitle); line-height: var(--baseline); margin: 0;
margin-top: -1cap; /* baseline ON the cell's band-top rule (360 / 600) */
text-box: trim-both cap alphabetic; }
.quad p { font-size: var(--type-small); line-height: var(--baseline); margin: 0;
margin-top: calc(2 * var(--baseline) - 1cap); /* one-liner ON the caption rule (456 / 696) */
text-box: trim-both cap alphabetic; }
.quad-axis { display: flex; justify-content: space-between;
margin-top: -1cap; /* label baselines ON the group-7 top rule (840) */
font-size: var(--type-kicker); letter-spacing: 0.08em; text-transform: uppercase;
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.quad-axis span { text-box: trim-both cap alphabetic; }
/* — data table (component class at projection scale) — */
/* The board rules the table: the component's own hairlines are suppressed
(a variant style call — no rules are drawn twice) and the rows take the
board's 120px row groups ONE-PER-ROW, each row's text baseline ON its
caption rule (456 / 576 / 696 / 816), the header on the group-3 top rule
(360). Columns seat on the grid columns — Role on the axis, Value on
column 2 (552), ground on column 3 (984), Use on column 4 (1416) — and
the swatch hangs LEFT of the axis like every other mark, its bottom edge
on the row's rule. */
.slide .table { max-width: 1680px; width: 1680px; font-size: 29px; font-feature-settings: "tnum" 1;
table-layout: fixed; border-collapse: collapse; }
/* Column plan: three 432px columns — one grid module each — and the rest
to Use. Carried on the header row's cells (table-layout: fixed reads row
one) rather than a <colgroup>: the Design Component pipeline parses the
template outside a real <table> context, where col elements are dropped,
and a fixed-layout table without them would fall back to equal columns. */
.slide .table th:nth-child(-n+3) { width: 432px; }
.slide .table thead tr { height: calc(2 * var(--baseline)); }
.slide .table tbody tr { height: calc(2.5 * var(--baseline)); } /* one board row group per data row */
.slide .table th, .slide .table td {
text-box: trim-both cap alphabetic; text-align: left; font-weight: 400;
border: none; background: none; vertical-align: bottom;
padding: 0 24px calc(0.5 * var(--baseline)) 0;
}
.slide .table th { font-size: var(--type-kicker); }
.slide .table thead th { padding-bottom: 0; } /* bottom-anchored: the header row closes ON the
group-3 top rule (360), each data row's LAST baseline sits 24px above its row's bottom —
on the caption rules 456 / 576 / 696 / 816 — and a wrapped cell grows upward into
its own cell instead of past the field (the vertical budget, kept by construction) */
.slide .table tbody tr:hover { background: none; }
.slide .table th:first-child, .slide .table td:first-child { padding-left: 0; position: relative; }
.slide .table th:last-child, .slide .table td:last-child { padding-right: 0; }
.slide .table .swatch { position: absolute; left: -72px; bottom: calc(0.5 * var(--baseline)); } /* hangs
left of the axis like every mark; bottom edge ON the row's rule */
.swatch { display: inline-block; width: 48px; height: 48px; border-radius: 0; /* square-corner
doctrine; 48px = one grid unit, bottom edge on the row's rule */ box-shadow: var(--shadow-sm); }
/* — hero figure — */
/* The variant's loudest size contrast: the figure at 432px, flush on the
axis, baseline ON the group-5 top rule (600); the caption moves to column
3 (x 984), its first baseline ON the 696 caption rule — the number and
its note read corner-to-corner across the empty grid. */
.hero { justify-content: flex-start; }
.hero-num {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 432px; line-height: 1; letter-spacing: 0; margin: 0;
color: var(--color-accent);
position: absolute; left: var(--pad-x); top: calc(12.5 * var(--baseline) - 1cap);
text-box: trim-both cap alphabetic;
margin-left: -0.010em; /* leads with the near-bearingless "1" — reviewed value, em-scaled */
}
.hero-caption { font-size: var(--type-small); line-height: var(--baseline); margin: 0;
position: absolute; left: 984px; top: calc(14.5 * var(--baseline) - 1cap); max-width: 816px;
text-box: trim-both cap alphabetic; }
/* — charts (static SVG on the tokens; no script, no chart library) — */
/* The plots seat their anatomy ON drawn rules — svg top at 336 (a caption
rule), the x-axis ON the 696 caption rule, and the line chart's 10k
gridlines (120px apart, the row-group pitch) phased by the axis onto the
drawn caption rules (576 / 456). Bar x-geometry is derived for the 1680
field. The board's column rules pass behind the plots — the drawing
stays on its grid. */
.chart { display: block; width: 100%; height: auto; overflow: visible; }
.chart text {
font-family: var(--font-body); font-size: 24px;
fill: color-mix(in srgb, var(--color-text) 55%, transparent);
font-feature-settings: "tnum" 1;
}
.chart .grid, .chart .axis { stroke: var(--color-divider); stroke-width: 1; }
.chart .bar { fill: none; stroke: var(--color-neutral-600); stroke-width: 1; }
.chart .bar-hi { fill: var(--color-accent); stroke: none; }
.chart .val { fill: color-mix(in srgb, var(--color-text) 70%, transparent); }
.chart .ref { stroke: var(--color-accent-700); stroke-width: 1; stroke-dasharray: 8 8; }
.chart .ref-label { fill: var(--color-accent-700); }
.chart .hi-label { fill: var(--color-accent-700); }
.chart .series-a, .key .series-a { stroke: var(--color-accent); stroke-width: 3; fill: none; }
.chart .series-b, .key .series-b { stroke: var(--color-neutral-800); stroke-width: 3; fill: none; stroke-dasharray: 10 8; }
.chart-caption { font-size: 25px; line-height: var(--baseline); max-width: 88ch; margin: 0; text-box: trim-both cap alphabetic; }
.chart-head { display: flex; align-items: baseline; justify-content: space-between;
font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: var(--type-title); }
.chart-legend { display: flex; align-items: baseline; gap: 16px; font-family: var(--font-body); font-weight: 400; font-size: var(--type-kicker); color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.chart-legend span { text-box: trim-both cap alphabetic; }
.chart-legend .key { width: 60px; height: 4px; overflow: visible; position: relative; top: -7px; }
.chart-legend .key:not(:first-child) { margin-left: 24px; }
.chart .tl-line { stroke: var(--color-divider); stroke-width: 1; }
.chart .tl-node { stroke: var(--color-neutral-600); stroke-width: 1.5; }
.chart .tl-node-hi-fill { fill: var(--color-accent); }
.chart .tl-node-hi { stroke: var(--color-bg); stroke-width: 1.5; }
.chart .tl-date { fill: color-mix(in srgb, var(--color-text) 55%, transparent); letter-spacing: 0.08em; }
.chart .tl-date-hi { fill: var(--color-accent-700); letter-spacing: 0.08em; }
.chart .tl-label { fill: var(--color-text); font-size: 30px; font-family: var(--font-heading); font-weight: var(--font-heading-weight); }
/* The timeline figure drops to the mid-field so its line sits ON the
group-4 top rule (480): svg top at 384, dates above, labels below, the
notes' baselines landing exactly ON the group-5 top rule (600). Node
pitch is 360 — milestone positions are data, not grid furniture. The
column-seated alternative (pitch 432, nodes on 120/552/984/1416/1800,
re-checked for the round-2 geometry) stays rejected: the fifth
milestone's ~340px notes would either overflow the stage or,
right-anchored, collide with the fourth's — and mixed anchoring
re-breaks the reviewed one-left-edge-per-milestone rule. */
.fig-timeline { margin-top: calc(3 * var(--baseline)) !important; }
/* — image slide — */
/* The photograph becomes a grid object: columns 3-4 by row-groups 2-7
exactly (x 984..1800, y 240..936), the blueprint frame's "+" marks seated
on board intersections. The copy keeps the axis in columns 1-2. */
.split { flex-direction: row; align-items: flex-start; gap: 0; padding-top: calc(5 * var(--baseline)); }
.split-copy { flex: none; width: 768px; display: flex; flex-direction: column; }
.split-copy .slide-title { margin-top: -1cap; }
.split-copy .note { font-size: var(--type-small); line-height: var(--baseline); margin: calc(2.5 * var(--baseline) - 1cap) 0 0;
text-box: trim-both cap alphabetic; max-width: 768px; }
.split-figure { position: absolute; left: 984px; top: calc(5 * var(--baseline)); width: 816px; height: calc(14.5 * var(--baseline)); margin: 0; }
.split-figure image-slot { width: 100%; height: 100%; } /* the grid-object box (columns 3-4 ×
row-groups 2-7) does the framing; cover is the component's own default */
.split-figure > .corner { pointer-events: none; } /* the registration crosses overlap the
slot by a few px — decorative marks must not eat its hover/drop events
(image-slot usage contract); scoped here rather than in styles.css, whose
.corner serves the whole system */
/* — full-bleed image slide — */
/* The board is drawn OVER the photograph in paper-alpha — the blueprint
laid onto the image — and UNDER the scrim, so the rules fade out exactly
where the type needs its measured ground. Type treatment unchanged from
the reviewed deck (it passes the type-over-photography rule): full-ink
note, kicker stepped to accent-700. */
.bleed .board { --board-rule: color-mix(in srgb, var(--color-bg) 13%, transparent); /* half of the pre-round-10 25%, rounded to a whole step */
z-index: auto; /* the one slide where the board rides ABOVE its photograph:
DOM-order painting — after the positioned figure, before the positioned
scrim — so photo → board → scrim → copy stack as documented */ }
.bleed .board > .corner { color: color-mix(in srgb, var(--color-bg) 55%, transparent); }
.bleed-figure { position: absolute; inset: 0; margin: 0; }
.bleed-figure image-slot { width: 100%; height: 100%; } /* framing is the component's (fit=cover default) */
.bleed-scrim {
position: absolute; inset: 0; pointer-events: none; /* the slot beneath is interactive (hover controls, drops) — overlays must not eat its events (image-slot usage contract) */
background:
linear-gradient(to top,
color-mix(in srgb, var(--color-bg) 98%, transparent),
color-mix(in srgb, var(--color-bg) 95%, transparent) 24%,
color-mix(in srgb, var(--color-bg) 60%, transparent) 42%,
transparent 66%),
linear-gradient(to bottom,
color-mix(in srgb, var(--color-bg) 92%, transparent),
color-mix(in srgb, var(--color-bg) 80%, transparent) 12%,
transparent 26%);
}
.bleed-copy { position: absolute; left: var(--pad-x); right: var(--pad-x); bottom: var(--pad-bottom); pointer-events: none; } /* text over the slot — must not eat its hover/drop events */
.bleed .kicker { color: var(--color-accent-700); pointer-events: none; /* sits on the photograph — same usage contract as the scrim */ }
.bleed-copy .note {
font-size: var(--type-small); line-height: var(--baseline); max-width: 56ch;
margin: calc(2 * var(--baseline) - 1cap) 0 0;
text-box: trim-both cap alphabetic;
color: var(--color-text);
} /* bottom-anchored block: the note's LAST baseline sits ON the field's 960
bottom rule; its earlier baselines (864/912) and the title's (768) ride the
48px lattice, not drawn rules — over a photograph the scrim, not the board,
is the ground (the board fades out beneath it) */
/* — half-bleed image slides — */
/* The photograph takes grid columns, not a raw half-stage: it bleeds to
three edges and stops at the gutter (base: columns 1-2, inner edge on
the 936 gutter rule; .flip mirrors to columns 3-4, starting exactly on
column 3's rule at 984), and the copy starts on a column rule — x 984
(column 3) for the base, the axis for the mirror. The board draws first,
the photograph covers its half. */
.half-bleed { padding-top: calc(5 * var(--baseline)); }
.half-bleed > .slide-title { margin-top: -1cap; max-width: 816px; }
.half-bleed .note {
font-size: var(--type-small); line-height: var(--baseline); max-width: 816px;
margin: calc(2 * var(--baseline) - 1cap) 0 0;
text-box: trim-both cap alphabetic;
}
.half-bleed .bullets { margin-top: calc(2.5 * var(--baseline) - 1cap); max-width: 816px; }
.half-bleed .bullets li + li { margin-top: calc(4 * var(--baseline) - 1cap); } /* items here
run up to two lines: the 4-unit gap from a two-line item's last baseline seats
each bullet's FIRST baseline back on a group-top rule (360 / 600 / 840), and a
one-line item simply leaves more air before the next */
.half-bleed-figure { position: absolute; inset: 0 auto 0 0; width: 936px; margin: 0; }
.half-bleed-figure image-slot { width: 100%; height: 100%; } /* framing is the component's (fit=cover default) */
.half-bleed:not(.flip) > .slide-title { margin-left: calc(864px - 0.052em); } /* column 3 (x 984), preserving the optical shift */
.half-bleed:not(.flip) .note,
.half-bleed:not(.flip) .bullets { margin-left: 864px; }
.half-bleed:not(.flip) .kicker { left: 984px; }
.half-bleed.flip .half-bleed-figure { inset: 0 0 0 auto; width: 936px; }
/* With the photograph ending on the 936 gutter rule and the copy on column
3 (984), the 48px gutter is the hanging margin: the crosses center on the
gutter's own centerline (x 960) with 16px of paper on either side —
"whatever sits behind them" now means the gutter itself. */
.half-bleed:not(.flip) .bullets li::before { left: -25px; }
.half-bleed:not(.flip) .bullets li::after { left: -32px; }
/* — quote — */
/* Flush on the axis, airy MB leading: 64px poster lines on a two-unit
(96px) leading, first baseline ON the group-3 top rule (360; the deck's
copy sets in two lines, 360/456 — top rule and caption rule); the
attribution drops to the 696 caption rule — see the figcaption note. */
.quote { justify-content: flex-start; }
.quote .rise { position: absolute; inset: 0 var(--pad-x); }
.quote blockquote {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 64px; line-height: calc(2 * var(--baseline)); letter-spacing: 0;
max-width: 1200px; margin: 0;
margin-top: calc(7.5 * var(--baseline) - 1cap); /* first baseline on the group-3 top rule (360) */
text-box: trim-both cap alphabetic;
text-indent: -0.316em; /* hang the opening “ (measured advance in the Condensed 600) */
}
.quote figcaption { font-size: var(--type-small); margin: 0;
position: absolute; top: calc(14.5 * var(--baseline) - 1cap); /* baseline on the 696
caption rule — a full row-group below the quote's two-line setting (360/456),
chosen for air over the nearer 576 rule; re-seat if the quote re-wraps */
text-box: trim-both cap alphabetic;
text-indent: -0.885em; /* hang the leading em-dash + space (measured advance in Barlow Regular) */ }
/* Review aid: the baseline grid drawn as a hairline under every --baseline
step. Debug-only — toggled by ?baselines or the B key; not part of the look. */
deck-stage[data-baselines] .slide::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
background:
repeating-linear-gradient(to bottom,
color-mix(in srgb, var(--color-accent) 35%, transparent) 0 1px,
transparent 1px var(--baseline)),
repeating-linear-gradient(to bottom,
transparent 0 calc(var(--baseline) / 2),
color-mix(in srgb, var(--color-accent) 16%, transparent) calc(var(--baseline) / 2) calc(var(--baseline) / 2 + 1px),
transparent calc(var(--baseline) / 2 + 1px) var(--baseline));
}
/* Entrance — end-state is the base style; animate from hidden, gated on the
active slide and the motion preference, so print and reduced-motion are safe. */
@media (prefers-reduced-motion: no-preference) {
[data-deck-active] .rise { animation: rise 0.45s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }
}
</style>
<script>
// Review aid: baseline-grid overlay — add ?baselines to the URL, or press B to toggle.
// This runs from <helmet>, before the deck renders, so the stage is looked up when it is
// needed instead of captured up front; the window flag keeps a second evaluation of
// this helmet script (a recreated script node) from binding the B key twice.
(() => {
if (window.__deckBaselineAid) return;
window.__deckBaselineAid = true;
const stage = () => document.querySelector('deck-stage');
if (new URLSearchParams(location.search).has('baselines')) {
let tries = 600; // wait out the deck's first render (up to ~10s of frames)
const arm = () => { const s = stage(); if (s) s.setAttribute('data-baselines', ''); else if (tries-- > 0) requestAnimationFrame(arm); };
arm();
}
addEventListener('keydown', (e) => {
const t = e.target;
if (t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName))) return; // typing guard, as in deck-stage's own keys
if ((e.key === 'b' || e.key === 'B') && !e.metaKey && !e.ctrlKey && !e.altKey) stage()?.toggleAttribute('data-baselines');
});
})();
</script>
</helmet>
<x-import component-from-global-scope="deck-stage" from="./deck-stage.js" width="1920" height="1080" hint-size="100%,100%">
</x-import>
</x-dc>
</body>
</html>