/* Motion for the CTA view. Everything here is decoration: with JS off, or with
   prefers-reduced-motion, the page renders in its finished state and nothing moves. */

/* ---- the CTA card's spinning border ---------------------------------------
   A ring of the four product colours turning slowly around the edge of the hero card. The
   card's resting edge is a plain 1px var(--rule-deep) declared in home.css; everything here
   paints ON TOP of that same 1px band and nothing here is load-bearing. Kill any of it and
   the deep static edge is what is left, which is a finished-looking card.

   THE ANGLE IS A REGISTERED CUSTOM PROPERTY, which is new to this codebase and is the one
   piece of this that a browser can decline. An unregistered custom property has no type, so
   it cannot be interpolated and the keyframes below would jump rather than sweep. @property
   gives it <angle>, and on an engine too old to honour it (Firefox <128, Safari <16.4) the
   declaration is dropped, the angle stays at its initial 0deg, and the ring is simply a
   stationary four-colour rim. Benign, because the base rule is already correct without it.
   inherits:false on purpose: this is consumed by the pseudo-element that animates it and
   must not leak into anything nested. */
@property --tw-ring-a{ syntax:'<angle>'; inherits:false; initial-value:0deg }

/* The ORDER of the four stops is a measured result, not a preference, and it is not the order
   you would guess. The adjacency I expected to fail was context beside governance -- green
   beside amber -- and it does not: that pair holds 9.0-12.5 deuteranopic. The one that fails is
   DOCUMENTS. Under dichromacy magenta loses its red component and lands on blue, so docs beside
   models measures 3.4 protanopic and docs beside context 4.7 deuteranopic, both under the floor
   of 8. Documents' only safe neighbour is governance (14.8-16.5), so documents must take
   governance on one side, and of the two remaining evils docs|context beats docs|models. That
   fixes the cycle as models -> context -> documents -> governance. All three candidate 4-cycles
   were scored in all three themes and this one wins in every theme.

   PLATEAUS, NOT BARE STOPS. Each colour is held across a 30deg arc with 60deg ramps between, so
   a colour registers as a band travelling past rather than as one instant in a continuous ramp.
   The fifth stop repeats models so the seam at 360/0 is invisible while the ring turns.

   `in oklab`, and specifically NOT `in oklch`. The sRGB default ramps from #006D56 to #AE3AA1
   through a muddy desaturated midpoint. oklch would fix that too but interpolates the HUE, and
   the shorter arc from context to documents passes through blue -- inventing a rainbow instead
   of cycling the four brand colours. oklab moves through the a/b plane and never visits a hue
   nobody chose.

   THE GRADIENT IS WRITTEN ON THE PSEUDO-ELEMENT ITSELF and not lifted into a custom property
   on .ctahero, which was the first shape of this and was silently broken. A var() inside a
   custom property is substituted on the element the PROPERTY is declared on, so a
   `--tw-ring:conic-gradient(... from var(--tw-ring-a) ...)` sitting on .ctahero resolves the
   angle against .ctahero -- where --tw-ring-a is not set, and is registered inherits:false, so
   it lands on its 0deg initial value. The pseudo then received a gradient with 0deg already
   baked in, the animation ran, and nothing moved. The angle has to be substituted on the same
   element the keyframes are animating. */

