@import url("tokens.css");
/* Geist Mono added 2026-08-22. It is the DATA voice in DESIGN.md §4 — OER readouts,
   batch IDs, telemetry stamps, every mono eyebrow and button label — and it was
   named by 63 rules across seven stylesheets while never being downloaded. The whole
   telemetry register was silently falling back to Menlo. Caught by the FONT rule in
   qa/perf.py; three families is exactly the budget, so a fourth must displace one. */
/* 2026-08-23 -- pinned to the STATIC weights this codebase actually uses (400/500/
   600/700 body, opsz144 wght400+420 display, 400/500/600 mono), replacing the old
   variable RANGES (`wght@9..144,300..600` / `400..600` / `400..800`). Two independent
   reasons, neither about file size:
   1) The real bug this was chasing: every "," and "." sitewide was rendering with a
      false wide gap baked into the glyph's own advance box (measured on the live hero,
      `.hero .sub b`: period width 12.70px vs the "o" beside it at 11.95px -- as wide as
      a whole letter). Root cause turned out NOT to be the variable font at all -- see
      the `body` rule below, `font-variant-numeric:tabular-nums` was scoped to every
      glyph on the site, and Schibsted Grotesk's tabular figure set reassigns "." and
      ","'s width too (they are decimal-point / thousands-separator glyphs). Isolated
      A/B at IDENTICAL weight/size/webfont: tabular-nums 12.70px vs normal 5.92px.
   2) Static pinning is still worth doing on its own merits (smaller payload, faster
      parse, no per-weight interpolation risk for a family this site never needed as a
      free-ranging variable axis) -- kept even after finding the real cause.
   FORMAT NOTE: Google Fonts also accepts one `family=Name:wght@NNN` param per weight
   repeated with `&` (used below), rather than `family=Name:wght@A;B;C` with the weights
   semicolon-joined inside one param -- both return identical static faces, but the
   semicolon form breaks `qa/qahtml.py`'s `@import` tokenizer: its prelude scan
   (`re.match(r'@([a-zA-Z-]+)([^;{]*)', ...)`) stops at the FIRST literal `;`, including
   one sitting inside the quoted URL string, silently truncating the parsed URL before
   `Geist+Mono`/`Schibsted+Grotesk` and producing false "never requested" FONT failures
   in qa/perf.py. qa/** is out of this lane's remit -- worked around here instead of
   fixed there. Family count is unchanged (still 3, still what qa/perf.py's FONT rule
   above counts) -- only the weight list per family changed. */
@import url("https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@144,400&family=Fraunces:opsz,wght@144,420&family=Geist+Mono:wght@400&family=Geist+Mono:wght@500&family=Geist+Mono:wght@600&family=Schibsted+Grotesk:wght@400&family=Schibsted+Grotesk:wght@500&family=Schibsted+Grotesk:wght@600&family=Schibsted+Grotesk:wght@700&display=swap");
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
/* font-variant-numeric:tabular-nums used to live HERE, on body -- scoped to every glyph on
   the site, not just numerals. Root cause, proven by isolated A/B at identical weight/size/
   webfont (Schibsted Grotesk 600 static, 20px): tabular-nums 12.70px period vs normal 5.92px
   period -- Schibsted Grotesk's tabular figure set reassigns the PERIOD and COMMA (decimal
   point / thousands separator glyphs) to the wide tabular-width slot too, and CSS
   font-variant-numeric has no way to say "only inside an actual number" -- it arms the
   OpenType tnum feature for every glyph in the element's text run. Applied to <body> this
   silently doubled the width of "." and "," in every paragraph on the site (a design gap:
   the register was never seen because a comma/period reads as isolated ink either way).
   DESIGN.md §4 scopes tabular-nums to the DATA voice only ("Geist Mono ... the telemetry
   voice: OER readouts, batch IDs, coordinates") -- moved to .mono below (already had it) and
   to .stat .v (the counted numerals motion.js animates, which DO need stable tabular digit
   widths so a count-up doesn't jitter in width frame to frame). Nothing else should carry it. */
body{margin:0;background:var(--deep-water);color:var(--ink);font-family:var(--f-body);
  font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased}
img,svg,video{max-width:100%;display:block}

/* A FAILED HERO IMAGE MUST NOT PAINT ITS ALT STRING ACROSS THE HERO.
   Abort the hero request and the <img> keeps its full-hero box ({complete:true,
   naturalWidth:0}); Chrome then lays the ~250-character alt string out inside that box
   at the inherited 17px. Measured 2026-08-22 by diffing the aborted-image render
   against the same render with the <img> set to display:none — the difference is, by
   construction, exactly the ink the broken image contributes:

       as shipped          4,423-10,699 alt-ink px, EVERY width 320-1440
       + background-color  4,423-10,699 px  (NO CHANGE — see below)
       + font-size:0             221-228 px  (-97.9%), confined to y0..15

   Two corrections to the original report, both measured. (1) It is not a <=560px
   problem: the ink is present at every width, worst at 940px. (2) It does not overlap
   the eyebrow/h1 boxes (worst 67px at 430px) — `.hero-copy::before` is z-index:-1
   INSIDE the z-index:1 `.hero>.wrap` stacking context, so that opaque .965 plateau
   sits ABOVE `.hero-media` and hides most of the string. What leaks is the plateau's
   feathered top edge (mask-image transparent 0 -> #000 11%), which is why the garbage
   lands in the band ABOVE the eyebrow rather than on it. Still a defect: three grey
   lines of prose across the top of the hero read as a broken page.

   background-color alone is REFUTED as a cure — it grounds the box but text still
   paints over it, and the measurement was byte-identical. It is kept anyway, doing the
   other half of the job: a failed hero degrades to brand ground deterministically
   rather than to whatever happens to be behind it. font-size:0 is what removes the
   text. The 228px residual is Chrome's broken-image icon in the top-left corner, which
   no CSS removes; leaving a small trace that the asset failed is preferable to hiding
   it. Alt remains in the accessibility tree — this changes rendering, not semantics.

   PLACEMENT: these two rules belong in home.css beside the rest of `.hero-media`, but
   home.css is the copy owner's file. The selectors match nothing on the other five
   pages, so living here is inert for them; move them when convenient. */
.hero-media{background-color:var(--deep-water)}
.hero-media img{font-size:0}

