/* ============================================================================
   EXHIBITS-EXTRA.CSS — geometry, palette and motion for the five extra kinds
   ============================================================================
   The colour half of inc/exhibits-extra.php. Scoped entirely under `.cz-xb`:
   not one selector in this file can reach anything that was on the page before
   it.

   THERE ARE EXACTLY THREE `!important` DECLARATIONS AND ALL THREE ARE IN THE
   `@media (scripting: none)` BLOCK AT THE FOOT. They are unavoidable there and
   nowhere else: that block has to beat a pre-state written as
   `.cz-xb[data-reveal]:not(.visible) .cz-xb__num`, which is MORE specific than
   the `… *` selector a catch-all reset has to use, so source order cannot win
   it. The alternative is repeating every pre-state selector a second time, and
   a list that has to be kept in step with another list is the thing that goes
   stale. Nothing outside that block carries one.

   Loaded on reading views only, never on an activity view (R50), after
   devices-charts.css and — where the route has it — after bizsetup.css.

   ----------------------------------------------------------------------------
   HIS BRIEF, WHICH IS WHAT THIS FILE IS ANSWERING
   ----------------------------------------------------------------------------
   "Your infographics are good I noticed, but could have been more artistic,
    aesthetic, creative, with animation, good ui ux and overall better and
    professional… Should be informative, but not too complicated 'info'."

   "Not too complicated" is the constraint that does the most work below. Every
   one of these five drawings had a richer draft and every one of them was cut
   back: `plot` lost the labels inside its field, `parts` lost a second
   encoding on its squares, `chrono` lost its horizontal axis, `weigh` lost a
   tilting beam and `routing` lost its curved connectors. What is left in each
   case is the one reading the exhibit exists to deliver, drawn once.

   ----------------------------------------------------------------------------
   1. TOKENS — READ, NEVER REDECLARED
   ----------------------------------------------------------------------------
   main.css:1 states the rule: a token is declared once, there, because nine
   competing :root blocks once shipped two golds and two inks on every page.
   bizsetup.css then declares the exhibit palette on `.cz-bz`. This file is on
   226 documents and bizsetup.css is on five slugs, so it can neither depend on
   those values nor declare them again.

   THE ANSWER IS `var(--bz-…, <measured literal>)` ON EVERY TOKEN. Where
   bizsetup.css is on the route these kinds take its palette by reference, so a
   page carrying both families has one palette and not two. Where it is not,
   each token falls back to the identical measured value. Nothing is declared
   twice and nothing can drift — the pattern bizsetup.css itself uses for
   --cz-gold-ink.

   MEASURED RATIOS, WCAG 2.x, against the two grounds these exhibits sit on:

     TOKEN        HEX        on #fff   on #f3f5f7   white text on it
     --xb-c1      #0b5fa5      6.57       6.02          6.57
     --xb-c2      #0c6f78      5.90       5.40          5.90
     --xb-c3      #856527      5.40       4.94          5.40   (= --cz-gold-ink)
     --xb-c4      #7b3f62      7.69       7.04          7.69
     --xb-quiet   #5b6570      5.93       5.43            --   (= --cz-mid)
     --xb-rule    #8b949d      3.08       2.82            --
     --xb-ink     #14181c     17.84      16.32            --   (= --cz-text)

   So every accent clears 4.5:1 as a word and as a thin mark, carries white type
   at 4.5:1 as a filled area, and --xb-rule clears the 3:1 floor a graphical
   object that carries meaning has to clear.

   --xb-grid IS #dcdbd8 AND IT IS NOT NEGOTIABLE. 23-EXHIBIT §1, from the real
   Economist styleguide: "Horizontal gridlines only. No verticals, no minors.
   Grey #dcdbd8." It measures 1.37:1 against paper, which is correct — a
   gridline that competes with a mark is chartjunk (§5, "Gridlines: light grey,
   6-10 maximum"). It is never the only cue for anything.

   ADJACENCY. Accent against accent measures 1.09:1 to 1.56:1, nowhere near the
   3:1 two touching marks need. So no two accent fills ever touch: the gridplot
   gaps its squares by 5px of paper, the plot's marks are separated by position,
   and every group, side and destination is DIRECTLY LABELLED in text. Colour is
   never the only means of conveying information (WCAG SC 1.4.1).

   ----------------------------------------------------------------------------
   2. THE ORDERED RAMP — and why it is not four hues
   ----------------------------------------------------------------------------
   23-EXHIBIT §4: "Chronological categories take a light-to-dark ramp of one
   hue, not different hues." `chrono` is a chronology, so its events walk
   --xb-r--1 to --xb-r--5 rather than cycling the categorical four. Four
   unrelated hues on four sequential events says the events are four unrelated
   things, which is the opposite of what a sequence means.

   FIVE STEPS, BUILT ON LIGHTNESS. Lightness is the load-bearing channel, not
   saturation, and the hue bends slightly across the run so a "monochromatic"
   ramp keeps its discriminability (§4). Every step is measured on paper:

     --xb-r--1  #6d93b4   3.06:1      --xb-r--4  #124f7e   7.94:1
     --xb-r--2  #3f7aa8   4.07:1      --xb-r--5  #0d3557  11.31:1
     --xb-r--3  #1f6698   5.52:1

   Steps 1 and 2 are under 4.5:1 and are therefore used ONLY as fills and
   marks, never as type: .cz-xb__evdate and .cz-xb__evh take --xb-ink and
   --xb-quiet at every step, and the ramp lands on the pin and the stalk. A mark
   needs 3:1 and step 1 clears it.

   ----------------------------------------------------------------------------
   3. TYPE — the datum dominates, which is the craft fix
   ----------------------------------------------------------------------------
   MEASURED BEFORE: on /business-setup-in-dubai/ at 1440 every figure in every
   exhibit rendered at 15px/700 and every label at 15px/600 — one size for the
   number and its own name, separated by a weight step and a hue. That is what
   "timid type" looks like from the inside: there is no hierarchy to read, so
   the eye has nowhere to land and the drawing reads as a styled list.

   FOUR STEPS HERE, AND THE GAP BETWEEN THEM IS DELIBERATE:

     strip      12px  800  .13em  uppercase   --xb-c3     the measure
     datum      17-20px 700 -.01em tabular    --xb-hue    the number
     label      15px  600                     --xb-ink    what it is
     note       13px  400                     --xb-quiet  why it matters

     Ratio datum:label is 1.13 to 1.33 — a real step, and still short of the 2x
     that turns an exhibit into a poster. 23-EXHIBIT §1 records the Economist's
     own headline:subtitle ratio as 9.5:8, "a subtle step, not the 2x that
     secondary blogs claim", and this is the same judgement applied one level
     down.

   THE 12px FLOOR IS R7 AND IT HOLDS AT EVERY VIEWPORT, because every label in
   this file is real CSS type. Nothing is drawn inside a scaled viewBox, so
   nothing shrinks with its container — the whole reason these engines are
   hybrid rather than SVG.

   TABULAR FIGURES on every datum, so a column of money or days aligns on its
   digits rather than on its glyph widths.

   ----------------------------------------------------------------------------
   4. R51 — NOTHING IS BOXED
   ----------------------------------------------------------------------------
   No card, no border, no radius, no shadow, no tinted field behind an exhibit.
   Section 5 below is written as a set of zeroes rather than as an absence,
   because `.cz-prose figure` and the browser would otherwise supply a panel.
   Structure comes from hairline rules, alignment and whitespace.

   TWO MARKS CARRY A SHAPE THAT IS NOT A RULE, and both are data:
     - the gridplot's unit square. It is the datum — one square is one thing
       counted — and its 1px radius is a drawing weight, not a container.
     - the plot's numeral chip. It is the mark AND its label in one, which is
       what lets the field carry eleven routes with no legend and no collision.
   Neither sits behind an exhibit; both ARE the exhibit.

   ----------------------------------------------------------------------------
   5. ALIGNMENT — one left edge, three widths, one rhythm
   ----------------------------------------------------------------------------
   THE LEFT EDGE. `margin-inline: 0`, and every kind's leftmost element sits at
   column start with no indent of its own. Nothing here sets a negative
   margin-left, a float or a hanging indent (R5). Exhibits that want room grow
   RIGHT, into the margin column.

   THE WIDTHS are computed from the reading grid's own custom properties —
   --cz-fig-measure, --cz-fig-full, --cz-read-gap — which placement.css declares
   on .cz-reading__body and redefines per breakpoint. So when that grid changes
   these follow. Which width a kind takes by default is decided in
   cz_xb_width(), and that is the correction it is: measured at 1440 on
   20 September 2026, every exhibit on the site was 715px against a 715.45px
   prose column, so no drawing anywhere had ever left the text measure.

   THE RHYTHM. `margin-block: clamp(30px, 3vw, 46px) clamp(18px, 1.8vw, 26px)`
   — the pair devices-variants.css:203 and bizsetup.css both use, so a page
   carrying exhibits from three engines has one spacing system.

   MEASURED AT 375, 768 AND 1440. Every breakpoint below is bounded at both ends
   where it needs to be: reading.css:70 records what an open-ended
   `max-width: 1400px` did to a 375px phone and this file does not repeat it.

   ----------------------------------------------------------------------------
   6. MOTION (R16, and his "with animations")
   ----------------------------------------------------------------------------
   THE CONTRACT, which is devices.css's and bizsetup.css's reused exactly so the
   four engines cannot disagree:

     - the pre-state exists ONLY under `.cz-xb[data-reveal]:not(.visible)`.
       cz_add_reveal_hooks() puts data-reveal on every <figure> and main.js adds
       .visible when it intersects — or IMMEDIATELY, to everything, when the
       reader prefers reduced motion or the browser has no IntersectionObserver.
       So THE FINISHED EXHIBIT IS THE DEFAULT and the animation is the
       exception. Nothing here is required to comprehend the drawing;
     - only `transform`, `opacity` and `stroke-dashoffset` are animated. No
       height, no width, no margin, no top: zero layout cost, zero CLS;
     - NOTHING BOUNCES. --cz-ease (.2,.7,.2,1) and --cz-ease-out (.16,1,.3,1),
       neither of which overshoots. There is no @keyframes rule in this file at
       all, so nothing loops, nothing spins and nothing plays twice;
     - the longest entrance is 540ms with a stagger window capped at 12 steps of
       34ms — 948ms end to end at the very worst, inside his "not too
       complicated" and inside the 1.2s a reveal has to live in;
     - `prefers-reduced-motion: reduce` and `print` each neutralise every
       pre-state and every transition, leaving the finished, fully visible
       exhibit.

   THE THREE BEATS, so a sequence is legible rather than decorative:
     A. GROUND   the baseline, rail or bus draws (stroke-dashoffset over a
                 normalised pathLength), and the gridlines fade in behind it.
     B. DATA     marks, squares, pins, rows and conditions arrive in reading
                 order, 34-58ms apart, from 6-8px off their resting place.
     C. THE CLAIM the denominator, the focus row and the destination land last.

   NEVER ON TYPE THE READER IS TRYING TO READ. content-enrich.php records what
   happened when headings were given data-reveal: "reading down the page you met
   a stretch of blank space where a heading should be". No heading and no body
   paragraph in this file has a pre-state. What moves is the mark and the row it
   sits in, and a row's text moves WITH its mark rather than on its own.
   ============================================================================ */


