/*
 * The Crypto Secret — the eight chapter cards (Story 2.3).
 *
 * SOURCE OF TRUTH. design/canvas/Main.dc.html:265-357 for desktop and
 * design/canvas/HomeMobile.dc.html:108-122 for mobile — with one deliberate
 * deviation from the mobile board, stated below.
 *
 * ───────────────────────────────────────────────────────────────────────────
 * THE ONE THING THIS FILE EXISTS TO DELIVER
 * ───────────────────────────────────────────────────────────────────────────
 *
 * THE MINIATURE IS THE DELIVERABLE, NOT DECORATION. This design was already
 * rejected once as eight cards carrying an icon and a gap, so a card without
 * real marks is a KNOWN DEFECT and not a shortfall. Everything below that
 * paints a mark — the gauge's track, the candles' fill, the depth gradients,
 * the seed cells — is load-bearing, and any rule here that would leave a panel
 * empty is a regression however tidy it looks.
 *
 * That has three consequences the rest of the file keeps returning to:
 *
 *   1. THE MINIATURES SURVIVE MOBILE. HomeMobile.dc.html:113-120 draws eight
 *      cards with a numeral and a title and NO miniature panel at all. That
 *      board predates the rejection; the rule that came out of it carries no
 *      width qualifier, and the product brief says most buyers arrive on a
 *      phone — so honouring the board here would ship the rejected state to
 *      the majority. The miniatures scale down inside the 2-column grid
 *      instead. The BODY COPY does stay dropped at mobile, exactly as drawn.
 *
 *   2. NOTHING RESOLVES TO BLANK. Every animation below has a reduced-motion
 *      state and a no-JavaScript state, and both land on the animation's
 *      FINISHED value — drawn, grown, complete. Never zero, never invisible,
 *      never mid-run. Section 6 at the foot of this file.
 *
 *   3. ON ENTRY, NEVER ON LOAD, NEVER LOOPING. _head.html:58-60 runs .drw and
 *      .bar unconditionally, which is coherent for an artboard with no scroll
 *      — six sections down a real page they would finish long before a reader
 *      arrived and the miniatures would read as static. They are gated here
 *      off .tcs-revealed, which js/reveal.js adds on intersection before
 *      unobserving. NO NEW JAVASCRIPT, NO SECOND OBSERVER, and
 *      animation-iteration-count is never `infinite` anywhere in this file.
 *
 * ───────────────────────────────────────────────────────────────────────────
 * THREE COLOURS FROM THE ARTBOARD FAIL AA AND ARE CORRECTED HERE
 * ───────────────────────────────────────────────────────────────────────────
 *
 * The artboards win on a visual conflict. They do not win on an accessibility
 * failure — the precedent story 2.2 set when it corrected #55524B on the black
 * band, and DESIGN.md:181 records that class of pairing as "a real defect
 * caught in review". Measured:
 *
 *   #B8B3A6 on #FFFFFF  = 2.09:1  — used 8x in this band. FAILS (needs 4.5:1)
 *   #8B877D on #FFFFFF  = 3.58:1  — used 7x on the white panel. FAILS
 *   #8A6A3C on #F4F1EA  = 4.42:1  — the chapter numerals. FAILS by 0.08
 *
 * They resolve to --tcs-caption (4.87:1), --tcs-label-on-white (4.87:1) and
 * --tcs-amber-numeral-aa (5.78:1). --tcs-amber-2 is a SHIPPED token, so its
 * value is not edited here — an AA-passing variant sits alongside it, which is
 * the change the story's boundaries permit without asking.
 *
 * The artboard's 9.5px, 10px, 10.5px and 11px interface text all fall under
 * epic 2's 12px floor and are raised. SVG <text> is a drawing measured in user
 * units rather than interface text — the exemption the existing gate already
 * uses — but it is scaled up anyway so labels stay legible as the panel
 * shrinks.
 *
 * ───────────────────────────────────────────────────────────────────────────
 *
 * CONSUMES, DOES NOT RE-AUTHOR. The band comes from sections.css; the hover
 * lift and the reveal stagger come from motion.css; every value below is a
 * --tcs-* token from tokens.css. There is no literal colour, no literal
 * duration and no literal easing in this file. Values that had no name — the
 * gauge's short arc, the candles' grow origin, the two card radii, the
 * AA-passing caption — were added to tokens.css first, in the Story 2.3 block
 * there.
 *
 * HARD LIMIT, inherited: only opacity and transform animate, never width,
 * height, top or left. The ONE exception is `stroke-dashoffset` on the two
 * drawn paths, which is the only way to express a line drawing itself and is
 * the exact thing the artboard draws. It is bounded to two short paths, it is
 * not a layout property, and story 2.2 already established it as documented.
 */

/* ===========================================================================
 * 1. THE BAND  —  Main.dc.html:266
 *
 * `background: #F7931A; color: #0C0C0D; padding: 92px 40px` is exactly
 * .tcs-band + .tcs-band--orange at the default padding, so this rule declares
 * NEITHER the ground NOR the type colour NOR the padding. Restating them would
 * create a second place for the orange band's pairing to drift, and the
 * pairing is one decision: sections.css:127-140 sets black type on orange
 * together, because a band that sets only its background is how black-on-black
 * happens.
 *
 * The band's own job here is the scroll anchor. It carries id="chapters", so
 * the nav fragment story 1.3 has been pointing at nothing since
 * (deferred-work.md:54-55) finally resolves — to the band's TOP EDGE, which is
 * why the id is on the <section> and not on an inner wrapper.
 * ========================================================================= */

.tcs-chapters {
	/*
	 * The grid's cards lift on hover (translateY(-6px), motion.css:343) and
	 * the eighth card sits on the band's right edge. Without a containing
	 * block the lifted shadow can spill past the band and, at exactly 1440,
	 * widen the document. Containing it here rather than upstream in
	 * .tcs-bands keeps the fix beside the thing that needs it — the same
	 * argument .tcs-noise makes for clipping its own blob.
	 */
	position: relative;
}

