/* The explorer: the reference site's overlapping card cascade.
   Shared by the two pages that ask a reader to locate themselves — the front door
   (six situations) and the arc (seven stages). It was inline on the arc page until
   the front door needed it too; nothing in it was ever specific to stages.

   Geometry comes entirely out of --i, --sel and --n, so a field of six and a field
   of seven are the same code with a different --n. Nothing measures the viewport. */
/* Registered, not merely declared: a plain custom property cannot be transitioned.
   --esc is the entrance's, and nothing else writes it. It rides at the end of `transform`;
   the scroll lag rides on `translate`. Four jobs, four owners, no shared clock: the deck's
   place in the fan, the size it arrives at, the size the pointer lends it, and how far it
   trails the wheel.

   --hov is the pointer's, a 1.01 scale on the card under it. It was removed once on the
   finding that the reference does not move its card box on hover; that finding measured
   the wrong element and the note at the hover rule below records what the right one does.

   --esc is the entrance's scale. It was once removed on the finding that "the reference does
   not scale its cards on entry at all", which is false and was measured wrong. Sampled from
   before their own script runs: every .fancy_card holds transform matrix(0.5,0,0,0.5,0,0)
   from 200ms to 1000ms, expands to matrix(1.0443,...) at 1503ms and settles at 1.0 by 1853ms,
   each card 100ms behind the one above it. Opacity is 1 the whole time - there is no fade.
   The scale sits INSIDE `transform`, after the fan's own translate, and it has to: CSS applies
   the standalone `scale` property outside `transform`, which would scale the fan's offsets
   too and close the deck's spacing up. Theirs holds 150px between cards at half size and at
   full size alike, which is what tells you where their scale is written.

   There used to be a fourth, --vel: a scroll-velocity smear the loop wrote to all seven
   cards on every frame, sliding each card's interior inside its own frame. It is gone.
   It was the only thing on this page writing style during a scroll, and because it fed
   a transform through a registered custom property it could not take the compositor
   path either: seven main-thread interpolations a frame, for nine pixels of lag nobody
   asked for. On the same scripted scroll it cost 325 style recalculations against 27 on
   a page with no deck. The reference site the deck is adapted from has no such effect;
   its cards do not move relative to the page at all. */
@property --esc{syntax:"<number>"; inherits:false; initial-value:1}
/* The hover swell. Registered so it interpolates as a number and so it can be
   transitioned on its own: `transform` carries the deck's position, explorer.js
   animates that on one timeline, and a transition declared on the whole property
   would fight it every frame the deck moves. */
@property --hov{syntax:"<number>"; inherits:false; initial-value:1}
/* The deck's move, and only the move. Every rest position below stays written in CSS, in
   `transform`, once per state; explorer.js animates the offset between two of them on these
   two properties and never writes `transform` itself. Registered so they interpolate as
   lengths rather than as strings, and so a card that JS never touches still has a value. */
@property --fx{syntax:"<length>"; inherits:false; initial-value:0px}
@property --fy{syntax:"<length>"; inherits:false; initial-value:0px}
body{font:400 23px/var(--leading-prose) var(--font-body)}
a{color:var(--ink-line)}
:is(a,button):focus-visible{outline:2px solid var(--ink-line); outline-offset:3px; border-radius:var(--radius)}

/* How far a section may reach past the 1180px measure before it hits the 24px gutter at
   the viewport edge. Named here because three things use it and they must agree: the
   deck, the arc, and the full index. The reference's own answer to this is absolute -
   its cards, both its section right columns and its footer all end at 1512, the viewport
   edge, with no exception anywhere on the page. Ours ended at 1488 for the deck and 1306
   for everything under it, which left 182px of nothing down the right of the bottom two
   thirds and is what made the sections read as belonging to a different page. */
/* .wrap and .k are in tokens.css. */

/* ---------- the frame ---------- */
/* The bar stays put. Every other page's bar already does — .upnav is sticky in
   hatch.css — and the front page was the one place it scrolled away, which is also the
   page where the menu is most likely to be wanted. The reference site's bar does not
   move by a single pixel across a full-page scroll: the logo, the field and the menu
   read 0 on every notch of a 24-notch trace. That fixed bar is half of why their scroll
   reads as calm; the deck moving under something that holds still is what gives the
   movement a reference point. 50 clears the deck, whose cards reach 41. */
header{
  position:sticky; top:0; z-index:50;
  background:var(--ground); border-bottom:1px solid var(--rule);
}
/* The bar's own height, for anything that comes to rest underneath it. A sticky element
   cannot ask how tall a sticky sibling is, so this is a number copied from a measurement,
   and it lives beside the rule that produces it: 16px of padding either side of the control
   row, plus the 1px rule. It is the same 70px at every width this matters at and at every
   viewport height, because only the horizontal padding is fluid.

   Every sheet that owns a top bar declares this, and a page loads exactly one such sheet
   plus hatch.css, whose .upnav is the other one — so on the front page the two definitions
   both apply and this wins on source order, Base.astro loading the page's own sheets after
   the shared ones. M6 in geometry-check.mjs is what makes that safe to rely on: it reads
   the bar a page actually renders and fails if anything rests inside it, which is what both
   a stale number and a wrong resolution order look like from the reader's side. */
:root{--bar:70px}
/* Direct child only. As `header div` it also matched anything nested inside the row, so
   the map's mount picked up the row's own max-width, padding and min-height and grew to
   235x72 around a 155x40 button. */
