/* ============================================================================
   SAVOY DRUGSTORE — figures.css
   THE SVG ART LANGUAGE (DESIGN_SPEC §7)

   BUILD ORDER
     tokens.css → base.css → components.css → figures.css → sections/*.css

   This file owns three things and nothing else:

     1  §7.1  the stroke system — five classes, four weights, two grounds.
     2  §7.2  the placement law — art bands, the 64px feathered mask, the bleed
              law, the void ceiling.
     3  §7.3  the five authored figures, plus §7.4's per-section variations.

   It never restyles a component (§8) and never declares a colour. Every value
   below resolves to a custom property from tokens.css.

   LAW OBSERVED
     · border-radius is 0 site-wide (base.css, zero specificity). Circles exist
       only as SVG <circle> in artwork. Nothing here reintroduces a radius.
     · The only box-shadows are `inset 0 0 0 1px` and `inset 0 1px 0` (§5).
       Exactly one appears here: the 1px champagne outline on a Gantt hatch
       extension, decreed by §8.11.
     · No blur filters. The 64px feather is built from gradient stops, not from
       a blur, because §5 permits blur only in the two backdrop-filter cases.
     · Every space is on the 4px lattice, except the two sizes the spec fixes by
       decree — the 6px Gantt bar (§8.11) and the 52px seal (§7.3) — which are
       named here rather than silently rounded.

   THIS FILE IS `@layer figures`, the last layer tokens.css declares. It may
   therefore overrule a component (a figure's caption geometry sits on top of
   §8.12's caption type) and is itself overruled by an unlayered section file.
   ========================================================================= */

@layer figures {


/* ============================================================================
   1.  §7.1  THE ART KIT — palette, weights, primitives
   ---------------------------------------------------------------------------
   The §2.7 semantic aliases are the right vocabulary for TEXT and RULES, but
   §7.1 specifies the artwork palette as its own table with its own dark-ground
   behaviour, and the two do not coincide: `.accent` goes --royal → --royal-lit
   on dark, while --ac-identity goes --royal → --beam. So the art layer carries
   its own five variables, set once here and inverted once below. A figure never
   reads a raw token; a section never reads these.

        class      weight     on paper                 on night
        .stem      1          rgba(21,23,28,.34)       rgba(242,241,236,.34)
        .hair      1          --hair-gold              rgba(217,192,138,.55)
        .accent    1.25       --royal                  --royal-lit
        .live      1.5        --royal                  --beam
        .node      1          paper fill / ink stroke  no fill / rgba(…,.75)
   ========================================================================= */

.fig,
.fig-art,
.fig-rail,
.fig-gantt {
  --art-stem:        var(--stroke-stem);
  --art-hair:        var(--hair-gold);
  --art-accent:      var(--royal);
  --art-live:        var(--royal);
  --art-leader:      var(--ln-leader);
  --art-node-fill:   var(--paper);
  --art-node-stroke: var(--ink);
  --art-live-fill:   var(--royal);
  --art-rule:        var(--rule-ink);
  --art-rule-soft:   var(--rule-soft);
  --art-sheet:       var(--paper-warm);
  --art-sheet-back:  var(--paper-deep);
  --art-ink:         var(--ink);
  --art-dim:         var(--ink-mute);
  --art-metal-text:  var(--champagne-ink);
  --art-metal-line:  var(--champagne);

  /* The gap colour of the §7.1 hatch must be fully transparent. §10.33 forbids
     writing rgba() outside tokens.css, so we borrow the one zero-alpha token
     whose premultiplied value is identical to `transparent`. The gradient uses
     hard stops, so no interpolation occurs and its hue is never rendered. */
  --art-void:        var(--night-3-fade);

  /* §7.1's Tick length lives in tokens.css: the kit's tick is drawn outside a
     figure too (the scroll indicator's foot), so it cannot be scoped to `.fig`. */
}

/* An OPEN node, a sheet face and the rail's terminal marker are opaque: they
   punch a hole in whatever is behind them, so their fill has to be the ground
   they are standing on. base.css re-points the §2.7 aliases only for the two
   dark grounds, so the two remaining steps of the paper ramp are declared here.
   Without this a node on --paper-deep reads as a slightly brighter dot — the
   kind of half-millimetre error that is invisible in review and obvious in
   print. */
[data-ground="paper-warm"] :is(.fig, .fig-art, .fig-rail, .fig-gantt) {
  --art-node-fill:  var(--paper-warm);
  --art-sheet:      var(--paper);
  --art-sheet-back: var(--paper-deep);
}
[data-ground="paper-deep"] :is(.fig, .fig-art, .fig-rail, .fig-gantt) {
  --art-node-fill:  var(--paper-deep);
  --art-sheet:      var(--paper-warm);
  --art-sheet-back: var(--paper-edge);
}
/* ORDER MATTERS: these two carry the same specificity as the dark inversion
   below, so they must come FIRST. A `.on-night` island inside a paper-deep
   section has to invert, not inherit the ramp. */

/* The inversion. Applied by ground (a section, the footer, an island) or by
   the explicit modifier, so a figure dropped on --night needs no attributes. */
[data-ground="night"] :is(.fig, .fig-art, .fig-rail, .fig-gantt),
[data-ground="royal"] :is(.fig, .fig-art, .fig-rail, .fig-gantt),
.ground--night :is(.fig, .fig-art, .fig-rail, .fig-gantt),
.ground--royal :is(.fig, .fig-art, .fig-rail, .fig-gantt),
.on-night :is(.fig, .fig-art, .fig-rail, .fig-gantt),
.fig-art--night,
.fig--night :is(.fig, .fig-art, .fig-rail, .fig-gantt) {
  --art-stem:        var(--stroke-stem-night);
  --art-hair:        var(--stroke-hair-night);
  --art-accent:      var(--royal-lit);
  --art-live:        var(--beam);
  --art-leader:      var(--rule-night-soft);
  --art-node-fill:   var(--night);
  --art-node-stroke: var(--stroke-node-night);
  --art-live-fill:   var(--beam);
  --art-rule:        var(--on-night);
  --art-rule-soft:   var(--rule-night-soft);
  --art-sheet:       var(--night-2);
  --art-sheet-back:  var(--night);
  --art-ink:         var(--on-night);
  --art-dim:         var(--on-night-52);
  --art-metal-text:  var(--champagne-lit);
  --art-metal-line:  var(--champagne-lit);
}

/* THE ROYAL FIELD IS NOT A DARK GROUND FOR ART (§2.5.5).
   The inversion above is calibrated against --night (L* 7). On --royal (L* 33)
   the same values collapse: the champagne hairline the licence seal is drawn
   in measures 2.2:1 there and its node fill (--night) 2.25:1 — the footer's
   single trust artefact rendered as the least visible mark on the page, in a
   grey that is not a token in the system at all. Every art colour on royal
   therefore steps to the --on-night ramp, which base.css has already done for
   text and rules on the same ground. Declared AFTER the inversion, with the
   same specificity, so it wins on order. */
[data-ground="royal"] :is(.fig, .fig-art, .fig-rail, .fig-gantt),
.ground--royal :is(.fig, .fig-art, .fig-rail, .fig-gantt) {
  --art-stem:        var(--on-night-70);
  --art-hair:        var(--on-night);         /* 7.03:1 — the seal's geometry */
  --art-metal-line:  var(--on-night);
  --art-metal-text:  var(--on-night);
  --art-node-stroke: var(--on-night-70);      /* 4.24:1 — the seal's boss      */
  --art-node-fill:   var(--night-3-fade);     /* transparent: the field shows  */
  --art-ink:         var(--on-night);
  --art-dim:         var(--on-night);
  --art-rule:        var(--on-night);
  --art-rule-soft:   var(--rule-night-strong);
}

/* ── the SVG box ─────────────────────────────────────────────────────────── */
.fig-art {
  display: block;
  width: 100%;
  height: auto;
  /* §7.2.6 — artwork sizes to the composition, never the reverse. A plate has
     an intrinsic ratio published by figures.js; nothing here sets a height. */
  aspect-ratio: var(--fig-ar, auto);
  overflow: visible;
}

/* ── stroke classes. Weights come from tokens; §7.1 admits no others. ────── */
.fig-art :is(.stem, .hair, .accent, .live, .node, .leader, .rule-ink, .rule-hair, .rule-soft, .barcode) {
  fill: none;
  stroke-linecap: butt;
  stroke-linejoin: miter;
  /* Weights hold at every scale — decreed for every stroked element (§7.1). */
  vector-effect: non-scaling-stroke;
}

.fig-art .stem   { stroke: var(--art-stem);   stroke-width: var(--sw-stem); }
.fig-art .hair   { stroke: var(--art-hair);   stroke-width: var(--sw-hair); }
.fig-art .accent { stroke: var(--art-accent); stroke-width: var(--sw-accent); }
.fig-art .live   { stroke: var(--art-live);   stroke-width: var(--sw-live); }

/* The one hue modifier in the kit. §7.3 sets Fig. II's MOHAP strand in
   --champagne at the same authority as the royal strand beside it, and §2.5.2
   names that strand as a permitted champagne form. It borrows the ACCENT
   weight rather than inventing a sixth class, so the weight table is intact. */
.fig-art .accent.is-metal { stroke: var(--art-metal-line); }

/* Leader — 1px dashed 2 6, orthogonal, label to subject. The dashes are
   authored as geometry in figures.js (motion.js overwrites stroke-dasharray on
   every stroked child of a .draw group), so nothing here declares a pattern. */
.fig-art .leader {
  stroke: var(--art-leader);
  stroke-width: var(--sw-hair);
  stroke-linecap: round;
}

/* Node — open by default; `live` is the solid identity fill and carries no
   stroke, so the two states differ in kind, not in shade. */
.fig-art .node {
  fill: var(--art-node-fill);
  stroke: var(--art-node-stroke);
  stroke-width: var(--sw-stem);
}
.fig-art .node--live {
  fill: var(--art-live-fill);
  stroke: none;
}

/* Rules drawn inside a figure (the dossier's ledger lines). */
.fig-art .rule-ink  { stroke: var(--art-rule);      stroke-width: var(--sw-stem); }
.fig-art .rule-hair { stroke: var(--art-hair);      stroke-width: var(--sw-hair); }
.fig-art .rule-soft { stroke: var(--art-rule-soft); stroke-width: var(--sw-stem); }
.fig-art .barcode   { stroke: var(--art-stem);      stroke-width: var(--sw-stem); }

/* ── NO TYPE IS SET INSIDE A VIEWBOX ─────────────────────────────────────────
   §3.2 fixes every micro size in px "precisely because tracked capitals fall
   apart when they scale", and a length inside a viewBox is a USER UNIT: it
   scales by definition. Fig. II's construction note already stated the rule —
   "micro type is fixed-px by §3.2 and would scale with the viewBox, so the
   legend and the REF are HTML" — and Fig. III was the one plate breaking it,
   running a private ramp of 14 / 14.3 / 34 / 34 user units that landed at 14px
   and 34px at full size (14 is inside the 12–16px band §3.1 declares empty) and
   at 9.8px and 23.8px at the 0.7 placement /about calls for — sizes on no tier
   of the scale.
   Fig. III's words are now an HTML overlay on the plate (see §6 below), so the
   whole site has ONE type system and `.fig-art text` has no work left to do.
   The declaration stays as a floor: if a figure ever needs a glyph inside its
   box again, it inherits the micro family and tracking rather than inventing
   them, and it is caught in review because it will be the only one. */
.fig-art text {
  fill: var(--art-dim);
  font-family: var(--f-micro);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--tr-m3);
  font-variant-numeric: tabular-nums lining-nums;
}


