/* ==========================================================================
   2 — HERO
   Two columns, 7/5. The argument takes the wider column; a row of tall cards
   holds the right and runs off the edge of the frame.

   THE CARDS ARE CUT OFF ON PURPOSE. The row is wider than the column that
   holds it, and the section clips it. A row that ended neatly inside the
   frame would say "here are four things"; a row that leaves the frame says
   "this continues" — which is the correct thing for a page about a room
   filling up. It is also why the row carries no arrows and does not scroll:
   it is a picture of a set, not a control.

   NO blend moments. Both were removed at review, in order:
     · the G4 blend shape — `hard-light` over pure black reads as a saturated
       blue mass, not as light
     · the G1 flat fill — it was the ground beneath the cell field, and the
       field is gone

   The Hero's dominant accent is still VIOLET; it is now carried by the CTA
   rather than by a gradient. Two flat fills remain permitted page-wide; the
   Hero simply no longer spends one.

   THE CELL FIELD IS GONE. The right column holds an image. This costs two
   things and both are recorded in the section plan: the page's opening
   signature (the count) and one of the six cell-motif sections. Neither is
   recoverable inside this layout.
   ========================================================================== */

.hero {
  position: relative;
  overflow: hidden;
}

.hero__inner { position: relative; z-index: var(--layer-raised); }

/* --------------------------------------------------------------------------
   TWO COLUMNS — the whole argument left, one image right
   Content reads top to bottom in a single column: eyebrow → H1 → lead →
   markers → CTA. The image holds the right at full height.
   -------------------------------------------------------------------------- */
.hero__head { display: grid; gap: var(--space-2xl); }

/* 5/7 → 7/5 at R5e. The text was reading in a 467px measure while the cards
   had 653px, which is backwards: the headline is eight words in an extrawide
   face and the cards hold nothing yet. Text takes the wider column.

   The cost is card width — see the row below. */
@media (min-width: 992px) {
  .hero__head {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: var(--space-3xl);
    align-items: stretch;
  }
}

.hero__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}

/* THE HEADLINE IS WHITE AGAIN — R5q, by direction. R5j is withdrawn.
   No rule here: .t-h1 already resolves to --color-text-heading at 21:1, so
   the override is removed rather than restated. The page's one <h1> is back
   to the highest contrast on the page.

   #6146ED did not become unused — it moved from the headline to the CTA
   ground at R5p, and its role in the colour table changed with it. It is now
   a CONTROL SURFACE and not a text colour, which retires the "large text
   only, never a control" caveat R5j attached to it. */

/* THE HEADLINE BREAKS WHERE IT IS TOLD — R5m, by direction. Four lines:
     The UAE’s First
     Higher-Education
     Marketing & Admissions
     Summit

   The overrun risk noted when this was three lines is gone — the longest
   line is now "Marketing & Admissions", shorter than what already fits. It
   costs a fourth line of height instead, roughly 52px at 48px/108%. If that
   pushes the CTA below the fold, the H1 size is the lever, not the breaks.

   DESKTOP ONLY. The spans go block at ≥992 and stay inline below, so the
   mobile H1 at 38px still breaks where its measure needs it. A <br> would
   have forced all three breaks at every width, which at 38px in a 24px-padded
   column produces a ragged four- or five-line stack.

   `text-wrap: balance` on .t-h1 is left alone. Against a one-line block it is
   a no-op, and if a line ever does overrun the column it splits it evenly —
   which is the behaviour you want in exactly that case.

   IF LINE 2 OVERRUNS: it wraps to a fourth line rather than overflowing, so
   the failure is soft. The fix at that point is the H1 size or the 7/5 split,
   NOT a different break — these three lines were specified. */
/* THE LEAD BREAKS TOO — R5n, by direction:
     HigherEd Collective is hosting a one-day summit to share knowledge,
     solve our biggest challenges, and get stronger together.
     Only 125 seats are available and attendance is by invitation only.  ←wraps

   Only the first two breaks are forced. The third span is the rest of the
   paragraph and wraps to its own measure over as many lines as it needs — it
   is a block so the break BEFORE it lands, not so it becomes one line.

   `> span` IS LOAD-BEARING. Direct children only, so the .t-emphasis wrapper
   around 125 stays inline. A descendant selector would put the numeral on a
   line of its own and break the sentence.

   `text-wrap: pretty` from .t-lead now applies per block, which is what you
   want on the third one: it is the only span that wraps, and pretty is what
   keeps it off an orphaned last word. */
