/* =============================================================================
   DEFCOM ONE — the whole surface, in one sheet.

   It lived inline in a <style> block in index.php, 1551 lines of it, re-sent on
   every request because there is only one page and its HTML is never cached.
   Out here it is cached once and shared with admin.php's own sheet.

   NOTHING WAS REWRITTEN IN THE MOVE. Not one selector, not one comment. The two
   things that would have broken were checked first and neither applies: there
   is no url() anywhere in this file (the only ones in the project are the two
   @font-face rules, which is why they are in font.css and not here — relative
   URLs resolve against the SHEET, so assets/font/x.woff2 would have become
   assets/assets/font/x.woff2), and there is no @import.

   The two-space indent is what it had inside the <style> block. Left alone on
   purpose: dedenting 1550 lines would have made the diff of this extraction
   unreadable, and the point of the extraction is that it changed nothing.

   The type tokens, the palette and the module are CSS custom properties written
   at runtime from the CMS — see applyStatic() in assets/site.js. Editing values
   here that the CMS also writes will be overwritten on the first frame.
   ============================================================================= */

  * { margin: 0; padding: 0; box-sizing: border-box; }
  @property --ms { syntax: '<number>'; initial-value: 1.16; inherits: false; }

  :root {
    /* Palette — driven by the CMS colour section. Every translucent rule, grid
       line and dot in the site is mixed from --ink-rgb / --bg-rgb rather than a
       literal white or black, so a scheme change carries the whole surface with
       it instead of leaving white marks on a repainted background. */
    --phantom: #0b0b0b;
    --bg-rgb: 11 11 11;
    --phx: #ff5a00;
    /* The accent's channels, for the same reason --ink-rgb exists: a translucent
       wash of the accent has to follow a CMS scheme change, and rgb() cannot take
       a hex custom property and an alpha. Written by applyColors() from PHX_RGB. */
    --phx-rgb: 255 90 0;
    --cursor: #ff5a00;           /* the pointer's own colour — set by the CMS */
    --grey-1: #8a8a8a;
    --grey-2: #b8b8b8;
    --ink: #ffffff;
    --ink-rgb: 255 255 255;
    --line-a: .16;
    --line: rgb(var(--ink-rgb) / var(--line-a));
    /* HOW MUCH COLOUR THE PICTURES KEEP. It was grayscale(1) in four places — the
       content slots, the film panel at rest, its hover, and the entry sequence — so
       "the pictures are black and white" was four decisions that had to be changed
       together. One number now.
       .62 is "colour, lightly desaturated": enough that skin and sky are not grey,
       not enough for a photograph to start competing with the accent, which is the
       only saturated thing on this site by design.
       MEASURED, AND IT DOES NOT REACH EVERY PICTURE: saturate() can only take colour
       out, never put it back. assets/defcom holds 12 of 13 files in real colour, so
       those change. assets/records holds 37 of 37 at 100.0% grey ON DISK, with no
       colour original anywhere in the project — those stay grey whatever this says.
       See the note at .media img. */
    --img-sat: .62;
    --w-light: 300;
    --w-med: 500;
    /* DISPLAY TYPE IS NOT BOLD. Outfit is a variable face across 100–900, and every
       headline on this site was set at 500 — which at 69px reads as weight doing the
       emphasising instead of size. 400 is the regular: the same letterforms, the
       stress taken off. The small labels keep 500, because at 11 and 16px weight is
       the only thing that separates a title from its own caption. */
    --w-reg: 400;
    /* One module. The menu is a row of the raster, not a bar of its own height —
       see the .nav block and the screen-space gate in mosSample(). */
    /* HALF A MODULE. It was a whole one, and a whole module of chrome across the top
       of a plane measured in modules is a row of the grid spent on a menu. Half is
       enough for a 26px wordmark and an 18px list, and it gives the plane its row
       back. ONE PLACE, and site.js reads the same fraction in navH() — the camera
       centres a landed point in the space UNDER this bar, so a bar height that only
       CSS knows about puts every landing half a bar out. */
    --nav-h: calc(var(--m-row) * .5);
    /* HALF A MODULE, and everything that lives ON the plane is raised by it —
       the points of interest, the records, the wordmark, the cases' bar and their
       reading area. The fixed chrome is not: the header, the minimap, the
       brackets, the cursor, the drawer and the lightbox belong to the window and
       stay where the window puts them.
       It is a VISUAL lift, a transform rather than a change of position, so the
       camera still centres a point on the layout it was authored at and the
       mosaic still reads its boxes off the rendered rects — the tiling follows
       the copy up without being told about it. */
    --lift: calc(var(--m-row) * .5);
    /* THE ROW MEASURE, and on every screen but a short landscape one it IS the
       module. A point is 13 modules across and 9 down — 1.44:1 — and a landscape
       phone is 2.6:1, so one square measure cannot answer both: sized to the width
       the point hangs out of the bottom, sized to the height it stands 416px wide
       in a 739px frame with 161px of dead air each side. The owner had both
       complaints in the same breath: "ZU ENG. ZU ABGESCHNITTEN."
       So the column keeps the width and the row takes the height. Written by
       site.js beside --m; see moduleRowNow(). The world grid follows it below, so
       the lattice still lands on the blocks it measures. */
    --m-row: var(--m);
    --ease: cubic-bezier(.76, 0, .24, 1);
    --f: "Outfit", "Helvetica Neue", Helvetica, Arial, sans-serif;
    /* THE MODULE BEFORE JAVASCRIPT HAS SPOKEN, and on a phone that value is
       what the visitor actually sees first. It was a flat 120px — the CMS's
       desktop default — so the first paint of a 390px phone laid out a plane
       whose every point was 1560px wide: enormous, and wider than the screen in
       both directions, which is a browser's cue to let the reader pinch their
       way out of it. Reported as "beim laden ist alles riesig gezoomt".
       Fitted to the window instead, by the same measure moduleNow() uses, so the
       pre-script paint is already near the final one and nothing has to be
       zoomed out of. site.js overwrites this the moment it runs (writeModule);
       this is only ever the opening frame — and, if the script ever fails to
       run at all, the layout it leaves behind. */
    --m: min(120px, calc(100vw / 13.5));
    /* The measure the POIs are authored to: 13 modules. A landed POI is centred,
       so its left edge sits at (100% - this) / 2 — the nav rides the same rail
       and the brand lines up with the copy beneath it. */
    --content-w: calc(13 * var(--m));
    /* DISPLAY TYPE IS A FUNCTION OF THE MODULE. Reading type is not.
       The layout is authored at module 112 with an 88px headline, and every
       block's row and column count was chosen against that pair. Fixed px broke
       the moment the module moved: shrinking it takes away row height AND column
       width at once, so the copy needs more lines in less space. Measured across
       the six surface points:

         Modul 112   kein Ueberlauf        POI 1008px, 220px zu hoch fuers Fenster
         Modul  90   passt ins Fenster     Text laeuft an fuenf Stellen ueber

       No module satisfied both, because the two were not tied together. As a
       ratio they are: 88/112 holds the authored fit at any module, and 10 rows
       (nine of POI plus the menu) then fit a 900px window at 88.
       READING TYPE NOW SCALES TOO, and it has to. This paragraph used to end
       ".lede, .sub, .small and .tag stay fixed — those are read, not composed,
       and reading size does not belong to the grid." True about typography and
       false about this layout, because a reading block here does not flow: it
       sits in a fixed count of module ROWS. One term scaling and one term fixed
       is the whole of "der Text steht zu fix" — drop the module and the row loses
       height while the line box keeps it, so the line count rises exactly when
       the space falls. Measured: the Careers lede has one row minus 21px of rule
       and air, a 20px/1.45 line box is 29px, so two lines need module >= 79
       against a slider that starts at 40.
       Clamped at both ends, the way .nav__menu button already is: a CMS module of
       40 must not turn a paragraph into a footnote, and 240 must not turn it into
       a headline. Every multiplier is set so the value is UNCHANGED at the live
       module 88 — nothing moves today, only what happens when the slider does. */
    --t-lede:  clamp(15px,   calc(var(--m) * .2273), 24px);   /* 20px   at m=88 */
    --t-sub:   clamp(13px,   calc(var(--m) * .1761), 19px);   /* 15.5px at m=88 */
    --t-small: clamp(10.5px, calc(var(--m) * .1364), 15px);   /* 12px   at m=88 */
    --t-neg:   clamp(11px,   calc(var(--m) * .1477), 16px);   /* 13px   at m=88 */
    --t-tag:   clamp(9.5px,  calc(var(--m) * .125),  13px);   /* 11px   at m=88 */
    --t-roles: clamp(13px,   calc(var(--m) * .1818), 20px);   /* 16px   at m=88 */
    /* The document layer's own three. It scrolls, so it has no rows to overflow —
       but it is the same classes at a different size, and a reader crossing from
       the map into a record should not feel the type stop answering the slider. */
    --t-lede-c: clamp(18px,  calc(var(--m) * .2841), 30px);   /* 25px   at m=88 */
    --t-sub-c:  clamp(14px,  calc(var(--m) * .2045), 22px);   /* 18px   at m=88 */
    --t-small-c: clamp(11px, calc(var(--m) * .1477), 16px);   /* 13px   at m=88 */
    --h-size: calc(var(--m) * 0.7857);     /* 88px at the authored module 112 */
    /* The deep layer's headline. Half the surface size, and the halving is the
       point rather than a taste: a surface headline has to carry while the
       camera is travelling, so it is a poster; a record is read with the camera
       stopped, and reading size is not poster size. */
    --h-rec: calc(var(--m) * 0.4107);      /* 46px at the authored module 112 */
    --grid-a: .10;               /* world grid alpha — set by the CMS */
    --world-w: 8400px;
    --world-h: 5400px;
    --dive: 0;                   /* depth 0..1 — written by assets/dive.js */
  }

  /* 100% FIRST, THEN 100dvh. The map is a fixed frame with overflow:hidden — there is
     no scrolling to reveal anything, so the frame has to be exactly what is visible.
     `height: 100%` resolves against the initial containing block, which on a phone is
     the LARGE viewport: the frame stays the size it would be with the URL bar hidden,
     so while the bar is showing the bottom of the map is under it. Nothing scrolls it
     back into view, because nothing scrolls.
     dvh follows the chrome as it comes and goes. It does mean a resize event each time
     the bar hides — which is correct here rather than merely tolerable: moduleNow()
     reads the height, so the plane re-lays to the frame it actually has.
     The 100% line stays as the fallback for anything without dvh. */
  html, body { height: 100%; height: 100dvh; overflow: hidden; }
  body {
    font-family: var(--f); font-weight: var(--w-light);
    -webkit-font-smoothing: antialiased; line-height: 1.15;
    background: var(--phantom); color: var(--ink); cursor: none;
  }
  /* MONOCHROME.
     The accent is spent in exactly three places on the page: the mouse marker, the
     underline under the point you are on, and hover. Everything that used to carry
     it for emphasis carries weight or position instead — a display headline in
     orange is not a monochrome site with an accent, it is a two-colour site.
     The CMS drawer and its sliders keep the accent: it is a tool, not the page. */
  ::selection { background: var(--ink); color: var(--phantom); }

  /* ================= VIEWPORT ================= */
  /* AND THE FRAME ITSELF HAS TO BE dvh, which the note above stops one element
     short of. html and body were put on 100dvh for exactly the reason written
     there — and .viewport is POSITION:FIXED, so its containing block is not the
     body but the INITIAL containing block, which on a phone is the large viewport.
     The frame therefore kept the size it would have with the URL bar hidden while
     the bar was standing, and since nothing scrolls, the bottom of the map simply
     lived under it. Measured from the owner's photograph: a point of 297 in 287 of
     visible screen, its foot cut mid-sentence, while site.js was told the frame was
     300 and believed it. Everything downstream is honest once this number is:
     moduleNow reads the frame, poiPan centres in the frame, the mosaic sizes to it.
     bottom:auto so the explicit height wins over the inset shorthand above it. */
  .viewport {
    position: fixed; inset: 0; bottom: auto; height: 100%; height: 100dvh;
    overflow: hidden; touch-action: none;
    user-select: none; -webkit-user-select: none;
  }

  /* ================= THE SPLIT, UNDER 1200px IN LANDSCAPE =================
     A phone in landscape is 844 x 390. The plane fits it — the module comes down to
     39 — but a point is 13 x 9 modules, an aspect of 1.44 against a frame of 2.16, so
     it fills the height and leaves 337px of width doing nothing. The point's
     proportion IS the grid, so that cannot be authored away.
     So the window becomes two: the map preview on the left as navigation, and the
     camera's frame on the right. --split-w is the one number, and site.js reads the
     frame back off the element rather than assuming the window — see applyFrame().

     THE PANEL WAS EATING THE MODULE. At 36vw of an 844px phone it took 304px, the
     frame came out at 540, and moduleNow() picks the smaller of vw/15 and vh/10 —
     36 against 39. So the WIDTH was binding, and the width is the half of the frame
     this stylesheet controls: the panel had made the point smaller than the screen
     could have carried, and every reading token underneath sat on its clamp minimum
     as a result.

     So the cap is a rule and not a taste. The point has to stay HEIGHT-bound, which
     is the constraint the device actually imposes and the one no layout can argue
     with:

         vw / 15  >=  vh / 10     ->     frame >= 1.5 * height
         frame = 100vw - split    ->     split <= 100vw - 150vh

     Measured at 844 x 390: 844 - 585 = 259px, and the module goes 36 -> 39.

     BOTH TERMS EARN THEIR PLACE. 26vw is what a phone lands on — the constraint
     there is 259 and 26vw is 219, so the proportion binds and the panel stays a
     proportion of its window. The subtraction only bites on a frame that is tall for
     its width — a 1024 x 768 tablet wants a NEGATIVE panel by this rule, so it takes
     the 150px floor, which is the corner minimap's own size. That is the honest
     answer there: the plane needs the width more than the navigation does.

     Below the crossover, shrinking further buys no module at all — only air. Hence a
     min() of the two and not the smallest number that fits. */
  /* A MENU IS AS WIDE AS ITS LONGEST WORD. This was 26vw — 219px of an 844px
     phone, a quarter of the screen spent on a plane preview nobody can read at
     that size ("die mini map nimmt zuviel platz weg"). The panel is a list of six
     names now, so the width is the words plus their inset, and every pixel it
     gives up goes to the content. */
  /* WIDER AGAIN, owner's word: "links das menu mehr platz geben". It had been cut
     to the words plus their inset when the panel was still a diagram fighting the
     content for the screen; as a list of six names it is the only navigation on a
     landscape phone and it was reading as a margin with type in it. 20vw against
     17, and the ceiling up from 172 to 208 so a wide short window gives it the
     room rather than spending it on air beside a point that already fits. */
  :root { --split-w: clamp(132px, 20vw, 208px); }
  body.is-split .viewport { left: var(--split-w); }
  /* The instrument is the panel PLUS the bar that hangs above it, and the centring
     below has to know that. Pinned as a number rather than measured, because the
     bar is content-sized and the calc cannot ask it — `min-height` on the bar
     makes the number honest instead of hopeful. */
  :root { --mm-bar: 23px; }

  /* The minimap stops being an ornament in a corner and becomes the panel. Same
     element, same six labelled points, same click that flies the camera — only the
     box changes, so nothing in the script has to learn about this.
     The aspect is the plane's, held by aspect-ratio rather than by the calc() the
     corner version used: the panel's width comes from a clamp, and a height derived
     from it in CSS would have to repeat the clamp. Centred vertically in what the bar
     leaves, because the panel is wider than it is tall and the plane is 1.56:1. */
  /* NOT top AND bottom. Setting both, with height:auto and an aspect-ratio, is
     OVER-CONSTRAINED: the edges win, the ratio is ignored, and the box comes out as
     tall as the column. Every point in here is placed in PER CENT of that box — see
     mmPlace() — so a box with the wrong ratio puts all six in the wrong place, which
     is exactly "the map rectangles are all wrong". The plane is 1.5556:1 and the box
     has to be too.
     So: one edge and a ratio. Centred with a translate rather than by margin:auto,
     which needs a definite height to work against. */
  /* THE PANEL IS THE MENU NOW — owner's word: "links die map zum menu machen".
     It was the plane in miniature: an 8400:5400 box with the six points pinned
     on it in per cent. Read at 219px wide that is a diagram of where things are,
     not a way to get there, and it cost a quarter of the screen to say it.
     The six points were ALREADY buttons carrying their own names and flying the
     camera on click (see the block that builds them in site.js) — so nothing is
     rebuilt here. They stop being pinned and become a list: `position: static`
     makes the inline left/top mmPlace() writes inert, and the flex column below
     puts them one under the other, flush on one left edge. "Gerade ausrichten."
     Full height rather than centred on a ratio: with no plane to hold there is
     no ratio to keep, and the free height belongs to the list. */
  /* :not(.is-doc) — AND THAT GUARD IS THE BUG FIX. `.is-doc .minimap { display:
     none }` further down has specificity (0,2,0); this rule has (0,2,1) and sets
     a display of its own, so the moment the panel became a flex column it started
     winning that fight and stood on top of every deep page: measured on
     /operations/stark, the menu at 0-143 over a page beginning at 56, covering
     the project rail and the first column of text. Guarding the selector is
     cheaper than a third rule and needs no !important — the panel is the map's
     menu, and a document is not the map. */
  body.is-split:not(.is-doc) .minimap {
    left: 0; right: auto; top: var(--nav-h); bottom: 0;
    width: var(--split-w); height: auto; aspect-ratio: auto;
    display: flex; flex-direction: column; justify-content: center;
    /* MORE AIR BETWEEN THE ROWS — owner's word, "gib dem menu vertical mehr
       space". Six words in a column five hundred pixels tall were using ninety of
       them. 3vh with a floor of 9 keeps the shortest landscape phone exactly where
       it was and lets a tall window breathe. */
    gap: clamp(9px, 3vh, 26px);
    padding: clamp(10px, 2vh, 18px) 0 0 max(20px, env(safe-area-inset-left));
    border: 0; border-right: 1px solid var(--line);
    background: rgb(var(--bg-rgb) / .92);
  }
  /* Out of the flow it was pinned in, into the column. */
  body.is-split .minimap .pt {
    position: static; transform: none;
    display: flex; align-items: center; gap: 9px;
    width: 100%; text-align: left;
  }
  /* ONE RULE FOR THE LABEL. There were two, forty lines apart and at the same
     specificity — a clamp here and a flat `font-size: 10px` further down — so
     source order won and the menu was pinned at ten pixels however much room the
     rail had. That is why it read small in a tall window.
     BIGGER, AND ON THE HEIGHT. The rail is the only navigation on this shape and
     it had six words in a column 500px tall using ninety of them. 2.4vh with a
     floor of 11 and a ceiling of 17: at the 287px landscape phone it stays where
     it was, and a tall window gets a menu instead of a caption. */
  body.is-split .minimap .pt .t {
    position: static; transform: none; left: auto; right: auto;
    font-size: clamp(11px, 2.4vh, 17px);
    letter-spacing: .1em;
  }
  /* The label hangs on a side only while the dot is a pin on a plane. */
  body.is-split .minimap .pt.flip .t { left: auto; right: auto; }
  /* The camera's rectangle means nothing without the plane it was drawn on. */
  body.is-split .minimap .rect { display: none; }
  /* The bar keeps the instrument's controls and goes to the foot of the rail —
     out of `bottom: 100%`, which with a full-height panel would have put it
     above the top edge of the screen. */
  /* ONLY THE IMPRINT DOWN THERE — owner's word, twice over: "unten links den
     kasten weg" and "in mobile kein nightmode. nur imprint".
     Three things stood in this corner: a rule, the camera's readout naming the
     point you are already standing on in the list above it, and the Nightvision
     lamp. The readout is a second answer to a question the highlighted menu row
     has already answered; the lamp is a colour scheme, which is a thing to offer a
     desk and not a phone held sideways; and the rule around them is what made the
     group read as a box. What is left is the one control German law wants
     reachable from anywhere, set as a plain line at the foot of the rail. */
  /* THE BAR IS EMPTY ON THIS SHAPE and so it goes. Everything it holds is a VIEW
     switch for a camera the reflow stack does not have: the readout named a point
     the lit menu row already names, Nightvision is a colour scheme to offer a desk
     rather than a phone held sideways, and Highground lifts you off a plane that is
     not there — wholeOpen() returns early unless mapMode(). What is left is the one
     control German law wants reachable from anywhere, and it is not on the bar any
     more; it is on the strip, which becomes the foot of the rail. */
  body.is-split .mm__bar { display: none; }
  body.is-split .mm__foot {
    position: static; order: 2; margin-top: auto;
    height: auto; border: 0; background: none;
    /* ZERO ON THE LEFT, FIVE AT THE FOOT, and both numbers are measured.
       The bar used to carry this line and kept its own 5px/6px inset into the rail,
       which put the Imprint at x=26 while every other thing in the column — all six
       menu words and the schematic — stands at x=20 (measured at 844x390 on the
       build before this one). Six pixels is not a lot and it is exactly enough to
       read as a wobble at the bottom of a column of five aligned words, so the
       strip has no horizontal padding at all and the line joins the column.
       The five at the bottom is the one part of that inset worth keeping: the rail
       runs to bottom:0, so without it the word sits ON the edge of the screen. Same
       clearance it had before, kept deliberately rather than inherited. */
    padding: 0 0 5px;
    flex-direction: column; align-items: flex-start;
  }
  body.is-split .mm__where,
  body.is-split .mm__high,
  body.is-split .mm__nv { display: none; }
  /* AND THE IMPRINT IS WHITE — owner's word. It is the one line at the foot of
     the rail and it was --grey-1 like the menu above it used to be, which on this
     scheme is the accent. White at .62, coming up on hover: the same voice as the
     menu, quieter because it is not where you are going. */
  /* THE SPAN AS WELL AS THE BUTTON. The word is wrapped in a <span> and the
     colour that reaches the glyphs is the span's, so a rule on the button alone
     changed the box and left the text where it was — measured: opacity and
     font-size took, colour did not. Both, and scoped to the rail so there is no
     doubt which instance is meant. */
  body.is-split .minimap .mm__foot .mm__cell,
  body.is-split .minimap .mm__foot .mm__cell > span {
    color: var(--ink);
  }
  /* THE RAIL'S IMPRINT IS A LINE, NOT A BUTTON. The desk instrument's two controls
     became bordered buttons; this one is alone at the foot of a menu column and a
     boxed control there would be the "kasten" that was taken out of this corner in
     the first place. Plain type, no ground, no border, no radius. */
  body.is-split .mm__cell {
    padding-left: 0;
    opacity: .62;
    background: none; border: 0; border-radius: 0;
    font-size: clamp(9px, 1.6vh, 12px);
    /* The hairline that separated it from the readout beside it. There is nothing
       beside it any more — the readout and the lamp are gone — so the border was
       the last stroke of the box that was asked to go. */
    border-left: 0;
  }
  body.is-split .minimap .mm__foot .mm__cell:hover,
  body.is-split .minimap .mm__foot .mm__cell:hover > span,
  body.is-split .minimap .mm__foot .mm__cell:focus-visible,
  body.is-split .minimap .mm__foot .mm__cell:focus-visible > span {
    color: var(--ink);
  }
  body.is-split .mm__cell:hover, body.is-split .mm__cell:focus-visible { opacity: 1; }
  /* ================= THE RAIL IS THE BIG MENU, IN A COLUMN =================
     Owner's word: "bei mobile keine roten quadrate. weiß mit underline wie das
     große menu."

     The square is gone, not restyled. It was the map pin — .pt is a POINT on the
     thumbnail and .d is its dot — and on the thumbnail that is exactly right: a
     mark at a coordinate, with the name hung beside it. In the rail there is no
     plane under it and no coordinate for it to be at, so it had become a bullet:
     six of them in a column, five red and one white, doing the job an underline
     does on the surface. Two grammars for one question.

     ONE GRAMMAR NOW, and it is the surface's, because that is the one the visitor
     has already read: every word in the ink, the one you are on carried by WEIGHT
     and confirmed by a rule in the accent. The transparent 2px sits on every item
     for the same reason it does up there — it reserves the rule's height so the
     column does not shift when the mark moves.

     NOT A SLIDING RULE, and that is deliberate rather than lazy. .nav__ink travels
     because the bar is a single line where one mark moving along it reads as one
     mark moving. A column has no such line: a rule jumping between rows is six
     borders switching on and off whichever way it is drawn, so it is drawn as
     borders — no second element, no placement pass, no measure to keep in step.

     The .62 that used to hold the inactive words back goes with the squares. It
     was the quieter half of a two-signal state that no longer needs two; the big
     menu sets all six words at full strength and lets weight do the telling. */
  body.is-split .minimap .pt .d { display: none; }
  body.is-split .minimap .pt .t {
    color: var(--ink); opacity: 1;
    padding-bottom: 4px; border-bottom: 2px solid transparent;
    transition: border-color .3s var(--ease), color .3s var(--ease);
  }
  body.is-split .minimap .pt.is-active .t {
    font-weight: var(--w-med); border-bottom-color: var(--phx);
  }
  /* A phone has no hover, but this shape is also a narrow window on a desk. Same
     answer as the surface gives. */
  body.is-split .minimap .pt:hover .t { color: var(--phx); }
  /* ================= THE MAP, MINIMISED, IN THE RAIL =================
     Owner's word, with the empty stretch under the menu circled: "mach links ins
     menu die minimap minimalisiert. nur quadrate."

     WHAT THE RAIL LOST WHEN IT BECAME A MENU. On this shape .minimap is not a map
     any more — the dots go static, the labels become the six words and the camera's
     rectangle is hidden, because there is no plane under it to draw a rectangle on.
     That trade was right and it stays; what went with it is the one thing the
     instrument existed for, which is WHERE the six lie in relation to each other. A
     column of words is an index, not a map. This is the map, at the size the rail
     can spare, and it is squares and nothing else — no labels, no rectangle, no
     ground. Six marks in their true positions.

     IT IS AN INDICATOR. pointer-events:none and aria-hidden in the markup: the
     labelled list above it already flies the camera, and this project keeps citing
     its own rule that two controls on one value end up disagreeing. A screen reader
     hears the six words once.

     THE WORLD'S OWN PROPORTION, 8400 BY 5400, and the sparse look is the truth
     rather than a fault: the points run 35 to 64 per cent across and 12 to 92 down,
     so they ARE a narrow vertical zigzag in a wide field. The desk instrument draws
     the same shape at 260px. Squaring the box to fill it would draw a plane the
     camera cannot fly. */
  body.is-split:not(.is-doc) .mm-mini {
    display: block; position: relative;
    /* Between the words and the imprint. .mm__foot takes order 2 and margin-top:auto
       (the bar itself is display:none on this shape), so this only has to be after
       the dots, which are order 0. */
    order: 1;
    /* Symmetric with the rail's own left padding — the panel has none on the right,
       its edge IS the border, so the inset has to be a margin here. */
    margin: clamp(14px, 4vh, 34px) max(20px, env(safe-area-inset-left)) 0 0;
    aspect-ratio: 8400 / 5400;
    pointer-events: none;
  }
  .mm-mini { display: none; }
  /* Square, and the size is the rail's to give. 1.2vh drew 5px marks at 405 and the
     zigzag read as dust rather than as six places; 1.6 is 6.5px there and 6 at the
     shortest landscape screen this shape reaches, which is a mark. The ceiling keeps
     it a schematic and not a second menu. */
  .mm-mini__d {
    position: absolute; display: block;
    width: clamp(6px, 1.6vh, 10px); height: clamp(6px, 1.6vh, 10px);
    transform: translate(-50%, -50%);
    background: var(--phx);
    transition: background .25s var(--ease);
  }
  /* The one you are on, in the ink — the same answer the underline gives upstairs,
     and the only place in the field where the accent is missing. */
  .mm-mini__d.is-active { background: var(--ink); }

  /* Touch targets. The dot is 9px and a finger is not — the hit area goes around it
     without the mark growing. */
  body.is-split .minimap .pt::before {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }
  /* ================= THE CONTENT, RE-DIVIDED FOR THE WIDE SHAPE =================
     Owner's word: "der content muss anders aufgeteilt werden". A landscape phone
     is 2.16:1 and a point is 1.44:1, so the points that fail here are the ones
     that stack what the shape would rather set side by side. Measured at 844x390,
     module 51 — four blocks overrun, and each is answered where it stands rather
     than by shrinking the whole plane.

     CONTACT is the big one: the form needs 359px and its five rows give 255. It
     is also the point with the most obviously wrong division for this shape —
     four fields stacked under a headline that is using the full measure above
     them. So the form takes the LEFT half over the point's whole height (seven
     rows, 357px — the number the form actually asked for) and the words move to
     the right of it. Only when no picture is set: with one, the right column is
     already spoken for and the authored composition stands. */
  /* 1/8, not 1/9 — the grid's end lines are EXCLUSIVE, and the first cut of this
     forgot it: column 8 belongs to the words on the right, so a form reaching to 9
     sat on them. Measured as three real overlaps before the correction.

     AND IT STARTS AT ROW ONE. Every point is authored from row 2 down, which leaves
     the top row as air — a margin the desk can afford and a 293px phone cannot. The
     form asked for 26px more than its seven rows gave at the module that fits the
     height; row 1 is 32 of them, already there, costing nothing but the air. */
  body.is-split [data-poi="contact"]:not(:has(.blk--img)) .blk:has(> .cform) {
    grid-column: 1 / 8 !important; grid-row: 1 / 10 !important;
  }
  /* WHICH TAKES ROW 9 FROM THE FOOT LINE, so the foot line moves rather than being
     squeezed. Across the whole point it was the desk's composition — a rule under
     everything. In the split the right column is empty from row 7 down (headline
     1/4, lede 4/7), so the address and the legal line go there, under the words
     they belong to, and the form gets the ninth row its fields need.
     The form is the one block whose height does not follow the module at all: its
     fields are padding and border in pixels. At a row of 30 it wanted 250 against
     240; with nine rows it clears every row down to 27, which is a screen of 257. */
  body.is-split [data-poi="contact"]:not(:has(.blk--img)) .blk:has(> .mail) {
    grid-column: 8 / 14 !important; grid-row: 7 / 10 !important;
  }
  /* AND IN SIX COLUMNS IT IS NOT ONE LINE. The row was authored across all
     thirteen, where "at thirteen modules there is room" is true; at six it measured
     22 to 94px past the edge depending on the screen. Same answer as the stacked
     page: the icons keep their row, the legal line takes the one under them. It has
     three rows here, which is what the move bought it. */
  /* THE FOOT SITS AT THE FOOT. Its three rows are the bottom of the point, and the
     address, the icons and the legal line were pinned to the TOP of them — so the
     right column's ink stopped at 361 of 504 and the last third of the point was
     air. Measured on the owner's shape, 1155x540. Pushed down, the column is
     anchored at both edges: the headline at the top line, the foot on the bottom
     one, and the reading between them. Owner's word: "contact layout könnte noch
     etwas liebe gebrauchen." */
  body.is-split [data-poi="contact"] .blk:has(> .mail) {
    display: flex; flex-direction: column; justify-content: flex-end;
  }
  body.is-split [data-poi="contact"] .social { flex-wrap: wrap; row-gap: 6px; }
  body.is-split [data-poi="contact"] .social .legal {
    margin-left: 0; white-space: normal; flex-wrap: wrap;
  }
  /* AND THE FIELDS GO TWO-UP. This is the change that actually uses the width:
     the form is the one block whose height barely follows the module, because a
     field's height is padding and border in pixels, not modules. Stacked, it
     asked for 354px at any module and got whatever nine rows happened to be.
     Side by side it asks for two hundred and something, and the block it sits in
     can shrink with everything else. Briefing, button and the status line keep
     the full measure — a textarea in half a column is a slot, not a place to
     write. */
  body.is-split .cform {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px;
  }
  body.is-split .cform__row--area,
  body.is-split .cform__cta,
  body.is-split .cform__note,
  body.is-split .cform__pot { grid-column: 1 / -1; }
  /* THE BUTTON IS NOT THE WIDTH OF THE FORM. It takes the full column pair so its
     ROW is not shared, and a grid item's default justify is stretch — so it grew to
     490px where the desk's identical button is 221. Measured side by side: seven
     modules against two and a half. A white slab across half the point, and the
     first thing the eye lands on instead of the words beside it. It keeps its own
     row and takes its own width. */
  body.is-split .cform__cta { justify-self: start; }
  /* THREE SHORT FIELDS, THREE COLUMNS. Two columns leave Deadline alone with an
     empty cell beside it — the lopsided half of the form in the owner's picture.
     Name, E-Mail and Deadline are all one-line inputs and the shape is wide; the
     briefing, the button and the status line keep the full measure below them. */
  body.is-split .cform { grid-template-columns: 1fr 1fr 1fr; }
  /* The words move up with it, or the two halves stop sharing a top edge. */
  body.is-split [data-poi="contact"]:not(:has(.blk--img)) .blk:has(> h1, > h2) {
    grid-column: 8 / 14 !important; grid-row: 1 / 4 !important;
  }
  body.is-split [data-poi="contact"]:not(:has(.blk--img)) .blk:has(> .lede) {
    grid-column: 8 / 14 !important; grid-row: 4 / 7 !important;
  }

  /* THE OPERATIONS CARD CAPTION takes the empty row under it. Three lines of
     type — number, name, filing — were authored into ONE module because at 88
     that is 88px and plenty; at 51 it is 51 and the name alone is 42. The row
     below is the deliberate air between the work and the door, and on a phone
     that air is the cheapest thing on the point. */
  body.is-split [data-poi="operations"] .opcard__meta {
    grid-row: 7 / 9 !important;
  }
  /* AND THE SQUARES TAKE THE ROW ABOVE THEM. Their width comes off their height
     (see .opcard), and a row is 30px on this shape against a column of 52 — so the
     three rows the markup gives them came out as 90px squares in a point 702 wide,
     which is a strip of stamps with more gutter than picture. Row 3 is empty here:
     the statement above stands on rows 1 and 2. Four rows makes them 120, and the
     foot still lands exactly on row 7 where the captions begin. */
  body.is-split [data-poi="operations"] .opcard {
    grid-row: 3 / 7 !important;
  }
  /* The gutter is a margin now (see index.php), and on this shape a quarter of a
     COLUMN is a quarter of 52 next to frames whose height is four rows of 30. Half
     a row is the same air measured in the direction that is actually scarce. */
  body.is-split [data-poi="operations"] .opcard,
  body.is-split [data-poi="operations"] .opcard__meta {
    margin-right: calc(var(--m-row) * .5) !important;
  }

  /* And the form's own rhythm gives back what the rows cannot. Eleven was right
     while a row was 43px; at 30 the form asked for ten more than its eight rows,
     and four gaps at seven instead of eleven is sixteen — the difference, without
     touching a field or the type in it. */
  body.is-split .cform { gap: 7px 14px; }

  /* MISSION'S PILLARS TAKE THE ROWS UNDER THEM. Three columns of ten to twelve
     lines were authored into three rows — which is 264px at the desk and 129 on
     a phone, and they were the single block group holding the whole plane's
     module up: at 45 they overran by 39px, at 40 by 106. Rows 8 and 9 beneath
     them are empty (the film beside them stands in columns 10-14 and is not
     touched), so the pillars simply reach down into their own point. Measured
     after: the module can come down from 51 to 43 with nothing overrunning. */
  body.is-split [data-poi="mission"] .blk--rule.blk--top {
    grid-row: 4 / 10 !important;
  }
  /* WHICH IS ROW 4 BECAUSE THE HEADLINE GAVE IT UP. Moving the headline from 2/5 to
     1/4 keeps it three rows — it loses nothing — and hands the pillars a sixth, the
     32px they were short of at the module that fits a 293px screen.
     It also settles an overlap that has been in the authored layout all along and
     only shows on this shape: the headline spanned 1/14 on rows 2-5 while the film
     panel stands at 10/14 on rows 4-10, so both owned row 4. Two blocks, one cell,
     measured on the desk as well. At 1/4 the headline stops one line short of it. */
  /* h1 OR h2, because the route decides which one this is. Every page names its
     own point in the h1 and demotes the other five to h2 (see route_h1()), so a
     rule written for h1 alone moved the headline on / and left it where it was on
     /careers — which is how three overlaps appeared in the sweep on one route and
     not on the other. The contact rule above already had both; this one did not. */
  body.is-split [data-poi="mission"] .blk:has(> h1, > h2) {
    grid-row: 1 / 4 !important;
  }

  /* CAREERS' LEDE keeps its second row — three lines of reading type were given
     one module, which at 88 is generous and at 43 is one line short. The authored
     rows moved up when the point was re-divided (the roster now starts at 5, not
     6), so the lede's second row is the roster's first and the two swap: the lede
     takes 4/5 as authored and the ROSTER gives up its top row instead, which it
     can afford — it is a list of short lines and the lede is a paragraph. */
  body.is-split [data-poi="careers"] .blk:has(> .lede) {
    grid-row: 4 / 6 !important;
  }
  body.is-split [data-poi="careers"] .blk:has(> .roles),
  body.is-split [data-poi="careers"] .blk.roles {
    grid-row: 6 / 8 !important;
  }

  /* The reading line-height comes in a step on the split — 1.45 is still
     comfortable at 13px, and every pixel it returns is one the module does not
     have to grow to provide. */
  body.is-split .poi .sub { line-height: 1.45; }

  /* ================= SMALLER, BECAUSE THE SCREEN IS SMALLER =================
     Owner's word from a real phone: "alles zu groß. map abgeschnitten. mach halt
     die headlines etwas kleiner." The photograph told the rest — a landscape
     Android with its address bar standing, which leaves about 300 CSS pixels of
     height, not the 390 an iPhone's chrome-free landscape suggests. A point is
     nine module-rows whatever the type does, so at module 43 it stood 387 tall in
     300 and lost its foot.
     The display size comes down a fifth (.7857 -> .62 of the module) and the
     three reading sizes drop one step at their FLOOR, which is the number that
     was pinning everything: below module 60 the type had stopped shrinking while
     the blocks kept going. Together they buy the module itself — 43 -> 36, a
     point of 324 instead of 387 — and the type is smaller as asked at every step
     of it. Split only: the desk still reads at its authored scale. */
  body.is-split {
    /* The column decides how wide a line may be; the ROW decides how tall a
       headline may be, because it is rows the headline has three of. min() of the
       two is the only size that answers both — at 54 columns and 30 rows that is
       28px rather than 33, and three lines of it clear a three-row block. */
    --h-size: min(calc(var(--m) * .62), calc(var(--m-row) * .95));
    --t-lede:  clamp(12.5px, calc(var(--m) * .2273), 24px);
    --t-sub:   clamp(11px,   calc(var(--m) * .1761), 19px);
    --t-small: clamp(9.5px,  calc(var(--m) * .1364), 15px);
  }
  /* The two blocks that still overran at 36, each where it stands: the Careers
     button is a 44px control in a one-module box, and the form's fields carry
     padding that has nothing to do with the module. */
  body.is-split [data-poi="careers"] .blk:has(> .copy__cta) {
    grid-row: 8 / 10 !important;
  }
  /* The two blocks under the roster move with it: the button follows the text it
     belongs to and the address follows the button, both one row lower than the
     desk sets them because the roster took that row back above. */
  body.is-split [data-poi="careers"] .blk:has(> .mail) {
    grid-row: 9 / 10 !important;
  }
  body.is-split .cform input, body.is-split .cform textarea {
    font-size: 14px; padding: 2px 0 5px;
  }
  body.is-split .cform__row > span { margin-bottom: 2px; }
  /* THE PILLAR TITLES COME OFF THEIR FLOOR. .rec-title is set in --t-lede-c, whose
     clamp bottoms out at 18px — and the note at that rule promises "the longest
     authored title (Unmistakable, 12 characters) measures well under half the
     three-module block". That promise was made at module 88, where the block is
     264px. At 32 the block is 96 and the type is still 18, so the word measured 108
     against 99 and printed straight into "Deployed" beside it. Photographed by the
     owner as one word: "UnmistakableDeployed".
     --h-rec is the fix and it is already in the sheet: .4107 of the module, no
     clamp, the deep layer's own reading-size headline. 13px at module 32 against a
     paragraph of 11 — still a step up, which is the whole reason the title grew in
     the first place — and the word measures 79 in a block of 80. */
  /* .poi, AND THAT SCOPE IS A FIX. This was written for MISSION'S THREE PILLARS —
     the only .rec-title on the surface — but it was left unscoped, and
     `body.is-split .rec-title` is (0,2,1) against the deep page's own
     `.case .rec-title` at (0,2,0). More specific wins wherever they meet, so every
     record NAME and every subhead on every deep page took the pillars' size in
     landscape: measured .411 of the module where the page asks for .27. The pillars
     keep what they were given; the pages keep theirs. */
  body.is-split .poi .rec-title { font-size: var(--h-rec); }
  /* THE CARD'S NAME IS BOUND BY ITS ROWS, NOT ITS COLUMN. .opcard__name is set off
     the module, which now follows the WIDTH — so on a wide short screen it grew to
     19px while the two rows under the photograph shrank to 30, and "Quantum
     Systems" wrapping to two lines put the block 9px over. Off the row measure it
     is 15px, the caption's three lines measure 60 in a block of 60, and the name is
     still the largest thing in the group. */
  body.is-split [data-poi="operations"] .opcard__name {
    font-size: calc(var(--m-row) * .5);
  }
  /* ================= AGENCY: THE PICTURE LEFT, THE WORDS RIGHT =================
     Owner's word: "da ist unten abgeschnitten" with the one-liner running off the
     bottom of the point, and then the answer: "bild schmaller und links, headline
     und text rechts on mobile?"

     WHAT WAS ACTUALLY BROKEN. The point stacks: picture across all thirteen
     modules, slider under it, then the callout in ROW NINE — the last row there
     is. On the desk that row is a full module and the three parts sit in it as
     authored (name and role on one line, the one-liner under them, "measured at
     ~56px of the 88"). On a landscape phone the row measure is 42px, so the
     one-liner had 42px to be the second line of a caption in. Measured at 880x405,
     module 52 / row 42: the copy ran 16.4px past the bottom of its own block, and
     row nine has nothing under it to grow into. Not a spacing fault — a stack that
     ran out of floor.

     A LANDSCAPE PHONE IS 2.16:1 AND A POINT IS 1.44:1, which is the whole argument
     of this section: the points that fail on this shape are the ones that stack
     what the shape would rather set side by side. So the picture takes the left
     seven modules over eight rows and every word moves to the right six — the
     statement at the top, the name, the role and the one-liner under it, in the
     order they are read. The one-liner now has five rows to wrap in instead of
     half of one.

     THE PORTRAITS GET BETTER, NOT WORSE, AND THAT IS ARITHMETIC RATHER THAN LUCK.
     faces.js keeps its tiles square, so the tile grid's aspect always equals the
     panel's; at thirteen by six that is 2.17 against a portrait's 0.81, which is
     why the comment on the markup says each tile shows a horizontal band of a face
     rather than a face. Seven by eight is 1.08 — near square. Smaller picture,
     more face in it. */
  body.is-split [data-poi="agency"] .blk--faces {
    grid-column: 1 / 8 !important; grid-row: 1 / 9 !important;
  }
  /* THE STATEMENT LEAVES THE PICTURE BY LEAVING ITS BOX. It is a child of the
     panel block and absolutely positioned at its top left — it lies ON the faces
     on the desk, which is the design and stays. left:100% walks it out of the
     right edge of a block that is now seven modules wide, which puts it in column
     eight without a second element and without the markup knowing there are two
     layouts. .blk--faces is overflow:visible already, for the fray.
     THE PLATE GOES WITH IT. The knockout exists because type on a photograph of a
     face needs a ground; on the plain plane beside it there is nothing to knock
     out, and inset:-6px 0 would only reach into the neighbours. */
  /* WIDTH, NOT MAX-WIDTH, and that is not a preference. An absolutely positioned
     box with a `left` and no `right` is shrink-to-fit, and the space it fits into
     is the containing block MINUS left — which at left:100% is nothing. Measured:
     it collapsed to its longest word, 138px, and the statement came out four lines
     tall and 5.5px into the callout under it. Stated width, six modules, and it is
     two lines again.
     SIX MODULES AND NO GUTTER, so its left edge is column eight — the same module
     line the callout below it stands on. A margin here would have set the
     statement 13px right of the name under it, which is the one thing a column of
     type must not do. The seam between picture and words is a grid line, the way
     it is on Contact and Careers.
     THE PLATE STAYS. I took it off on the argument that it is no longer lying on a
     photograph, and that was half the argument: it is still lying on the RASTER.
     Nothing in the grid claims rows 1-3 of the right column, so the mosaic tiles
     straight through there — the knockout is what it was always for. */
  body.is-split [data-poi="agency"] .faces__over {
    left: 100%; top: 0;
    width: calc(var(--m) * 6);
    padding: 0;
  }
  /* Under the picture, on the picture's own measure — it drives what is above it,
     so it belongs to that column and not to the words. */
  body.is-split [data-poi="agency"] .faces__walk {
    grid-column: 1 / 8 !important; grid-row: 9 / 10 !important;
  }
  /* Under the statement, in the same column. FOUR ROWS FOR THE WORDS AND FOUR FOR
     THE HEADLINE ABOVE THEM, and both numbers are measured rather than balanced.
     The callout's worst case is the longest one-liner in the unit — measured by
     walking all nine: a name, a role on its own line and four lines of copy, 135px
     of the 168 four rows give. The statement's real content is two lines, 66px;
     four rows let it run to five before it reaches the name, which is more than
     twice what either the local or the live copy uses.
     THAT IS A BOUND, NOT A GUARANTEE, and it is worth saying out loud: the
     statement is absolutely positioned — it lies ON the faces on the desk, which
     is the design — so the grid cannot know how tall it got. Five lines of display
     type in a six-module column on a landscape phone is already a content error
     rather than a layout one, but whoever writes a longer opening line is
     re-authoring this edge with it. Same note as the film on Mission. */
  body.is-split [data-poi="agency"] .faces__callout {
    grid-column: 8 / 14 !important; grid-row: 5 / 9 !important;
  }
  /* AND IT IS A COLUMN NOW, NOT A LINE THAT WRAPS. Six modules is 312px and the
     name alone is most of it, so `row wrap` with two nowrap items put the role on
     its own line anyway — but by overflow rather than by intent, with the 16px
     column gap still sitting between them. Named as the stack it has become. */
  body.is-split [data-poi="agency"] .faces__callout { flex-flow: column nowrap; }
  body.is-split [data-poi="agency"] .faces__callout > * { flex: 0 0 auto; }
  /* THE ROLE MAY WRAP HERE. It is nowrap because it shares a line with the name on
     the desk, where thirteen modules make that easy. In a six-module column it does
     not share anything — and the longest of the nine measures 293px against a
     column of 246 at the narrowest split module, which is 13px of a job title
     hanging off the right of its own block. Measured at 700x340, the narrowest
     window this shape reaches before the reflow stack takes over at 699. */
  body.is-split [data-poi="agency"] .faces__role { white-space: normal; }
  /* AND THE ONE-LINER FOLLOWS THE MODULE. 17px is a hard pixel — the one size on
     this point that does not answer to the grid — and it was set for a caption
     running the full thirteen modules under a picture. In six it is four lines,
     and four lines of 17px against a name of 24 is a paragraph wearing the
     callout's clothes. Module-bound with the same floor the name has: 14.6px at
     the module this was measured on, 12 at the narrowest, and the step down from
     the name is kept at every size instead of only at one. */
  body.is-split [data-poi="agency"] .faces__copy {
    font-size: clamp(12px, calc(var(--m) * .28), 17px); line-height: 1.35;
  }

  /* ================= THE FORM TAKES THE BLOCK IT WAS GIVEN =================
     Owner's word, with the empty half of the left column in the picture: "die
     input form findet darunter doch noch gut Platz."

     It did, and it was not using it. The block is nine rows — 378px, the whole
     height of the point, which it was given when the form needed 359 and its seven
     rows gave 255. The form inside it measured 190. The briefing box was still
     wearing the 4em it was cut down to for that old shortage, so the one field
     anybody actually writes in was 56px tall with 188px of nothing under it.

     THE SLACK GOES WHERE WRITING HAPPENS. The form stretches to its block and the
     rows are named: the three short fields and the button and the status line are
     as tall as they are, and the briefing takes everything left over. minmax(4em,
     1fr) keeps the old height as its FLOOR, so on a screen with no slack to give
     the form is exactly what it was before this.
     The label is the growable row's item and grid items stretch, so it is made a
     flex column and the textarea flexes inside it — height:auto instead of 4em,
     with the same 4em as a min-height. Three declarations rather than a second
     hard number to keep in step. */
  body.is-split .blk:has(> .cform) > .cform {
    flex: 1 1 auto; min-height: 0;
    grid-template-rows: auto minmax(4em, 1fr) auto auto;
  }
  body.is-split .cform__row--area { display: flex; flex-direction: column; min-height: 0; }
  body.is-split .cform textarea { height: auto; flex: 1 1 auto; min-height: 4em; }


  /* ================= THE DOOR IS NARROWER ON A PHONE =================
     Owner's word: "mach den deep recon button on mobile schmaller." Five modules
     of a thirteen-module measure is 260px of 676 — over a third of the point given
     to the way OUT of it, on the shape with the least room to give. Measured what
     the band actually asks for at module 52: 164px, or 3.16 modules — the label at
     .38 of a module, the arrow at .58, the padding at .2 either side, all three
     module-bound, so that ratio holds at every module the split can produce. Four
     modules is 208px: a quarter narrower, and still 26% more than the band needs
     at its tightest.
     Flush right, as it was. It is the point's last block and its right edge is the
     point's right edge; taking the width off the LEFT is what makes it read as a
     door at the end of the row rather than a band that shrank. */
  body.is-split .dive-band { grid-column: 10 / 14 !important; }

  /* The whole-plane button is the other way of seeing all six, and in the split the
     panel already is that. Two controls for one thing, in a window this size. */
  body.is-split .wholebtn { display: none; }

  /* THE MENU GOES. The panel on the left IS the navigation — six labelled points that
     fly the camera — so the six words in the bar are the same six a second time, in a
     window that has no room for either of them twice. Same argument as the overview,
     where the plane itself became the menu.
     display:none and not opacity: there is nothing to fade to here, the split is a
     layout and not a state you pass through. */
  body.is-split .nav__menu { display: none; }

  /* AND THE WORDMARK SITS OVER THE MAP. The bar spans the window, the panel starts
     under it on the left — so the name is already above the panel; what it needed was
     to stop being a masthead for a 1440px window. .19 of the module against the .295
     it has on a desktop: at module 36 that is 7px of type in a 18px bar, which is a
     label over a column rather than a title over a site.
     No margin-right either: the six words it was being separated FROM are gone. */
  /* .38, not .19. The first number halved the desktop's .295 on the argument that a
     smaller window wants a smaller masthead — and at module 36 that came out at 7px,
     which is not a masthead, it is a footnote. .38 of the module is 14px at 36 and 17px
     at 46, so it reads as the name of the thing while still being the smallest it has
     ever been. The module is already doing the shrinking; this does not need to as
     well. */
  body.is-split .nav__brand {
    /* .33 and a floor of 14, down from .38 and 15 — owner's word, "dem logo etwas
       luft und ggf kleiner". The module now follows the WIDTH on this shape, so
       .38 of it had quietly grown: at a column of 54 the mark came out at 21px in
       a bar half a row high, and it was the largest thing on the rail without
       being the most important. */
    font-size: max(14px, calc(var(--m) * .33));
    margin-right: 0;
  }
  /* THE AIR IS UNDER IT, not around it: the bar is fixed and half a row tall, so
     padding inside it would only push the mark into the plane. The list below is
     what moves. */
  body.is-split:not(.is-doc) .minimap {
    padding-top: clamp(16px, 3.4vh, 30px);
  }
  .viewport img { -webkit-user-drag: none; user-drag: none; }

  .world { position: absolute; top: 0; left: 0; width: var(--world-w); height: var(--world-h);
           will-change: transform; transform-origin: 0 0; }

  /* The module raster. This is NOT backdrop — it is the grid every block is
     authored against, the thing the hairlines continue and the phantom plates
     knock holes in. It stays for that reason, and because --grid-a is a CMS
     slider: taking it to 0 removes it without touching code. */
  .world-grid {
    position: absolute; inset: 0; pointer-events: none;
    background-image:
      linear-gradient(to right, rgb(var(--ink-rgb) / var(--grid-a)) 1px, transparent 1px),
      linear-gradient(to bottom, rgb(var(--ink-rgb) / var(--grid-a)) 1px, transparent 1px);
    /* var(--m) across, var(--m-row) down — the two measures of the point, so the
       lattice stays the visible form of the grid the blocks are authored on even
       where the two differ. */
    background-size: var(--m) var(--m-row);
    /* Lifted with everything else on the plane. This raster is the visible form of
       the same lattice the backdrop tiles on, so leaving it at the world origin
       would draw lines half a module away from the blocks they measure. */
    background-position: 0 calc(var(--lift) * -1);
    /* GONE BY THE TIME THE OVERVIEW IS OPEN. The camera scales this element, and a
       1px gradient line at an eighth of scale is an eighth of a pixel: the browser
       resamples it and the result crawls while the scale eases — the flickering lines
       along the edge of the plane. Faded on --whole-e (written by render) rather than
       switched on a class, so it goes out with the movement instead of popping.
       Times two, so it is out by the time the overview is a third open and the whole
       flicker window is skipped. Nothing is lost: an 11px lattice measures nothing a
       reader can use at that distance. */
    opacity: clamp(0, calc(1 - var(--whole-e, 0) * 2), 1);
  }
  /* ON A PAGE, no lift. The plane's half-module lift is a transform on the .poi
     elements of the MAP; a document takes none — mosLift() returns 0 in doc mode —
     so the lattice starts at the top of the reading rail. It sits inside .doc-body
     (see inc/plane-deep.php) rather than in the fixed .world--doc, or it would hold
     still while the bands scrolled past it. z-index 0 keeps it under the bands, which
     are positioned; pointer-events are already off. */
  /* z-index -1, not 0. A positioned element at 0 paints in the same step as the
     positioned .blk blocks and stays under them only because it comes first in the
     DOM — which is one reorder away from grid lines over the copy. At -1 it escapes
     to the nearest stacking context, which is .world--doc (z-index 2), and lands
     between that element's own opaque ground and everything else in it.

     COLUMNS ONLY, and that is a correction rather than a saving. A first version drew
     the full square lattice, and the horizontal lines were a lie: the bands are
     separated by HALF and one-and-a-half module margins (.band--head/.band--body take
     .5, .band--hero/.band--shot 1.5), so after the first band every row edge sits off
     a one-module pitch and the raster claimed a rhythm the layout does not have. The
     thirteen columns are exact — every block on every band is placed with
     grid-column on them — so those are what is worth drawing. A scrolling page has
     columns; it does not have rows. */
  .world-grid--doc {
    background-position: 0 0;
    background-image:
      linear-gradient(to right, rgb(var(--ink-rgb) / var(--grid-a)) 1px, transparent 1px);
    background-size: var(--m) 100%;
    z-index: -1;
  }

  /* The mosaic. Colour, shape and weight are set per-frame from the palette in
     JS, so there is nothing to style here beyond placing the surface. */
  .worldmap { position: absolute; inset: 0; pointer-events: none; }

  /* .poi__over lived here — a block sitting ON another one in the same grid area,
     centred and inert, which is how Mission's headline lay over the film. Mission
     was its only user and the headline is a block of its own now: the film shrank
     to four modules, and a headline centred over a quarter-size frame would be a
     caption for a thumbnail. With it went .poi__over h1's text-shadow (there is
     no moving frame behind the type any more) and the two --spread variants,
     which had already lost their markup. The faces panel keeps its own overlay —
     that is .faces__over, a different class, still in use on Agency. */

  /* ================= THE THING IN THE CORNER =================
     See assets/invaders.js. Everything here is inside .world, so it pans with the
     plane and is measured in the same module as everything else. */

  /* One module of nothing in the plane's bottom-left corner. No mark at rest: a
     hidden thing with a label is a menu item. The hairline on hover is the only
     tell, and you only get it if you are already sweeping the corner. */
  .inv-trigger {
    position: absolute; left: 0; bottom: 0;
    width: var(--m); height: var(--m);
    background: none; border: 0; padding: 0; cursor: pointer;
    appearance: none; -webkit-appearance: none;
  }
  /* A registration mark, not a button face: two short hairlines meeting in the
     plane's own corner. It reads as the drawing's corner tick — which every other
     hairline in this project also is — so it gives the thing a target without
     announcing itself as a control. Without it the trigger was one invisible module
     in an 8400 x 5400 field, which is not hidden, it is absent. */
  .inv-trigger::before, .inv-trigger::after {
    content: ""; position: absolute; background: var(--line);
    transition: background .2s var(--ease);
  }
  .inv-trigger::before { left: 0; bottom: 0; width: 1px; height: 38%; }
  .inv-trigger::after  { left: 0; bottom: 0; height: 1px; width: 38%; }
  .inv-trigger:hover::before, .inv-trigger:hover::after { background: var(--phx); }
  .inv-trigger:hover::before { height: 76%; }
  .inv-trigger:hover::after  { width: 76%; }

  /* pointer-events off on the whole layer: a drag that starts on an invader has to
     pan the plane like a drag anywhere else. */
  .inv-layer { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
  .inv-layer[hidden] { display: none; }

  /* One element, one box-shadow, eleven by eight art pixels — the shape is built in
     JS so the pixel size can follow the module. currentColor so it inherits the ink
     and a scheme change takes it along. */
  .inv {
    position: absolute; display: block; color: var(--ink);
    opacity: .5; transition: opacity .25s var(--ease), transform .25s var(--ease);
  }
  .inv.is-hit { opacity: 0; transform: scale(1.6); }

  .inv-shot {
    position: absolute; display: block; width: 2px;
    background: var(--phx); transform: translateX(-1px);
  }

  /* The only readout, and it sits in the corner it came from. */
  .inv-count {
    position: fixed; left: 24px; bottom: 18px; z-index: 45;
    font-size: 10px; letter-spacing: .22em; text-transform: uppercase;
    color: var(--grey-1); font-variant-numeric: tabular-nums; pointer-events: none;
  }
  .inv-count[hidden] { display: none; }

  /* ================= THE WORDMARK, IN THE PLANE =================
     Two lines, set at module positions in the GAPS between the points of interest,
     and part of the world rather than of the chrome — so it pans with the map and
     you come across it rather than being watched by it.

     Deliberately NOT a .poi: the camera resolves the landed point by nearest .poi
     centre, so a wordmark that qualified would be a place the wheel route could
     stop at, which it is not.

     And pointer-events: none. It is a mark, not a control; a drag that started on
     it must pan the plane like a drag anywhere else. The route to Mission is the
     menu item named Mission.

     Its boxes go into the mosaic's cutout list like any other content, so the
     tiling stops around it instead of running through the letters. */
  /* ONE wordmark, two ways of placing it: .mark is dropped into the gaps BETWEEN
     the points, .poi__mark into the free grid of a point itself. The type is the
     same in both — it has to be, or the one on Mission would read as a second
     mark rather than as the same one landing closer in. */
  .mark, .poi__mark {
    pointer-events: none; user-select: none;
    font-size: calc(var(--m) * .44); line-height: .88;
    font-weight: var(--w-med); letter-spacing: -.035em;
    color: var(--ink); white-space: pre;
  }
  /* GONE FROM THE HIGH GROUND — owner's word, "bei highground auch die andren
     bilder auf der map ausblenden."
     WHICH TURNED OUT TO BE THESE. The overview already empties every point
     (body.is-overview .poi > *:not(.poi__ov)), so I enumerated what was actually
     left standing on the plane with the state forced on: ten .mark wordmarks and
     nothing else. No loose ground picture is set in this install at all — the
     class is there, the count is zero — so it joins the rule for the day one is,
     rather than being left as the next thing to notice.
     FADED ON --whole-e, NOT SWITCHED, which is the same device the module lattice
     uses eleven hundred lines up and for the same reason: the decoration leaves
     WITH the movement instead of popping out of it at the start. Times two, so it
     is gone by the time the overview is a third open.
     The mosaic stays. It is not decoration set onto the plane, it is the plane's
     own texture — the thing the six points are placed in — and at this distance it
     is what makes the whole read as a map rather than as six boxes on black. */
  .mark, .blk--loose {
    opacity: clamp(0, calc(1 - var(--whole-e, 0) * 2), 1);
  }
  .mark {
    position: absolute;
    /* The wordmark is set INTO the plane, so it rides up with it. Left behind, it
       would be the one thing on the map still sitting at the old height. */
    transform: translateY(calc(var(--lift) * -1));
  }
  /* "one" flushes RIGHT against the width of "defcom". The mark is absolutely
     positioned, so its box shrink-wraps to the wider of the two lines — which is
     "defcom" — and text-align has something real to align to. */
  .mark b, .poi__mark b { font-weight: var(--w-light); display: block; text-align: right; }

  /* Inside a point the box does NOT shrink-wrap by itself: a grid item fills its
     cell, and "one" would then flush right against four modules of empty cell
     instead of against "defcom". max-content buys back exactly what position:
     absolute gives .mark for free. */
  /* align-self:start, not end, and it is not a preference: the point is 9 modules
     tall against a viewport that is 8 at module 120, so the camera cuts the last
     row. Hung on the BOTTOM of row 8 the mark ends at 955 against 950 of frame —
     five pixels of it were gone. It hangs off the top of the row instead, on the
     film's own bottom line. */
  .poi__mark { justify-self: start; align-self: start; width: max-content; }

  /* ================= POIs ================= */
  /* translateY, not a change to `top`: offsetTop stays the authored world position,
     so gotoPOI centres on the same number it always did and the whole plane lifts
     together rather than each point drifting from the route between them. */
  /* NINE ROWS, WHATEVER IS IN THEM. grid-auto-rows alone means a point is as tall as
     its content, and that turned out to be load-bearing by accident: removing the
     wordmark from Mission's row 8 took a whole module off the point, dropping it from
     8 rows to 7 while the other five stood at 9. The camera centres a point on its
     BOX, so a point that shrinks when a decoration leaves also moves its own copy up
     the window — a layout that depends on what happens to be in the last row.
     grid-template-rows pins the frame; grid-auto-rows still catches anything placed
     past it. The points are the same size now because they are the same size, not
     because they happen to be equally full. */
  .poi { position: absolute; display: grid;
         grid-template-rows: repeat(9, var(--m-row)); grid-auto-rows: var(--m-row);
         gap: 0; transform: translateY(calc(var(--lift) * -1)); }
  /* position:relative is load-bearing — .media inside is absolutely positioned and
     would otherwise resolve against the .poi and cover the whole point of interest. */
  /* THE PADDING FOLLOWS THE MODULE DOWNWARD, and only downward. A flat 20px was
     right at every module the desktop ever sees and wrong on a phone: at the
     landscape module of 39 a two-module block is 78px wide, of which 40 was
     padding — half the block spent on air, and the copy inside it crushed into
     38px of measure. min() is the whole trick: 20px stays the ceiling, so every
     module at or above 80 renders exactly as before (88 and the CMS default 120
     both hit the ceiling — bit-identical, verified), and only the small modules
     get their measure back. Owner's word: the content has to fit the landscape. */
  .blk { min-width: 0; position: relative; display: flex; flex-direction: column;
         justify-content: center; padding: min(20px, calc(var(--m) * .25));
         background: var(--phantom); }
  .blk--bare  { background: transparent; padding: 0; }
  .blk--top   { justify-content: flex-start; }
  /* ================= NO HAIRLINES, ANYWHERE =================
     The horizontal separators are gone — asked for twice, each time plainly. It
     went in three cuts, and the trail matters: first the deep pages
     (`.case .blk--rule { border-top: 0 }` stood here), then Mission's pillars
     (a [data-poi=mission] override in index.php), and now the owner circled the
     Careers lede, both Contact rules and the Operations card meta — which was
     everything still drawing one. Three overrides chasing one property is the
     property being wrong at the source, so the border is off the BASE rule and
     the overrides are deleted, not stacked.

     THE CLASS STAYS, and that is the whole care here. .blk--rule:has(> .sub) and
     its siblings further down this file carry the PLATE — the ::before filled with
     --phantom that stops the mosaic and the .world-grid hairlines running straight
     through reading copy. Swapping the class out for .blk--bare would have kept the
     plate (that selector list covers both) but dropped .blk--rule's own padding;
     dropping the class outright would have taken the plate with it and let the
     tiling through the text. Turning off one property keeps both.

     No side padding: a headline sits in a --bare block (padding 0) and its lede
     in a --rule block directly beneath, on the same columns. Inheriting .blk's
     20px would push the lede in by 20px and break the left edge they share. The
     top padding stays — it is the air where the rule used to be, and taking it
     too would re-set every lede on the map. */
  .blk--rule  { background: transparent; padding: 20px 0 0; }
  .blk--left  { border-left: 1px solid var(--line); background: transparent; }
  .blk--fill  { background: var(--ink); }
  .blk--fill, .blk--fill * { color: #fff; }
  .blk--img   { padding: 0; overflow: hidden; background: rgb(var(--ink-rgb) / .04); }
  /* FRAYED INTO THE RASTER, the way the faces panel is. Two things make that look,
     and both are needed: the block is clipped on the half-module lattice with its
     border tiles knocked out (the path is written by frayApply() in assets/site.js
     from Faces.frayPath()), and it is taken OUT of the mosaic's cutout list — see
     :not(.blk--fray) there — so there is tiling behind it to show through the
     holes. Either one alone gives holes onto bare page or tiling behind an opaque
     rectangle. The 4% tint goes: it would sit in the solid tiles as a grey wash
     over the photograph, which is the hard rectangle this is getting rid of. */
  .blk--fray  { background: none; }
  /* Landing on a point brightens its tag rather than colouring it. */
  .poi.is-active .tag { color: var(--ink); }

  /* Reading and label type is clamped to the module — see the --t-* tokens in :root.
     This comment used to read "Type is fixed-px: the whole world plane scales, so it
     must not scale twice", which the line under it now contradicts. */
  .tag { font-size: var(--t-tag); letter-spacing: .22em; text-transform: uppercase; color: var(--grey-1);
         display: flex; align-items: center; gap: 10px; }
  .tag i { width: 8px; height: 8px; background: var(--ink); flex: none; }
  /* One headline size across the whole map. The narrowest headline column is
     6 modules (720px) and its longest line is "More ground truth." — that is
     what sets the ceiling, so every other block can carry the same size without
     a single one overflowing its column. */
  /* A LITTLE MORE LEADING. .94 against a face whose own box is 1.2583 em means the
     lines were set 24px tighter than the glyphs are drawn at 69px — negative leading,
     which is right for a one-line display line and cramped the moment there are two.
     1.02 is the smallest step that reads as air rather than as a rounding.
     MEASURED FIRST, because there is no room to discover this in: a two-line headline
     sits in a 2-module block with 176px of height against 152px of ink, so 24px of
     reserve, split evenly by justify-content: center. The step adds 6.9px per extra
     line, so a two-liner goes to 159px and keeps 17px, and Mission's three-liner goes
     from 211 to 225 inside 264. Nothing had to grow and nothing overflows. */
  h1, h2, .statement {
    font-size: var(--h-size); font-weight: var(--w-reg);
    line-height: 1.02; letter-spacing: -.035em;
  }
  /* NOT CAPITALS, and it was the last one left. There is exactly one h1 in the
     document — Mission's opening headline — so this rule was a one-off dressed as a
     general one, and it made Mission the only point of the six set in capitals while
     agency, operations, intel, careers and contact all run sentence case at the same
     69px. Measured: text-transform none on all five, uppercase on this.
     The tracking is what gives it away as a mistake rather than a choice. h1 inherits
     letter-spacing: -.035em from the rule above, which at 69px is -2.42px — negative
     tracking is what lowercase display type wants and the opposite of what capitals
     need. So it was not just the odd one out, it was capitals set with the settings
     for lowercase: cramped, and the reason the block read as a wall. */
  /* h1 keeps its own, four hundredths under the shared one: it is the largest thing on
     the plane and the only three-line headline, so the same leading that opens up a
     two-liner would drift it apart. The gap between the two values is what it was. */
  h1 { line-height: .98; color: var(--ink); }
  /* THE TAG TRAVELS, THE LOOK DOES NOT. Every route serves this one document, and
     the routed page's own heading carries the h1 (see route_h1() in index.php) —
     which demotes Mission's headline to h2 on every other address and promotes an
     h2 or a statement <p> in its place. These two pin the pixels: hd-1 is the
     mission headline keeping the h1 rule above after losing the tag; hd-2 is a
     promoted heading keeping what it looked like before gaining it (the shared
     rule two up sets the rest; class-styled heads like rec-h out-specify both
     and never notice the swap). */
  .hd-1 { line-height: .98; color: var(--ink); }
  h1.hd-2 { line-height: 1.02; color: inherit; }
  .lede { font-size: var(--t-lede); line-height: 1.45; color: var(--ink); }
  .sub { font-size: var(--t-sub); line-height: 1.55; color: var(--grey-2); }
  .small { font-size: var(--t-small); line-height: 1.6; color: var(--grey-1); letter-spacing: .03em; }
  .neg { font-size: var(--t-neg); line-height: 1.9; color: var(--grey-2); letter-spacing: .02em; }
  .neg span { display: block; }
  .n-big { font-size: clamp(30px, calc(var(--m) * .5227), 72px); font-weight: var(--w-med);
           line-height: .9; font-variant-numeric: tabular-nums; }
  /* .lbl was here: 10px, .2em, uppercase. Nothing has ever carried the class —
     the only other occurrence in the project is a doc comment in inc/content.php. */
  /* The one emphasis mark an author can write (*word*). It used to be the accent;
     it is weight now, because emphasis inside a sentence is not a UI state. */
  .phx { color: var(--ink); font-weight: var(--w-med); }
  /* **bold** from the authoring marks (content_marks). The same weight the accent
     uses — the browser's 700 against a 300 body reads as a different typeface, and
     500 is what every title on this site already means by "bold". The difference
     from .phx: bold keeps its line's own colour, the accent forces ink. Scoped to
     the two content roots so the structural <b>s in the chrome (wordmarks, panel
     hints) keep their own rules. */
  .poi b, .case b { font-weight: var(--w-med); }
  .pre { font-size: 18px; letter-spacing: .06em; display: block; margin-bottom: 10px; }

  /* Content knocks a hole in the square grid: any block carrying real copy paints
     a phantom plate just behind its text, so the module lines stop at the type
     instead of running through it.

     THE PLATE NEVER LEAVES ITS OWN COLUMNS. It used to be `inset: -6px -14px` for
     every one of these, and the 14px was the black edge that runs into the
     photograph next door: on Careers the picture is grid-column 1/6 and the
     headline 6/13, so they share module line 6 — at module 88 that line is at
     x 2376, the plate starts at 2362, and the result is an opaque 14px strip of
     page ground standing on the picture for the full height of the block. Intel
     has the same collision over eight rows, and Operations' three caption blocks
     sit directly under their three photographs with the plate 5px into each one.
     Horizontal bleed is therefore ZERO. Every .blk is placed by grid-column, so
     its vertical edges ARE module lines; flush to them the plate can neither
     reach a sibling nor cut a grid cell in half.

     The vertical bleed stays only where it is doing work, and is now derived from
     the type instead of being a constant. A display block sets `line-height: .9`,
     so the glyphs overshoot their line box and a plate flush to the box would
     leave the grid line crossing an ascender — 7% of the headline size covers it
     at any module, where -6px covered it at 88 and either overdid it or fell
     short everywhere else. A reading block runs 1.45-1.9 line-height and needs
     none at all: those get inset 0, which is also what takes the 5px bar off the
     bottom of the Operations photographs. */
  .blk--bare:has(> h1), .blk--bare:has(> h2), .blk--bare:has(> .statement),
  .blk--bare:has(> .lede), .blk--bare:has(> .sub), .blk--bare:has(> .neg),
  .blk--rule:has(> .lede), .blk--rule:has(> .sub), .blk--rule:has(> .neg),
  .blk--rule:has(> .small), .blk--rule:has(> .roles__row),
  .blk--left:has(> .neg),
  /* The faces callout had a cutout and no plate, so the .world-grid hairlines ran
     straight through the hovered name. Gated exactly like the cutout in
     assets/site.js — the panel is deliberately NOT spared, so an ungated plate
     would stand as a black rectangle over the tiling whenever no face is named. */
  .poi:has(.blk--faces.is-naming) .faces__callout { isolation: isolate; }

  /* reading copy — flush, no bleed on either axis */
  .blk--bare:has(> .lede)::before, .blk--bare:has(> .sub)::before,
  .blk--bare:has(> .neg)::before, .blk--rule:has(> .lede)::before,
  .blk--rule:has(> .sub)::before, .blk--rule:has(> .neg)::before,
  .blk--rule:has(> .small)::before, .blk--rule:has(> .roles__row)::before,
  .blk--left:has(> .neg)::before,
  .poi:has(.blk--faces.is-naming) .faces__callout::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: var(--phantom);
  }
  /* display type — a little air above and below, taken from the type's own size */
  .blk--bare:has(> h1)::before, .blk--bare:has(> h2)::before,
  .blk--bare:has(> .statement)::before {
    content: ""; position: absolute; z-index: -1; background: var(--phantom);
    inset: calc(var(--h-size) * -.07) 0;
  }

  /* ================= THE DEEP LAYER =================
     A deep plane is the same object as the map — same size, same module grid,
     same points of interest at 1:1. So there is almost nothing to style here:
     what follows is the depth ordering, the one type size that differs, and the
     chrome's reaction to leaving the surface. Everything else is inherited,
     which is the test of whether the two layers really are one system. */

  /* Below the surface, literally: the deep plane sits UNDER the mosaic canvas in
     paint order, so the membrane is between you and it — you are looking through
     the plate at something further away. Once you are through, the record has to
     sit above the mosaic like surface content does, or the backdrop would print
     over the copy you came down here to read. Hence the swap on .is-deep, which
     lands at d > 0.5: halfway is where you pass the membrane. */
  /* .world.world--deep, not .world--deep. Every deep plane carries BOTH classes
     (inc/plane-deep.php emits `class="world world--deep …"`), so the one-class form
     tied on specificity with `.world` on the next line and lost on source order —
     the plane computed to z-index 2 from first paint, sat ABOVE the mosaic for the
     whole descent, and the .is-deep swap below set a value that was already in
     force. Everything the paragraph above describes was unreachable. */
  .world.world--deep { z-index: 0; }
  .world        { z-index: 2; }
  .worldmap     { z-index: 1; }
  /* Above the world, so it can be seen at all — and blended, so it reads as
     something happening TO the photograph rather than paint lying on it. The
     mode is written from the CMS; `normal` would be paint, which is why the
     control offers multiply and difference and not an on switch. */
  .worldmap--over { z-index: 3; mix-blend-mode: var(--mos-over, multiply); }
  .is-deep .world.world--deep { z-index: 2; }

  /* The record headline. .rec-h is an h2, so it must beat the shared headline
     rule that follows it in the cascade — hence the .poi--rec qualifier rather
     than a bare class. */
  /* :is(.poi--rec, .case) — a record on a plane and a case in the document are
     the same thing read two ways, and reading size is not poster size in either.
     Left off the case, the bare h2 rule won and the cases were set at 88px: the
     ARX headline came out 428px tall in a 168px block, which is how a headline
     ends up underneath the navigation. */
  :is(.poi--rec, .case) .rec-h {
    font-size: var(--h-rec); font-weight: var(--w-reg);
    line-height: 1.02; letter-spacing: -.02em; text-transform: none; color: var(--ink);
  }
  /* On the unit plane the NAME is the content, so it is set at surface headline size
     rather than at record size. A record headline is smaller than a poster because
     you read it stopped; a name is not read, it is recognised. */
  .poi--rec .rec-name {
    font-size: var(--h-size); font-weight: var(--w-reg);
    line-height: .94; letter-spacing: -.035em; text-transform: none; color: var(--ink);
  }
  .blk--bare:has(> .rec-name) { isolation: isolate; }
  .blk--bare:has(> .rec-name)::before {
    content: ""; position: absolute; inset: calc(var(--h-size) * -.07) 0; z-index: -1;
    background: var(--phantom);
  }

  .poi--rec .blk--num { align-items: flex-end; }
  .tag--num { color: var(--grey-1); font-variant-numeric: tabular-nums; }

  /* The record headline joins the phantom-plate family above. Left out, the
     module lines would run straight through 46px of type — the one place the
     grid is most obviously wrong. */
  .blk--bare:has(> .rec-h) { isolation: isolate; }
  .blk--bare:has(> .rec-h)::before {
    content: ""; position: absolute; inset: calc(var(--h-rec) * -.07) 0; z-index: -1;
    background: var(--phantom);
  }

  /* ---- the dive affordance ----
     The route ALONG a plane and the route THROUGH it are different kinds of move,
     so they must not read as the same control. .next is a 13px text link; this is
     two rows and the full measure at headline size. */
  /* NEGATIVE. The one block on the page that inverts — ink ground, background-colour
     type — because it is the one control that leaves the surface rather than moving
     along it. A hairline rule and a bigger typeface said "important link"; a hole
     punched the other way round says "door".
     It stays monochrome: the inversion is the emphasis, and hover is the only colour
     it takes. */
  /* FIVE MODULES, RIGHT-HAND END, ONE ROW — a third of the measure. It ran the
     full thirteen at poster size, which made the way out of a point the largest
     thing on it, and the label overhung its single row: 69px of type with the
     flex baseline at the bottom put the cap heights outside the block, so
     "DEEP RECON" was cut along the top. Centred now, and the label is a function
     of the module like every other display size, so it cannot outgrow its row. */
  .dive-band {
    /* appearance:none because this is the one button on the page that is FILLED —
       every other one is transparent, so the native widget never showed. Hygiene,
       not a fix: while chasing what looked like the ink colour being ignored here,
       the culprit turned out to be the background TRANSITION below reporting its
       start value forever in a context that never commits a frame. transition:none
       returned rgb(227,227,227) immediately. The CSS was right the whole time. */
    appearance: none; -webkit-appearance: none;
    /* COMPOSED, not a row of three things in a line. Two columns: the words on
       the left over two lines — label above, count under it — and the arrow
       alone on the right, as tall as the block. A single baseline row put a
       46px word, an 11px label and an arrow on one line and read as a caption
       with a mark after it. */
    display: grid; grid-template-columns: 1fr auto; align-items: center;
    column-gap: .6em; row-gap: .15em;
    font: inherit; text-align: left; cursor: pointer; border: 0;
    background: var(--ink); color: var(--phantom);
    padding: 0 calc(var(--m) * .2);
    transition: background .25s var(--ease), padding .25s var(--ease);
  }
  .dive-band__label {
    grid-column: 1; grid-row: 1; align-self: end;
    font-size: calc(var(--m) * .38); font-weight: var(--w-med);
    line-height: .95; letter-spacing: -.03em; text-transform: uppercase;
    white-space: nowrap;
  }
  /* THE COUNT SCALES WITH THE LABEL ABOVE IT. It was a hard 11px in a band whose
     headline is calc(--m * .38) — measured across the module range, "DEEP RECON" grows
     from 32px to 91px while "009 operators" underneath it stayed 11px, so at a large
     module the band reads as a poster with a footnote stuck to it. --t-small is the
     token every other label of this size uses and it lands on the same 11-12px at the
     module this was authored for. */
  .dive-band__count {
    grid-column: 1; grid-row: 2; align-self: start;
    font-size: var(--t-small); letter-spacing: .2em; text-transform: uppercase;
    color: var(--phantom); opacity: .62; white-space: nowrap; overflow: hidden;
  }
  /* Down, not right — the whole point of the control. Its own column, spanning
     both rows, so it reads as the threshold rather than as punctuation. */
  .dive-band__ar {
    grid-column: 2; grid-row: 1 / 3; align-self: center;
    color: var(--phantom);
    font-size: calc(var(--m) * .58); line-height: .8;
    transition: transform .25s var(--ease);
  }
  .dive-band:hover { padding-left: calc(var(--m) * .3); }
  .dive-band:hover { background: var(--phx); }
  .dive-band:hover .dive-band__ar { transform: translateY(.12em); }

  /* ---- the three shown records ----
     A card is a button, so the whole photograph is the target rather than a link
     under it. The meta sits in its own block below, on the same columns: inside
     the button it would be inside the hover scale on the image. */
  /* SQUARE, AND SQUARE ON BOTH SHAPES. The markup gives every card three columns
     and three rows, which on the desk is already 1:1 because a row and a column
     are the same measure there. On a landscape phone they are not — 54 across and
     30 down — so three by three would be a wide letterbox.
     The height is the grid's (three rows, the default stretch), and the WIDTH comes
     off it through aspect-ratio: justify-self:start turns the inline stretch off, so
     the box is as wide as it is tall and its left edge stays on the column line the
     markup put it on. On the desk that computes to exactly three columns and every
     edge is on a grid line; on the phone it is a smaller square hanging from the
     same line. One rule, no breakpoint, and nothing to keep in step. */
  /* 4:3, NOT 1:1 — owner's word, "mehr breitbild damit der platz genutzt wird".
     The square was honest about the grid and quiet about the work: three of them
     used nine of thirteen modules on the point whose whole job is showing what was
     made. Four columns each and a landscape frame fills the width and gives every
     photograph a shape it was probably shot in.
     The height still leads and the width is derived — see the cap below for why —
     so on the desk three rows of 88 make a frame of 264 by 352, which is exactly
     the four columns the markup gives it. */
  .opcard {
    border: 0; padding: 0; cursor: pointer; display: block;
    aspect-ratio: 4 / 3; height: 100%; width: auto;
    justify-self: start; align-self: start;
    /* AND NEVER WIDER THAN THE TRACK IT WAS GIVEN. The height leads and the width
       follows, so wherever a row is TALLER than a column is wide the frame grows
       past its own columns: measured with the square at 844x390, four rows of 41
       made a 164px frame in three columns of 50 and the third card hung 14px over
       the right edge of the point. The cap is the width the frame may reach,
       expressed as a height — four columns minus the quarter-module gutter, times
       three quarters. Because the width is derived it stays 4:3 while being
       capped. */
    max-height: calc((var(--m) * 4 - var(--m) * .25) * 0.75);
  }
  .opcard__meta { gap: 6px; }
  .opcard__name {
    font-size: 19px; font-weight: var(--w-med); line-height: 1.1; letter-spacing: -.01em;
  }
  .opcard:hover + .opcard__meta .opcard__name,
  .opcard__meta:hover .opcard__name { color: var(--phx); }


  /* Nothing is clickable mid-transition. A record link that landed while the
     plane was still travelling would fire against a layout that is about to
     change under it. */
  .is-diving .poi, .is-diving .minimap { pointer-events: none; }

  /* ---- hairlines ----
     A grid cell that is nothing but one rule on one edge. They carry an edge
     that text or media already established on into the empty part of the
     module grid — the right edge of a panel continuing past its last row, a
     column rule running under a headline. No box, no padding, no content.
     They are drawing, not layout: below 1200px the module grid is gone and so
     is any edge for them to continue, so they go with it. */
  .hair { position: relative; pointer-events: none; }
  .hair--t { border-top: 1px solid var(--line); }
  .hair--b { border-bottom: 1px solid var(--line); }
  .hair--l { border-left: 1px solid var(--line); }
  .hair--r { border-right: 1px solid var(--line); }

  .media { position: absolute; inset: 0; overflow: hidden; }
  /* img AND video: a content slot may hold a short silent loop instead of a
     still (see c_media_tag), and it has to be greyed, scaled and hover-lifted
     exactly like a photograph or the two kinds would read as different objects. */
  /* COLOUR, LIGHTLY DESATURATED — see --img-sat in :root for the number and for what
     it cannot fix. Worth repeating here because this is where someone will look: the
     37 case photographs in assets/records are 100.0% grey as FILES, measured pixel by
     pixel, and no original with colour exists in the project. This rule turns colour
     back on wherever there is any; on those it is a no-op. They have to be exported
     again from a colour master before the deep layer stops being monochrome. */
  .media img,
  .media video {
    width: 100%; height: 100%; object-fit: cover; display: block;
    filter: saturate(var(--img-sat)) contrast(1.04) brightness(.86);
    transform: translate3d(0, var(--py, 0px), 0) scale(var(--ms, 1.16));
    transition: filter .5s ease, --ms .7s ease; will-change: transform;
  }
  .blk--img:hover img,
  .blk--img:hover video { filter: none; --ms: 1.22; }

  /* THE WHOLE MAP IS MONOCHROME — owner's word, one commit after the Operations
     cards went first: every photograph and every ambient loop on the surface, the
     loose ground pictures, and the faces panel's canvas. What stays in colour is
     exactly two things: the FILM on Mission (the showreel is the one moving image
     the site leads with, and it was just given its own treatment — the fray), and
     the DEEP pages, which are the record and make their own call.
     THE HOVER STAYS MONOCHROME TOO — the site-wide hover reveals full colour, and
     a picture that popped to colour on touch would un-decide the decision this
     rule is. The lift keeps answering the pointer through brightness and the --ms
     scale; only the colour is spoken for.
     The faces canvas takes plain grayscale without the darkening pair: faces.js
     draws its own tones, and stacking brightness on them would darken the one
     panel twice. */
  .poi .media img, .poi .media video,
  .blk--loose .media img, .blk--loose .media video {
    filter: grayscale(1) contrast(1.04) brightness(.86);
  }
  .poi .blk--img:hover img, .poi .blk--img:hover video,
  .blk--loose:hover img, .blk--loose:hover video {
    filter: grayscale(1) contrast(1.04) brightness(1);
    --ms: 1.22;
  }
  .faces canvas { filter: grayscale(1); }

  /* ================= THE CASES, AS A DOCUMENT =================
     The deep layer for the cases is not a second map. It is a page: eight cases
     of exactly the same length, one after the other, scrolled natively, with the
     project names standing above them as the navigation.

     Two bars of the same raster at the top — the site menu, then this layer's
     own navigation — each one module tall, and the reading area under them. The
     mosaic runs behind all of it and steps around both bars in screen space, the
     same way it steps around the menu on the surface. */
  .world--doc {
    position: fixed; inset: 0; width: auto; height: auto;
    /* --phantom: there is no --bg token, so this said `background: var(--bg)` and
       resolved to nothing — the cases layer had no ground of its own and the map
       showed through the pages. Same slip as the lightbox's. */
    transform: none; background: var(--phantom);
    /* ONE COLUMN, SO THE BAR CAN SET THE TOP OF THE PAGE.
       The bar and the reading area were both `position: absolute` and the page's
       top was written as `calc(var(--nav-h) + var(--doc-navh))` — an arithmetic
       top against a bar whose height is DECIDED BY ITS CONTENT. The project names
       wrap, and how many lines they take is a CMS question: nine short names take
       two lines, seven long ones take four. Measured at 1920, 1645, 1440, 1280 and
       1200 with the live server's names: the strip runs 9px PAST the bar it is in
       and its last line lands on the headline. Same number at every width, because
       the strip and the bar both scale with the module — which is why the owner's
       word was "in JEDEM deep recon zu gequetscht".
       In flow, the bar is as tall as it is and the page starts under it. The
       header's own line is the parent's padding now, so the bar keeps its place
       under the menu without a second element knowing that number. */
    display: flex; flex-direction: column; padding-top: var(--nav-h);
  }
  /* NEVER drop this. `hidden` is only a UA rule of one type selector, so ANY
     class rule with a display here beats it — the plane then stands open on the
     map from the first paint, cases lying over the surface, and every attribute
     check still says hidden="true" because the attribute is set and simply not
     doing anything. That is exactly how it shipped once. */
  .world--doc[hidden] { display: none; }
  /* The minimap and the pin are a map's instruments: they say where you are on a
     plane 8400 wide. There is no plane down here — the scrollbar is the position
     indicator, and two of those disagreeing is worse than one. */
  .is-doc .minimap, .is-doc .pin { display: none; }
  /* The camera never mounts this plane, so nothing else writes its transform —
     the dive writes the scale here and only while it is descending. */
  /* The bar spans the window so its ground is unbroken, but its CONTENT rides
     the same thirteen-module rail the pages are set on — the first project name
     starts exactly above the first column of the article instead of hanging off
     the left edge of the screen. */
  /* AT LEAST TWO MODULES TALL, like the record strip on the map plane — because
     the X is two modules square and this bar was one, so the cross hung out of the
     bar it sits in and the names had no room to run to a second line. The strip on
     Agency takes rows 1–2 for exactly the same reason. "At least", now: see below. */
  .doc-nav {
    /* HALF A MODULE LOWER THAN THE PLANE'S LIFT, and that argument still stands
       even though the mechanism moved to the parent's padding-top. It took the lift
       with everything else and that put its first row of names at y=74, thirty
       pixels under a site menu sitting at 33 — two rows of type of different sizes
       almost touching, with nothing between them to say they belong to different
       layers. Sitting on the header's own line gives it that clearance and leaves
       it on the module lattice, which a smaller nudge would not. */
    /* A MINIMUM, NOT A HEIGHT — and the air under the last line is stated rather
       than left over. Two modules was never an arbitrary number: at the strip's
       own measure (.38 of a module, line-height 1.1) it is exactly .34 of a module
       above two lines of names and .78 below them. Writing those two out keeps a
       two-line strip pixel-identical to what it has always been (the minimum wins,
       1.956 modules against 2) and gives a four-line one the SAME .78 of a module
       under it instead of nine pixels of overlap. The bar grows; the page follows,
       because the page is under it in flow now. */
    position: relative; flex: 0 0 auto;
    min-height: var(--doc-navh); z-index: 4;
  }
  /* Eleven modules of names and two of cross, which is the map plane's split
     (grid-column 1/12 and 12/14) expressed as a flex row. Aligned to the TOP, so
     a second row of names grows downward instead of pushing the first one up. */
  /* No `height: 100%` any more: the bar's height IS this rail's, so a percentage
     of it would be a circular question. */
  .doc-nav__rail {
    width: calc(var(--m-doc) * 13); margin: 0 auto;
    display: flex; align-items: flex-start; gap: 0;
  }
  .doc-nav__rail .rec-nav__list { flex: 1 1 auto; min-width: 0; align-self: flex-start; }
  .doc-nav__rail .rec-x { flex: none; align-self: flex-start; }
  /* .doc-nav__list is gone. It was a second declaration of the record strip —
     18px, its own separator colour, an underline drawn with text-decoration —
     for a list doing exactly what .rec-nav__list does on the map plane. The
     markup now carries .rec-nav__list and the two deep layers name their records
     in one voice: .38 of a module, lowercase, and the accent rule under the one
     you are on. Only the two lines above are specific to this bar, and they are
     layout, not type. */

  /* The X sits in the bar rather than in every case: the way back belongs to the
     layer, and repeating it eight times would have been eight of the same button.
     IT IS THE MAP PLANE'S CROSS, at its full two modules. It was overridden to
     one module here with its key pulled up to match — which is how the same
     component ended up half the size on one deep layer and full size on the
     other. The bar is two modules tall now, so it fits without being shrunk. */
  .doc-nav .rec-x { margin-left: auto; }

  /* The reading area — and it SCROLLS. A case is as long as the project is; it
     is not folded into one screen. Folding it was the mistake: a magazine page
     that has to fit a window stops being a page and becomes a summary. */
  .doc-stage {
    /* Flush under the bar — and that is now a fact of the flow rather than a sum
       this rule has to keep in step with. min-height:0 is what lets a flex item
       scroll at all: without it the item's floor is its content's height and the
       whole page grows instead of the box scrolling. */
    position: relative; flex: 1 1 auto; min-height: 0;
    overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
    /* both-edges: the scrollbar is taken off BOTH sides, so the centred page stays
       centred in the WINDOW rather than in the window minus a scrollbar. Without
       it the article sat 8px to the left of the menu standing above it — exactly
       the drift the module grid exists to prevent. */
    scrollbar-gutter: stable both-edges;
  }

  /* ---- the scrollbar in the cases layer ----
     The one place on the site with a scrollbar, so it is drawn rather than
     inherited: a square accent thumb, no track, no arrows.
     Firefox takes the two standard properties; WebKit needs its pseudo-elements
     and gets the thumb INSET — the channel is wide and the visible thumb is
     narrow, the difference being a transparent border with background-clip:
     padding-box. That is what moves the bar off the window edge without moving
     the page, which a narrower scroll container would have done. */
  /* IT RIDES BESIDE THE PAGE, not on the window's edge. The channel is as wide as
     the slack between the window and the thirteen modules, so its LEFT edge — where
     the visible thumb sits — lands just outside the text instead of 200px away from
     it on a wide screen.
     Clamped at both ends, and the lower clamp is not decoration: the gutter is
     reserved on BOTH sides, so a channel wider than the slack would eat into the
     measure the page needs. At 1200px, the narrowest window that gets this layer at
     all, the slack is 28px and it falls back to a plain narrow bar; at 1600 it sits
     a module and a half off the text. */
  /* THE DEEP LAYER HAS ITS OWN MODULE, and the reason is that it is not in the
     camera's frame: .world--doc is fixed inset:0, so it covers the whole window
     INCLUDING the strip the split panel took — while --m was sized for the frame
     the panel had already narrowed. Measured at 844 x 390: a reading measure of
     13 x 39 = 507px inside an 844px window, 337px of dead margin, and the gutter
     below reserving 281 more. A page that SCROLLS has no nine-row height to
     satisfy, so this module answers to width alone.
     Equal to --m everywhere else by construction, so the desktop and the reflow
     stack render exactly as before; only the split overrides it (below). Owner's
     word: the deep recon pages may be wider. */
  .world--doc {
    --m-doc: var(--m);
    /* The project bar's FLOOR. It was its height, and named once because two rules
       set the same edge: the bar itself and the top of the reading area under it.
       The reading area no longer reads it — it is under the bar in flow — so this
       is now one rule's minimum, and the bar may stand taller when the names wrap.
       Two modules is what it has always been; on the split it is smaller, because
       two modules of a CONTENT-sized module is a third of a phone in landscape and
       a floor that tall would push the page off the screen before it started. */
    --doc-navh: calc(var(--m-doc) * 2);
    /* The gutter must be computed from the SAME module the page is set in, or a
       widened page and a gutter still sized for the narrow one would together
       exceed the window. */
    --sb-w: clamp(14px, (100vw - var(--m-doc) * 13) / 2 - 28px, 220px);
    scrollbar-width: auto; scrollbar-color: var(--phx) transparent;
  }
  /* max(34px, …) is MODULE_FLOOR from site.js, the same floor the map module
     obeys; min(asked, …) keeps the CMS's own number as the ceiling, so this can
     only ever give width back, never take it. */
  body.is-split .world--doc {
    --m-doc: max(34px, min(var(--m-asked, 120px), calc(100vw / 15)));
    --doc-navh: min(calc(var(--m-doc) * 2), 22vh);
  }
  /* The padding follows the page's own module, not the map's — the same min()
     trade .blk itself makes, one scope down. */
  .world--doc .blk { padding: min(20px, calc(var(--m-doc) * .25)); }
  .world--doc .blk--bare { padding: 0; }
  .world--doc ::-webkit-scrollbar,
  .world--doc::-webkit-scrollbar { width: var(--sb-w); background: transparent; }
  .world--doc ::-webkit-scrollbar-track,
  .world--doc::-webkit-scrollbar-track { background: transparent; }
  .world--doc ::-webkit-scrollbar-thumb,
  .world--doc::-webkit-scrollbar-thumb {
    background: var(--phx); border-radius: 0;
    /* Everything but the leftmost 14px is transparent border, so the thumb draws
       at the channel's inner edge — the side facing the page. */
    border-right: calc(var(--sb-w) - 14px) solid transparent; background-clip: padding-box;
  }
  .world--doc ::-webkit-scrollbar-thumb:hover,
  .world--doc::-webkit-scrollbar-thumb:hover { background: var(--ink); background-clip: padding-box; }
  .world--doc ::-webkit-scrollbar-button,
  .world--doc::-webkit-scrollbar-button { display: none; }
  /* 13 modules, centred: the same measure the map is composed on — and the same
     rail the project menu rides — so the cases do not read as a different site. */
  /* position:relative so the module lattice inside it (.world-grid--doc) resolves
     against the reading rail and scrolls with the bands. */
  .doc-body { width: calc(var(--m-doc) * 13); margin: 0 auto; padding-bottom: var(--m-doc);
              position: relative; }

  /* ONE PROJECT, ONE PAGE. Only one is in the document at a time — `hidden` from
     doc.js as well as the class, because a page that is merely invisible still
     has a layout, and the mosaic measures layout: the backdrop would cut holes
     for eight cases nobody can see. */
  .case[hidden] { display: none; }

  /* A case is a run of BANDS, not one fixed grid. Each band is its own thirteen
     columns on the module lattice, and its height comes from the rows its blocks
     span — so a case with two photographs and a case with four are both properly
     set instead of one being padded and the other cropped. */
  .band {
    display: grid; grid-template-columns: repeat(13, var(--m-doc));
    grid-auto-rows: var(--m-doc);
  }
  /* THE LEFT EDGE IS THE MODULE LINE, on the pages as on the map. `.band > .blk`
     is 0,2,0 and used to carry a flat `padding: 20px`, which beat `.blk--rule`'s
     `padding: 20px 0 0` (0,1,0) and pushed every rule block 20px into its column
     while the bare block above it stayed flush — so on a deep page a headline and
     the paragraph under it did NOT share a left edge, although the comment on
     .blk--rule says out loud that they must. That 20px of drift, under fixed type
     in module columns, is most of "völlig durcheinander". Side padding is now the
     same nothing it is on the surface; the air stays where it was earned, above
     and below. */
  .band > .blk { padding: 20px 0; }
  .band > .blk--bare { padding: 0; }
  .band > .blk--img, .band > .blk--panel { padding: 0; }
  /* Air between the bands, on the lattice: one module under a picture, half a
     module under a text band. */
  /* A MODULE AND A HALF UNDER A PICTURE, one under text. It was one module for
     both, which put a photograph and a paragraph the same distance from whatever
     came next — and a picture needs more room than a paragraph to stop being
     part of it. Still on the lattice: .5 steps, so nothing drifts off the grid
     the whole layer is measured against. */
  .band--hero, .band--shot, .band--gal { margin-bottom: calc(var(--m) * 1.5); }
  .band--head, .band--body { margin-bottom: calc(var(--m) * .5); }
  /* TIGHTER ON THE PAGES — owner's word, with the gap between a quote and the
     paragraph under it circled in red: "Absatz kleiner / zwischenraum kleiner".
     The type bands drop to a quarter module BELOW (the pictures keep their
     module and a half — the picture/paragraph distinction stands), and the
     20px the block carries where its rule used to be comes down with it. Deep
     pages only: the map's points are composed against fixed rows and their
     spacing is position, not margin. */
  .case .band--body, .case .band--quote, .case .band--caps {
    margin-bottom: calc(var(--m) * .25);
  }
  /* THE HEAD IS NOT A PARAGRAPH, and it was in that list by collateral. The
     quarter module was measured against a quote and the paragraph under it —
     "Absatz kleiner" — and a quarter module IS the distance between two
     paragraphs. The head band is the page's title: under it comes the hero, or
     the first paragraph of the piece, and 21px of a 62px headline's own height
     reads as the headline having been pushed into the text. Half a module is what
     it had before the tightening and what it earns. */
  .case .band--head { margin-bottom: calc(var(--m) * .5); }
  /* An opening page carries no headline (see .band--head--open below), so it keeps
     the quarter. SCOPED, because `.band--head--open` is (0,1,0) and the rule above
     is (0,2,0): the unscoped version has been losing this argument silently since
     the tightening, and only went unnoticed while both numbers were .25. */
  .case .band--head--open { margin-bottom: calc(var(--m) * .25); }
  /* A HEADING INSIDE THE PAGE HAD NO AIR AT ALL. .band--subhead appears in exactly
     one other place in this sheet — the min-content row list — and in none of the
     margin lists, so it computed to 0 and sat straight on the paragraph it
     introduces. That is the owner's "subheadline, copytext, alles zu nah
     zusammen" in its literal form. A heading belongs to what FOLLOWS it, so the
     air above is the larger of the two. */
  .case .band--subhead {
    margin-top: calc(var(--m) * .5); margin-bottom: calc(var(--m) * .25);
  }
  .case .blk--rule { padding-top: 12px; }

  /* ================= A BAND OF TYPE IS AS TALL AS ITS TYPE =================
     Reported as too much air between a headline and the paragraph under it.
     Measured on one record page at module 72: the head band reserved 288px for
     169px of type, the quote 144 for 57, a body band 216 for 141, the outcome 144
     for 57, the Next row 144 for 34. 478px of nothing on a single page — and the
     photographs had ZERO slack, because a picture fills the box it is given while
     a sentence does not.

     WHY THE FIXED HEIGHTS BOUGHT NOTHING HERE. On the map they are real: a point
     is thirteen by nine modules and a block's row span is its position. A deep
     page is not a point. Its lattice — .world-grid--doc, up at the top of this
     file — is `linear-gradient(to right, …)` at `background-size: var(--m) 100%`:
     VERTICAL LINES ONLY, full height, no horizontal rule anywhere. So nothing on
     these pages aligns to a band's height, and reserving four module-rows for a
     one-line headline was paying the map's rent on a page that scrolls.

     The row counts stay in deep_band_grid() and stay authored. They still set the
     COLUMNS, they are still what the panel draws its schematic from, and they are
     still the true height of a photo band. For type they become nominal: with
     min-content rows a block spanning `1/5` is as tall as what is in it, and it
     may also grow PAST four rows rather than clipping a long headline — which the
     old fixed row could not do.

     THE SPACING IS NOW ENTIRELY THE MARGINS ABOVE, which is where it belongs and
     which is why they were already there. A photograph keeps a module and a half
     under it, type keeps half a module, and the difference between a picture and a
     paragraph is stated once instead of twice.

     PICTURES ARE DELIBERATELY NOT IN THIS LIST. A hero is seven modules because
     seven modules is the shape; object-fit does the rest. */
  .case .band--head, .case .band--subhead, .case .band--quote,
  .case .band--body, .case .band--out, .case .band--caps, .case .band--next {
    grid-auto-rows: min-content;
  }
  /* The capability list on an opening page: a text band like the others, so it
     keeps the same half-module of air under it. */
  .band--caps { margin-bottom: calc(var(--m) * .5); }

  /* ================= THE OFFER, SET AS AN OFFER =================
     The eight capability blocks. Owner's word: "sauberer, copytext größe, mehr
     whitespace." Measured first, because the fault was a ratio rather than a size:

       title 26.4px / copy 18px  =  0.68

     Two voices a third apart is not a title and its lines, it is two titles, one of
     them bold. The copy comes down to the site's own reading size — --t-sub, which
     is 15.5px at this module — and the ratio becomes 0.59: still the largest body
     text on the page, and unmistakably the second voice.

     WHITESPACE IN THE TWO PLACES IT WAS MISSING. The title had margin-bottom 0, so
     its lines started on the next baseline as if they were a fourth line of it;
     .45em puts a step between them. And the rows sat 22px apart — a quarter module,
     which is the air between two paragraphs, not between two subjects. Two thirds
     of a module, 59px, is what the eye needs to read eight blocks as eight.

     LINE-HEIGHT STAYS AT 1.6. The lines are short and they are a list; opening them
     further would make each block a paragraph, and the whole point of the grammar
     (content_caps_grid) is that they are not. */
  /* SCOPED TO .case, AND THAT IS NOT DECORATION. The deep page sets its own
     reading size and its own margins one class deeper than a band can reach —
     `.case .sub` (--t-sub-c), `.case .rec-title { margin: 0 }` and the body band's
     own bottom margin are all (0,2,0) and all come later in this sheet than a rule
     written as `.band--offer .sub`. The first cut of this changed nothing at all
     for exactly that reason: same weight, later wins. Matching their depth is what
     makes these apply. */
  .case .band--offer { margin-bottom: calc(var(--m) * .67); }
  .case .band--offer .rec-title { margin-bottom: .45em; }
  .case .band--offer .sub {
    font-size: var(--t-sub); line-height: 1.6;
  }
  /* .band--head--open lived here, unscoped, and lost to `.case .band--head` — see
     the band margins above, where it is now written at the depth that reaches it
     and beside the rule it has to argue with. */

  /* READING SIZE, on the pages only. The map's points are tight on rows and this
     type is what fills them; the cases scroll, so they can afford to be read
     rather than scanned. */
  .case .lede { font-size: var(--t-lede-c); line-height: 1.4; }
  .case .sub  { font-size: var(--t-sub-c); line-height: 1.6; color: var(--ink); }
  .case .small { font-size: var(--t-small-c); }
  /* The filing line sits beside the project name on one row now, so it is part of
     the same caption and reads in the same ink. In grey it looked like a footnote
     that had drifted up from somewhere else. */
  .case .rec-meta { color: var(--ink); }

  /* The caption lies ON the photograph, in the corner: white on black, the way a
     caption sits in print. Beside the picture it was taking a column the picture
     could have had. */
  .shot-cap {
    position: absolute; left: 0; bottom: 0; z-index: 2; margin: 0;
    max-width: calc(var(--m) * 6);
    display: flex; align-items: baseline; gap: 14px;
    padding: 12px 18px; background: var(--phantom); color: var(--ink);
    font-size: 13px; line-height: 1.45;
  }
  .shot-cap i {
    font-style: normal; color: var(--grey-1); flex: none;
    font-size: 11px; letter-spacing: .18em;
  }

  /* ================= A FILM IN A RECORD PAGE =================
     The slim bar, and slim is the whole specification: play, a line, the running
     time, mute. The launch film's .vp bar carries a buffered track, a fullscreen
     button and a close button, and it earns them by filling the window. This one
     sits in a page you are reading, and every control it does not have is one less
     thing between the reader and the page.

     .clip sits inside .media, so it inherits the frame's clipping, its grey and its
     hover lift. The two kinds of picture must not read as different objects — only
     the bar says which is which. */
  .clip { position: absolute; inset: 0; }
  .clip__v { width: 100%; height: 100%; object-fit: cover; display: block; }

  /* THE ONE CONTROL BEFORE IT RUNS. Centred and large, because on a frame that is
     otherwise a still it has to be the obvious thing to press; the bar at the foot
     is for steering, not for starting. It goes once the film has been started. */
  .clip__go {
    position: absolute; inset: 0; z-index: 2;
    appearance: none; background: none; border: 0; padding: 0; cursor: pointer;
    display: grid; place-items: center;
  }
  .clip.is-live .clip__go { display: none; }
  .clip__go-ic {
    width: 64px; height: 64px; border-radius: 50%;
    background: var(--phantom); border: 1px solid var(--line);
    display: block; position: relative;
    transition: border-color .2s var(--ease), transform .25s var(--ease);
  }
  /* Drawn with a border, like every other mark in this sheet. Nudged right because
     a triangle's optical centre is not the centre of its box. */
  .clip__go-ic::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    transform: translate(-40%, -50%);
    border: 9px solid transparent; border-left: 15px solid var(--ink);
  }
  .clip__go:hover .clip__go-ic { border-color: var(--phx); transform: scale(1.06); }

  /* THE BAR IS NOT THERE UNTIL THE FILM HAS BEEN STARTED. Before that the big mark
     is the control, and a bar would be an instrument for something that has not
     begun. It sits ON the picture rather than under it: the frame is the film's own
     box, and a bar below would have to come out of the band's rows. */
  .clip__bar {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
    display: none; align-items: center; gap: 14px;
    padding: 10px 14px; background: var(--phantom);
    font-size: 12px; color: var(--ink);
  }
  .clip.is-live .clip__bar { display: flex; }

  .clip__btn {
    appearance: none; background: none; border: 0; padding: 0; cursor: pointer;
    width: 18px; height: 18px; flex: none; position: relative;
  }
  .clip__play[data-state="play"]::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    transform: translate(-40%, -50%);
    border: 6px solid transparent; border-left: 10px solid var(--ink);
  }
  /* Two strokes for pause, one box with a border on each side — the device the
     nav's three strokes already use. */
  .clip__play[data-state="pause"]::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 10px; height: 12px;
    border-left: 3px solid var(--ink); border-right: 3px solid var(--ink);
  }
  .clip__mute::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 12px; height: 12px;
    border: 1px solid var(--ink); border-radius: 2px;
  }
  .clip__mute[data-state="off"]::after { background: var(--ink); }
  .clip__play:hover::after { border-left-color: var(--phx); }
  .clip__mute:hover::after { border-color: var(--phx); }

  /* THE LINE. Its hit area is the whole 18px row and not the 3px it draws — a 3px
     target cannot be hit with a finger, and the padding is the difference. */
  .clip__scrub { flex: 1 1 auto; height: 18px; display: flex; align-items: center; cursor: pointer; }
  .clip__track { position: relative; width: 100%; height: 3px; background: var(--line); }
  .clip__played { position: absolute; inset: 0 auto 0 0; width: 0; background: var(--phx); }
  .clip__scrub:focus-visible { outline: 1px solid var(--phx); outline-offset: 3px; }

  .clip__time { flex: none; letter-spacing: .08em; color: var(--grey-1); }
  .clip__time b { font-weight: inherit; color: var(--ink); }

  /* The way on. Set at record-headline size because it is the only thing on the
     band and it ends the page — a 13px link there would have read as a footnote. */
  .band--next { margin-top: calc(var(--m) * .5); }
  .case-next {
    grid-column: 1/14; grid-row: 1/3;
    appearance: none; background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; text-align: left; color: var(--grey-1);
    display: flex; align-items: baseline; gap: .5em;
    border-top: 1px solid var(--line); padding-top: 18px;
    transition: color .2s var(--ease), gap .25s var(--ease);
  }
  .case-next__pre { font-size: 12px; letter-spacing: .22em; text-transform: uppercase; }
  .case-next__name { font-size: var(--h-rec); font-weight: var(--w-reg);
                     letter-spacing: -.02em; color: var(--ink); }
  .case-next__ar { margin-left: auto; font-size: var(--h-rec); color: var(--ink); }
  .case-next:hover { color: var(--ink); gap: 1em; }
  .case-next:hover .case-next__name, .case-next:hover .case-next__ar { color: var(--phx); }
  /* The number, the discipline and the year, under the outcome rather than over
     the picture. The client's name is not here at all — it is in the navigation
     above, and twice in one frame is once too many. */
  .rec-meta { margin-top: auto; color: var(--grey-1); }

  /* ---- the head of a case page ----
     BIG on the pages, and only there. A record on the map plane is read at 46px
     because the camera is stopped and the record is short — reading size, not
     poster size, which is the rule the deep layer is set on. A case page is a
     different object: it opens on a statement and then hands you a column of
     photographs, so its headline is the thing the page is about rather than a
     line inside it. .78 of a module is 69px at the authored module — under the
     map's 88, over the record's 46. */
  /* A TICK SMALLER — owner's word, "die headlines in deep recon können alle einen
     tick kleiner sein." .78 of the module was 69px at the authored module, chosen
     when this page's argument was "under the map's 88, over the record's 46". It
     still is at .70: 62px, which keeps it clearly the largest thing on the page and
     stops it filling three lines of the reading measure before the reader has
     started. Line-height opens from .98 to 1.0 with it — a display face set tight
     needs the tightness less as it comes down. */
  .case .rec-h { font-size: calc(var(--m) * .70); line-height: 1; }
  /* The project's own name, printed on the page under the statement. */
  /* Down with it, and by the same proportion: .30 to .27, 26.4px to 23.8. The
     subhead band is set in this rule too, so the headings INSIDE a page come down
     with the name above them and the three-step ladder — title, name, reading —
     keeps its intervals instead of closing up at the top. */
  .case .rec-title {
    margin: 0; font-size: calc(var(--m) * .27); line-height: 1.12;
    letter-spacing: -.015em; font-weight: var(--w-med); color: var(--ink);
  }
  /* Air under the quote, on the lattice, like the other text bands. */
  .band--quote { margin-bottom: calc(var(--m) * .5); }
  /* THE HEAD IS ONE STACK NOW: headline, name, figures, in one block that packs
     from the top. So the spacing between them is margin rather than grid rows —
     that is the whole point of the change, because a row cannot shrink to fit a
     headline that came out shorter than the tallest one.
     .rec-meta pushes itself to the bottom of its block with margin-top:auto,
     which is right at the foot of a tall record on the map plane and would here
     shove the figures to the bottom of four rows — the hole this was meant to
     close, moved down one line. */
  /* THE STEP BELONGS TO THE HEADLINE, NOT TO THE LINE UNDER IT. .55em measured
     against .rec-title — 23px type — gave 13px of air under a 62px headline: a
     step sized by the smaller of the two things it separates, which is the wrong
     way round and reads as the name having been tucked under the title. .28 of a
     module is 24px at the authored module, four tenths of the headline's own
     height, and it is stated in the unit the whole layer is measured in rather
     than in ems of whichever face happens to sit below. */
  .case .band--head .rec-title { margin-top: calc(var(--m) * .28); }
  .case .band--head .rec-meta  { margin-top: .5em; }

  /* ================= PANELS =================
     Mission and Agency each carry one panel on the same
     columns, so the three points read as a matched set: two of them play the
     film, the third decodes the team. Both fill their block absolutely, the
     way .media does, because the block's height comes from the module grid. */
  .panel { position: absolute; inset: 0; overflow: hidden; background: rgb(var(--ink-rgb) / .04); }

  /* ---- video ---- */
  .vp { position: absolute; inset: 0; background: var(--phantom); }
  .vp video {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; display: block;
    filter: saturate(var(--img-sat)) contrast(1.04) brightness(.78);
    transition: filter .5s ease;
  }
  .vp:hover video { filter: saturate(var(--img-sat)) contrast(1.04) brightness(.9); }

  /* The panel sits on a draggable plane, so everything in it is transparent to
     the pointer except the one control that has to be clickable. */
  .vp__ui { position: absolute; inset: 0; pointer-events: none; }

  /* DEAD CENTRE — owner's word ("play button mittig"), and the reason it sat low
     is gone: the low anchor ducked a headline that was centred over the same box
     when the film was Mission's whole 12 x 6 opening frame. That frame is
     history — the headline has its own block and the film is a 4 x 6 panel with
     nothing over it but its own picture — so the duck was ducking nothing, and
     on a 6-row-tall block it read as a control sliding off the frame.
     Still not corner-anchored: a POI is 13 modules wide and map mode starts at
     1200px, so the panel's left edge is off-screen by up to 180px on a narrow
     window and a corner control would be unreachable there. */
  .vp__open {
    position: absolute; left: 50%; top: 50%; bottom: auto;
    transform: translate(-50%, -50%);
    pointer-events: auto;
    font: inherit; font-weight: var(--w-light); font-size: 11px;
    letter-spacing: .2em; text-transform: uppercase; color: var(--ink);
    background: none; border: 0; padding: 0; cursor: none;
    display: flex; flex-direction: column; align-items: center; gap: 16px;
    text-shadow: 0 1px 12px rgb(0 0 0 / .6);
  }
  .vp__open .txt { white-space: nowrap; }
  .vp__open .txt .vp__open-time { color: var(--grey-1); font-variant-numeric: tabular-nums; margin-left: 8px; }
  /* A square of ink with a triangle punched out of it — the same mark as .tag i,
     at play-button size. Sized in MODULES so it grows with the grid instead of
     staying a 60px relic, and the triangle is a fraction of the square so the two
     never come apart. */
  .vp__open .ic {
    width: calc(var(--m) * .82); height: calc(var(--m) * .82);
    background: var(--ink); flex: none;
    display: grid; place-items: center; transition: transform .3s var(--ease);
  }
  .vp__open .ic::after {
    content: ""; width: 0; height: 0; margin-left: calc(var(--m) * .06);
    border-left: calc(var(--m) * .26) solid var(--phantom);
    border-top: calc(var(--m) * .17) solid transparent;
    border-bottom: calc(var(--m) * .17) solid transparent;
  }
  .vp__open:hover .ic { transform: scale(1.1); }
  .vp.is-open .vp__open { display: none; }

  /* ---- open: the film takes the browser window ----
     Re-parented to <body> on open, because .world carries a transform and a
     transformed ancestor would make position:fixed resolve against it. */
  .vp.is-open {
    position: fixed; inset: 0; z-index: 200; background: #000;
    animation: vp-in .28s var(--ease) both;
  }
  @keyframes vp-in { from { opacity: 0; } to { opacity: 1; } }
  .vp.is-open video { object-fit: contain; filter: none; }
  /* The overlay owns the pointer now, so the ui layer stops being transparent. */
  .vp.is-open .vp__ui { pointer-events: auto; }
  @media (prefers-reduced-motion: reduce) { .vp.is-open { animation: none; } }

  .vp__bar {
    position: absolute; left: 0; right: 0; bottom: 0; display: none;
    pointer-events: auto; padding: 0 clamp(16px, 3vw, 40px) clamp(14px, 2.4vh, 30px);
    background: linear-gradient(to top, rgb(0 0 0 / .82), rgb(0 0 0 / 0));
    opacity: 1; transition: opacity .35s ease;
  }
  .vp.is-open .vp__bar { display: block; }
  .vp.is-open.is-idle .vp__bar { opacity: 0; pointer-events: none; }

  /* A 2px line that thickens under the pointer. The hit area stays 16px tall
     regardless, so seeking never depends on hitting a hairline. */
  .vp__scrub { position: relative; height: 16px; display: flex; align-items: center; cursor: none; }
  .vp__scrub .track {
    position: relative; width: 100%; height: 2px; background: rgb(255 255 255 / .22);
    transition: height .15s ease;
  }
  .vp__scrub:hover .track { height: 4px; }
  .vp__buffered, .vp__played { position: absolute; left: 0; top: 0; height: 100%; width: 0; }
  .vp__buffered { background: rgb(255 255 255 / .3); }
  .vp__played { background: var(--ink); }
  .vp__played::after {
    content: ""; position: absolute; right: -5px; top: 50%; width: 10px; height: 10px;
    margin-top: -5px; background: var(--ink); opacity: 0; transition: opacity .15s ease;
  }
  .vp__scrub:hover .vp__played::after { opacity: 1; }

  .vp__row { display: flex; align-items: center; gap: 20px; margin-top: 12px; }
  .vp__btn {
    font: inherit; background: none; border: 0; padding: 0; cursor: none;
    color: #fff; width: 22px; height: 22px; flex: none;
    display: grid; place-items: center; opacity: .82; transition: opacity .2s ease;
  }
  .vp__btn:hover { opacity: 1; }
  /* Glyphs are drawn from borders and boxes rather than an icon font: two
     shapes, no extra request, and they follow the ink colour. */
  .vp__play[data-state="play"]::before {
    content: ""; width: 0; height: 0; margin-left: 3px;
    border-left: 13px solid currentColor;
    border-top: 8px solid transparent; border-bottom: 8px solid transparent;
  }
  .vp__play[data-state="pause"]::before {
    content: ""; width: 12px; height: 16px;
    border-left: 4px solid currentColor; border-right: 4px solid currentColor;
  }
  .vp__mute::before {
    content: ""; width: 14px; height: 10px; background: currentColor;
    clip-path: polygon(0 30%, 35% 30%, 70% 0, 70% 100%, 35% 70%, 0 70%);
  }
  .vp__mute[data-state="off"]::after {
    content: ""; position: absolute; width: 20px; height: 2px;
    background: currentColor; transform: rotate(-45deg);
  }
  /* Two opposite L-corners. Which two edges of each 7px box are drawn decides
     whether they point outwards (enter fullscreen) or inwards (leave it). */
  .vp__fs::before, .vp__fs::after {
    content: ""; position: absolute; width: 7px; height: 7px;
    border: 0 solid currentColor;
  }
  .vp__fs::before { top: 3px;    left: 3px; }
  .vp__fs::after  { bottom: 3px; right: 3px; }
  .vp__fs[data-state="off"]::before { border-top-width: 2px;    border-left-width: 2px; }
  .vp__fs[data-state="off"]::after  { border-bottom-width: 2px; border-right-width: 2px; }
  .vp__fs[data-state="on"]::before  { border-bottom-width: 2px; border-right-width: 2px; }
  .vp__fs[data-state="on"]::after   { border-top-width: 2px;    border-left-width: 2px; }
  .vp__close::before, .vp__close::after {
    content: ""; position: absolute; width: 17px; height: 2px; background: currentColor;
  }
  .vp__close::before { transform: rotate(45deg); }
  .vp__close::after  { transform: rotate(-45deg); }
  .vp__btn { position: relative; }

  .vp__time {
    font-size: 11px; letter-spacing: .16em; color: rgb(255 255 255 / .72);
    font-variant-numeric: tabular-nums; white-space: nowrap;
  }
  .vp__time b { font-weight: var(--w-light); color: #fff; }
  .vp__spacer { flex: 1 1 auto; }
  .vp__label {
    font-size: 11px; letter-spacing: .2em; text-transform: uppercase;
    color: rgb(255 255 255 / .5); white-space: nowrap;
  }

  /* ---- faces ---- */
  /* No box: the background is the page's own colour, so the panel has no edge.
     FULL STRENGTH. The portraits were held at .78 back when the mosaic was drawn
     across them and had to read as the top layer. The block is frayed now — it
     meets the backdrop at its edge instead of underneath it — so the only thing
     the transparency still did was wash out every face. */
  .faces { position: absolute; inset: 0; overflow: hidden; background: var(--phantom); }

  /* ---- the faces block, frayed into the raster ----
     No panel ground at all: the tiles are the only thing drawn, and faces.js
     thins them out along the border (see FRAY). A filled box would have put a
     hard rectangle back around a block whose whole point is that it has no
     edge — the fraying only reads if the background behind it is the page. */
  .blk--faces { background: none; padding: 0; overflow: visible; }
  .blk--faces .faces { background: none; }
  /* The headline lies ON the faces. It keeps its knockout plate, so the type is
     legible over whatever tile happens to be behind it, and the plate is what
     ties it to the raster rather than floating it over a picture. */
  .faces__over {
    position: absolute; left: 0; top: 0; z-index: 2;
    max-width: calc(var(--m) * 8); margin: 0;
    padding: 0 14px;
  }
  .faces__over::before {
    content: ""; position: absolute; inset: -6px 0; z-index: -1;
    background: var(--phantom);
  }
  /* The canvas must not eat the drag: the effect reads the pointer off the
     window, so it needs no events of its own. */
  .faces canvas { position: absolute; inset: 0; width: 100%; height: 100%;
                  display: block; pointer-events: none; }

  /* THE NAME, ON HOVER AND NOWHERE ELSE.
     It sat in the grid — first as a 64px headline, then as a caption — and both
     times it was a permanent label on a wall of faces, which answers a question
     nobody is asking until they look at someone.

     The hover gate is CSS and the fade is faces.js, and they must not both write
     `opacity` or the last writer wins: the decode's fade arrives as --fade and
     this rule multiplies it by the hover. Bottom left, on its own knockout plate,
     because it now lies on the tiles rather than beside them. */
  /* IT LIES ON A PHOTOGRAPH, so the plate under it is not decoration and the type
     on it is not paper type. Both were wrong: 14px of padding let the picture come
     within a hair of the letters, and the role and the one-liner were set in two
     greys — mid-grey over a face, at the size copy is set on a white page. Solid
     plate, more air, one ink. */
  /* A GRID ITEM IN THE ROW UNDER THE PICTURE, not a plate on top of it.
     It was position:absolute at the panel's bottom left — measured flush with the
     picture's bottom edge, so it could not go lower inside the frame, and it
     needed its own knockout plate to be readable over a face. Down here it stands
     on bare ground and needs nothing.
     Name and role share the first line and the one-liner takes the second, which
     is what makes three pieces of type fit one 88px row: flex-wrap with the copy
     forced onto its own line by flex-basis 100%. Baseline-aligned, so a 30px name
     and a 12px role sit on the same line rather than on their box centres. */
  /* ================= WALK THE UNIT =================
     The slider under the panel. Its row is the point's own — the panel gave up
     row 8 for it — and the track runs the full thirteen columns, because what it
     scrubs is the full width of the wall above it.

     THE HANDLE IS A SQUARE, half a module, in the accent. This site's alphabet is
     squares — the mosaic, the painted mask, the pointer — and a round thumb would
     be the one circle on the page. The track is a hairline of the same rule
     colour every other edge uses, with the travelled part in the accent so the
     control says where you are twice: by position and by length.

     -webkit- AND -moz- BOTH, spelled out separately. A range input's thumb cannot
     be styled through a shared selector list: a browser that does not understand
     one vendor's pseudo-element drops the WHOLE rule, so a combined list styles
     nothing anywhere. Two rules that look duplicated are the only form that
     works. */
  /* The hint sits ON the track's line, at the left, and the slider takes the rest
     of the row under it. A column rather than the label beside the track: the track
     is the full thirteen modules because that is the width of the wall it scrubs,
     and taking two of them for a word would make the control shorter than the thing
     it controls. */
  .faces__walk {
    display: flex; flex-direction: column; justify-content: center;
    align-items: stretch; gap: calc(var(--m) * .08); padding: 0;
  }
  /* WHITE ON BLACK, AND NO PLATE — owner's word: "cut through the noise weiß auf
     schwarz. kein weißer Balken."
     It was accent-coloured type, which read as one more caption in a column of
     them, so I gave it the slider's own colours: a filled block, ink ground,
     background-coloured letters. Seen on the plane that is a white bar sitting on
     the picture — the one thing on a point about faces that is neither a face nor
     the plane. The words carry it instead: the plane's ink, no ground, at the
     medium weight the first attempt earned. Still the strongest label on the point,
     without putting a rectangle over it.
     var(--ink), NOT a literal white: the site has a night scheme and a
     hand-painted palette, and a hard-coded colour would be the one mark on the
     point that does not turn with them. On this install it resolves to white. */
  .faces__walkhint {
    font-size: var(--t-tag); font-weight: var(--w-med);
    letter-spacing: .2em; text-transform: uppercase;
    white-space: nowrap;
    color: var(--ink);
    transition: opacity .4s var(--ease);
  }
  /* Obeyed, so it goes. See the note in index.php. */
  .faces__walk.is-used .faces__walkhint { opacity: 0; }
  .faces__slider {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: calc(var(--m) * .5);
    margin: 0; padding: 0; background: none; border: 0;
    cursor: pointer;
  }
  .faces__slider:focus { outline: 0; }
  .faces__slider:focus-visible { outline: 1px solid var(--phx); outline-offset: 4px; }

  .faces__slider::-webkit-slider-runnable-track {
    height: 1px; border-radius: 0;
    background: linear-gradient(to right,
      var(--phx) 0 var(--walk, 0%), var(--line) var(--walk, 0%) 100%);
  }
  .faces__slider::-moz-range-track {
    height: 1px; border-radius: 0; background: var(--line);
  }
  .faces__slider::-moz-range-progress { height: 1px; background: var(--phx); }

  .faces__slider::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: calc(var(--m) * .5); height: calc(var(--m) * .5);
    /* Half its own height up, so the square is centred ON the line rather than
       hanging under it — a range thumb is laid against the track's top edge. */
    margin-top: calc(var(--m) * -.25);
    border: 0; border-radius: 0;
    background: var(--phx);
    transition: background .18s var(--ease);
  }
  .faces__slider::-moz-range-thumb {
    width: calc(var(--m) * .5); height: calc(var(--m) * .5);
    border: 0; border-radius: 0;
    background: var(--phx);
    transition: background .18s var(--ease);
  }
  .faces__slider:hover::-webkit-slider-thumb,
  .faces__slider:focus-visible::-webkit-slider-thumb { background: var(--ink); }
  .faces__slider:hover::-moz-range-thumb,
  .faces__slider:focus-visible::-moz-range-thumb { background: var(--ink); }

  /* Nothing to walk in the stacked page — the panel is a 16/10 picture on a phone
     and the callout under it is the whole of what that layout ever had. */
  @media (max-width: 699px), (max-width: 1199px) and (orientation: portrait) {
    .faces__walk { display: none; }
  }

  .faces__callout {
    /* flex-flow, not flex-wrap alone: this element also carries .blk, which sets
       flex-direction:column, and a rule that only says "wrap" inherits the column
       — all three lines stacked and the one-liner broke over four lines in an 88px
       row. Both parts of the shorthand, every time, on anything that is a .blk. */
    display: flex; flex-flow: row wrap;
    /* ANCHORED AT THE TOP, and that is the fix for a jump rather than a taste.
       With align-content:center the lines were centred in the row, so a person
       whose one-liner wraps to two lines pushed the NAME up by half a line — the
       name moved as you passed from one face to the next, which reads as the
       layout twitching. Started at the top, the name's baseline is the same for
       everyone and a second line can only grow downward, into the air under it.
       SIX PIXELS OF TOP PADDING, not twelve. Twelve was the air I wanted under
       the picture's edge, and it put the longest one-liner's second line 6px past
       the bottom of its own row — outside the box the mosaic cuts its hole from.
       Measured, no tiling actually reached it, but that is the cutout's padding
       being generous rather than the layout being right. Six fits the worst case
       exactly inside the row. No side padding, so the left edge stays on the
       module line the copy is set to. */
    align-items: baseline; align-content: flex-start; justify-content: flex-start;
    padding: 6px 0 0;
    column-gap: 16px; row-gap: 0;
    opacity: 0; transition: opacity .25s var(--ease);
    pointer-events: none;
  }
  /* THE NAME IS ALWAYS THERE NOW, and taking it away was my doing. It was gated on
     hovering the PANEL, which worked while the only way to change a face was to
     sweep across the panel itself. Then the controls moved OUT of the panel — first
     the keys, now the slider — and hovering them is not hovering the panel, so the
     name never appeared at all. Owner: "die namen sollten schon sichtbar sein.
     warum machst du die weg??" Fair.
     There is no gate any more. --fade still dips while a face dissolves into the
     next, which is the panel telling the caption to get out of the way of its own
     transition, and the :empty rule below still holds it back until the opening
     decode has produced a first name. */
  .faces__callout { opacity: var(--fade, 1); }
  /* Nothing to show before the first face has decoded. */
  .faces__callout:has(> .faces__name:empty) { opacity: 0 !important; }
  /* THE NAME IS THE SECOND-BIGGEST THING ON THIS POINT and it was a hard 30px, so at
     module 240 it sat under a 189px statement without having grown at all. Clamped
     module-relative: 30px at the module this was authored for, and it keeps its
     proportion to the headline instead of shrinking away from it. The role beside it
     takes the label token, which is what every other 12px caption on the page uses. */
  /* BOLD — owner's word. Outfit is a variable face across 100..900 and the site's
     tokens stop at --w-med (500), which is the weight of a caption's title. The
     name is not a caption's title: it is the second-biggest thing on the point and
     the only word that changes as you walk the unit. 700, written here rather than
     as a new token, because one element wants it. */
  .faces__name {
    flex: 0 0 auto; font-size: clamp(24px, calc(var(--m) * .341), 52px);
    font-weight: 700; line-height: 1.05;
    letter-spacing: -.02em; text-transform: uppercase; color: var(--ink);
    white-space: nowrap;    /* the newline in the data belonged to display size */
  }
  .faces__role {
    flex: 0 0 auto; font-size: var(--t-small); font-weight: var(--w-light);
    letter-spacing: .2em; text-transform: uppercase; color: var(--ink);
    white-space: nowrap;
  }
  /* THE ONE-LINER IS SHOWN. It was display:none, with the argument that a caption
     has no room for it and the deep plane would carry it instead — the nine
     operators ARE these faces now, so this is the only place it can be, and it is
     the half of a person that a name and a role do not give you.
     It wraps inside the callout's 6-module max-width; the longest of the nine runs
     to two lines there. */
  /* flex-basis 100% is what puts it on the second line, whatever the name above
     it measures — the alternative was guessing a width for nine different names. */
  .faces__copy {
    flex: 1 0 100%; margin-top: .2em; font-size: 17px; line-height: 1.4;
    letter-spacing: 0; text-transform: none; color: var(--ink);
  }

  .next {
    font: inherit; font-weight: var(--w-light); font-size: 12px;
    letter-spacing: .16em; text-transform: uppercase;
    background: none; border: 0; padding: 0; cursor: none; text-align: left;
    color: var(--grey-1); align-self: flex-start;
    display: inline-flex; align-items: baseline; gap: 10px;
    transition: color .25s var(--ease), gap .25s var(--ease);
  }
  .next em { font-style: normal; color: var(--ink); }
  .next .ar { color: var(--ink); }
  .next:hover { color: var(--ink); gap: 18px; }
  .next:hover em { color: var(--phx); }

  .cta { display: inline-flex; align-items: center; gap: 12px; align-self: flex-start;
         font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
         color: var(--phantom); background: var(--ink); padding: 13px 18px; text-decoration: none; }
  .cta:hover { background: var(--phx); color: #fff; }
  .mail { font-size: 12px; letter-spacing: .06em; color: var(--grey-2); text-decoration: none; }

  /* ---- the address as the button, on Careers ----
     It is a <button> carrying an e-mail address, so two things have to be undone
     that .cta assumes about a link: the uppercase transform, which would print
     BASE@DEFCOM.ONE and make the reader wonder whether the case matters, and the
     browser's own button font. The tracking comes down with the transform — .12em
     was compensating for capitals and on lowercase it spaces the letters out.
     `cursor: copy` says what the press does before it is pressed, which is the one
     thing a button cannot borrow from a link's status bar. */
  .copy__cta {
    text-transform: none; letter-spacing: .02em; font-size: 13px;
    font-family: inherit; border: 0; cursor: copy;
  }
  /* Held for the three seconds the confirmation is up, so the button agrees with the
     line above it rather than snapping back on its own. */
  .copy__cta.is-copied { background: var(--phx); color: #fff; }
  /* The label, and the confirmation in the same place: the answer belongs where the
     question was asked. min-height so the block does not jump by a line when the two
     strings differ in length enough to wrap. */
  .copy__note { margin: 0 0 10px; min-height: 1.4em; }

  /* ---- the open positions on Careers ----
     One block, one hairline, a line per position: the title on the reading edge and
     the place at the far end of the same line. Three .blk--rule rows stacked under
     the lede's own rule put four hairlines in four consecutive rows, which is a
     table. justify-content:space-between rather than a second column, so a long
     title simply takes the room it needs and the place stays right. */
  /* ---- the painted mask editor (assets/paint.js) ----
     Styled once here and used in two places: this drawer and the CMS. */
  .paint { display: block; margin: 6px 0 18px; }
  .paint__cv {
    display: block; width: 100%; height: auto; margin-bottom: 10px;
    border: 1px solid var(--line); background: #0b0b0b;
    cursor: crosshair; touch-action: none;   /* or the stroke pans the page on a tablet */
    image-rendering: pixelated;              /* the cells ARE the resolution; do not smooth them */
  }
  .paint__row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
  .paint__label {
    flex: 0 0 96px; font-size: 11px; letter-spacing: .01em;
    color: var(--grey-1);
  }
  /* THE SEGMENTS NEED THEIR OWN WIDTH IN THIS ROW, and the same fix is in
     assets/admin.css because paint.js is mounted by both pages and the two sheets
     deliberately do not share (see the note at the top of admin.css).
     `.seg button` is flex:1 with no horizontal padding, which works in a grid row
     where the .seg fills a 1fr column. A .paint__row is a FLEX container, so the
     .seg shrinks to its content, flex:1 has nothing to divide, and the labels come
     out glued together — "0LowHalfFull". Scoped to this row rather than put on
     .seg button, where extra padding could push a segment past its grid column. */
  .paint__row .seg { flex: 0 0 auto; }
  .paint__row .seg button { padding: 6px 12px; white-space: nowrap; }
  .paint__tip {
    font-size: 10px; line-height: 1.5; color: var(--grey-1);
    margin: 10px 0 0;
  }

  /* .rec-title ON THE SURFACE — which is the three Mission pillar titles and
     nothing else; every other user sits under .case, whose own override outranks
     this on specificity either way.
     A REAL STEP UP NOW. It was --t-roles, 16px at m=88, over a .sub body of 15.5px
     — a heading half a pixel taller than the paragraph it heads, and with the
     hairline and the 001 gone from the pillars (owner's word; see the markup) the
     size was the only thing left saying "title". --t-lede-c is the existing face
     that fits: 25px at m=88, the reading size of the deep pages, and the longest
     authored title ("Unmistakable", 12 characters) measures well under half the
     three-module block at it. No new token — a heading needs no new face, only a
     place, which is the subhead band's own argument.
     The .3em under it is what the number's line used to contribute: without it the
     grown title sat on its paragraph. */
  .rec-title { font-size: var(--t-lede-c); font-weight: var(--w-med); color: var(--ink);
               line-height: 1.2; letter-spacing: -.01em; margin-bottom: .3em; }

  /* The Mission-only hairline override stood here — the base .blk--rule draws no
     border anywhere now (owner's word, third ask; see the note at the rule), so
     the override joined the .case one in the bin rather than in the cascade. */

  /* A LOOSE PICTURE ON THE GROUND — authored in the panel, absolutely placed on
     the plane like the ten .mark wordmarks, sized in whole modules by its inline
     style. Not inside a .poi, so the grid classes' column arithmetic never sees
     it; padding 0 because .blk's padding is for text and this block is all
     picture. frayApply() and the mosaic's samplers pick it up by class — see the
     notes at frayApply and imgRects in site.js. */
  .blk--loose { position: absolute; padding: 0; }

  .roles { display: flex; flex-direction: column; gap: .85em; }
  .roles__row { display: flex; align-items: baseline; justify-content: space-between;
                gap: 18px; margin: 0; }
  .roles__title { font-size: var(--t-roles); font-weight: var(--w-med); color: var(--ink);
                  letter-spacing: -.01em; }
  .roles__place { font-size: var(--t-small); letter-spacing: .03em; color: var(--grey-1);
                  white-space: nowrap; }

  /* .redact and .censor-armed stood here — the per-LINE marker bars on the Mission
     headline, retracted on a timer. Gone with the entry sequence, which does the
     same thing per WORD and at the visitor's own pace; see .seq__w further down and
     the note where the JS used to be. The headline is printed plainly now. */

  /* ================= CHROME ================= */
  /* ================= THE MENU =================
     At the TOP, and it occupies one ROW of the mosaic raster: exactly one module
     tall, with nothing behind it and NO rule under it — the row the mosaic leaves
     empty is the separation, so a hairline on top of that was the same statement
     made twice.
     Nothing behind it is the point. It used to carry a translucent panel and a
     12px blur, which is what a bar floating OVER a page needs; a row that belongs
     to the raster needs the raster to stop instead. mosSample() gates the band out
     in SCREEN space and the paint clears it, so the tiling genuinely ends at the
     line rather than being hidden under a frosted strip. */
  /* THE FULL WIDTH OF THE WINDOW, not the 13-module rail.
     The padding used to be max(24px, (100% - --content-w)/2), which held the bar's
     contents on the same measure as a point of interest. The reason for that was
     the wordmark: it sat in this bar and had to stand on the same left edge as the
     copy beneath it. The wordmark is set into the plane now, scattered through the
     gaps between the points — so the rail was aligning the menu to something that
     is no longer there, and on a wide window it left the six words huddled in the
     middle with a third of the bar empty on each side.
     A quarter module of gutter, so the menu starts on the left and the grid button
     ends on the right, and both stay on the lattice at any module. */
  /* BASELINE, NOT CENTRE. `align-items: center` centred the wordmark and the menu
     INDEPENDENTLY, and the two are set at different sizes — 26px against 14px at
     module 88 — so they sat on two different lines. Measured with Range rects: the
     wordmark's letters ran to y=60 and the menu's stopped at y=50, and their baselines
     were about 7px apart. Two sizes centred in one row never share a line; only a
     baseline does. Now the bar reads as one line of type with a change of size in it.

     PADDING AS LOGICAL LONGHANDS, and that is the load-bearing part of this rule.
     Baseline alignment puts the group at cross-start, so the line has to be placed
     from the top — and `padding-top` here would have been dead on arrival, because
     `body.is-nav-rail .nav` below sets the `padding` SHORTHAND and wins on
     specificity, resetting all four sides. Splitting inline from block means the rail
     variant can move the left and right edges without touching the vertical
     placement, which is what it was always about. Same for the narrow-window rule.
     .32 of the module lands the wordmark's baseline where centring used to put it, so
     nothing else in the bar moves. */
  /* TOP OF THE STACK, AND NOTHING ELSE. z-index 60 rather than 50, so the bar is above
     the minimap and the overview's brackets — both at 45, and both able to reach into
     the row before this. Still below the three real takeovers: the sequence at 100, an
     opened film at 200, the rotate lid at 300.
     NO BACKGROUND. A ground was tried and it was the wrong reading of "over
     everything": an opaque strip across the top turns the bar into a header, and this
     bar is set INTO the plane.
     IT USED TO BE CUT OUT OF THE TILING, in screen space, so the raster could run to
     the top edge with the menu in it. That cut is gone — a box that stands still over
     a lattice that pans makes the tiling churn underneath it, which is the flicker the
     owner reported at the wordmark and around the Grid switch. See the note where it
     was deleted, in assets/site.js. The raster still runs to the top edge; it now runs
     BEHIND the words rather than around them. */
  .nav {
    position: fixed; inset: 0 0 auto 0; height: var(--nav-h); z-index: 60;
    display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
    /* THE NOTCH IS IN THE INLINE PADDING. viewport-fit=cover hands the page the whole
       screen including the hardware cut-out, and in landscape that cut-out is on the
       left or the right — exactly where the wordmark and the Grid switch sit. max()
       against the module measure so it costs nothing on a screen without one. */
    padding-inline: max(20px, calc(var(--m) * .25), env(safe-area-inset-left))
                    max(20px, calc(var(--m) * .25), env(safe-area-inset-right));
    /* The shared baseline, placed. Baseline alignment puts the group at cross-start,
       so the line is positioned from the top — and in a bar of 44px holding a 26px
       wordmark there is exactly (44 - 26) / 2 = 9px of air, which is .205 of --nav-h.
       Measured the wrong way first: .68 was the fraction that worked when the bar was
       a WHOLE module, and reused against the half it pushed the wordmark to 30..56 —
       twelve pixels out through the bottom of a 44px bar. */
    padding-block: calc(var(--nav-h) * .205) 0;
  }
  /* ---- ON THE RAIL, as an option: navRail, see cfg_defaults() ----
     The bar still spans the window; what moves is where its CONTENTS begin. The
     wordmark then stands directly above the left edge of a landed point, because a
     point is centred by the camera and its edge therefore sits at
     (100vw - 13 modules) / 2 — which is exactly this padding.
     It is the measure the old fixed rail used, restored as a switch rather than as the
     only behaviour: the bar was asked to run the full length of the window, and where
     the words inside it start is a different question from how wide the bar is.
     `100%` resolves against the containing block's inline size, and .nav is fixed with
     inset 0 0 auto 0, so that is the viewport — no --vw variable needed.
     max() is load-bearing at the top of the module range: 13 x 240 is 3120px, wider
     than any window, and without the floor the padding would go negative and push the
     wordmark off the left edge. */
  /* padding-INLINE only. The rail moves where the contents begin, left and right; the
     vertical placement of the shared baseline belongs to .nav and must survive this. */
  body.is-nav-rail .nav {
    padding-inline: max(20px, calc((100% - var(--content-w)) / 2), env(safe-area-inset-left))
                    max(20px, calc((100% - var(--content-w)) / 2), env(safe-area-inset-right));
  }
  /* NOT IN THE SPLIT. The rail centres the header on the assumption that the
     content is centred in the WINDOW — and it is not, once the panel takes the
     left of it: the camera frame starts at --split-w, so the arithmetic put the
     wordmark at 169px while the copy below it began at 278px and the panel ended
     at 219px. Three left edges, none of them shared. Measured at 844 x 390.
     In the split the header goes back to its plain inset, which is the panel's
     own: the wordmark left, the Grid switch right, the same 20px off each edge.
     Owner's word — the menu belongs further left, and evenly. */
  body.is-split.is-nav-rail .nav {
    padding-inline: max(20px, env(safe-area-inset-left)) max(20px, env(safe-area-inset-right));
  }
  /* Together already packs from the left edge, so there is nothing more to say there.
     Across spreads between the two rail edges instead of the two window edges — the
     menu's own clamp() gutter would only inset it a second time. */
  body.is-nav-rail.is-nav-spread .nav__menu { padding: 0; }
  /* ---- the record strip ----
     Every record of the plane, above the headline, as its own number. It is the way
     from project to project without going back up: the deep plane's own route.
     Built like the header menu — buttons with a separator between them, and the one
     you are on carries the UNDERLINE. That underline is one of the three places on
     the page still allowed the accent, because it is the only mark that answers
     "where am I" rather than decorating. */
  /* The strip is set as TYPE, not as a tab bar: the records are the content of this
     plane, so their names are the largest thing on it after the record's own
     headline. Lowercase and light, with the one you are on in medium — weight
     carries "where am I", and the underline confirms it.
     It takes two rows and wraps, because nine real names do not fit on one at this
     size. Generic numbers did fit on one row, which is exactly why they were the
     wrong answer. */
  /* THE STRIP SITS THE SAME DISTANCE FROM THE TOP OF ITS TWO MODULES ON BOTH
     DEEP PLANES — a fixed inset, not centred.
     The map's strip is a .blk, so it was centred in its block; the doc bar's was
     flush to the top. Matching them by centring the doc one too looks right for
     exactly as long as both lists wrap to the same number of rows, and they do
     not: nine people's names take three rows, eight project names take two, so
     centring seated the first line 30px down on Agency and 49px down on
     Operations — the same box, the same type, nineteen pixels apart, with the
     cross fixed at the top of both. Hence a fixed inset: it does not care how
     many rows the roster wraps to.
     .34 of a module is where the centred three-row version already sat, so the
     map plane does not move. */
  .rec-nav { justify-content: flex-start; padding-top: calc(var(--m) * .34); }
  /* THE AIR UNDER THE STRIP IS THE STRIP'S, and that is why it is written here
     rather than as padding on the bar. The bar's height is the taller of two
     things: the cross, which is two modules square and fixed, and the names with
     their insets. Air added to the BAR is added under whichever of the two won —
     so a two-line strip, whose height the cross already covers, grew by .78 of a
     module for nothing (measured: 172 -> 239). Air added to the LIST is inside
     that comparison, and the two-line bar comes out at 1.956 modules against a
     two-module minimum: unchanged, to the pixel.
     .34 above and .78 below is the two-module bar taken apart. It was never an
     arbitrary number — at the strip's own measure it is exactly those two insets
     around two lines of names. A four-line strip now gets the SAME .78 under it
     instead of running nine pixels past the bar and onto the headline. */
  .doc-nav__rail > .rec-nav__list {
    margin-top: calc(var(--m-doc) * .34); margin-bottom: calc(var(--m-doc) * .78);
  }
  /* A phone in landscape cannot spend .78 of a module under a strip that may
     already be taking four lines of a 390px screen. It keeps the top inset's .34
     and no more — the bar still grows honestly, it just stops paying for elegance
     it has no room for. */
  body.is-split .doc-nav__rail > .rec-nav__list { margin-bottom: calc(var(--m-doc) * .34); }

  .rec-nav__list {
    display: flex; align-items: baseline; flex-wrap: wrap;
    font-size: calc(var(--m) * .38); line-height: 1.1;
    letter-spacing: -.02em; text-transform: lowercase;
    font-weight: var(--w-light); color: var(--grey-1);
  }
  .rec-nav__list button {
    font: inherit; letter-spacing: inherit; text-transform: inherit;
    background: none; border: 0; border-bottom: 2px solid transparent;
    padding: 0; cursor: pointer; color: inherit; white-space: nowrap;
    transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .rec-nav__list button:hover { color: var(--phx); }
  .rec-nav__list button.is-here {
    color: var(--ink); font-weight: var(--w-med); border-bottom-color: var(--phx);
  }
  .rec-nav__list i { font-style: normal; margin: 0 .34em; color: var(--line); }
  .rec-nav__list i:last-child { display: none; }

  /* ---- the way back, ON the record ----
     One module square in the top right corner of every record, drawn as two
     hairlines rather than set as a ✕ glyph: the cross then lands on the module
     grid at exactly the weight the rest of the drawing uses, and it scales with
     the module instead of with a font size.
     It sits in the LAYOUT, not in the chrome. The trade is real — a deep plane can
     be panned, so between two records there is no X on screen — but the camera is
     magnetic and lands on records, and Esc works from anywhere. */
  .rec-x {
    position: relative; background: none; border: 0; padding: 0; cursor: pointer;
    justify-self: end; align-self: start;
    /* Two modules, and the strokes run nearly the whole width of them. One module
       with the strokes inset to 22% read as a small mark in a big empty cell. */
    width: calc(var(--m) * 2); height: calc(var(--m) * 2);
  }
  .rec-x::before, .rec-x::after {
    content: ""; position: absolute; left: 12%; right: 12%; top: 50%;
    border-top: 1px solid var(--ink);
    transition: border-color .2s var(--ease), transform .3s var(--ease);
  }
  .rec-x::before { transform: rotate(45deg); }
  .rec-x::after  { transform: rotate(-45deg); }
  .rec-x:hover::before { border-color: var(--phx); transform: rotate(135deg); }
  .rec-x:hover::after  { border-color: var(--phx); transform: rotate(45deg); }
  /* The key, named once beside the cross rather than on every record. */
  .rec-x__key {
    position: absolute; top: calc(var(--m) * 1.52); left: 50%; transform: translateX(-50%);
    font-size: 9px; letter-spacing: .18em; text-transform: uppercase;
    color: var(--grey-1); white-space: nowrap; pointer-events: none;
  }

  /* THE MASTHEAD. It is a <button> that goes to Mission, so it needs the button
     reset — without it the UA sheet gives it a grey background, a border and its
     own font, and the wordmark stops being type. */
  .nav__brand {
    font: inherit; font-weight: var(--w-med); font-size: 26px; line-height: 1;
    letter-spacing: -.035em; background: none; border: 0; padding: 0;
    color: var(--ink); text-decoration: none; white-space: nowrap;
    cursor: none; flex: 0 0 auto;
    transition: color .3s var(--ease);
  }
  .nav__brand:hover { color: var(--phx); }
  .nav__brand b { font-weight: var(--w-light); color: inherit; margin-left: .32em; }

  /* .nav__right was the wrapper that held the menu and the grid switch together
     while the bar had nothing on its left. The wordmark is back in that corner, so
     the bar's own space-between does the work and the wrapper is gone.

     TWO WIDTHS, and which one is a CMS switch — navSpread, see cfg_defaults().
     Either way each word and its comma are one .nav__item. That grouping is what
     makes the spread possible at all: with the buttons and the commas as flat
     siblings, space-between spreads the COMMAS too — six of them stranded in the
     middle of six gaps.

     TOGETHER MEANS TOGETHER WITH THE WORDMARK. The base rule used to carry
     `flex: 1` and `padding-left: clamp(24px, 4vw, 72px)`: the menu box swallowed
     every spare pixel of the bar and then inset its own content, so the wordmark
     sat alone in the left corner with 40-88px of nothing between it and the first
     word. Both declarations have moved into the spread branch, which is the only
     mode that needs them — `flex: 1` is what gives space-between a measure to
     spread over. Here the menu is only as wide as its words, so brand + six words
     pack as one group and read as one line. The right end is unaffected:
     #adminToggle carries `margin-left: auto`, and an auto margin resolves before
     justify-content, so nothing is left for space-between to distribute. */
  /* BIGGER, AND THAT IS WHAT "STRETCH" COSTS. It was .159 of the module — 14px at 88,
     against a 26px wordmark, a ratio of 1.86 that read as a caption next to a
     masthead. .205 gives 18px and a ratio of 1.44: still clearly the smaller of the
     two, but the same voice. The row grows with it, which is the stretch — the six
     words and their gaps take more of the bar without the gaps being widened, so
     Together stays packed against the wordmark.
     Clamped at both ends like every other module-bound size here: 13px is the floor
     where the words are still words on a narrow window, 22px the ceiling before the
     menu starts competing with the copy on the plane at module 240. */
  /* THE GAP IS THE SEPARATOR NOW. It was 18px with a comma after every word; the
     commas are gone, so the space has to do their work and it needs to be big enough
     to be read as one. .34 of the module is 30px at 88 — a comma plus its old 18px
     came to about the same optical distance, which is why this is not "more air" so
     much as the same air with nothing in it.
     Module-bound, not a pixel: at module 240 an 18px gap between 22px words would
     close up, which is the failure the whole --m discipline exists to prevent. */
  /* position: relative — .nav__ink is placed against this box. */
  .nav__menu { position: relative;
               display: flex; flex: 0 1 auto; align-items: baseline; flex-wrap: nowrap;
               justify-content: flex-start; gap: calc(var(--m) * .09) calc(var(--m) * .34);
               font-size: clamp(13px, calc(var(--m) * .205), 22px);
               /* NOT CAPITALS ANY MORE. The six words were 14px/.14em uppercase,
                  the one large all-caps run left on the surface — standing next to
                  a wordmark that is deliberately lowercase and set with no
                  transform at all. The tracking went with the transform: .14em is
                  what capitals need to breathe and what lowercase reads as gappy.
                  The commas do the separating now, which is what they are for. */
               letter-spacing: .015em; }
  .nav__item { display: inline-flex; align-items: baseline; }

  /* THE WORDMARK STOOD CLOSER TO THE MENU THAN THE MENU STOOD TO ITSELF. Measured: 16px
     from "one" to "Mission", against 30px between every pair of words after it. So the
     masthead read as the seventh item of the list, and the tightest gap in the row was
     the one that separates two different things.
     .75 of a module is 66px at 88 — comfortably more than the 30px inside the list, so
     the hierarchy is the other way round now: wide gap between name and menu, even gaps
     inside the menu. A margin on the brand rather than a bigger gap on .nav, because
     .nav's gap also spaces the right-hand pair and has no business growing with this. */
  .nav__brand { margin-right: calc(var(--m) * .75); }

  /* ACROSS. The trailing comma goes with it: packed after "Contact" it is the last
     mark of a list, spread it sits alone against the grid switch and reads as a
     typo. It is a separator and there is nothing left to separate. */
  body.is-nav-spread .nav__menu {
    flex: 1; flex-wrap: wrap;
    justify-content: space-between;
    padding: 0 clamp(18px, 3vw, 56px);
  }
  .nav__menu button { font: inherit; letter-spacing: inherit; text-transform: inherit;
    background: none; border: 0; color: var(--ink); cursor: none; padding: 0 0 4px;
    border-bottom: 2px solid transparent; transition: border-color .3s var(--ease), color .3s var(--ease); }
  .nav__menu button:hover { color: var(--phx); }
  /* The point you are on is BOLD as well as underlined: weight carries "where am
     I" at a glance, the rule confirms it — the same pairing the project menu in
     the cases layer uses. */
  /* THE BORDER IS GONE FROM THE BUTTON and lives in .nav__ink, which slides. The
     transparent 2px stays on every button: it is what reserves the rule's height so
     the row does not jump when the mark arrives. Weight still carries "where am I"
     on its own — the bar is the second signal, and it is the one that says you are
     MOVING, which six borders switching on and off cannot say. */
  .nav__menu button.is-active { font-weight: var(--w-med); }
  /* ---- THE RULE THAT TRAVELS ----
     Placed from site.js against the active button's own box, so it is exactly as
     wide as the word it sits under and needs no guess about tracking or weight.
     Width AND transform are transitioned: a bar that slides but jumps its length
     reads as two marks rather than one moving.
     Zero width until it has been placed once, and out wherever the menu is not a
     row of six words — the compass draws its own answer and a deep page is not on
     the map. */
  .nav__ink {
    position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
    background: var(--phx); pointer-events: none;
    transform: translate3d(0, 0, 0);
    transition: transform .34s var(--ease), width .34s var(--ease), opacity .2s var(--ease);
  }
  body.is-compass .nav__ink, body.is-doc .nav__ink { opacity: 0; }
  /* THE ROW MUST NOT MOVE. Medium is wider than light, so the active item alone
     would grow and shove every item after it sideways — and the wheel steps
     through these six, so it would shove on every flick. Each button therefore
     reserves its own BOLD width up front: a hidden copy of the label at the
     heavier weight sets the column, and the visible text is laid over it. */
  .nav__menu button { display: inline-grid; }
  .nav__menu button::before {
    content: attr(data-label); grid-area: 1 / 1;
    font-weight: var(--w-med); visibility: hidden;
  }
  .nav__menu button span { grid-area: 1 / 1; }
  /* THE COMMA RULES ARE GONE WITH THE COMMAS. Three of them lived here — one to
     un-italicise the <i>, one to hide the trailing one, and a :last-child fix for a
     bug where all six were hidden at once. None of them has anything left to match:
     index.php no longer emits the element. Kept as a note rather than as code,
     because dead selectors are how a stylesheet stops describing its own page. */

  /* ============ THE MENU AS A COMPASS ============
     An option — Menu · Compass in the Grid panel, off by default. The six items
     leave the bar and ride the inside edge of the window, each one held on the
     BEARING of its own point with a dart aimed at it. Pan the plane and they
     travel around the frame; a point you have left slides toward the edge it went
     out by. It is the map's own rose, with places where a compass has directions.

     They are the SAME buttons. position:fixed lifts them out of the row without
     moving them in the DOM, so is-active, the wheel route, the cursor's link
     targets and every listener that already knows #navMenu keep working — this is
     a placement, not a second menu.

     The camera writes --cx / --cy / --ca and the transform is composed HERE, in
     one rule, rather than as an inline transform string: it keeps the compass off
     the layout path, and it leaves the stylesheet able to overrule the placement
     when a record opens (see is-doc below), which an inline transform would win
     against. */
  body.is-compass .nav__menu { display: contents; }
  body.is-compass .nav__menu button {
    position: fixed; left: 0; top: 0; will-change: transform;
    transform: translate3d(var(--cx, 0px), var(--cy, 0px), 0) translate(-50%, -50%);
    display: inline-grid; grid-template-columns: auto auto;
    align-items: center; column-gap: .55em;
    isolation: isolate;
    /* BIGGER THAN IN THE BAR, and it has to be. In the row the six words stand
       together and are read as one list, where 14px is plenty. On the ring each
       one stands alone against the plate, the photographs, and whatever else the
       camera has put under it — at bar size they read as a caption on the picture
       behind them rather than as the chrome of the map.
       Module-derived like every other display size on the page, with both ends
       pinned: a CMS module of 40 or of 240 must not turn the menu into a whisper
       or into a poster. */
    font-size: clamp(15px, calc(var(--m) * .2), 22px);
    letter-spacing: .12em;
  }
  /* The word keeps the second column, the dart takes the first — so the width
     reservation from the bar (button::before, a hidden bold copy of the label)
     still sets the box. Without it an item would change width the moment it went
     active, and the wheel steps through these six: every step would shove the
     mark it just landed on sideways along the edge. */
  body.is-compass .nav__menu button::before,
  body.is-compass .nav__menu button > span { grid-area: 1 / 2; }
  /* The dart is the COMPASS's, and it is built once: switching the option off
     leaves six of them in the DOM, so the bar has to say what they are there.
     Not drawn, and sized — an inline <svg> with a viewBox and no dimensions falls
     back to 300 x 150, which would have thrown the row apart the moment anyone
     switched back to Bar. */
  .nav__menu .ar { display: none; width: 9px; height: 9px; overflow: visible; }
  body.is-compass .nav__menu .ar {
    display: block; grid-area: 1 / 1;
    /* WITH THE WORD, AND MEASURED IN IT. It was a hard 12px — the one size in this
       whole block that did not answer the module, while its own label already did
       (clamp above): at module 88 that made the dart 0.68em of the type beside it,
       and at 240 it stayed 12px against a 22px word. In em it inherits the label's
       scaling AND both of the label's pins for nothing, and it can never drift out
       of proportion with the word it belongs to.
       1.15em is 20px at the live module against 12px before. */
    width: 1.15em; height: 1.15em;
    transform: rotate(var(--ca, 0deg));
    transition: opacity .25s var(--ease), color .25s var(--ease);
  }
  body.is-compass .nav__menu .ar path { fill: currentColor; }
  /* OUTBOARD. Held against the right edge the dart moves to the far side of the
     word, so it points away from the type instead of across it. */
  body.is-compass .nav__menu button.is-flip::before,
  body.is-compass .nav__menu button.is-flip > span { grid-area: 1 / 1; }
  body.is-compass .nav__menu button.is-flip .ar { grid-area: 1 / 2; }
  /* STANDING ON THE POINT — the item goes red. That is the state the request names
     ("wenn ich da bin"), and .is-here is the class that means it: within
     COMPASS_HERE = 40px of the ring centre, as against .is-active, which is merely
     the nearest of the six however far away it is.
     Scoped :not(.is-doc) because is-compass survives a dive — doc.js only adds and
     removes is-doc — and is-here is cleared only when the compass option itself
     goes off. Unscoped, one word in the parked bar row could sit red over a frozen
     camera.
     The dart no longer goes quiet, it turns with the word: there is no bearing to
     report standing on the point, but a dimmed red dart is neither a bearing nor a
     mark. `fill: currentColor` above is what makes this one declaration. */
  body.is-compass:not(.is-doc) .nav__menu button.is-here { color: var(--phx); }
  body.is-compass:not(.is-doc) .nav__menu button.is-here .ar { opacity: .5; }
  /* The one plate in the chrome. The bar needs none: the tiling is gated out of
     that band in screen space and genuinely stops at the line. These six stand
     anywhere the camera puts them, photographs included, so they carry the same
     phantom the record headlines do. ::before is taken by the width reservation,
     hence ::after. */
  body.is-compass .nav__menu button::after {
    content: ""; position: absolute; inset: -6px -11px; z-index: -1;
    background: var(--phantom);
  }
  /* THE COMPASS PARKS WHILE A RECORD IS OPEN. In the document there is no plane
     to bear on and the camera is frozen, so the six would hang at whatever
     bearing they had when the dive started — six darts pointing at a map that is
     no longer under them. They go back to being a row in the bar instead, which
     is also the way back out that the bar has always been. */
  body.is-compass.is-doc .nav__menu { display: flex; }
  body.is-compass.is-doc .nav__menu button {
    position: static; transform: none; grid-template-columns: auto; column-gap: 0;
  }
  body.is-compass.is-doc .nav__menu button::before,
  body.is-compass.is-doc .nav__menu button > span { grid-area: 1 / 1; }
  body.is-compass.is-doc .nav__menu button::after,
  body.is-compass.is-doc .nav__menu .ar { display: none; }
  /* One instrument at a time: the minimap answers exactly the question the
     compass is now answering, and it answers it in the corner the compass has to
     travel through. Switch the menu back to Bar and it returns. */
  body.is-compass .minimap { display: none; }

  /* IT TAKES THE ROOM WHEN THERE IS ROOM — owner's word, "wenn der platz reicht,
     gerne etwas größer und höher." It was a flat 230px on every screen from a
     laptop to a 4K panel, which is a thumbnail on one and a postage stamp on the
     other.
     17vw between 230 and 270, and the number came down from 24vw/360 after seeing
     it: "jetzt ist es viel zu groß. es darf nichts überlagern in einer normalen
     fenstergröße." At 24vw a 1440 window got 346 by 222, which reaches 222px into
     the point's own right-hand columns — the preview stopped being chrome in the
     corner and became a panel over the reading.
     The floor stays at the original 230, so nothing is smaller than it ever was;
     the ceiling is 270. That is 230 at 1280 — exactly what it always was there —
     245 at 1440 and 270 at 1920, where there is room to spare.
     THE HEIGHT IS NOT FREE. 5400/8400 is the world's own proportion and the six
     points are pinned on this box in PER CENT of it (see mmPlace) — a taller box at
     the same width would move every mark away from the place it is describing. So
     "höher" is bought by "größer": at 360 wide the box is 231 tall against 148,
     which is fifty per cent more height than it had. */
  .minimap {
    /* THE STRIP UNDER THE MAP IS PART OF THE OBJECT, so the object stands off the
       screen edge by its height plus the same 18px the box had. Stated as a
       variable and used in both places, because the two are one measurement: the
       strip hangs at top:100% and anything but the exact height either opens a gap
       or pushes the Imprint off the bottom of the window. Verified against the
       rendered strip rather than guessed — see the note on .mm__foot.
       MINUS ONE, because the strip hangs at top:100% and its own top border sits ON
       the box's bottom hairline — the same overlap the bar takes on the other edge.
       So the strip adds 31 of visible height, not 32, and without the term the whole
       instrument stood a pixel further off the screen edge than it did before the
       strip existed (measured: 19px of clearance against the 18 that were asked
       for). */
    --mm-foot: 32px;
    position: fixed; right: 24px; z-index: 40;
    bottom: calc(18px + var(--mm-foot) - 1px);
    width: clamp(230px, 17vw, 270px);
    height: calc(clamp(230px, 17vw, 270px) * 5400 / 8400);
    /* MORE CONTRAST — owner's word. It was a .16 hairline on a .72 ground, which
       over a dark plane is an instrument you have to look for. The ground goes to
       .90, so the map behind it stops showing through its own preview, and the edge
       is drawn in the ink at .34 rather than in --line: --line is the rule this site
       draws INSIDE its layout, and this box is not in the layout — it sits over it,
       and an object that floats needs an edge that says so. */
    border: 1px solid rgb(var(--ink-rgb) / .34);
    background: rgb(var(--bg-rgb) / .90); overflow: visible;
    /* A TOUCH BROWSER MUST NOT OFFER ANYTHING ELSE HERE. .viewport carries
       touch-action: none and the minimap is fixed OUTSIDE it, so it was the one
       interactive surface on the page still on the browser's own terms: a press raised
       the selection callout, a double tap zoomed, and every tap left a highlight box.
       That is why the map preview was the only thing on a phone that did not simply
       answer — reported as "a bit of browser chrome appears, all the other links work".
       manipulation keeps pan and pinch and drops the double-tap delay with it. */
    touch-action: manipulation;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    user-select: none; -webkit-user-select: none;
  }
  /* The points are <button>s now (see the note in site.js), so the button's own
     chrome has to go — a browser default border and background would draw a box
     around every mark on the plane. */
  .minimap .pt {
    position: absolute; transform: translate(-50%,-50%);
    padding: 0; border: 0; background: none; font: inherit; color: inherit;
    cursor: pointer; -webkit-tap-highlight-color: transparent;
  }
  .minimap .pt:focus-visible { outline: 1px solid var(--phx); outline-offset: 3px; }
  /* The marks and their words come up with the box. At .72 over a busy plane the
     six of them read as smudges; the point of a preview is that you can tell at a
     glance where you are, and that is the one thing it has to do well. */
  .minimap .pt .d { width: 6px; height: 6px; background: rgb(var(--ink-rgb) / .72); }
  .minimap .pt .t { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
    font-size: 8px; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-1); white-space: nowrap; }
  .minimap .pt.flip .t { left: auto; right: 10px; }
  .minimap .pt.is-active .d { background: var(--ink); width: 9px; height: 9px; margin: -1.5px; }
  .minimap .pt.is-active .t { color: var(--ink); }
  .minimap .rect { position: absolute; border: 1px solid var(--ink); pointer-events: none; }

  /* ================= THE CONTROL PANEL ON THE MINIMAP =================
     The map preview reads as an instrument now, not a thumbnail: a panel on its top
     edge, named DEFCOM CONTROL, with the two view switches side by side under the
     name. INSIDE #minimap, so compass mode, the reading layer, the reflow stack and
     the split all carry it with the one selector they already have.
     -1px left and right, so the bar's outer edges sit ON the box's own 1px border
     rather than inside it; border-bottom 0, because the box's top hairline is the
     junction and two hairlines there read as a seam. */
  /* The bar holds two bordered buttons now rather than three flush cells, so it
     spaces and pads them instead of letting them butt against its own edges. Its
     corners follow theirs — the top two, because it sits on the map's own box. */
  .mm__bar {
    position: absolute; left: -1px; right: -1px; bottom: 100%;
    /* A COLUMN NOW: the tag line, then the row of switches — owner's word, "defcom
       control darüber. buttons zusammen". The bar hangs off the box's top edge at
       bottom:100%, so the extra row grows UPWARD and costs the map none of its own
       height and the foot none of its clearance. */
    display: flex; flex-direction: column; align-items: stretch;
    gap: 5px; padding: 5px 6px 6px;
    /* The bar is the same object as the box under it, so it takes the same edge
       and the same ground — see the note there on why this is the ink and not
       --line. */
    border: 1px solid rgb(var(--ink-rgb) / .34); border-bottom: 0;
    border-radius: 4px 4px 0 0;
    background: rgb(var(--bg-rgb) / .90);
  }
  /* THE CORNER TICKS — the "bisschen mehr scifi" that costs nothing. Two 7px
     brackets on the panel's top corners, the same stroke the whole-map control
     already draws with its four <i>s, so the motif is quoted rather than invented.
     In the ink at .5, not the accent: on this site the accent means "on" or
     "here", and a frame that is always lit would spend that word on decoration.
     -1px, so they sit ON the border rather than beside it. */
  .mm__bar::before, .mm__bar::after {
    content: ''; position: absolute; top: -1px; width: 7px; height: 7px;
    border-top: 1px solid rgb(var(--ink-rgb) / .5); pointer-events: none;
  }
  .mm__bar::before { left: -1px; border-left: 1px solid rgb(var(--ink-rgb) / .5); }
  .mm__bar::after { right: -1px; border-right: 1px solid rgb(var(--ink-rgb) / .5); }
  /* THE TAG LINE. The instrument's smallest voice: a lamp, the name, and a hairline
     running out to the right edge — the group closes where the line ends. The lamp
     is the accent at 5px, quoting the schematic's own marks (.mm-mini__d is the
     same square in the same paint), so the name reads as belonging to the map under
     it rather than to the buttons beside it.
     8px against the buttons' 9: the name must sit UNDER the controls in the
     hierarchy — it is a caption, and a caption that shouts is a second button. */
  .mm__tag {
    display: flex; align-items: center; gap: 6px;
    font-size: 8px; letter-spacing: .3em; text-transform: uppercase;
    color: var(--grey-1); white-space: nowrap;
  }
  .mm__tag::before {
    content: ''; width: 5px; height: 5px; flex: none;
    background: var(--phx);
  }
  .mm__tag::after {
    content: ''; flex: 1 1 auto; height: 1px;
    background: rgb(var(--ink-rgb) / .18);
    /* The tracking puts .3em of dead air after the final L; the hairline starts
       where the word actually ends, not where its letter-spacing does. */
    margin-left: -.3em;
  }
  /* The pair, together at the left — not spaced to the ends. Two switches under
     one name are one panel; at opposite corners they were two unrelated objects. */
  .mm__row { display: flex; align-items: center; gap: 6px; }
  /* THE READOUT IS GONE EVERYWHERE, not just in the rail. It named the point the
     camera is standing on — and the point the camera is standing on is the one
     already lit in the list directly underneath it, at full ink with a filled
     mark, while the other five stand back. A second answer to a question the
     instrument has already answered, taking a third of its bar to do it. Owner's
     word, looking at the desk version: "das agency kann weg."
     The rule is kept as display:none rather than the markup being deleted, because
     mmWhere is written by the camera in site.js on every landing and a missing
     element there is a null check in a hot path for a thing nobody sees. */
  .mm__where { display: none; }
  /* The switch is a CELL of the bar, not a boxed button floating in one — its only
     border is the hairline that separates it from the readout. Same lamp, same
     aria-pressed paint as the nav fallback, so the two read as one control.
     .mm__cell is the same cell without the lamp or the pressed state — the
     Imprint button wears it: it opens the lightbox and holds nothing. */
  /* TWO BUTTONS THAT LOOK LIKE BUTTONS — owner's word, "mach das etwas mehr
     design, round corners, buttons". They were CELLS of the bar: no outline of
     their own, separated by a single left hairline, which read as a table more
     than as a pair of controls. Each has its own border now, its own ground, and a
     small radius.
     THE RADIUS IS 3px AND THAT IS DELIBERATE RESTRAINT. Everything else on this
     site is a square — the mosaic, the painted mask, the fray, the pointer — so a
     rounded control is a foreign shape by definition. Three pixels at a 9px type
     size is enough to read as "button" and small enough that the bar does not stop
     belonging to the plane behind it.
     Now that they carry their own edges, the bar spaces them instead of butting
     them: see the gap on .mm__bar. */
  /* ================= THE STRIP UNDER THE MAP =================
     Mirror of the bar, on the other edge: same inset of -1px so its outer edges sit
     ON the box's own border, and border-top:0 because the box's bottom hairline is
     the junction. flex-start, so the Imprint reads as the foot of the object rather
     than as a third switch that drifted.
     THE HEIGHT IS STATED and it has to be, because .minimap's bottom offset is
     calc(18px + var(--mm-foot)) and the two must agree exactly. 32px against a
     button measured at 23 — five of padding each side and nothing left over. */
  .mm__foot {
    position: absolute; left: -1px; right: -1px; top: 100%;
    height: var(--mm-foot); box-sizing: border-box;
    display: flex; align-items: center; justify-content: flex-start;
    padding: 0 6px;
    border: 1px solid rgb(var(--ink-rgb) / .34); border-top: 0;
    border-radius: 0 0 4px 4px;
    background: rgb(var(--bg-rgb) / .90);
  }
  /* .mm__high JOINS THE DRESS rather than bringing the nav bar's with it. It was
     .nav__high — 11px type, a .38 border, no ground — cut to sit on the baseline of
     six menu words in a bar half a module high. None of that is true here: it is on
     the instrument now, beside Nightvision, and a control that is the other one's
     twin in function and a size larger in appearance reads as more important rather
     than as its pair. One declaration for the three of them. */
  /* SIX AND SEVEN-HUNDREDTHS, AND BOTH NUMBERS ARE ARITHMETIC rather than taste.
     The bar is as wide as the box it sits on, and the box is clamp(230px, 17vw,
     270px) — so below a 1431px window it stands on its 230 floor and the bar has
     exactly 216px inside its own padding and borders. Two switches at the old
     9px/.1em measured 118.33 and 98.94, which is 217.27 before a gap: the pair
     overflowed at 1280x720 and 1200x800 (scrollWidth 229 against clientWidth 228,
     measured) and had 5px of NEGATIVE room at 1366x768. Not a tight fit — a fit
     that had already failed on three of the five desk sizes this site is checked at.
     A media query was the wrong instrument for it: the floor holds under 1431px,
     which is most laptops, so the narrow case would have been the normal case and
     the wide one the exception.
     Six of horizontal padding and .07em of tracking take the pair to about 196,
     which leaves twenty for the gap at the floor and more above it. Nine pixels of
     padding at a 9px type size was generous for a control this small; six still
     draws a box, and the tracking stays wide enough to read as the instrument's
     voice rather than as running text. */
  .mm__nv, .mm__cell, .mm__high {
    display: inline-flex; align-items: center; gap: 7px; flex: none;
    font: inherit; font-size: 9px; letter-spacing: .07em; text-transform: uppercase;
    background: rgb(var(--ink-rgb) / .04);
    border: 1px solid var(--line); border-radius: 3px;
    color: var(--grey-1); padding: 5px 6px; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: color .18s var(--ease), border-color .18s var(--ease),
                background .18s var(--ease);
  }
  .mm__nv:hover, .mm__cell:hover, .mm__high:hover {
    color: var(--ink); border-color: var(--ink);
    background: rgb(var(--ink-rgb) / .10);
  }
  .mm__cell:focus-visible, .mm__high:focus-visible {
    outline: 1px solid var(--phx); outline-offset: -3px;
  }
  /* The accent while the overview is actually open — the same paint Nightvision's
     lamp takes when it is on, and the same paint this button took in the nav bar.
     It is a state you are IN, which is the one thing on this site that earns the
     accent on a control. */
  .mm__high[aria-pressed="true"] {
    color: var(--phx); border-color: var(--phx);
    background: rgb(var(--phx-rgb) / .10);
  }
  .mm__nv > i {
    width: 6px; height: 6px; flex: none;
    background: rgb(var(--ink-rgb) / .3);
    transition: background .18s var(--ease), box-shadow .18s var(--ease);
  }
  .mm__nv:hover, .mm__nv[aria-pressed="true"] { color: var(--phx); }
  .mm__nv[aria-pressed="true"] > i {
    background: var(--phx);
    box-shadow: 0 0 0 3px rgb(var(--phx-rgb) / .22);
  }
  .mm__nv:focus-visible { outline: 1px solid var(--phx); outline-offset: -3px; }

  /* In the split the panel has no left border and sits on the window edge; the
     bar follows both. */
  /* min-height pins the number --mm-bar promises the centring calc above. It is
     the bar's own content height today, so nothing moves — it simply stops the
     two from drifting apart the next time a cell changes. */
  body.is-split .mm__bar { left: 0; right: 0; border-left: 0; min-height: var(--mm-bar); }

  /* ---- THE WAY OUT: the whole plane at once ----
     HIDDEN ON PURPOSE, and the corner is the argument. The map is meant to be
     walked; a permanent "show me everything" sitting in the chrome is a standing
     invitation to skip the walk, and the walk is the site. So it rests at 7% —
     present enough that a pointer crossing the corner finds it, quiet enough that
     nobody is offered it. The same bargain as the thing in the other corner.

     Drawn as four brackets rather than a magnifier with a minus in it: the
     gesture is "fit the frame", not "make smaller", and brackets say that with no
     glyph, no word and nothing to translate. On the way back they close inward,
     which is the same sentence read backwards.

     Below the bar and hard right, mirroring the minimap in the opposite corner —
     the two controls that speak about the plane as a whole stand at its two ends. */
  .wholebtn {
    /* --nav-h, not --m: it hangs under the bar, and the bar is half a module now. */
    position: fixed; right: 24px; top: calc(var(--nav-h) + 18px); z-index: 45;
    width: 34px; height: 34px; padding: 0; border: 0; background: none;
    color: var(--ink); cursor: pointer; opacity: .07;
    transition: opacity .32s var(--ease), color .32s var(--ease);
  }
  .wholebtn:hover, .wholebtn:focus-visible { opacity: 1; color: var(--phx); }
  /* The overview's own way back, so it stays lit for as long as it is the exit. */
  .is-overview .wholebtn { opacity: 1; }
  /* There are no :focus styles anywhere else in this project, and everywhere else
     there is at least something visible to land on. Here there is not: a control
     at 7% that never answers the keyboard is a control only a mouse can reach. */
  .wholebtn:focus-visible { outline: 1px solid var(--phx); outline-offset: 5px; }
  .wholebtn i {
    position: absolute; width: 11px; height: 11px;
    transition: transform .32s var(--ease);
  }
  .wholebtn i:nth-child(1) { left: 0;  top: 0;    border-left:  1px solid; border-top:    1px solid; }
  .wholebtn i:nth-child(2) { right: 0; top: 0;    border-right: 1px solid; border-top:    1px solid; }
  .wholebtn i:nth-child(3) { right: 0; bottom: 0; border-right: 1px solid; border-bottom: 1px solid; }
  .wholebtn i:nth-child(4) { left: 0;  bottom: 0; border-left:  1px solid; border-bottom: 1px solid; }
  /* Open: the brackets draw in, and the control now reads "come back". */
  .is-overview .wholebtn i:nth-child(1) { transform: translate( 5px,  5px); }
  .is-overview .wholebtn i:nth-child(2) { transform: translate(-5px,  5px); }
  .is-overview .wholebtn i:nth-child(3) { transform: translate(-5px, -5px); }
  .is-overview .wholebtn i:nth-child(4) { transform: translate( 5px, -5px); }

  /* ---- the bar in the overview: the name stays, the list goes ----
     The six words are a way of GOING somewhere, and in the overview the plane itself is
     that — every point is a target and each one now carries its own name at a readable
     size (see .poi__ov). So the menu is the same six words twice, once in 18px chrome
     and once on the thing it points at, and the chrome copy is the one that is not
     also the destination.
     The wordmark stays: it is the masthead, not navigation, and the one fixed thing
     that says whose plane this is while the plane is too small to say it.
     Faded on --whole-e rather than switched, like the module grid, so it leaves with
     the movement. pointer-events off at the same time — a word at 8% opacity that
     still answers a click is a trap. */
  .nav__menu {
    opacity: clamp(0, calc(1 - var(--whole-e, 0) * 2), 1);
    transition: opacity .2s var(--ease);
  }
  body.is-overview .nav__menu { pointer-events: none; }

  /* In the overview a whole point is a target, so it takes the pointer and says
     so. At 1:1 nothing changes: a point is something you are reading. */
  .is-overview .poi { cursor: pointer; }

  /* ---- the point's label, in the overview only ----
     Absent at 1:1: the point already says all of this at a readable size, and a second
     copy over it would be a caption on top of its own subject. */
  .poi__ov { display: none; }
  /* THE BLOCKS GO, THE FRAME STAYS. display:none on the children is safe here only
     because .poi carries grid-template-rows: repeat(9, var(--m)) — the box keeps its
     nine modules with nothing in it, so the camera, the minimap and the route all
     still measure the same point they did before. With the old content-derived height
     this would have collapsed every point to nothing. */
  body.is-overview .poi > *:not(.poi__ov) { display: none; }
  /* AND THEY ARRIVE RATHER THAN STANDING THERE THROUGH THE WHOLE FLIGHT. Sized from
     the destination, a label is at its final size in PLANE px from the first frame —
     which at 1:1 is enormous, because the plane has not shrunk yet. Held back until
     the camera is most of the way there, what you see is the last of the travel: the
     names settling onto the plane as it stops. It is a fade and a scale, both
     composited, and neither asks for a layout.
     Out instantly on the way back, which is right: the moment you are moving toward a
     point, the six names are not the thing you are looking at. */
  body.is-overview .poi__ov {
    opacity: clamp(0, calc((var(--whole-e, 0) - .62) * 3.4), 1);
    display: flex; flex-direction: column; justify-content: center;
    grid-area: 1 / 1 / -1 / -1;
    height: 100%; padding: 0 calc(var(--m) * .25);
    gap: calc(26px / var(--cam-fit, 1) * .28);
  }
  /* A SIZE ON THE GLASS, not on the plane — and sized from --cam-fit, not
     --cam-scale. Dividing by the scale the camera is TRAVELLING TOWARD gives the
     same answer once it arrives, and unlike the live scale it holds still while the
     zoom runs. Dividing by the live one re-laid this type at a new px size on every
     frame of the movement, and the engine re-rounded every glyph at each of them:
     that is the wobble that was reported, in both directions for the same reason.
     Laid out once, the plane's own transform carries it, and a transform on a
     composited layer does not re-measure anything.
     The fallback of 1 matters for the frame before render() has run once. */
  /* min(), and the cap is the one thing that keeps this honest. Dividing by the scale
     grows the type without limit as the scale falls, and the point does NOT grow with
     it — it is 13 modules whatever the camera does. On a wide, short window wholeFit()
     drops (measured 0.088 on a tall frame against 0.132 on 1440 x 900) and "Operations"
     would have run out of its own box.
     BIGGER — owner's word, "mach bei map view die headlines und sublines größer". 22
     to 26 on the glass, and the cap follows from 2.2 to 2.45 modules.
     26 IS THE LARGEST THAT KEEPS EVERY NAME ON ONE LINE, measured rather than judged —
     and measured twice, because the first attempt measured the wrong thing. These names
     WRAP (no nowrap here: a longer one should break rather than run out of the point),
     so scrollWidth is always the box width and reads 100% whether the text fits or not.
     The honest number comes from a nowrap clone. At the cap, with .poi__ov's quarter
     module of padding taken off each side:
         1440x900   "Operations" 962 of 1100 usable   87%   one line
         1280x720   "Operations" 858 of  900          95%   one line
     Every other name lands between 57 and 68%. At 28px the worst case computes to 102%
     and Operations breaks over two lines on a 1280 window; at 30 it breaks on all of
     them. The CAP is what guarantees this rather than the 26: as the scale falls the
     divided size runs away, and 2.45 modules is the ceiling it runs into first. */
  .poi__ov b {
    font-size: min(calc(26px / var(--cam-fit, 1)), calc(var(--m) * 2.45));
    font-weight: var(--w-med); line-height: 1;
    letter-spacing: calc(-0.4px / var(--cam-fit, 1));
    color: var(--ink);
  }
  .poi__ov span {
    font-size: min(calc(13px / var(--cam-fit, 1)), calc(var(--m) * 1.3));
    font-weight: var(--w-light); line-height: 1.25;
    color: var(--grey-1);
    /* Three lines is what fits under the name in the screen height the point has at
       this distance — measured at eleven pixels, and the line count still holds at
       fourteen because the name grew by the same proportion above it. Past three it is
       a paragraph nobody can read from here. */
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
    overflow: hidden;
  }
  /* The one you are on, named louder. Same device as the bar's underline: the accent
     answers "where am I" and nothing else. */
  body.is-overview .poi.is-active .poi__ov b { color: var(--phx); }
  /* Nothing on a receding plane should answer the mouse individually — the film's
     controls and the record links are 1/6 of their size and not aimed at. */
  .is-overview .poi * { pointer-events: none; }

  /* Above everything, the open film included (z 200) — the page hides the native
     pointer, so this dot IS the pointer and must never end up behind a layer.
     It takes no events, so nothing can be blocked by putting it on top. */
  /* A one-pixel WHITE ring around the square, and white literally rather than
     var(--ink): the ring is there to keep the orange mark legible on dark
     ground, and on a dark scheme ink IS white — but hung on the token it turned
     black the moment the palette was switched to a light one, which is the one
     case where the ring had nothing left to do. A contrast ring is not a palette
     decision.
     Drawn OUTSIDE the box (box-shadow, not border) so the dot keeps its 16px and
     the ring states below are untouched. */
  /* ---- the briefing form on Contact ----
     Rules, not boxes: an input is a baseline with a label above it, which is the
     same figure the .blk--rule blocks draw everywhere else. A bordered field would
     have been the one rounded, shaded object on a page made of hairlines. */
  .cform { display: flex; flex-direction: column; gap: 12px; }
  .cform__row { display: block; }
  .cform__row > span {
    display: block; font-size: var(--t-small); letter-spacing: .01em;
    color: var(--grey-1); margin-bottom: 5px;
  }
  .cform input, .cform textarea {
    width: 100%; font: inherit; font-size: 15px; color: var(--ink);
    background: none; border: 0; border-bottom: 1px solid var(--line);
    padding: 4px 0 7px; resize: none; border-radius: 0;
  }
  .cform input::placeholder, .cform textarea::placeholder { color: var(--line); }
  .cform input:focus, .cform textarea:focus {
    outline: none; border-bottom-color: var(--phx);
  }
  /* The submit IS the call to action — .cta, the same filled mark the point used
     to carry on a black plate of its own. Underlined text was the wrong weight for
     the one thing here you are meant to press, and the wrong answer to a panel
     that was already shouting beside it. */
  .cform__cta {
    /* 4px, was 8: the form's status line below must END at the block's own
       edge (module row 9), because the mail note's reserved line starts there —
       at 8 the two lines' boxes overlapped by 4px, which shows the one time
       both speak at once. Measured to land the status line at exactly 612/72. */
    font: inherit; border: 0; cursor: pointer; margin-top: 4px;
    transition: background .2s var(--ease), color .2s var(--ease);
  }
  /* The address LEADS the foot block now (grid-row 9/10, full width): address,
     then the reserved confirmation line, then icons and the legal line sharing
     the last row — "make the mail bigger, higher", so it stands first and at
     display weight instead of 12px under its own note. The 6px on top duck the
     form's status line, whose FILLED box ends a few px past the block edge
     above — the same measurement the note's old top margin was carrying; the
     duck moved with the top position. */
  .cform__mail { display: block; margin: 6px 0 2px; }
  .cform__mail:hover { color: var(--ink); }
  /* It is a <button> — a press copies the address (see the note in the markup).
     The reset keeps it reading as type rather than as a control; the copied
     state answers in the accent, the same voice the Careers button uses,
     without the filled plate. Sized against the module like .legal one step
     up from it: the address is the point's way in for anyone who will not
     touch a form, and it was dressed as a caption. */
  button.cform__mail {
    appearance: none; background: none; border: 0; padding: 0;
    font: inherit; font-size: clamp(16px, calc(var(--m) * .23), 24px);
    /* 1.15, because the foot stack has to close inside one module row at the
       smallest module: 6 + address + 2 + reserved note + 3 + 24px of icons —
       the body's 1.5 on a display-size line was the 2px that pushed the icons
       past the block's edge. Measured at module 72, the tight case. */
    line-height: 1.15;
    font-weight: var(--w-med); letter-spacing: .01em;
    text-align: left; cursor: pointer;
  }
  button.cform__mail.is-copied { color: var(--phx); }
  /* The confirmation line is RESERVED, the Careers way. It was
     `:empty { display:none }` here, "no blank line for a message that is not
     there" — and on a fixed grid that argument loses: the line APPEARING is
     what shoved the address across the icons every time the note spoke. An
     empty reserved line in this block costs black air that was already black.
     1lh and not .copy__note's 1.4em: the reserve must equal the line box the
     text will actually take, or the note still grows the moment it fills —
     measured at 2px with 1.4em and at 1px with 1.5em (16.5px of em rounds
     apart from 16.5px of line box; lh is the same rounding by definition).
     A shove of 1px is the same defect smaller. The em line stays as the
     fallback for engines without lh.
     UNDER the address now, not over it — the address took the block's top
     edge when it grew to display size, and its own top margin took over the
     duck under the form's status line. */
  .cform__mailnote { min-height: 1.5em; min-height: 1lh; margin: 0 0 3px; }
  .cform__note { margin: 0; font-size: 11px; line-height: 1.5; color: var(--grey-1); min-height: 1em; }
  .cform__note.is-bad { color: var(--phx); }
  /* THE HONEYPOT, and it has to be hidden the way a trap is hidden rather than the
     way a decoration is. `display: none` would do it, but a well-built bot reads the
     computed style and skips what it cannot see; taken out of the flow and left
     nominally rendered it looks like a field somebody forgot to lay out.
     Not `visibility: hidden` either, for the same reason. The markup carries
     aria-hidden and tabindex="-1" so nobody using a screen reader or the Tab key is
     asked to walk into it. */
  .cform__pot {
    position: absolute; left: -9999px; top: auto;
    width: 1px; height: 1px; overflow: hidden;
  }

  /* ---- the legal line ----
     A size of its own, up from the 12px .small carries elsewhere. It is not a
     caption: without a footer it is the site's ONLY route to the imprint, and a
     route has to be legible before it is discreet. */
  /* ONE STEP ABOVE .small, and now it stays one step. Both were fixed px, so the
     relationship held by coincidence; once .small became module-relative the two
     converged — at module 240 .small reaches 15px and a flat 15px here would have made
     the only route to the imprint exactly as small as a caption again. */
  .legal { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1em;
           font-size: clamp(13px, calc(var(--m) * .1705), 19px); letter-spacing: .02em; }
  .legal i { font-style: normal; color: var(--line); margin: 0 .35em; }
  .legal__link {
    font: inherit; letter-spacing: inherit; text-transform: inherit;
    background: none; border: 0; padding: 0; cursor: pointer;
    color: var(--grey-1); border-bottom: 1px solid transparent;
    transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .legal__link:hover { color: var(--phx); border-bottom-color: var(--phx); }

  /* ---- the other ways in ----
     Marks, not words: they are places this unit also is, not a third call to
     action beside the booking and the address. Hairline strokes on the same
     lattice as everything else — no brand colours and no filled badges, which
     would be another site's idea of a footer. */
  /* ---- the gallery band and its lightbox ----
     A thumbnail is a BUTTON dressed as a picture: the reset strips the control
     chrome, the .media inside keeps every rule pictures already obey. The hover
     answer is the map's own — brightness, not colour. */
  button.gal__t { appearance: none; background: none; border: 0; padding: 0; cursor: pointer; }
  .gal__t .media { width: 100%; height: 100%; }
  .gal__t:focus-visible { outline: 1px solid var(--phx); outline-offset: 2px; }

  /* The picture lightbox. Fixed and re-parent-free: unlike the film, this never
     lives inside the transformed world — site.js builds it once, directly on
     <body>, so position:fixed means what it says. Above the film's 200: a
     gallery opens INSIDE a deep page, which is already above everything else. */
  .glb {
    position: fixed; inset: 0; z-index: 220;
    background: rgb(0 0 0 / .94);
    display: grid; place-items: center;
  }
  .glb[hidden] { display: none; }
  .glb figure { margin: 0; max-width: 88vw; }
  .glb__img { display: block; max-width: 88vw; max-height: 82vh; margin: 0 auto; }
  .glb__cap {
    margin-top: 12px; text-align: center;
    font-size: 12px; letter-spacing: .04em; color: var(--grey-1);
  }
  .glb__cap:empty { display: none; }
  /* The three controls speak the lightbox's own quiet language: type, not glyph
     fonts. Prev/next ride the middle of the frame's sides; the exit sits where
     the deep pages' own exit sits, top right. */
  .glb__x, .glb__p, .glb__n, .glb__ct {
    position: fixed; background: none; border: 0; cursor: pointer;
    font: inherit; font-size: 18px; color: var(--grey-1);
    padding: 14px; transition: color .2s var(--ease);
  }
  .glb__x:hover, .glb__p:hover, .glb__n:hover { color: var(--ink); }
  .glb__x { top: 18px; right: 22px; }
  .glb__p { left: 14px; top: 50%; transform: translateY(-50%); }
  .glb__n { right: 14px; top: 50%; transform: translateY(-50%); }
  .glb__ct { left: 22px; top: 18px; cursor: default; font-size: 12px; letter-spacing: .08em; }
  /* One picture needs no arrows and no count — hidden rather than disabled,
     because a disabled arrow promises a neighbour that does not exist. */
  .glb--one .glb__p, .glb--one .glb__n, .glb--one .glb__ct { display: none; }

  .social { display: flex; align-items: center; gap: 18px; }
  /* The legal line rides the icons' row now — one foot line, owner's word —
     and margin-left:auto is what flushes it against the block's right edge
     (column 14, the same edge the lede above ends on). flex-wrap stays off
     this row: at thirteen modules there is room, and a wrapped copyright
     under the icons would be the two-line state this change exists to end. */
  .social .legal { margin-left: auto; flex-wrap: nowrap; white-space: nowrap; }
  /* "AT THIRTEEN MODULES THERE IS ROOM" — and the stacked page has no modules. In
     reflow the block is the window minus its padding, and the row measured 76px too
     wide at 390 and 106 too wide at 360: the copyright ran out through the right
     edge of the phone. The map's foot line is not in question here, only the
     stack's, so the exception is scoped to reflow and says the honest thing — on a
     narrow page the row wraps, and the legal line takes the line under the icons.
     Reached by anyone whose longest side is under MAP_MIN_LANDSCAPE, which is the
     one class of phone that never sees the rotate lid and lives in this layout. */
  @media (max-width: 560px) {
    .social { flex-wrap: wrap; row-gap: 10px; }
    .social .legal { margin-left: 0; white-space: normal; flex-wrap: wrap; }
  }
  .social__i {
    display: block; width: 24px; height: 24px; padding: 0;
    background: none; border: 0; cursor: pointer; color: var(--grey-1);
    transition: color .2s var(--ease);
  }
  .social__i:hover, .social__i:focus-visible { color: var(--phx); }
  .social__i svg { display: block; width: 100%; height: 100%; }
  .social__i svg * {
    fill: none; stroke: currentColor; stroke-width: 1.6;
    stroke-linecap: square; stroke-linejoin: miter;
  }
  /* A zero-length line with a round cap IS the dot — one primitive fewer than a
     filled circle, and it inherits the stroke like everything else here. */
  .social__i svg .dot { stroke-linecap: round; stroke-width: 2.6; }

  /* the newsletter's field, inside the lightbox */
  .lb-form { margin-top: 1.2em; max-width: 340px; }

  /* ---- the legal lightbox ----
     Above everything, the open film included, because it is the one thing that has
     to be reachable from anywhere. It does not move the camera: the plane stays
     exactly where it was and the sheet arrives over it. */
  .lightbox { position: fixed; inset: 0; z-index: 260; display: grid; place-items: center; }
  .lightbox[hidden] { display: none; }
  /* Nearly opaque: a legal text is read, not glanced at, and at .82 the map
     underneath was still legible straight through the address. */
  .lightbox__scrim { position: absolute; inset: 0; background: rgb(var(--bg-rgb) / .94); }
  .lightbox__sheet {
    position: relative; z-index: 1;
    /* BIGGER — owner's word, "lightbox größeres overlay". Eight modules and 76vh
       was a column of legal text in a tall narrow slot with the map showing round
       all four sides of it. Eleven modules and 86vh gives the text a measure worth
       reading and the sheet enough of the window to be the thing you are looking
       at. The two min() arms are unchanged in kind: the viewport still wins on a
       screen too small for eleven modules. */
    width: min(calc(var(--m) * 11), calc(100vw - 48px));
    max-height: min(86vh, 1040px); overflow-y: auto;
    /* --phantom, NOT --bg. There is no --bg token on this site — only --bg-rgb and
       --phantom — so `background: var(--bg)` resolves to nothing and the sheet came
       out transparent with the page showing through its own text. */
    background: var(--phantom); border: 1px solid var(--line);
    padding: calc(var(--m) * .5) calc(var(--m) * .5) calc(var(--m) * .6);
  }
  /* The same cross as the records', at one module here: the sheet is not on the
     module lattice, so two would be a quarter of its width. */
  .lightbox__x {
    position: absolute; top: 8px; right: 8px;
    width: var(--m); height: var(--m);
  }
  .lightbox__x .rec-x__key { top: calc(var(--m) * .74); }
  .lightbox__title {
    font-size: calc(var(--m) * .38); line-height: 1.05; letter-spacing: -.02em;
    margin: 0 0 .6em; padding-right: var(--m); color: var(--ink);
  }
  .lightbox__body { font-size: 15px; line-height: 1.6; color: var(--grey-1); }
  /* pre-line: a blank line starts a paragraph, a single newline is a line break.
     An address is written as lines and has to stay lines — collapsed, the whole
     of "Musterstraße 1 / 10115 Berlin" ran into the company name. */
  .lightbox__body p { margin: 0 0 1em; white-space: pre-line; }
  .lightbox__body p:last-child { margin-bottom: 0; }

  .cursor { position: fixed; top: 0; left: 0; width: 16px; height: 16px; background: var(--cursor);
            box-shadow: 0 0 0 1px #fff;
            z-index: 300; pointer-events: none; transform: translate3d(-100px,-100px,0);
            transition: width .28s var(--ease), height .28s var(--ease),
                        background-color .2s ease, box-shadow .28s var(--ease); }
  .cursor.is-blend { mix-blend-mode: multiply; width: 54px; height: 54px; }

  /* Over anything clickable the dot opens into a ring with a pip in the middle:
     the shape changes, not just the size, so the state reads at a glance rather
     than looking like the same square drifting bigger. The ring wins over
     is-blend — a link on top of a photo should still read as a link. */
  .cursor.is-link, .cursor.is-blend.is-link {
    width: 46px; height: 46px;
    background: transparent; mix-blend-mode: normal;
    box-shadow: inset 0 0 0 2px var(--cursor);
  }
  .cursor.is-link::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 6px; height: 6px; margin: -3px 0 0 -3px; background: var(--cursor);
  }
  /* Pressed: the ring snaps in, so a click has a physical answer. */
  .cursor.is-link.is-down { width: 34px; height: 34px; }



  /* ================= ADMIN CMS ================= */
  /* WIDER: 420 rather than 340. Thirty-seven controls, a painted mask editor and a
     colour section were living in a 340px column where the label column alone took
     92 of it — a slider had about 200px of travel and the numbers sat on top of the
     thumb. Held to 34vw so it cannot eat a small laptop. */
  .admin {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 34vw); z-index: 70;
    background: var(--phantom); border-left: 1px solid var(--line);
    transform: translateX(100%); transition: transform .45s var(--ease);
    display: flex; flex-direction: column; cursor: auto;
    /* NOT A CAPITALS PANEL ANY MORE. Every label, heading and button in here carried
       `text-transform: uppercase` with .12em to .24em of tracking, over strings that
       are already correctly cased in cfg_controls() — "Root cell", "Hold, then fade",
       "Over images". The capitals carried no information: the hierarchy in here is
       colour, weight and the group bars. So they came off, and the tracking that was
       compensating for them came off with them.
       THIS CONTAINER VALUE IS WHY EACH RULE SETS ITS OWN. letter-spacing is not part
       of the `font` shorthand, so every `font: inherit` control in the drawer inherits
       whatever is here — a rule that merely DELETED its tracking would have landed
       back on .06em instead of on nothing. */
    font-size: 11px; letter-spacing: .01em;
  }
  @media (max-width: 1400px) { .admin { width: 380px; } }
  .admin.is-open { transform: none; }
  .admin * { cursor: auto; }
  .admin__head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 18px 20px; border-bottom: 1px solid var(--line);
    letter-spacing: .01em; font-size: 13px;
  }
  .admin__head b { font-weight: var(--w-light); color: var(--phx); }
  .admin__body { overflow-y: auto; padding: 0 20px 28px; flex: 1; }

  /* A HEADING IS A LID, NOT A LINE.
     It used to be one word over a hairline with 22px of air on both sides — which
     at nine groups is nine hairlines and no grouping: you cannot tell where Cursor
     ends and Menu begins while scrolling thirty-seven rows.
     Now it is a bar: full-bleed across the drawer's padding, tinted, and STICKY, so
     the group you are inside of is named at the top of the panel the whole time you
     are in it. That is the part that does the work — the tint alone would still let
     you lose your place halfway down eleven cursor sliders. */
  .admin h4 {
    position: sticky; top: 0; z-index: 2;
    font-size: 13px; font-weight: var(--w-med); letter-spacing: -.005em;
    color: var(--phx); margin: 0 -20px; padding: 11px 20px 10px;
    background: var(--phantom);
    box-shadow: inset 0 0 0 100vmax rgb(var(--ink-rgb) / .05);
    border-top: 1px solid var(--line);
    /* It is the collapse handle, so it takes the pointer and the caret sits at the
       right where a disclosure belongs. */
    cursor: pointer; user-select: none;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
  }
  .admin h4::after {
    content: '–'; font-size: 13px; line-height: 1; letter-spacing: 0;
    color: var(--grey-1); transition: color .2s var(--ease);
  }
  .admin h4.is-shut::after { content: '+'; }
  .admin h4:hover::after, .admin h4:focus-visible::after { color: var(--ink); }
  .admin h4:focus-visible { outline: 1px solid var(--phx); outline-offset: -1px; }
  .admin .sect:first-child h4 { border-top: 0; }

  /* The group: a real element, so it can be inset, accented and shut. Its top
     padding is the air the heading's margin used to fake. */
  .admin .grp { padding: 12px 0 14px; }
  .admin .grp.is-shut { display: none; }
  .admin .grp > *:last-child { margin-bottom: 0; }

  /* THE CURSOR GROUP IS SET APART, because it is the one you actually tune and it
     was eleven identical rows in the middle of thirty-seven. The accent moves from
     the type into the bar, and the rows step in behind a rule — so the block reads
     as somewhere you are inside of rather than as more of the same list.
     The same treatment marks the paint group, for the opposite reason: it is the one
     group that is not sliders at all. */
  .admin h4.is-cursor, .admin h4.is-paint {
    box-shadow: inset 0 0 0 100vmax rgb(var(--phx-rgb) / .12);
    border-top-color: rgb(var(--phx-rgb) / .55);
    color: var(--ink);
  }
  .admin .grp--cursor, .admin .grp--paint {
    padding-left: 12px; margin-left: -1px;
    border-left: 1px solid rgb(var(--phx-rgb) / .4);
  }

  .row { display: grid; grid-template-columns: 104px 1fr 52px; align-items: center; gap: 10px; margin-bottom: 8px; }
  .row label { color: var(--grey-1); font-size: 11px; letter-spacing: .01em; }
  .row output { color: var(--ink); text-align: right; font-variant-numeric: tabular-nums; font-size: 11px; }
  .admin input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 2px;
    background: rgb(var(--ink-rgb) / .2); outline: none; }
  .admin input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px;
    background: var(--phx); cursor: pointer; }
  .admin input[type=range]::-moz-range-thumb { width: 12px; height: 12px; background: var(--phx);
    border: 0; cursor: pointer; }
  .seg { display: flex; border: 1px solid var(--line); }
  .seg button { flex: 1; font: inherit; font-size: 11px; letter-spacing: .01em;
    background: none; border: 0; color: var(--grey-1); padding: 6px 0; cursor: pointer; }
  .seg button.on { background: var(--phx); color: #fff; }

  /* ---- colour section ---- */
  /* The swatch is the native colour input with its chrome stripped, so the
     square you click IS the value — no proxy element to keep in sync. */
  .admin input[type=color] {
    -webkit-appearance: none; appearance: none; width: 100%; height: 22px;
    background: none; border: 1px solid var(--line); padding: 0; cursor: pointer;
  }
  .admin input[type=color]::-webkit-color-swatch-wrapper { padding: 2px; }
  .admin input[type=color]::-webkit-color-swatch { border: 0; }
  .admin input[type=color]::-moz-color-swatch { border: 0; }
  .admin select {
    font: inherit; font-size: 11px; letter-spacing: .01em;
    background: var(--phantom); color: var(--ink); border: 1px solid var(--line);
    padding: 5px 6px; width: 100%; cursor: pointer;
  }
  .admin input.txt {
    font: inherit; font-size: 11px; letter-spacing: .06em;
    background: none; border: 1px solid var(--line); color: var(--ink);
    padding: 4px 6px; width: 100%;
  }
  .scheme-bar { display: grid; grid-template-columns: 1fr auto auto; gap: 6px; margin-bottom: 10px; }
  .scheme-bar button {
    font: inherit; font-size: 11px; letter-spacing: .01em;
    background: none; border: 1px solid var(--line); color: var(--ink); padding: 0 9px; cursor: pointer;
  }
  .scheme-bar button:hover { border-color: var(--phx); color: var(--phx); }
  .scheme-io { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 8px 0 2px; }
  .scheme-io button {
    font: inherit; font-size: 11px; letter-spacing: .01em;
    background: none; border: 1px solid var(--line); color: var(--grey-1); padding: 7px 0; cursor: pointer;
  }
  .scheme-io button:hover { border-color: var(--phx); color: var(--phx); }
  .admin__foot { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 14px 20px;
                 border-top: 1px solid var(--line); }
  .admin__foot button {
    font: inherit; font-size: 12px; letter-spacing: .01em;
    background: none; border: 1px solid var(--line); color: var(--ink); padding: 9px 0; cursor: pointer;
  }
  .admin__foot button:hover { border-color: var(--phx); color: var(--phx); }
  .admin__foot .primary { background: var(--phx); border-color: var(--phx); color: #fff; }
  .admin-toggle {
    font: inherit; font-size: 11px; letter-spacing: .01em;
    background: none; border: 1px solid var(--line); color: var(--grey-1);
    padding: 5px 10px; cursor: none;
  }
  .admin-toggle:hover, .admin-toggle.on { border-color: var(--phx); color: var(--phx); }

  /* ---- the right-hand pair ----
     One flex child holding the two controls, so .nav keeps the three items its
     space-between was written for. The gap is the bar's own, not a new number.

     AND IT INHERITS THE AUTO MARGIN. #adminToggle carries `margin-left: auto`, and
     the note above .nav__menu says why that rather than a justify-content: an auto
     margin resolves BEFORE space-between, which is the only reason Together packs
     the six words against the wordmark instead of letting space-between centre them.
     Wrapping the switch moved that margin inside this box, where there is no free
     space to eat — and the menu promptly slid from x=295 to x=518, undoing Together.
     Measured, not guessed. The margin belongs to whichever element is .nav's own
     child, so now it is this one. */
  /* NOT ON THE BASELINE. .nav aligns on the baseline so the wordmark and the menu
     share a line — but these are a lamp, a bordered box and three hairlines, not
     letters. A button's baseline is its own label's, so hung on the text line the
     boxes drop below everything else. Marks get placed; type gets the baseline.

     flex-start, and then the bar's own top padding does the centring for free: the
     padding is (bar - wordmark) / 2 by construction, and #adminToggle is .3 of a
     module which is within a pixel of the wordmark's line box. Both are 26px at
     module 88, so one number places both. An earlier version used align-self: center
     plus a negative relative offset to undo the padding — two numbers that had to
     stay in step, and they did not survive the bar going from a whole module to a
     half. */
  /* BASELINE, NOT FLEX-START, and this is what puts the named button on the menu's
     line rather than near it. The group is the bar's flex CHILD, so an align-self on
     the button inside it aligns the button within the GROUP and never reaches the
     bar — measured 6 to 7px of baseline error at every window size, which is exactly
     the amount that reads as "not quite on the line".
     align-self on the group hands it to the bar's baseline, align-items inside hands
     the button's own text baseline to the group's, and the two lines meet. The lamp
     and the imprint sit on it too; they are type of the same size and always did. */
  .nav__tools { display: flex; align-items: baseline; gap: 10px; margin-left: auto;
                align-self: baseline; }

  /* ---- Nightvision ----
     Built out of the Grid switch rather than beside it: same hairline border, same
     11px, same quiet grey, so the two read as one pair of controls and not as a
     control next to a badge. What it adds is a LAMP, because this switch has a state
     worth seeing at a glance and the Grid switch does not — that one draws its state
     as the cross it becomes.

     The lamp is unlit off and the accent lit, which is the one place in this bar
     where the accent means "on" rather than "here". It gets a glow at the same time,
     for a reason beyond decoration: lit and unlit differ in COLOUR, and colour alone
     is the thing that stops carrying information on a display that is already green.
     The ring is a second channel — size, not hue.

     ON THE STATE ATTRIBUTE, not on a class: aria-pressed is what the button already
     has to say for anything that cannot see the lamp, and hanging the paint on the
     same attribute means the two cannot fall out of step. There is no way to make it
     look on while reading off. */
  /* .nav__imprint shares the whole dress: it is the Imprint button's fallback
     for the same three minimap-less states (see below), a lampless cell in the
     same voice. */
  .nav__nv, .nav__imprint {
    display: inline-flex; align-items: center; gap: 7px;
    font: inherit; font-size: 11px; letter-spacing: .01em;
    background: none; border: 1px solid var(--line); color: var(--grey-1);
    padding: 5px 10px; cursor: none;
    transition: color .18s var(--ease), border-color .18s var(--ease);
  }
  .nav__nv > i {
    width: 6px; height: 6px; flex: none;
    /* The unlit lamp is the ink at a low mix rather than a grey of its own: on Paper
       or Concrete a fixed dark dot would be the only near-black mark in the bar. */
    background: rgb(var(--ink-rgb) / .3);
    transition: background .18s var(--ease), box-shadow .18s var(--ease);
  }
  .nav__nv:hover, .nav__imprint:hover { border-color: var(--phx); color: var(--phx); }

  /* ---- THE WAY UP, NAMED ----
     Same dress as the bar's other two controls, because it is one of them: a
     bordered pill in the label face. It carries a word rather than a lamp, so it
     is wider and the letterspacing is the menu's rather than the switch's.
     PRESSED IS THE ACCENT, not a fill. The overview is a state you are IN, and the
     button is the way back out of it while you are — the same argument the
     brackets make by staying lit. */
  /* ON THE MENU'S LINE, AND LOUDER. Two corrections in one: the bar is
     align-items:baseline and a bordered pill has no baseline of its own to give,
     so it hung off the row the six words sit on — align-self fixes that by taking
     the button out of the baseline argument and centring it on the line instead.
     And it was --grey-1 on a .16 hairline at 9px, which is the quietest thing in a
     bar whose six menu words are at 18: a control nobody sees is a control that
     does not exist. The ink, at the menu's own size and tracking, with an edge to
     match. Owner's word: "mach View Highground etwas mehr sichtbar."
     NOT THE ACCENT. The accent on this site answers "where am I" — the menu's
     underline, the active mark — and this button is not a place. It answers by
     being as bright as the words beside it, and it takes the accent only while the
     overview is actually open, which IS a state you are in. */
  /* ---- THE HAMBURGER LEAVES THE ROW ----
     Owner's word: "mach den hamburger nach rechts. aus dem layout raus." It was the
     last item of .nav__tools, so it took part in the flex row and pushed everything
     before it leftward — the named button included, which is why that button sat in
     from the edge rather than at it. Absolutely placed against the bar's own right
     edge, it stops being a member of the layout and becomes what it always was: a
     way into the panel, parked in the corner.
     THE TOOLS THEN NEED THE ROOM BACK. Without padding they would run under it, so
     the row reserves the width the toggle occupies plus its inset. */
  body:has(.admin-toggle#adminToggle) .nav__tools { padding-right: 42px; }
  .nav .admin-toggle#adminToggle {
    position: absolute; right: max(12px, env(safe-area-inset-right));
    top: 50%; transform: translateY(-50%);
  }
  /* THE MAP IS THE ONLY PLACE THERE IS A WHOLE TO SEE, and the button no longer
     needs a rule of its own to say so. wholeOpen() returns early unless mapMode(),
     so on a deep page and in the stacked layout it is a control that can be pressed
     and does nothing — which is why it had display:none for .is-doc, for .is-split
     and for the reflow query. All three are now free: the button sits inside the map
     preview, and the preview is already hidden on exactly those three shapes (see
     .is-doc .minimap, body.is-compass .minimap, and the empty bar in the split).
     THAT WAS THE REASON FOR MOVING IT, beyond the owner's word — three visibility
     rules kept in step by hand with three others describing the same fact. */
  .nav__nv[aria-pressed="true"] { border-color: var(--phx); color: var(--phx); }
  .nav__nv[aria-pressed="true"] > i {
    background: var(--phx);
    box-shadow: 0 0 0 3px rgb(var(--phx-rgb) / .22);
  }

  /* THE NAV'S COPY IS THE FALLBACK. The switch lives on the minimap's control bar
     now (.mm__nv), so this one is hidden by default and shown where the minimap is
     not on screen: compass mode, and the reflow stack below the map's breakpoint.
     Never both at once — the split keeps the minimap (as the left panel), so the
     split keeps this hidden even under 1200px. One state behind both; site.js
     paints both aria-pressed. */
  /* THE READING LAYER IS NOT ONE OF THOSE PLACES — owner's word: "bei deep recon
     nicht imprint und nightvision nach oben. wenn die minimap weg ist, ist es das
     auch."
     It used to be in this list, on the argument that the fallback belongs wherever
     the instrument is gone. The argument was one step too wide: on a deep page the
     instrument is gone because there is no plane to point at, and the two controls
     went with it — a lamp and a legal link appearing in the bar the moment you dive
     is chrome arriving instead of chrome leaving, on the one layer that is meant to
     be a page you read.
     AND THE IMPRINT IS STILL REACHABLE, which is the only reason this is allowed.
     Esc or the cross puts you back on the map, where the minimap's bar carries it;
     the Contact point carries the three legal links in its own foot; the split's
     rail carries it; and the reflow stack keeps this fallback because that stack has
     no instrument at all. Reachable from anywhere still holds — it is one gesture
     away rather than nailed to every layer. */
  .nav__nv, .nav__imprint { display: none; }
  body.is-compass .nav__nv, body.is-compass .nav__imprint { display: inline-flex; }
  /* THE LAMP ON A SPLIT DEEP PAGE was excepted here, because the rule above used to
     put both fallbacks in the bar on a deep page and the split had to take the lamp
     back out. The deep page is no longer in that rule, so the exception has nothing
     left to except — kept as a note rather than as a selector, because a rule that
     matches nothing is how a sheet stops describing its own page. */
  @media (max-width: 1199px) {
    .nav__nv, .nav__imprint { display: inline-flex; }
    body.is-split .nav__nv, body.is-split .nav__imprint { display: none; }
    /* AND NO EXCEPTION FOR THE DEEP PAGE EITHER. I put one here last commit: the
       rail carries the Imprint and the rail is not on a deep page, so the bar's
       fallback was switched back on there. The owner saw the result on the device
       and it is a fair verdict — "bei mobile liegt jetzt überall oben rechts
       imprint drüber. das braucht es nicht, links im menu reicht" — a bordered
       button in the corner of a bar half a row high, sitting over the top of the
       page on every deep view.
       So the split has NO chrome fallback at all: the way to the Impressum on this
       shape is the foot of the rail, on the map. THAT LEAVES A REAL GAP, recorded
       here rather than quietly: a landscape phone reading a deep page has to come
       back up to the map to reach it. The honest fix is a line in the deep page's
       OWN bar or at its foot, which is where a legal link belongs on a document
       anyway — not a floating button over the corner. Owner's call, not mine to
       take while removing the thing they asked me to remove. */
  }

  /* Below the map's breakpoint the bar is a sticky strip with room for the wordmark
     and not much else — the same place .nav__brand drops to 17px. The word goes and
     the lamp stays: it is the state, and a lamp on its own is still a switch. */
  @media (max-width: 700px) {
    .nav__nv > span { display: none; }
    .nav__nv { padding: 5px 7px; }
  }

  /* ---- the Grid switch, drawn ----
     Three rules of the same hairline the whole page is drawn with, at the module's
     own measure — not a glyph and not a boxed word. It hangs on the ID and not on
     the class ON PURPOSE: .admin-toggle is also the drawer's own Close button, and
     that one is a word.
     Open, the three strokes become the cross that shuts it — the middle goes, the
     outer two meet in the middle. Same device as .rec-x on a record, so the mark
     always says the state rather than needing a second control to undo it. */
  #adminToggle {
    border: 0; padding: 0; position: relative;
    width: calc(var(--m) * .3); height: calc(var(--m) * .3);
    min-width: 24px; min-height: 24px;
    /* Hard right, and an auto margin rather than a justify-content on the parent:
       in compass mode the menu becomes display:contents and the switch is the only
       child left, at which point space-between would hand it the LEFT end again. */
    margin-left: auto;
  }
  #adminToggle::before, #adminToggle::after, #adminToggle i {
    content: ""; position: absolute; left: 10%; right: 10%;
    border-top: 1px solid currentColor;
    transition: transform .3s var(--ease), top .3s var(--ease), opacity .2s var(--ease);
  }
  #adminToggle::before { top: 28%; }
  #adminToggle i       { top: 50%; }
  #adminToggle::after  { top: 72%; }
  #adminToggle.on::before { top: 50%; transform: rotate(45deg); }
  #adminToggle.on i       { opacity: 0; }
  #adminToggle.on::after  { top: 50%; transform: rotate(-45deg); }

  body.admin-open .cursor { display: none; }
  body.admin-open { cursor: auto; }

  @media (hover: none), (prefers-reduced-motion: reduce) { .cursor { display: none; } body { cursor: auto; } }
  /* At 160px the bar cannot hold a point name AND the word Nightvision — the word
     goes, the lamp stays; it is the state, and a lamp on its own is still a switch
     (the same trade the nav fallback makes under 700px). */
  @media (max-width: 1100px) {
    .minimap { width: 160px; height: calc(160px*5400/8400); }
    .mm__nv > span { display: none; }
  }

  /* ================= REFLOW MODE =================
     The world stops being a pannable plane and becomes an ordinary stacked document:
     every POI reflows to one column and the page scrolls natively. The mosaic goes
     with it — painted in screen space against a camera that no longer exists here.

     THE CONDITION IS THE EXACT NEGATION OF mapMode() IN site.js, and it has to be
     written out because it is not a width. It used to be a plain (max-width: 1199px),
     matching the old rule, and when mapMode() was widened to give a phone the map in
     landscape ONLY THE SCRIPT LEARNED ABOUT IT. The result was the worst of both: the
     camera ran, the route ran, the compass ran — and the stylesheet had already turned
     .world into a static flex column, so a phone held sideways got a stacked document
     with a live camera behind it. Two definitions of one threshold, and only one moved.

         mapMode()  =  w >= 1200  OR  (w >= 700 AND w > h)

     so its negation, as a comma list because a comma is OR in a media query:

         (max-width: 699px)                              too narrow to walk a point
         (max-width: 1199px) and (orientation: portrait)  tall frame, turn it

     CSS `orientation: portrait` is h >= w, which is exactly NOT(w > h). If the rule in
     site.js changes, this changes with it — there is no way to derive one from the
     other, so they are written next to each other's names on purpose. */
  @media (max-width: 699px), (max-width: 1199px) and (orientation: portrait) {
    /* THE PAGE SCROLLS DOWN, NEVER ACROSS — and this line is the whole of the
       "beim laden ist alles riesig gezoomt" report. Measured in a true 390px
       frame: documentElement.scrollWidth came out 798, more than twice the
       phone, so the browser took 798 as the LAYOUT viewport and the visitor was
       handed a page at half scale with a fixed rotate lid whose centre sat off
       the right edge of the glass. Nothing was oversized; the canvas was.
       Two things were doing it, and both are legitimate where they came from:
       the header menu asks for 468px of unwrappable words (fixed below), and
       the CMS drawer parks itself one full width to the right of the screen —
       which costs nothing while the root clips, and everything the moment this
       query hands the root back its overflow. overflow-x is the clip that has
       to stay; overflow-y is what this mode exists for. */
    html, body { overflow-x: hidden; overflow-y: visible; height: auto; cursor: auto; }
    /* .mark goes with them: it is absolutely positioned inside .world, and .world
       becomes position:static here — the marks would resolve against the viewport
       instead and land at five arbitrary spots over the stacked document. */
    /* .blk--loose joins the marks: the loose ground pictures are decor set into a
       plane that no longer exists down here — in the stacked column they would be
       six random photographs between the points. */
    .cursor, .minimap, .pin, .worldmap, .world-grid, .mark, .blk--loose { display: none !important; }

    /* THE SIX WORDS GO, the same call the split already makes. Their min-content
       is 468px of unwrappable type — wider than the phone — so instead of
       shrinking they grew the header's content box and took the document's width
       with it, which is what widened the layout viewport. Letting them scroll
       inside what is left was tried and measured: 25 visible pixels of 468, a
       strip nobody can use. A wordmark, six words and three switches do not fit
       one 44px row at 390px, and the row is the wrong place to insist.
       Nothing is lost that the reader cannot reach: down here every point is a
       section of one scrolling column, in the same order the menu listed them,
       and the wordmark still returns to the first. */
    .nav__menu { display: none; }

    .viewport { position: static; overflow: visible; touch-action: auto; }

    .world {
      position: static; width: auto; height: auto;
      transform: none !important; padding: calc(var(--nav-h) + 28px) 18px 60px;
      display: flex; flex-direction: column; gap: 56px;
    }
    .poi {
      /* THE NINE FIXED ROWS GO WITH THE COLUMNS. The line below turns the
         thirteen columns into one and the blocks' rows to `auto` — but `.poi`
         also carries `grid-template-rows: repeat(9, var(--m))`, and an
         auto-placed block lands in an EXPLICIT row if one is free. So every
         block in the stack came out exactly one module tall while its text
         needed three or four: measured in a true 390px frame, Mission's
         headline overrunning its block by 310px and the contact form by 279.
         `none` leaves only implicit rows, which `grid-auto-rows: auto` then
         sizes to their content. Measured after: 0 overruns at 390x844,
         360x640, 667x375 and 768x1024, where there were 5, 6, 2 and 2. */
      grid-template-rows: none !important;
      /* No lift down here. There is no map and no fixed chrome to align to — the
         points are stacked in normal flow, so half a module up would only pull the
         first one under the header and shorten every gap by the same amount. */
      transform: none !important;
      position: static !important; width: 100%;
      grid-template-columns: 1fr !important; grid-auto-rows: auto !important;
      gap: 18px;
    }
    /* .poi__mark goes with them: it is placed by grid-column/grid-row like a
       .blk, and left alone it would open implicit columns in a grid that has
       exactly one. It stays visible — down here the wordmark is nowhere else,
       the nav lost it and .mark is switched off with the plane. */
    .poi > .blk, .poi > .poi__mark {
      grid-column: 1 / -1 !important; grid-row: auto !important;
      padding: 0; min-height: 0; opacity: 1 !important;
    }
    /* The .poi__over override lived here and is gone with the class. The reason it
       existed is worth keeping in view: it kept its authored grid-column in a grid
       that has ONE column, which opened twelve implicit ones — the type ran off the
       right edge of the phone, took a horizontal scrollbar with it and left the film
       standing as a 180px thumbnail. The rule above still does that job for every
       .blk, and the headline is a .blk now, so it is covered. The margin-right the
       three pillars carry on the map has to go, though: down here they are stacked
       full-width and a quarter module of air on the right would ride in as a dent. */
    .poi > .blk[style*="margin-right"] { margin-right: 0 !important; }
    .blk--fill  { padding: 18px; }
    .blk--img   { aspect-ratio: 16/10; }
    /* THE OPERATIONS SQUARE, UNDONE DOWN HERE — and it had to be, because it went
       to nothing: .opcard takes its height from the grid row and its width from
       that, and the stack has no rows (grid-template-rows: none, above) so
       height:100% resolved against an auto box and came out 0. With the width
       derived from the height, the three cards measured 0 x 0 and the photographs
       were simply absent from the stacked page. Found by sweeping 390x844 and
       768x1024 rather than by looking at them.
       The width leads here instead — one column, full bleed, like every other
       picture in the stack — and aspect-ratio 1/1 from the base rule keeps them
       square, which is the shape that was asked for on both layouts. */
    .opcard {
      height: auto; width: 100%; max-height: none;
      justify-self: stretch; align-self: auto;
      /* SPELLED OUT, not inherited from the base rule. .blk--img sets 16/10 one
         line up and carries the same specificity, so source order decided it and
         the cards came out at the wrong ratio in the stack while being right on
         the map. Same shape on every layout is the whole of the ask. */
      aspect-ratio: 4 / 3;
    }
    /* No lattice down here, so no fray — and a path written for the map's box would
       clip a phone-width block to a strip of it. frayApply() clears the property
       outside mapMode() as well; this is the belt to that brace. */
    .blk--fray  { clip-path: none !important; }
    /* The plane's module grid is gone, so the panels have to carry their own
       height. 2:1 is the film's native ratio and the faces crop follows it. */
    .blk--panel { aspect-ratio: 2/1; }
    .blk--portrait { aspect-ratio: 5/4; }
    .hair { display: none !important; }   /* no module grid left to draw against */
    .faces__name { font-size: clamp(30px, 8vw, 52px); }
    .faces__copy { font-size: 17px; }
    /* No separator here either — the map lost every horizontal rule on the
       owner's word, and the reflow list turning them back on would make the
       phone the one place the old look survives. The 16px keeps the stack's
       rhythm; border-left 0 still folds the desktop's vertical rules away. */
    .blk--rule, .blk--left { border-left: 0; padding-top: 16px; }
    /* the knockout plates are pointless without a grid behind them — the callout is
       named separately because its plate hangs on .faces__callout, not on a .blk */
    .blk::before, .faces__callout::before { display: none !important; }

    h1 { font-size: clamp(40px, 13vw, 68px); }
    h2 { font-size: clamp(26px, 7.5vw, 38px); }
    /* The travelling tag again (see hd-1/hd-2 at the type rules): the demoted
       mission headline keeps h1's clamp, a promoted plain heading keeps h2's.
       The statement line below must WIN over hd-2 for a promoted statement —
       same specificity, later rule — so a statement stays statement-sized
       whatever tag it carries. */
    .hd-1 { font-size: clamp(40px, 13vw, 68px); }
    h1.hd-2 { font-size: clamp(26px, 7.5vw, 38px); }
    .statement, h1.statement { font-size: clamp(21px, 6vw, 30px); }
    .lede { font-size: 17px; }
    .sub { font-size: 15px; }
    .n-big { font-size: 34px; }

    .nav { position: sticky; }
    .intro { position: static; padding: calc(var(--nav-h) + 60px) 18px 30px; opacity: 1 !important; }
    body.is-intro .nav { opacity: 1; pointer-events: auto; }
  }
  @media (max-width: 560px) {
    /* Longhands, for the same reason as the base rule: the shorthand would take the
       shared baseline's top padding with it and drop the whole row to the ceiling. */
    .nav { padding-inline: 16px; padding-block: 10px 0; }
    .nav__brand { font-size: 17px; }
    .admin { width: 100%; }
  }

  /* ================= THE ROTATE GATE =================
     Above everything, including the entry sequence: a portrait phone must not be
     handed the film either — it would play in a frame the plane cannot use, and the
     first thing after it would be the lid anyway.

     z-index 300 puts it over .seq (100) and over an opened film panel (200), which is
     the only ordering that holds when the device is turned mid-film. */
  .rotate {
    position: fixed; inset: 0; z-index: 300;
    background: var(--phantom);
    display: grid; place-items: center; padding: 28px;
    text-align: center;
  }
  .rotate[hidden] { display: none; }
  /* Under the lid the page is a real scroller in reflow mode, and a lid you can scroll
     behind reads as broken. */
  body.is-rotate { overflow: hidden; }
  .rotate__in { max-width: 30ch; display: grid; justify-items: center; gap: 22px; }
  .rotate__t {
    font-size: clamp(21px, 5.6vw, 30px); font-weight: var(--w-reg);
    line-height: 1.1; letter-spacing: -.02em; color: var(--ink);
  }
  .rotate__b { font-size: var(--t-small); line-height: 1.5; color: var(--grey-1); }
  .rotate__b:empty { display: none; }

  /* THE PHONE, DRAWN, AND IT TURNS. Two hairlines of the same rule the whole site is
     drawn with — an outline and a bar for the screen — rotated a quarter turn on a
     loop. It says "turn this" without a word, which matters here more than anywhere
     else on the site: this is the one screen a visitor may reach before knowing what
     language it speaks.
     Motion, because a static phone icon reads as an error badge. Held still for
     anyone who asked for that. */
  .rotate__ph {
    width: 34px; height: 56px; border: 1px solid var(--ink); border-radius: 3px;
    display: grid; place-items: end center; padding-bottom: 5px;
    animation: rot-turn 2.6s var(--ease) infinite;
  }
  .rotate__ph b { display: block; width: 12px; height: 1px; background: var(--ink); }
  @keyframes rot-turn {
    0%, 22%   { transform: rotate(0deg); }
    45%, 78%  { transform: rotate(-90deg); }
    100%      { transform: rotate(0deg); }
  }
  @media (prefers-reduced-motion: reduce) {
    .rotate__ph { animation: none; transform: rotate(-90deg); }
  }

  /* ================= THE ENTRY SEQUENCE =================
     Ported from the editorial site. The redaction rules below are that site's,
     unchanged — they are a per-WORD marker wipe: a ::after bar that scales to zero
     from the left while the word's own colour comes back from transparent. No blur,
     no clip-path, no transform on the glyphs, so nothing reflows as words clear.
     What is NOT ported: the 420vh rail and the position:sticky stage. Both need a
     scrolling document and there is none here — see the note at the top of seq.js. */
  /* 100dvh, AND THE SAME REASON .viewport CARRIES IT. A fixed element's containing
     block is the initial containing block, which on a phone is the LARGE viewport —
     so inset:0 makes this lid as tall as the screen would be with the URL bar
     retracted, and the transport docked at its bottom edge lives under that bar.
     Nothing scrolls it back: the lid is the whole window. */
  .seq {
    position: fixed; inset: 0; bottom: auto; z-index: 100;
    height: 100%; height: 100dvh;
    background: var(--phantom);
    display: grid; place-items: center;
    padding: 0 clamp(24px, 8vw, 160px);
    opacity: 1; transition: opacity .55s var(--ease);
  }
  .seq[hidden] { display: none; }
  .seq.is-out { opacity: 0; pointer-events: none; }

  /* The film sits UNDER the copy and both are absolutely placed, so the handover is
     two opacity transitions crossing rather than one element being replaced. */
  .seq__film {
    position: absolute; inset: 0; z-index: 0;
    opacity: 0; transition: opacity .9s var(--ease);
  }
  .seq.is-film .seq__film { opacity: 1; }
  .seq__film video { width: 100%; height: 100%; object-fit: cover; display: block;
                     filter: saturate(var(--img-sat)) contrast(1.04) brightness(.7); }

  .seq__text {
    position: relative; z-index: 4; max-width: 46ch;
    font-size: clamp(20px, 2.9vw, 42px); font-weight: var(--w-reg);
    line-height: 1.24; letter-spacing: .005em; color: var(--ink);
    white-space: pre-wrap;            /* the tokeniser keeps the separators as text */
    transition: opacity .46s var(--ease);
  }
  .seq.is-film .seq__text { opacity: 0; }

  /* ---- the scroll marker: an arrow, built out of pixels ----
     OUT OF THE FLOW, and that is the fix for a bug I put in. .seq is a grid with
     place-items:center and ONE item in it — the copy — which is why the copy sits in
     the middle of the window. Adding the marker as a second grid item opened a second
     ROW, so the two shared the height and the text was pushed up off centre. It is
     absolutely positioned now: the copy is the only thing in the grid again.

     CENTRED AT THE BOTTOM. Not on the text's left edge, which was the first attempt —
     the mark is about the whole page answering the wheel, not about that paragraph.

     AN ARROW, AND IT DOES NOT MOVE. Five squares laid out as a chevron pointing down,
     on a 5 x 3 lattice of the same 6px cell:

         X . . . X
         . X . X .
         . . X . .

     No pulse and no accent. A blinking coloured mark is the loudest thing on a page
     whose whole surface is one ink and one ground, and it was reading as an alert
     rather than as a hint. The shape says the direction; it does not need to flash to
     say it. The accent on this site answers "where am I" and nothing else. */
  .seq__scroll {
    position: absolute; z-index: 4;
    left: 50%; transform: translateX(-50%);
    bottom: clamp(26px, 5vh, 64px);
    display: grid;
    grid-template-columns: repeat(5, 6px);
    grid-template-rows: repeat(3, 6px);
    gap: 3px;
    transition: opacity .45s var(--ease);
  }
  .seq__scroll i { display: block; background: rgb(var(--ink-rgb) / .38); }
  /* Authored placements, not a formula: it is a five-pixel glyph. */
  .seq__scroll i:nth-child(1) { grid-area: 1 / 1; }
  .seq__scroll i:nth-child(2) { grid-area: 2 / 2; }
  .seq__scroll i:nth-child(3) { grid-area: 3 / 3; }
  .seq__scroll i:nth-child(4) { grid-area: 2 / 4; }
  .seq__scroll i:nth-child(5) { grid-area: 1 / 5; }
  /* The film has the screen: the copy is gone and so is the instruction about it. And
     the player's own bar is down there now, so the two would sit on each other. */
  .seq.is-film .seq__scroll { opacity: 0; }

  .seq__w { position: relative; }
  /* THE AUTHOR'S BOLD. One class on the word's own span — see the note in
     seq.js for why it cannot be a <b>. --w-med is the weight every other title on
     this site is set in, so a bolded word in the entry copy reads as the same
     emphasis it would have anywhere else rather than as the browser's 700. */
  .seq__w--bold { font-weight: var(--w-med); }
  .seq__w--redacted { color: transparent; transition: color .22s var(--ease) .06s; }
  /* var(--ink), NOT currentColor: the span's own colour goes transparent and would
     take the bar with it. */
  .seq__w--redacted::after {
    content: ''; position: absolute; left: -.06em; right: -.06em; top: .04em; bottom: .12em;
    background: var(--ink); transform-origin: left center;
    transition: transform .32s var(--ease);
  }
  .seq__w--redacted.is-clear { color: inherit; transition-delay: 0ms; }
  .seq__w--redacted.is-clear::after { transform: scaleX(0); }

  /* ---- the sequence's player ----
     THREE RULES, and that is the whole point. The controls are the film panel's own
     — .vp__scrub, .vp__played, .vp__play, .vp__mute, .vp__now — so every style they
     need already exists a few hundred lines up. What is left is undoing the one part
     of .vp.is-open that does not apply here: that class exists because the panel
     RE-PARENTS itself to <body> and takes the whole window, and this bar does not
     move anywhere. It only borrows is-open to get past `.vp__bar { display: none }`.

     Placed on the stage rather than over a video element of its own, above both the
     film and the copy, and it stays put while those two trade opacity underneath. */
  /* AT THE BOTTOM, where a player's controls belong. It was briefly docked at the top
     on the argument that this site keeps its chrome up there — but the chrome up there
     is the MENU, and a film's transport is not chrome. Every player anyone has ever
     used puts it under the picture.
     Half a module tall: the smallest unit the plane is measured in, and a transport is
     not content, so it costs the grid a half step. At module 88 that is 44px and the
     panel's own measurements already fit it — the scrub's 16px hit area, 4px, a 22px
     button row. */
  /* .seq__player.vp.is-open, THREE CLASSES, and it has to be: the borrowed is-open
     brings `position: fixed; inset: 0; background: #000` with it at specificity
     (0,2,0), which beat a plain .seq__player and pinned this to the top-left of the
     WINDOW — measured at y=0 while the rule below said bottom. Borrowing a class
     means inheriting its weight, so the override has to outweigh it. */
  /* MIN-HEIGHT, NOT HEIGHT, and that is the reported fault. Half a module was
     measured against module 88 — 44px for a 42px row, "2px to spare" — and the
     module is not 88 any more: it follows the WIDTH now, so a 1280px window gives
     72 and the box came out 36 for a row that still needs 41. The scrub's hit area
     is 16px and the button row 22, both fixed; only the little margin between them
     scales. A flex column centred on an overflow spills at BOTH ends, so the scrub
     rose into the picture and Skip's bottom edge went one pixel under the window.
     Measured: box 36, content 41, Skip's bottom at 721 in a 720px window.
     The floor keeps the grid's intent where the module is generous; the content
     keeps the transport whole where it is not. */
  .seq__player.vp.is-open {
    position: absolute; inset: auto 0 0 0; z-index: 5;
    height: auto; min-height: calc(var(--m) * .5);
    background: none; animation: none;
  }
  .seq__player .vp__bar {
    position: static; min-height: 100%;
    /* Block padding as well as inline, so the row never sits flush on the window's
       edge — it is what turns "just fits" into "is inside". ON THE MODULE, because
       a flat 8 and 10 made the whole transport 65px, and 65px of chrome over a
       287px landscape screen is a fifth of the picture. At module 88 this is 7 and
       9; at 52 it is 4 and 5, and the scrim thins with the screen it is on. */
    padding: calc(var(--m) * .08) clamp(16px, 3vw, 40px) calc(var(--m) * .105);
    display: flex; flex-direction: column; justify-content: center;
    background: linear-gradient(to top, rgb(0 0 0 / .7), rgb(0 0 0 / 0));
  }
  /* The panel's row is spaced for a full-window overlay, and in half a module it does
     not fit: measured at 16 + 6 + 29 = 51px in a 44px box, seven over, and a flex
     column centred on overflow spills at BOTH ends — the scrub would have hung above
     the window edge. The row's height was set by Skip's 8px of vertical padding, not
     by the buttons, so that is what goes: 16 + 4 + 22 = 42, with 2px to spare. */
  .seq__player .vp__row { gap: calc(var(--m) * .16); margin-top: calc(var(--m) * .045); }
  /* SKIP KEEPS ITS PRESS AREA. Its 8px of vertical padding was taken away to make
     the row fit a box that could not hold it — the box is content-sized now, so the
     padding comes back rather than the way out staying an 11px line of type to aim
     at with a thumb. Owner's word: "skip button rein". */
  .seq__player .seq__skip { padding: calc(var(--m) * .07) 0; }

  /* WITH THE FILM. Before FILM_ON there is no duration to show — preload is "none",
     the file being tens of megabytes — and nothing to seek. A player interface over
     type that has not reached the film yet is a control that lies about being usable.
     (This said 38 MB and index.php said 109; the film is content and its size moves,
     so neither comment names one now.) */
  .seq__player { opacity: 0; pointer-events: none; transition: opacity .6s var(--ease); }
  .seq.is-film .seq__player { opacity: 1; pointer-events: auto; }

  /* WITHOUT THE FILM there is nothing to wait for. .is-film is set by playing(),
     which never runs when the page carries no <video> — so the rule above would
     hold this row at opacity 0 for the whole sequence, and Skip lives in it. The
     transport is not rendered in that case (see index.php), so what shows here is
     the way out and nothing else. */
  .seq--nofilm .seq__player { opacity: 1; pointer-events: auto; }

  .seq__scrub:focus-visible, .vp__scrub:focus-visible { outline: 1px solid var(--phx); outline-offset: 4px; }

  /* Skip rides in the player's own row now, so it needs no position of its own —
     .vp__spacer pushes it to the right end. */
  .seq__skip {
    flex: none;
    font: inherit; font-size: 11px; letter-spacing: .2em; text-transform: uppercase;
    background: none; border: 0; padding: 8px 0; color: var(--grey-1); cursor: pointer;
    transition: color .25s var(--ease);
  }
  .seq__skip:hover { color: var(--phx); }

  /* The bar and the plane are inert while the lid is on. */
  body.is-seq .nav { opacity: 0; pointer-events: none; }

  /* Reduced motion never sees it — seq.js does not start, and this is the belt: if
     it somehow did, the words stand clear and the bars are not drawn. */
  @media (prefers-reduced-motion: reduce) {
    .seq__w--redacted { color: inherit; }
    .seq__w--redacted::after { display: none; }
  }

  /* ================= NO SCRIPT =================
     The one block that exists only when the script does not. It sits above the
     clipped plane and reads as the page's own type — a browser default sans-serif
     on white here would look like an error page, which is the opposite of the
     point. Fixed rather than in flow: .viewport is fixed and would cover it. */
  .noscript {
    position: fixed; inset: auto 0 0 0; z-index: 300;
    background: var(--phantom); border-top: 1px solid var(--line);
    padding: calc(var(--m) * .4) clamp(18px, 4vw, 48px);
    display: flex; flex-direction: column; gap: .8em;
  }
  .noscript p { font-size: 15.5px; line-height: 1.55; color: var(--grey-2); max-width: 74ch; }
  .noscript b { font-weight: var(--w-med); color: var(--ink); }
  .noscript a { color: var(--phx); }
