/* ═══════════════════════════════════════════════════════════════════════
   DRIFT — TECHNOLOGY AND GOLDILOCKS PAGES
   Sections X1–X11, and G1–G6 on /goldilocks/, which was split out of this
   page (James, 28 Aug 2026) and loads this same stylesheet rather than a
   copy of two thirds of it. Every class the split took with it — .softsec,
   .stackin, .stack5, .ctrlgrid, .vgrid, .band.stmt, .util — is still
   defined here, under its original heading, and is used by that page only.
   Built to the same rule as the team page: interior pages
   are EVIDENCE, not vision. Denser than the homepage, document-like, the
   blueprint lattice at full strength, and no pinned or horizontal
   choreography — an investor reads this page out of order and must be able
   to jump straight to the section their diligence is stuck on.

   The step-down in type scale comes from `dense: true` in the front matter
   (see the DENSE SCALE block in site.css). Shared tokens, type, chips,
   buttons, nav and footer live in site.css; .sec/.lattice/.sechead are
   restated here so this page does not depend on team.css loading.
   ═══════════════════════════════════════════════════════════════════════ */

/* 12vh became 9vh on 28 Aug 2026 (James — "can we shrink the gaps"). At a
   900px-tall laptop the old clamp resolved to 108px at each end, so two
   sections met with 216px of empty page between them and the reader crossed
   a screen's worth of nothing to get from one to the next. 9vh is ~77px, so
   a section change costs ~155px — still unmistakably a break. The phone
   value is set separately in site.css (body .sec, 56px). */
.sec{position:relative;padding:clamp(56px,9vh,104px) 0}
.sec.lattice::before{content:"";position:absolute;inset:0;background:var(--grid-schematic);
  z-index:0;pointer-events:none}
.sec .wrap{position:relative;z-index:1}
.sechead{margin-bottom:clamp(38px,6vh,64px);max-width:62ch}
.sechead .lede{color:var(--mist);margin-top:18px}
/* .wide takes the 62ch cap off the block so the heading runs across the top
   in one line instead of stacking three (James, 27 Aug 2026 — "if the
   heading words went across the top then you would save room"). The cap
   moves to the lede, which is the only thing under it that needs a reading
   measure. Two heading lines saved at every width above a tablet. */
.sechead.wide{max-width:none}
.sechead.wide h2{max-width:none}
.sechead.wide .lede{max-width:74ch}
/* .wide.full drops the lede's reading measure too, so the whole head block
   runs to the column edge under a one-line heading (James, 27 Aug 2026).
   Only for ledes of two lines or so: at a desktop width this is well past a
   comfortable measure, and a long paragraph set to it gets hard to track
   from one line to the next. */
.sechead.wide.full .lede{max-width:none}

/* ── X1 · HERO — SPLIT ────────────────────────────────────────────────
   The old hero was a photo under a 50%-brightness filter and two stacked
   gradients, because the copy sat on top of it. Splitting the two gives
   the type a solid ground and lets the render be shown as drawn — which
   is the whole point of an image-led page: the picture is a half of the
   composition, not a texture behind the words.

   The copy pads to max(28px, 50vw - 620px) so its left edge lines up with
   .wrap (min(1240px,90vw)) further down the page, while the picture still
   runs to the right edge of the viewport. */
.xhero{display:grid;grid-template-columns:1fr 1.06fr;align-items:stretch;
  min-height:clamp(560px,86vh,900px);background:var(--abyss);position:relative}

/* Hero and factbar as one screenful, per Jess's review: the four numbers an
   investor needs are part of the first impression, not something below the
   fold. The hero takes whatever height is left once the strip has taken its
   own, so the two always land together.

   min-height, never height: on a short laptop the headline and the four
   numbers together need more than 100svh, and the block must grow rather
   than crop. svh rather than vh so a phone browser's collapsing toolbar
   does not leave the strip permanently half-off the screen — though below
   900px the hero stacks and this lifts entirely (see the block at the foot
   of this file). */
@media(min-width:901px){
  .xtop{min-height:100svh;display:flex;flex-direction:column}
  .xtop .xhero{flex:1;min-height:0}
  /* The hero cannot shrink below its own copy, so the copy is what has to
     give: at 1440×900 the headline, the lede and the four numbers came to
     931px against 900 of viewport. These two trims are worth ~54px and the
     factbar's own ~12px, which lands the strip on screen at the laptop
     sizes this page is actually read at.

     Below roughly 860px of viewport height it no longer fits, and the block
     grows rather than cropping the headline — the alternative is shrinking
     the d1, which is the one thing on this page that should not shrink. */
  .xtop .xhero-copy{padding-top:clamp(76px,9vh,120px);padding-bottom:clamp(24px,4vh,48px)}
  .xtop .factbar>div{padding-block:clamp(16px,2vw,24px)}
  .xtop .factbar .l{margin-top:8px}
  .xtop .factbar .chip{margin-top:10px}
  /* 44ch set the lede six lines deep in a column with room for more. Letting
     it run to 52ch takes two lines out of it at 1280 without touching a
     word — the cheapest 60px on the page. */
  .xtop .xhero-copy .lede{max-width:52ch}
  /* The headline runs to four lines here, and 1.02 is loose leading for four
     lines of a 5.2rem display face — tightening it both looks better and
     buys the last dozen pixels. */
  .xtop .xhero-copy .d1{line-height:.98}
  /* THE thing that was actually setting the hero's height. The render is a
     1800×1800 square at width:100%, and height:100% against an auto row
     resolves to auto — so the picture fell back to its own square ratio and
     made the hero as tall as the right-hand column is wide (741px at 1440).
     Every trim above was being thrown away against it. Taking the image out
     of flow lets the copy and the flex share decide the height, and the
     picture fills whatever that turns out to be. */
  .xtop .xhero-pic img{position:absolute;inset:0}
}
/* The same block, on a screen that is not tall enough for it. Everything in
   the rule above assumed a ~900px viewport; a laptop gives 760–800 once the
   browser has taken its chrome, and there the four numbers sat below the
   fold — which defeats the point of putting them in the first screen.

   The height back comes out of the headline, and it costs less than it
   sounds. "A ship that makes" needs 592px to set on one line and the copy
   column gives the d1 541 at 1440, so at full size it wraps and the three
   written lines render as four — 326px of headline for three lines of
   words. Under about 70px it fits as written, and three lines come to 206.
   That is 120px recovered, more than the ~99 the block was over at 1440×790,
   and the headline reads better for losing the "A ship that / makes" break.

   min() not clamp(), and both terms are measured rather than picked. The
   5.5vw keeps that first line inside a column that is only 374px wide at
   902px of viewport — 5.8 was tried and wrapped there, and left 2px of
   slack at 1024. The 70px ceiling holds it inside the column on very wide
   screens: the copy column gets NARROWER past 1440, because its left
   padding grows to meet .wrap while the column stays a fraction of the
   grid, so it is 528px at 1920 against 541 at 1440 and a taller ceiling
   would start wrapping again. The factbar gives up a few more pixels at the
   same time.

   Above 920px of viewport height none of this applies and the hero is
   untouched: there the block fits at full size. */
@media(min-width:901px) and (max-height:920px){
  .xtop .xhero-copy .d1{font-size:min(5.5vw,70px)}
  .xtop .factbar>div{padding-block:clamp(14px,1.6vw,18px)}
}
.xhero-copy{display:flex;flex-direction:column;justify-content:center;
  padding:clamp(96px,13vh,150px) clamp(28px,4vw,64px)
          clamp(40px,7vh,80px) max(28px,calc(50vw - 620px))}
.xhero-copy .lede{margin-top:26px;color:var(--mist);max-width:44ch}
/* The one button in either hero — /technology/ into /goldilocks/ and back.
   margin-top matches the lede's own, so hero copy reads as eyebrow, heading,
   sentence, door on one rhythm. It sits inside .xtop on /technology/, where
   the hero and the factbar share a screen, so the spacing is on the tighter
   side of what the lede gets: an extra 26px there pushes the factbar's
   chips off a short laptop viewport. */