@media (min-width: 992px) {
  .hero__title > span,
  .hero__lead > span { display: block; }
}

/* THE LEAD — white, one step down the scale. R5k, by direction.
   Two changes, and the second one forces a third:

   SIZE. Lead → body-lg, so 22 → 20px at desktop and 20 → 18 at tablet. Both
   are values the scale already owns; nothing new was invented for this.

   WEIGHT — NOT OPTIONAL. Weight 300 has a hard 22px floor (§5, and §10 as an
   accessibility rule: a thin stroke on pure black thins further on most
   displays). .t-lead switches to light at ≥992 precisely because that is
   where it reaches 22px. At 20px it may not, so this drops back to 400. The
   size change alone would have broken the floor silently.

   COLOUR — R5r, reverted. The lead is back to text-body #B4B4B4 at 10.1:1.
   No rule sets it: .t-lead already resolves there, so the white override is
   removed rather than restated. This restores the value separation between
   the H1 and the lead, which R5q had collapsed when the headline went back
   to white and left both at 21:1.

   THE WEIGHT OVERRIDE MUST STAY even though 400 is .t-lead's own default
   below 992. .t-lead switches to 300 at ≥992 — the exact breakpoint this
   text is read at — so removing this line would put light back on a 20px
   line and breach the 22px floor (§5, §10). It is not redundant.

   Scoped at 0,2,0 so it beats the ≥992 `.t-lead` weight rule regardless of
   stylesheet order. */
.hero .hero__lead {
  margin-block-start: var(--space-xl);   /* 40 — see the R5u note below */
  font-size: var(--font-size-body-lg);
  font-weight: var(--font-weight-regular);
}

/* THE SEAT FIGURE IS UNIFORM WITH THE SENTENCE — R6b, by direction.
   No colour rule, no weight rule. It inherits the lead's #B4B4B4 at 400 and
   is indistinguishable from the words either side of it. Sand (R5r and
   earlier), violet (R5y) and white-at-700 (R6a) are all withdrawn.

   TABULAR FIGURES ARE KEPT, and they are the one thing here that is not
   cosmetic. 125 is UNRESOLVED — the Final CTA says 100 — so this number will
   be edited. Tabular digits mean the sentence does not reflow when it is.
   The property changes nothing visible today; it prevents a wrap shifting
   the moment the figure is settled.

   §6b IS NOW FULLY UNMET for this numeral — not display scale, not 700, no
   accent. That rule treats numerals as compositional objects, and this one is
   deliberately not one: it is a word in a sentence. Recorded so the gap reads
   as a decision rather than an oversight. */
.hero .hero__seats { font-variant-numeric: tabular-nums lining-nums; }

/* THE ONE BARE EYEBROW.
   design-rules §10a says every eyebrow on the page sits in a notched hairline
   frame — "not some of them, all of them". This one does not, by direction.

   It takes .t-eyebrow alone: 14px / 600 / uppercase / +0.06em, in the sand
   accent #F2D98A, which .t-eyebrow already sets. No new type rule.

   TWO THINGS THIS CHANGES, both recorded in design-rules §10a:

   1. The frame used to carry the accent so the LABEL could stay white, which
      is how an eyebrow avoided spending its section's accent allowance.
      With no frame, the accent is the text — so the Hero now shows sand
      alongside the violet CTA. Sand is a supporting accent on a single
      element, which §8 permits; it is not a second dominant.

   2. The other three eyebrows on the page — the modal, the §7 plate and §13
      — are still boxed. One bare and three framed is an inconsistency, not a
      variant. Either this treatment goes page-wide or the rule keeps its
      "all of them" and this becomes the Hero's stated exception.

   The leading pixel glyph went with the frame. One line to restore. */