header > div{
  max-width:1180px; margin:0 auto; padding:16px clamp(22px,4vw,40px);
  display:flex; align-items:center; gap:16px 26px; flex-wrap:wrap; min-height:64px;
}
.mark{
  font:500 13px/1 var(--font-mono); letter-spacing:var(--track-label);
  text-transform:uppercase; color:var(--ink-line); text-decoration:none; margin-right:auto;
}
.closer{
  font:500 13px/1 var(--font-mono); letter-spacing:var(--track-label); text-transform:uppercase;
  background:none; border:1px solid var(--ink-line); color:var(--ink-line);
  padding:10px 15px; border-radius:var(--radius); cursor:pointer;
}
.closer:hover{background:var(--raised)}
html:not(.js) .closer{display:none}
/* The closer arrives the way theirs does, taken literally rather than adapted. Measured on
   their live site: .close_fancydetail rests at matrix(0,0,0,0,20,0) — translateX(20px)
   rotate(180deg) scale(0) — and on open runs `transform 1s cubic-bezier(.8,0,.2,1.4) .25s`,
   still at rest through 250ms, unwinding through 119.5deg at 700ms, overshooting to 1.07 at
   1000ms and settled by 1400ms.

   The curve is theirs, not --ease-back. --ease-back lands 1.14 because a reveal that happens
   to a reader over 75 should not bounce 10%; this is a control the reader summoned by opening
   a card, and matching it was asked for explicitly. It is written here rather than tokenised
   because one element uses it, and a token would invite a second.

   A half-turn is exactly what prefers-reduced-motion exists for, so a reader who asked for
   less gets the button with no entrance at all. */
@media (prefers-reduced-motion:no-preference){
  .closer{
    transition:transform var(--t-reveal) cubic-bezier(.8,0,.2,1.4) .25s;
    @starting-style{transform:translateX(20px) rotate(180deg) scale(0)}
  }
}

/* ---------- the rail: everything a reader needs before choosing ---------- */
.explorer{padding:clamp(30px,4vw,52px) 0 0}
.rail h1{
  font:400 clamp(34px,6vw,58px)/1.05 var(--font-display); letter-spacing:var(--track-display);
  margin:0 0 20px; text-wrap:balance;
}
.rail .lede{font:400 clamp(19px,2.2vw,22px)/1.5 var(--font-display); color:var(--muted); margin:0 0 26px; max-width:var(--measure-lead)}
.rail .lede b{color:var(--ink); font-weight:400}
/* A button, not an underlined line of prose with an arrow after it. The reference site's
   own .btn is a pill: inline-block, generous padding, and a fill that arrives on hover.
   Ours is filled from the start instead of outlined, because a hairline may never bound
   anything clickable here. */
.more{
  display:inline-block; background:var(--ink); color:var(--ground);
  border:0; border-radius:999px; padding:15px 30px;
  font:500 17px/1 var(--font-body); text-decoration-line:none;
  transition:background var(--t-micro) var(--ease-quick);
}
.more:hover{background:var(--ink-line)}
.more:focus-visible{outline:2px solid var(--ink); outline-offset:3px}

/* ---------- a stage, before any of the interactive geometry ----------
   This is what a crawler, a reader with JavaScript off, and the pre-hydration
   paint all get: seven readable sections in order. The explorer is layered on
   top of it, and only ever one copy of the content is in the DOM. */
.field{--n:7; --sel:0}
.stage{
  border:1px solid var(--ink-line); border-radius:var(--radius);
  margin:0 0 22px; background:var(--ground); position:relative; overflow:hidden;
}
/* The stage's stop, run down the card's own left edge. It used to be a block shadow
   cast behind the card, which at a 24px sideways step piled seven stripes into one
   column with nothing to attach them to. Inside the border it reads as part of the
   card, and it is the brand's construction anyway: an accent fill within intact ink. */
.stage::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:13px;
  background:var(--stop); z-index:1; transition:background var(--t-micro) var(--ease);
}
/* The bar lives at the card's foot, which the next card covers all but a sliver of.
   The spine says the same thing in the part of the card a reader can actually see. */
html.js .explorer[data-open="false"] .field .bar{display:none}
.hit{
  z-index:2;
  display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-start;
  width:100%; text-align:left; background:none; border:0;
  font:inherit; color:inherit; cursor:pointer; position:relative;
  padding:clamp(20px,2.4vw,28px) clamp(22px,2.6vw,32px) clamp(30px,3vw,38px) clamp(38px,4vw,48px);
}
/* flex:none on both. The card is a fixed-height flex column and its content is taller
   than the visible band by design, so the browser was shrinking whatever would give:
   the number circle flattened to a 19px ellipse and pulled the title up into the mark
   on every card with a three-line title. Nothing in the head of the card may shrink. */
.num{
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  width:46px; height:46px; border:1px solid var(--ink-line); border-radius:100%;
  font:500 17px/1 var(--font-mono); color:var(--ink-line); letter-spacing:normal;
}
.nm{display:block; flex:none; font:400 clamp(25px,2.4vw,32px)/1.16 var(--font-display);
  letter-spacing:var(--track-display); margin:16px 0 10px; max-width:20ch}
/* ---------- the mark ----------
   One drawing per stage, in the same register as the panel's graph paper: circles,
   rectangles, straight lines and arcs, nothing freehand. Areas carry the stage's ramp
   stop, line work is always ink. That is the brand's own construction, and it also
   gives the reference's solid/line pair for free — draining the fill leaves the outline
   standing, so there is one copy of the geometry rather than two stacked layers. */
.explorer .mk{flex:1 1 auto; align-self:stretch; min-height:110px; max-height:200px; margin:20px 0 0}
.explorer .mk svg{display:block; width:100%; height:100%; overflow:visible}
.explorer .mk .a{fill:var(--stop); stroke:var(--ink-line); stroke-width:2.5; transition:fill var(--t-state) var(--ease)}
.explorer .mk .s{fill:none; stroke:var(--ink-line); stroke-width:2.5; stroke-linecap:round; stroke-linejoin:round}
/* Position on the arc, drawn as a fill. One seventh at the first approach,
   the full width at the silence. Not a progress track: there is nothing behind it. */
.bar{
  position:absolute; left:0; bottom:0; height:8px;
  width:calc((var(--i) + 1) / 7 * 100%); background:var(--stop);
}