.xhero-cta{margin-top:clamp(24px,3.4vh,32px)}
/* The Goldilocks hero only. Its five-line headline made the panel 894px at
   1440×900 — content-driven, well over the 774 its own min-height asks for,
   and tall enough that the section under it never showed. Three trims, all
   of them height and none of them type size: the leading tightens to .96,
   a shade under the .98 the /technology/ hero runs for four lines and worth
   ~25px over five, and the copy's top and bottom padding come down.
   Together they take the panel to about 830 at 1440×900 without shrinking a
   word, and the "On this page" rail below now shows on a laptop. The floor
   is the .xhero min-height, so this cannot collapse the hero.

   96px is the floor on the top padding and not a pixel less: the header is
   sticky, ~79px tall and transparent over this panel, and at a 720px-high
   viewport a 76px floor tucked the eyebrow underneath it. The vh term is
   what gives on a short screen, and it bottoms out at that 96. */
.xhero--named .d1{line-height:.96}
/* The leading trim above got the panel to 830 at 1440×900 — still the whole
   screen, so the rail under it and the section under that never showed
   (James, 28 Aug 2026 — "doesn't quite fit well"). One step down in type
   size on short viewports only, the same device /technology/'s hero already
   uses two blocks up, and worth ~55px over five lines. That is enough to
   put the panel back on its own 774px min-height floor at 1440×900 rather
   than 56px above it, so the hero clears the fold and all but the last few
   pixels of the rail under it show. Going smaller buys nothing: the floor,
   not the type, is what sets the height from here. Above 920px of height the hero is
   untouched and the headline runs at full display size. */
@media(min-width:901px) and (max-height:920px){
  .xhero--named .xhero-copy .d1{font-size:min(5.15vw,71px)}
}
/* Padding trim above the stack point only. Below 901px the hero is a single
   column with its own padding (foot of this file), and this selector is
   specific enough to beat that shorthand from up here — which would have
   quietly taken 18px off the phone layout's top padding, a number that block
   chose for itself. */
@media(min-width:901px){
  .xhero--named .xhero-copy{padding-top:clamp(96px,10vh,120px);
    padding-bottom:clamp(32px,5vh,64px)}
}
.xhero-pic{position:relative;margin:0;overflow:hidden;background:var(--hull)}
.xhero-pic img{width:100%;height:100%;object-fit:cover;display:block}
/* A wash on the joining edge only — enough to stop a hard seam between
   panel and photograph, not enough to dim the render. */
.xhero-pic::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(var(--abyss-rgb),.9),rgba(var(--abyss-rgb),.14) 24%,rgba(var(--abyss-rgb),0) 52%)}

/* The four numbers an investor needs before any mechanism — now a strip
   spanning the full width under the hero, so the hero itself stays a
   clean two-panel composition. */
.factbar{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--line);border-bottom:1px solid var(--line)}
.factbar>div{background:var(--hull);padding:clamp(20px,2.6vw,30px) clamp(20px,2.4vw,32px)}
.factbar .n{font-family:var(--mono);font-size:clamp(1.7rem,3.2vw,2.6rem);font-weight:500;
  line-height:1;letter-spacing:-.045em}
.factbar .n .sup{font-size:.5em;vertical-align:super;letter-spacing:0}
.factbar .l{color:var(--fog);font-size:.8rem;line-height:1.45;margin-top:10px;max-width:26ch}
.factbar .chip{margin-top:14px}

/* X2 · ON THIS PAGE — the rail's rules moved to site.css on 28 Aug 2026;
   this file and interior.css each held an identical copy. Section numbering
   below keeps its old letters so the page's own comments still line up. */
/* ── X4 · THE PILLAR CARDS ──────────────────────────────────────────
   Was SPLIT ROWS: a full-bleed two-panel row per pillar, picture on
   alternating sides, four of them down the page. It read well and cost
   four screens to say four things, so the four pillars are now one row
   of four cards — picture above, copy below (James, 27 Aug 2026).

   The card is a flex column so the source link can be pushed to the
   bottom edge: four cards of unequal copy still line their links up, which
   is what makes the row read as a set of four rather than four columns of
   different lengths.

   Pictures are card-width now instead of half-screen, and every one is
   SQUARE. 4:3 photographs beside the square general arrangement left one
   card's copy starting 66px below the other three, and the square is the
   ratio the drawing needs — so the photographs take the crop and all four
   copy blocks start on the same line. */
.sgrid{list-style:none;display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(22px,2.6vw,38px)}
.scard{display:flex;flex-direction:column}
.scard-pic{position:relative;margin:0 0 clamp(16px,1.6vw,22px);overflow:hidden;
  background:var(--hull);border:1px solid var(--line);aspect-ratio:1 / 1}
.scard-pic img{width:100%;height:100%;object-fit:cover;display:block}
/* The drawing sheet: square, on the white ground it was drawn on — the one
   deliberately light panel on the page, because that is what a drawing is. */
/* A general arrangement is not a photograph: cropping it loses the ends of
   the ship, which is the only thing it is there to show. So this variant
   contains rather than covers and keeps the white ground the drawing was
   made on — an inset sheet, not a photograph that has gone wrong. */