/* SPACING, R5l — the title group's two gaps were swapped in weight.
   Eyebrow → heading 48 → 24px. Heading → lead 16 → 32px (below).

   The old ratio said the eyebrow was a separate element and the lead was
   part of the heading. It is the other way round: the eyebrow is a LABEL ON
   the headline — date and venue, read as one unit with it — while the lead
   is a second statement and needs to sit as one.

   Both values are on the §4 scale, and both are set HERE rather than on
   --gap-heading-to-content / --gap-heading-to-paragraph, which have eleven
   other consumers between them. This is a Hero composition, not a change to
   the page's title-group rhythm. */
/* THE HERO EYEBROW — R6t. It is the standard component now.
   .eyebrow + .eyebrow--emphasis in the markup carries the whole treatment:
   the stepped frame, radius 0, transparent interior, twelve hairline layers,
   and the sand this eyebrow has worn since R5c.

   EVERY LOCAL RULE THAT USED TO LIVE HERE IS DELETED, not overridden — the
   surface-03 fill, the 1px border, the 8px radius, the 12/16 padding and the
   chip layout from R5v/R5w/R5x. A section re-styling a component into a
   different shape is how two things stop being one thing; the component owns
   its geometry again.

   WHAT THIS CLOSES: §10a reads "every eyebrow sits in a notched hairline
   frame — not some of them, all of them". The Hero's was the stated exception
   from R5c onward and it moved three times — bare text, then a pill, then a
   filled box — without ever rejoining the rule. Four eyebrows, one frame.

   WHAT IT DOES NOT CLOSE: --emphasis puts the accent on the LABEL as well as
   the frame, so this eyebrow still spends the Hero's sand. §10a's "the label
   stays white, so an eyebrow never spends the allowance" holds for --action
   and --confirm only. That was already true of the bare sand text; it is now
   visible in the component instead of hidden in a section override.

   All that is left here is the gap down to the headline. */
/* R19p — --space-md -> --gap-title-group. The VALUE is unchanged: the Hero
   was already the only eyebrow at 24, and the page has now converged on it. */
.hero__eyebrow { margin-block-end: var(--gap-title-group); }

/* WRAPPING IS EXPECTED, NOT A FAULT. Uppercase at 14px, this string runs past
   a narrow column and the box wraps to two lines. At 8px that is simply a
   taller rounded rectangle — the shape holds, which it did not reliably do at
   999px. No breakpoint override is set: a component that changes shape at a
   breakpoint stops being the same component. */

/* THE MARKER ROW — now BELOW the CTA, R5o, by direction.
   Reading order changed with it; the CSS did not. The row was already 32px
   from whatever preceded it and the CTA is still 32px from the paragraph, so
   the vertical rhythm is unchanged and only the order moved.

   What DID change is what the row means. Above the button it qualified the
   invitation before you were asked to accept it; below it, it reads as the
   terms of the action just offered — free, invited, no pitching. That is the
   better position for it, and the reason the two gaps are left equal is that
   the row should read as its own line of terms, not as a caption clamped to
   the button. Tighten to 24px if it should bind to the CTA instead.

   Horizontal, with a hairline between each pair. The three conditions are
   read as one set of terms rather than as a list of three claims — which is
   what they are: the qualifying conditions of a single invitation.
   Wraps to a column below 768 rather than compressing; a hairline between
   stacked items would read as a table. */
.hero__markers {
  margin-block-start: var(--space-xl);   /* 40 — R5u */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  list-style: none;
  margin-inline: 0;
  padding: 0;
}

/* THE STAR, WHITE — R6h, by direction, and SCOPED TO THE HERO.
   The tick stays everywhere else, and that is not tidiness: §4 runs the
   page's ✓/✗ opposition, where the two glyphs differ by FORM first and hue
   second (design-rules §18 — colour is never the sole carrier). Swapping the
   ✓ globally would rewrite the argument section from the Hero.

   Three things had to move with the glyph:
     · fill → currentColor. .marker already sets colour to text-heading, so
       white comes from the component rather than from a second hard-coded
       value. The green stays green in §4, untouched.
     · shape-rendering → auto. The tick is a pixel glyph on a 15-unit grid and
       crispEdges is what keeps its steps exact. This star is curves; snapping
       curves to the device grid is what makes an icon look chewed.
     · size → 18px. 15 exists because it is 1:1 with the pixel grid, and this
       glyph is not on that grid, so the reason for 15 does not apply. 18 is
       §13's stated default for an inline glyph.

   §13 IS BROKEN HERE: "filled pixel glyphs · stepped, axis-aligned, no
   curves" and "one set page-wide". The page now runs pixel glyphs, the CTA's
   stroke arrow, the unconverted menu/close icons, and this. Recorded at
   design-rules R6h. */