/* ---------- what is inside a stage, as cards ----------
   The reference does not put a list of links in an opened panel. It puts one card per
   topic, and the card is two boxes: a filled block, and a white panel carrying the same
   ink border, sitting on the block and offset up and to the right so the block shows as
   an L along the left and bottom edges. Hover pulls the panel back down toward the block
   rather than lifting it away, so the card presses under the pointer instead of floating.
   Nothing in the construction is a shadow, which is why it survives FILL ONLY.

   Their block colour is per topic. Ours is the stage's own stop, so the cards read as one
   stage rather than eight unrelated things, and the panel keeps to a single accent.

   The cards are the body's own children rather than sitting in a wrapper, because the
   stagger that lights them one at a time is written against `.stage-body > *`. A wrapper
   would make that one element and the whole grid would arrive at once.

   The offset panel escapes its cell 20px up and 20px to the right, so the gap a reader
   sees is not the gap declared here — it is the gap less that 20. At 30 and 36 the gap
   cleared the offset and almost nothing else: 11px of daylight between one card's white
   face and the next card's block, and 17px between rows, on a page whose whole subject is
   that things are easier to take in when they are apart. The reference leaves 46 and 40 at
   the same 20px offset, because its gaps are 65 and 60. Ours are 50 now, which is 31 seen,
   and the panel's side padding pays for them rather than the card: at 40 instead of 56 the
   card comes out 176 against the 179 it was, so no title wraps that did not wrap before.
   The gaps are equal on both axes because there is no reason for them not to be; theirs
   differ by 5px and nothing depends on it. */
.stage-body{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  column-gap:50px; row-gap:50px;
  padding:20px calc(clamp(22px,2.6vw,32px) + 22px) clamp(26px,3vw,36px) clamp(38px,4vw,48px);
}
/* Grid rather than block so the panel stretches to the row. A percentage height against a
   parent that has only a min-height resolves to auto, and the card collapses onto its text.

   Three across is theirs taken as it stands. The minimum above is the widest one that still
   fits three columns in a 598px panel; a smaller number would fit a fourth, not a wider
   third, because auto-fill packs as many as the minimum allows. It buys the count at 179px
   a card against their 220.

   Their 300px card floor came with it and has now gone, because the reason given for taking
   it was not true of every door. It was taken on the grounds that our titles are step
   instructions where theirs are chapter names, running four to seven lines rather than one,
   so the floor would rarely bind. Measured across all seven doors that holds for six of
   them: their cards stand 207 to 322px on their own and the floor barely touched them. Door
   one is the exception, because its cards are stage names — "The approach", "The hook" — one
   line each and 150px tall, which the floor then doubled into a box that was nine tenths
   empty. A number worth taking from the reference is one their content and ours both earn;
   this one only theirs did.

   What replaces it is a floor our own content earns: a round number of lines of the 23px
   title, plus the padding, the gap and the icon, which resolves to one height at every
   width the card is served at because only the line count varies. Sized purely to content
   the cards came out in six different shapes, and door one's came out landscape - wider
   than tall, where every other card on the site is portrait.

   The number of lines is read off what the cards actually hold, so it moves when they do.
   It was four, at 236px, while a door showed four hand-picked steps and a link reading
   "the 13 steps not listed here". A door now lists every step it holds, and those are the
   pages' own headings: the longest run six lines at 176px wide, so the floor is six, at
   294px. 236 + 2 x 28.75, the same padding, gap and icon, and nothing else moved. Under
   the old number M7 reported 37 cards standing over their shape - not because any title
   is too long, but because the shape was measured against a shorter list. Raising it also
   restores what M7 is for: a grid row stretches to its tallest card either way, so at 236
   the short rows sat 58px under the tall ones and the panel held two shapes again. */
.sec{
  display:grid; min-height:294px; background:var(--stop);
  border:1px solid var(--ink-line); border-radius:var(--radius); text-decoration-line:none;
}
.sec .info{
  background:var(--ground); border:1px solid var(--ink-line); border-radius:var(--radius);
  box-sizing:border-box; padding:17px 19px 19px; transform:translate(20px,-20px);
  display:flex; flex-direction:column; justify-content:space-between; gap:18px;
  transition:transform var(--t-state) var(--ease-press);
}
.sec:hover .info, .sec:focus-visible .info{transform:translate(10px,-10px)}
.sec .t{font:400 23px/1.25 var(--font-display); color:var(--ink); text-wrap:balance}
/* Centred by align-self rather than by their 0 56.5px margin, which is that number only
   because their card is 220 wide. Ours is 179 and the same margin would push it off. */
.sec .ic{
  width:63px; height:63px; flex:none; align-self:center;
  border:1px solid var(--ink-line); border-radius:100%;
  background:center/29px no-repeat url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%232f6470%22 stroke-width=%221.3%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22><path d=%22M13.2 3.8H6.4a1 1 0 0 0-1 1v14.4a1 1 0 0 0 1 1h6.8%22/><path d=%22M8.4 8.6h4.2M8.4 12h4.2M8.4 15.4h2.6%22/><path d=%22M14.4 12h5.4M17.1 9.3 19.8 12l-2.7 2.7%22/></svg>");
}

/* ---------- interactive, at every width ---------- */
html.js .explorer[data-open="false"] .stage-body,
html.js .explorer[data-open="true"] .stage .stage-body{display:none}
html.js .panel{margin:0}
.panel{container-type:inline-size}

/* The opened panel fades in. Theirs runs opacity 0.4s on cubic-bezier(0,.5,.2,1), which is
   --ease-quick exactly — the token was taken from this transition. The duration is rounded to
   the state token rather than given a fourth of its own: 100ms on an opacity fade is not
   visible, and a fourth duration in the file would be.

   @starting-style because the panel arrives from display:none, and without a start value
   there is nothing to interpolate from and the fade never runs.

   It leaves the same way. Their detail holds its whole box while it goes: measured opacity
   1 -> 0.426 at 60ms -> 0 by 450ms with the box still 1530px tall, collapsing to 450px only
   at 1200ms. So data-closing keeps the panel's own rules matching for one --t-state while
   the body fades, and explorer.js moves it back to its card at the end of that. Ours moved
   it back on the same tick, which is why the panel vanished rather than left. */