.scard-pic.sheet{background:#F4F7FA}
.scard-pic.sheet img{object-fit:contain;padding:clamp(12px,1.4vw,22px)}
/* A pending shot fills the picture slot the same way a photograph does. No
   card uses it at the moment — every slot on this page is now filled — but
   the rule is what makes a swap back to a placeholder markup-only:
   {% from "partials/figures.njk" import placeholder %} at the top of the
   template, then the macro in place of the <img>. */
.scard-pic .ph{position:absolute;inset:0;margin:0;border-radius:0}
.scard .eyebrow{margin-bottom:10px}
/* Two-line eyebrow on the pillar cards (James, 27 Aug 2026): the part on the
   first line, who validates it on the second. The <span> is there so the
   text is a block-level run of its own, which is what lets the <br> break
   inside it predictably.

   ALL FOUR HAVE TO SIT EXACTLY TWO DEEP, and that is what the type size
   here is set by. The row is only four across above 1080px, where a card is
   222–283px wide, and the longest second line — the rig's — wrapped to three
   at .72rem/.26em, so the type comes down to .66rem/.16em. Longest line then
   measures ~208px inside 222px at the tightest four-column width.

   If a fifth pillar or a longer partner name ever lands here, re-check it
   against 222px before assuming it fits — a too-long line fails silently by
   wrapping to three and pushing that one card out of step with the others. */
.scard .eyebrow.stack{
  display:block;font-size:.66rem;letter-spacing:.16em;line-height:1.5}
.scard-t{font-size:clamp(1.05rem,1.5vw,1.32rem);font-weight:500;line-height:1.2;
  letter-spacing:-.02em;margin:0 0 12px}
.scard-t em{color:var(--current-lift);font-style:normal}
.scard>p{color:var(--mist);font-size:.88rem;line-height:1.62}
/* The link is the card's floor. margin-top:auto on it rather than on a
   wrapper, so it drops to the bottom of whichever card is tallest. */
.scard .tlink{margin-top:auto;padding-top:20px;font-size:.82rem;align-self:start}

/* ── X4 · STAGE COPY FURNITURE ─────────────────────────────────────────── */
.stagesec{background:var(--abyss)}

/* Three lines per card, so they are set tight and mono-led: the bold half
   is the named thing, the rest is what it is. */
.spec{list-style:none;display:grid;gap:9px;margin-top:18px}
.spec li{font-size:.82rem;line-height:1.5;color:var(--fog);
  padding-left:16px;position:relative}
.spec li::before{content:"";position:absolute;left:0;top:.62em;width:8px;height:1px;
  background:var(--current-lift)}
.spec b{font-family:var(--mono);font-weight:500;color:var(--white);letter-spacing:-.01em}

/* ── X5 · THE HARDWARE — THE RENDER ────────────────────────────────────
   Was an annotated figure: three positioned callouts on the picture and a
   caption sunk into a gradient at its foot. Both are gone (James, 27 Aug
   2026) and the render is shown clean, so the hotspot and caption rules
   went with them — nothing else on the site used .hs, and .glfield carries
   its own caption treatment.

   The 16/9 here is now only for the stacked layout; beside the accordion
   the picture is sized by its column. See .hard-pic below. */
.annot{position:relative;margin:0 0 clamp(30px,4.4vh,52px);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;background:var(--hull)}
.annot>img{width:100%;height:auto;aspect-ratio:16 / 9;object-fit:cover;display:block;
  filter:brightness(1) contrast(1.02)}

/* Ten rows, each collapsed to its label. The label was a mono micro-cap in
   a fixed left column when the body sat permanently beside it; as a control
   it runs the full row width instead, so the hit area is the row and not
   the word.

   Ruled hairlines were not enough once the rows became controls (James,
   27 Aug 2026 — "needs to stand out more"): ten faint labels on the page
   ground read as a list of headings, not as ten things to open. They are
   filled panels now, hairline-gapped inside one bordered block — the same
   device as .factbar under the hero, which is the page's existing way of
   saying "these are separate cells of one instrument". The open row lifts
   to --hull and takes a --current edge, so which row is open is legible
   from across the section rather than only from the chevron.

   The chevron, its rotation and the focus ring are still the .qa component
   at the foot of the page, so the two accordions read as one control. */
.hwlist{display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
.hwlist details{background:var(--schematic)}
.hwlist details[open]{background:var(--hull);box-shadow:inset 2px 0 0 var(--current)}
.hwlist summary{list-style:none;cursor:pointer;position:relative;
  display:flex;align-items:center;gap:10px;
  padding:15px 42px 15px clamp(16px,1.4vw,22px);background:transparent;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--current-lift);transition:background var(--t-fast),color var(--t-fast)}
.hwlist summary::-webkit-details-marker{display:none}
.hwlist summary:hover{background:var(--hull);color:var(--white)}
.hwlist details[open] summary{color:var(--white)}
.hwlist summary:focus-visible{outline:2px solid var(--current-lift);outline-offset:-3px}
/* Smaller than the .qa chevron: that one sits against a 1.2rem question,
   this one against a .68rem label, and at 11px it outweighed the word. */
.hwlist summary::after{content:"";position:absolute;right:18px;top:50%;width:8px;height:8px;
  margin-top:-6px;border-right:1px solid var(--current-lift);border-bottom:1px solid var(--current-lift);
  transform:rotate(45deg);transition:transform var(--t-fast) var(--ease-out)}
.hwlist details[open] summary::after{transform:rotate(-135deg);margin-top:-2px}
/* No reading measure on the body: the column is narrow enough now to be
   the measure, and a cap inside it only opened a gutter on the right. */
.hwlist .hw-b{padding:0 clamp(16px,1.4vw,22px) 18px}
.hwlist .hw-b p{margin:0;color:var(--mist);font-size:.88rem;line-height:1.6}
/* The render used to sit full-width above the ten kit rows, so the picture
   and the words it names were never on screen together (James, 27 Aug
   2026). It moved beside the list, then took the larger half of the split
   once the rows collapsed and stopped needing the width. */
.hardsplit{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  gap:clamp(26px,3.4vw,54px)}

/* The picture is sized by its column rather than by its own ratio, so its
   bottom edge lands on the accordion's (James, 27 Aug 2026). .hard-pic is a
   stretched grid item, so its height IS the taller of the two columns — the
   list. The figure is taken out of flow so it contributes nothing back to
   that height: a height:100% picture inside an auto-height row resolves
   against itself and collapses. Same device, and the same reason, as the
   hero render at the top of this file.

   The upshot at a desktop width is a roughly square picture instead of a
   16/9 one, and it re-proportions as rows open. 78vh is the guard on that:
   open enough rows and the column runs past 1500px, and a 683-wide slot
   that tall crops the 1.8 source to a quarter of its width — a sliver of
   one rig and no ship.

   Which is what .hard-pin is for, and the only reason there are two divs
   here. Sticky needs a definite height to size against and a box to travel
   inside; the figure cannot be both the thing that is out of flow and the
   thing that sticks. So the wrapper takes the full column height out of
   flow, and the figure sticks inside it. Under the cap the picture is the
   column and sticky never fires; over it, the picture holds at 78vh and
   rides down the rest rather than leaving the overrun blank. */
.hard-pic{position:relative}
.hard-pin{position:absolute;inset:0}
.hard-pin .annot{position:sticky;top:clamp(84px,11vh,116px);
  height:min(100%,78vh);margin:0}
.hard-pin .annot>img{height:100%;aspect-ratio:auto}

.note{margin-top:clamp(22px,3vh,34px);color:var(--fog);font-size:.85rem;max-width:70ch}

/* The section ends on one short left-aligned line of fine print, so a full
   108px foot under it — against the physics section's own 108px head —
   read as a hole rather than as a break (James, 27 Aug 2026). The vh term
   is what actually governs here — roughly 23px on a 900-tall laptop, and
   the 36px ceiling only bites above ~1385 — so the boundary lands near
   130px against the 216px every other section gets. This section only:
   the rest end on something with enough weight to earn the standard gap. */
.hardsec{padding-bottom:clamp(20px,2.6vh,36px)}
.hardsec .note{margin-top:clamp(14px,1.8vh,22px)}

/* The same argument at this section's head. The CFD stage above ends on a
   one-line credit and a link, so its 12vh foot plus this section's 12vh head
   put roughly 150px of nothing between fine print and a section title
   (James, 28 Aug 2026). Cut to a third here, and scoped to the junction:
   .hardsec keeps the standard head anywhere it follows something with
   weight. */
.stagesec + .hardsec{padding-top:clamp(28px,4vh,56px)}

/* ── X6 · THE SOFTWARE ─────────────────────────────────────────────────── */
/* Same cut as .sec — 12vh to 9vh (James, 28 Aug 2026, "can we shrink the
   gaps"). This one sits directly above the validation section on
   /goldilocks, and the two 12vh feet met as 223px of empty page under the
   software cards. */
.softsec{position:relative;overflow:hidden;padding:clamp(56px,9vh,104px) 0}
.softsec>img{position:absolute;left:0;top:-10%;width:100%;height:120%;object-fit:cover;z-index:0;
  filter:grayscale(.12) brightness(.66);will-change:transform}
.softsec .sc{position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(var(--abyss-rgb),.90),rgba(var(--abyss-rgb),.42) 40%,rgba(var(--abyss-rgb),.90))}
.softsec .wrap{position:relative;z-index:2}
.soft{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4vw,64px);align-items:start}
/* On a photograph the columns need their own ground to stay readable — which
   is what this rule says, and was not what it did. It was scoped to
   :first-child because the software section used to pair one prose column
   with a .lf panel that brought its own ground; the software section is two
   prose columns now, so the second one was left sitting on bare photograph.

   The same selector was also boxing the first column of every OTHER .soft on
   the page, none of which is over an image — and in the physics row, where
   the first child IS a .lf, it was drawing a second panel around a panel.
   Scoped to the photographic section, and applied to both columns. */
.softsec .soft>div{background:rgba(var(--abyss-rgb),.66);backdrop-filter:blur(6px);
  border:1px solid var(--line);border-radius:var(--r-md);padding:clamp(24px,3vw,36px)}
.soft p{color:var(--mist)}
.soft p+p{margin-top:16px}
/* — the roster row's alignment (James, 28 Aug 2026: "weird alignment") —
   Two things were out. The right column's paragraph was 416px inside a
   591px column, because .page-dense p{max-width:52ch} was setting it as
   body copy while its own tick list beside it ran the full column — so the
   column read ragged against the left one. And the two columns started at
   different heights: the left column's .ticks carry a 24px top margin and
   the paragraph carried none, so the right column's first line sat 24px
   above the left column's first tick under headings that were level.
   Both scoped to this row: the measure is right where a column really is
   body copy. */
#validation .soft>div>p{max-width:none}
#validation .soft h3+p{margin-top:24px}