/* THE RING GEOMETRY, and the two traps in it.

   inset:-1px, not 0. An absolutely positioned child resolves inset against the containing
   block's PADDING box, so inset:0 would land inside the border and leave the 1px edge showing
   through underneath. At -1px the pseudo's border box is exactly the card's border box, and
   border-radius:inherit is then exactly right rather than approximately -- 22px is the card's
   border-box radius, and the two boxes now coincide.

   The mask is what makes this a ring rather than a wash. .ctahero carries backdrop-filter, which
   forces a stacking context AND makes the card the containing block for this pseudo, so
   z-index:-1 does NOT push the gradient behind the card: within a stacking context, negative-z
   children paint AFTER the root's own background and border. The gradient would cover the entire
   frosted fill. (This codebase has been bitten by the mirror image of that already -- see the
   note on .claim's z-index near the bottom of this file.) The two-layer mask cuts the content
   box back out, leaving only the 1px padding band, so what is painted is precisely the border
   and nothing else. z-index:-1 is still worth having: it keeps the rim under the card's text if
   a long headline ever reaches the edge.

   Declared unconditionally rather than switched on at data-motion="full", which is the house
   pattern here -- the shimmer above does the same. "full" is the ABSENCE of the two kills at the
   bottom of this file, and it has to be, because with JS off there is no data-motion attribute
   at all and a positive selector would never match.

   COST. This is a full-card conic gradient plus a mask, repainted every frame, on top of the
   wave canvas. motion.js measures per-frame cost and can demote the whole page to "reduced" for
   the session, which would cost the reader the shimmer, the door handoff and the live field --
   not just the ring. 16s is slow enough that it stays cheap on the compositor's terms and slow
   enough to read as drift rather than as a spinner; if the auto-demote ever starts tripping on
   this page, lengthen it further or drop the ring before touching anything else. */
.ctahero::before{
  content:"";
  position:absolute;inset:-1px;z-index:-1;pointer-events:none;
  border-radius:inherit;
  padding:1px;
  background:conic-gradient(in oklab from var(--tw-ring-a),
    var(--b-models)    0deg  15deg,
    var(--b-context)  75deg 105deg,
    var(--b-docs)    165deg 195deg,
    var(--b-gov)     255deg 285deg,
    var(--b-models)  345deg 360deg);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  animation:tw-ringspin 16s linear infinite;
}

/* One keyframe, deliberately. `to` alone means the start of the sweep is the property's own
   current value, so if this animation never advances -- reduced motion, the no-JS net's
   .001ms duration, an engine that dropped the @property -- the ring sits at 0deg, which is a
   perfectly ordinary four-colour rim. No fill-mode, ever: `both` is what stranded a section at
   its first keyframe and made it invisible the last time something on this page animated. */
@keyframes tw-ringspin{ to{ --tw-ring-a:360deg } }