/*
 * code-review fix, 2026-09-04: this comment named a "sticky header" that
 * does not exist — .tcs-header is position: relative (chrome.css), not
 * sticky or fixed, so nothing on screen actually covers the jumped-to edge.
 * The real reason to keep: without this, the nav's #chapters jump lands the
 * band flush against the viewport's top edge with no breathing room at all.
 * The offset reuses --tcs-section-y-mobile — the section's own mobile top
 * padding — rather than a one-off value, so the landing gap reads as the
 * section's own spacing instead of an arbitrary number. It is a scroll
 * property — it moves the SCROLL POSITION, never the element, so it cannot
 * shift the layout.
 */
.tcs-chapters {
	scroll-margin-top: var(--tcs-section-y-mobile);
}

/* ===========================================================================
 * 2. THE HEADER ROW  —  Main.dc.html:267-270
 *
 * An h2 on the left and a lede on the right, BOTTOM-ALIGNED (align-items:
 * flex-end) so the lede's last line sits on the headline's baseline rather
 * than floating at the top of a 2-line heading. 60px gap, 50px below.
 * ========================================================================= */

.tcs-chapters__head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 60px;
	margin-bottom: 50px;
}

.tcs-chapters__headline {
	margin: 0;
	font-size: var(--tcs-fs-h2);
	font-weight: var(--tcs-fw-semibold);
	line-height: var(--tcs-lh-h2);
	/*
	 * The negative tracking is load-bearing typography, not decoration: a
	 * 54px heading at default tracking reads as a different website.
	 * epic-2-context.md names it explicitly.
	 */
	letter-spacing: var(--tcs-ls-h2);
	/* No `color`: .tcs-band--orange resolves headings to --tcs-black already. */
}

.tcs-chapters__lede {
	margin: 0;
	max-width: 400px;
	font-size: var(--tcs-fs-body);
	line-height: 1.6;
	/*
	 * The artboard draws `rgba(12,12,13,.72)` — black at 72% on orange. That
	 * composites to roughly 4.9:1 and passes AA, but it is a fourth way to
	 * spell a colour in a system that has tokens for exactly this. The band's
	 * own --tcs-black at full strength (8.51:1) is used instead and the
	 * subordinate weight comes from SIZE and MEASURE, which is what actually
	 * separates a lede from a heading.
	 */
	color: var(--tcs-black);
	opacity: .78;
}

/*
 * The mobile lede is REWRITTEN, not rewrapped (HomeMobile.dc.html:110 drops
 * the second sentence), so both copies ship in the served HTML and a media
 * query displays exactly one. The precedent is .tcs-noise__mobile-copy:
 * wp_is_mobile() was rejected because under full-page caching the first
 * visitor's device decides what every later visitor sees, and it does not
 * respond to a window being resized.
 */
.tcs-chapters__mobile-copy {
	display: none;
}

/* ===========================================================================
 * 3. THE GRID  —  Main.dc.html:271
 *
 * repeat(4, minmax(0, 1fr)) with a 16px gap. minmax(0, 1fr) rather than a bare
 * 1fr is not a flourish: a grid item's implicit min-width is `auto`, so one
 * long word in a card title widens its column past its share and pushes the
 * whole row sideways. epic-2-context.md states the rule; this is the section
 * with the longest titles, so it is the section that would have shown it.
 *
 * THE GRID IS THE STAGGER PARENT. js/reveal.js:208-227 groups reveal targets
 * by their PARENT and steps each child by --tcs-stagger-sibling (80ms),
 * capping the index at 7. Eight cards as direct children of one element is
 * therefore exactly the cascade the story asks for — 0, 80, 160 … 560ms, with
 * nothing clipped. Splitting them into two rows of four would restart the
 * cascade halfway and lose the choreography with no visible cause.
 * ========================================================================= */

.tcs-chapters__grid {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: var(--tcs-card-gap);
}

/* ===========================================================================
 * 4. THE CARD  —  Main.dc.html:273
 *
 * Cream ground, 14px radius, 22px padding, a 15px column gap between the
 * miniature panel and the text block. NO BORDER — and that is the artboard's
 * decision, not an omission: these cards sit on the ORANGE ground, where
 * DESIGN.md's two card types (bordered on cream, bordered on black) do not
 * apply. DESIGN.md also specifies a 12px radius against the drawn 14px. The
 * artboard wins on a visual conflict, so both radii are tokens
 * (--tcs-radius-chapter-card / --tcs-radius-chapter-panel) rather than being
 * rounded into --tcs-radius-md.
 *
 * .tcs-card ALSO APPLIES, from motion.css:324 — the hover lift and its shadow
 * transition. Nothing about that is restated here.
 * ========================================================================= */

.tcs-chapter {
	display: flex;
	flex-direction: column;
	gap: var(--tcs-chapter-card-gap);
	padding: var(--tcs-chapter-card-pad);
	border-radius: var(--tcs-radius-chapter-card);
	background: var(--tcs-cream);
	/*
	 * min-width: 0 on the CARD as well as minmax(0, 1fr) on the column. The
	 * column's minimum is what stops the TRACK growing; this is what stops the
	 * card's own flex children — a long title, a wide miniature — refusing to
	 * shrink inside it. Both are needed; either alone still overflows.
	 */
	min-width: 0;
}

/*
 * The eighth card is on black — Main.dc.html:350. A designed accent closing
 * the row of eight, not a state, which is why it is a modifier on the card and
 * not a variant of the whole grid.
 */
.tcs-chapter--dark {
	background: var(--tcs-black);
}

/* ---------------------------------------------------------------------------
 * 4a. The text block  —  Main.dc.html:280
 *
 * Numeral, title, copy. 6px gap.
 * ------------------------------------------------------------------------- */

.tcs-chapter__body {
	display: flex;
	flex-direction: column;
	gap: 6px;
	min-width: 0;
}