/* ============================================================================
   2.  §6.2 (order 5) · §6.5  DRAW-ON
   ---------------------------------------------------------------------------
   motion.js owns the numbers: it measures each stroked child with
   getTotalLength(), publishes --len, parks the offset and releases it 300ms
   into the section's reveal. components.css owns the transition. This block
   adds only what those two cannot know:

     · the no-JS guard. components.css hides an undrawn figure unconditionally;
       if the module never runs, the art must still be there.
     · the node and text reveals. A node is a mark, not a line — it cannot draw,
       so it lands (--e-stamp, the easing reserved for seals). Type fades with
       the body, never with a headline (§6.2 forbids fading a headline; a
       figure's caption type is body).
   ========================================================================= */

/* No script, no hidden art. Beats components.css on specificity, as intended. */
html:not([data-js]) .fig-art :is(path, circle, polygon, line, rect, ellipse, polyline) {
  stroke-dasharray: none;
  stroke-dashoffset: 0;
}

/* Nodes and figure type: land and fade, staggered behind the strokes. */
.fig-art .node,
.fig-art text,
.fig-art .seal {
  opacity: 0;
  transform: scale(.86);
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transition:
    opacity   var(--t-l) var(--e-out) var(--d-art),
    transform var(--t-l) var(--e-stamp) var(--d-art);
}
.fig-art text,
.fig-art .seal { transform: none; }

.is-in .fig-art .node,
.is-in .fig-art text,
.is-in .fig-art .seal,
html:not([data-js]) .fig-art :is(.node, text, .seal) {
  opacity: 1;
  transform: none;
}

/* The live node arrives last — it is the sentence's full stop. */
.fig-art .node--live { transition-delay: calc(var(--d-art) + var(--t-l)); }