/* ---- the announcement capsule: one arrival ring, and nothing else -------------------------
   OPTIONAL, and the smallest thing that could possibly work. Everything that makes .ctapill
   the first thing a reader sees is STATIC and lives in home.css -- opaque fill, product
   colour, a contour that clears 3:1, ink-weight text, one shadow. This adds no prominence. It
   adds a single "and this one is new" beat and then gets out of the way. If it ever draws the
   "too busy" note, deleting the two declarations below is the whole fix and the capsule is
   unchanged.

   WHAT WAS TRIED AND REJECTED, which is the point of this block:
   - an infinite pulse, and a breathing glow. A four-colour ring is already turning around this
     exact card at 16s/turn, and a second perpetual motion INSIDE it is how a hero starts to
     look cheap -- an earlier CTA revision was rejected in precisely those words. A pulse is
     also the notification-badge cliche, which reads as an ad rather than as news, and a
     repeating scale on a ~40px object is the shape of motion vestibular-sensitive readers
     report first. The glow additionally fights the static shadow that is doing the prominence
     work: a lift that keeps changing stops reading as depth.
   - a sheen travelling across the chip. "hardware you own" is already wearing a repeating
     sheen roughly 55px below this, deliberately, as the one phrase on the page emphasised by
     motion alone. A second sheen in the same hero does not add emphasis, it divides it.
   - a perpetual arrow nudge. Same objection. The nudge survives as a HOVER response in
     home.css, where it answers the reader instead of soliciting them.
   - a fade or slide entrance on the capsule. The candidate that sounds most reasonable and is
     the most wrong: this element's entire job is to be seen FIRST, and animating it in delays
     exactly the thing that must not be delayed. A returning visitor gets nothing from it and a
     slow connection gets an empty space where the news should be. What ships is the inverse --
     the capsule is at full opacity and final position from frame zero, and only a ring on top
     of a finished element moves.

   This is tw-newcard's gesture reused rather than a new idea: the platform grid already says
   "this one is new" with a single expanding rim, so the page has a vocabulary for it. Two
   deliberate differences from tw-newcard:

   - NO fill-mode. tw-newcard carries `both` because it holds through a .4s delay. This has no
     delay and needs none, and `both` is the exact declaration recorded in this repo as having
     stranded a section at its first keyframe and made it invisible. Without it, an animation
     that never advances leaves the element's resting style, which is the finished capsule.
   - BOTH endpoints are already the resting appearance: `from` is a 0-spread ring (invisible)
     and `to` is a fully transparent one (invisible). Frozen at either end -- the no-JS net's
     .001ms, a compositor that skips the paint, an engine that drops the keyframe -- what is
     left is the capsule as home.css drew it. Nothing here is load-bearing, same contract as
     the ring above.

   TWO DETAILS THAT ARE NOT COSMETIC:
   - every keyframe RESTATES var(--pill-lift). box-shadow is one property, so a keyframe naming
     only the ring would replace the resting shadow for the animation's duration and the
     capsule would drop flat for 1.15s and then land again. (The known cost of that coupling:
     a hover inside the first 1.15s gets the animated shadow rather than the deeper hover one,
     because animations outrank transitions. 1.15s, once, on first paint -- accepted.)
   - the `to` colour is color-mix(... 0%, transparent) rather than the keyword `transparent`.
     The reason first written here -- that `transparent` is rgba(0,0,0,0) and would drag the
     ring through a grey haze -- is FALSE, and worth leaving corrected rather than deleted,
     because it is the intuitive wrong answer. CSS interpolates colour PREMULTIPLIED by alpha,
     so a zero-alpha endpoint contributes no channels at all: scrubbing a probe that animates
     to the bare keyword gives rgba(31,95,168,.286) at 25% and rgba(31,95,168,.192) at 50% --
     the blue's own hue the whole way down, no grey anywhere. Both spellings are equivalent
     here. The color-mix form is kept only because it names the colour it is fading, which
     reads better next to the `from` stop, not because the keyword would misbehave.
     REJECTED: a hardcoded rgba matching tw-newcard's rgba(15,127,102,.22). That literal is
     MINT, and it would reintroduce the wrong product's colour into the one component this
     whole change exists to re-colour; it also would not follow the theme. color-mix off
     var(--p-models) degrades safely -- an engine that cannot parse it treats that keyframe's
     box-shadow as invalid and drops it, the ring never paints, and home.css's resting shadow
     stands. */
.ctapill{ animation:tw-pill-land 1.15s cubic-bezier(.22,.85,.3,1) }
@keyframes tw-pill-land{
  from{ box-shadow:var(--pill-lift), 0 0 0 0    color-mix(in oklab,var(--p-models) 38%,transparent) }
  to  { box-shadow:var(--pill-lift), 0 0 0 10px color-mix(in oklab,var(--p-models)  0%,transparent) }
}

/* ---- "hardware you own": the emitter words --------------------------------
   The shimmer is a slow sheen travelling left to right across the three words,
   staggered so it reads as one pass rather than three blinks. The matching
   disturbance in the wave field is driven from cta-motion.js, which hands the
   word positions to waves.js as point sources. */
.claim .emit{ white-space:nowrap }
/* The full phrase fits as one animated unit on larger screens. On a phone it is wider than the
   hero plate, so let it break between its inline-block words instead of widening the page and
   clipping the first and last letters. Each word keeps its own shimmer. */
@media(max-width:560px){.claim .emit{white-space:normal}}
.claim .emit .w{
  position:relative;
  display:inline-block;
  /* A shimmer, not a sweep. The gradient REPEATS, so several narrow glints are
     crossing the letters at any moment and the light never leaves; the previous
     single band spent most of its cycle off-screen, which is what made it read
     as an occasional flash rather than a shimmering surface. Linear timing, so
     it never eases to a stop. */
  background-image:repeating-linear-gradient(102deg,
    var(--ink)  0%,  var(--ink)  9%,
    var(--mint) 13.5%,
    #8fe0c6     15.5%,
    var(--mint) 17.5%,
    var(--ink)  22%,  var(--ink) 30%);
  background-size:230% 100%;
  -webkit-background-clip:text;
          background-clip:text;
  -webkit-text-fill-color:transparent;
  animation:tw-shimmer 5.5s linear infinite;
}
.claim .emit .w:nth-child(2){ animation-delay:-1.8s }
.claim .emit .w:nth-child(3){ animation-delay:-3.6s }