/*
 * THE NUMERAL, AND THE ONE AA CORRECTION THAT NEEDED A NEW TOKEN.
 *
 * --tcs-amber-2 (#8A6A3C) is the shipped "chapter numerals on orange cards"
 * token and it measures 4.42:1 on the #F4F1EA card — 0.08 short of AA for
 * 12px text. Editing a shipped token's value is an ask-first change, so an
 * AA-passing variant sits alongside it instead: #75592C at 5.78:1, the same
 * amber darkened.
 */
.tcs-chapter__numeral {
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-semibold);
	color: var(--tcs-amber-numeral-aa);
}

/*
 * On the black card the numeral is --tcs-orange (Main.dc.html:355), which
 * measures 8.02:1 there. Orange as a small text accent on black is correct;
 * the palette rule bans it as a large FILL, which this is not.
 */
.tcs-chapter--dark .tcs-chapter__numeral {
	color: var(--tcs-orange);
}

/*
 * The title is an <h3>, so the band's heading rule (sections.css:136-140)
 * resolves it to --tcs-black on the cream cards. Only the margin and the type
 * scale are set here.
 */
.tcs-chapter__title {
	margin: 0;
	font-size: var(--tcs-fs-card-title);
	font-weight: var(--tcs-fw-semibold);
	line-height: var(--tcs-lh-card-title);
	letter-spacing: var(--tcs-ls-card-title);
}

.tcs-chapter--dark .tcs-chapter__title {
	color: var(--tcs-cream);
}

/*
 * THE BODY COPY, AND THE FIRST OF THE THIRTEEN SIZE CORRECTIONS.
 *
 * The artboard draws this at 12.5px. That clears the 12px floor, so it is kept
 * — the floor is a minimum, not a target, and rounding every size up to 15px
 * would flatten the card's own hierarchy against its 17px title.
 *
 * --tcs-body is 7.79:1 on the cream card. The artboard's --tcs-dim (#6E6A62)
 * measures 4.77:1 there, which also passes; --tcs-dim is used, as drawn.
 */
.tcs-chapter__copy {
	margin: 0;
	font-size: 12.5px;
	line-height: 1.55;
	color: var(--tcs-dim);
}

/*
 * On the black card the copy is --tcs-muted (Main.dc.html:355), 5.46:1 on
 * #0C0C0D. NOT --tcs-body, which is 2.51:1 there and is the exact defect
 * story 2.2 corrected on the noise band.
 */
.tcs-chapter--dark .tcs-chapter__copy {
	color: var(--tcs-muted);
}

/* ===========================================================================
 * 5. THE MINIATURE PANEL  —  the inner surface all eight cards share
 *
 * Main.dc.html:274. White fill, a 1px --tcs-border hairline, 9px radius, 13px
 * padding, and a 104px MINIMUM HEIGHT.
 *
 * THAT MIN-HEIGHT IS THE SINGLE MOST LOAD-BEARING VALUE IN THIS FILE. It is
 * what guarantees a miniature has room to draw at every width — the thing
 * standing between this section and the rejected empty card. It is a token
 * (--tcs-chapter-panel-min-h) precisely so it cannot be quietly tuned away in
 * one rule while the gate keeps passing on the desktop layout.
 *
 * The panel is a flex column by default; the miniatures that need centring or
 * a different axis override it individually below.
 * ========================================================================= */

.tcs-chapter__panel {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: var(--tcs-chapter-panel-min-h);
	padding: var(--tcs-chapter-panel-pad);
	border: var(--tcs-border-width) solid var(--tcs-border);
	border-radius: var(--tcs-radius-chapter-panel);
	background: var(--tcs-white);
	/*
	 * The panel is the miniatures' clipping frame. An SVG scaled by
	 * preserveAspectRatio cannot exceed it, but the candle row and the seed
	 * grid are laid out in real pixels and could, at a narrow column, paint
	 * one mark past the rounded corner. Clipping is cheaper and more certain
	 * than making seven separate rules defensive.
	 */
	overflow: hidden;
}

/*
 * The eighth card's panel is the dark one — Main.dc.html:351. --tcs-panel on
 * --tcs-border-dark, the raised-panel-on-black pairing tokens.md names.
 */
.tcs-chapter--dark .tcs-chapter__panel {
	border-color: var(--tcs-border-dark);
	background: var(--tcs-panel);
}

/*
 * EVERY MINIATURE FILLS ITS PANEL. Without this an SVG miniature would size
 * to its intrinsic width attribute and sit off-centre in a column narrower
 * than 150px — visible at 390, which is exactly where the mobile deviation
 * has to hold up.
 */
.tcs-chapter__mini {
	width: 100%;
	min-width: 0;
}

/*
 * THE TWO SHARED TEXT ROLES INSIDE A PANEL.
 *
 * --tcs-caption (#767164, 4.87:1 on white) replaces the artboard's #B8B3A6,
 * which is 2.09:1 and fails AA by the widest margin on the page. The size is
 * raised from the drawn 9.5px/10px to the 12px floor.
 */
.tcs-chapter__caption {
	font-size: var(--tcs-fs-eyebrow);
	line-height: 1.4;
	color: var(--tcs-caption);
}

/*
 * SVG <text> gets its fill by class, because CSS `color` DOES NOT INHERIT INTO
 * `fill` — a <text> left bare paints BLACK, which on the white panel is not
 * invisible but is wrong, and on any darker panel would be. The same
 * discipline story 2.2 established for the noise illustration, and the reason
 * both gates check it.
 *
 * 11 user units in a 150-unit-wide viewBox that renders at roughly 150px, so
 * it lands near 11px on screen — SVG text is a drawing measured in user units
 * and is exempt from the interface floor by the reading the existing gate
 * already uses, but it is scaled up from the drawn 9.5 anyway so it stays
 * legible when the panel shrinks at 390.
 */
.tcs-chapter__svg-caption {
	font-family: var(--tcs-font);
	font-size: 11px;
	fill: var(--tcs-caption);
}

/* ===========================================================================
 * MINIATURE 1 · THE DEFINITION CARD  —  Main.dc.html:274-279
 *
 * A term, its definition, a rule, and the next term greyed out behind it. The
 * chapter's promise drawn: every term defined before it is used again.
 * STATIC — there is no motion in a definition.
 * ========================================================================= */