@media (prefers-reduced-motion: reduce) {
  .fig-art :is(.node, text, .seal) {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}


/* ============================================================================
   3.  §7.2  THE PLACEMENT LAW
   ========================================================================= */

/* ── 7.2.1 · the art band ────────────────────────────────────────────────────
   Every section declares its band as a comment and positions ONE `.fig-band`.
   The band is out of flow, so §7.2.6 holds by construction: artwork can never
   be the reason a section is tall. It is inert to the pointer and to the
   accessibility tree; the figure inside carries its own aria. */
.fig-band {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  /* `clip` rather than `hidden`: no scroll container, no accidental scrollbar. */
  overflow: clip;
}
.fig-band > * { pointer-events: none; }

/* Content must sit above its own band without every section re-declaring it. */
.fig-band ~ * { position: relative; z-index: 1; }

/* ── 7.2.2 · never over live text ────────────────────────────────────────────
   "If art must pass within 24px of a text block, it needs BOTH opacity ≤ .10
    AND a 64px-feathered mask punching out the text block's bounding box."

   One class applies both halves, so the pair cannot be separated by accident —
   which is exactly how this defect got into every direction.

   The punch is the intersection of a horizontally feathered band and a
   vertically feathered band, subtracted from an opaque base. Feathering is
   gradient stops, not a blur filter (§5 permits blur only on the nav frost).

   USAGE
     <div class="fig-band fig--under-text"
          style="--punch-l:0px; --punch-t:120px; --punch-r:38%; --punch-b:96px">
   The four values are insets from the band's own edges to the text block's
   bounding box. Defaults punch nothing, so the class is safe to add first and
   measure second. */
.fig--under-text {
  --feather: 64px;
  --punch-l: 50%;
  --punch-t: 50%;
  --punch-r: 50%;
  --punch-b: 50%;

  opacity: .10;                     /* the ≤.10 half of the rule */

  --punch-h: linear-gradient(
    to right,
    var(--art-void) 0 var(--punch-l),
    var(--art-ink)  calc(var(--punch-l) + var(--feather))
                    calc(100% - var(--punch-r) - var(--feather)),
    var(--art-void) calc(100% - var(--punch-r)) 100%);
  --punch-v: linear-gradient(
    to bottom,
    var(--art-void) 0 var(--punch-t),
    var(--art-ink)  calc(var(--punch-t) + var(--feather))
                    calc(100% - var(--punch-b) - var(--feather)),
    var(--art-void) calc(100% - var(--punch-b)) 100%);

  -webkit-mask-image: linear-gradient(var(--art-ink) 0 100%), var(--punch-h), var(--punch-v);
          mask-image: linear-gradient(var(--art-ink) 0 100%), var(--punch-h), var(--punch-v);
  -webkit-mask-composite: subtract, intersect, add;
          mask-composite: subtract, intersect, add;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

/* If mask-composite is unavailable the punch cannot be built, so the art
   recedes further rather than risking a stroke crossing a paragraph. */
@supports not (mask-composite: subtract) {
  .fig--under-text {
    -webkit-mask-image: none;
            mask-image: none;
    opacity: .05;
  }
}

/* ── 7.2.3 · the bleed law ───────────────────────────────────────────────────
   "Art may bleed off an edge ONLY along a straight continuation — a rule, a
    stem, a hatch field", and the outermost geometry must terminate ≥40px
   inside the viewport. --bleed / --bleed-inset are the §4.2 tokens.

   `.fig-band--bleed-left` pushes the band's left edge to exactly --bleed-inset
   from the viewport's left edge, whatever shell it is nested in. The figures
   authored for these bands (Fig. I "ring") are drawn so that only straight
   tangents reach the edge. */
.fig-band--bleed-left {
  left: calc(50% - 50vw + var(--bleed-inset));
}
.fig-band--bleed-right {
  right: calc(50% - 50vw + var(--bleed-inset));
}
.fig-band--bleed-both {
  left: calc(50% - 50vw + var(--bleed-inset));
  right: calc(50% - 50vw + var(--bleed-inset));
}
/* The wide art layer, capped at --bleed and never wider than the viewport. */
.fig-band--wide { width: var(--bleed); max-width: calc(100vw - (var(--bleed-inset) * 2)); }

/* ── 7.2.4 / §4.6 · resolve or remove ────────────────────────────────────────
   "Artwork that runs out partway down a section is a rejection." A figure that
   is a column's answer to the void rule spans that column's full height and
   keeps its geometry centred inside it rather than stretching. */
.fig--span,
.fig--span > .fig-art {
  height: 100%;
  min-height: var(--s12);           /* 200px — under the 240px void ceiling */
}
.fig--span > .fig-art {
  width: 100%;
  aspect-ratio: auto;
  object-fit: contain;
}

/* THE ART CELL — a grid column that hosts a figure sized BY the composition.
   §7.2.6: "Artwork sizes to the composition; the composition never sizes to the
   art." A figure placed as an ordinary grid child does the opposite — its
   intrinsic ratio sets the row height, and a 426 × 813 plate in a cover's art
   band made the row 813px tall and left 300px of dead paper under the display
   block beside it. The cell takes its height from the row (i.e. from the text
   it is composed against) and the figure fills that box out of flow, so it can
   never add a pixel of height. The drawing keeps its own proportions:
   preserveAspectRatio letterboxes it inside the band. */
/* THE COVER'S ART BAND, AND ITS CAPTION (§7.2.5, §8.12).
   The plate letterboxes to a band whose height comes from the display block
   beside it — and it now stands on the LEFT of that band rather than centred in
   it, for two reasons that are the same reason: §8.12 puts the caption at the
   drawing's optical bottom-LEFT, and a centred plate leaves the caption
   stranded under bare paper; and centring opened a 261 × 288px channel between
   the headline's rag and the ring, which read as two objects on one row rather
   than one composition (§4.6). Hung left, the plate closes that channel and its
   caption lands on its own left edge, by construction. */
.art-cell { position: relative; min-height: var(--s12); }
.art-cell > .fig {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.art-cell > .fig > .fig-art {
  flex: 1 1 auto;
  min-height: 0;
  width: auto;
  height: auto;
  max-width: 100%;
  aspect-ratio: auto;
}
.art-cell > .fig > .fig-cap { flex: none; }
@media (max-width: 767px) {
  /* STACKED, THE CELL HAS NO ROW TO TAKE ITS HEIGHT FROM — SO THE PLATE LEADS.
     Forcing the band to 78vw and letterboxing the drawing inside it rendered
     the hero armature 159px wide inside a 310px box at 390: a 0.52 portrait
     plate squeezed into a landscape band, centred, with 95px of paper on its
     left and 136px on its right — the composition sizing the art rather than
     the other way round, and the page's single left edge broken for the one
     element that most needs to hold it. Below 768 the figure comes back into
     flow and keeps its own ratio; the §4.2 inset above caps it at
     `100vw − 80px` and hangs it on the measure origin. */
  .art-cell { position: static; min-height: 0; }
  .art-cell > .fig { position: static; }
  .art-cell > .fig > .fig-art {
    height: auto;
    aspect-ratio: var(--fig-ar, auto);
  }
}

/* ── §4.2 / §7.2.6 · THE PLATE IS COMPOSED, NOT STRETCHED, AT PHONE WIDTHS ───
   "Artwork's outermost geometry must terminate ≥40px inside the viewport edge"
   (§4.2), and "artwork sizes to the composition; the composition never sizes to
   the art" (§7.2.6). At 390 every plate was rendering at the full 350px of the
   content column — the hexagon's left node landed on x=20.5 and the outer ring's
   right stroke on x=369.5, both flush against the gutter with zero optical
   inset, so the figure read as a `width:100%` SVG rather than as a drawn plate.
   Capping the drawing at `100vw − 80px` puts 40px of paper outside its
   outermost geometry on both sides; hanging it on the measure origin rather
   than centring it keeps the page's single left edge (§4.5.8), so the plate,
   the paragraph above it and its FIG. caption below all start at the same x.
   The seal is exempt: it is a 52px mark, already far inside the gutter, and
   `--seal-size` is decreed. */
@media (max-width: 767px) {
  .fig-art,
  .fig-iii-plate {
    max-width: calc(100vw - (var(--bleed-inset) * 2));
    /* CENTRED ON THE MEASURE, NOT HUNG ON IT. Left-hanging the plate put its
       box on the shell's own 20px gutter, so the hero armature's outermost
       geometry — the champagne ring's left tangent — terminated 19.5px inside
       the viewport where §4.2 requires ≥40px, while the right side cleared 60px.
       A perfectly symmetric closed curve sitting 20px off the optical centre of
       a single-column measure reads as a figure that overflowed its box rather
       than one that was placed, and it is the one geometry on the page that
       cannot be allowed to look accidental. Centred, both tangents land at
       exactly --bleed-inset and the plate is symmetric about the column it is
       drawn in. §4.5.8's fixed left origin governs LISTS; a plate is a plate. */
    margin-inline: auto;
  }
  .fig--v > .fig-art,
  .fig-iii__seal > .fig-art { max-width: none; }
}

/* An ambient underlay recedes; a plate does not. Both are declared, never
   improvised, so a reviewer can grep every opacity in the art layer. */
.fig--ghost  { opacity: .40; }       /* §7.4 Services: "the ring alone, 0.4"  */
.fig--quiet  { opacity: .62; }

/* ── 7.2.5 · captions ────────────────────────────────────────────────────────
   components.css §8.12 owns `.fig-cap` / `<figcaption>`. Only the figure-side
   geometry is here, and §8.12 gives it ONE number: 12px below the drawing's
   optical bottom-left edge.

   ONE VALUE, FIVE FIGURES. It used to be five values — 14 / 31 / 33.5 / 43.5 /
   145px measured on one sheet — and the horizontal offset was five values too
   (0 / 22 / 24.5 / 29 / 33.5px), because the caption aligns to its column while
   each drawing floated somewhere inside a viewBox with its own slack. Both are
   now structural rather than per-figure:
     · every figure's viewBox is cut to its own geometry in figures.js, so the
       drawing's optical left edge IS the figure box's left edge and every
       caption lands at 0px;
     · this one rule sets the vertical gap, with a single exception below for
       Fig. II, whose optical bottom is the bottom of its legend block. */
/* ONE VALUE, EVERY FIGURE, NO EXCEPTIONS. Fig. II used to take 24px on the
   argument that its caption follows a LEGEND rather than a drawing and needs
   more air to read as a caption. It is still the only value §8.12 states, the
   sheet still prints "one value for all five, not five" two paragraphs above
   the figures, and a sheet that argues its own exception is a sheet with two
   caption systems. The legend's own bottom rule and the caption's 24px
   champagne rule are already two different marks at two different weights. */
.fig > .fig-cap,
.fig--ii > .fig-cap { margin-top: var(--s3); }

/* The right-hung reference (§7.3 Fig. II: `REF 61109`). Same micro tier as the
   caption, mirrored, so the plate is closed on both edges. */
.fig-ref {
  margin: var(--s3) 0 0;
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  text-align: right;
  /* MICRO COLOUR, NOT METAL. §8.12 reserves champagne-ink for the FIG. caption;
     this is the plate's right-hung REFERENCE, which §7.3 names but does not
     colour. It shares the caption's tier and its mirrored geometry, not its
     role — and on a section that already carries a set of captions, a strand
     and a seal it would be the fourth champagne group (§2.5.1). */
  color: var(--tx-micro);
  font-variant-numeric: tabular-nums lining-nums;
  /* Cancel the tracked capital's trailing sidebearing so the last glyph lands
     ON the plate's right edge rather than 1.5px short of it (§8.6's axis). */
  margin-right: calc(var(--tr-m3) * -1);
}

/* A caption and a reference on the same baseline, closing a plate. */
.fig--closed {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: var(--s5);
}
.fig--closed > .fig-art  { grid-column: 1 / -1; }
.fig--closed > .fig-cap  { grid-column: 1; }
.fig--closed > .fig-ref  { grid-column: 2; margin-top: var(--s4); }

/* THE CAPTION GETS THE WHOLE MEASURE BEFORE THE REFERENCE GETS A COLUMN.
   Right-hanging `REF 61109` on the caption's baseline costs the caption 81px,
   which at 390 leaves it 269px of a 350px column — and §8.12's caption is a
   fixed 39-character string at a fixed 10px / .28em, so it needs 262. It broke
   as "FIG. 02 — AUTHORITY PATHWAYS —" / "SPECIMEN": an em-dash left as the last
   glyph on a line, which §10.21 makes an instant fail, with SPECIMEN orphaned
   under it. The tracking is decreed and the string is decreed, so the COLUMN is
   what gives: below 560 the plate closes on two stacked rows — caption left,
   reference right beneath it — and both keep the full measure. */
@media (max-width: 559px) {
  .fig--closed { grid-template-columns: minmax(0, 1fr); }
  .fig--closed > .fig-cap { grid-column: 1; }
  .fig--closed > .fig-ref {
    grid-column: 1;
    text-align: right;
    margin-top: var(--s2);
  }
}

/* Screen-reader-only text for the one figure whose meaning is its data
   (Fig. IV). Scoped to the art layer so it cannot collide with a utility a
   later layer introduces. */
.fig-gantt .u-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
  border: 0;
}


/* ============================================================================
   4.  FIG. I — THE ARMATURE  (§7.3, §7.4)
   ---------------------------------------------------------------------------
   Home hero · /assessment hero (inverted) · Services underlay (ring) ·
   Philosophy specimen (split) · Process rail and stage-06 marker.
   ========================================================================= */

.fig--i-armature > .fig-art { --fig-ar: .5240; }    /* 426 : 813, cut to geometry */
.fig--i-ring     > .fig-art { --fig-ar: .375; }     /* 432 : 1152 */
.fig--i-split    > .fig-art { --fig-ar: 1.8652; }   /* 664 : 356, cut to geometry */
.fig--i-hex      > .fig-art { --fig-ar: 1; }

/* ── ONE USER UNIT IS ONE CSS PIXEL. THE WHOLE INVENTORY IS DRAWN BY ONE HAND ──
   §7.3: a figure is authored once and varied per section "only by scale, which
   classes are .live, and which subset of nodes is drawn". Scale is legal — but
   scale is also the thing that made five plates measure as five different
   drawings. A node is decreed at `r="5.5"` in USER units, so a plate mounted at
   1.53× renders an 8.4px node beside another plate's 5.5px one, and a reviewer
   measuring the sheet finds the kit's one fixed radius rendered at four
   different sizes. Stroke weights hold (non-scaling-stroke); nothing else does.
   Every discrete plate is therefore capped at its own viewBox width, so its
   user unit IS a CSS pixel wherever it is placed: node radii, ring radii and
   hex circumradii are then directly comparable across figures and every
   published ratio (ring 1.85R, inner 0.52R) measures the same on every plate.
   A section still varies a figure by scale — by giving it a narrower column —
   but it can never render the kit at a scale the kit was not drawn at.
   `.art-cell` (the hero band) is the one deliberate exception: there the plate
   is letterboxed to fill a band whose height comes from the text beside it. */
.fig--i-armature {
  width: 100%;
  max-width: 426px;
}
/* §7.2.6 / §4.2 / §2.5.4 — THE PLATE IS SIZED BY THE PHONE'S COMPOSITION.
   At 390 the armature drew 310px wide and therefore 592px TALL — 70% of a
   viewport of botanical stem between the hero's buttons and its metadata rail,
   which is the composition sizing itself to the art. Its ring also passed
   within ~30px of both viewport edges (§4.2 wants ≥40) and its right arc ran
   under the fixed WhatsApp square at x=322 — a saturated royal fill sitting
   directly on a champagne hairline, which §2.5.4 forbids at any distance under
   16px. Capping the plate at 220px below 560 answers all three at once: the
   drawing is 420px tall, both tangents stand 85px inside the viewport, and the
   ring's right tangent clears the affordance by 17px. */
/* Capping the FIGURE BLOCK (not the drawing inside it) is what moves the
   plate's own box, so the cap carries the block's centring with it — otherwise
   a 220px plate hangs on the 20px gutter and its ring lands 20px inside the
   viewport, under §4.2's 40px floor. */
/* §10.21 — THE HERO CAPTION'S DECLARED BREAK.
   Between 768 and 1079 the cover's art band is three of eight columns — 252px —
   and `FIG. 01 — REGISTRATION NODE — SPECIMEN` is 41 tracked characters. It
   broke on the second em dash and printed a separator as the last glyph of line
   one. Here the break is declared instead: the `SPECIMEN` qualifier takes its
   own line and gives up its separator, so neither line opens or closes on a
   dash. Below 768 the band is the full column again and the caption is one
   line, so the rule is scoped to the width that needs it. */
@media (min-width: 768px) and (max-width: 1079px) {
  .art-cell .fig-cap { flex-wrap: wrap; }
  .art-cell .fig-cap > .fig-cap__tail { flex-basis: 100%; }
  .art-cell .fig-cap > .fig-cap__tail::before { content: none; }
}

/* THE CAP IS ON THE DRAWING, NOT ON THE FIGURE BLOCK. Capping the block took
   the §8.12 caption with it: at 390 `FIG. 01 — REGISTRATION NODE — SPECIMEN`
   was set inside a 220px box and broke over FOUR lines. The drawing is capped
   and centred; the caption keeps the column's own measure and its own left
   origin, which is also where §8.12 puts it. */
@media (max-width: 767px) {
  .fig--i-armature > .fig-art { max-width: 320px; margin-inline: auto; }
}
@media (max-width: 559px) {
  .fig--i-armature > .fig-art { max-width: 220px; }
}
.fig--i-split {
  width: 100%;
  max-width: 664px;
}

/* The ring variant spans a list's full height (§7.4 Services) and is the §4.6
   answer for that column. Its arcs meet the left edge on a horizontal tangent,
   which is the only bleed §7.2.3 allows. The SVG carries
   preserveAspectRatio="xMinYMid meet", so however tall the column grows the
   chord axis runs its full height and the arc set stays optically centred
   against the bleed edge — the art can never "run out partway down" (§7.2.4). */
.fig--i-ring {
  position: relative;
  height: 100%;
  width: 100%;
}
/* The chord the arc set is struck from. It is CSS, not SVG geometry, for the
   same reason the process rail's stem is: it has to run the column's whole
   height at any height, and a <line> inside a `meet`-fitted viewBox cannot. */
.fig--i-ring::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--rule-w);
  background: var(--art-stem);
}
/* THE PLATE **IS** THE COLUMN. `width:100%; height:auto` on a 200px-wide box
   pinned the drawing at 356px however tall the column grew: the wrapper ran the
   full 1,476px list height and the artwork occupied 24% of it, floating in the
   middle with nothing above or below but the 1px chord — checklist #17. The SVG
   now takes the column's whole box and figures.js solves the arc radii for that
   box (alignRingSets), so one user unit is one CSS pixel and the outer arc
   leaves the chord at the list's first row and returns to it at the last. */