@keyframes tw-shimmer{
  from{ background-position:0 0 }
  to  { background-position:-230% 0 }
}

/* ---- the three doors land in the three slots below ------------------------
   The doors do not ghost in place: at .09 they were still painting on top of the
   subtitle, which read as a rendering fault rather than as movement. They now
   leave completely (opacity 0, pointer-events off) and the first three cards of
   the platform grid arrive FROM ABOVE, so the same three things are understood to
   have travelled down into their slots. Documents then joins as the fourth. */
.ctacard{
  transition:opacity .5s cubic-bezier(.4,0,.2,1),
             transform .62s cubic-bezier(.4,0,.2,1),
             border-color .2s;
  will-change:opacity, transform;
}
/* ---- the doors settle ---------------------------------------------------------------------
   The doors used to shrink slightly as the subtitle took focus -- handing over rather than
   leaving. They now do the opposite: as the reader arrives they square up. Reversible, and free:
   .handoff is already toggled off the scroll position by cta-motion.js in both directions, the
   same class the weave and the subtitle ride. (Each door also used to open a second line of
   fact here; the cards carry one line now, so that reveal is gone.) */
.ctarouter.handoff .ctacard{ transform:translateY(-4px); border-color:var(--rule-strong) }
.ctarouter.handoff .ctacard:nth-child(2){ transition-delay:.05s }
.ctarouter.handoff .ctacard:nth-child(3){ transition-delay:.1s }
.ctarouter.handoff .ctacard:nth-child(4){ transition-delay:.15s }


.grid4 .cap{
  transition:opacity .55s cubic-bezier(.22,.85,.3,1),
             transform .62s cubic-bezier(.22,.85,.3,1);
}
/* Slots 1-3 drop in from above, in the same order the doors left. */
html.tw-motion .grid4:not(.arrived) .cap{ opacity:0; transform:translateY(-38px) }
html.tw-motion .grid4.arrived .cap{ opacity:1; transform:none }
html.tw-motion .grid4.arrived .cap:nth-child(1){ transition-delay:.10s }
html.tw-motion .grid4.arrived .cap:nth-child(2){ transition-delay:.18s }
html.tw-motion .grid4.arrived .cap:nth-child(3){ transition-delay:.26s }

/* Documents is the new one. It does NOT come from above with the others: it grows
   in place, later and alone, which is what makes it read as an addition rather
   than as a fourth thing that was always there. */
html.tw-motion .grid4:not(.arrived) .cap:nth-child(4){ opacity:0; transform:translateY(16px) scale(.92) }
html.tw-motion .grid4.arrived .cap:nth-child(4){
  opacity:1; transform:none; transition-delay:.4s;
  animation:tw-newcard 1.1s cubic-bezier(.22,.85,.3,1) .4s both;
}
@keyframes tw-newcard{
  0%   { box-shadow:0 0 0 0 rgba(15,127,102,0) }
  35%  { box-shadow:0 0 0 3px rgba(15,127,102,.22) }
  100% { box-shadow:0 0 0 0 rgba(15,127,102,0) }
}

/* ---- the weave: four doors converging on one line -------------------------------------
   The subtitle claims four problems run as one system. The four doors are directly above it
   and the four pillars directly below, so the claim was sitting between its own evidence and
   saying it in words only. These are the four paths, one per door, in that door's own pillar
   colour, drawn from the bottom of each card down into a single point at the head of the
   subtitle. Four lines becoming one is the sentence, drawn.

   Geometry comes from cta-motion.js because it is measured, not authored: the router is four
   across, then 2x2, then a single column, and a hand-written path would be wrong at two of
   those three. Everything that does NOT depend on measurement lives here.

   Height is 72px, of which 56 came off .ctarouter's bottom padding, so the section grows by
   16px in total. The extra is the curve's: at 56 the outer paths ran 500px sideways for 56px
   down and drew as near-horizontal rules rather than as lines coming down to meet. */