/* ── THE GOLDILOCKS SEARCH FIELD ───────────────────────────────────────
   Picture left, copy right. The figure is close to square — it is a map
   window, not a crop decision — so the copy column gets the wider share
   and the two finish at roughly the same depth.

   Not covered by the .softsec .soft>div panel rule above, because it is a
   <figure> rather than a div — which is the right outcome anyway: the map
   is already an opaque plate, and boxing it would draw a panel around a
   panel. It keeps the .annot treatment instead — hairline, radius, caption
   sunk into a gradient at the foot — so the two big figures on this page
   are recognisably the same object.

   The source export is a dark satellite basemap, so it needs no scrim to
   sit on the section photograph, but it IS 927px wide and no larger. Never
   let it render above that: the tracks are hairlines and they go to mush
   the moment it is upscaled. */
.softmap{grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr)}
.glfield{position:relative;margin:0;border:1px solid var(--line-signal);
  border-radius:var(--r-md);overflow:hidden;background:var(--schematic)}
.glfield img{display:block;width:100%;height:auto;max-width:927px}
.glfield figcaption{position:absolute;left:0;right:0;bottom:0;padding:14px 20px;
  font-family:var(--instrument);font-size:.6rem;letter-spacing:.13em;text-transform:uppercase;
  line-height:1.5;color:var(--mist);
  background:linear-gradient(180deg,rgba(var(--abyss-rgb),0),rgba(var(--abyss-rgb),.92) 55%)}
/* The copy column now runs prose → ticks → subhead → ticks in one panel,
   which nothing else on the page does. A .d3 landing straight on the tick
   above it reads as the last, larger item in that list. */
/* And the paragraph above them. Same global measure, same argument as the
   footnote below — .page-dense p{max-width:52ch} was setting it to 416px
   inside a 639px panel (James, 28 Aug 2026 — "stretch across the whole
   box"). It is the only paragraph in a panel whose width is already the
   design's choice, so the measure has nothing to add. */
.softmap>div>p{max-width:none}
.softmap .ticks+.d3{margin-top:clamp(28px,3.4vh,40px)}

/* — ONE TOOL, TWO JOBS —
   Lifted out of the prose column beside the map (James, 28 Aug 2026) so the
   head and the map row fit a laptop screen. Full width, and the two ticks
   run side by side rather than stacking, which is the whole height saving:
   the same words are two columns of three lines instead of one column of
   six. Same translucent ground as the columns above it, because this
   section is set over a photograph and a hairline alone will not hold a
   panel on one. The minmax floor is where a tick's own text stops fitting
   two to a row; below it they stack, which is correct on a phone. */
.softjobs{margin-top:clamp(40px,5.5vw,88px);
  padding-top:clamp(34px,4vw,48px);border-top:1px solid var(--line-signal);
  background:linear-gradient(180deg,rgba(var(--abyss-rgb),.72),rgba(var(--abyss-rgb),.6));
  backdrop-filter:blur(6px);
  border-left:1px solid var(--line);border-right:1px solid var(--line);
  border-bottom:1px solid var(--line);border-radius:var(--r-md);
  padding-right:clamp(24px,3vw,36px);padding-bottom:clamp(24px,3vw,36px);
  padding-left:clamp(24px,3vw,36px)}
/* Two columns, stated rather than auto-fit. auto-fit with a 340px floor
   resolved to THREE tracks at 1240 and left an empty one on the end, and at
   other widths it put both ticks in the left half of a full-width row —
   which is the opposite of "stretch across the full page". Two items, two
   columns, and one below the width where a tick's own text stops fitting. */
.softjobs .ticks{grid-template-columns:1fr;gap:clamp(18px,2.2vw,32px)}
@media(min-width:1024px){
  .softjobs .ticks{grid-template-columns:1fr 1fr}
}
/* max-width:none on both — the global .page-dense p{max-width:52ch} was
   setting this line to 324px inside a 1240px row (James, 28 Aug 2026 —
   "stretch across the full page"). It is a footnote across the foot of a
   full-width block, not a column of body copy. */
.softjobs .fine{margin-top:clamp(24px,2.8vh,34px);max-width:none;
  padding-top:clamp(18px,2.2vh,26px);border-top:1px solid var(--line)}
.softjobs .ticks li{max-width:none}
.ticks{list-style:none;display:grid;gap:12px;margin-top:24px}
.ticks li{position:relative;padding-left:24px;font-size:.92rem;color:var(--fog);line-height:1.55}
.ticks li::before{content:"";position:absolute;left:0;top:.5em;width:10px;height:10px;
  border:1px solid var(--charge);border-radius:2px}
/* Was scoped to the validation split, where the only bolded ticks used to
   live. The
   software and stack sections now lead their ticks with a token too, and an
   unstyled <b> in --fog is indistinguishable from the clause after it. */
.ticks b{color:var(--white);font-weight:500}

.lf{border:1px solid var(--line-signal);border-radius:var(--r-md);
  background:rgba(var(--abyss-rgb),.78);backdrop-filter:blur(6px);padding:clamp(24px,3vw,34px)}
.lf-t{font-family:var(--instrument);font-size:.6rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--fog);padding-bottom:16px;margin-bottom:22px;border-bottom:1px solid var(--line)}
.lf .row{display:grid;grid-template-columns:1fr;gap:8px;margin-bottom:22px}
.lf .k{font-size:.85rem;color:var(--mist)}
.lf .t{display:block;height:10px;border-radius:5px;background:rgba(255,255,255,.07);overflow:hidden}
/* Filled by default and emptied only while a JS-driven reveal is pending —
   never the other way round. Spec D1: the content is visible, the animation
   is the enhancement, and a failed observer must not leave empty bars. */
.lf .t i{display:block;height:100%;width:var(--w);border-radius:5px;background:var(--ghost);
  transition:width 1.1s var(--ease-out)}
.lf .row.b .t i{background:var(--current)}
.js-reveal .lf[data-reveal]:not(.is-in) .t i{width:0}
.lf .v{font-family:var(--mono);font-size:.8rem;color:var(--white)}
.lf .foot{margin-top:26px;padding-top:18px;border-top:1px solid var(--line);
  color:var(--fog);font-size:.85rem;line-height:1.6;max-width:none}

/* ── X9 · QUESTIONS ────────────────────────────────────────────────────
   Native <details>: keyboard-operable and screen-reader-correct with no
   JavaScript, and it degrades to plain open sections if anything fails. */
.qgrid{display:grid;grid-template-columns:.62fr 1fr;gap:clamp(24px,3.4vw,54px);
  align-items:start}
.qshot{margin:0;position:relative;border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;position:sticky;top:132px}
.qshot img{width:100%;height:auto;aspect-ratio:4 / 5;object-fit:cover;object-position:center 22%;display:block;
  filter:grayscale(0) brightness(.98)}
.qshot figcaption{position:absolute;left:0;right:0;bottom:0;padding:16px 20px;
  font-family:var(--instrument);font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--mist);background:linear-gradient(180deg,rgba(var(--abyss-rgb),0),rgba(var(--abyss-rgb),.9))}
.qa{border-top:1px solid var(--line)}
.qa details{border-bottom:1px solid var(--line)}
.qa summary{list-style:none;cursor:pointer;padding:24px 44px 24px 0;position:relative;
  font-size:clamp(1rem,1.5vw,1.2rem);font-weight:400;color:var(--white);
  transition:color var(--t-fast)}
.qa summary::-webkit-details-marker{display:none}
.qa summary:hover{color:var(--current-lift)}
.qa summary:focus-visible{outline:2px solid var(--current-lift);outline-offset:3px}
.qa summary::after{content:"";position:absolute;right:8px;top:50%;width:11px;height:11px;
  margin-top:-7px;border-right:1px solid var(--current-lift);border-bottom:1px solid var(--current-lift);
  transform:rotate(45deg);transition:transform var(--t-fast) var(--ease-out)}