.tcs-chapter__mini--definition {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.tcs-chapter__term {
	display: flex;
	align-items: center;
	gap: 7px;
}

.tcs-chapter__term-dot {
	flex: 0 0 auto;
	width: var(--tcs-chapter-dot);
	height: var(--tcs-chapter-dot);
	border-radius: var(--tcs-radius-full);
	background: var(--tcs-orange);
}

/*
 * The second term's dot is the artboard's #CFC9BC — a value with no token and
 * no text on it. --tcs-border-2 (#DCD5C6) is the nearest shipped token and is
 * a decorative fill of the same weight; it is used rather than adding a
 * nineteenth colour token for one 5px dot.
 */
.tcs-chapter__term-dot--next {
	background: var(--tcs-border-2);
}

/*
 * The eyebrow treatment, at the 12px floor rather than the drawn 10.5px.
 * --tcs-label-on-white (#767164, 4.87:1) replaces the artboard's #8B877D,
 * which is 3.58:1 on white and fails.
 */
.tcs-chapter__term-label {
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-bold);
	letter-spacing: .1em;
	line-height: 1.2;
	color: var(--tcs-label-on-white);
}

/*
 * The greyed-out next term. The artboard sets #B8B3A6 (2.09:1) to say
 * "not yet defined".
 *
 * AN OPACITY TRICK WAS TRIED HERE AND REJECTED, and the reason is worth
 * keeping: .72 of --tcs-label-on-white composites to rgb(156,153,143) on
 * white, which measures 2.85:1 — WORSE than several colours this file exists
 * to correct, and no better than the #B8B3A6 it replaced. Compositing hides an
 * accessibility failure behind a value that looks compliant in the source,
 * which is strictly more dangerous than the literal it replaced. Any contrast
 * gate reading `color` alone would have passed it.
 *
 * The "not yet defined" reading is carried by the DOT instead — orange for the
 * defined term, --tcs-border-2 for the next one — which is where the artboard
 * already puts the same signal, and by the absence of a definition beneath it.
 * The label itself stays at full strength and passes at 4.87:1.
 */
.tcs-chapter__term-label--next {
	color: var(--tcs-label-on-white);
}

/* The definition itself. --tcs-ink is 11.75:1 on white, as drawn. */
.tcs-chapter__term-def {
	font-size: var(--tcs-fs-eyebrow);
	line-height: 1.5;
	color: var(--tcs-ink);
}

.tcs-chapter__rule {
	height: var(--tcs-border-width);
	background: var(--tcs-cream-2);
}

/* ===========================================================================
 * MINIATURE 2 · THE COMPARISON BARS  —  Main.dc.html:283-286
 *
 * Three labelled tracks filled to 72%, 48% and 90%.
 *
 * STATIC, AND DELIBERATELY SO. These are NOT the .bar keyframe: the artboard
 * gives them a CSS gradient and no animation class at all, because a time
 * horizon per approach is a comparison, not an event. Reading "miniatures
 * animate once on entry" as requiring all eight would invent motion the design
 * does not have.
 *
 * The fill is a GRADIENT STOP, never a width. Animating or sizing width would
 * break the hard limit; a hard-stop gradient paints the identical picture at
 * no layout cost, and it is what the artboard draws.
 * ========================================================================= */

.tcs-chapter__mini--bars {
	display: flex;
	flex-direction: column;
	gap: 9px;
}

.tcs-chapter__bar-row {
	display: flex;
	align-items: center;
	gap: 9px;
}

/*
 * The label column is 44px in the artboard at 10.5px type. At the 12px floor
 * "Swing" no longer fits 44px, so the column grows to 52px — the alternative
 * would be a clipped word, and the floor is not negotiable.
 */
.tcs-chapter__bar-label {
	flex: 0 0 auto;
	width: 52px;
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-semibold);
	line-height: 1.2;
	color: var(--tcs-label-on-white);
}

.tcs-chapter__bar-track {
	flex: 1 1 auto;
	min-width: 0;
	height: 6px;
	border-radius: 3px;
	/*
	 * --tcs-bar-fill is set per row inline from tcs_chapter_miniature(),
	 * resolving to --tcs-bar-swing / --tcs-bar-day / --tcs-bar-hold. The
	 * percentages are drawing data; the two colours are tokens.
	 */
	background: linear-gradient(90deg, var(--tcs-orange) var(--tcs-bar-fill), var(--tcs-cream-2) var(--tcs-bar-fill));
}

/* ===========================================================================
 * MINIATURE 3 · THE RISK GAUGE  —  Main.dc.html:293-300   ANIMATED (.drw)
 *
 * A full grey arc, an orange arc drawn over its first third, a needle, and two
 * axis labels.
 *
 * ONLY THE ORANGE ARC ANIMATES. The track, the hub, the needle and both labels
 * paint immediately, so the miniature carries real marks from the first frame
 * — it is never blank, not even mid-animation. That is deliberate: an entry
 * animation that starts from nothing is indistinguishable from a card that
 * failed to render.
 *
 * THE TWO OVERRIDES ARE THE DESIGN. Main.dc.html:297 sets stroke-dasharray:120
 * and animation-duration:2s against the shipped 1200 / 3.4s, because this arc
 * is about 120 units long and a 1200 dasharray would hold the stroke entirely
 * offscreen for most of its run — a reader would watch an empty gauge for
 * three seconds. Both are tokens (--tcs-gauge-dash, --tcs-dur-gauge-draw); see
 * section 6 for where the animation is actually armed.
 * ========================================================================= */

.tcs-chapter__mini--gauge {
	display: block;
	height: auto;
}

.tcs-chapter__gauge-track {
	fill: none;
	stroke: var(--tcs-cream-2);
	stroke-width: 11;
	stroke-linecap: round;
}

.tcs-chapter__gauge-arc {
	fill: none;
	stroke: var(--tcs-orange);
	stroke-width: 11;
	stroke-linecap: round;
}

