/* =====================================================================
   UTILITY ROW — five utilities on a solid rule
   The utilities PAGE turns this content into accordions (Unit 2). This
   row is the home-page summary and stays a row.
   ===================================================================== */

.uwrap{margin-top:54px}
.uintro{margin:22px auto 0;font-size:16px;color:var(--on-page-body);max-width:74ch}

.uline{position:relative;max-width:1040px;margin:0 auto}

/* THE RULE IS A GRADIENT, NOT A BORDER, FOR THE SAME REASON AS THE MEMBER
   STRIP: a border paints all at once or not at all and has no axis to draw
   along. `background-repeat:no-repeat` plus an animated `background-size`
   on the inline axis grows the painted box left to right.

   THIS ONE IS SOLID, SO IT IS A PLAIN linear-gradient AND NEEDS NO PITCH
   CALIBRATION. The member strip's dotted line is a repeating gradient
   whose authored period had to be tuned against the rasteriser (4px
   authored -> 4.2286px rendered); a two-stop solid fill has no period to
   get wrong. Do not copy the calibration comment from there — it does not
   apply here, and the two lines are deliberately different patterns:
   dotted --stone for the members, solid --ink here.

   COLOUR AND HEIGHT MEASURED AGAINST THE BORDER IT REPLACES, not assumed:
   the old `border-top:2px solid var(--on-page)` rendered rgb(41,38,49) at
   2px; so does this. See the report for the sampled figures.

   THE PAPER GROUNDS STILL MASK IT. `.uc .umark` carries --ground-page and
   paints over this rule — that is why the line can run edge to edge behind
   five marks without touching them. The draw therefore reads as passing
   behind each mark in turn. THEY ARE NO LONGER DISCS: 8r replaced the
   circular badges with near-square wrap logos and the 50% radius went with
   them. The masking is unchanged in effect, only in outline. */
.uline .line{
  position:absolute;left:4%;right:4%;top:29px;
  height:2px;
  background-image:linear-gradient(to right,var(--on-page),var(--on-page));
  background-repeat:no-repeat;
  background-position:left center;
  background-size:0 100%;
}
.uline .row{position:relative;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap}

.uc{flex:1 1 160px;min-width:160px;text-align:center}

/* THE UTILITY MARKS — the director's wrap logos as of 8r, ALWAYS PAIRED
   WITH THE CHARTER NAME, never the abbreviation alone. That rule is
   unchanged and is now carried by pages/index.html's h3 headings, which
   stay by ruling: the artwork itself spells the full name, but the
   heading is the only TEXT that does, and the images take an empty alt
   because of it. See the comment block in index.html before touching
   either.

   THE PAPER GROUND STILL MASKS THE RULE, but it is a RECTANGLE now, not
   a disc. The old mark was a circular badge and took a 50% radius; this
   artwork is near-square with the word arched across its top corners,
   and a circular mask would have clipped them. The rule still runs edge
   to edge behind five marks and still reads as passing behind each one.

   ============ HOW THE ASPECT IS ACTUALLY HELD ============
   The artwork is 184.39 x 172.96 — aspect 1.06609, near-square, where
   the badge it replaces was circular. The project's documented
   anti-distortion pattern is a pinned cross-axis size with a `flex`
   shorthand of 0/0/auto and aspect-ratio driving the other axis.

   THE `flex` HALF OF THAT PATTERN IS INERT HERE AND MUST NOT BE COPIED
   IN AS IF IT WERE LOAD-BEARING. `.uc` is display:block, so `.umark` is
   NOT a flex item and no flex property on it resolves against anything.
   Probed live rather than reasoned about: forcing the shorthand to
   99/99/0px moved the box by 0.000px in both axes (72.484 x 68 before
   and after). The pattern carries over from a component whose target WAS
   a flex item; here it would be decoration.

   WHAT HOLDS THE ASPECT IS THE PAIR BELOW: a definite `height` plus
   `aspect-ratio`, with the image's own intrinsic ratio (from the file's
   viewBox — the files carry no width/height attributes) agreeing with
   it. Measured across 1440 / 1024 / 768 / 560: all five render
   72.484 x 67.984 at every width, aspect 1.06619, which is 0.00010 off
   the authored ratio against a 0.01 tolerance, and no two of the five
   differ from each other. Document overflow is 0 at all four widths.

   NO TILT. The old marks took the hero sphere's angle (0.2094 rad =
   11.998 deg) because a globe badge has no upright. This artwork
   contains the word LONGITUDE, and rotating type is a different act from
   rotating a symbol. Removed deliberately — do not restore it here
   without a decision about the lettering.

   IF YOU ARE RE-MEASURING THE OLD BADGE, 54.557px IS NOT A DEFECT. The
   46px image was rotated, and getBoundingClientRect returns the
   axis-aligned box of a rotated square: 46 x (cos 11.998 + sin 11.998) =
   54.557. The box was never the artwork. Recorded so it is not reopened.

   No motion on the mark itself; the entry animation belongs to `.uc`. */