.fig--i-ring > .fig-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}

/* Below 768px the services list has no room for a bleeding arc set without it
   running behind live copy, and §7.2.2's mask is not a licence to ship art
   under a paragraph on a phone. The band is simply not drawn. */
@media (max-width: 767px) {
  .fig-band--drop-sm { display: none; }
}

/* ── the process rail (§8.13, §4.6) ──────────────────────────────────────────
   Two layers: a stem that stretches (only vertical rules inside it, which
   non-uniform scaling cannot distort) and square node viewports that do not.
   The stem's <path> is what motion.js measures — getTotalLength() reports user
   units, so the scroll-linked dash offset stays correct at any column height. */
/* THE RAIL IS THE COLUMN'S INSTRUMENT PLATE, NOT A STRIPE IN IT (§4.6).
   At 96px wide it left 392 × 948px of bare paper beside it — 4.5 × §4.6's
   height ceiling and 2.2 × its width ceiling, in the column §8.13 wrote the
   rail to keep full. The plate now spans the column: a stem at the module's
   own x, a node per stage, and from each node a §7.1 leader running right to
   the column's edge, where the chart row it marks begins 28px later. The
   largest unmarked rectangle inside it is one row's pitch — 142 × 400px at
   1440 — inside the ceiling on the axis that matters. */
.fig-rail {
  position: relative;
  height: 100%;
  min-height: var(--s12);
  width: 100%;
  --rail-mod: var(--s9);            /* 96px, one construction module × 4 */
  --rail-x:   calc(var(--rail-mod) / 2);   /* the stem's own axis, 48px */
}
.fig-rail__stem {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--rail-mod);
  /* The pen lifts at the terminal marker: alignRails() publishes the hex's own
     stop as --rail-end. Before it measures (and with no JS at all) the stem
     runs the plate, which is the authored drawing. */
  height: var(--rail-end, 100%);
  aspect-ratio: auto;
}

/* §7.1 LEADER — 1px dashed 2 6, orthogonal, from the node it labels to the
   thing it labels. It stops at the plate's right edge; the chart row it points
   into starts one grid gap later, so no leader pixel can fall inside a text
   box (§7.2.2). */
.fig-rail__leader {
  position: absolute;
  top: var(--at, 0%);
  left: calc(var(--rail-x) + var(--s3));
  right: 0;
  height: 0;
  border-top: var(--rule-w) dashed var(--art-leader);
  /* The plate is drawn once and does not light: the NODES are the indicator
     (§8.13), and a leader that brightened with them would double the signal.
     §7.1 already fixes the leader's colour at rgba(21,23,28,.20); knocking it
     back a further 45% took it to .09 — below the threshold at which a 1px
     dashed rule registers as ink at all, which is why the rail "spanned but did
     not occupy" its column. It is drawn at the weight the kit decrees. */
}
/* §7.1 TICK — 1px, 6px, --hair-gold, perpendicular to its axis. It closes the
   leader on the plate's right edge, so the run terminates on a mark instead of
   stopping in open paper (§7.2.4). */
.fig-rail__leader::after {
  content: "";
  position: absolute;
  right: 0;
  top: -3px;
  width: var(--rule-w);
  height: 6px;
  background: var(--art-hair);
}
.fig-rail__node {
  position: absolute;
  top: var(--at, 0%);
  /* The stem's own axis — the plate is now the column's full width, so the
     node hangs on --rail-x, never on the plate's centre. */
  left: var(--rail-x);
  width: var(--s5);                 /* 24px */
  height: var(--s5);
  transform: translate(-50%, -50%);
  transition: opacity var(--t-m) var(--e-ui);
  opacity: .55;
}
.fig-rail__node.is-on { opacity: 1; }
.fig-rail__node > .fig-art { aspect-ratio: 1; }

/* The stage-06 terminal marker: the hexagon from Fig. I, closing the rail. It
   occupies stage 06's own band, not the foot of the column, so it can never
   collide with node 05 however tall the section is. */
.fig-rail__cap {
  position: absolute;
  left: var(--rail-x);
  top: var(--at, 92%);
  width: var(--s7);                 /* 48px */
  height: var(--s7);
  transform: translate(-50%, -50%);
  opacity: .55;
  transition: opacity var(--t-m) var(--e-ui);
}
.fig-rail__cap.is-on { opacity: 1; }
.fig-rail__cap > .fig-art { aspect-ratio: 1; }
/* The marker sits ON the rail, not behind it: the hexagon is filled with the
   ground so the stem stops at its edge instead of striking through it. */
.fig-rail__cap polygon { fill: var(--art-node-fill); }

/* The rail is a desktop apparatus; at 820 the A4 archetype collapses to one
   column, the sticky rail ceases to exist and the rail would be a stripe next
   to nothing. 819 — not 899 — because 819 is where `.arch--a4` actually
   collapses: hiding the rail one breakpoint early left the 820–899 band with a
   two-column chart whose left column had nothing in it below the counter,
   which is the §4.6 void the rail was drawn to prevent. base.css collapses the
   `.has-sticky-rail` row at the same width. */
@media (max-width: 819px) {
  .fig-rail { display: none; }
}


/* ============================================================================
   5.  FIG. II — THE DUAL-AUTHORITY PLATE  (§7.3, §7.4)
   ---------------------------------------------------------------------------
   Home overview · /compliance intro · /compliance dark band (inverted) ·
   /assessment margin band.
   ========================================================================= */

.fig--ii > .fig-art { --fig-ar: 3.2667; }           /* 588 : 180, cut to geometry */

/* One user unit, one pixel — see §4 above. Fig. II is the figure that proved
   why the rule is needed: the specimen sheet printed it twice under the caption
   "THE SAME FIGURE, INVERTED" and mounted it at 1.263× on paper and 1.255× on
   night, so the hexagon grew 3.6% and the ring shrank 1.8% between two views of
   one plate. Capped at its own viewBox width both instances measure identically
   and the caption is true. */
.fig--ii {
  width: 100%;
  max-width: 588px;
}

/* The legend. §7.3 sets it in --t-m3; §3.5 caps the tiers at three and forbids
   inventing a fourth tracking value, so key and value share the tier and are
   separated by weight and colour, not by size. The colour swatch is the strand
   itself — the plate is labelled by its own strokes, not by a key box. */
.fig-legend {
  display: grid;
  gap: var(--s2);
  margin: var(--s5) 0 0;
  padding-top: var(--s3);
  border-top: var(--rule-w) solid var(--ln-soft);
}
.fig-legend__row {
  display: grid;
  grid-template-columns: var(--s5) auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: var(--s3);
}
/* The swatch IS the strand: a 24px length of it, at the strand's own weight
   and its own colour, so the key cannot drift out of step with the drawing. */
.fig-legend__row::before {
  content: "";
  grid-column: 1;
  align-self: center;
  height: calc(var(--sw-accent) * 1px);
  background: var(--art-accent);
}
.fig-legend__row--a::before { height: 1.25px; background: var(--art-accent); }
.fig-legend__row--b::before { height: 1.25px; background: var(--art-metal-line); }
/* The exit strand — .stem weight, .stem colour, because the route out of the
   node is an outcome and not a third authority. The swatch states that. */
.fig-legend__row--c::before { height: var(--rule-w); background: var(--art-stem); }

.fig-legend__key,
.fig-legend__val {
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  margin: 0;
}
.fig-legend__key { font-weight: 700; color: var(--tx-display); }
.fig-legend__val { font-weight: 400; color: var(--tx-micro); }

/* Below 560px the two-part legend row would wrap mid-phrase; it stacks, and the
   swatch keeps its column so the left margin never drifts (§4.5.8). */