.tcs-chapter__gauge-hub {
	fill: var(--tcs-black);
}

.tcs-chapter__gauge-needle {
	fill: none;
	stroke: var(--tcs-black);
	stroke-width: 2.4;
	stroke-linecap: round;
}

/* ===========================================================================
 * MINIATURE 4 · THE CANDLES  —  Main.dc.html:305-308   ANIMATED (.bar)
 *
 * Seven candles growing from the baseline on an 0.08s step, two of them
 * orange.
 *
 * NO CLAIM IS MADE BY THIS SHAPE. epic-2-context.md bans green candles
 * implying returns; these are the artboard's grey and orange, they carry no
 * axis, no figure and no direction of travel, and the card's own copy is about
 * what a chart does NOT tell you.
 *
 * HEIGHT IS SET, NEVER ANIMATED. Each candle's height is an inline custom
 * property (drawing data, per candle); the growth is scaleY from a `bottom`
 * origin, which is a transform and costs no layout. Animating height would
 * break the hard limit and force a reflow on every frame of seven elements.
 * ========================================================================= */

.tcs-chapter__mini--candles {
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: var(--tcs-candle-gap);
	/*
	 * The panel is a centred column by default; the candles need their own
	 * baseline, and the tallest is 70px inside a 104px panel less 26px of
	 * padding — so the row is given the full content height and the candles
	 * sit on its bottom edge.
	 */
	height: 78px;
}

.tcs-chapter__candle {
	flex: 0 0 auto;
	width: var(--tcs-candle-w);
	height: var(--tcs-candle-h);
	border-radius: 2px;
	background: var(--tcs-border-2);
}

.tcs-chapter__candle--accent {
	background: var(--tcs-orange);
}

/* ===========================================================================
 * MINIATURE 5 · THE TREND LINE  —  Main.dc.html:312-317   ANIMATED (.drw)
 *
 * A baseline, a dashed level, a six-point line that draws on entry, and a
 * magnifier ringing the setup it points at.
 *
 * ONLY THE ORANGE LINE ANIMATES. Both axis rules and the whole magnifier paint
 * immediately, for the same reason the gauge's track does: a miniature that
 * begins empty is indistinguishable from a miniature that failed.
 *
 * THIS PATH KEEPS THE SHIPPED DASH TOKEN. Unlike the gauge's 120-unit arc,
 * the trend line runs roughly 190 units across six points, so --tcs-svg-dash
 * (1200) covers it with headroom and no override is needed — which is exactly
 * why the gauge's override earned its own token rather than one being applied
 * to both.
 * ========================================================================= */

.tcs-chapter__mini--trend {
	display: block;
	height: auto;
}

.tcs-chapter__trend-axis {
	fill: none;
	stroke: var(--tcs-cream-2);
	stroke-width: 1;
}

/*
 * The dashed level. This dasharray is STATIC TEXTURE, not an animation — it
 * never moves, so unlike the noise band's flow line it needs no reduced-motion
 * treatment. Worth stating because the two look identical in a diff.
 */
.tcs-chapter__trend-level {
	fill: none;
	stroke: var(--tcs-cream-2);
	stroke-width: 1;
	stroke-dasharray: 3 5;
}

.tcs-chapter__trend-line {
	fill: none;
	stroke: var(--tcs-orange);
	stroke-width: 2.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.tcs-chapter__trend-lens {
	fill: none;
	stroke: var(--tcs-black);
	stroke-width: 1.8;
}

.tcs-chapter__trend-lens-handle {
	fill: none;
	stroke: var(--tcs-black);
	stroke-width: 1.8;
	stroke-linecap: round;
}

/* ===========================================================================
 * MINIATURE 6 · THE ORDER BOOK  —  Main.dc.html:322-329
 *
 * Three depth rows between a BIDS/ASKS header and a limit/market footer.
 *
 * STATIC, AND THAT IS A PRODUCT DECISION RATHER THAN A DESIGN ONE. An order
 * book that animated would read as live market data, and epic-2-context.md
 * bans fabricated liveness outright — no live counters, no "viewers right
 * now", nothing that implies a feed this page does not have.
 *
 * The green and red are --tcs-green and --tcs-error used as GRADIENT FILLS,
 * never as text. --tcs-error is 5.44:1 on white and would pass as type, but
 * the token's own rule is "errors only" and a bid/ask depth bar is not an
 * error state. Depth is a gradient stop, not a width — same reason as the
 * comparison bars.
 * ========================================================================= */

.tcs-chapter__mini--book {
	display: flex;
	flex-direction: column;
	gap: 5px;
}

.tcs-chapter__book-head {
	display: flex;
	justify-content: space-between;
}

/*
 * Raised from the drawn 9.5px to the 12px floor, and from the failing #B8B3A6
 * (2.09:1) to --tcs-label-on-white (4.87:1).
 */
.tcs-chapter__book-side {
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-bold);
	letter-spacing: .09em;
	line-height: 1.2;
	color: var(--tcs-label-on-white);
}

.tcs-chapter__book-row {
	display: flex;
	gap: 4px;
}

.tcs-chapter__depth {
	flex: 1 1 0;
	min-width: 0;
	height: 7px;
	border-radius: 2px;
}

/*
 * The two sides fill from OPPOSITE EDGES — bids grow rightward from the
 * centre gutter, asks leftward — which is what makes the pair read as one
 * book rather than as two unrelated bars. --tcs-depth is set per row inline.
 */
.tcs-chapter__depth--bid {
	background: linear-gradient(90deg, var(--tcs-cream-2) var(--tcs-depth), var(--tcs-green) var(--tcs-depth));
}

.tcs-chapter__depth--ask {
	background: linear-gradient(90deg, var(--tcs-error) var(--tcs-depth), var(--tcs-cream-2) var(--tcs-depth));
}

.tcs-chapter__book-foot {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 3px;
}

.tcs-chapter__book-type {
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-semibold);
	line-height: 1.2;
	color: var(--tcs-label-on-white);
}