.ctaweave{
  position:relative;z-index:1;
  max-width:1180px;margin:0 auto;padding:0 24px;
  height:72px;pointer-events:none;
}
.ctaweave svg{display:block;width:100%;height:100%;overflow:visible}
.ctaweave path{
  fill:none;stroke-width:1.5;stroke-linecap:round;
  /* Set per path by the script, from the path's own measured length. */
  stroke-dasharray:var(--len);
  stroke-dashoffset:var(--len);
  opacity:0;
}
/* The convergence point. It arrives last, once there is something to converge on. */
.ctaweave circle{ fill:var(--mint); opacity:0; transform-origin:center; }

html.tw-motion .ctaweave path{
  transition:stroke-dashoffset .72s cubic-bezier(.22,.85,.3,1), opacity .32s ease;
}
html.tw-motion .ctaweave circle{ transition:opacity .3s ease .5s }
html.tw-motion .ctaweave.on path{ stroke-dashoffset:0; opacity:.85 }
html.tw-motion .ctaweave.on circle{ opacity:1 }
/* Drawn in the doors' own order, left to right, so the eye follows the same path twice. */
html.tw-motion .ctaweave.on path:nth-child(1){ transition-delay:0s }
html.tw-motion .ctaweave.on path:nth-child(2){ transition-delay:.07s }
html.tw-motion .ctaweave.on path:nth-child(3){ transition-delay:.14s }
html.tw-motion .ctaweave.on path:nth-child(4){ transition-delay:.21s }

/* No JS, or motion turned down: the lines are already there rather than absent. They are
   decoration for a sentence that stands without them, but an undrawn path is a blank gap
   where a diagram should be, which reads as broken rather than as quiet. */
.ctaweave path{ stroke-dashoffset:0;opacity:.85 }
html.tw-motion .ctaweave:not(.on) path{ stroke-dashoffset:var(--len);opacity:0 }
.ctaweave circle{ opacity:1 }
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctaweave path,
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctaweave circle{ transition:none }
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .ctaweave path{ stroke-dashoffset:0;opacity:.85 }
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .ctaweave circle{ opacity:1 }

/* ---- subtitle -> section heading -----------------------------------------
   The element carries its FINAL size natively and starts scaled down, so the
   state the reader ends on is pixel-crisp and only the transient small state is
   interpolated. Transform also keeps this off the layout path: the panels travel
   past it without anything reflowing underneath them. */
.ctasubtitle{
  position:relative;z-index:2;
  max-width:900px;margin:26px auto 0;padding:0 24px;
  text-align:center;text-wrap:balance;
  font-family:var(--sans);font-weight:600;letter-spacing:-.02em;line-height:1.12;
  font-size:calc((clamp(24px,3.4vw,38px)) * var(--ts));
  color:var(--ink);
  transform-origin:50% 0;
  transition:transform .85s cubic-bezier(.22,.85,.3,1),
             color .6s ease,
             margin-top .85s cubic-bezier(.22,.85,.3,1);
}
html.tw-motion .ctasubtitle{
  transform:scale(.46);
  margin-top:14px;
  color:var(--muted);
}
html.tw-motion .ctasubtitle.grown{
  transform:none;
  margin-top:26px;
  color:var(--ink);
}