@media (max-width: 559px) {
  .fig-legend__row {
    grid-template-columns: var(--s5) minmax(0, 1fr);
    row-gap: var(--s1);
  }
  .fig-legend__val { grid-column: 2; }
}

/* §10.13 — THE LEGEND NAMES THE STRANDS; IT DOES NOT RESTATE THE RECORD.
   On the overview the plate's legend and the ON THE RECORD ledger sit in two
   different columns at ≥900px and read as figure and record. Stacked at 390 the
   ledger lands ~90px under the legend and prints the same two scope strings
   again — DUBAI MUNICIPALITY / COSMETICS & PERSONAL CARE and MOHAP / MEDICAL &
   PROFESSIONAL-USE CLAIMS, four identical strings inside a screen and a half,
   which reads as a duplication bug rather than as two registers. Below 768 the
   legend keeps its swatches and its authority names — the part that keys the
   drawing — and drops the scope line the ledger below carries. */
@media (max-width: 767px) {
  .fig-legend__val { display: none; }
}


/* ============================================================================
   6.  FIG. III — THE DOSSIER SPECIMEN  (§7.3)
   ---------------------------------------------------------------------------
   /about overview · home services intro band.

   TWO LAYERS: the SVG plate (three sheets and a spine — geometry, which may
   scale) and an HTML overlay pinned to the front sheet carrying every word at
   the page's own fixed sizes. See figures.js §5 for why: type inside a viewBox
   is a user unit and scales, which is the one thing §3.2 fixes micro type in px
   to prevent.
   ========================================================================= */

.fig--iii > .fig-iii-plate > .fig-art { --fig-ar: .6928; }   /* 424 : 612, cut to geometry */
.fig--iii {
  width: 100%;
  max-width: 424px;
}

/* The two layers share one box — and that box, not the cell around it, is what
   the container queries below measure. The §4.2 mobile inset caps the PLATE at
   `100vw − 80px` while its cell stays the width of the column, so a query on
   the cell reports 350 for a 310px drawing and hands the overlay 40px of
   measure it does not have. */
.fig-iii-plate {
  position: relative;
  display: block;
  container-type: inline-size;
  container-name: fig-iii;
}

/* §7.1: "No fills except node interiors and hatch." The three sheets used to
   carry tonal grey-beige planes and the spine a near-black bar, which made this
   the only figure in the inventory not drawn by the same hand as the other four
   — pure 1px line at the declared weights. The stack now reads as three ruled
   outlines: the same construction, in the system's own material.
   `--art-node-fill` (the ground, opaque) keeps the front sheet reading as the
   TOP of a stack — it occludes the two behind it without adding a tone. */
.fig-iii .sheet__face {
  fill: none;
  stroke: var(--art-stem);
  stroke-width: var(--sw-stem);
  vector-effect: non-scaling-stroke;
}
.fig-iii .sheet__face--front { fill: var(--art-node-fill); stroke: var(--art-node-stroke); }
.fig-iii .sheet__rule {
  fill: none;
  stroke: var(--art-rule-soft);
  stroke-width: var(--sw-stem);
  vector-effect: non-scaling-stroke;
}
/* The binding edge, drawn as two 1px rules 6 units apart rather than as a
   filled bar — the same information, at the same weights as everything else. */
.fig-iii .sheet__spine {
  fill: none;
  stroke: var(--art-node-stroke);
  stroke-width: var(--sw-stem);
  vector-effect: non-scaling-stroke;
}
/* The two sheets behind the specimen are the stack, not the subject. */
.fig-iii .sheet--back { opacity: .55; }
.fig-iii .sheet--mid  { opacity: .78; }


/* ── the document layer ─────────────────────────────────────────────────────
   The box is DERIVED from the drawing, never guessed. The front sheet is
   x 96…480 of a 56…480 viewBox and y 132…708 of 96…708, so it is inset
   (96−56)/424 = 9.434% from the left and (132−96)/612 = 5.882% from the top and
   is flush with the other two edges.
   The padding is the type column's own margins — 30 / 24 units either side of a
   384-unit sheet, 38 above the running head's cap line and 16 under the foot —
   written as percentages of the sheet's WIDTH so the margins scale with the
   plate while the type does not. That asymmetry is the point: a document's
   margins are proportional to its page; its type is not. */
.fig-iii__doc {
  position: absolute;
  inset: 5.882% 0 0 9.434%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 9.9% 6.25% 4.17% 7.813%;
  /* Fixed type in a proportional box: the reduced variant below keeps it from
     ever exceeding the sheet, and this guarantees it visually as well. */
  overflow: hidden;
  font-variant-numeric: tabular-nums lining-nums;
}

/* The running head, over the sheet's one structural rule (E1). */
.fig-iii__head {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  padding-bottom: var(--s2);
  border-bottom: var(--rule-w) solid var(--art-rule);
  font-family: var(--f-micro);
  font-size: var(--t-m2);
  line-height: var(--lh-m2);
  letter-spacing: var(--tr-m2);
  font-weight: var(--fw-m2);
  text-transform: uppercase;
  color: var(--art-dim);
}
/* The tracked capital's trailing sidebearing, stripped on the flush-right end
   so the two ends of the head land on the same measure. */
.fig-iii__head > :last-child { margin-right: calc(var(--tr-m2) * -1); }

/* The title, in the display serif exactly as the page sets it. Two lines, the
   second one dimmed — a title and its subject, not a heading and a subtitle. */
.fig-iii__title {
  margin-top: var(--s4);
  padding-bottom: var(--s3);
  border-bottom: var(--rule-w) solid var(--art-rule-soft);
  font-family: var(--f-display);
  font-size: var(--t-h4);
  line-height: var(--lh-h4);
  letter-spacing: var(--tr-h4);
  color: var(--art-ink);
}
.fig-iii__title i { display: block; font-style: normal; color: var(--art-dim); }

/* The contents table: six rows on one grid, opened by its own header row. */
.fig-iii__contents {
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
}
.fig-iii__crow {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: var(--s3);
  align-items: baseline;
  padding: var(--s2) 0;
  border-bottom: var(--rule-w) solid var(--art-rule-soft);
  white-space: nowrap;
  overflow: hidden;
}
.fig-iii__crow:last-child { border-bottom: 0; }
.fig-iii__crow--h { padding-top: 0; color: var(--art-dim); }
.fig-iii__cidx  { font-weight: 700; color: var(--art-dim); }
.fig-iii__cname { color: var(--art-ink); justify-self: start; }
.fig-iii__cwk   { color: var(--art-ink); justify-self: end; margin-right: calc(var(--tr-m3) * -1); }
.fig-iii__crow--h .fig-iii__cname,
.fig-iii__crow--h .fig-iii__cwk { color: var(--art-dim); }

/* The foot: the §3.6 lockup and the barcode left, the licence mark right, all
   on one baseline grid, none of them crossing another. */
.fig-iii__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s4);
  padding-top: var(--s3);
  border-top: var(--rule-w) solid var(--art-rule-soft);
}
.fig-iii__k {
  display: block;
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  color: var(--art-dim);
}
/* §3.6's big-figure lockup in miniature and to the same two ratios: the unit
   word is .42 × the numeral and stands .12 numeral-em clear of its terminal,
   so one "locked" composition cannot render two ways on one sheet. */
.fig-iii__fig {
  display: block;
  margin-top: var(--s1);
  font-family: var(--f-display);
  font-size: var(--t-h3-s);
  line-height: var(--lh-h3-s);
  letter-spacing: var(--tr-fig);
  color: var(--art-accent);
}
.fig-iii__fig i {
  font-size: var(--t-fig-unit);
  font-style: italic;
  letter-spacing: 0;
  margin-left: var(--t-fig-unit-gap);
  color: var(--art-ink);
}
.fig-iii__barcode {
  width: 80px;
  height: var(--s5);
  margin-top: var(--s3);
  aspect-ratio: auto;
}
.fig-iii__mark { display: grid; justify-items: end; gap: var(--s2); }
.fig-iii__seal { display: block; width: var(--s7); }   /* 48px */
/* figureBlock publishes the PLATE's ratio as --fig-ar on the .fig wrapper and
   `.fig-art` reads it, so a nested figure inherits the wrong one and the seal
   rendered 48 × 70. A seal is square by construction (§7.3). */
.fig-iii__seal > .fig-art { aspect-ratio: 1; }
.fig-iii__lic {
  font-family: var(--f-micro);
  font-size: var(--t-m4);
  line-height: var(--lh-m4);
  letter-spacing: var(--tr-m4);
  text-transform: uppercase;
  color: var(--art-metal-text);
  margin-right: calc(var(--tr-m4) * -1);
}

/* ── the reduced variant ────────────────────────────────────────────────────
   Fixed type in a shrinking box runs out of measure eventually. Below a 264px
   plate the contents table's fine print is set as RULES instead of words: the
   row keeps its grid, its baseline and the length of the string it stands for
   (the hairline is drawn on the glyphs' own box), and the glyphs go
   transparent. The document still reads as a ruled contents table; it stops
   pretending to be legible at a size where it is not. Head, title, total and
   seal stay set — those tiers still hold at this width. */
.fig-iii__cname,
.fig-iii__cwk,
.fig-iii__cidx {
  background-image: linear-gradient(var(--art-rule-soft), var(--art-rule-soft));
  background-repeat: no-repeat;
  background-position: 0 62%;
  background-size: 0 var(--rule-w);
}
/* Between 264 and 360 the sheet's own margins tighten before its type does:
   a document narrows its margins before it gives up its words. 5% / 4.2% keeps
   the type column proportional to the sheet while returning ~15px of measure,
   which is the difference between the longest stage name clearing its duration
   column and touching it. */
@container fig-iii (max-width: 360px) {
  .fig-iii__doc { padding-left: 5%; padding-right: 4.2%; }
}