.hero__markers .marker { align-items: center; }

.hero__markers .marker__glyph {
  inline-size: var(--icon-size-pixel);
  block-size: var(--icon-size-pixel);
  fill: currentColor;
  shape-rendering: auto;
  margin-block-start: 0;   /* the tick's optical nudge; the star is centred */
}

@media (min-width: 768px) {
  .hero__markers { flex-direction: row; align-items: center; gap: var(--space-md); }

  /* Separator 0.10 → 0.20 (R6h). border-default is tuned for a hairline on a
     panel edge, where it sits against a filled surface; a 1px line of it
     floating on pure black between two words all but disappears.
     border-strong is the next value on the scale and no new one was added —
     "lighter, but not white" is exactly the gap between the two. */
  .hero__markers .marker + .marker {
    padding-inline-start: var(--space-md);
    border-inline-start: var(--border-hairline) solid var(--color-border-strong);
  }
}

/* The CTA sits inline, sized to its label. The column is narrow enough now
   that a full-width button would run the whole measure and read as a bar.

   ONE button, not two. The reference pairs a primary with a secondary; the
   content document gives the Hero a single action and no second label, and
   inventing one would put a competing choice next to the page's only
   conversion point. */
/* THE COLUMN'S RHYTHM — R5u, by direction: open the gaps between blocks.
   32 → 40 on all three, one step up the §4 scale. Nothing invented.

     eyebrow  →  heading    24    ← UNCHANGED, and that is the point
     heading  →  lead       40
     lead     →  CTA        40
     CTA      →  markers    40

   THE 24 STAYS. R5l set it deliberately: the eyebrow is a label ON the
   headline — date and venue, read as one unit with it — not a block of its
   own. Opening it with the others would flatten the column into five equally
   spaced items and undo the grouping. One tight gap and three open ones is
   what makes the title read as a title.

   The three open gaps are EQUAL on purpose. Lead, CTA and markers are three
   peers following the title; varying them would imply a hierarchy between
   them that the content does not have.

   SECTION PADDING IS NOT TOUCHED. design-rules §14 gives the page one shared
   padding utility and says not to vary it case by case, so the Hero cannot
   buy breathing room at its own top and bottom without breaking every other
   section's rhythm. If the section still reads tight after this, that is a
   page-wide change to the utility, not a Hero one. */
.hero__cta { margin-block-start: var(--space-xl); }

/* THE HERO CTA takes the shared .btn--solid modifier — see button.css.
   It was scoped here at R5p specifically so it could not spread; at R6e the
   header asked for the same treatment, so it was promoted to a modifier
   rather than duplicated. The Hero keeps only what is genuinely local. */

/* --------------------------------------------------------------------------
   THE CARD SLIDER — one card centred, its neighbours cut and faded
   Advances on its own every 1500ms, crossing in 900ms.

   The card is 48% of the viewport, so a partial neighbour shows on each side
   at roughly 105px. Those neighbours are what say "this continues"; a single
   card alone would just be a picture.

   58% → 48% at R5g, to show more of the neighbours. The centre card gives up
   width to do it, and that is the trade: the more of the set you can see, the
   less any one card dominates. Below about 40% the centre stops reading as
   the subject and the row becomes three equal cards, which is a different
   component.

   TWO FADES, doing different jobs:
     · opacity on the non-current cards — hierarchy. One card is the subject.
     · a mask on the viewport edges — containment. The row dissolves at its
       boundary instead of ending on a hard cut, which is what stops a slider
       from reading as a filmstrip in a box.

   The row no longer runs off the section edge; it is a contained viewport
   now, because a slider needs a frame to slide inside.
   -------------------------------------------------------------------------- */
