/* Contrast · hero C in five grounds (heroes-c.html). Loaded after heroes.css, whose .roll, .ruler, .tick and .dot it
   reuses. A ground sits in .bg, behind the type; the dark and violet ones recolour the hero by resetting the colour
   variables on .body, and use the panel's padding as --m so the ruler runs to the panel's own edges. */
.cx .body{position:relative;display:grid;grid-template-rows:1fr auto auto;isolation:isolate}
.cx .bg{position:absolute;inset:0 calc(-1 * var(--m));z-index:-1;overflow:hidden;pointer-events:none}
.cx h1{align-self:center;font-size:clamp(3.7rem,10.6vw,10rem)}
.cx .under{display:grid;gap:14px var(--g);padding-top:22px;align-items:end}
@media (min-width:900px){ .cx .under{grid-template-columns:7fr 5fr} .cx .under .mono{text-align:right} }
.cx .roll>span{transform:translateY(-80%)}                              /* five slots, the first one empty: at rest it shows the last */
.cx .ruler{z-index:auto}                                               /* no layer of its own: what sits behind it (grid, glow, rings) stays behind the type */
.cx .fill{position:absolute;top:-1px;left:0;width:calc(100% - var(--m));height:1px;background:var(--stop)}   /* the way already travelled */
.cx .glow,.cx .rings,.cx .ping{display:none}
/* the tick labels: the first starts on its tick; on a phone only the two ends are named (the word in the headline
   names the stages in between). heroes.css counts ticks by position, which no longer holds here: the ruler also
   carries the grid and the travelled line, so the ticks are picked by their own --x */
.cx .tick[style^="--x:0;"] span{transform:none}
@media (max-width:600px){ .cx .tick span{display:none} .cx .tick[style^="--x:0;"] span,.cx .tick[style^="--x:1;"] span{display:block} }
.cx .panel{--m:clamp(18px,2.8vw,44px);padding:clamp(18px,2.8vw,44px) var(--m) var(--m)}

/* 2026-11-14 (Werner): the sentence sits under the headline, a size up, and the timeline lower: only the small note
   is left beneath it, at the right */
.cx .lead-in{align-self:center;display:grid;gap:clamp(18px,2.4vw,34px)}
.cx .lead-in .lede{font-size:clamp(1.3125rem,2vw,1.8125rem);line-height:1.2;letter-spacing:-.022em;max-width:23em;text-wrap:balance}
/* "over 10 years" (2026-11-14) made the sentence a word longer: on a wide screen it gets the room for two lines
   again; below that it is three even ones, which keep clear of the portrait */
@media (min-width:1280px){ .cx .lead-in .lede{max-width:27.5em} }
.cx .under{grid-template-columns:minmax(0,1fr);padding-top:18px}
@media (min-width:900px){ .cx .under{grid-template-columns:minmax(0,1fr)} }

/* 1 · grid. The grid is a child of the ruler, so it is drawn from the timeline outwards: one of its horizontal lines
   IS the timeline, and every eighth vertical line meets a tick. A square is 1/32 of the ruler's length (1/16 on a
   phone), set in cqw: the section is the container and its content box is exactly the ruler's travel. The square
   under the pointer gets a .lit (js/heroes.js), which glows and fades. */