.qa details[open] summary::after{transform:rotate(-135deg);margin-top:-3px}
.qa .a{padding:0 0 26px}
.qa .a p{color:var(--mist);font-size:.95rem;line-height:1.65;max-width:78ch}
.qa .a p+p{margin-top:14px}
.qa .a .chip{vertical-align:middle;margin-left:2px}

/* X11 · CLOSE — the band came out with the section (James, 27 Aug 2026), and
   this was its only user: tech.css is loaded by this page alone, and carried
   its own copy of the interior layer's .xclose rules. The shared originals
   are untouched in interior.css, where the six other closing bands take
   them. */

/* ── RESPONSIVE ────────────────────────────────────────────────────────
   The pillar row halves rather than shrinks: a quarter-column below
   ~1080px cannot hold a readable measure, so it goes to two cards across
   and then to one. */
@media(max-width:1080px){
  .hardsplit{grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);gap:clamp(22px,2.6vw,34px)}
  .qgrid{grid-template-columns:.8fr 1fr}
  .sgrid{grid-template-columns:1fr 1fr}
}
@media(max-width:900px){
  .xhero{grid-template-columns:1fr;min-height:0}
  .xhero-copy{padding:clamp(104px,14vh,140px) clamp(24px,6vw,40px) clamp(34px,5vh,52px)}
  .xhero-pic{order:2;aspect-ratio:16 / 9}
  .xhero-pic::after{background:linear-gradient(180deg,rgba(var(--abyss-rgb),.5),rgba(var(--abyss-rgb),0) 40%)}
  /* /goldilocks/ only. Its hero is shot from sea level, so a square source
     centre-cropped to 16/9 lands on the middle of the sails and cuts the
     hulls off the bottom of the band — sails and water, no ship. Pushing
     the crop down to 78% puts the waterline back in it. /technology/'s
     hero is a high aerial with the vessel already mid-frame and stays
     centred. */
  .xhero-pic--waterline img{object-position:50% 78%}

  /* Two cards across: the link no longer needs to line up with three
     others, but keeping it on the floor costs nothing. */
  .scard>p{font-size:.9rem}

  .factbar{grid-template-columns:1fr 1fr}
  .soft,.qgrid,.softmap{grid-template-columns:1fr}
  /* stacked, the render goes back to full width and stops being sticky —
     there is nothing beside it to hold against */
  /* Stacked, there is no second column to match, so the render goes back
     into flow at full width and sets its own 16/9 again. */
  .hardsplit{grid-template-columns:1fr}
  .hard-pin{position:static}
  .hard-pin .annot{position:static;height:auto}
  .hard-pin .annot>img{height:auto;aspect-ratio:16 / 9}
  .qshot{position:relative;top:auto;max-width:420px}
  .qshot img{aspect-ratio:16 / 10}
}
@media(max-width:620px){
  /* .factbar keeps the two columns it takes at 900px — see the note on the
     same rule in interior.css. */
  /* One card across. The picture goes wide-format at full width — 4:3 at
     375px is a very tall photograph to scroll past four times. */
  .sgrid{grid-template-columns:1fr;gap:clamp(34px,6vh,48px)}
  .scard-pic{aspect-ratio:16 / 9}
  /* The drawing keeps a taller box: letterboxed into 16:9 at 375px it is a
     thumbnail of a ship, which is the one thing it exists not to be. */
  .scard-pic.sheet{aspect-ratio:4 / 3}
  .scard .tlink{padding-top:16px}
}

/* Reduced motion: the load-factor bars are informative, so they are shown
   at full length immediately rather than not shown at all. */
@media(prefers-reduced-motion:reduce){
  .lf .t i{transition:none;width:var(--w)}
}


/* ── PLACEHOLDER FIGURES ───────────────────────────────────────────────
   Deliberately unfinished. Dashed rule, blueprint grid, the shot written
   across the middle and a chip in the corner — so it cannot be mistaken
   for a design choice, and so it doubles as the shot list for whoever
   commissions the photography. See _includes/partials/figures.njk. */
.ph{position:relative;margin:0;border:1px dashed var(--line-strong);border-radius:var(--r-md);
  background:
    linear-gradient(rgba(120,163,214,.05) 1px,transparent 1px) 0 0/100% 22px,
    linear-gradient(90deg,rgba(120,163,214,.05) 1px,transparent 1px) 0 0/22px 100%,
    rgba(14,58,92,.4);
  aspect-ratio:var(--ph-ratio,4 / 3);overflow:hidden}
.ph-in{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:16px;padding:clamp(18px,2.4vw,30px);
  text-align:center}
.ph-glyph{width:clamp(38px,4vw,52px);height:auto;flex:none;opacity:.5}
.ph-glyph .f{fill:none;stroke:var(--line-strong);stroke-width:1.5;stroke-dasharray:5 5}
.ph-glyph .l{fill:none;stroke:var(--current-lift);stroke-width:1.5}
.ph-glyph .c{fill:none;stroke:var(--current-lift);stroke-width:1.5;stroke-linecap:round}
.ph figcaption{display:grid;gap:7px}
.ph-label{font-size:.9rem;color:var(--mist);line-height:1.4}
.ph-note{font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;color:var(--ghost);
  line-height:1.5}
.ph-chip{position:absolute;top:12px;left:12px;
  font-family:var(--mono);font-size:.56rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--warn);border:1px solid rgba(255,180,84,.4);border-radius:999px;
  padding:4px 10px;background:rgba(var(--abyss-rgb),.7)}

/* ── X3b · THE RESOURCE BAND ───────────────────────────────────────────
   A photographic breath between two long reading sections. */
/* The statement band keeps more air than a section does — it is one
   sentence over a photograph and the space is the point — but 15vh was
   135px each side against neighbours that now use 81, which made it read
   as a gap with a sentence in it. 11vh is ~99px. */
.band{position:relative;overflow:hidden;padding:clamp(64px,11vh,132px) 0;text-align:center}
.band>img{position:absolute;left:0;top:-10%;width:100%;height:120%;object-fit:cover;z-index:0;
  filter:brightness(.84);will-change:transform}
.band .sc{position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(var(--abyss-rgb),.84),rgba(var(--abyss-rgb),.32) 45%,rgba(var(--abyss-rgb),.86))}
.band .wrap{position:relative;z-index:2}
.band .big{font-size:clamp(1.4rem,3.2vw,2.4rem);font-weight:300;line-height:1.3;
  max-width:22ch;margin-inline:auto;text-shadow:0 2px 30px rgba(var(--abyss-rgb),.9)}
/* The band left on this page carries a sentence rather than a statistic. .q
   is a mono treatment sized for "92%" and looks like a readout when a clause
   is put through it, so the emphasis here is a colour shift at the same
   weight — and the measure widens, because 22ch was set for four characters
   and a noun. */
.band.stmt .big{max-width:28ch}
.band.stmt .big .q{font-family:inherit;font-weight:400;letter-spacing:inherit}
.band .q{font-family:var(--mono);font-weight:500;color:var(--current-lift);letter-spacing:-.03em}
.band .attr{text-shadow:0 1px 18px rgba(var(--abyss-rgb),.95);
  margin:22px auto 0;font-family:var(--instrument);font-size:.68rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--fog);
  display:flex;align-items:center;justify-content:center;gap:12px;max-width:none}

/* ── THE LCoH PARAMETER TREE ───────────────────────────────────────────
   "We optimise for levelised cost" is not a claim until you can see what is
   being traded against what. Four columns in the order the design loop
   traverses them: what can be changed, what that does to the ship, what the
   ocean does to that, and what the whole thing costs.

   Set as small type on purpose. It is a reference exhibit, not a reading
   passage — the density IS the argument, and anyone who wants a single
   parameter will scan for it rather than read the column. */
.lcohtree{border:1px solid var(--line);
  border-radius:var(--r-md);background:var(--schematic);overflow:hidden}
.lcohtree .lt-head{padding:clamp(24px,3.2vw,40px) clamp(24px,3.2vw,40px) clamp(20px,2.6vw,30px);
  border-bottom:1px solid var(--line)}