/* Keyed on data-motion rather than on the media query, so ONE decision (the reader's own
   setting, the system preference, and whether this machine could afford to draw the wave
   field) reaches the stylesheet and the scripts alike. See motion.js.

   :where() carries no specificity, so each of these still weighs exactly what the media-query
   version weighed and still wins purely on coming last, which is what the rules above were
   written against. The two that name <html> compound onto it instead of sitting in front of
   it: html cannot be a descendant of html.

   The media query has not been abandoned (the no-JS net in home.css covers the case where the
   attribute is never written). */
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctasubtitle{ transition:none }
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .ctasubtitle{ transform:none; margin-top:26px; color:var(--ink) }
:where(html[data-motion="reduced"],html[data-motion="off"]) .claim .emit .w{ animation:none; -webkit-text-fill-color:currentColor; background:none }
/* The ring is removed, not frozen. Freezing it would leave the card wearing a stationary
   four-colour rim, which measures 4.69-6.06:1 on the light papers against the 2.42:1 of the
   edge underneath -- so the quiet setting would end up with the LOUDER border, and a reader
   who turned motion down would get a more insistent card than one who left it on. Dropping
   the pseudo hands the card back its var(--rule-deep) edge, which is the state this whole
   thing degrades to everywhere else as well. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctahero::before{ display:none }
/* The capsule's arrival ring, off, so both pieces of hero motion are switched off in the same
   place. Unlike the spinning rim this one would be harmless left declared -- it ends
   transparent either way -- but leaving a 1.15s animation live on an element at
   data-motion="off" is a lie about what that setting does, and the next person reading this
   file should see the two hero animations killed together. The capsule's TRANSITIONS are
   killed in home.css, alongside .ctacard. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctapill{ animation:none }
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctacard,:where(html[data-motion="reduced"],html[data-motion="off"]) .grid4 .cap{ transition:none }
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctarouter.handoff .ctacard{ transform:none }
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .grid4:not(.arrived) .cap,
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .grid4:not(.arrived) .cap:nth-child(4){ opacity:1; transform:none }
html.tw-motion:where([data-motion="reduced"],[data-motion="off"]) .grid4.arrived .cap:nth-child(4){ animation:none }

/* ---- what carries the emphasis once nothing moves -------------------------------------
   One phrase on this page is emphasised BY its motion and by nothing else: the shimmer
   travelling across "hardware you own". Turn the motion off and the rules above do their job,
   but they leave the phrase as plain body text, and the page quietly loses the thing it was
   pointing at. Suppressing an animation is not supposed to suppress what the animation was FOR.

   This used to cover a second phrase, the count retyping itself from Three to Four. That
   typewriter is gone: the heading now reads "Four" from the first paint, so there is no
   transient emphasis left to restate, and .em-mint carries the half of the sentence that
   matters. The .ctasubtitle .cnt half of this selector went with it.

   So at `off` the emphasis is restated as a highlighter. Same --hl the nav links and the
   platform cards paint on hover, same linear-gradient-as-background technique, same --on-hl
   ink on top of it (this is the site's existing highlight sitting still, not a new idea). One
   token, defined once at :root and never themed, so it is the identical yellow in day, dusk
   and night.

   At BOTH still levels, not just `off`. The rules above suppress the shimmer and the typewriter
   the moment the level stops being `full` (reduced and off lose the emphasis identically) so
   scoping the replacement to `off` alone left `reduced` as the one state that took the loss
   without the compensation. What distinguishes reduced from off is the wave field, which
   answers the pointer at one and not the other; it was never the text.

   Painted on .emit rather than on the three .w spans inside it, so the spaces between the
   words are highlighted too and it reads as one marker stroke over one phrase rather than as
   three separate blocks. .emit is already white-space:nowrap, so it can never break across
   lines and the single background is always safe. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .claim .emit{
  background-image:linear-gradient(var(--hl),var(--hl));
  background-repeat:no-repeat;
  background-size:100% 100%;
  background-position:left center;
  color:var(--on-hl);
  /* em, not px: these sit on a heading that ranges from 24px to about 72px, and the 5px the
     hover rules use would be a rim at one end and invisible at the other. The negative margin
     cancels the left padding so the phrase does not shift sideways when the highlight
     appears (the words stay exactly where they were, and only the paper behind them changes). */
  padding:.04em .12em;
  margin-left:-.12em;
  border-radius:.08em;
  -webkit-box-decoration-break:clone;
          box-decoration-break:clone;
}
/* The words inside already had their gradient text-fill undone by the rule above; this makes
   sure they take the highlight's ink rather than re-inheriting the heading's. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .claim .emit .w{ color:var(--on-hl) }

/* ...and the highlight has to sit UNDER the line above it.

   An inline background is painted when its own element is reached in the inline flow, which is
   after every character that precedes it (including the characters on the previous line). This
   heading runs at line-height 1.1, so the descender of the g in "large" hangs down into the row
   below, and the marker stroke was being drawn straight across its tail. It read as a rendering
   fault, which is the one thing a decorative flourish must never do.

   z-index moves the whole highlighted phrase (its ground and its own letters together) into
   the layer that paints between the heading's background and the heading's TEXT. The g belongs
   to that text, so it now lands on top of the marker, which is what a highlighter does on paper:
   the ink was already there and the pen goes underneath it.

   The heading is given a stacking context of its own on the line above, and that part is not
   optional. Without it the -1 resolves against whichever ancestor happens to be a stacking
   context (here the hero card, which is opaque) and the phrase would disappear behind it
   entirely rather than slipping behind three glyphs.

   position:relative on .emit is likewise load-bearing: z-index does nothing to a static box, so
   without it this rule is silently inert. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .claim{ position:relative; z-index:0 }
:where(html[data-motion="reduced"],html[data-motion="off"]) .claim .emit{ position:relative; z-index:-1 }

/* ...and on the dark paper it needs an edge to be seen against.

   Getting the g in front of the marker only solved half of it. On day and dusk the heading is
   near-black ink, so a descender crossing the yellow is the most legible thing on the page. On
   night the heading is near-WHITE, and white on #FFE1A8 is about 1.3:1 (the tail was in front
   of the highlight and still invisible, which looked identical to the bug it replaced).

   A hairline of --on-hl around the glyphs fixes it, and costs nothing anywhere else: --on-hl is
   the ink that belongs ON the highlighter, and everywhere other than the highlight this heading
   sits on the near-black hero card, where a near-black outline cannot be seen at all. So the
   rule reads as "outline the whole heading" and shows up on exactly the three glyphs that need
   it.

   Eight offsets rather than -webkit-text-stroke, which is centred: it eats into the glyph and
   thins a 600-weight face noticeably. These sit entirely outside the letterform. In em, so the
   outline holds its proportion across the heading's clamp from 28px to 47.5px.

   Scoped to night by name. Day and dusk are ink on light paper and must not get this (a dark
   outline around dark text is just a heavier weight). */