@container fig-iii (max-width: 264px) {
  .fig-iii__contents { color: transparent; }
  .fig-iii__cname,
  .fig-iii__cwk,
  .fig-iii__cidx {
    color: transparent;
    background-size: 100% var(--rule-w);
  }
}
@supports not (container-type: inline-size) {
  @media (max-width: 559px) {
    .fig-iii__contents { color: transparent; }
    .fig-iii__cname,
    .fig-iii__cwk,
    .fig-iii__cidx {
      color: transparent;
      background-size: 100% var(--rule-w);
    }
  }
}

/* The seal lands rather than draws: it is a struck impression, and --e-stamp is
   the easing reserved for exactly that (§6.1). */
.fig-iii__seal .fig-v { transition-delay: calc(var(--d-art) + var(--t-l)); }


/* ============================================================================
   7.  FIG. IV — THE WEEK AXIS  (§7.3, §8.11)
   ---------------------------------------------------------------------------
   The site's primary information graphic. Home process · /process ·
   /services process CTA (axis only).

   Not an SVG, by decree rather than convenience: §8.11 fixes the axis labels
   at --t-m3 and the bar at 6px, both absolute; §6.3 animates the bars with
   transform: scaleX(); §8.11's mobile rule needs a STICKY axis row. A viewBox
   can satisfy none of the three.

   GEOMETRY  Every horizontal position is a percentage of the shared 0–24 week
   axis, published by figures.js as --max (bar length) and --ext (the hatched
   fraction of it). The six rows therefore share one scale by construction, and
   stage 05 dominates honestly.
   ========================================================================= */

.fig-gantt {
  --gantt-bar-h:  6px;              /* §8.11, decreed off-lattice */
  --gantt-name:   clamp(var(--s11), 17vw, var(--s12));
  --gantt-axis-h: var(--s9);        /* 96px: unit label + tick + numeral */
  /* The track column's inset, so the axis scale can be positioned against
     exactly the same origin as every bar. */
  --gantt-track-l: calc(var(--col-index) + var(--s5) + var(--gantt-name) + var(--s5));

  /* WHERE THE BAR'S LINE IS, stated once. The baseline rule, the guide rules
     and the value all measure from it, so the four can never drift apart.
     .58em optically centres the 6px bar on the stage title's cap height. */
  --gantt-bar-y:  calc(.58em + (var(--gantt-bar-h) / 2));

  /* THE FIELD IS THE WHOLE TRACK. There is no reserved label strip any more,
     and removing it fixed three defects at once.
       · §8.11 puts the duration label UNDER the bar. Held on the bar's own line
         it had to be given a strip of its own at the right of every row, and
         that strip was subtracted from the plotting field — so at 1440 the
         0–24 axis ran 336px inside a 452px track and four of the six bars
         measured 14px: the site's primary information graphic, drawn as
         scratch marks. The field is now 453px and a one-week bar is 19px, wide
         enough for the §7.1 hatch to resolve as a hatch.
       · Every row rule and the axis rule ran to the track's edge while the
         scale stopped 116px short of it, so six hairlines overshot the data
         they ruled. Field and rules now share one terminus by construction.
       · Below 820 the same strip put "1–2 WEEKS" ON the axis line, at weeks
         4.0–13.0 of a 0–24 scale, with the track rule knocked out behind it —
         a one-to-two week stage painting ink across thirteen weeks.
     --gantt-val survives as the width the label is allowed to claim back from
     its own bar when it would otherwise overhang the field's right edge. */
  --gantt-val:    calc(var(--s9) + var(--s2));   /* 104px, the label's own run */
  --gantt-inset:  0px;

  /* The guide rule's lane: the 6px bar plus 9px of air above and below it. */
  --gantt-lane:   var(--s5);        /* 24px */

  position: relative;
  display: block;
}

/* ── the axis (§8.11) ────────────────────────────────────────────────────── */
.fig-gantt__axis {
  display: grid;
  grid-template-columns: var(--col-index) var(--gantt-name) minmax(0, 1fr);
  column-gap: var(--s5);
  align-items: end;
  height: var(--gantt-axis-h);
  padding-bottom: var(--s3);
  border-bottom: var(--rule-w) solid var(--ln-structural);   /* E1 — opens the field */
}
.fig-gantt__unit {
  grid-column: 2;
  justify-self: end;
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  color: var(--tx-micro);
}
.fig-gantt__scale {
  grid-column: 3;
  position: relative;
  display: block;
  height: var(--s5);
  /* The scale measures the PLOTTING FIELD, not the track: the label strip is
     reserved out of both, so week 24 is at the same x on the axis and in every
     bar. Inset the axis and not the field (or the reverse) and the chart lies. */
  margin-right: var(--gantt-inset);
}

/* THE TICK IS THE SCALE; THE LABEL IS ONLY ITS NAME.
   §8.11 asks for labels "optically aligned to the tick, not the cell", with the
   first flush-left and the last flush-right. That was implemented by shifting
   the whole mark — tick and numeral together — which moved the two end TICKS
   off their own week and made the four intervals 74 / 77 / 78 / 71px: a 9.9%
   spread on an axis whose entire job is to be evenly divided.
   So the mark's box is zero-width and sits exactly on its week. The tick is
   centred in it always; only the NUMERAL goes flush at the two ends. All four
   intervals are then identical by construction. */
.fig-gantt__mark {
  position: absolute;
  left: var(--at);
  bottom: 0;
  /* A ZERO-WIDTH TRACK, not merely a zero-width box: with `width: 0` alone the
     single grid column still sizes to its widest item, and every child is then
     centred in a track as wide as its numeral — which put the "12" tick 3.2px
     off its own week and made the intervals 242.4 / 245.6 / 242.3 / 242.3.
     Pinning the track to 0 lets the numeral overhang it while the tick stays
     exactly on --at. */
  width: 0;
  display: grid;
  grid-template-columns: 0;
  justify-items: center;
  gap: var(--s1);
}
.fig-gantt__mark.is-first { justify-items: start; }
.fig-gantt__mark.is-last  { justify-items: end; }
.fig-gantt__tick { justify-self: center; }

.fig-gantt__num {
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  font-weight: var(--fw-m3);
  color: var(--tx-micro);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;      /* the mark's box is 0 wide; the numeral overhangs it */
}
.fig-gantt__tick {
  display: block;
  width: var(--rule-w);
  height: var(--art-tick);          /* §7.1 Tick — 1px × 6px, --hair-gold */
  background: var(--art-hair);
}

/* The coarse axis (0 / 12 / 24) exists only below 820px. */
.fig-gantt__axis--coarse { display: none; }

/* ── the field ───────────────────────────────────────────────────────────── */
.fig-gantt__body { position: relative; }

/* THE GUIDE RULES AT 6 / 12 / 18, CLIPPED TO THE BAR'S OWN LANE — at every
   width, by one mechanism (§8.11, §7.2.2, §10.15).

   §8.11 asks for "full-height guide rules", and full height is only safe while
   the lane is the ONLY thing the rules pass through. It never was: the rules
   were drawn on the chart body, so they ran the whole height of every row and
   crossed the micro-tag row on all six of them — x=1027 through COORDINATION,
   x=1140 through SUBMISSION CONFIRMATION — with nothing but each tag row's
   opaque background knocking them out again. That knockout works, but it is a
   maintenance trap (it holds only while every label paints the section's exact
   ground) and it left the chart claiming a clipping behaviour it did not have.
   Below 820 the same rules crossed the duration labels, where the knockout did
   NOT hold, and the 12-week rule bisected the final S of "1–2 WEEKS" on every
   row.

   So the guides are drawn on the TRACK, not on the body: three rules per row at
   25 / 50 / 75% of the plotting field, each 24px tall and centred on that row's
   bar line. No guide pixel can fall inside a text bounding box at any width,
   the mechanism is identical at both breakpoints, and the chart still reads as
   a ruled field because six rows of lane marks stack into three columns.
   §7.2.2 is not a preference — line-art crossing running text is an automatic
   rejection, and this is the site's primary information graphic. */
.fig-gantt__guides {
  position: absolute;
  inset: 0 var(--gantt-inset) 0 0;
  z-index: 0;
  pointer-events: none;
}

.fig-gantt__guide {
  position: absolute;
  top: calc(var(--gantt-bar-y) - (var(--gantt-lane) / 2));
  height: var(--gantt-lane);
  left: var(--at);
  width: var(--rule-w);
  background: var(--ln-soft);
}

.fig-gantt__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}

.fig-gantt__row {
  display: grid;
  grid-template-columns: var(--col-index) var(--gantt-name) minmax(0, 1fr);
  column-gap: var(--s5);
  row-gap: var(--s3);
  align-items: start;
  padding: var(--s5) 0 var(--s6);
  border-bottom: var(--rule-w) solid var(--ln-soft);
}
.fig-gantt__row:last-child { border-bottom: 0; }

.fig-gantt__idx {
  grid-column: 1;
  font-family: var(--f-micro);
  font-size: var(--t-m2);
  line-height: var(--lh-m2);
  letter-spacing: var(--tr-m2);
  font-weight: var(--fw-m2);
  text-transform: uppercase;
  color: var(--ac-metal-text);
  font-variant-numeric: tabular-nums lining-nums;
  padding-top: .34em;               /* sits on the title's cap line */
}
.fig-gantt__name {
  grid-column: 2;
  font-family: var(--f-display);
  font-size: var(--t-h4);
  line-height: var(--lh-h4);
  letter-spacing: var(--tr-h4);
  color: var(--tx-display);
  text-wrap: balance;
}

/* ── the track and its bar ───────────────────────────────────────────────── */
.fig-gantt__track {
  grid-column: 3;
  position: relative;
  padding-top: .58em;               /* optically centres the bar on the title */
  /* The plotting field is the track MINUS the reserved label strip, at every
     width, so week 24 lands in the same place on the axis and in every row. */
  padding-right: var(--gantt-inset);
}
/* §8.11 — a 1px --rule-soft baseline under every bar, spanning the field. */
.fig-gantt__track::before {
  content: "";
  position: absolute;
  left: 0;
  right: var(--gantt-inset);
  top: var(--gantt-bar-y);
  height: var(--rule-w);
  background: var(--ln-soft);
}