/*
 * The separator is the artboard's #CFC9BC — decorative punctuation between two
 * labels rather than text carrying meaning.
 *
 * IT IS aria-hidden IN THE MARKUP, so it never reaches a screen reader. It is
 * still PAINTED text, though, so it still has to pass: a .6 opacity was tried
 * and rejected for the same reason as the greyed term above (it composites to
 * 2.33:1, below even the large-text threshold). It sits at full strength on
 * --tcs-caption, 4.87:1, and the visual subordination comes from it being a
 * single narrow glyph between two words.
 */
.tcs-chapter__book-sep {
	font-size: var(--tcs-fs-eyebrow);
	line-height: 1.2;
	color: var(--tcs-caption);
}

/* ===========================================================================
 * MINIATURE 7 · THE SEED-PHRASE GRID  —  Main.dc.html:333-341
 *
 * A padlock, a label, twelve cells with the sixth marked, and a caption.
 * TWELVE IS MEANING, NOT LAYOUT — it is the seed-phrase length the chapter is
 * about — which is why the loop in front-page.php counts to 12 rather than
 * filling however many cells fit. STATIC: a seed phrase does not move.
 * ========================================================================= */

.tcs-chapter__mini--seed {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.tcs-chapter__seed-head {
	display: flex;
	align-items: center;
	gap: 8px;
}

/*
 * The padlock. Sized in the stylesheet rather than on the element so the
 * stroke and the box stay one decision; `flex: 0 0 auto` stops it being
 * squeezed to nothing when the label wraps at a narrow column — a squashed
 * lock is a mark that has effectively vanished.
 */
.tcs-chapter__lock {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	stroke: var(--tcs-orange);
	stroke-width: 1.9;
	stroke-linecap: round;
}

/* --tcs-ink is 11.75:1 on white. Raised from the drawn 11px to the floor. */
.tcs-chapter__seed-label {
	font-size: var(--tcs-fs-eyebrow);
	font-weight: var(--tcs-fw-semibold);
	line-height: 1.2;
	color: var(--tcs-ink);
}

.tcs-chapter__seed-grid {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 4px;
}

.tcs-chapter__seed-cell {
	height: var(--tcs-seed-cell-h);
	border-radius: 3px;
	background: var(--tcs-cream-2);
}

.tcs-chapter__seed-cell--marked {
	background: var(--tcs-orange);
}

/* ===========================================================================
 * MINIATURE 8 · THE THREE COIN MARKS  —  Main.dc.html:351-353
 *
 * Bitcoin, Ethereum and a generic altcoin on the dark panel.
 *
 * THE FIRST TWO ARE REUSED, NOT REDRAWN — tcs_hero_coin_bitcoin() and
 * tcs_hero_coin_ethereum() already take a size argument and their paths are
 * geometrically identical to what the artboard draws here.
 *
 * THE THIRD IS NOT tcs_hero_coin_tether(), and that is a content decision. The
 * artboard draws a generic ringed glyph in --tcs-muted, not the Tether mark,
 * and the card's title is "Bitcoin, Ethereum & altcoins" — substituting a
 * specific stablecoin would state something about the chapter's contents that
 * the design does not. Reuse where the geometry matches; do not force it where
 * it does not.
 *
 * The two reused marks paint their own brand colours as SVG fills and carry no
 * text, so no contrast pairing arises from them. --tcs-muted on --tcs-panel is
 * 5.18:1 and passes for the third.
 * ========================================================================= */

.tcs-chapter__mini--coins {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
}

/*
 * The marks shrink together at a narrow column rather than one of them
 * wrapping out of the row. `min-width: 0` and a shared flex basis keep all
 * three visible at 390 — the width where the mobile deviation has to hold.
 */
.tcs-chapter__coin {
	display: flex;
	flex: 0 1 auto;
	min-width: 0;
	align-items: center;
}

.tcs-chapter__coin svg {
	display: block;
	max-width: 100%;
	height: auto;
}

.tcs-chapter__alt-ground {
	fill: var(--tcs-border-dark);
}

.tcs-chapter__alt-ring {
	stroke: var(--tcs-muted);
	stroke-width: 2.2;
}

.tcs-chapter__alt-glyph {
	stroke: var(--tcs-muted);
	stroke-width: 1.7;
	stroke-linecap: round;
}

/* ===========================================================================
 * 6. THE ENTRY-GATED ANIMATIONS
 *
 * The three animated miniatures — the gauge, the trend line, the candles — and
 * the whole reason this section is more than static CSS.
 *
 * ───────────────────────────────────────────────────────────────────────────
 * THE INVERSION, WHICH IS THE ONLY THING THAT MAKES THIS SAFE
 * ───────────────────────────────────────────────────────────────────────────
 *
 * THE DEFAULT STATE IS FINISHED. The rules below paint every miniature at its
 * completed value — stroke-dashoffset 0, scaleY(1), opacity 1 — and NOTHING
 * hides them. The start state is armed only inside `.tcs-js`, the class
 * js/reveal.js sets on <html> as its very first synchronous act, and the
 * animation runs only once `.tcs-revealed` lands on the card. So:
 *
 *   script blocked / fails to parse  -> .tcs-js never set  -> drawn and grown
 *   JavaScript disabled entirely     -> .tcs-js never set  -> drawn and grown
 *   no IntersectionObserver          -> .tcs-js REMOVED    -> drawn and grown
 *   observer never fires             -> .tcs-revealed absent, see below
 *
 * The naive form — hide the marks in CSS and reveal them in JS — turns every
 * one of those into eight empty cards, which is precisely the rejected state
 * this story exists to prevent. It is ruled out structurally here rather than
 * by care. This is the same inversion motion.css:39-53 established for the
 * reveal itself, and it is deliberately identical so the two cannot drift.
 *
 * WHY THE CARD AND NOT THE PANEL. The gate is `.tcs-reveal.tcs-revealed`, and
 * the CARD is the reveal target — so a miniature's animation is chained to the
 * same intersection that fades its card in, with no second observer, no second
 * class, and no possibility of the two disagreeing about when "entry" was. The
 * spec's boundaries forbid a second IntersectionObserver; this is how the
 * requirement is met without one. js/reveal.js is untouched by this story.
 *
 * ON ENTRY, NOT ON LOAD. _head.html:58-60 runs both animations unconditionally
 * because an artboard has no scroll. This band is six sections down a real
 * page: unconditional animations would finish long before a reader arrived and
 * every miniature would read as static. That is a medium difference, not a
 * contradiction with the board.
 *
 * NEVER LOOPING. Both animations use `forwards` / `both`, whose implied
 * iteration count is 1. `infinite` appears nowhere in this file, and the gate
 * asserts the computed value rather than trusting the shorthand.
 * ========================================================================= */

/*
 * THE DRAW — the gauge arc and the trend line.
 *
 * `stroke-dashoffset` is the one documented exception to "only opacity and
 * transform animate". It is the only way to express a line drawing itself, it
 * is the exact thing the artboard draws, it is not a layout property, and its
 * cost here is bounded to two short paths. Story 2.2 established the exception
 * for the noise band's flow line on the same terms.
 */
@keyframes tcs-chapter-draw {

	from {
		stroke-dashoffset: var(--tcs-chapter-dash);
	}

	to {
		stroke-dashoffset: 0;
	}
}

/*
 * THE RESTING STATE, unconditional. Every drawn path is fully painted unless
 * something below explicitly arms it. Setting dashoffset to 0 rather than
 * simply omitting a dasharray means the resting value is stated rather than
 * implied — so a later edit that adds a dasharray cannot silently leave a path
 * half-drawn.
 */
.tcs-chapter__drw {
	stroke-dashoffset: 0;
}

/*
 * THE GAUGE'S OWN DASH LENGTH. --tcs-chapter-dash is the per-path variable the
 * keyframe reads; the gauge overrides it to the short-arc token because its
 * path is roughly 120 units and the shipped 1200 would hold the stroke
 * offscreen for most of its run (Main.dc.html:297). The trend line inherits
 * the shipped --tcs-svg-dash below.
 */
.tcs-chapter__drw {
	--tcs-chapter-dash: var(--tcs-svg-dash);
	--tcs-chapter-draw-dur: var(--tcs-dur-svg-draw);
}

.tcs-chapter__gauge-arc {
	--tcs-chapter-dash: var(--tcs-gauge-dash);
	--tcs-chapter-draw-dur: var(--tcs-dur-gauge-draw);
}

/*
 * ARM THE START STATE — only with JS present, and only before entry.
 *
 * `:not(.tcs-revealed)` is what makes this an ENTRY animation rather than a
 * load animation: the path is held at its full offset while the card is
 * offscreen, and released the moment the observer marks it revealed.
 *
 * The dasharray is set HERE rather than on the resting rule for a specific
 * reason: with no JS the path must have NO dasharray at all, because a partial
 * dasharray is the animation's texture rather than its finished value — the
 * same trap noise.css:744 documents for the flow line.
 */
.tcs-js .tcs-chapter__drw {
	stroke-dasharray: var(--tcs-chapter-dash);
}

.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__drw {
	stroke-dashoffset: var(--tcs-chapter-dash);
}

.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__drw {
	animation: tcs-chapter-draw var(--tcs-chapter-draw-dur) var(--tcs-ease-draw) forwards;
}

/*
 * THE GROW — the seven candles.
 *
 * scaleY from --tcs-bar-scale-from (.05), not from 0: a bar at scaleY(0) has
 * no painted pixel at all, and a miniature whose marks are momentarily absent
 * is the rejected empty card arriving one frame at a time. The origin is
 * `bottom` so a candle grows up from its baseline; at the default centre
 * origin the bars would expand in both directions and detach from the axis.
 */
@keyframes tcs-chapter-grow {

	from {
		opacity: 0;
		transform: scaleY(var(--tcs-bar-scale-from));
	}

	to {
		opacity: 1;
		transform: scaleY(1);
	}
}

/* The resting state: grown, opaque, sitting on the baseline. */
.tcs-chapter__bar {
	transform-origin: var(--tcs-bar-origin);
	transform: scaleY(1);
	opacity: 1;
}

.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__bar {
	transform: scaleY(var(--tcs-bar-scale-from));
	opacity: 0;
}

.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__bar {
	/*
	 * `both` holds the final value after the run, so nothing snaps back, and
	 * its implied iteration count is 1 — never `infinite`.
	 *
	 * The per-candle delay is its index times the token step, composed here
	 * rather than written into seven inline styles: --tcs-candle-i is the only
	 * thing the markup supplies, and the 0.08s step stays a design-system
	 * value. calc() on a time is valid in every browser this project targets.
	 */
	animation: tcs-chapter-grow var(--tcs-dur-bar-grow) var(--tcs-ease-reveal) both;
	animation-delay: calc(var(--tcs-candle-i, 0) * var(--tcs-bar-stagger));
}

/* ===========================================================================
 * 7. REDUCED MOTION
 *
 * Every animation above resolves to its FINISHED value — drawn, grown,
 * complete. Never to zero, never to invisible, never to a gap.
 *
 * NOT SCOPED TO .tcs-js, following motion.css:379, hero.css:1307 and
 * noise.css:729 for the same reason each of them gives: reveal.js can only
 * remove that class at DOMContentLoaded, and anything scoped to it would spend
 * the interval before that at its start value — which is motion, for a reader
 * who asked for none.
 *
 * THE DASHARRAY MUST BE CLEARED, not just the animation stopped. `animation:
 * none` alone would leave the gauge arc at dasharray 120 with its offset
 * wherever the compositor last put it — a stroke with pieces missing, which is
 * the animation's texture rather than its final value. noise.css:744 documents
 * the identical trap on the flow line.
 * ========================================================================= */

@media (prefers-reduced-motion: reduce) {

	.tcs-chapter__drw,
	.tcs-js .tcs-chapter__drw,
	.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__drw,
	.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__drw {
		animation: none !important;
		stroke-dasharray: none !important;
		stroke-dashoffset: 0 !important;
	}

	.tcs-chapter__bar,
	.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__bar,
	.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__bar {
		animation: none !important;
		transform: scaleY(1) !important;
		opacity: 1 !important;
	}
}

/* ===========================================================================
 * 8. MOBILE  —  HomeMobile.dc.html:108-122, WITH ONE DELIBERATE DEVIATION
 *
 * The board draws eight cards at 2 columns, 11px radius, 20px 16px padding,
 * a 12px gap, a 34px headline and NO BODY COPY. All of that is honoured.
 *
 * WHAT IS NOT HONOURED, AND WHY. The board draws NO MINIATURE PANEL on any of
 * the eight cards — numeral and title only. That board predates the rejection
 * this story exists to answer; the rule that came out of it ("an empty card
 * with an icon and a gap is the defect state") carries no width qualifier, and
 * the product brief says most buyers arrive on a phone. Honouring the board
 * here would ship the rejected state to the MAJORITY of readers while the
 * desktop gate stayed green. So the miniatures survive and scale down inside
 * the 2-column grid. This is the one place in this file where an artboard is
 * knowingly overridden, and it is overridden toward the story's own explicit
 * boundary rather than away from it.
 * ========================================================================= */

@media (max-width: 768px) {

	/*
	 * The header stops being a row. At 390 a 34px headline and a lede cannot
	 * sit side by side, and flex-end alignment on a wrapped column would push
	 * the lede against the cards.
	 */
	.tcs-chapters__head {
		display: block;
		margin-bottom: 28px;
	}

	.tcs-chapters__headline {
		margin-bottom: 12px;
		font-size: var(--tcs-fs-h2-mobile);
		line-height: var(--tcs-lh-h2-mobile);
		letter-spacing: var(--tcs-ls-h2-mobile);
	}

	.tcs-chapters__lede {
		max-width: none;
		font-size: 15.5px;
	}

	/* Swap the two lede copies. Both shipped; exactly one displays. */
	.tcs-chapters__desktop-copy {
		display: none;
	}

	.tcs-chapters__mobile-copy {
		display: block;
	}

	.tcs-chapters__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 12px;
	}

	.tcs-chapter {
		gap: 10px;
		padding: 16px;
		border-radius: var(--tcs-radius-chapter-card-mobile);
	}

	/*
	 * THE PANEL SHRINKS BUT DOES NOT DISAPPEAR. 104px inside a ~163px column
	 * would crowd the title out, so the minimum drops to 76px and the padding
	 * tightens — the miniature stays, which is the whole point of the
	 * deviation. A `display: none` here would be the rejected state.
	 */
	.tcs-chapter__panel {
		min-height: 76px;
		padding: 10px;
	}

	/* The candle row loses height with the panel; the bars keep their ratio. */
	.tcs-chapter__mini--candles {
		height: 56px;
		gap: 3px;
	}

	.tcs-chapter__candle {
		width: 7px;
		/*
		 * Scale the drawn heights rather than restating seven values: 0.72
		 * maps the tallest candle (70px) to 50px, inside the 56px row.
		 */
		height: calc(var(--tcs-candle-h) * .72);
	}

	/*
	 * The definition card is the densest miniature and the one most at risk of
	 * overflowing a 163px column. Its 3-line definition drops; the term, the
	 * rule and the next term remain, so it still carries four real marks and
	 * reads as the same drawing.
	 */
	.tcs-chapter__term-def {
		display: none;
	}

	/* The bar labels give up width so the tracks stay visibly filled. */
	.tcs-chapter__bar-label {
		width: 40px;
	}

	.tcs-chapter__mini--bars {
		gap: 6px;
	}

	/* Three coin marks in a narrow column: tighten the gap, keep all three. */
	.tcs-chapter__mini--coins {
		gap: 8px;
	}

	.tcs-chapter__seed-grid {
		gap: 3px;
	}

	.tcs-chapter__seed-cell {
		height: 10px;
	}

	/*
	 * The card TITLE drops to 15px, as drawn (HomeMobile.dc.html:113). The
	 * body copy is already hidden by the .tcs-chapters__desktop-copy rule
	 * above, which the card copy also carries — the board drops it, and unlike
	 * the miniature there is no accessibility argument for keeping it.
	 */
	.tcs-chapter__title {
		font-size: 15px;
		letter-spacing: -.02em;
		line-height: 1.28;
	}
}