/* ------------------------------------------------------------
   TOKENS — read from bizsetup.css where present, measured where not
   ------------------------------------------------------------ */
.cz-xb {
  /* The categorical four. Section 1 carries every ratio. */
  --xb-c1: var(--bz-c1, #0b5fa5);
  --xb-c2: var(--bz-c2, #0c6f78);
  --xb-c3: var(--bz-c3, var(--cz-gold-ink, #856527));
  --xb-c4: var(--bz-c4, #7b3f62);

  /* The ordered ramp, for chronological categories only. Section 2. */
  --xb-r1: #6d93b4;
  --xb-r2: #3f7aa8;
  --xb-r3: #1f6698;
  --xb-r4: #124f7e;
  --xb-r5: #0d3557;

  --xb-ink: var(--bz-ink, var(--cz-text, #14181c));
  --xb-quiet: var(--bz-quiet, var(--cz-mid, #5b6570));
  --xb-paper: var(--bz-paper, var(--cz-paper, #fff));

  /* Two line weights, and they are not interchangeable. --xb-rule carries
     meaning and clears 3:1; --xb-hair is structure only and is never the sole
     cue for anything. --xb-grid is the house gridline and nothing else. */
  --xb-rule: var(--bz-rule, #8b949d);
  --xb-hair: var(--cz-line, rgba(8, 10, 13, .11));
  --xb-grid: #dcdbd8;

  /* The current accent. Every kind sets this on a row, a mark or a group and
     everything inside reads it, which is how one rule paints a square, a span,
     a pin, a chip and a bus without five copies of each. */
  --xb-hue: var(--xb-c1);

  /* Spacing. One gutter, one row step, one label column — so the plot's
     legend, the ledger's criterion and the fan's condition all break at the
     same place and a page of exhibits has one grid. */
  --xb-gap: var(--bz-gap, clamp(12px, 1.2vw, 18px));
  --xb-row: var(--bz-row, clamp(10px, 1vw, 14px));
  --xb-key: var(--bz-key, clamp(150px, 15vw, 240px));

  /* The measure of a secondary note. A cap, so a narrower column ignores it.
     66ch is inside the comfortable 45-75 band by construction, whatever the
     type size — which is why the unit is ch and not px. */
  --xb-note: var(--bz-note, 66ch);

  /* Type — section 3. */
  --xb-t-strip: 12px;
  --xb-t-note: 13px;
  --xb-t-body: 15px;
  --xb-t-datum: clamp(17px, .38vw + 15px, 20px);

  /* Motion — section 6. */
  --xb-in: .54s;
  --xb-step: 56ms;
  --xb-ease: var(--cz-ease, cubic-bezier(.2, .7, .2, 1));
  --xb-ease-out: var(--cz-ease-out, cubic-bezier(.16, 1, .3, 1));

  display: block;

  /* THE EXHIBIT IS ITS OWN QUERY CONTAINER, and a media query could not do this
     job: these kinds come in three widths a viewport cannot tell apart — at
     1440 a `measure` exhibit is 715px and a `step` exhibit beside it is 759px,
     and the row layout that is right at 759 is wrong at 715. Asking the EXHIBIT
     its width gets both cases right with one rule, and it keeps working when
     the rail form (R69) takes the margin column and every exhibit on the page
     narrows at once.

     Safe on all three widths because every one of them is declared explicitly
     below: inline-size containment means the box does not consult its contents
     to size itself, and none of these boxes ever did. A browser without support
     never matches the query and keeps the narrow layout, which is the fallback
     either way. */
  container-type: inline-size;
  container-name: xb;

  margin-inline: 0;                       /* R5 — one left edge, always. */
  margin-block: clamp(30px, 3vw, 46px) clamp(18px, 1.8vw, 26px);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--xb-ink);
  font-family: var(--sans, "Overpass", "Segoe UI", Arial, sans-serif);
  font-size: var(--xb-t-body);
  line-height: 1.45;
}

/* Belt and braces on the left edge: placement.css pins .cz-fig, editorial.css
   pins its own, bizsetup.css pins .cz-bz, and this pins these — under both the
   body and the prose, so a template that nests differently cannot reintroduce
   the hang R5 forbids. */
.cz-reading__body .cz-xb,
.cz-prose .cz-xb {
  margin-left: 0;
  margin-right: 0;
  float: none;
}

/* R51, written as zeroes. `.cz-prose figure` and the browser both supply a
   panel otherwise. */
.cz-xb,
.cz-xb > * {
  border-radius: 0;
  box-shadow: none;
}

.cz-xb ol,
.cz-xb ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.cz-xb li::before,
.cz-xb li::marker {
  content: none;
}

/* THE THREE WIDTHS. Every one grows RIGHT from the same left edge. */
.cz-xb--w-measure {
  width: var(--cz-fig-measure, 74ch);
  max-width: 100%;
}

.cz-xb--w-step {
  width: calc(var(--cz-fig-measure, 74ch) + var(--cz-read-gap, 40px));
  max-width: var(--cz-fig-full, 100%);
}

.cz-xb--w-bleed {
  width: var(--cz-fig-full, 100%);
  max-width: var(--cz-fig-full, 100%);
}

/* The accent slots. Set --xb-hue and everything downstream follows. */
.cz-xb__c--1 { --xb-hue: var(--xb-c1); }
.cz-xb__c--2 { --xb-hue: var(--xb-c2); }
.cz-xb__c--3 { --xb-hue: var(--xb-c3); }
.cz-xb__c--4 { --xb-hue: var(--xb-c4); }

/* Slot 0 is the QUIET slot, for a row that carries data but no mark — the
   plot's unplaced routes. Not an accent, on purpose: an unplaced row must not
   look like it has a position. */
.cz-xb__c--0 { --xb-hue: var(--xb-quiet); }

/* The ordered ramp. Section 2 — lightness carries it, the hue bends slightly. */
.cz-xb__r--1 { --xb-hue: var(--xb-r1); }
.cz-xb__r--2 { --xb-hue: var(--xb-r2); }
.cz-xb__r--3 { --xb-hue: var(--xb-r3); }
.cz-xb__r--4 { --xb-hue: var(--xb-r4); }
.cz-xb__r--5 { --xb-hue: var(--xb-r5); }


/* ============================================================
   THE MEASURE STRIP — the one piece of shared furniture
   ============================================================
   NOT a caption (R42, R3.9): it names the measure, which is the one thing an
   exhibit cannot leave to the prose without becoming ambiguous. The 26px rule
   to its left is the house equivalent of the Economist's tag — a hairline, not
   a container, and the only thing in this file that is decoration adjacent.
   It earns its place by making every exhibit on the site open the same way. */
.cz-xb__unit {
  display: flex;
  /* nowrap, and it is load-bearing. A unit strip is a phrase, and at 375px
     "first-year cost across, time to licence up" is three lines. Allowed to
     wrap as a FLEX LINE the text drops below the rule and leaves it sitting
     alone; held on one line the text item shrinks and wraps inside itself, so
     the rule hangs to the left and every line of the label shares one left
     edge. That is section 5 applied to the one item outside the grid. */
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: 11px;
  margin: 0 0 calc(var(--xb-row) * 1.6);
  padding: 0;
  color: var(--xb-c3);
  font-size: var(--xb-t-strip);
  font-weight: 800;
  letter-spacing: .13em;
  line-height: 1.3;
  text-transform: uppercase;
}

.cz-xb__unit::before {
  flex: none;
  width: 26px;
  height: 2px;
  margin-top: .58em;
  background: currentColor;
  content: "";
}


/* ============================================================
   SHARED TYPE PRIMITIVES
   ============================================================
   Every kind's label, datum and note come from here, which is the other half of
   "five shapes, one system". */
.cz-xb__rowh,
.cz-xb__evh,
.cz-xb__wh,
.cz-xb__condh,
.cz-xb__desth {
  color: var(--xb-ink);
  font-size: var(--xb-t-body);
  font-weight: 600;
  line-height: 1.32;
  /* A label in a narrow column wraps, and a wrap that leaves one word alone on
     the second line is the most visible misalignment an exhibit has. `balance`
     evens the lines; unsupported, it wraps exactly as it does today. */
  text-wrap: balance;
}

/* THE DATUM. Section 3 — this is the step the old exhibits did not have. */
.cz-xb__rowv,
.cz-xb__of-v,
.cz-xb__evdate {
  color: var(--xb-hue);
  font-size: var(--xb-t-datum);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.16;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* The SECOND datum, where a kind has two. One step down, because two figures at
   the same weight compete and the first one named is the one the row is sorted
   or argued on. */
.cz-xb__rowv2 {
  color: var(--xb-quiet);
  font-size: var(--xb-t-body);
  font-weight: 700;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.cz-xb__rowd,
.cz-xb__evd,
.cz-xb__condd {
  display: block;
  max-width: var(--xb-note);
  margin-top: 3px;
  color: var(--xb-quiet);
  font-size: var(--xb-t-note);
  font-weight: 400;
  line-height: 1.42;
  text-wrap: pretty;
}

/* The swatch that ties a legend row to its mark. 10px is under the 12px type
   floor and is allowed to be: R7 governs TEXT, and this carries none — it is a
   graphical object, and at 10px its accent clears the 3:1 that governs those. */
.cz-xb__sw {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  margin-top: .34em;
  background: var(--xb-hue);
}

.cz-xb__row.is-open .cz-xb__sw {
  background: none;
  box-shadow: inset 0 0 0 1.5px var(--xb-rule);
}

/* The stroked marks. One normalised dash each, so the stylesheet animates them
   without knowing the geometry. */
.cz-xb__rulesvg {
  display: block;
  width: 100%;
  height: 2px;
}

.cz-xb__rulesvg--rail,
.cz-xb__rulesvg--bus {
  width: 2px;
  height: 100%;
}

.cz-xb__ruleline {
  stroke: var(--xb-ink);
}

/* The open chevrons. §"the scale continues and is not being quantified here". */
.cz-xb__head {
  display: block;
  flex: none;
  width: 12px;
  height: 12px;
}

.cz-xb__head-mark {
  stroke: var(--xb-ink);
}


/* ============================================================
   1. PLOT — two numeric axes at once
   ============================================================ */

.cz-xb--plot .cz-xb__field {
  position: relative;
  /* ONE 54px BAND PER DISTINCT Y ANSWER, so the field is exactly as tall as the
     data needs and a chip is never crowded. NO aspect-ratio: a fixed ratio
     crowds nine bands and wastes paper on three, and `aspect-ratio` beside a
     `max-height` resizes rather than caps, which is a trap this project has
     already paid for. The +44px is the head room above the top band so the
     chevron and the top chip are not flush with the figure's edge. */
  height: calc(var(--rows, 5) * 54px + 44px);
  min-height: 210px;
  margin-bottom: calc(var(--xb-row) * 1.8);
}

/* THE GRIDLINES ARE THE AXIS. Horizontal only, grey, unlabelled, no frame, no
   y-axis line and no y ticks — 23-EXHIBIT §1. */
/* THE BANDS SHARE THE MARKS' VERTICAL SPACE EXACTLY — same top and bottom
   inset — so a chip always sits ON its band and never a few pixels off it. They
   span the full width, because they are the axis. */
.cz-xb__grid {
  position: absolute;
  top: 22px;
  right: 0;
  bottom: 18px;
  left: 0;
}

.cz-xb__gridline {
  position: absolute;
  right: 0;
  bottom: var(--t, 0);
  left: 0;
  height: 1px;
  background: var(--xb-grid);
}

/* THE ZERO LINE TAKES FULL BLACK; every other gridline stays grey (§1). */
.cz-xb__baseline {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: center;
  gap: 0;
}

.cz-xb__baseline .cz-xb__rulesvg {
  flex: 1 1 auto;
}

.cz-xb__baseline .cz-xb__head {
  margin-left: -1px;
}

.cz-xb__yhead {
  position: absolute;
  top: 0;
  left: -1px;
  display: block;
}

/* The mark layer is inset on three sides, and every inset is load-bearing.
   Right and top: a chip at the extreme of either axis still lands inside the
   figure, so overflow is zero by construction rather than by measurement (R5).
   Bottom: the first data point clears the baseline instead of straddling it —
   23-EXHIBIT §5 asks for "a gap of a quarter to a third of the chart between
   the axis start and the first data point" on position-encoded marks, and a
   chip sitting ON the zero rule reads as part of the furniture rather than as
   data. Measured: chip 1 overlapped the baseline and its arrow before this. */
.cz-xb__marks {
  position: absolute;
  top: 22px;
  /* 28px, because the widest chip is the focus one at 26px plus its padding and
     it is centred on its position: measured at 375, a 22px inset let the chip at
     rank maximum stand 3px past the figure's right edge, which is 3px of page
     scroll (R5). Half the widest mark plus a hair, so the arithmetic is the
     reason and not the number. */
  right: 28px;
  bottom: 18px;
  left: 0;
}

/* A POINT, NOT A BOX. The mark is the route's pair of ranks, so it has a
   position and no extent — an earlier build drew a cross spanning each authored
   range and it is gone with the value axes it belonged to (see the design note
   in cz_xb_plot()). */
.cz-xb__mark {
  position: absolute;
  bottom: var(--y, 0);
  left: var(--x, 0);
  width: 0;
  height: 0;
  pointer-events: none;
}

/* THE CHIP IS THE MARK AND ITS LABEL IN ONE, which is what lets the field carry
   eight routes with no legend and no collision. Real text, so it survives
   colour-blindness and a greyscale print — 23-EXHIBIT §5 files a colour key
   where direct labelling would work as an accessibility failure. */
.cz-xb__num {
  position: absolute;
  bottom: 0;
  left: 0;
  display: grid;
  place-items: center;
  min-width: 21px;
  height: 21px;
  padding: 0 4px;
  background: var(--xb-paper);
  box-shadow: inset 0 0 0 2px var(--xb-hue);
  border-radius: 999px;
  color: var(--xb-hue);
  font-size: var(--xb-t-strip);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transform: translate(-50%, 50%);
}

.cz-xb__mark.is-focus .cz-xb__num {
  min-width: 26px;
  height: 26px;
  background: var(--xb-hue);
  box-shadow: none;
  color: var(--xb-paper);
  font-weight: 800;
}

/* THE DATA, AS TEXT. Not a legend — every row carries its own name and both of
   its own figures, so this list is the exhibit's content and the field is its
   shape. It is also the whole exhibit for a screen reader. */
.cz-xb__legend {
  display: grid;
  gap: calc(var(--xb-row) * .9);
}

/* THREE COLUMNS AT THE NARROW END, NOT TWO, AND THAT IS A DENSITY FIX.
   Measured at 375 on the proof page: with the label, the first figure and the
   second figure each on their own line, a row ran 121px and the eight-route
   legend ran 1,496px — a phone reader scrolling nine screens of list for one
   exhibit. Label and first figure share a line here, the second figure takes
   the next one right-aligned under it, and a row comes down to about 70px. */
.cz-xb__row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: var(--xb-gap);
  row-gap: 2px;
  padding-top: calc(var(--xb-row) * .75);
  border-top: 1px solid var(--xb-hair);
}

.cz-xb__rowv {
  text-align: right;
}

.cz-xb__rowv2 {
  grid-column: 2 / -1;
  justify-self: end;
}

.cz-xb__rowd {
  grid-column: 2 / -1;
}

.cz-xb__legend > .cz-xb__row:first-child {
  padding-top: 0;
  border-top: 0;
}

.cz-xb__rown {
  color: var(--xb-hue);
  font-size: var(--xb-t-strip);
  font-weight: 800;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}

.cz-xb__row.is-focus .cz-xb__rowh {
  font-weight: 700;
}

.cz-xb__row.is-focus .cz-xb__rowv {
  color: var(--xb-hue);
}

/* An unplaced route keeps its text and loses its accent. It must not look like
   it has a position on the field. */
.cz-xb__row.is-unplaced .cz-xb__rowv,
.cz-xb__row.is-unplaced .cz-xb__rowv2 {
  color: var(--xb-quiet);
  font-weight: 600;
}

@container xb (min-width: 560px) {

  /* Room for both figures on the label's own line, so a row is one line. */
  .cz-xb__row {
    grid-template-columns: 24px minmax(0, 1fr) auto auto;
    column-gap: var(--xb-gap);
  }

  .cz-xb__rowv2 {
    grid-column: auto;
    justify-self: auto;
    min-width: 9ch;
    text-align: right;
  }
}


/* ============================================================
   2. PARTS — whole units out of a stated denominator
   ============================================================ */

/* PROPORTIONAL INK HOLDS EXACTLY: every square is the same size, so a group's
   shaded area IS its count. Lie factor 1.00 by construction. */
.cz-xb__units {
  display: grid;
  grid-template-columns: repeat(var(--cols, 8), 1fr);
  gap: 5px;
  /* A square never falls below 12px, and never grows into a slab: at 8 columns
     this caps the plot at 272px, so each square is ~29px at 1440 and ~28px at
     375. The count is what is being read, not the area of one unit. */
  max-width: calc(var(--cols, 8) * 34px);
  margin-bottom: calc(var(--xb-row) * 1.6);
}

.cz-xb__unitsq {
  aspect-ratio: 1;
  min-height: 12px;
  background: var(--xb-hue);
  border-radius: 1px;
}

/* THE REMAINDER IS THE ARGUMENT AS OFTEN AS THE PARTS ARE. "Five of the sixteen
   have always been federal law" is a claim about the other eleven, so they are
   drawn — open, hairlined, unmistakably not a group. */
.cz-xb__unitsq.is-open {
  background: none;
  box-shadow: inset 0 0 0 1.5px var(--xb-grid);
}

.cz-xb__unitsq.is-focus {
  box-shadow: inset 0 0 0 1.5px var(--xb-paper);
}

.cz-xb__legend--parts .cz-xb__row {
  grid-template-columns: 10px minmax(0, 1fr) auto;
}

.cz-xb__legend--parts .cz-xb__rowd {
  grid-column: 2 / -1;
}

/* THE DENOMINATOR LANDS LAST, and it is the one figure in this drawing that is
   about the whole rather than a part. Printed verbatim, never computed (R40). */
.cz-xb__of {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--xb-gap);
  margin: calc(var(--xb-row) * 1.5) 0 0;
  padding-top: calc(var(--xb-row) * .9);
  border-top: 2px solid var(--xb-ink);
}

.cz-xb__of-k {
  flex: 1 1 auto;
  color: var(--xb-quiet);
  font-size: var(--xb-t-strip);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.cz-xb__of-v {
  color: var(--xb-ink);
}


/* ============================================================
   3. CHRONO — dated instruments on a true time axis
   ============================================================ */

.cz-xb--chrono .cz-xb__time {
  position: relative;
  min-height: var(--h, 360px);
  /* Room for the pin and its stalk to the left of the label column. */
  padding-left: 0;
}

/* The rail spans the BAND, not the field: pins scale into the band and a rail
   that ran the full height would carry on past the last of them. */
.cz-xb__rail {
  position: absolute;
  top: 11px;
  left: 6px;
  display: block;
  width: 2px;
  height: var(--band, 290px);
}

.cz-xb__rail .cz-xb__ruleline {
  stroke: var(--xb-rule);
}

.cz-xb__events {
  position: relative;
  height: var(--h, 360px);
}

/* THE LABEL SITS AT ITS DECLUTTERED SLOT; THE PIN SITS AT THE TRUE DATE. Both
   are emitted in pixels so the stalk can span the difference — see the note in
   cz_xb_chrono(). */
.cz-xb__ev {
  position: absolute;
  top: var(--slot, 0);
  right: 0;
  left: 0;
  padding-left: 34px;
}

/* THE PIN SITS AT THE TRUE DATE and the label at its decluttered slot, both in
   pixels so this subtraction works — a percentage would resolve against the
   EVENT's height instead of the field's and the pin would drift. +4px centres
   the 14px mark on the 22px first line of its own label. */
/* B266 — CLUSTERED DATES MERGED INTO A BLOB. His screenshot of /difc/,
   20 September 2026: *"circles are not rendered properly in this infographic
   too."*
   The three late events sit at 436px, 457px and 468px on the true date axis —
   11px and 21px apart — and the mark was 14px across, so the discs overlapped
   into one dark shape with a ring stuck to it. That clustering is the POINT of
   this exhibit (the gaps are the argument), so the positions do not move. What
   changes is that each mark now punches itself out of its neighbours with a
   2px ring in the paper colour, and later events stack above earlier ones, so
   three touching marks read as three marks. */
.cz-xb__pin {
  position: absolute;
  z-index: calc(2 + var(--i, 0));
  top: calc(var(--pin, 0px) - var(--slot, 0px) + 4px);
  left: 0;
  width: 13px;
  height: 13px;
  border-radius: 999px;
  background: var(--xb-hue);
  box-shadow: 0 0 0 2px var(--xb-paper, #fff);
}

/* THREE PRECISIONS, THREE MARKS. A bare year is not a day and the drawing never
   says it is: hollow for a year, half-filled for a month, solid for a full
   date. R40 applied to a position rather than to a figure. */
.cz-xb__ev[data-prec="month"] .cz-xb__pin {
  background: var(--xb-paper);
  box-shadow: inset 0 0 0 4px var(--xb-hue), 0 0 0 2px var(--xb-paper, #fff);
}

.cz-xb__ev[data-prec="year"] .cz-xb__pin {
  background: var(--xb-paper);
  box-shadow: inset 0 0 0 2px var(--xb-hue), 0 0 0 2px var(--xb-paper, #fff);
}

/* The stalk joins the pin to its label. `max()` of the two differences is the
   absolute value of one of them, because `abs()` is not yet safe to ship. */
/* The stalk joins the pin to its label, from whichever of the two is higher to
   whichever is lower. `max()` of the two differences is the absolute value of
   one of them, because `abs()` is not yet safe to ship. Both offsets are +11px,
   the half-line the pin is centred on, so the stalk lands on the pin at both
   ends. */
.cz-xb__stalk {
  position: absolute;
  top: calc(11px + min(0px, var(--pin, 0px) - var(--slot, 0px)));
  left: 6px;
  width: 1px;
  height: max(calc(var(--pin, 0px) - var(--slot, 0px)), calc(var(--slot, 0px) - var(--pin, 0px)));
  background: var(--xb-hue);
  opacity: .55;
}

.cz-xb__evbody {
  display: block;
}

.cz-xb__evdate {
  display: block;
  margin-bottom: 1px;
}

.cz-xb__evh {
  display: block;
  max-width: var(--xb-note);
}

.cz-xb__ev.is-focus .cz-xb__evh {
  font-weight: 700;
}

.cz-xb__ev.is-focus .cz-xb__pin {
  /* The paper ring stays first so a focused mark still punches out of a
     neighbour it touches; the tint sits outside it. */
  box-shadow: 0 0 0 2px var(--xb-paper, #fff), 0 0 0 6px color-mix(in srgb, var(--xb-hue) 22%, transparent);
}

/* An event whose date could not be read keeps its text and gets no position. */
.cz-xb__legend--loose {
  margin-top: calc(var(--xb-row) * 1.4);
  padding-top: calc(var(--xb-row) * .9);
  border-top: 1px solid var(--xb-hair);
}


/* ============================================================
   4. WEIGH — an opposed pair, qualitative
   ============================================================ */

.cz-xb__sides {
  display: none;
  margin: 0 0 calc(var(--xb-row) * .8);
}

.cz-xb__side {
  font-size: var(--xb-t-strip);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* NEVER RED AND GREEN (23-EXHIBIT §4). Teal and plum, which differ in lightness
   as well as in hue, and each side is NAMED as well as coloured. */
.cz-xb__side--pro,
.cz-xb__wside--pro {
  --xb-hue: var(--xb-c2);
}

.cz-xb__side--con,
.cz-xb__wside--con {
  --xb-hue: var(--xb-c4);
}

.cz-xb__side {
  color: var(--xb-hue);
}

.cz-xb__weighs {
  display: grid;
}

.cz-xb__wrow {
  display: grid;
  gap: 4px var(--xb-gap);
  padding: calc(var(--xb-row) * .9) 0;
  border-top: 1px solid var(--xb-hair);
}

.cz-xb__weighs > .cz-xb__wrow:first-child {
  border-top: 0;
}

.cz-xb__wside {
  position: relative;
  padding-left: 14px;
  color: var(--xb-ink);
  font-size: var(--xb-t-note);
  line-height: 1.44;
  text-wrap: pretty;
}

/* A directional tick per side, pointing away from the centre rule, so the
   opposition is a shape and not only two colours. clip-path rather than a
   border triangle: one declaration, no inherited box, and it takes
   currentColor. */
.cz-xb__wside::before {
  position: absolute;
  top: .46em;
  left: 0;
  width: 7px;
  height: 8px;
  background: var(--xb-hue);
  content: "";
}

.cz-xb__wside--pro::before {
  clip-path: polygon(100% 0, 100% 100%, 0 50%);
}

.cz-xb__wside--con::before {
  clip-path: polygon(0 0, 0 100%, 100% 50%);
}

/* The side name, printed from data-side. This is the one-column layout, so it
   is the DEFAULT and the wide layout hides it — a small screen is designed for
   here, not degraded to. */
.cz-xb__wside::after {
  display: block;
  margin-top: 2px;
  color: var(--xb-hue);
  font-size: var(--xb-t-strip);
  font-weight: 800;
  letter-spacing: .09em;
  text-transform: uppercase;
  content: attr(data-side);
}

.cz-xb__wrow.is-focus .cz-xb__wh {
  font-weight: 700;
}

@container xb (min-width: 620px) {

  .cz-xb__sides {
    display: grid;
    grid-template-columns: var(--xb-key) minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--xb-gap);
  }

  .cz-xb__side--pro { grid-column: 2; }
  .cz-xb__side--con { grid-column: 3; }

  .cz-xb__wrow {
    grid-template-columns: var(--xb-key) minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }

  /* THE CENTRE RULE. A hairline at the head of the "against" column, so each
     row owns its own segment and the run reads as one spine down the ledger
     (R51 — structure from hairlines, never from a container). */
  .cz-xb__wside--con {
    padding-left: calc(var(--xb-gap) + 14px);
    margin-left: calc(var(--xb-gap) * -1);
    border-left: 1px solid var(--xb-hair);
  }

  .cz-xb__wside--con::before {
    left: var(--xb-gap);
  }

  /* The heads name the sides once, at the top, so the rows do not repeat it. */
  .cz-xb__wside::after {
    display: none;
  }
}


/* ============================================================
   5. ROUTING — conditions fanning into named destinations
   ============================================================ */

.cz-xb__fan {
  display: grid;
  gap: calc(var(--xb-row) * 1.5);
}

.cz-xb__dest {
  display: grid;
  gap: calc(var(--xb-row) * .6);
  padding: 0;
  border: 0;
}

/* The destination is a HEADING, and at one column it comes first — a reader on
   a phone is told where these conditions land before reading them, which is the
   better reading order anyway. */
.cz-xb__desth {
  order: -1;
  margin: 0;
  color: var(--xb-hue);
  font-size: var(--xb-t-body);
  font-weight: 700;
  letter-spacing: -.01em;
}

.cz-xb__conds {
  display: grid;
  gap: calc(var(--xb-row) * .7);
  padding-left: var(--xb-gap);
  border-left: 2px solid var(--xb-hue);
}

/* The bus is the wide-layout mark. At one column the group's left border is
   doing its job, so the SVG is not drawn at all — nothing hidden that carries
   information. */
.cz-xb__bus {
  display: none;
}

.cz-xb__cond.is-focus .cz-xb__condh {
  font-weight: 700;
}

@container xb (min-width: 700px) {

  .cz-xb__dest {
    /* conditions | bus | destination. The destination column is capped so a
       long name wraps rather than stealing the conditions' measure. */
    grid-template-columns: minmax(0, 1fr) 24px minmax(130px, .5fr);
    align-items: center;
    column-gap: 0;
  }

  .cz-xb__conds {
    grid-column: 1;
    padding-left: 0;
    border-left: 0;
  }

  .cz-xb__cond {
    position: relative;
    padding-right: var(--xb-gap);
    text-align: right;
  }

  /* Each condition reaches the bus with its own stub. This is the fan. */
  .cz-xb__cond::after {
    position: absolute;
    top: .72em;
    right: calc(var(--xb-gap) * -1);
    width: var(--xb-gap);
    height: 1px;
    background: var(--xb-hue);
    opacity: .6;
    content: "";
  }

  .cz-xb__condd {
    margin-left: auto;
  }

  /* THE BUS IS ABSOLUTE INSIDE A RELATIVE CELL, AND THAT IS A BUG FIX.
     Measured on the proof page: with the stretched `<svg>` in normal flow, its
     `height: 100%` resolved against an auto-height grid cell, fell back to the
     element's own intrinsic `height="1000"`, and drove the ROW to 1000px — two
     destinations made a 2,059px figure out of six short rows. Taking the mark
     out of flow means the row is sized by the conditions beside it, which is
     what decides how tall a bus should be in the first place. */
  .cz-xb__bus {
    position: relative;
    display: block;
    grid-column: 2;
    align-self: stretch;
    justify-self: center;
    width: 2px;
    min-height: 100%;
  }

  .cz-xb__bus .cz-xb__rulesvg--bus {
    position: absolute;
    inset: 0;
    height: 100%;
  }

  /* THE RAIL WEIGHT IS THE ENCODING, AND IT IS ORDINAL. No count is printed
     (R40): a stroke width is not a quantity anybody can measure off a page, so
     it steps rather than scales, and the length of the list beside it carries
     the rest. */
  .cz-xb__dest--w1 .cz-xb__ruleline { stroke-width: 1.5; }
  .cz-xb__dest--w2 .cz-xb__ruleline { stroke-width: 2.5; }
  .cz-xb__dest--w3 .cz-xb__ruleline { stroke-width: 4; }

  .cz-xb__bus .cz-xb__ruleline {
    stroke: var(--xb-hue);
  }

  .cz-xb__desth {
    position: relative;
    order: 0;
    grid-column: 3;
    padding-left: var(--xb-gap);
  }

  .cz-xb__desth::before {
    position: absolute;
    top: .72em;
    left: 0;
    width: var(--xb-gap);
    height: 1px;
    background: var(--xb-hue);
    opacity: .6;
    content: "";
  }
}

/* PAST FOUR DESTINATIONS THE FOUR ACCENTS STOP IDENTIFYING ANYTHING, so the
   drawing stops using them as an encoding and switches to the documented focus
   palette: the destination holding the most conditions keeps its accent and
   every other one goes quiet. A hue that appears twice explains nothing
   (23-EXHIBIT §4). */
.cz-xb--flat .cz-xb__dest {
  --xb-hue: var(--xb-quiet);
}

.cz-xb--flat .cz-xb__dest.is-lead {
  --xb-hue: var(--xb-c1);
}


/* ============================================================
   MOTION — section 6 of the header is the contract
   ============================================================
   THE PRE-STATE EXISTS ONLY UNDER `[data-reveal]:not(.visible)`, so the
   finished exhibit is what a page renders with no JavaScript, with an observer
   that never fires, and under reduced motion. Nothing below is required to
   comprehend a drawing. */

/* --- the transitions, declared on the finished state --- */

.cz-xb__ruleline {
  transition: stroke-dashoffset .62s var(--xb-ease-out);
}

.cz-xb__gridline {
  transition:
    opacity .4s var(--xb-ease) calc(min(var(--i, 0), 6) * 40ms),
    transform .4s var(--xb-ease) calc(min(var(--i, 0), 6) * 40ms);
}

.cz-xb__head {
  transition: opacity .3s var(--xb-ease) .5s;
}

/* A. GROUND. B. DATA — marks in authored order, behind the bands. */
.cz-xb__num {
  transition:
    opacity .34s var(--xb-ease) calc(min(var(--i, 0), 10) * var(--xb-step) + 120ms),
    transform .42s var(--xb-ease-out) calc(min(var(--i, 0), 10) * var(--xb-step) + 120ms);
}

.cz-xb__unitsq {
  transition:
    opacity .3s var(--xb-ease) calc(min(var(--i, 0), 12) * 34ms),
    transform .38s var(--xb-ease-out) calc(min(var(--i, 0), 12) * 34ms);
}

.cz-xb__pin {
  transition:
    opacity .34s var(--xb-ease) calc(min(var(--i, 0), 8) * 60ms + 160ms),
    transform .44s var(--xb-ease-out) calc(min(var(--i, 0), 8) * 60ms + 160ms);
}

.cz-xb__stalk {
  transition: transform .4s var(--xb-ease-out) calc(min(var(--i, 0), 8) * 60ms + 220ms);
}

.cz-xb__ev .cz-xb__evbody,
.cz-xb__row,
.cz-xb__wrow,
.cz-xb__cond {
  transition:
    opacity .42s var(--xb-ease) calc(min(var(--i, var(--j, 0)), 8) * 58ms),
    transform .42s var(--xb-ease-out) calc(min(var(--i, var(--j, 0)), 8) * 58ms);
}

/* C. THE CLAIM lands last. */
.cz-xb__of {
  transition:
    opacity .4s var(--xb-ease) .56s,
    transform .4s var(--xb-ease-out) .56s;
}

/* --- the pre-states --- */

.cz-xb[data-reveal]:not(.visible) .cz-xb__ruleline {
  stroke-dashoffset: var(--l, 100);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__gridline {
  opacity: 0;
  transform: translateY(4px);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__head {
  opacity: 0;
}

/* The chip scales up from the point it marks, which is the honest direction:
   the position is the datum, so the mark grows out of the position. */
.cz-xb[data-reveal]:not(.visible) .cz-xb__num {
  opacity: 0;
  transform: translate(-50%, 50%) scale(.8);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__unitsq {
  opacity: 0;
  transform: scale(.72);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__pin {
  opacity: 0;
  transform: scale(.55);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__stalk {
  transform: scaleY(0);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__ev .cz-xb__evbody,
.cz-xb[data-reveal]:not(.visible) .cz-xb__row,
.cz-xb[data-reveal]:not(.visible) .cz-xb__wrow {
  opacity: 0;
  transform: translateY(6px);
}

/* The conditions arrive from the LEFT of where they settle, because the reading
   is "these arrive here". */
.cz-xb[data-reveal]:not(.visible) .cz-xb__cond {
  opacity: 0;
  transform: translateX(-8px);
}

.cz-xb[data-reveal]:not(.visible) .cz-xb__of {
  opacity: 0;
  transform: translateY(5px);
}

/* Transform origins, so nothing grows from the wrong edge. */
.cz-xb__num { transform-origin: center center; }
.cz-xb__stalk { transform-origin: top center; }
.cz-xb__unitsq { transform-origin: center center; }


/* ============================================================
   375 — THE SMALL SCREEN IS DESIGNED, NOT SCROLLED PAST
   ============================================================
   R7: nothing under 12px, no horizontal overflow, and no drawing that has to
   scroll inside itself. Every kind here reflows because every label is real
   HTML text; what changes below is only what the shape needs. */

@container xb (max-width: 559px) {

  /* The bands tighten rather than the chips shrinking. 46px still clears a
     26px focus chip with room on both sides, and the chip keeps its 12px
     numeral at every width (R7) — nothing in this field is drawn inside a
     scaled viewBox, so nothing shrinks with the container. */
  .cz-xb--plot .cz-xb__field {
    height: calc(var(--rows, 5) * 46px + 36px);
    min-height: 190px;
  }

  .cz-xb__marks {
    top: 18px;
  }
}

@media (max-width: 700px) {

  .cz-xb {
    /* The margin column is gone below 1000px, so `step` and `bleed` both
       resolve to the prose width through --cz-fig-full. Stated anyway, because
       a width that depends on another file's breakpoint is a width that goes
       stale. */
    --xb-key: 100%;
  }

  .cz-xb__units {
    max-width: calc(var(--cols, 8) * 30px);
  }

  /* ------------------------------------------------------------
     CHRONO BELOW 700px — THE EVENTS GO INTO NORMAL FLOW
     ------------------------------------------------------------
     MEASURED AT 375 ON THE PROOF PAGE: three pairs of labels overlapped. The
     absolute positions were right; the labels are simply taller on a phone — a
     five-word event name runs to four lines in a 290px column — and no fixed
     reserve computed in PHP can be correct at both widths, because PHP cannot
     know how a sentence wraps.

     So below 700px each event takes its own height, which makes an overlap
     impossible, and the INTERVAL is spent on the margin above it (`--gap`, the
     normalised time since the previous event). 8px keeps consecutive events
     apart and up to 54px opens a real hole where the chronology is quiet — so
     the cluster is still the first thing a reader sees, and the order is still
     exact. What a phone loses is the pin's precise position on a continuous
     axis, which is a wide-screen reading; every date is printed verbatim at
     every width either way (R40).
     ------------------------------------------------------------ */
  .cz-xb--chrono .cz-xb__time {
    min-height: 0;
  }

  .cz-xb__events {
    height: auto;
    min-height: 0;
    padding-left: 0;
    /* The rail becomes the left edge of the run, which is what it was drawing. */
    border-left: 2px solid var(--xb-rule);
  }

  .cz-xb__rail,
  .cz-xb__stalk {
    display: none;
  }

  .cz-xb__ev {
    position: relative;
    top: auto;
    margin-top: calc(8px + var(--gap, 0) * 54px);
    padding-left: 22px;
  }

  .cz-xb__events > .cz-xb__ev:first-child {
    margin-top: 0;
  }

  /* The pin sits on the rail beside its own date line. */
  .cz-xb__pin {
    top: 4px;
    left: -8px;
    width: 13px;
    height: 13px;
  }
}


/* ============================================================
   REDUCED MOTION, AND PRINT
   ============================================================
   Every pre-state neutralised and every transition removed, leaving the
   finished, fully visible exhibit. main.js also adds .visible to everything
   immediately when the reader prefers reduced motion, so this is the second of
   two independent guarantees rather than the only one — and print gets it too,
   because a printed page never runs an observer. */

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

  .cz-xb *,
  .cz-xb *::before,
  .cz-xb *::after {
    transition: none;
    animation: none;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__ruleline {
    stroke-dashoffset: 0;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__gridline,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__head,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__num,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__unitsq,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__pin,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__stalk,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__ev .cz-xb__evbody,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__row,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__wrow,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__cond,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__of {
    opacity: 1;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__gridline,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__unitsq,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__pin,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__ev .cz-xb__evbody,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__row,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__wrow,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__cond,
  .cz-xb[data-reveal]:not(.visible) .cz-xb__of {
    transform: none;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__num {
    transform: translate(-50%, 50%);
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__stalk {
    transform: none;
  }
}

@media print {

  .cz-xb *,
  .cz-xb *::before,
  .cz-xb *::after {
    transition: none;
    animation: none;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__ruleline {
    stroke-dashoffset: 0;
  }

  .cz-xb[data-reveal]:not(.visible) * {
    opacity: 1;
  }
}


/* ============================================================
   NO SCRIPTING — the third guarantee
   ============================================================
   The reveal contract has two guarantees already: the pre-state only exists
   under `[data-reveal]:not(.visible)`, and main.js adds `.visible` to
   everything immediately when the reader prefers reduced motion or the browser
   has no IntersectionObserver. Both of those still need main.js to RUN.

   This is the case neither covers: scripting off, or main.js blocked, or it
   threw before it reached the observer. Without this rule the pre-state would
   be the final state and a bar at scaleX(0) is an invisible exhibit — the
   finished drawing is the default everywhere else in this file and it has to be
   the default here too.

   `scripting: none` is the media feature written for exactly this and it
   cannot false-positive: where it is unsupported the query never matches and
   the other two guarantees carry it, and where it is supported it matches only
   when script genuinely will not run. Cheap insurance against the one failure
   mode that turns an animation into a missing exhibit.
   ============================================================ */

@media (scripting: none) {

  .cz-xb[data-reveal]:not(.visible) * {
    opacity: 1 !important;
    transform: none !important;
  }

  .cz-xb[data-reveal]:not(.visible) .cz-xb__ruleline {
    stroke-dashoffset: 0 !important;
  }
}