.uc .umark{
  display:flex;
  height:68px;width:auto;
  aspect-ratio:184.39/172.96;
  align-items:center;justify-content:center;
  margin:0 auto 18px;
  background:var(--ground-page);
}
.uc .umark img{width:100%;height:auto;display:block}

.uc h3{font-family:var(--serif);font-size:clamp(16px,1.7vw,20px);font-weight:700}
.uc p{font-size:13px;color:var(--on-page-soft);margin-top:8px;text-align:center}

/* NO STAGE LABELS ON UTILITIES. The originating CEO directive read "No
   stat counters — figures render static. No stage labels on utilities."
   Only the first clause has been lifted (2026-08-04, stat counters
   authorised on the confirmed figure set). THE SECOND CLAUSE STANDS and
   is not lifted by association. Do not add a status, phase, maturity or
   availability label to any utility.
   NOTHING IN THE ENTRY BELOW IS A SEQUENCE LABEL. The marks arrive in an
   order, but no mark is numbered, named by position, or captioned — the
   stagger is timing, not content. */

/* =====================================================================
   ENTRY — the rule draws left to right and each mark arrives as it is
   reached. One-shot, never reverses, no scroll-position coupling.
   =====================================================================

   NO NEW OBSERVER. `.uwrap` already carries `.rv`, so js/reveal.js's
   existing one-shot observer adds `.on` and unobserves. Third component to
   drive off it — same as the member strip and the stat arc.

   :nth-child IS SAFE HERE AND THAT WAS CHECKED, NOT ASSUMED. The caution
   in components/badge.css is about nth-of-type counting WITHIN a parent;
   the problem band was bitten by it. Verified against the DOM rather than
   the source: `.uline .row` has exactly 5 element children, all
   `DIV.uc`, one parent, homogeneous — and nth-child(1..5) resolves to
   Rx / FX / CGT / CTS / MD in that order. Re-check if anything else is
   ever put inside `.row`.

   ROSTER ORDER IS Rx · FX · CGT · CTS · MD AND DOES NOT CHANGE. The
   CTS/CGT swap ruled 2026-08-07 applies to the utilities-page accordion
   only, not to this row. These delays are keyed to POSITION, so swapping
   two marks in the markup would silently re-key their timings as well.

   DELAYS ARE KEYED TO EACH MARK'S OWN NODE POSITION, exactly as the member
   strip's are: delay = the mark's centre as a fraction of the rule x the
   rule's duration. Measured fractions at 1440: .05936 / .27969 / .50000 /
   .72031 / .94062, x 1300ms. Unlike the member strip these come out very
   nearly uniform (287 / 286 / 286 / 287 ms) — NOT because the method
   changed but because these five marks ARE evenly spaced, where the member
   logos' node dots sit on optical centroids. Same rule, different row. */
@keyframes ucRise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes ulineDraw{from{background-size:0 100%}to{background-size:100% 100%}}

.uwrap .uc{opacity:0;transform:translateY(14px)}
.uwrap.rv.on .uc{animation:ucRise .52s cubic-bezier(.22,.85,.3,1) both}
.uwrap.rv.on .uc:nth-child(1){animation-delay:.077s}
.uwrap.rv.on .uc:nth-child(2){animation-delay:.364s}
.uwrap.rv.on .uc:nth-child(3){animation-delay:.650s}
.uwrap.rv.on .uc:nth-child(4){animation-delay:.936s}
.uwrap.rv.on .uc:nth-child(5){animation-delay:1.223s}
.uwrap.rv.on .line{animation:ulineDraw 1.3s linear both}