/* CENTRED AGAINST THE LEFT COLUMN — R6i, by direction.
   .hero__head aligns its items to `stretch`, so the gallery was filling the
   full row height and the track sat at the top of it — the cards read as
   hanging from the headline rather than as sitting beside the block.

   `align-self: center` collapses the gallery to its own content height and
   centres it in the grid area, so the card row's midline meets the midline of
   the eyebrow → H1 → lead → CTA → markers stack.

   Deliberately NOT `display: flex` on the gallery: that turns the track into
   a flex item sized to its content, and the slider measures its offsets from
   live rects. Changing the track's box is exactly the kind of thing that
   works until it silently does not. This changes only the wrapper. */
.hero__gallery {
  position: relative;
  align-self: center;
  overflow: hidden;
  min-inline-size: 0;
  /* Edge dissolve. Cheap, and it means the partial cards never end on a line.
     Pushed out 12% → 8% with the wider reveal: the fade has to stay shorter
     than the neighbour it runs across, or the extra card width that was just
     bought is spent dissolving it. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.hero__track {
  display: flex;
  gap: var(--space-sm);
  align-items: center;
}

/* 48% → 56% at R6i, by direction: the cards were reading small against the
   text block. THE CARD BUYS ITS WIDTH FROM ITS NEIGHBOURS — that is the whole
   arithmetic of this row, and it is worth having the numbers rather than
   nudging the percentage:

     gallery 507px at a 1280 container, 48px side padding, 64px grid gap
     48%  → card 243px · 132px of each neighbour showing
     56%  → card 284px · 111px of each neighbour showing
     62%  → card 314px ·  96px  ← R5g asked for MORE than this

     ⚠ THE HEIGHTS THAT USED TO BE IN THIS TABLE ARE GONE, and the widths are
     corrected. The old figures read 467px for the gallery, which assumed the
     container's 1280 INCLUDED its padding; it does not — .container caps at
     calc(1280 + padding*2), so the content box is a full 1280 and the gallery
     is 507. Heights are omitted rather than restated because they follow from
     aspect-ratio, which R12t changed; a height written here is a second place
     for the ratio to be recorded and to go stale.

   56 is 17% more card while keeping the neighbours generous. Past roughly 62
   the row starts undoing the reveal that was deliberately widened at R5g. If
   the cards need to be bigger still, the lever is the COLUMN, not the card —
   and §15 allows only 7/5 or halves, so that is a real decision about the
   headline's measure, not a percentage tweak. */
.hero__card {
  flex: 0 0 auto;
  inline-size: 56%;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-control);
  /* R15d — THE CARD IS A CONTAINER NOW, not the <img> itself. It holds the
     photograph as its ground and a name plate at its foot.

     align-content: end with one in-flow child puts the plate on the floor;
     the photograph is out of flow and unaffected. --space-sm rather than
     --gap-card-padding: this card is 284px wide at desktop, and the 32px card
     padding would leave a 220px measure for a three-line attribution. */
  position: relative;
  overflow: hidden;
  display: grid;
  align-content: end;
  padding: var(--space-sm);
  opacity: 0.35;
  /* The fade rides the travel, on the same curve. Two different durations
     here and the card is at full opacity before it is centred, or still
     dimming after it has stopped — either one reads as a fault. */
  transition: opacity var(--duration-slide-travel) var(--ease-slide);
}

.hero__card.is-current { opacity: 1; }

/* R12w — block-size: auto IS THE FIX, and its absence was the bug.

   THE HTML `height="1800"` ATTRIBUTE IS A PRESENTATIONAL HINT, and a hint
   still beats nothing. Nothing in .hero__card or in reset.css sets a height
   for an img, so the attribute won and the card rendered 56% wide by
   EIGHTEEN HUNDRED PIXELS TALL. aspect-ratio was not ignored — it simply does
   not apply when both width and height are already definite, and the
   attribute made the height definite. object-fit: cover then did exactly what
   it was told and cropped a 1200x1800 portrait into a 392x1800 slot, which is
   why the card came back as one enormous face.

   The .ph divs were never affected: a div has no height attribute, so
   aspect-ratio was the only thing setting their height all along. That is the
   whole difference between the two cards, and it is why the placeholders were
   right and the photo was not.

   THE ATTRIBUTES STAY. width/height on an <img> reserve the box before the
   file arrives and are what stop the row shifting as it loads. The fix is to
   override the height in CSS, not to delete the hint that also prevents
   layout shift.

   object-fit: cover then has nothing to crop — 56% wide at 2/3 is exactly the
   file's own ratio — and is kept for the next photo, which may not be. */