/* The 64ch measure came off on James's note, 28 Aug 2026 — "stretch across".
   It is a standfirst across the head of a four-column exhibit, not a column
   of body copy: at 64ch it set to roughly half the panel and stacked to five
   lines with the other half empty, and it now carries the datapoints
   sentence as well. Same argument as .wpd-f above and .vcap p in site.css. */
.lcohtree .lt-head p{color:var(--mist);font-size:.92rem;line-height:1.62;
  max-width:none;margin-top:14px}
.lcohtree .lt-cols{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line)}
.lt-col{background:var(--schematic);padding:clamp(20px,2.2vw,28px)}
.lt-col h4{font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--current-lift);font-weight:500;
  padding-bottom:13px;margin-bottom:16px;border-bottom:1px solid var(--line)}
.lt-g{margin-bottom:16px}
.lt-g:last-child{margin-bottom:0}
.lt-g b{display:block;font-size:.82rem;font-weight:500;color:var(--white);margin-bottom:5px}
.lt-g span{display:block;font-size:.76rem;line-height:1.62;color:var(--fog)}
/* The one output cell. Everything above it is an input, and the column has
   to end on the thing they are all traded against or the tree has no point. */
.lt-g.out{margin-top:20px;padding-top:16px;border-top:1px solid var(--line-signal)}
.lt-g.out b{color:var(--charge)}
.lt-g.out span{color:var(--mist)}
/* The METHOD caveat that used to close the panel came out on James's note,
   28 Aug 2026 — "remove". Its rules went with it; if it ever comes back it
   was a .fine paragraph inside .lcohtree with the chip leading it as a label
   rather than trailing it as a sentence. */

@media(max-width:900px){
  .lcohtree .lt-cols{grid-template-columns:1fr 1fr}
}
/* The tree stays two-up all the way down (James, 28 Aug 2026 — "I don't
   like it all stacked"). Stacked, the four columns ran 1,715px on a 375px
   phone: the reader scrolled five screens to cross a diagram whose whole
   point is that you can see the four stages at once. Two-up is ~168px a
   column, which the mono labels and short lists sit in, and it halves the
   height. */

/* — the investor row under the tree —
   James, 28 Aug 2026: "Add a call to action here for investors". It sits
   OUTSIDE the tree's border rather than inside its foot, where the METHOD
   line used to be, because an ask inside an exhibit reads as part of the
   exhibit. One line and one button on a row, wrapping to two on a phone. */
.treecta{display:flex;align-items:center;justify-content:space-between;
  gap:clamp(18px,2.4vw,32px);flex-wrap:wrap;
  margin-top:clamp(26px,3.4vh,40px);padding:clamp(22px,2.6vw,30px) clamp(24px,3.2vw,40px);
  border:1px solid var(--line-signal);border-radius:var(--r-md);
  background:linear-gradient(140deg,rgba(62,140,255,.08),transparent 62%)}
/* max-width:none — the global p measure again. This is one line opposite a
   button, not body copy, and the measure pre-wrapped it. */
.treecta p{margin:0;max-width:none;color:var(--white);font-size:1.02rem;line-height:1.5}
.treecta .btn{flex:0 0 auto}

/* — the closing doorway to /goldilocks/ —
   The page's last band (James, 28 Aug 2026). Centred, because it is a
   handover rather than a section of argument, and it carries no image: the
   band above it is a dense accordion and the one thing this has to do is
   be a clear exit. .xclose in interior.css does the same job with a photo
   behind it, but /technology/ does not load that file. */
.gocta{text-align:center;border-top:1px solid var(--line);
  padding:clamp(56px,8vh,104px) 0;
  background:linear-gradient(180deg,rgba(62,140,255,.06),transparent 70%)}
.gocta .eyebrow{justify-content:center}
.gocta h2{margin-inline:auto;max-width:26ch}
.gocta h2 em{color:var(--current-lift);font-style:normal;display:block}
.gocta p{margin:20px auto 0;max-width:66ch;color:var(--mist)}
.gocta .btn{margin-top:clamp(28px,3.6vh,40px)}

/* — the junction into Diligence —
   James, 28 Aug 2026: "Reduce gap". The LCoH section ends on a bordered
   panel and the Diligence head follows it, so the standard 12vh foot plus
   12vh head put roughly 200px between two things that belong together.
   Scoped to the junction, like .stagesec + .hardsec above. */
#lcoh{padding-bottom:clamp(34px,4.6vh,64px)}
#questions{padding-top:clamp(34px,4.6vh,64px)}

/* ══════════════════════════════════════════════════════════════════════════
   DEEP DIVE — X6a…X6e
   Charts here are drawn in CSS and markup, not dropped in as images. The
   source charts (docs/Tech and proof of concept) are matplotlib defaults on
   white, and the route maps are screenshots over licensed basemap tiles —
   neither belongs on the page. The numbers are ours; the styling is not.
   ══════════════════════════════════════════════════════════════════════ */

/* — small print under a claim — */
.fine{margin-top:20px;font-size:.78rem;line-height:1.6;color:var(--ghost)}
.fine .chip{margin-left:4px;vertical-align:middle}

/* — WIND POWER DENSITY DISTRIBUTIONS ————————————————————————————————
   The analysis plot itself — Drift_Vs_Turbine_windpwr_plot.png, one year of
   ERA5 at a fixed point off western Scotland — rather than a redraw of it.
   It comes with a white ground, so it is shown on one: the same rule as the
   general arrangement in the pillar row, where a drawing keeps the sheet
   it was made on instead of being tinted to match the page. A matplotlib
   figure recoloured to brand palette stops being the evidence and becomes an
   illustration of the evidence.

   Its own legend does not survive being set at column width, which is why
   the three means are repeated as HTML text underneath — that row is also
   the only accessible route to the numbers. No .wpd-t label above it either:
   the plot is titled already, and the panel label would have restated the
   title. The plot takes the larger share of the row because its axis type is
   baked in and cannot be set larger on its own. */
.wpdplot{margin:0;border:1px solid var(--line-signal);
  border-radius:var(--r-md);background:rgba(var(--abyss-rgb),.5);
  padding:clamp(18px,1.9vw,24px)}
.wpd-sheet{background:#fff;border:1px solid var(--line);border-radius:2px;
  padding:clamp(6px,.7vw,10px);overflow-x:auto;min-width:0}
/* The type in the plot is part of the raster, so it cannot be set larger for
   a narrow column — below about 600px of drawing width the axis labels stop
   being readable at all. So the sheet scrolls at that point rather than
   shrinking the plot into decoration. */
/* And the same cap the film frame carries in site.css, for the same review
   note and the same reason: on a laptop the drawing is sized by width alone
   and the panel ends up taller than the screen under it (James, 28 Aug 2026).
   The subtrahend is everything the panel shares the screen with — the fixed
   header, the section head, the number row and the footnote under the plot.
   The 1706/1000 is the drawing's own 1006x590 aspect ratio.

   The cap is on the WIDTH, and .vframe's note in site.css says why: width
   here is definite (100%), so a max-height clamps the height and leaves the
   width where it was — which squashed the plot to 705x352 against a native
   1.71 ratio. Capping width shrinks the drawing whole.

   Short viewports only: at 1080 of height the cap computes past the width
   the panel gives it and nothing moves. min-width wins over max-width when
   the two disagree, so the floor is still a 600px-wide drawing in a sheet
   that scrolls, rather than one shrunk into decoration. */
.wpd-sheet img{display:block;width:100%;min-width:600px;height:auto;
  max-width:calc((100svh - 548px) * 1706 / 1000);margin-inline:auto}

/* The anchor is the plot's frame, not a link in prose — no underline, no
   colour, and a zoom cursor so a mouse knows before it clicks. It must not
   set its own width: it wraps the image inside the sheet that pans, so it
   takes the image's. */
.wpd-open{display:block;cursor:zoom-in;text-decoration:none;border:0}
.wpd-open:focus-visible{outline:2px solid var(--current-lift);outline-offset:2px}