:where(html[data-motion="reduced"][data-theme="night"],html[data-motion="off"][data-theme="night"]) .claim{
  text-shadow:
     .028em 0 0 var(--on-hl), -.028em 0 0 var(--on-hl),
     0 .028em 0 var(--on-hl),  0 -.028em 0 var(--on-hl),
     .02em .02em 0 var(--on-hl), -.02em -.02em 0 var(--on-hl),
     .02em -.02em 0 var(--on-hl), -.02em .02em 0 var(--on-hl);
}
/* The highlighted words are already --on-hl on the yellow. Outlining dark ink with the same
   dark ink is just a bolder weight, so they opt out, and inheritance carries this to the .w
   spans inside without naming them. */
:where(html[data-motion="reduced"][data-theme="night"],html[data-motion="off"][data-theme="night"]) .claim .emit{ text-shadow:none }

/* The shimmer is the one piece of decoration on this page that loops forever in pure CSS, so
   it is the one piece the no-JS net in home.css has to stop on its own. That net can only
   freeze the animation, which would leave these words wearing a gradient they can no longer
   move (transparent text-fill over a static sheen). This hands them their plain ink back. */
@media (prefers-reduced-motion:reduce){
  html:not([data-motion]) .claim .emit .w{
    -webkit-text-fill-color:currentColor; background:none;
  }
  /* Same argument for the ring, and for the same reason the net cannot make it on its own: the
     net freezes animations, and a frozen ring is a heavier border than the one it covers. With
     scripts off and the OS asking for less motion, the card gets its deep static edge. */
  html:not([data-motion]) .ctahero::before{ display:none }
  /* The capsule's arrival ring needs no rule here, and that omission is a property of how it
     was written rather than an oversight -- do not "fix" it. The net in home.css can only
     FREEZE an animation (.001ms, iteration-count 1), which is fatal for the shimmer
     (transparent text-fill over a sheen that can no longer move) and for the ring above (a
     frozen four-colour border that is louder than the edge it covers). tw-pill-land has no
     such failure state: both its keyframes are visually identical to rest -- a 0-spread ring
     and a 0-alpha ring -- and it carries no fill-mode, so freezing it, snapping it to the end,
     or never running it at all all produce the same finished capsule. Choosing an animation
     that is safe if it never advances is what buys the exemption. */
}