/* R15d — THE PHOTOGRAPH, which used to be .hero__card itself.

   Absolute rather than a grid row: it is the card's GROUND, and a photograph
   in the flow would sit above the plate rather than behind it.

   block-size: 100% for the reason every <img> on this page needs a height —
   `height="1800"` is a presentational hint and wins wherever no CSS sets one.
   As .hero__card it needed `auto` so the card's own aspect-ratio governed;
   inset-filling, 100% is the equivalent. */
.hero__shot {
  position: absolute;
  inset: 0;
  z-index: var(--layer-base);
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   R15d — THE NAME PLATE
   §6's construction, at this card's scale: opaque --color-surface-panel with
   a 2px violet leading edge, so the two sliders on the page label a face the
   same way.

   OPAQUE, NOT A SCRIM. §6 spent five revisions discovering that a gradient
   over a photograph is a running cost — it has to be re-measured whenever the
   image, the gradient or anything above it moves. A plate is 17.9:1 for white
   and 8.6:1 for the role lines whatever the photograph does, and it costs the
   picture only the area the words occupy.

   ⚠ justify-self: start IS LOAD-BEARING, as it is in §6: a grid item stretches
   to its column by default, and without this the plate would run the full
   width of the card and read as a band rather than a label.

   12px padding rather than §6's 16: this card is 284px wide against §6's 628,
   so the same inset would take twice the share of it.
   -------------------------------------------------------------------------- */
/* ⚠⚠ R23n — THE PLATE HOLDS A BRAND MARK NOW, NOT A NAME, by direction.
   Same ground, same corner, same padding; two things changed.

   1. THE VIOLET LEFT EDGE IS GONE, by direction ("without purple stroke at
      the left end"). ⚠ It was not a local flourish: design-rules §8 calls the
      2px violet vertical edge "a recurring device", shared with §6's video
      name card. §6 KEEPS IT — this is now the only place the device has been
      dropped, so the recurrence is down to one instance and the rule should
      be re-read before it is cited again.

   2. THE PLATE IS A FIXED BOX, by direction ("uniformly sized across all
      image cards"). It used to size to its own text, so a three-line title
      made a taller, wider plate than a two-line one — Dylan's measured
      160 x 110 where others were shorter. A logo has no such excuse to vary.

   ⚠ --hero-brand-width IS A BOX DIMENSION, NOT SPACING. §4 is explicit that
   control and icon boxes answer to no scale and carry their own reason: 160
   is the width the widest name plate already occupied, so the marks land in
   the footprint the names vacated rather than in a new one. */
.hero__id {
  position: relative;
  z-index: var(--layer-raised);
  /* R23o — `start` -> `center`, by direction: "center align all logo cards to
     the image cards vertically." The plate is centred on the card's VERTICAL
     axis — i.e. across its width — and stays on the card floor, which
     .hero__card's own `align-content: end` puts it on.

     ⚠ THE OTHER READING WAS REJECTED FOR A REASON, not overlooked: centring
     the plate in the card's HEIGHT would park it across the middle of the
     photograph, over the face of every person in the row. The plate is an
     overlay on a portrait, so the floor is the only edge it can sit on
     without covering the subject. */
  justify-self: center;
  --hero-brand-width: 160px;
  inline-size: var(--hero-brand-width);
  padding: var(--space-xs);
  background-color: var(--color-surface-panel);
  /* ⚠⚠ R24o — A VIOLET STROKE RETURNS, BUT NOT THE OLD DEVICE. R23n removed a
     2px violet edge on the LEFT ONLY — design-rules §8's "recurring vertical
     edge", shared with §6's video name card. This is a hairline on all four
     sides, by direction, which is a border rather than that device. §6 still
     holds the only instance of the edge; do not read this as its return.

     ⚠ --color-accent-identity, NOT --color-control-stroke. They are the same
     hex, and §3 draws the line: identity is for "identity, logo, links" and
     is annotated NEVER A CONTROL; control-stroke is for boundaries of things
     you press. This plate holds a logo and is not pressable, so it takes the
     identity token — the distinction is about role, not colour.

     ⚠ 1px, not --border-emphasis. Two pixels around a 160 x 71 box reads as a
     frame competing with the mark inside it; the old 2px worked because it
     was ONE edge. */
  border: var(--border-hairline) solid var(--color-accent-identity);
}

/* ⚠ THE ASPECT RATIO IS DECLARED, NOT INHERITED FROM THE FILE, AND object-fit
   BACKS IT UP. All six supplied marks are 774x268 on one canvas, so today
   both are belt and braces — but "uniform" has to survive a seventh logo
   exported at another size, and this is what makes the box authoritative
   instead of the asset. A file with a different ratio letterboxes inside the
   same rectangle rather than resizing every card's plate. */
.hero__logo {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 774 / 268;
  object-fit: contain;
  /* R23p — THE OPTICAL-SIZE HOOK, and it exists because the plate cannot fix
     this on its own. Every supplied mark sits on the same 774x268 canvas but
     with a different amount of empty space around it, so `contain` renders
     them at wildly different optical sizes inside identical boxes:

         Studyportals   125.7 x 17.7   2227 px2
         Cosmic Centaurs 123.6 x 11.4  1409
         HubSpot          76.6 x 21.7  1667
         KHDA             52.3 x 27.4  1430
         Bond & Coyne     55.2 x 23.1  1279
         Tetr             36.7 x 24.6   900   <- the outlier

     ⚠ IT DEFAULTS TO 1 AND IS SET PER CARD, INLINE, next to the src it
     corrects — the same pattern .seats uses for its two figures. A card with
     no override is untouched, so this can never silently rescale a logo
     nobody looked at.

     ⚠ `scale` GROWS THE WHOLE CANVAS, NOT JUST THE MARK, so a scaled logo's
     element spills past the plate — transparently, because the empty canvas
     is what is spilling. Judge headroom by the INK, never by the element box.

     ⚠ THIS IS A WORKAROUND FOR THE ASSETS, NOT A DESIGN FEATURE. The real fix
     is to trim each PNG to its ink bounds and re-place them on a normalised
     canvas; then every scale here goes back to 1 and can be deleted. See
     design-rules.md §6 R23n. */
  scale: var(--hero-logo-scale, 1);
}

/* MOTION IS OPT-IN, NOT OPT-OUT.
   The track only animates where motion is welcome. Under reduced motion the
   script does not advance at all and every card sits at full opacity — the
   set renders as a static row, which is a legitimate state rather than a
   degraded one. */
@media (prefers-reduced-motion: no-preference) {
  /* `will-change` moved in here at R6j. It was unconditional, which meant the
     track held a permanent compositor layer even under reduced motion, where
     the slider never starts and the transform never changes — a layer paid
     for on every scroll frame and never used. It now exists only where the
     transition does. */
  .js .hero__track {
    will-change: transform;
    transition: transform var(--duration-slide-travel) var(--ease-slide);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__card { opacity: 1; }
}


/* --------------------------------------------------------------------------
   EVENT PARTNERS — the strip (R7m), promoted to a component at R9k.

   The mechanism now lives in css/components/marquee.css, because §8 needed
   the same thing and two copies of a seamless-loop calculation is two places
   to get it wrong. What stays here is only what is about the HERO:

   FULL-BLEED. It is a child of the section, not of .hero__inner, so nothing
   constrains it to the container. Only the LABEL is contained, which is what
   keeps it aligned with the H1 above it. §8's copy is NOT full-bleed — it
   sits inside that section's box — which is exactly why the component owns
   the loop and the sections own their placement.
   -------------------------------------------------------------------------- */
.hero__partners { margin-block-start: var(--space-3xl); }

/* Not an eyebrow — deliberately. §10a's framed eyebrow belongs to a section's
   opening, and this section already has one at the top. A second frame two
   feet below the first would read as two openings. This is a rail label: the
   same 14px uppercase muted metadata the placeholder plates themselves use. */
.hero__partners-label {
  font-size: var(--font-size-meta);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-caps-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-block-end: var(--space-md);
}

/* Full-bleed only while the row is passing through the frame. Under reduced
   motion the component stops it and wraps it, so it becomes ordinary content
   and takes the container's own side padding instead. */
@media (prefers-reduced-motion: reduce) {
  .hero__partners .marquee { padding-inline: var(--container-padding-inline); }
}