/* THE HERO TYPE HAD NO OVERFLOW-WRAP AT ALL.
   `.chain .pos h3` was given overflow-wrap:anywhere and `.creds dd` break-word; the
   hero — the largest type on the site, and the only type sitting over a photograph —
   got neither. Measured 2026-08-22 with "Pengoptimuman" (13 chars, an ordinary Malay
   word; a `ms/` directory exists, so this is a real localisation case and not a
   hypothetical) planted into h1/.sub/.hero-stamp:

       320px  .hero h1  overflow-wrap:normal  ->  35px past its own box,
                                                  35px past the opaque plateau,
                                                  15px off-screen entirely
       with overflow-wrap:break-word          ->   0px overflow, ink 11px INSIDE
                                                  the plateau at every width tested

   `break-word` rather than `anywhere`, matching the `.creds dd` precedent: it breaks a
   word only when it would otherwise overflow the line, and unlike `anywhere` it does
   not shrink the element's min-content width, so ordinary English headlines keep the
   line breaks they were composed with. Verified unchanged at 360/390/430/768/1440.

   Two measurement notes, so these numbers are not over-read. `.hero-stamp` reports a
   constant 180px of self-overflow at every width: that is its own `::before` plateau
   (right:-180px) inflating scrollWidth, NOT text, which is why its ink stays well
   inside the plateau. And docOverflow is 0 throughout — `.hero{overflow:hidden}` means
   the headline never scrolled the page, it was silently CLIPPED, which is worse: the
   defect is invisible to any page-overflow check.

   PLACEMENT: like the two rules above, these belong in home.css next to the other
   `.hero` rules but that file is the copy owner's. Scoping here is safe in a way the
   print `.hero{min-height:0}` reset was NOT — home.css never sets overflow-wrap on
   these selectors, so there is no later same-specificity declaration to lose to.
   Verified by measuring the shipped file, not by assuming the cascade. */
.hero h1,.hero .sub,.hero-stamp{overflow-wrap:break-word}
a{color:var(--airei-blue-lift);text-decoration:none}
h1,h2,h3,h4{margin:0;font-family:var(--f-display);font-weight:420;line-height:1.06;letter-spacing:-0.0068em}
p{margin:0 0 1em}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 var(--gut)}
.mono{font-family:var(--f-mono);font-variant-numeric:tabular-nums}
/* THE EYEBROW MUST SURVIVE WRAPPING, ON EVERY PAGE.
   This is a flex row: a 26x1px rule, then the label. `align-items:center` is right for
   the one-line case and wrong for the two-line case — it parks the dash at the vertical
   middle of the whole block, half a line below the text it introduces. Measured at
   390px as the dash's offset from the first line's optical centre:

       align-items:center      +8.7px  against an 18.4px line-height
       align-items:flex-start  -0.6px  (dash on the first line, where it belongs)

   home.css shipped this correction for itself on 2026-08-22 and it stayed there, so
   index and ms/index were right while smart-mill, demo and proof/minsawi kept the
   defect on four wrapped eyebrows. `.eyebrow` is declared HERE, so the correction
   belongs here. The three declarations are one calibrated set — the .72em is measured
   against the 1.55 line-height, not against the inherited 1.6 — so they travel together
   and must not be split up again. */
.eyebrow{font-family:var(--f-mono);font-size:13px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--crude-amber);display:flex;align-items:flex-start;gap:10px;line-height:1.55}
.eyebrow::before{content:"";width:26px;height:1px;background:var(--crude-amber);flex:none;
  margin-top:.72em}
:focus-visible{outline:2px solid var(--signal-gold);outline-offset:3px;border-radius:1px}
.skip{position:absolute;left:-9999px}
.skip:focus{left:var(--gut);top:12px;z-index:200;background:var(--signal-gold);color:var(--deep-water);
  padding:10px 16px;font-family:var(--f-mono);font-size:13px}