.fig-gantt__fill {
  position: relative;
  z-index: 1;
  display: block;
  width: var(--max, 0%);
  min-width: var(--s2);             /* §8.11 — 8px floor, no stub bars ever  */
  height: var(--gantt-bar-h);
  /* §6.3 — scaleX from the left, --t-l / --e-out, 70ms per row (motion.js
     stamps the delay). Bars do not re-animate. */
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-l) var(--e-out);
}
.fig-gantt.is-in .fig-gantt__fill,
.fig-gantt__fill.is-in,
html:not([data-js]) .fig-gantt__fill { transform: none; }

/* Solid to the typical value … */
.fig-gantt__bar {
  position: absolute;
  inset: 0 var(--ext, 0%) 0 0;
  background: var(--art-accent);
}
/* … then the §7.1 hatch to the upper bound: 45°, 2px stroke, 4px gap,
   champagne, with the 1px champagne outline §8.11 requires. The outline is an
   `inset 0 0 0 1px` shadow — one of the two box-shadow forms §5 permits.

   THE STROKE IS --champagne, NOT --hair-gold. This is the single semantic mark
   in the whole art language — §7.1: "Hatch … used EXCLUSIVELY for extended /
   upper-bound quantities" — and the chart's entire argument is that solid means
   the duration you will get and hatched means the contingency you might. Drawn
   at --hair-gold's .42 alpha the strokes composited to rgb(223,206,173) against
   a rgb(251,249,245) gap: a 12-point difference, which at a 6px pitch inside a
   6px-tall bar reads as one flat beige field. The mark that carries the
   argument was the only mark on the chart too faint to read as a mark, and the
   colour it rendered in is not a value the colour system contains.
   --art-metal-line is the kit's champagne (#B9954A on paper, --champagne-lit on
   night), opaque, so the alternation is unmistakable at any bar length. */
.fig-gantt__ext {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--ext, 0%);
  background-image: repeating-linear-gradient(
    45deg,
    var(--art-metal-line) 0 2px,
    var(--art-void) 2px 6px);
  box-shadow: inset 0 0 0 1px var(--ac-metal);
}

/* The value, under the bar (§8.11): Pagella tabular for the number, HerosC for
   the unit. Never Pagella for a chart unit (§3.6).

   AND THE VALUE TRAVELS WITH ITS BAR. §8.11 fixes the label's ROW ("under the
   bar"); it fixes nothing about its column, and every value was left-aligned to
   the field's origin — so stage 04 (1 week, a 17px bar) and stage 05 (12–24
   weeks, a 452px bar) printed their figures at the identical x, twelve times
   apart in quantity and zero apart on the page. The <820px layout already did
   it correctly, which meant one component ran two different rules and the wrong
   one survived at reading size.
   `margin-left` is a percentage of the same containing block --max is a
   percentage of, so the label's left edge lands 12px past its own bar's end by
   construction, at every width, and the reserved label strip guarantees the
   24-week case still has somewhere to land. */
.fig-gantt__dur {
  display: inline-block;
  position: relative;
  z-index: 1;
  margin-top: var(--s3);
  /* THE LABEL TRAVELS WITH ITS BAR, AND STOPS AT THE FIELD'S EDGE.
     `min()` is what replaced the reserved strip: the label stands 12px past its
     own bar's end, except on the bar that reaches the axis terminus, where it
     is pulled back by its own run so it lands inside the field instead of
     hanging off it. Stage 04 (1 week) and stage 05 (12–24) therefore still
     print their figures 434px apart, and the axis keeps its full width. */
  margin-left: min(calc(var(--max, 0%) + var(--s3)),
                   calc(100% - var(--gantt-val)));
  color: var(--tx-micro);
  /* Knocks the guide rule out behind it — the draughtsman's answer, and the
     belt to the lane-clipping's braces (§7.2.2). */
  background: var(--sf-page);
  padding-right: var(--s1);
  white-space: nowrap;
}
/* ROMAN, EXPLICITLY. §3.6 locks ONE numeral-plus-unit convention for the whole
   site — "numeral in Pagella roman, unit word in italic" for the big-figure
   lockup — and this label states the same quantity (weeks) as that lockup does.
   base.css coerces `li b` to italic (it is the legal emphasis form inside
   running copy, §3.3), and this <b> lives inside the chart's <li>, so the
   duration was rendering numeral-italic + unit-roman: the hero's convention,
   inverted, three screens apart. A reader meeting `18–35 weeks` and then
   `1–2 WEEKS` met the same fact in two different costumes. */