/* The cue. Quiet on a laptop, where the plot is already legible and this is
   just an offer; it earns its place on a phone, where the drawing is bigger
   than the screen and the reader has no other way of knowing that tapping
   it does anything (James, 28 Aug 2026 — "need to encourage mobile users to
   do this"). Hence the size and colour step below 620px, not a second
   element. */
/* NOT display:flex. As a flex row every text node in the line becomes its
   own item, so "Tap" and "the chart to enlarge" were two boxes and the line
   broke between them on a phone. It is one sentence with a glyph after it —
   ordinary inline layout is what sets that. */
.wpd-cue{margin:10px 0 0;max-width:none;
  font-family:var(--mono);font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ghost)}
.wpd-cue svg{width:12px;height:12px;margin-left:7px;vertical-align:-2px;
  fill:none;stroke:currentColor;stroke-width:1.7}
/* "Tap" or "Click", by input rather than by width: a touchscreen laptop at
   1200px is tapped, a phone in a desktop-width emulator is not. */
.cue-t{display:none}
@media(hover:none){
  .cue-t{display:inline}
  .cue-c{display:none}
}
@media(max-width:620px){
  .wpd-cue{margin-top:12px;font-size:.66rem;color:var(--current-lift)}
  .wpd-cue svg{width:14px;height:14px;vertical-align:-3px}
}

.wpk{list-style:none;display:grid;gap:clamp(12px,1.4vw,20px);
  grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  margin-top:clamp(20px,2.6vh,28px)}
.wpk li{padding-left:14px;border-left:2px solid var(--ghost)}
/* Two across on a phone, with the apparent-wind figure — the one the whole
   panel is about — taking the full width underneath (James, 28 Aug 2026:
   "I don't like it all stacked"). auto-fit at a 158px floor gave one column
   inside a 300px panel, so three numbers cost three rows. The two means
   being compared now sit side by side, which is the comparison, and the
   result sits under them. */
@media(max-width:620px){
  .wpk{grid-template-columns:1fr 1fr;gap:14px 12px}
  .wpk li.aws{grid-column:1 / -1}
}
.wpk li.tws{border-left-color:var(--fog)}
.wpk li.aws{border-left-color:var(--current)}
.wpk b{display:block;font-family:var(--mono);font-size:1.18rem;font-weight:500;color:var(--white);
  line-height:1.1}
.wpk li.aws b{color:var(--current-lift)}
.wpk span{display:block;margin-top:6px;font-size:.78rem;line-height:1.5;color:var(--fog)}
/* max-width:none because the global p{max-width:46ch} — 52ch on a dense
   page — was winning here and is the wrong rule for this line (James,
   28 Aug 2026 — "stretch across the box"). It is a footnote across the foot
   of a panel, not a column of body copy: the measure was setting it to 354px
   inside a 713px panel and stacking it to four lines with half the panel
   empty beside it. Same argument, and the same fix, as .vcap p in site.css.
   Two selectors deep to beat .page-dense p. */
.wpdplot .wpd-f{margin-top:clamp(20px,2.6vh,28px);padding-top:18px;
  border-top:1px solid var(--line);max-width:none;
  color:var(--fog);font-size:.85rem;line-height:1.6}
.wpd-f em{color:var(--current-lift);font-style:normal;font-weight:500}
.wpd-f .chip{margin-left:6px;vertical-align:middle}

/* The plot goes BESIDE the prose rather than above it. Stacked, the section
   ran longer than the bar chart it replaced; side by side it is shorter than
   either, which is what "compress this down" has to mean once a graph has
   been added to it.

   Two blocks, two arrangements, one grid — because where the plot can go
   depends on how much width it needs to stay readable:

   ≥1280px  plot | prose   the plot holds ~0.67 of native and the head plus
                          the panel fit one laptop screen
   901—1279 plot          the plot takes the full measure to stay legible
            prose         and the prose sits under it
   ≤900px   the same, stacked

   The rated-power block that used to be the third area came out on James's
   note, 28 Aug 2026 — "remove this" — so the middle band no longer has two
   text blocks to pair up underneath, and the section is a row shorter at
   every width. In that band the whole panel is capped rather than the sheet
   inside it: given the full measure the plot renders near native size and
   the section runs half a screen longer, and capping only the sheet leaves
   the number row stretched out beside a short drawing. */
/* minmax(0,1fr), NOT 1fr — and .wpdplot/.wpd-sheet carry min-width:0 with
   it (James, 28 Aug 2026: "the physics graph and text to be resized" on
   mobile). A grid item's automatic minimum is its min-content, and this
   item contains an image with a 600px floor on it, so a plain 1fr track
   grew to fit that floor: on a 375px phone the panel laid out 652px wide.
   site.css sets body{overflow-x:hidden}, so it did not scroll — it was
   simply CUT, and it took the panel's own text with it. The number row and
   the footnote were laid out to 616px and lost their right-hand halves off
   the screen edge, which is why the note names the graph and the text.

   The floor on the image stays. Its axis labels are baked into the raster
   and stop being readable below ~600px of drawing, which is the whole
   reason .wpd-sheet scrolls; with these three declarations the sheet is the
   only thing that scrolls, the panel fits the column, and the reader pans
   the plot inside its own frame. Verified at 375px: page scrollWidth back
   to 375 from 671, panel 338, sheet pans 300 → 612. */
.physgrid{display:grid;gap:clamp(26px,3.4vw,44px);align-items:start;
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"plot" "copy"}
.physgrid .wpdplot{min-width:0}
.physgrid .wpdplot{grid-area:plot}
.physgrid .physcopy{grid-area:copy}
.physcopy p{color:var(--mist)}
.physcopy .ticks{margin-top:20px}
@media(min-width:901px){
  .physgrid .wpdplot{max-width:800px}
}
@media(min-width:1280px){
  .physgrid{grid-template-columns:minmax(0,1.3fr) minmax(0,.7fr);
    grid-template-areas:"plot copy"}
  .physgrid .wpdplot{max-width:none}
}
.physgrid .physcopy{padding-top:2px}

/* — the section's height, on a laptop —
   The blocks and the plot inside them are as short as they go without losing
   the drawing's legibility, so what is left is the space AROUND them
   (James, 28 Aug 2026 — "this module doesn't all fit on my laptop screen").
   The standard 12vh section foot and head plus a 64px head margin spend
   about 150px on nothing. Scoped to #physics: every other section keeps the
   page's rhythm. */
#physics{padding:clamp(52px,7.5vh,96px) 0}
#physics .sechead{margin-bottom:clamp(24px,3vh,34px)}

/* — validation stat grid — */
/* The margin is on the TOP now, not the bottom: the grid moved below the
   roster row on 28 Aug 2026, so it is the last thing in the section and a
   bottom margin was adding to the section's own foot. */
.vgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;margin-top:clamp(38px,6vh,64px)}
.vstat{background:var(--abyss);padding:26px 22px 24px}
/* TWO ACROSS ON A PHONE (James, 28 Aug 2026 — "I don't like it all
   stacked"). auto-fit wants 168px a tile and 375px of phone leaves 338 for
   the grid, one pixel short of two tracks plus their rule, so six tiles fell
   into six rows: 677px to read six numbers. Forcing the pair costs each tile
   ~11px of width and gets the block back to something you can take in.
   Padding comes in with it, or a 168px tile is mostly padding. */
@media(max-width:620px){
  .vgrid{grid-template-columns:1fr 1fr}
  .vstat{padding:20px 16px 18px}
}
.vstat b{display:block;font-size:clamp(1.5rem,3.4vw,2.15rem);font-weight:300;
  letter-spacing:-.03em;color:var(--white);line-height:1}
.vstat span{display:block;margin-top:12px;font-size:.8rem;line-height:1.5;color:var(--fog)}
/* One column where the validation split used to be two. The measure is held
   to what a half-column gave it, so the paragraph does not run the width of
   the page now that it has the width available. */
.vsolo{max-width:68ch}
.vsolo h3{margin-bottom:16px}
.vsolo p{color:var(--mist)}

/* — the Cape Horn CFD reel —
   The frame, the ring and the caption row are the shared component in
   site.css (── FILM FRAME); all this does is give it its head and rule it
   off from the pillar cards above, and it now closes the section. */