section{padding:clamp(64px,9vw,132px) 0;border-top:1px solid var(--hairline)}
section.tight{padding:clamp(44px,6vw,80px) 0}
.paper{background:var(--kernel-white);color:#16202B;border-top:0}
.paper h1,.paper h2,.paper h3{color:#0E1922}
.paper .eyebrow{color:#8A5E14}.paper .eyebrow::before{background:#8A5E14}
/* THE FOCUS RING IS THE FOURTH TOKEN THIS THEME HAS TO SUBSTITUTE, and it was the one left
   out. `:focus-visible` above paints --signal-gold #FFD658, drawn for Deep Water, where it
   measures 13.14:1. On Kernel White #FAFAF8 the same ring is 1.34:1 against the 3:1 that
   WCAG 2.2 SC 1.4.11 requires of a non-text indicator — so on a paper section the keyboard
   user's position on the page is, in practice, not shown.

   The site had already reached this conclusion and written it down twice — home.css, on
   `.creds .fold > summary:focus-visible` and again on `.creds a:focus-visible`: "#10598E for
   the focus outline (a gold ring on warm paper is not a visible focus indicator)". Both
   times the cure was scoped to `.creds`, which exists only inside #credentials on index and
   ms/index. Everywhere else the theme is used it kept the gold: measured 2026-08-27 with a
   real keyboard Tab, 16 indicators on mypalm, ms/mypalm, smart-mill and ms/kilang-pintar.
   That is the same shape as the #3D9AE0 link colour on this ground — a value corrected for
   one component instead of for the theme — so the correction belongs HERE, next to the other
   three substitutions, not in a fifth page-level rule.

   Nothing new is invented: #10598E is the paper-ground blue those home.css rules already
   name, from DESIGN.md §3b, and it measures 7.07:1 on Kernel White. Scoped to `.paper`, so
   the dark ground keeps the gold it clears 9.31-13.14:1 on.
   Measured after: 1622 indicators over 29 pages, 0 below 3:1. */
.paper :focus-visible{outline-color:#10598E}
.statband{display:grid;gap:clamp(24px,4vw,52px);grid-template-columns:repeat(auto-fit,minmax(232px,1fr))}
/* A THREE-STAT BAND MUST NEVER RESOLVE TO TWO TRACKS. `auto-fit` reads the CONTAINER, and
   between roughly 600px and 900px of viewport it lands on two — so the four three-figure
   bands on this site (smart-mill, traceability and both Malay twins) printed 2 + 1 with the
   third figure alone under a half-width rule. Measured at 820px: container 738, gap 32.8,
   three tracks need 761.6 and do not fit; at 900px container 810 and they do. It is the
   worst place on the site for a ragged row, because those three cells ARE the result claim.
   The same auto-fit gives FOUR tracks at 1440 for a three-item band, leaving a fourth track
   empty and the three figures crowded into three-quarters of the row.
   shell.css:209 already states the rule this follows: "with a KNOWN item count, auto-fit is
   the wrong tool". So a band that holds exactly three declares three above 700px and one
   below it, and is never two. Scoped with :has() so it reads the count rather than the page,
   and 1-item and 4-item bands (mypalm; index/ms-index, which carry their own explicit 1/2/4
   in home.css; the two .delta bands on minsawi) are untouched. Fails safe: where :has() does
   not resolve, the auto-fit above is what applies, exactly as before. */
@media (min-width:700px){
  .statband:has(> .stat:nth-child(3):last-child){
    grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(22px,2.8vw,44px)}
}
@media (max-width:699px){
  .statband:has(> .stat:nth-child(3):last-child){grid-template-columns:minmax(0,1fr)}
}
/* THE THREE FIGURES MUST SHARE ONE BASELINE. Three tracks make each ~195px at 700 and
   ~231px at 820, and there a .2em uppercase mono label wraps — pushing ITS figure down while
   its siblings stay, so the results print on two baselines 20-21px apart. Measured: [2,1,1]
   smart-mill 700; [1,1,2] traceability 760-880; [1,2,1] kebolehkesanan 700 past 900. At 820
   "470" and "411" sit level and "87%" floats a line below, in the band where the numbers ARE
   the claim. Not a min-height: that pays 21px of dead space at every width the labels fit.
   Subgrid shares the BAND's rows, so the label row is as tall as the tallest label and no
   taller — dH = +0.0px at 7 widths x 4 pages, +20.8px only on kebolehkesanan at 820. Needs
   row-gap:0, or the band's `gap` also lands between .k/.v/.u (+44 to +88px). @supports so a
   browser without subgrid keeps today's rendering, not a half-applied one. */
@supports (grid-template-rows:subgrid){
  @media (min-width:700px){
    .statband:has(> .stat:nth-child(3):last-child){grid-template-rows:auto auto auto;row-gap:0}
    .statband:has(> .stat:nth-child(3):last-child) > .stat{
      grid-row:span 3;display:grid;grid-template-rows:subgrid}
  }
}
.stat{padding-top:16px;border-top:1px solid var(--hairline-strong)}
.stat .k{font-family:var(--f-mono);font-size:13px;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-subtle);margin-bottom:14px}
.stat .v{font-family:var(--f-display);font-size:clamp(38px,5.4vw,68px);line-height:.94;
  letter-spacing:-0.0102em;color:var(--ink);font-variant-numeric:tabular-nums}
.stat .v em{font-style:normal;color:var(--signal-gold)}
.stat .u{margin-top:10px;font-size:14.5px;color:var(--ink-muted);line-height:1.45}
/* 11px floor, second pass. Every `.src` on the site is a CITATION — the line that says
   where a number came from — and it was the smallest reading text left at 10.5px. On a
   site whose whole argument is that each claim carries its source, the source line is the
   last thing to set below the floor. Tracking comes down to pay for the width. */
/* THIRD PASS, 2026-08-27 — 11px -> 12.5px, and the pass above is the reason, not a
   reversal of it. That pass named the principle and then stopped one rung short: 11px is
   still under 12px, which is where "too small to read comfortably" actually begins, so the
   citation was still the smallest reading text on the page — it had simply stopped being
   the smallest by the largest margin. Measured 2026-08-27 in Chromium across 25 pages:
   222 text nodes carrying a full SENTENCE were rendering between 11px and 11.5px, and the
   `.src` family is the bulk of them. A citation here is not a caption fragment: on
   news.html one `.src` runs 183 characters, on index.html the fold bodies carry 39-68.
   12.5px is the mono body rung this sheet already uses at `.appc .lede` and `.apps-note`,
   so nothing new enters the type scale — the citation simply joins the size that the rest
   of the mono prose is already set in. Uppercase LABELS stay at 11px throughout: a label
   is taken in as one shape and does not need the x-height a sentence needs. */
.stat .src{margin-top:8px;font-family:var(--f-mono);font-size:12.5px;letter-spacing:.02em;color:var(--ink-ghost)}
/* Same tap-target repair as `.proof-src a` in home.css, for the credit that closes every
   fold — including "Mill crew 66 -> 31", the block the client had open when the article
   would not open for him. At 390px this line wraps, and a tap at the centre of the box
   was landing on `P.src` rather than the anchor. Vertical padding on an inline element
   grows the hit area of BOTH line fragments without touching the line box, so the dead
   gutter between them closes and nothing reflows. Precedent in this tree: `.lead .src a`
   in news.css already carries a 44px min-height for the same reason. */
.stat .src a{color:var(--ink-subtle);border-bottom:1px solid var(--hairline-strong);
  padding-block:.35em}
.btn{display:inline-flex;align-items:center;gap:10px;font-family:var(--f-mono);font-size:13px;
  font-weight:600;letter-spacing:.09em;text-transform:uppercase;padding:15px 26px;border-radius:2px;
  border:1px solid transparent;cursor:pointer;transition:background var(--d2) var(--e-quad),
  border-color var(--d2) var(--e-quad),color var(--d2) var(--e-quad),transform var(--d1) var(--e-quad)}
.btn-primary{background:var(--signal-gold);color:#14202C}
.btn-secondary{background:transparent;color:var(--ink);border-color:var(--hairline-strong)}
/* Pin the hover colour too. Without it the button relies on the cascade to keep its dark
   ink, and any ancestor link rule (or a checker that cannot evaluate :not()) reads it as
   light-on-gold — 1.19:1. An explicit value cannot be reached by a different path. */
.btn-primary,.btn-primary:hover,.btn-primary:focus,.btn-primary:active{color:#14202C}
@media (hover:hover) and (pointer:fine){.btn-primary:hover{background:#FFE180;color:#14202C}
  .btn-secondary:hover{border-color:var(--signal-gold);color:var(--signal-gold)}}
.btn:active{transform:scale(.98)}
@media (prefers-reduced-motion:reduce){.btn{transition:none}.btn:active{transform:none}}
.rev{opacity:0;transform:translateY(var(--lift-2));
  transition:opacity var(--d4) var(--e-cubic),transform var(--d4) var(--e-cubic)}
.rev.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.rev{opacity:1;transform:none;transition:none}}
footer{padding:clamp(56px,7vw,96px) 0 48px;border-top:1px solid var(--hairline);
  color:var(--ink-subtle);font-size:14.5px}

/* ---------------------------------------------------------------------------
   PRINT. Measured before this existed: printing smart-mill.html produced TWELVE
   pages of which pages 4 and 5 were 100% BLANK, and the H1 rendered as pale grey
   on white — because the whole palette is built for a dark ground and Chromium
   omits background colours by default. A capex committee prints this document.
   So: invert to ink-on-paper rather than force backgrounds, which is the version
   that survives a monochrome office printer.
   --------------------------------------------------------------------------- */
@media print {
  :root {
    --deep-water:#FFFFFF; --mill-navy:#FFFFFF; --panel-navy:#FFFFFF; --panel-navy-2:#FFFFFF;
    --ink:#101820; --ink-muted:#2C3A47; --ink-subtle:#41505E; --ink-ghost:#55636F;
    --hairline:rgba(16,24,32,.22); --hairline-strong:rgba(16,24,32,.42);
    --signal-gold:#6B4A0C; --crude-amber:#7A5310; --airei-blue-lift:#0B4F86;
    /* The drawn display, on paper. Left at its screen value, .scr lays a 33x11px band of
       near-black around twelve captures on one page — the ink-on-paper inversion two lines
       above exists precisely so a capex committee's office printer is not asked for that.
       Light grey keeps the chin readable as a chin and costs almost no toner. */
    --device-bezel:#DCE0E4; --device-recess:#B9C2CB;
  }
  body { background:#fff; color:#101820; font-size:11pt; line-height:1.45 }
  header.site, .tele, .navtoggle, .lang, nav.main, .btn { display:none !important }
  /* PAGE 1 WAS MOSTLY WHITESPACE, BUT NOT FOR THE REASON IT LOOKED LIKE.
     The reported cause was `.hero{min-height:clamp(540px,84vh,840px)}` (home.css)
     going unreset in print, reserving ~84% of a page for a photograph print drops.
     Measured on the A4 render (48dpi raster, largest CONTIGUOUS ink-free band — plain
     ink%% cannot tell a reserved empty box from a normal page of type, since a full
     page of prose is only 3-7%% ink):

       as shipped                     10 pages, page-1 band 21.2%, page 3 = 92% blank
       .hero{min-height:0} ALONE      10 pages, page-1 band 35.4%   <-- WORSE
       section break-inside:auto       8 pages, page-1 band 18.3%, no near-blank page
       both + atoms protected          8 pages, page-1 band 13.5%, no near-blank page

     Resetting the hero alone makes page 1 emptier, because the hero was never what
     held the space: `break-inside:avoid` on EVERY section is. A section taller than the
     room left on the page cannot honour it, so the whole section jumps to the next page
     and leaves the remainder blank — shrinking the hero just donates more space to that
     gap. The rule is right for a short band and actively harmful for a long one; the
     correct granularity is to let long sections break and protect the ATOMS inside them
     (the same conclusion smart-mill.css reached independently for its own stations).

     Both halves are kept: the hero reset is correct on its own terms (print drops the
     photograph, so reserving 840px for it is meaningless) and contributes the last
     18.3% -> 13.5%. Everything here is inside @media print and cannot reach screen. */
  section { padding:14pt 0; border-top:1px solid rgba(16,24,32,.22);
            break-inside:auto; page-break-inside:auto }
  /* !important is load-bearing, not decoration. home.css loads AFTER base.css and sets
     .hero{min-height:clamp(...)} at the SAME (0,1,0) specificity; a media query adds
     none, so the later sheet wins and this reset was INERT. It was measured as working
     only because the trial injected it via add_style_tag, i.e. at the end of the
     cascade — a different position from where it actually ships. Measured with the file
     as shipped: page-1 band 18.3%% (the section fix alone). With !important: 13.5%%. */
  .hero { min-height:0 !important }
  .stat, .chain .pos, .hero-copy, figure, blockquote, li, tr,
  .creds .row { break-inside:avoid; page-break-inside:avoid }
  h1,h2,h3 { color:#0B1218; break-after:avoid; page-break-after:avoid }
  .stat .v { color:#101820 }
  .stat .v em { color:#6B4A0C }
  .rev { opacity:1 !important; transform:none !important }
  img { max-width:100%; break-inside:avoid }
  /* a printed page cannot be clicked — spell every link out */
  main a[href^="http"]::after { content:" (" attr(href) ")"; font-size:8.5pt; color:#41505E;
                                word-break:break-all }
  footer { border-top:1px solid rgba(16,24,32,.22); color:#41505E;
           break-inside:avoid; page-break-inside:avoid }
  /* .legal is a flex-wrap row; without this the copyright span and the city span split
     across a page boundary and leave a ~99% blank trailing page. Measured on the grading
     page print PDF: page 6 ended with the copyright, page 7 held only the city line. */
  footer .legal { break-inside:avoid; page-break-inside:avoid; padding-top:10pt; gap:10pt }
  footer { padding-bottom:0 }
}

/* ══ .pband — the section-break picture band ═══════════════════════════════════
   Every page ran 400 to 1,300 words per visual; the competitor set this site is
   measured against runs 11 to 39. The cure is not smaller type, it is more pictures,
   placed where a reader's attention actually drops — at the seam between sections.

   The frames are 4:5 because a portrait frame is what these photographs were shot
   for, but a lone 4:5 image full-bleed on a desktop is a 1,700px wall. Pairing it with
   a short caption gives the band a horizontal rhythm at desktop and stacks to a plain
   picture-then-caption on a phone, which is the correct reading order either way.
   .pband--rev flips the sides so consecutive plates alternate.

   THE FRAME IS A RATIO, NOT A PIXEL HEIGHT (2026-08-26). It used to be
   `height:clamp(300px,34vw,440px)`, and that is the line that made the band lie
   about its own shape. Measured at 1280/1440/1600/1920/2560 on careers.html: the
   band settles at 535x440 = 1.216:1 from 1440px up, while EVERY image it is fed
   is 4:5 (0.800) — assets/img/45/ holds 68 photographs across 204 jpgs counting
   rungs, and all 204 measure 0.800 with no second ratio anywhere in the set. Cover
   then discards 34.2% of each frame, top and bottom, at every desktop width. On
   grading-camera-rig.jpg that is the whole subject: the camera hangs at the top of
   the frame and the fruit runs along the bottom, and a 1.216:1 window severs the
   one relationship the photograph exists to show.

   1:1 and not 4:5, and this was shot both ways rather than argued. At 4:5 the
   crop is zero, but the frame becomes 535x671 and the caption column — capped at
   38ch, ~260px of text — sits in 671px with ~410px of dead navy beside it, which
   is the imbalance the paragraph above rejected when it chose a horizontal band.
   It also costs +1651px on careers.html and +1820px on ai-ffb-grading.html. The
   square keeps the horizontal rhythm, costs +711px and +586px on those same two
   pages, and takes the discard from 34.2% to 20.0%. Screenshots of all three
   states were compared before this changed.

   Modifiers that opt out, both of which override the ratio below because they are
   later in this file at equal specificity: --tall (true 4:5, the photograph is the
   point) and --screen (the picture's own shape, for product screens, which must
   never be cover-cropped at all). */
.pband{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(20px,3vw,42px);align-items:center;margin:var(--d4) 0;max-width:var(--wrap)}
.pband>img{width:100%;height:auto;aspect-ratio:1/1;object-fit:cover;display:block;
  background:var(--panel-navy);border:1px solid var(--hairline)}
/* 38ch -> 46ch, 2026-08-27. A `ch` is a property of the element's OWN font, and this cap is
   set on the FIGCAPTION, which inherits the 17px body face — so 38ch resolved to 395px and
   was then handed down to `.pband figcaption p:not(.k)`, which is clamp(16px,1.35vw,19px)
   and runs at 19px across desktop. In that child 395px is 27.3ch, not 38ch: the cap said
   "38 characters" and delivered 27, under the 30ch floor, on ai-ffb-grading, traceability
   and both Malay twins (33-37 characters to a line over 4-5 lines).
   46ch of the caption's own 17px face is 478px, which is 33ch — and 33 characters of the
   19px face — in the child that actually carries the prose. Same defect as `.sec-note` in
   home.css: a measure token set on a parent does not survive a font change in a child, and
   the only reliable cure is to set the measure on the element whose font it is about, or to
   do the conversion deliberately as here. Still well inside the picture band's half. */
.pband figcaption{max-width:46ch}

/* Reading measure for caption prose. .pband above says 38ch, .pr-herofig says 44ch;
   these eleven inherited the full column and set 100-145ch (worst: figcaption.cap, 145ch,
   a full-bleed all-caps mono caption). ENUMERATED, NOT a blanket figcaption{max-width},
   because .plate figcaption is display:flex — a wrapping row of metadata chips, whose
   "character count" is meaningless and which capping would needlessly re-wrap. Every
   selector here was measured as display:block prose. */
.cap,
.src-line,
.slot p.mono,
.span2 p,
figure.pull>figcaption,
figure.dfig>figcaption,
.fx-scan>figcaption,
.mp-pair>figcaption,
.mp-scene>figcaption,
.mp-gal figure.wide>figcaption{max-width:76ch}
.pband .k{font:600 12px/1 var(--f-mono);letter-spacing:.17em;text-transform:uppercase;
  color:var(--airei-blue-lift);margin:0 0 12px}
.pband figcaption p:not(.k){font-size:clamp(16px,1.35vw,19px);line-height:1.55;
  color:var(--ink-muted);margin:0 0 10px}
.pband figcaption p:not(.k):first-of-type:not(.k){color:var(--ink)}
/* 11px -> 12.5px, same third pass as `.stat .src` above and for the same reason. */
.pband .src{font:500 12.5px/1.55 var(--f-mono);letter-spacing:.02em;color:var(--ink-ghost);margin-top:14px}
.pband--rev>img{order:2}
@media (max-width:820px){
  .pband{grid-template-columns:1fr;gap:18px;margin:var(--d3) 0}
  .pband>img{height:auto;aspect-ratio:4/5;max-height:74vh}
  .pband--rev>img{order:0}
  .pband figcaption{max-width:none}
}
/* A plate that wants the frame to keep its true 4:5 at desktop too — used where the
   photograph is the point rather than the caption.

   IT KEEPS THE RATIO AND GIVES BACK THE `align-items:start`, 2026-08-27. The start-align
   was the one line in this band with no argument behind it, and it was the only thing on
   the site that turned space beside a caption into a hole. Measured live at 1440 across
   both languages, 91 `.pband` figures:

       87 of 91   caption optically centred, void SYMMETRIC, asymmetry 0px
        2 of 91   `--screen`, asymmetry 149px
        2 of 91   `--tall`   asymmetry 448px (traceability) / 546px (kebolehkesanan)

   Those last two are the same figure in each language, and they are the only asymmetric
   voids on the site. `start` stacks the entire 4:5-minus-caption difference into one
   block UNDER the caption — 535 x 448 of empty navy on the English page and 535 x 546 on
   the Malay, which is larger because the Malay caption is one paragraph shorter. Beside a
   photograph of a man's face, that reads as content that failed to arrive.

   Centring does not shrink the difference; it halves it and puts a half on each side,
   which is what the other 87 bands already do and is why nobody reads them as broken.
   224/224 here, 273/273 on the Malay twin.

   NOT CURED BY RE-RATIOING THE TRACKS, and this was swept before settling: `.pband--rev`
   puts the image in column 2, so the track ratio sizes the CAPTION, and `figcaption` is
   capped at 46ch (478px) — past that the caption stops growing and only the picture moves.
   Every ratio that brings the void under 200px per side does it by shrinking the picture
   to 382-412px, SMALLER than the 535px square this modifier exists to escape. A modifier
   whose only working geometry is worse than not using it is not a modifier; ratios 1.0,
   0.85, 0.75, 0.68, 0.6, 0.55, 0.5 were measured in both languages before this line went.

   NOT CURED BY DROPPING `--tall` EITHER, though that was the closer call. Rendered and
   looked at, not argued: `object-fit:cover` into 1:1 discards 20.0% of this 1024x1280
   file, and the discard takes the crown of the man's straw hat. Face, phone, both hands
   and the blade behind him all survive — but a sliced hat is a visible crop tell on the
   one photograph the modifier was added for. Centring costs the picture nothing. */
.pband--tall>img{height:auto;aspect-ratio:4/5}

/* ══ .figures — the big-number infographic ═════════════════════════════════════
   For the places where the page is really presenting a small set of quantities and
   is currently doing it in four paragraphs. The numeral carries the answer, the label
   says what it counts, one line says what it means, and the citation stays — compressed
   to a source line rather than removed, because the citation is the reason to believe
   the numeral. */
.figures{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:1px;background:var(--hairline);border:1px solid var(--hairline);margin:var(--d3) auto}
@media (max-width:1000px){ .apps{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width:820px){ .apps{grid-template-columns:minmax(0,1fr);width:100%;margin-left:0} }
.figures>div{background:var(--mill-navy);padding:clamp(20px,2.4vw,30px)}
.fg-n{font:600 clamp(46px,6vw,80px)/0.92 var(--f-display);letter-spacing:-0.0119em;
  color:var(--airei-blue-lift);margin:0 0 4px;font-variant-numeric:tabular-nums}
/* line-height is set HERE and not left to inherit, and that is the whole point of this rule.
   `.fg-n` sets `/0.92` in its `font` shorthand, which is right for a single-line 46-80px
   numeral and wrong for anything that wraps. Unitless leading re-multiplies against the
   CHILD's font-size, so the caption inherited 0.92 and computed 33.6px text on 30.91px
   leading — leading SMALLER than the font. Measured 2026-08-27 at 1440x900: 5 of the 6
   `.fg-n small` on the site wrap (mypalm 2, ms/mypalm 3, one of them to three lines), and
   at 2x the ascenders of the second line sit inside the descenders of the first — "kilang"
   ran into "pembekal di sebalik satu" above it and the two lines read as one mass.
   1.15 clears Fraunces' extenders at 33.6px while staying display-tight; the numeral keeps
   its own 0.92 because it never wraps. */
.fg-n small{font-size:.42em;line-height:1.15;letter-spacing:-0.0034em;color:var(--ink-muted);
  display:block;margin-top:8px}
.fg-k{font:600 12px/1 var(--f-mono);letter-spacing:.17em;text-transform:uppercase;
  color:var(--ink);margin:0 0 10px}
.figures p:not(.fg-n):not(.fg-k):not(.fg-src){font-size:15px;line-height:1.6;color:var(--ink-muted);margin:0}
.fg-src{font:500 12px/1.5 var(--f-mono);color:var(--ink-ghost);margin:14px 0 0;
  padding-top:12px;border-top:1px solid var(--hairline)}
.fg-src a{color:var(--ink-muted)}
.fg-ico{width:22px;height:22px;color:var(--crude-amber);margin-bottom:14px;display:block}
@media (max-width:560px){ .figures{grid-template-columns:1fr} }

/* ── ON A PHONE, THE ACTION COMES BEFORE THE PICTURE ──────────────────────────────
   Measured at 390px on the product pages: eyebrow 20px, headline 85px, standfirst 307px,
   hero photograph 262px — and only then the call to action, at y=942 against an 844px
   viewport. A visitor who does not scroll never sees a way to contact anyone.

   The photograph is not the problem and is not being hidden; it simply does not need to
   stand between the argument and the response to it. Desktop layout is untouched — there
   the whole stack is inside the first screen anyway. */
@media (max-width:820px){
  main>section.portrait>.wrap,
  main>section.hero>.wrap{display:flex;flex-direction:column}
  main>section.portrait>.wrap>.cta-row,
  main>section.hero>.wrap>.cta-row{order:1}
  main>section.portrait>.wrap>figure,
  main>section.hero>.wrap>figure{order:2}
  /* Only the hero's OWN opening copy is promoted. The blanket `:not(.cta-row):not(figure)`
     also caught trailing notes that belong after the button — a 50-word .note on mypalm was
     lifted above the CTA and the phone fold went from 41 words to 101. Name what moves up
     instead of naming what does not. */
  main>section.portrait>.wrap>.eyebrow,
  main>section.portrait>.wrap>h1,
  main>section.portrait>.wrap>.lede,
  main>section.portrait>.wrap>.sub,
  main>section.hero>.wrap>.eyebrow,
  main>section.hero>.wrap>h1,
  main>section.hero>.wrap>.lede,
  main>section.hero>.wrap>.sub{order:0}
  main>section.portrait>.wrap>*,
  main>section.hero>.wrap>*{order:3}
}

/* ── .filmplayer — the six-minute company film ────────────────────────────────────
   The page carried a reserved slot saying the film "is not playing yet" because it had not
   been encoded. It has been: 720p H.264, 360.4s, faststart, with a poster frame, plus a
   960-wide variant for phones. The slot's own stated condition is met, so the slot becomes
   the player.

   preload="none" is the whole reason a 55MB file is acceptable here: nothing downloads
   until someone presses play, so the page weight is the poster (250KB), not the film.
   Two sources, smaller first, so a phone takes the lighter encode. */
.filmplayer{position:relative;margin:var(--d3) 0 0;border:1px solid var(--hairline);
  background:var(--device-recess);aspect-ratio:16/9;overflow:hidden}
/* NEVER #000. Changed 2026-08-27. This is the letterbox a photograph or a video is laid
   into, and pure black is the one neutral this design system does not own: every other
   ground on the site is tinted toward the brand hue, so a #000 gap beside --deep-water
   #0B1520 reads as a hole punched in the page rather than as the back of a frame.
   --device-recess (#05080B) is the token that already means exactly this — the shadow gap
   behind a screen — and it is tinted. The #000 stops inside mask-image and
   linear-gradient declarations elsewhere in these files are ALPHA STOPS, not colours, and
   are deliberately left alone. */
.filmplayer video{width:100%;height:100%;display:block;object-fit:cover;background:var(--device-recess)}
.filmnote{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:12px;
  font:500 11px/1.5 var(--f-mono);letter-spacing:.03em;color:var(--ink-ghost)}
.filmnote b{color:var(--ink-muted);font-weight:500}

/* The mobile hero reorder above assumed the hero's children sit directly inside .wrap.
   smart-mill nests them one level deeper in a .grid, so the rule matched nothing there and
   the page's first call to action stayed 18,566px down — past a six-screen sticky rail.
   Cover both shapes. */
@media (max-width:820px){
  main>section.hero>.wrap>.grid{display:flex;flex-direction:column}
  main>section.hero>.wrap>.grid>.cta-row{order:1}
  main>section.hero>.wrap>.grid>figure{order:2}
  main>section.hero>.wrap>.grid>.eyebrow,
  main>section.hero>.wrap>.grid>h1,
  main>section.hero>.wrap>.grid>.lede,
  main>section.hero>.wrap>.grid>.sub,
  /* ...and the shape where the copy is a COLUMN rather than loose children. Naming the four
     text elements assumed they are direct children of .grid. On smart-mill.html and its Malay
     twin ms/kilang-pintar.html they are one level deeper, inside an unnamed div, so NONE of the
     four selectors above matched: the column fell through to the `*{order:3}` catch-all below
     while the figure kept order:2, and the PHOTOGRAPH painted above the entire argument.
     Measured at 390px before this line, on the flagship product page in both languages:
         smart-mill.html      figure 172..419,  eyebrow 487,  call to action 774 (box ends 827)
         ms/kilang-pintar.html figure 172..419, eyebrow 447,  call to action 887 (box ends 993)
     — the Malay one 149px past the 844px fold, which is this rule's own stated failure ("a
     visitor who does not scroll never sees a way to contact anyone") surviving inside the rule
     meant to prevent it. The div is given a NAME rather than matched with :not(), because the
     comment above is right that naming what does not move is how the mypalm note got promoted. */
  main>section.hero>.wrap>.grid>.hero-col{order:0}
  main>section.hero>.wrap>.grid>*{order:3}
}

/* PARENT BREADCRUMB — one line that answers "what does this page belong to".
   Traceability was a seventh item in a seven-item nav, which made it read as a fourth product
   beside Smart Mill, AI Grading and MyPalm. It is a MyPalm capability. Taking it out of the
   nav without saying where it went would have made that worse, so the page names its parent
   and the nav marks MyPalm current while you stand on it. */
.eyebrow .parent{color:var(--airei-blue-lift);text-decoration:none}
@media (hover:hover){ .eyebrow .parent:hover{text-decoration:underline} }
/* This breadcrumb is the ONLY route from Traceability back to the parent it belongs to,
   and the nav no longer carries Traceability at all — so on a phone it is the only way
   back up. Measured 61x20 at 390px CSS px (is_mobile + has_touch, 2026-08-26), under the
   24px WCAG 2.5.8 AA floor. min-height rather than padding: the eyebrow is a flex row and
   padding would push the "·" separator off the parent's baseline. Coarse pointers only,
   per shell.css:332. */
@media (pointer:coarse){
  .eyebrow .parent{min-height:44px;display:inline-flex;align-items:center}
}
.eyebrow .sep{margin:0 .5em;color:var(--ink-ghost)}

/* ══ .apps — the store-listing showcase ═══════════════════════════════════════════
   The operator asked to "show the apps". The strongest way to show software on a vendor
   site is not a screenshot — a screenshot is something we made. It is the public store
   listing, because a last-updated date is a fact the vendor's marketing department does not
   control and the reader can check in one tap.
   So the showcase is the listings themselves: name as the store prints it, package id,
   published date, last-updated date, and a live link. Sorted by recency of maintenance,
   because "will this still be supported in two years" is the question a risk-averse mill
   owner is actually asking. */
/* NINE cards, so three columns or one — never two, which leaves an orphan on row five.
   auto-fill gave two inside .measure and produced exactly the 4+4+1 raggedness the operator
   flagged on the station grid. The grid also breaks out of .measure: a reading measure is for
   prose, and nine store listings are a table of facts, not a paragraph. */
.apps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));

  gap:1px;background:var(--hairline);border:1px solid var(--hairline);margin:var(--d3) auto}
@media (max-width:1000px){ .apps{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width:820px){ .apps{grid-template-columns:minmax(0,1fr);width:100%;margin-left:0} }
.appc{background:var(--mill-navy);padding:20px 20px 18px;display:flex;flex-direction:column;gap:9px}
.appc.live{background:var(--panel-navy)}
.appc .plat{font:600 11px/1 var(--f-mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-ghost);display:flex;align-items:center;gap:8px}
.appc .plat b{color:var(--airei-blue-lift);font-weight:600}
.appc h3{margin:0;font-size:18px;line-height:1.16;letter-spacing:-0.0034em;font-weight:600;color:var(--ink)}
.appc .pkg{font:500 12px/1.4 var(--f-mono);color:var(--ink-ghost);word-break:break-all;margin:0}
.appc dl{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:4px 0 0;
  font:500 12.5px/1.5 var(--f-mono)}
.appc dt{color:var(--ink-ghost);letter-spacing:.05em}
.appc dd{margin:0;color:var(--ink-muted)}
.appc dd.fresh{color:var(--airei-blue-lift)}
.appc .go{margin-top:auto;padding-top:12px;font:600 12px/1 var(--f-mono);letter-spacing:.1em;
  text-transform:uppercase;color:var(--airei-blue-lift);text-decoration:none;
  display:inline-flex;align-items:center;gap:6px}
@media (hover:hover){ .appc .go:hover{color:var(--signal-gold)} }
@media (pointer:coarse){ .appc .go{min-height:44px;align-items:center} }
.apps-note{font:500 12.5px/1.6 var(--f-mono);color:var(--ink-ghost);margin:14px 0 0}

/* ══ SCREENSHOT MODIFIERS on the two existing showcase components ══════════════
   Added 2026-08-26 for the MyPalm screen showcase. These are MODIFIERS, not a
   third gallery: the components are still .pband (the picture band) and .apps
   (the store-listing grid), exactly as they stand above. .pband already carries
   two modifiers of this shape, --rev and --tall.

   WHY --screen HAS TO EXIST. `.pband > img` is height-clamped with
   `object-fit:cover` because it was written for the 4:5 commissioned photographs.
   Measured on the frames this ships: a 1600x806 dashboard in a 440px-tall,
   ~590px-wide band is scaled to fill the height and then cropped to ~31% of its
   own width — the left navigation and the right of the chart, which is precisely
   the half of the frame that proves it is a working screen rather than a mock.
   `contain` would letterbox inside a height the picture does not want; height:auto
   simply lets the screenshot be its own shape, on both grounds and at every width.

   .appc > img matches NOTHING that exists: all nine store-listing cards are text.
   It cannot change them. */
.pband--screen{align-items:start;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr)}
/* 60/40 at desktop: --screen's picture is a DOCUMENT, to be read — 642px, 372px of glass. */
.pband--screen>img{height:auto;aspect-ratio:auto;object-fit:contain;
  background:var(--panel-navy)}
@media (max-width:820px){
  .pband--screen{grid-template-columns:1fr}  /* beats the 60/40 split above */
  .pband--screen>img{height:auto;aspect-ratio:auto;max-height:none}
}
.appc>img{width:100%;height:auto;display:block;border:1px solid var(--hairline);
  background:var(--panel-navy)}

/* ══ .scr — A SCREEN, DRAWN INTO A DISPLAY ═══════════════════════════════════════
   Added 2026-08-27. Goes ON the <img> of a product capture, nowhere else.

   THE PROBLEM IT SOLVES, measured rather than felt. The MyPalm UI is dark navy by
   DESIGN.md §6, and so is every ground this site puts it on (--panel-navy #1B273A,
   --deep-water #0B1520). So a capture butted onto the page had ONE pixel of hairline
   between the software and the page, and read as a raw crop — a flat rectangle of
   dark, not a thing anyone is looking AT. Four figures on this site already answer
   that question well (the tablet on the press floor, the control-room monitor, the
   weighbridge monitor, the phone on the nursery bench) and they answer it by putting
   the screen in a room. Those are composites, and the mypalm page discloses them as
   such by name. Twelve more rooms would be twelve more compositions to disclose.

   SO THE DISPLAY IS DRAWN, NOT PHOTOGRAPHED. This is the same decision mypalm.css
   already records for the phone — "THE PHONE BEZEL IS DOM, NOT A PICTURE" — applied
   to the desktop captures: moulding, recess and chin are CSS, obviously drawn, and
   claim nothing about what hardware a customer owns. DESIGN.md §6 forbids stacking
   two renders; a border is not a render.

   WHY A CHIN. Symmetric padding reads as a picture MOUNT. A monitor is not symmetric
   — its bottom moulding is deeper than its sides, and that asymmetry alone is what
   makes the eye call it a device instead of a frame. 2.3x, measured against the four
   composites on this site: the weighbridge monitor's chin is ~2.4x its side bezel.
   A stand was built and thrown away: at wall scale it read as two floating grey bars,
   and twelve of them is furniture, not evidence.

   NO SHADOW, NO SHEEN, NO RADIUS. DESIGN.md §8 blacklists glassmorphism, neon glow
   and "uniform 16px radius on everything"; a screen glow is the tell that would have
   put this straight onto that list. The only relief is one hairline at the outer
   edge, which is the site's own separator everywhere else.

   BORDER-BOX IS LOAD-BEARING. `*{box-sizing:border-box}` is set at the top of this
   file, so width:100% still measures the OUTSIDE of the display and the capture keeps
   its own aspect ratio inside. Containers that set their own `border` on the capture
   (.mp-gal img, .mp-frame>img) out-specify a bare class, so those two carry an
   explicit `:not(.scr)` in mypalm.css. That is the coupling to watch: a THIRD
   container that borders its own images will silently swallow this frame. */
.scr{--bez:clamp(7px,0.85vw,11px);
  border:var(--bez) solid var(--device-bezel);
  border-bottom-width:calc(var(--bez)*2.3);
  padding:2px;background:var(--device-recess);
  box-shadow:0 0 0 1px var(--hairline-strong)}

/* ══ .hs-doc — a capture shown at DOCUMENT size, above its own in-use plate ═══════
   2026-08-27, homepage "What the software looks like". NOT a `.pband` variant, and
   the measurement is why. `.pband--screen`'s note above calls its picture "a DOCUMENT,
   to be read — 642px, 372px of glass"; both halves are true and they contradict each
   other. 372px of glass carrying a UI captured at 1600px puts its type at ~2.6px,
   while the caption beside it names one specific cell — the Remarks column — and asks
   to be believed. One column, capture at the full wrap: 1086px of content against that
   1600px source, ~7.5px type, READ at 1:1 before this shipped. The band below keeps
   the composite, the only frame on the homepage of a person USING the product; same
   screen, two reasons, in that order. */
.hs-doc{display:grid;gap:clamp(16px,1.6vw,22px);margin:0 0 clamp(28px,3.4vw,48px)}
.hs-doc>img{width:100%;height:auto;display:block}
.hs-doc>figcaption{max-width:64ch}
.hs-doc .k{font:600 12px/1 var(--f-mono);letter-spacing:.17em;text-transform:uppercase;
  color:var(--crude-amber);margin-bottom:12px}
.hs-doc figcaption p:not(.k){font-size:clamp(16px,1.35vw,19px);line-height:1.55;
  color:var(--ink-muted);margin:0}
/* `.hs-doc figcaption p:not(.k)` above is (0,2,2) — one class, plus the class inside
   :not(), plus two elements — so a plain `.hs-doc .src` at (0,2,0) LOSES to its
   `margin:0` and the source line closed up against the paragraph. Matched shape wins
   it back rather than reaching for !important. */
.hs-doc figcaption p.src{font:500 12.5px/1.55 var(--f-mono);letter-spacing:.02em;
  color:var(--ink-ghost);margin-top:14px}
@media (max-width:820px){
  /* Same reasoning as .plate--screen in proof.css: below the break the wrap gutter is
     all that stands between a dense capture and the edge, and 13% of width is free.
     It does not make a 1600px UI readable at 390 and is not sold as doing so — the
     "See the other screens" link is the route to a screen you can actually read. */
  .hs-doc>img{margin-inline:calc(-1 * var(--gut));width:calc(100% + var(--gut) * 2);
    max-width:none}
}