.fig-gantt__dur b {
  font-family: var(--f-display);
  font-weight: 400;
  font-style: normal;
  font-size: var(--t-body);
  color: var(--tx-display);
  font-variant-numeric: tabular-nums lining-nums;
}
.fig-gantt__dur i {
  font-family: var(--f-micro);
  font-style: normal;
  font-size: var(--t-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  margin-left: var(--s2);
}

/* The mobile leader; no purpose on the desktop axis. */
.fig-gantt__lead { display: none; }

/* Per-stage deliverables, as a micro-tag row — never checkmarks (§9, §10.7). */
.fig-gantt__tags {
  grid-column: 2 / -1;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 21px;                      /* §3.5's compliant separator geometry */
  font-family: var(--f-micro);
  font-size: var(--t-m3);
  line-height: var(--lh-m3);
  letter-spacing: var(--tr-m3);
  text-transform: uppercase;
  color: var(--tx-micro);
}
/* ONE LINE. A wrapped tag row loses a separator at every break and reads as a
   grey paragraph rather than as a row of tags (§3.5), so the box is clamped to
   exactly one line height and the overflow line is whole items.
   The ground behind it is the section's own, through the alias: the row spans
   from the name column to the field's right edge, and painting its ground is
   what keeps any mark in the field — an axis tick, a bar's outline — from
   showing through the type (§7.2.2). The guides themselves no longer reach it:
   they are clipped to each row's bar lane. */
.fig-gantt__tags {
  max-height: calc(var(--t-m3) * var(--lh-m3));
  overflow: hidden;
  background: var(--sf-page);
}
.fig-gantt__tags > li { position: relative; white-space: nowrap; }
/* The middot is drawn as a ::before on every item EXCEPT the first, so a
   wrapped line can never orphan a trailing separator (§3.5, §10.21). */
.fig-gantt__tags > li + li::before {
  content: "·";
  position: absolute;
  left: -11px;
  /* The same separator tone as every other tag row (components.css --tag-sep):
     a divider glyph is never one of a section's three champagne elements, and
     the chart already spends all three on its indices, its hatch and its
     eyebrow. */
  color: var(--tag-sep);
}

/* ── the axis-only miniature (/services §4) ──────────────────────────────────
   THE RULE STARTS AT THE ZERO TICK. It used to be the axis row's own
   border-bottom, which spans the index and name columns as well — so on a
   1440 sheet the "axis" ran 639px while its 0–24 scale occupied the right
   302px of it, putting the zero origin at 51% of the line and leaving 330px
   of axis encoding nothing. In the full chart that border is the E1 opener of
   a three-column table and belongs there; in the miniature there is no table,
   only a scale, so the structural rule moves onto the scale column itself.
   The guides go with it: with no bars to guide they were three rules dropping
   96px into blank paper and stopping in mid-air (§7.2.4). What remains is a
   scale: five ticks, five numerals, one track rule under them. */
.fig-gantt--axis {
  /* The miniature is a SCALE and nothing else: no bars, so no values, so no
     label strip to reserve. The field is the whole width. */
  --gantt-inset: 0px;
}
.fig-gantt--axis .fig-gantt__axis {
  border-bottom: 0;
  /* No index and no name column: the miniature is a scale, so the scale takes
     the whole width and the unit word stands to its left. */
  grid-template-columns: auto minmax(0, 1fr);
}
.fig-gantt--axis .fig-gantt__unit  { grid-column: 1; justify-self: start; }
.fig-gantt--axis .fig-gantt__scale { grid-column: 2; }
.fig-gantt--axis .fig-gantt__scale {
  border-bottom: var(--rule-w) solid var(--ln-structural);
  /* Every tick lands ON the rule it measures. */
  padding-bottom: 0;
}
.fig-gantt--axis .fig-gantt__guides { display: none; }
/* THE MINIATURE'S OPTICAL BOTTOM IS ITS RULE. The empty 24px body under the
   scale was invisible slack that the §8.12 caption then measured from, so this
   one figure's caption stood 36px under its drawing while the other four stood
   at 12. A caption offset is a system value; it may not be set by a spacer. */
.fig-gantt--axis .fig-gantt__body { height: 0; }
.fig-gantt__rule {
  position: absolute;
  left: var(--gantt-track-l);
  right: 0;
  top: 0;
  height: var(--rule-w);
  background: var(--ln-soft);
}
.fig-gantt--axis .fig-gantt__rule { display: none; }

/* ── §8.11 MOBILE — "the axis survives" ──────────────────────────────────────
   Below 820px the field stacks, the coarse axis becomes a sticky header pinned
   under the nav, every bar keeps the same 0–24 scale, and a champagne leader
   carries a short bar's value out to its label. A 20px blue stub with no axis
   is a rejection; so is a chart that loses its axis. */
@media (max-width: 819px) {
  .fig-gantt {
    --gantt-axis-h: var(--s8);
    /* The axis is inset by exactly the index column, so the sticky 0 / 12 / 24
       header and every bar under it start on the same x. */
    --gantt-track-l: calc(var(--s7) + var(--s4));
    /* THE FIELD GETS THE LABEL STRIP BACK, WHICH IS WHERE THE MOBILE CHART'S
       SCALE WENT. The stacked layout inset the field by the index column
       (64px) AND by a reserved label strip (108px), leaving 178px of a 350px
       measure to carry 24 weeks: 7.4px a week, so four of the six bars
       rendered under §8.11's own 8px floor and the chart stopped encoding
       anything. With the label under the bar (below) the strip is not needed
       and the field is 286px — 11.9px a week, so the shortest stage in the
       brief draws a 12px bar and a 24-week stage still fits the row.
       The index column's 64px stays: title, bar, value and tags then share ONE
       measure origin and only the index hangs left, which is the same rule
       §8.5's mobile collapse follows. Two origins inside one row is the defect
       §4.5.8 bans, and an edge-to-edge axis under an indented title is exactly
       that. */
    --gantt-val: var(--s9);
    /* A slightly tighter lane than the desktop 24px: the stacked row is denser
       and the bar's line sits 16px under the stage title. */
    --gantt-lane: 20px;
    /* The track's box now opens ON the bar (the label sits under it, in flow),
       so the bar's line is half a bar from the track's top — and the baseline
       rule, the leader tick and the guide lane all measure from there. */
    --gantt-bar-y: calc(var(--gantt-bar-h) / 2);
  }

  .fig-gantt__axis { display: none; }

  /* …and the miniature drops to the coarse 0 / 12 / 24 scale exactly as the
     chart does (§8.11). It used to keep all five marks below 820 — five labels
     where the spec allows three — because it has no rows and therefore never
     rendered a coarse header of its own; figures.js now emits both rows for
     the miniature too, and the pair below simply swaps them. */
  .fig-gantt--axis .fig-gantt__axis--fine { display: none; }

  /* NO GUIDE RULES IN THE STACKED LAYOUT. §8.11 gives the guides to the ≥820px
     FIELD, where six bars share one plotting area and the reader compares
     across rows; its mobile clause replaces that field with a stack and a
     STICKY 0 / 12 / 24 axis, which is the reference here. A guide drawn into
     the stack has only one place to land — the bar's own line — which is where
     the duration label stands, so it either crosses live type (§7.2.2) or is
     knocked out behind it on four rows in six. The axis above does the job. */
  .fig-gantt__guides { display: none; }
  .fig-gantt--axis .fig-gantt__axis--coarse {
    /* Nothing is scrolling past it here — it is the whole figure. */
    position: static;
    padding-top: 0;
    border-bottom: 0;
    /* One column: the unit word is hidden at this width, and the two-track
       template the miniature uses at ≥820 would size the scale's track to its
       own content — which is nothing, because every mark is positioned. The
       scale collapsed to zero width and all three marks stacked on the 0. */
    grid-template-columns: minmax(0, 1fr);
  }
  .fig-gantt--axis .fig-gantt__axis--coarse .fig-gantt__scale {
    border-bottom: var(--rule-w) solid var(--ln-structural);
    padding-bottom: 0;
    /* No rows, so no index column to clear: the zero tick and the rule's left
       terminus are the same x. */
    margin-left: 0;
    margin-right: 0;
  }

  /* ── THE STICKY AXIS PINS UNDER THE STUCK NAV, AND ITS RULE IS INSIDE ITS
     OWN OPAQUE BOX (§8.11, §7.2.2, §10.15) ─────────────────────────────────
     Two defects, one geometry:

     · `top: var(--nav-h)` pinned the axis 88px down while the STUCK nav — the
       only nav state that exists once the reader is inside the chart — is 64px
       tall, so a 24px transparent band ran between the nav's rule and the
       axis's ground and the stage rows scrolled through it, sliced mid-glyph.
     · the axis's structural rule sat on the last pixel of its own box, with no
       opaque paper beneath it, so every stage title emerged from under the
       axis bisected at cap-height: a 1px rule drawn across live type, which
       §10.15 fails outright on the page's primary information graphic.

     The rule moves onto the scale (where it belongs — it is the axis's
     baseline) and the box keeps 12px of its own ground below it, so type is
     fully occluded before it reaches the rule instead of being struck by it. */
  .fig-gantt__axis--coarse {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    position: sticky;
    top: var(--nav-h-stuck);
    z-index: 2;
    background: var(--sf-page);
    height: auto;
    padding-top: var(--s2);
    padding-bottom: var(--s3);
    border-bottom: 0;
  }
  .fig-gantt__axis--coarse .fig-gantt__scale {
    border-bottom: var(--rule-w) solid var(--ln-structural);
    padding-bottom: var(--s2);
  }
  /* An anchor jump must not land a stage title under the axis either. */
  .fig-gantt__row { scroll-margin-top: calc(var(--nav-h-stuck) + var(--s9)); }
  .fig-gantt__axis--coarse .fig-gantt__unit { display: none; }
  .fig-gantt__axis--coarse .fig-gantt__scale {
    grid-column: 1;
    margin-left: var(--gantt-track-l);
    margin-right: var(--gantt-inset);
  }

  /* THE GUIDES NEED NO SPECIAL CASE HERE ANY MORE. They are drawn on the track
     and clipped to the bar's lane at every width (see .fig-gantt__guides), so
     the only thing this breakpoint changes about them is which weeks are
     marked — figures.js emits the coarse set (12 only) below 820, matching the
     coarse 0 / 12 / 24 axis. */

  .fig-gantt__row {
    grid-template-columns: var(--s7) minmax(0, 1fr);
    column-gap: var(--s4);
    padding: var(--s5) 0;
  }
  .fig-gantt__idx   { grid-column: 1; }
  .fig-gantt__name  { grid-column: 2; }
  /* margin, not padding: the track's box is then exactly the bar's line, which
     is what the value's `top: 50%` and the baseline rule both measure from. */
  /* Column 2 — the title's measure. The index alone hangs in the 64px rail. */
  .fig-gantt__track { grid-column: 2; margin-top: var(--s4); padding-top: 0; }
  .fig-gantt__tags  { grid-column: 2; }

  /* The bar sits on the track's own line and the value stands under it, so the
     plotting field carries nothing but the bar and its 12px leader tick. */
  .fig-gantt__track { padding-right: 0; }

  /* §8.11'S MOBILE LEADER, DRAWN ORTHOGONALLY (§7.1).
     "Minimum bar width 8px with a 1px champagne leader tick from the bar's end
     to its label, so a 1-week bar still encodes a value rather than reading as
     a stub." With the label under the bar rather than beside it, the run from
     the bar's end to its label is vertical — a 12px champagne tick dropping
     from the bar's terminus onto the label's own line. It is the same
     statement, made in the axis that is now free, and it keeps every mark
     except the bar itself out of the plotting field: the label can no longer
     paint ink across nine weeks of a scale it is only naming. */
  .fig-gantt__lead {
    display: block;
    position: absolute;
    left: var(--max, 0%);
    top: var(--gantt-bar-y);
    width: 0;
    height: var(--s3);
    border-left: var(--rule-w) solid var(--ac-metal);
  }
  .fig-gantt__dur {
    /* Clear of the bar's line and of the leader tick that reaches down to it. */
    margin-top: var(--s5);
    /* The same rule as the desktop label: it travels with its bar and is pulled
       back by its own run at the field's right edge, so it always names the
       quantity it stands under and never overhangs the scale. */
    margin-left: min(var(--max, 0%), calc(100% - var(--gantt-val)));
    text-align: left;
    /* Nothing to knock out here — the label is below the bar's line, not on
       it — but the ground is kept so a guide or a tick can never show through
       the type on a future variant (§7.2.2). */
    background: var(--sf-page);
    padding-right: var(--s1);
  }
  .fig-gantt__dur i { margin-left: var(--s2); }

  /* THE HATCH SURVIVES THE BREAKPOINT, exactly as the axis does.
     It used to be swapped for a flat 40% champagne wash below 820 on the
     argument that a 2px/4px hatch inside a 10px box shows one stroke. It does —
     and one opaque champagne stroke crossing a bar diagonally, inside a 1px
     champagne outline, is unmistakably a hatch, while a beige rectangle is
     unmistakably nothing. Trading the language's only semantic mark for a fill
     at the width where the chart carries the LEAST other information was
     backwards, and it is the reason every extension bar in the mobile stack
     read as "a second colour of bar". Nothing is overridden here any more. */
}

/* §3.5 tag caps: 5 at ≥1080, 3 at 768–1079, 2 below 768. */
@media (max-width: 1079px) {
  .fig-gantt__tags > li:nth-child(n+4) { display: none; }
}
@media (max-width: 767px) {
  .fig-gantt__tags > li:nth-child(n+3) { display: none; }
  /* Two long capability strings cannot share a line at this width, and §3.5's
     ::before middot — correct as it is about never orphaning a TRAILING
     separator — would then open every wrapped line with one. Below 768 the tag
     row becomes a column and the separator goes away with the wrap. */
  .fig-gantt__tags {
    display: grid;
    gap: var(--s1) 0;
    justify-items: start;
    /* Two stacked tags by design here, so the one-line clamp is released. */
    max-height: none;
  }
  .fig-gantt__tags > li + li::before { content: none; }
}

/* On a dark ground the sticky axis needs the band's own colour behind it. */
[data-ground="night"] .fig-gantt__axis--coarse,
.ground--night .fig-gantt__axis--coarse { background: var(--night); }


/* ============================================================================
   8.  FIG. V — THE SEAL  (§7.3)
   ---------------------------------------------------------------------------
   Footer 52px · /compliance trust band 96px · form success state 52px.
   ========================================================================= */

.fig--v { --seal-size: 52px; }        /* §7.3, decreed off-lattice */
.fig--v > .fig-art {
  --fig-ar: 1;
  width: var(--seal-size);
  height: var(--seal-size);
}
.fig--v-lg { --seal-size: var(--s9); }    /* 96px — /compliance A5 */

/* The seal lands rather than draws: it is a struck impression, and --e-stamp
   is the easing reserved for exactly that (§6.1). Its strokes still draw, so
   the two readings agree. */
.fig-v { transform-origin: 50% 50%; }


/* ============================================================================
   9.  §6.5  REDUCED MOTION
   base.css §12 already flattens `.draw *`. These are the mechanisms it cannot
   see: the Gantt bars (which are transforms, not strokes) and the rail nodes.
   §6.5: "Gantt bars render at full scale."
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  .fig-gantt__fill { transform: none !important; transition: none !important; }
  .fig-rail__node,
  .fig-rail__cap   { opacity: 1 !important; transition: none !important; }
}

} /* ── end @layer figures ──────────────────────────────────────────────────── */