/* ===========================================================================
 * 9. PRINT / READER MODE  (code-review fix, 2026-09-04)
 *
 * .tcs-js is armed — JS ran — but IntersectionObserver never fires in a
 * print render, a "save as PDF", or a reader-mode strip: there is no scroll,
 * so the entry gate never flips a card to .tcs-revealed. Neither of the two
 * fallbacks above covers this. The reduced-motion block only fires when the
 * READER has asked for no motion, which print has nothing to do with. The
 * no-JS path never applies either, because .tcs-js IS present. Left
 * unhandled, the three animated miniatures would print at their pre-entry
 * state: the gauge and trend line's stroke held at full dashoffset
 * (invisible) and the candles at scaleY(--tcs-bar-scale-from) opacity 0 —
 * the rejected empty-card state, shipped to every reader who prints or
 * saves the page. Resolved the same way as the reduced-motion block: force
 * every animated miniature to its finished value.
 * ========================================================================= */

@media print {

	.tcs-chapter__drw,
	.tcs-js .tcs-chapter__drw,
	.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__drw,
	.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__drw {
		animation: none !important;
		stroke-dasharray: none !important;
		stroke-dashoffset: 0 !important;
	}

	.tcs-chapter__bar,
	.tcs-js .tcs-reveal:not(.tcs-revealed) .tcs-chapter__bar,
	.tcs-js .tcs-reveal.tcs-revealed .tcs-chapter__bar {
		animation: none !important;
		transform: scaleY(1) !important;
		opacity: 1 !important;
	}
}