.cfdfilm{margin-top:clamp(44px,7vh,76px);padding-top:clamp(38px,6vh,60px);
  border-top:1px solid var(--line)}
/* Head and lede centre on the frame rather than sitting flush to the column
   (James, 27 Aug 2026). Once the frame was capped by viewport height it
   stopped filling the column, so a left-flush head above a centred frame put
   two different left edges in the same block. This is the one centred head
   on the page, and it is centred because the picture under it is. */
.cfdfilm h3{margin-bottom:16px;text-align:center}
/* Still wider than the page's 60ch reading measure — a column much narrower
   than the frame below reads as an indent rather than as a lede — but no
   longer 88ch, which now overhangs the capped frame on either side. */
.cfdfilm .lede{color:var(--mist);text-align:center;margin-inline:auto;
  max-width:min(100%,74ch);margin-bottom:clamp(26px,4vh,40px)}

/* The frame is capped by viewport HEIGHT as well as by the column. At the
   full column width a 16/9 frame is ~650px tall, which put the chapter strip
   below the fold on any laptop — and a strip you have to scroll to find
   cannot do its job, which is to tell you before you watch that the reel has
   two halves. 60vh keeps the film and both buttons on screen together.

   The strip and the caption take the same width so they stay aligned to the
   frame rather than running out past it. Centred, because a media block
   narrower than the lede above it reads as ragged when it is left-flush. */
.cfdfilm .vframe,
.cfdfilm .chapters,
.cfdfilm .vcap{width:min(100%,calc(60vh * 16 / 9));margin-inline:auto}

/* — utilisation chart —
     19 bars is too many for a label each, so only the peak band is called
     out and the axis carries every 100km. --h is set inline per bar. */
.util{margin:0 0 clamp(34px,5vh,54px);border:1px solid var(--line-signal);
  border-radius:var(--r-md);padding:clamp(22px,3vw,34px);background:rgba(255,255,255,.02)}
.util-t{font-family:var(--instrument);font-size:.6rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ghost);margin-bottom:26px}
.util-plot{display:grid;grid-template-columns:auto 1fr;gap:14px}
.util-axis{display:flex;flex-direction:column;justify-content:space-between;
  height:210px;font-family:var(--mono);font-size:.62rem;color:var(--ghost);text-align:right}
.util-bars{display:flex;align-items:flex-end;gap:clamp(2px,.6vw,7px);height:210px;
  border-bottom:1px solid var(--line);border-left:1px solid var(--line);padding-left:6px}
.ub{position:relative;flex:1 1 0;min-width:0;height:var(--h);border-radius:3px 3px 0 0;
  background:linear-gradient(to top,rgba(62,140,255,.30),rgba(62,140,255,.66));
  transition:height 900ms var(--ease-out)}
.ub.pk{background:linear-gradient(to top,rgba(62,140,255,.55),var(--current))}
.js-reveal .util[data-reveal]:not(.is-in) .ub{height:0}
.ub .uv{position:absolute;left:50%;top:-19px;transform:translateX(-50%);
  font-family:var(--mono);font-size:.58rem;color:var(--ghost);opacity:0}
.ub.pk .uv{opacity:1;color:var(--current-lift)}
.ub:hover .uv{opacity:1;color:var(--white)}
.ub .ul{position:absolute;left:50%;bottom:-21px;transform:translateX(-50%);
  font-family:var(--mono);font-size:.55rem;color:var(--ghost);opacity:0}
.ub:nth-child(odd) .ul{opacity:.85}
.util-x{margin-top:30px;padding-left:52px;font-family:var(--instrument);font-size:.58rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--ghost)}
.utnote .lede b{color:var(--current-lift);font-weight:500}

/* UNUSED as of 28 Aug 2026. Its only instance was the rated-power block in
   the physics section, removed on James's note. Kept because it is the panel
   style the LCoH argument used and the block may come back; delete it if the
   page ships without one. .lcohtree below is a different component. */
.lcoh{border:1px solid var(--line-signal);border-radius:var(--r-md);
  padding:clamp(26px,4vw,42px);background:linear-gradient(140deg,rgba(62,140,255,.08),transparent 62%)}
.lcoh h3{margin-bottom:16px}
.lcoh h3 em{color:var(--current-lift);font-style:normal}
.lcoh p{color:var(--fog);line-height:1.68;max-width:76ch}
.lcoh .fine{color:var(--ghost)}

/* — the five-part stack —
   Lives inside the software section now, ruled off from the routing copy
   above it. Everything below the rule is over the same photograph as the
   two prose columns, so the tiles and the two rooms take the same
   translucent ground: on bare image, a hairline border does not hold a
   panel together. */
.stackin{margin-top:clamp(48px,7vh,84px);padding-top:clamp(38px,5.5vh,60px);
  border-top:1px solid var(--line)}
.stackin>.lede{color:var(--mist);max-width:74ch;margin:14px 0 clamp(30px,4.4vh,44px)}
.stack5{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));
  gap:14px;margin-bottom:clamp(38px,6vh,64px)}
.s5{border:1px solid var(--line);border-radius:var(--r-sm);padding:22px 20px;
  border-top:2px solid var(--current);
  background:rgba(var(--abyss-rgb),.66);backdrop-filter:blur(6px)}
.s5 b{display:block;font-size:.92rem;font-weight:500;color:var(--white);margin-bottom:10px}
.s5 span{font-size:.8rem;line-height:1.55;color:var(--fog)}

/* — on board / ashore — */
/* Two cards, two columns, stated rather than auto-fit — which resolved to
   three tracks at 1240 and carried an empty 0px one on the end. */
.ctrlgrid{display:grid;grid-template-columns:1fr;gap:clamp(24px,3vw,38px)}
@media(min-width:901px){
  .ctrlgrid{grid-template-columns:1fr 1fr}
}
.ctrlgrid>article{background:rgba(var(--abyss-rgb),.66);backdrop-filter:blur(6px);
  border:1px solid var(--line);border-radius:var(--r-md);
  padding:clamp(24px,3vw,36px);overflow:hidden}
.ctrlgrid h4{margin:10px 0 14px}
.ctrlgrid p{color:var(--fog);line-height:1.66}
/* — the picture at the head of each card —
   Full-bleed to the card's edges rather than inset inside its padding: a
   picture with a border inside a bordered panel draws two frames around one
   thing. Negative margins take it back out to the panel edge and the card's
   own radius clips it, which is why .ctrlgrid>article carries overflow:hidden.
   The same ratio on both, so the two cards start at the same depth whatever
   is in the frame — one is a render, the other a pending-image placeholder.

   A wide crop rather than 16/9, and the reason is the scroll (James,
   28 Aug 2026 — "doesn't sit well in the scroll"). These two panels are the
   longest reading on the page; at 16/9 the picture put each card at 880px,
   which is a whole laptop screen for one card and the reader never sees a
   card entire. At 2.4/1 they land near 800 and a card fits the fold. The
   render is a wide seascape and loses nothing to the crop. */
.ctrlpic{margin:calc(-1 * clamp(24px,3vw,36px));margin-bottom:clamp(22px,2.6vw,30px);
  aspect-ratio:2.4 / 1;overflow:hidden;background:var(--schematic)}
.ctrlpic img{display:block;width:100%;height:100%;object-fit:cover}
/* The placeholder brings its own dashed frame and radius, both of which are
   wrong once it is flush in a panel: it IS the top of the card here. */
.ctrlpic .ph{position:absolute;inset:0;margin:0;border-radius:0;border-width:0 0 1px 0;
  border-bottom-style:dashed}
.ctrlpic:has(.ph){position:relative}

@media(max-width:760px){
  .util-bars,.util-axis{height:168px}
  .ub .uv{font-size:.5rem;top:-16px}
  .ub .ul{display:none}
  .util-x{padding-left:0}
}
@media(prefers-reduced-motion:reduce){
  .ub{transition:none}
  .js-reveal .util[data-reveal]:not(.is-in) .ub{height:var(--h)}
}