@media (prefers-reduced-motion:no-preference){
  html.js .explorer:is([data-open="true"],[data-closing="true"]) .panel .stage-body{
    transition:opacity var(--t-state) var(--ease-quick);
    @starting-style{opacity:0}
  }
  html.js .explorer[data-closing="true"] .panel .stage-body{opacity:0}

  /* Their detail does not arrive as one block. The wrapper fades, and then the cards inside
     it light one at a time: measured at 948, 1048, 1147, 1247, 1348, 1448, 1548 and 1648ms
     after the click, each taking about 0.4s. The 100ms step is theirs exactly and is what
     this takes.

     The ~900ms they wait before the first one is not taken. That is half a second of blank
     paper after the box has already finished fading, and where theirs holds eight chapter
     links, ours holds the stage number, the title and the sentence the reader opened the
     card to read. Ours steps from the start of the fade, so the last block is lit at about
     920ms rather than 2060ms. */
  html.js .explorer[data-open="true"] .panel .stage-body > *{
    transition:opacity var(--t-state) var(--ease-quick);
    @starting-style{opacity:0}
  }
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(2){transition-delay:.1s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(3){transition-delay:.2s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(4){transition-delay:.3s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(5){transition-delay:.4s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(6){transition-delay:.5s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(7){transition-delay:.6s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(8){transition-delay:.7s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(9){transition-delay:.8s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(10){transition-delay:.9s}
  html.js .explorer[data-open="true"] .panel .stage-body > :nth-child(11){transition-delay:1s}
}
/* Beats the display:none above on source order, which is the only thing separating them. */
html.js .explorer[data-closing="true"] .panel .stage-body{display:block}
/* Below 1250 the stack is an index row and the selected card a cropped header, and
   neither has room for a drawing. Above it they are whole cards and it belongs, so this
   is scoped rather than global. */
@media (max-width:1249px){
  html.js .stage.stk .mk,
  html.js .stage.sel .mk{display:none}
}

.xclose{
  border:1px solid var(--ink-line); background:var(--ground); border-radius:100%;
  width:52px; height:52px; cursor:pointer; position:relative; padding:0;
}
.xclose::before,.xclose::after{
  content:""; position:absolute; left:16px; top:25px; width:20px; height:1.5px;
  background:var(--ink-line);
}
.xclose::before{transform:rotate(45deg)} .xclose::after{transform:rotate(-45deg)}
html:not(.js) .xclose,.explorer[data-open="false"] .xclose{display:none}

/* Entrance is a GSAP tween on --esc alone. Transitions are suspended while it
   runs, or the cascade eases GSAP's already-eased values a second time.

   The suspension is written long on purpose. At (0,3,0) it lost to the closed cascade's
   own transition at (0,4,0) and to the narrow one at (0,5,1), so it never suspended
   anything: GSAP wrote a value, a CSS transition chased it on --ease-snap, and because
   that curve overshoots about 3.9x it drove a 1 to 0.5 scale change to -0.94. The cards
   turned inside out and flew off the top of the page on every load. Two clocks on one
   property, and the louder one was a curve meant for a hover of one percent.

   Cards are only ever hidden once GSAP is present and motion is allowed, so no-JS and
   reduced-motion readers never meet a hidden card. */
html.js .stage{transition:opacity var(--t-reveal) var(--ease)}
html.js .explorer[data-open="false"] .field.entering .stage{transition:none}
/* No rise. The deck is rigid: the gap between any two cards is --dy at every scroll
   position, which is what the reference does and what this page did not. Each card used
   to lift 80% of its own height as its resting top crossed 70% of the viewport, and
   because they fired in sequence the boundary between the lifted and the unlifted stood
   open as a 461px hole that travelled down the deck as you scrolled. Measured against the
   reference over the same scripted scroll: its six gaps never leave 150px, ours moved
   through 138-250 and settled with the hole still open at three of six scroll positions.
   The reveal is opacity, which moves nothing. */

/* ---------- the cascade, 1250 and up ----------
   Geometry comes entirely out of --i, --s and --n. Nothing measures the viewport,
   so there is no resize handler and none of the bugs that come with one. */
@media (min-width:1250px){
  /* The site index below the deck ends where the deck ends. Only the right edge is shared:
     the label rail is deliberately 320px beside the deck and 200px beside the sections,
     which is what the reference does too (320 for its intro, 200 for its sections). It
     lets the rail change and never lets the right edge move, so that is the rule copied
     here. */
  .full{margin-right:calc(-1 * var(--bleed-r))}

  /* Two columns: the intro is a narrow left column and the deck sits beside it. That is
     the reference's structure, and it is the whole reason it can show its intro and four
     cards on one screen where ours showed the intro and two. Stacked, our intro ran the
     full page width, put the deck's top at y=641 and left a 190px void to its right.

     This arrangement was tried once and abandoned because the rail took a 332px track and
     a 60px gutter, so the closed cascade started at x=598 while the open stack landed at
     x=40 and every card inherited a 558px haul. The haul was never the fault: the
     reference hauls its own cards 320px (card 1) to 860px (card 7) on open. What read as
     chaos was the field sliding underneath the cards and the deck flexing on scroll, both
     since fixed. The rail's track collapses to zero on open so the cards are measured
     against where they land, not against a container still on its way. */
  .explorer{
    display:grid; align-items:start;
    grid-template-columns:minmax(0,320px) minmax(0,1fr);
    column-gap:clamp(40px,4vw,72px);
  }
  /* One column now, and narrow, so the search sits under the copy instead of beside it.
     Beside it, the search lived in a second rail track that ran off the right of the
     page and was clipped at the viewport edge. */
  /* Pinned, so the left column holds something for the deck's whole run. Unpinned it
     scrolled out at scrollY 654 and left a 320px column empty for the next 800px, with
     nothing beside seven cards streaming past: a still, empty half-screen next to fast
     movement is what made the movement read as faster than it measures. Pinned, the
     intro and the search stay where the reader put them and the cards move against
     something that does not.

     `align-self:start` is what makes it stickable at all. The grid stretches its items,
     and an item as tall as its own row has nowhere to travel; sized to its content, it
     travels the difference. It sticks inside .explorer, so it releases at the foot of
     the deck rather than following the reader down the page. */
  .rail{
    grid-column:1; grid-row:1; container-type:inline-size; margin-bottom:0;
    /* Below the bar, not below the viewport. This read `clamp(20px,3vh,32px)` alone, which
       parked the rail 43px behind a header that is 70px tall and sliced the top off the
       title for the whole time it was stuck — the first line hidden outright and the second
       cut through its ascenders. The clamp is the gap under the bar now, which is what it
       was always describing. */
    position:sticky; top:calc(var(--bar) + clamp(20px,3vh,32px)); align-self:start;
  }
  .rail > *{max-width:100%}
  .rail h1{font-size:clamp(28px,11cqw,40px)}

  /* The deck runs out into the page margin on the right. The diagonal is paid for out
     of the container's leftover width, and inside the 1180px measure there is barely any:
     540 of the field's 676 is card, which left 22px a step against the reference's 90.
     The reference has no such problem because its cascade owns the full page width and
     the intro sits above it; ours sits beside a rail. Bleeding right spends the dead
     outer margin on the diagonal instead, and keeps a 24px gutter to the viewport edge.
     Closed state only: opened, the rail collapses and the field already has the width. */
  .explorer[data-open="false"] .field{
    grid-column:2; grid-row:1;
    container-type:inline-size; position:relative; display:block;
    margin-right:calc(-1 * var(--bleed-r));
    /* --dx and the selected card's travel are read in cqw. Both are USED on .stage, whose
       container is .field: an element cannot query itself, so neither may be resolved here. */
    /* Fixed, the way the reference fixes its own card at 549x400 from 800px up to
       1512px and lets only the diagonal respond. A card whose width tracks the viewport
       reflows its title at every size and there is no width at which the three-line
       ones are known to be right. Above ~1286 the old clamp was already resolving to
       540 anyway; this makes that the rule instead of a coincidence. */
    --cw:540px;
    --ch:calc(var(--cw) * .727);           /* 1.375:1, the reference's ratio */
    /* The exposed band, at the reference's own ratio: it steps its 400px card by 150.
       .53 was here because the band had to clear the card's top padding, a 46px number
       stacked above the title, and then three lines of title - 91px gone before the title
       began, which left the title LESS room in a bigger band. The number is now in the
       corner (see .field .num below), so the band is the title's alone and .375 fits it
       with room to spare, the same way theirs does. */
    --dy:calc(var(--ch) * .375);
    /* Their formula exactly: the leftover width of the container, divided by the six
       steps. On the reference that is (1092 - 549) / 6 = 90px at 1512 and it falls to
       zero and then negative as the window narrows, which is the whole of their
       responsive behaviour. Ours reads the same leftover; the floor is kept because
       below it the deck stops being a cascade and becomes a list. */
    --dx:clamp(24px, calc((100cqw - var(--cw)) / 6), 96px);
    /* Constant, and it has to stay constant. This height is the deck's whole contribution
       to the height of the document, so anything that changes it moves every section
       below the deck while the reader is scrolling through them.

       The box is exactly the deck: the last card's bottom is the field's bottom. It used
       to be stated 80% of a card shorter, which was the height the deck needed only once
       every card had lifted, and left the last card overhanging onto the section below
       until it did. With no rise there is nothing to leave room for. */
    height:calc((var(--n) - 1) * var(--dy) + var(--ch));
  }
  /* The rise is the `translate` property and nothing else, set from a literal by a class.
     A transform assembled out of var() is interpolated on the main thread: Chrome will not
     hand it to the compositor, so folding the rise into this transform made every lift
     repaint six 540x393 cards in full, every frame, at whatever the display's pixel ratio
     is. Split out, the animated value is a literal on its own property and the lift becomes
     a composited move. -80% is the same distance the old JS wrote as -0.8 x the card's
     height, because a percentage on `translate` resolves against the element's own box.

     `translate` also applies OUTSIDE `transform`, so the cascade offset below stays out of
     the rise's way. Nothing else shares either property: the card box carries the deck's
     position and the entrance's rise, and no third thing. */
  .explorer[data-open="false"] .field .stage{
    position:absolute; top:0; left:0; width:var(--cw); height:var(--ch); margin:0; overflow:hidden;
    transform:translate(var(--fx), var(--fy))
              translate(calc(var(--i) * var(--dx)), calc(var(--i) * var(--dy)))
              scale(calc(var(--esc) * var(--hov)));
    z-index:calc(var(--i) + 1);
    box-shadow:none;
    /* Declared, because otherwise these six get promoted anyway and for the wrong reason:
       they overlap each other, so the compositor was making the layers reactively and
       re-making them as the overlaps changed. */
    will-change:translate, transform;
    /* No transition on `transform`: that property is the deck's position, and the deck's
       position is animated by explorer.js on one timeline. Opacity is the entrance's and
       is a different clock, rightly its own. --hov is the pointer's, transitioned as a
       number so the swell eases without a transition on `transform` chasing GSAP. */
    transition:opacity var(--t-reveal) var(--ease), --hov var(--t-state) var(--ease-snap);
  }
  .explorer[data-open="false"] .field .hit{height:100%}
  /* The label stays at the card's foot, where the base rule puts it, and runs the card's
     full height. It can, because the diagonal leaves this card's left --dx uncovered from
     top to bottom: that column is the one part of a covered card a reader still sees, and
     on the reference it is where the colour block and the section label both live. It was
     briefly moved up into the exposed band, which is what a 24px step forces, and three of
     the seven labels were longer than the band was tall. */
  /* The drawing sits in the part of the card the next card covers, which is where the
     reference puts its own illustration and is what makes lifting a card worth doing.
     It used to sit in the band beside the number, because the covered part held a
     summary sentence and there was nowhere else for it to go. The summaries are gone,
     so the drawing has the room, and the band is now what it should have been all
     along: a number and a name, nothing competing with them. Card 06, which nothing
     covers, shows its drawing always. */
  .explorer[data-open="false"] .field .mk{
    position:absolute; left:48px; right:24px; top:calc(var(--dy) + 10px); bottom:20px;
    width:auto; height:auto; margin:0; max-height:none; min-height:0;
  }
  /* Focus by subtraction, the reference's mechanic. The moment the pointer enters
     the field every card loses its ramp block except the one being pointed at, so
     attention is taken away from the rest rather than added to one. That is why the
     page reads calm rather than busy. Pointer only: it never fires for a keyboard. */
  .explorer.pointing[data-open="false"] .field .stage::before{background:var(--rule)}
  .explorer.pointing[data-open="false"] .field .stage:hover::before{background:var(--stop)}
  .explorer.pointing[data-open="false"] .field .mk .a{fill:transparent}
  .explorer.pointing[data-open="false"] .field .stage:hover .mk .a{fill:var(--stop)}
  /* No transition of its own. It used to redeclare one for `transform`, which while the
     pointer was over the deck replaced the whole list and left the rise with none: a card
     that lifted under the cursor jumped. The swell's own easing now lives on `transform`
     in the rule above, which since the split is the only thing `transform` carries. */
  /* The hovered card comes to the front of the stack and swells one percent.
     The swell was on the drawing alone, on a note claiming that is the whole of what the
     reference does. It is not: measured on their live site, `.fancy_card:hover .info`
     scales 1.01, and .info is 548x398 on a 550x400 card - the entire face, artwork and
     all. The note also argued a deck of overlapping cards has no room to grow one. Their
     deck overlaps too and grows anyway, because the z-index above lifts the card clear
     first: at 1.01 on a 540px card it gains 2.7px a side, over neighbours it now covers.

     The swell was removed once before, on a note reading that their card box "does not
     move on hover at all, measured flat at 550px wide across the entire hover". That
     measured .fancy_card, which is a transparent placeholder and is flat. Forcing :hover
     on their live card and watching the painted face: 548px at rest, 569.10px at 240ms,
     settling to 553.48px by 540ms - 1.0385 at the peak and exactly 1.01 at rest. The same
     note faulted ours for overshooting 3.9x on --ease-snap. Theirs overshoots 3.85x, and
     --ease-snap is cubic-bezier(0,0,.2,8), which is their hover curve exactly; --t-state
     is .5s, which is their hover duration exactly. Both tokens came from this effect.

     Written into `transform`'s tail rather than the standalone `scale` property, which
     composes outside `transform` and would scale the fan offsets that place the card
     instead of the card. */
  /* :focus-visible lifts too. Without it a keyboard reader's focus ring is drawn under the
     next card in the cascade, which covers its right and bottom edges: measured clear on the
     left and top, COVERED on the other two. It stayed legible as an L, so 2.4.7 passed and
     the defect was invisible to the suite. */
  .explorer[data-open="false"] .field .stage:hover,
  .explorer[data-open="false"] .field .stage:has(:focus-visible){z-index:40}
  /* The swell is motion and a reader who asked for less does not get it; the lift and the
     accent below are not motion and stay. */
  @media (prefers-reduced-motion:no-preference){
    .explorer.pointing[data-open="false"] .field .stage:hover,
    .explorer[data-open="false"] .field .stage:has(:focus-visible){--hov:1.01}
  }
  /* One size in both states. Scoped to the closed deck this jumped to the 32px base
     the moment a card was picked, so every title in the stack grew 348px->360px while
     it travelled. The card holds its box; its contents have to hold theirs, or the
     object still reads as changing rather than moving. */
  /* The number leaves the column and takes the corner, which is where the reference
     keeps its own: .num absolute at the card's top right, .name starting at the very top
     edge. Stacked, the number and its gap spent 91px of the band before the title got a
     line, and that single fact is what held --dy at .53. Out of the flow the title starts
     at the padding edge, three lines of it end 11px inside a .375 band, and the drawing -
     which is positioned from --dy - follows the band up on its own. */
  .field .num{position:absolute; top:clamp(20px,2.4vw,28px); right:clamp(22px,2.6vw,32px)}
  .field .nm{font-size:clamp(24px,2.3vw,31px); margin-top:0}

  /* Open. The intro fades and its track collapses, so the deck takes the page the way
     the reference's does. The collapse happens inside the FLIP measuring window: the
     cards' new resting places are measured after it, and each card animates from where
     it actually was to where it actually lands, so nothing is inherited from a container
     mid-move. */
  .explorer[data-open="true"]{grid-template-columns:0 minmax(0,1fr); column-gap:0}
  .explorer[data-open="true"] .rail{opacity:0; pointer-events:none; overflow:hidden}
  .explorer[data-open="true"] .field{
    grid-column:2; grid-row:1;
    position:relative; display:block; container-type:inline-size;
    /* The field takes the page. The reference's cascade owns the full page width
       because their intro sits above it; ours now does too, but the closed deck still
       sits inside .wrap's 1180px measure, and inside that measure a 540px stack, a
       540px selected card and a readable panel do not all fit - the panel came out at
       426px of text against the 600px --measure is.

       The pull is capped at 166px, which is what it resolves to at 1512: past that the
       field would keep widening and leave the panel a graph-paper box far wider than
       any measure it will ever print into.

       The margins change instantly rather than easing. The field paints nothing of its
       own - no background, no border - so its edges moving is not something there is
       anything to see, and the cards inside it are positioned by explorer.js against
       where they actually land, not against where the container is on the way there. */
    --pull:clamp(0px, (100vw - 1180px) / 2, 106px);
    margin-left:calc(-1 * var(--pull));
    margin-right:calc(-1 * var(--pull));
    /* The field's own width, written out rather than read back. .wrap holds a 1100px
       measure at every width the open state is served at, and the pull above adds to
       both ends of it. The selected card is positioned against this, NOT against
       100cqw: cqw resolves against the field, and the field's width used to animate for
       a full second, which made this a transform target that moved every frame and so
       restarted its own transition every frame. Measured: the selected card settled at 1991ms, a second
       after the last stacked card, against 908ms for the reference's. Both sides of
       this share the same 100vw, so a scrollbar shifts them together. */
    --fw:calc(1100px + 2 * var(--pull));
    /* The same card, and it stays the same card: not its width, not its height, not
       the band it exposes. A stacked card and the selected one are the object that was
       clicked, moved. That is the reference's whole construction, measured on their
       live site: every one of their cards is 550x400 in the deck, in the stack and
       selected, at every width they serve. Ours used to shrink to a 350x126 index row
       on the way across, which reads as a different object arriving rather than as the
       one you chose travelling, and it is most of why their deck felt considered and
       ours did not. It also removes the last thing that was animating a height, so the
       overshoot now lands on position alone, which is where theirs lands. */
    --cw:540px; --ch:calc(var(--cw) * .727);
    /* The stack steps by very nearly the band the closed cascade already exposes, so the
       part of a card a reader can see holds the same number and the same title at the same
       size whether the deck is open or shut. .48 was here, which is not that band and not
       theirs either: it stepped 188px where the closed deck steps 147. Measured open, the
       reference steps 160px on a 400px card and 150px closed - .40 and .375 of its own card,
       opening a little wider than it sits shut. Ours is .375 closed already, so this is the
       same pair of numbers on our card. */
    --sdy:calc(var(--ch) * .4);
    /* The panel begins where the stack ends, on the 22px gutter the closed deck leaves
       between a card and the one covering it. */
    --pl:calc(var(--cw) + 22px);
    /* The selected card's top edge against the panel's. At .15 the card breaks the
       panel's top rule and the other 85% of it is plainly on top: opaque, in its own
       ink frame, and hanging off the panel's right corner. */
    --ptop:calc(var(--ch) * .15);
    --overhang:min(40px, clamp(24px,3.2vw,80px));
    /* Six steps and the card that sits on the last of them. */
    min-height:calc((var(--n) - 1) * var(--sdy) + var(--ch));
  }
  /* The stack. Only transform carries the move now: width and height are the closed
     card's and no longer change, so each card is one composited translate and nothing
     inside it reflows on the way.

     Each card's slot is its own index minus one if it sits below the selection, which
     is what the clamp computes. The reference leaves the selected card's slot standing
     empty and can afford to: they step 160px against a 400px card, so a missing card
     merely uncovers more of the one above it. Ours steps .53 of a card, so two steps is
     more than one card is tall and the same hole would be a real gap with the page
     showing through it. It was reported as a rendering fault twice when it was one. */
  .explorer[data-open="true"] .field .stage.stk{
    position:absolute; top:0; left:0; width:var(--cw); height:var(--ch); margin:0;
    --rank:calc(var(--i) - clamp(0, var(--i) - var(--sel), 1));
    transform:translate(var(--fx), var(--fy)) translateY(calc(var(--rank) * var(--sdy)));
    z-index:calc(var(--i) + 2);
  }
  /* At the card's foot, which the next card covers all but a sliver of. Hidden here for
     the same reason the closed cascade hides it: the spine says it where it can be seen. */
  .explorer[data-open="true"] .field .stage.stk .bar{display:none}
  /* The label goes with it, and only on the stacked cards. In the closed cascade it can
     run the card's full height because the diagonal leaves every card's left --dx column
     uncovered top to bottom. The stack has no sideways step, so that column is covered
     too. */
  .explorer[data-open="true"] .field .stage .hit{height:100%}
  /* The drawing sits in the part of the card the next one covers, read off the same band
     and at the same offset as the closed deck. */
  .explorer[data-open="true"] .field .mk{
    position:absolute; left:48px; right:24px; top:calc(var(--sdy) + 10px); bottom:20px;
    width:auto; height:auto; margin:0; max-height:none; min-height:0;
  }

  /* The selected card stays an absolutely positioned box in the container it was
     already in, and only its transform changes. That is what makes the move continuous
     rather than a jump followed by an ease.

     It is pinned to the field's right edge, which is the reference's rule stated from
     the other end: their stack sits 100px in from the left of the page and their
     selected card 100px in from the right, at every width. Ours are inset by .wrap's
     own page padding rather than by their 100. */
  .explorer[data-open="true"] .field .stage.sel{
    position:absolute; top:0; left:0; width:var(--cw); height:var(--ch); margin:0;
    transform:translate(var(--fx), var(--fy)) translateX(calc(var(--fw) - var(--cw)));
    z-index:40;
    background:var(--raised); border-color:var(--ink-line);
    box-shadow:-22px 22px 0 var(--stop);
  }

  /* The header card is laid ON the panel, not beside it and not through its corner.
     That is the reference site's own construction: their selected card sits inside
     their detail sheet on every axis but the top, which it breaks by about 30px.

     The panel runs from the stack's right edge to the field's, less the overhang, so
     the card hangs off its top right corner. The first printed line does not move when
     --ptop changes: the panel's padding is derived from the same number as its margin,
     so the two cancel and the grid begins a fixed distance below the card's foot.

     That distance is 80, not the 40 it was, because the cards lift their faces 20px and
     40 left 22px of daylight under the open card - the tightest gap on a panel whose rows
     clear each other by 31 and whose columns clear by 71. Seen, 80 is 60: twice the row
     gap, for the one edge in the grid that meets something which is not another card. The
     reference leaves 71 there. Ours is theirs scaled by the same ratio our gaps already
     take against theirs, which is where 50 came from against their 65 and 60. */
  .explorer:is([data-open="true"],[data-closing="true"]) .field .panel{
    margin-left:var(--pl); margin-right:var(--overhang); margin-top:var(--ptop);
  }
  /* Graph paper, in the drawing register the cards already use. */
  .explorer:is([data-open="true"],[data-closing="true"]) .panel .stage-body{
    border:1px solid var(--ink-line); background-color:var(--ground);
    background-image:linear-gradient(var(--panel-grid) 1px,transparent 1px),
                     linear-gradient(90deg,var(--panel-grid) 1px,transparent 1px);
    background-size:50px 50px,50px 50px;
    padding:calc(var(--ch) - var(--ptop) + 80px) 40px 40px;
  }
  /* On the panel's left edge, level with the middle of the card it closes. */
  .explorer[data-open="true"] .xclose{
    position:absolute; z-index:41;
    top:calc(var(--ch) / 2 - 26px); left:calc(var(--cw) - 4px);
  }
}

/* ---------- the deck's lag against the wheel ----------
   The reference's signature move: each card trails the deck in proportion to how fast the
   page is moving and to how far down the deck it sits, so a turn of the wheel opens the
   deck out and letting go closes it again.

   THE WHOLE CARD MOVES. This is the thing we got wrong twice by measuring it wrong. Their
   div.fancy_card is a 550x400 placeholder with no background, no border and no radius - it
   is invisible, and its transform does not change on scroll. The box a reader can actually
   see is div.content inside it, and through a spin that box and all ten of its descendants
   move together by the same 22px. Nothing slides against anything else. A metric that reads
   the face against its own frame cannot tell that apart from the frame holding still while
   the face slides, which is how a lag applied to the contents kept measuring as a match.

   Theirs needs two elements because one transform cannot hold both a position and an offset.
   We do not, because `translate` and `transform` are independent properties and the deck has
   only spent one of them: `transform` is the card's place in the fan, so the lag rides on
   `translate` alongside the entrance's rise, and the painted box itself moves. Same result,
   one element fewer.

   --scroll-acc is the scroll's speed as a fraction of the viewport's width, written by
   pace.js every frame the page is in motion and zero the rest of the time; the step per card
   and the fifth of a viewport height are their arithmetic taken as it stands. Closed deck
   only: opened, the cards are a column of panels with nowhere to travel. */
@media (min-width:1250px) and (prefers-reduced-motion:no-preference){
  .explorer[data-open="false"] .field .stage{
    translate:0 calc(var(--scroll-acc,0) * var(--i) * .2 * 100vh);
  }
}

/* ---------- the deck below 1250 ----------
   The reference keeps its cards on small screens rather than degrading to a list:
   they turn portrait, stay large, the sideways step goes to zero, and the exposed
   band grows to a constant, because it has to hold a number and a two-line title
   whatever the card's height. */
@media (max-width:1249px){
  .rail{margin-bottom:clamp(36px,6vw,56px)}
  .rail .lede{max-width:var(--measure)}
  html.js .explorer[data-open="false"] .field{
    /* Stated against the viewport rather than as a percentage. --ch is --cw scaled, and
       a percentage in a height resolves against the parent's height, which is itself
       calculated from --ch: circular, so it resolved to zero and every card in the deck
       collapsed to 2px. The subtraction is .wrap's own padding, doubled. */
    --cw:min(560px, calc(100vw - 2 * clamp(22px,4vw,40px)));
    /* Below 1250 there is no hover, so a drawing parked in the covered part of the card
       is a drawing nobody ever sees, and the band above it is left holding two lines of
       type and eighty pixels of nothing. So the phone card is the band, and the band
       holds all three things: number, name, drawing. 260 is the longest of the six names
       at 320px wide, four lines of it, plus the drawing strip below. What the next card
       covers is a 66px stub, which is enough to read as a stack and too little to hide
       anything. */
    --ch:calc(var(--dy) + 66px); --dy:260px;
    position:relative; display:block; container-type:inline-size;
    height:calc((var(--n) - 1) * var(--dy) + var(--ch));
  }
  /* No diagonal at these widths, so nothing leaves a left column standing: run full
     height the seven strips would nest into one striped block. Cut to the band, they
     step down the left edge as seven separate marks. */
  html.js .explorer[data-open="false"] .field .stage::before{bottom:auto; height:var(--dy)}
  html.js .explorer[data-open="false"] .field .stage{
    position:absolute; top:0; left:0; width:var(--cw); height:var(--ch); margin:0;
    transform:translateY(calc(var(--i) * var(--dy))) scale(var(--esc));
    z-index:calc(var(--i) + 1);
    box-shadow:none;
    will-change:translate;
    transition:opacity var(--t-reveal) var(--ease);
  }
  html.js .explorer[data-open="false"] .field .hit{height:100%}
  /* Under the name rather than beside it: the name runs to the card's full width at
     these sizes, so there is no room at its side. The box is the drawing's own 4:3, so
     the drawing fills it exactly and its left edge lands on the text's, which a box
     wider than the drawing would not do. */
  html.js .explorer[data-open="false"] .field .mk{
    position:absolute; left:30px; right:auto; top:auto; bottom:80px;
    width:83px; height:62px; margin:0; max-height:none; min-height:0;
  }
  /* Open expands the card in place: the title stays where it was and the content
     opens directly beneath it, so nobody loses their position in the deck. */
  html.js .explorer[data-open="true"] .field{display:flex; flex-direction:column}
  html.js .explorer[data-open="true"] .field .stage{order:calc(var(--i) * 2)}
  html.js .explorer[data-open="true"] .field .stage.stk{border-color:var(--rule)}
  html.js .explorer[data-open="true"] .field .stage.stk .bar{display:none}
  html.js .explorer[data-open="true"] .field .stage.stk .nm{font-size:28px; margin:12px 0 0}
  html.js .explorer[data-open="true"] .field .stage.stk .hit{padding-bottom:20px}
  html.js .explorer[data-open="true"] .field .stage.sel{background:var(--raised); border-bottom:0}
  html.js .explorer:is([data-open="true"],[data-closing="true"]) .field .panel{order:calc(var(--sel) * 2 + 1)}
  html.js .explorer[data-open="true"] .xclose{
    order:calc(var(--sel) * 2 + 1); margin:16px auto -10px; z-index:5;
  }
  html.js .explorer:is([data-open="true"],[data-closing="true"]) .panel .stage-body{
    border:1px solid var(--rule); border-top:0;
    background-color:var(--ground);
    background-image:linear-gradient(var(--panel-grid) 1px,transparent 1px),
                     linear-gradient(90deg,var(--panel-grid) 1px,transparent 1px);
    background-size:50px 50px,50px 50px;
    /* Stacked, the panel is joined to the card above it rather than laid under one, so
       the top edge takes the row gap rather than twice it - plus the 20px the faces lift. */
    padding:calc(clamp(26px,6vw,36px) + 20px) clamp(20px,5vw,28px);
  }
}
@media (max-width:560px){
  html.js .explorer[data-open="false"] .field .mk{left:22px}
}
/* Folded up from 760px. Both of these are the same change the rest of the site makes at
   820: the label rail stops fitting beside the words. Having them at 760 meant the front
   page changed shape at a width nothing else on the site cared about. */
@media (max-width:820px){
  .stage-body{padding-left:clamp(22px,5vw,32px)}
  .hit{padding-left:clamp(22px,5vw,32px)}
}
@media (prefers-reduced-motion:reduce){
  *{transition:none !important; animation:none !important; scroll-behavior:auto !important}
}
/* Only the OPEN panel's copy is named. The other six bodies are still in the DOM inside
   their own cards, and a duplicate view-transition-name aborts the whole transition.

   The names sit on the open card's own header now. They used to be on the panel's copy of
   the number, title and deck, and the panel holds no prose at all since it became a grid of
   section cards. The card carries the same two elements the hero does, so the pair the
   browser morphs is the number and the name the reader clicked. */
.explorer[data-open="true"] .stage.sel .num{view-transition-name:stage-num}
.explorer[data-open="true"] .stage.sel .nm{view-transition-name:stage-title}