/* =====================================================================
   BELOW THE WRAP, THE POSITION KEYING AND THE DRAWN RULE BOTH STOP
   MEANING ANYTHING — so this presents a different coherent behaviour, not
   a shortened version of the one above.
   =====================================================================

   MEASURED, NOT INFERRED FROM A BREAKPOINT. The row wraps at a content
   width of 856px, which is 5 x `min-width:160px` + 4 x `gap:14px`. Add the
   68px of `section{padding:110px 34px}` and the boundary lands at a
   924px content-derived width: bisected, 924 keeps all five `.uc` on one
   `offsetTop`, 923 does not, and at 924 each `.uc` measures exactly
   160.00px. Above the boundary the keying is sound all the way down to it
   — worst mark-centre error across 924..1440 is 0.88px on a 787.53px rule.
   Below it, marks move and the baked delays do not follow: at 920 the
   worst error is already -345.49px, and at 375/320 every mark is alone on
   its row so every measured fraction reads 0.50000.

   THE BOUND IS 940, NOT THE MEASURED 924, AND THAT GAP IS DELIBERATE.
   A media query evaluates against `window.innerWidth`, which INCLUDES the
   scrollbar gutter; the section's content box comes from `clientWidth`,
   which EXCLUDES it. Under overlay scrollbars the two agree and 924 is
   exact. Under a classic ~15-17px scrollbar they diverge, and a bound at
   924 would leave a band where the row has already wrapped but the query
   has not matched — the rule pinned across row one with marks below it,
   which is precisely the defect. The error is asymmetric: firing EARLY
   only removes a decorative connector from a layout that could still have
   carried it; firing LATE paints a rule that connects nothing. 940 takes
   the early side with headroom for the widest classic scrollbar.

   940 IS NOT THE BUILD'S 920 BREAKPOINT AND MUST NOT BE ALIGNED TO IT.
   The 920 queries in pages/home.css, components/stat-arc.css,
   components/badge.css and components/bands.css are a layout breakpoint
   chosen by hand. This one is solved from this row's own geometry and is
   only coincidentally nearby. This file has no 920 query and should not
   acquire one: a bound at 920 would strand 921-923, where the row is
   wrapped and the remedy would not apply.

   ANY CHANGE TO `min-width`, `gap`, THE SECTION PADDING, OR THE NUMBER OF
   MARKS INVALIDATES THIS NUMBER. RE-SOLVE IT, DO NOT RE-CHECK IT — the
   inputs are in the arithmetic above, and a spot-check at one width cannot
   tell you the boundary moved, only that the width you happened to sample
   still behaves.

   PLACEMENT IS LOAD-BEARING TWICE OVER. This block sits AFTER the base
   entry rules so its same-specificity overrides win on source order, and
   BEFORE the reduced-motion query so reduced motion keeps the last word on
   `animation`. It is deliberately NOT nested inside the reduced-motion
   query: the rule must be gone at these widths whether or not motion is
   reduced. `display` and `background-size` are different properties, so
   the reduced block below cannot resurrect it. */
@media(max-width:940px){
  /* both selectors restated — `.uline .line` is (0,2,0) and
     `.uwrap.rv.on .line` is (0,4,0), and a media query adds no
     specificity. `animation:none` so no keyframe runs on a hidden box. */
  .uline .line,.uwrap.rv.on .line{display:none;animation:none}

  /* Position keying is a claim about where a mark sits ALONG THE RULE.
     With the rule gone and the marks stacked, that claim has no referent,
     so the delays collapse to a plain document-order stagger. The 90ms
     step is NOT a new value — it is the house stagger already used by
     components/stat-arc.css (.00/.09/.18/.27/.36).
     Same selectors and same (0,5,0) specificity as the base rules; these
     win on source order alone. Roster order and the nth-child mapping to
     Rx / FX / CGT / CTS / MD are untouched — nothing here numbers,
     captions or names a mark by position. */
  .uwrap.rv.on .uc:nth-child(1){animation-delay:0s}
  .uwrap.rv.on .uc:nth-child(2){animation-delay:.09s}
  .uwrap.rv.on .uc:nth-child(3){animation-delay:.18s}
  .uwrap.rv.on .uc:nth-child(4){animation-delay:.27s}
  .uwrap.rv.on .uc:nth-child(5){animation-delay:.36s}
}

/* REDUCED MOTION — final state, not a shortened animation: all five marks
   present and the rule complete, no motion at any point.
   EVERY SELECTOR THE BASE RULES USE IS RESTATED. `.uwrap .uc` is (0,2,0)
   and `.uwrap.rv.on .uc` is (0,4,0); `.uline .line` is (0,2,0) and
   `.uwrap.rv.on .line` is (0,4,0). A MEDIA QUERY ADDS NO SPECIFICITY, so
   naming only the low-specificity halves would lose the moment the
   observer fires. Same trap documented in components/stat-arc.css. */
@media(prefers-reduced-motion:reduce){
  .uwrap .uc,.uwrap.rv.on .uc{opacity:1;transform:none;animation:none}
  .uline .line,.uwrap.rv.on .line{background-size:100% 100%;animation:none}
}