.cx-1{container-type:inline-size;--c:3.125cqw;--cols:32}                /* --cols: squares across the ruler */
@media (max-width:760px){ .cx-1{--c:6.25cqw;--cols:16} }
.cx-1 .grid{position:absolute;left:0;right:0;top:-1201px;height:2400px;z-index:-1;pointer-events:none;
  background:linear-gradient(var(--line) 1px,transparent 1px) var(--m) 1200px / var(--c) var(--c),linear-gradient(90deg,var(--line) 1px,transparent 1px) var(--m) 1200px / var(--c) var(--c);
  -webkit-mask-image:radial-gradient(ellipse 74% 30% at 56% 50%,#000 30%,transparent 100%);mask-image:radial-gradient(ellipse 74% 30% at 56% 50%,#000 30%,transparent 100%)}
.cx-1 .grid .pin{left:calc(var(--m) + var(--c) * var(--i));top:calc(1200px + var(--c) * var(--j))}
/* 2026-11-15 (Werner): the grid does not end on a hard edge. The hero clips what reaches past its foot (heroes.css:
   overflow:clip), 146px under the timeline at every width, and the grid was still at full strength there, so its last
   row was cut straight across. A wash of the page's own colour lies over the grid's last 110px (from just under the
   tick labels to the edge), behind the type: the grid, its glow and the wave fade out before the edge */
.cx-1 .body::after{content:"";position:absolute;z-index:-1;left:calc(-1 * var(--m));right:calc(-1 * var(--m));bottom:-30px;height:110px;pointer-events:none;
  background:linear-gradient(180deg,rgba(241,240,243,0),rgba(241,240,243,.5) 35%,rgba(241,240,243,.86) 68%,var(--paper))}
/* the pins that come and go (js/heroes.js): a dot at a crossing fades in over 3.2s to a low peak and out again. With motion
   allowed the two fixed pins above give way to them */
.cx-1 .grid .pin.wink{opacity:0;transform:none;transition:opacity 3.2s ease-in-out}
.cx-1 .grid .pin.wink.up{opacity:.6}
@media (prefers-reduced-motion:no-preference){ .js .cx-1 .grid>.pin:not(.wink){display:none} }
.cx-1 .lit{position:absolute;width:var(--c);height:var(--c);background:rgba(124,107,255,.2);box-shadow:0 0 30px 8px rgba(124,107,255,.42),inset 0 0 0 1px rgba(124,107,255,.6);animation:lit 1.3s ease-out forwards}
@keyframes lit{from{opacity:1}to{opacity:0}}
.cx.on .pin{transform:none}
/* the portrait (2026-11-14, Werner): on the right, standing on the timeline and centred on the Tested tick. It is an
   even number of squares wide (16 at most, fewer on a low or a very wide window), so its edges are grid lines. Unseen until the
   squares over it light up: js/heroes.js lays a .px on each of those squares, a lit square holding its own piece of
   the picture (--x, --y: the square, counted from the portrait's top left; --n: squares per side; --img: the file).
   A soft light (.halo) travels with the pointer behind them. Without JS, or with reduced motion, the picture simply
   shows. Not beside the headline on a narrow screen: it needs the room. There (2026-11-15, Werner: on mobile, the
   picture behind the text, revealed only by the ripple after the timeline has loaded) the portrait stands on the
   timeline behind the type, as wide as the ruler on a phone (the right half of it on a tablet), and nothing shows it
   but the wave: js/heroes.js leaves the touch brush off. With reduced motion, or without JS, there is no wave, so
   there it stays away rather than sit behind the words. */
.cx-1 .who{display:none}
@media (min-width:900px){
  .cx-1 .who{display:block;position:absolute;z-index:-1;left:calc(var(--m) + var(--c) * 24);bottom:calc(100% + 1px);width:calc(var(--c) * 12);aspect-ratio:1;transform:translateX(-50%);pointer-events:none}
  @supports (width:round(down,1px,1px)){ .cx-1 .who{width:round(down,min(calc(var(--c) * 16),calc(100svh - 250px),720px),calc(var(--c) * 2))} }
}
@media (max-width:899px) and (prefers-reduced-motion:no-preference){
  .js .cx-1 .who{--w:calc(var(--c) * 16);display:block;position:absolute;z-index:-1;bottom:calc(100% + 1px);width:var(--w);aspect-ratio:1;pointer-events:none;
    left:calc(var(--m) + var(--c) * (var(--cols) - 16) + (var(--c) * 16 - var(--w)) / 2)}
  @supports (width:round(down,1px,1px)){ .js .cx-1 .who{--w:round(down,min(calc(var(--c) * 16),calc(100svh - 250px)),calc(var(--c) * 2))} }
}
.cx-1 .who img{display:block;width:100%;height:100%}
.js .cx-1 .who img{opacity:0}
.cx-1 .who .px{position:absolute;left:calc(var(--c) * var(--x));top:calc(var(--c) * var(--y));width:var(--c);height:var(--c);opacity:0;
  background-color:var(--lav);background-repeat:no-repeat;
  background-image:linear-gradient(rgba(124,107,255,.1),rgba(124,107,255,.1)),var(--img);
  background-size:auto,calc(var(--c) * var(--n)) calc(var(--c) * var(--n));
  background-position:0 0,calc(var(--c) * var(--x) * -1) calc(var(--c) * var(--y) * -1);
  box-shadow:inset 0 0 0 .5px rgba(124,107,255,.34);transition:opacity 1.4s ease-out}
.cx-1 .who .px.on{opacity:var(--o,1);transition-duration:.16s}
.cx-1 .who .halo{position:absolute;left:0;top:0;width:calc(var(--c) * 12);height:calc(var(--c) * 12);margin:calc(var(--c) * -6);border-radius:50%;
  background:radial-gradient(closest-side,rgba(124,107,255,.36),rgba(124,107,255,.12) 60%,rgba(124,107,255,0));opacity:0;transform:translate(var(--hx,0),var(--hy,0));transition:opacity .6s ease-out}
.cx-1 .who.near .halo{opacity:1;transition-duration:.25s}
@media (prefers-reduced-motion:reduce){ .js .cx-1 .who img{opacity:1} }
/* the wave that runs over the grid when the dot lands (js/heroes.js): one canvas over the hero, behind the type */
.cx-1 .wave{position:absolute;left:0;z-index:-1;pointer-events:none}

/* 2 · dots and light */
.cx-2 .bg{background:radial-gradient(circle at 1.5px 1.5px,var(--faint) 1.3px,transparent 1.8px) 0 0 / 22px 22px;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 30%,#000 100%);mask-image:linear-gradient(180deg,transparent 0,#000 30%,#000 100%)}
.cx-2 .glow{display:block;position:absolute;top:0;left:calc(100% - var(--m));z-index:-1;width:640px;height:640px;margin:-320px;border-radius:50%;background:radial-gradient(closest-side,rgba(124,107,255,.42),rgba(124,107,255,.14) 55%,rgba(124,107,255,0))}

/* 3 · fluted violet */
.cx-3 .body,.cx-5 .body{--m:clamp(18px,2.8vw,44px);padding:clamp(18px,2.8vw,44px) var(--m) var(--m);color:#fff;--ink:#fff;--grey:rgba(255,255,255,.74);--faint:rgba(255,255,255,.42);--line:rgba(255,255,255,.3)}
.cx-3 .body{--stop:#15111f}
.cx-3 .bg,.cx-5 .bg{inset:0;border-radius:var(--r)}
.cx-3 .bg{background:radial-gradient(70% 60% at 16% 10%,#b0a5ff 0,rgba(176,165,255,0) 70%),radial-gradient(80% 70% at 88% 94%,#4330cf 0,rgba(67,48,207,0) 70%),#7c6bff}
.cx-3 .bg::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,rgba(255,255,255,.2) 0,rgba(255,255,255,0) 34%,rgba(18,16,22,0) 64%,rgba(18,16,22,.2) 100%) 0 0 / calc(100% / 30) 100%}
.cx-3 .lede,.cx-5 .lede{color:#fff}

/* 4 · rings */
.cx-4 .rings{display:block;position:absolute;top:0;left:calc(100% - var(--m));z-index:-1;width:2400px;height:2400px;margin:-1200px;border-radius:50%;
  background:repeating-radial-gradient(circle,transparent 0 63px,var(--line) 63px 64px);-webkit-mask-image:radial-gradient(circle,#000 8%,transparent 62%);mask-image:radial-gradient(circle,#000 8%,transparent 62%)}
.cx-4 .ping{display:block;position:absolute;top:0;left:calc(100% - var(--m));width:128px;height:128px;margin:-64px;border-radius:50%;border:1px solid var(--stop);opacity:0}

/* 5 · ink columns */
.cx-5 .bg{background:var(--night);--w:calc(100% - 2 * var(--m))}
.cx-5 .col{position:absolute;top:0;bottom:0;left:calc(var(--m) + var(--w) * .25 * var(--k));width:calc(var(--w) * .25);border-left:1px solid rgba(255,255,255,.1);background:linear-gradient(180deg,rgba(124,107,255,0) 20%,rgba(124,107,255,.3) 100%)}
.cx-5 .col:last-child{border-right:1px solid rgba(255,255,255,.1)}

/* ── the timeline ───────────────────────────────────────────────────────────
   5.2s after a .5s wait: four legs of 1.3s (25% each). On a leg the dot travels for .95s (18.25% of the whole) on an
   ease-out, then rests. The easing is written out in each keyframe (cubic-bezier(.25,.1,.25,1), the browser's own
   "ease"): a var() there is ignored, so the --flow curve this file once pointed at never applied, and "ease" is what
   was always on screen. On that curve the dot is 54% of the way after .3s, 87% after .55s, 96% after .7s. The word
   rolls to meet it: it starts .4s into the leg (7.69% of the whole) and takes .4s, so it is mostly changed by the time
   the dot is nine tenths there (.65s) and in place as it lands (2026-11-15, Werner: the word changed after the dot
   had arrived; it used to start .8s in). The tick's label turns dark in step (--t, set per tick in heroes-c.mjs: LAND). */
@media (prefers-reduced-motion:no-preference){
  .cx{--flow:cubic-bezier(.22,1,.36,1)}
  .js .cx:not(.on) .roll>span{transform:none}
  .js .cx:not(.on) .ruler .dot,.js .cx:not(.on) .glow{left:var(--m)}
  .js .cx:not(.on) .fill{width:var(--m)}
  .js .cx:not(.on) .tick span{color:var(--faint)}
  .js .cx-5:not(.on) .col{opacity:0}
  .cx.on .roll>span{animation:croll 5.2s .5s both}
  .cx.on .ruler .dot,.cx.on .glow{animation:crun 5.2s .5s both}
  .cx.on .fill{animation:cfill 5.2s .5s both}
  .cx.on .tick span{animation:clit .5s var(--flow) var(--t) both}
  .cx.on .tick{animation:ctick .5s var(--flow) var(--t) both}
  .cx-5.on .col{animation:ccol 1.1s var(--flow) var(--t) both}
  .cx-4.on .ping{animation:cping 1.5s var(--flow) 5.05s both}
  @keyframes crun{
    0%{left:var(--m);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    18.25%,25%{left:calc(var(--m) + var(--w) * .25);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    43.25%,50%{left:calc(var(--m) + var(--w) * .5);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    68.25%,75%{left:calc(var(--m) + var(--w) * .75);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    93.25%,100%{left:calc(100% - var(--m))}
  }
  @keyframes cfill{
    0%{width:var(--m);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    18.25%,25%{width:calc(var(--m) + var(--w) * .25);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    43.25%,50%{width:calc(var(--m) + var(--w) * .5);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    68.25%,75%{width:calc(var(--m) + var(--w) * .75);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    93.25%,100%{width:calc(100% - var(--m))}
  }
  @keyframes croll{
    0%,7.69%{transform:translateY(0);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    15.38%,32.69%{transform:translateY(-20%);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    40.38%,57.69%{transform:translateY(-40%);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    65.38%,82.69%{transform:translateY(-60%);animation-timing-function:cubic-bezier(.25,.1,.25,1)}
    90.38%,100%{transform:translateY(-80%)}
  }
  @keyframes clit{from{color:var(--faint)}}
  @keyframes ctick{from{height:6px;background:var(--faint)}}
  @keyframes ccol{from{opacity:0}}
  @keyframes cping{0%{opacity:.9;transform:scale(.1)}100%{opacity:0;transform:scale(3.2)}}
}
