/* defense.css - the Defense page, and nothing else.

   A separate stylesheet rather than another layer appended to hero.css, which
   is already five passes of overrides deep. Every rule here is scoped to
   body.pg-def or to a .dx class, so loading this file changes no other page and
   removing one <link> removes the whole design.

   THE RULES, AND THEY OUTRANK THE REFERENCE BOARDS.

   1. No type on a picture. Ever. A photograph or a film is a plate with nothing
      written on it, and the words live on black above or below it.
   2. No overlay. Nothing is laid on top of anything else.
   3. No small text and no grey text. Nothing is set below 1rem and nothing is
      set at a percentage of white: a 22 percent white hairline on black is a
      grey line, and this page has neither.
   4. Almost no drawn lines at all. There are no rules between sections, none
      above the rows, none under the opening and none across the foot. The space
      does the dividing.
   5. Two inks. White carries the display type, red carries the run that sits
      beside it and every figure. There is no third colour in a word.

   No em dashes and no middle dots in rendered text, per the house rule. */

/* ============================================================ THE FACE
   Chakra Petch went. It is an angular display face with squared terminals and
   it reads as games rather than as defense: the squared cut is decoration, and
   at display size it is the loudest thing on a page that is otherwise silent.

   IBM Plex Sans Condensed replaces it, self hosted, latin only, about 14KB a
   weight. It is a working grotesque drawn for technical documentation, and
   condensing it does two things at once: it reads as engineering rather than as
   styling, and it sets the same words narrower, which is what makes the page
   feel smaller without losing the scale of the lines.

   IBM Plex Mono carries anything that reads as a control or a readout.

   Scoped to body.pg-def by re-pointing --angular, so every rule that already
   asked for the display face picks this up and no other page changes. */
body.pg-def{
  --dxbar:clamp(58px,5vw,72px);
  --dxpad:clamp(18px,3.4vw,58px);
  --dxred:#E0362E;
  --dxdeep:#1E4FB0;
  --dxease:cubic-bezier(.16,.84,.3,1);
  background:#000;color:#fff}

body.pg-def main{display:block;background:#000}
body.pg-def section{background:#000;position:relative}

/* One hard rule, applied to the page rather than run by run: everything that
   renders a word renders it in white unless it is named below. site.css, os.css
   and hero.css between them set body copy at 42, 72, 78, 88, 90 and 92 percent
   white, and on a black ground every one of those is a different grey. */
body.pg-def,body.pg-def p,body.pg-def h1,body.pg-def h2,body.pg-def h3,
body.pg-def span,body.pg-def a,body.pg-def li,body.pg-def b,body.pg-def i,
body.pg-def small,body.pg-def figcaption{color:#fff}

/* The second and third inks. A run is red when it sits beside a display line
   rather than being one, or when it is a figure; blue when it names the thing
   the page is about. The glyph spans have to be named too: the uncover splits
   a run into one span per character, and those spans are caught by the blanket
   rule above, which outranks a bare class on their parent. */
body.pg-def .dxred,body.pg-def .dxred span{color:var(--dxred)}
/* NOTE for anything added later: the blanket rule above is a tag qualified by a
   class, so a bare .someclass{color:...} on a <span> LOSES to it and renders
   white. Every accent rule below is written body.pg-def .someclass for that
   reason. Three separate runs of numbers shipped white before this was
   written down. */

/* Two type registers and no more: the angular face at display size for
   anything that is a name, and the same face one step down for the line that
   qualifies it. */
.dxsub{font-family:var(--angular);font-weight:500;
  font-size:clamp(1.05rem,1.6vw,1.6rem);line-height:1.3;letter-spacing:0;margin:0}


/* ============================================================ THE OPENING
   Four lines that overlap and step across the measure, and nothing behind
   them. The board this is taken from sets the same composition on a blue field
   with a panel of small print laid over it; both are gone, because a field
   behind the words is a picture under the words. */
.dxhero{min-height:100svh;
  padding:calc(var(--dxbar) + clamp(20px,4vh,54px)) var(--dxpad) clamp(24px,4vh,50px);
  display:flex;flex-direction:column}
.dxhero__in{flex:1;display:flex;flex-direction:column;justify-content:center}

.dxstack{margin:0}
/* Four lines and nothing under them, so the type takes the screen. Sized
   against the height as well as the width: at 10vw alone a short viewport gets
   a composition it cannot show. The steps across the measure came in when the
   size went up, because the longest line plus its indent plus the tracking it
   opens into has to stay inside the page. */
.dxstack__l{display:block;font-family:var(--angular);font-weight:600;
  font-size:clamp(2rem,min(8.4vw,15.5vh),9.4rem);line-height:.92;
  letter-spacing:-.018em;margin:0 0 -.09em;white-space:nowrap;
  margin-left:calc(var(--dxi,0) * 1%)}
.dxstack__l i{font-style:normal;font-variant-numeric:tabular-nums}
.dxstack__l:last-child{margin-bottom:0}

/* ============================================================ THE CLAIM */
.dxclaim{min-height:84svh;display:grid;place-items:center;
  padding:clamp(60px,12vh,150px) var(--dxpad)}
.dxclaim__l{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.8rem,6.4vw,6rem);line-height:.96;letter-spacing:-.012em;
  text-align:center;max-width:18ch;margin:0}
.dxclaim__l > span,.dxend__l > span{display:block}


/* ============================================================ THE POSTER
   Type on black, then a plate with nothing written on it. */
.dxduo{padding:0}
/* centred, so the page runs left only at the opening and is centred from the
   claim down */
.dxduo__top{padding:clamp(56px,10vh,140px) var(--dxpad) clamp(34px,5vh,66px);
  text-align:center}
.dxduo__t,.dxduo__do{margin-inline:auto}
.dxduo__t{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.6rem,5.2vw,4.9rem);line-height:.98;letter-spacing:-.01em;
  text-transform:uppercase;margin:0;max-width:22ch}
body.pg-def .dxduo__plus{display:block;margin:clamp(18px,2.6vh,38px) 0;
  font-family:var(--angular);font-weight:600;
  font-size:clamp(1.2rem,2.8vw,2.5rem);line-height:1;color:var(--dxred)}
.dxduo__do{margin:clamp(20px,3vh,44px) 0 0;font-family:var(--angular);
  font-weight:600;font-size:clamp(1.6rem,5.2vw,4.9rem);line-height:.98;
  letter-spacing:-.01em;text-transform:uppercase}
/* a colour photograph reduced to two inks. Isolated, or the luminosity blend
   escalates to the root and takes the fixed menu panel with it. */
.dxduo__plate{position:relative;height:min(62svh,620px);overflow:hidden;
  isolation:isolate;background:var(--dxdeep)}
.dxduo__plate img{position:absolute;left:0;top:-10%;width:100%;height:120%;
  object-fit:cover;mix-blend-mode:luminosity;
  filter:contrast(1.34) brightness(.45);will-change:transform}


/* ============================================================ THE SHEET
   A title, one line beside it, a flat block next to a photograph, then the
   nameplate. The reference prints corner telemetry across its photograph, rules
   over its blocks and a letterspaced nation strip across the foot. None of
   those are here. */
.dxboard{padding:clamp(60px,10vh,150px) var(--dxpad) clamp(50px,8vh,110px)}
.dxboard__head{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.3fr);
  gap:clamp(20px,4vw,80px);align-items:start;padding-bottom:clamp(30px,4.6vh,64px)}
.dxboard__title{font-family:var(--angular);font-weight:600;
  font-size:clamp(2rem,4.6vw,4.4rem);line-height:.96;letter-spacing:-.018em;margin:0}
.dxboard__head .dxsub{font-size:clamp(1.05rem,1.7vw,1.7rem);max-width:30ch;padding-top:.4em}

.dxboard__plates{display:grid;grid-template-columns:minmax(0,.72fr) minmax(0,1.28fr);
  gap:clamp(14px,2.2vw,34px);align-items:stretch}
.dxflat{background:var(--dxred);position:relative;overflow:hidden;
  min-height:clamp(240px,42vh,470px);display:grid;place-items:center}
.dxflat svg{height:86%;width:auto;max-width:76%;display:block}
.dxshot{position:relative;overflow:hidden;background:#050505;
  min-height:clamp(240px,42vh,470px)}
.dxshot img{position:absolute;left:0;top:-9%;width:100%;height:118%;
  object-fit:cover;filter:grayscale(1) contrast(1.12) brightness(.8);
  will-change:transform}

.dxplate2{padding:clamp(34px,5.4vh,80px) 0 0}
.dxplate2 > span{display:block;font-family:var(--angular);font-weight:600;
  font-size:clamp(2.4rem,8vw,8rem);line-height:.9;letter-spacing:-.03em}


/* ============================================================ THE PICTURE */
.dxpic{padding:clamp(56px,9vh,130px) var(--dxpad) clamp(60px,10vh,140px)}
.dxpic__h{font-family:var(--angular);font-weight:600;
  font-size:clamp(2.4rem,11vw,10.5rem);line-height:.84;letter-spacing:-.034em;
  text-transform:uppercase;margin:0 0 clamp(22px,3.4vh,48px)}
.dxscene{position:relative;overflow:hidden;background:#04070b;aspect-ratio:16/8}
.dxscene video,.dxscene img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;filter:grayscale(.85) brightness(.72) contrast(1.1)}

.dxthree{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(20px,3.4vw,70px);margin-top:clamp(32px,4.8vh,70px)}
.dxthree__c{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:clamp(10px,1.4vw,22px);align-items:start}
body.pg-def .dxthree__n{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.5rem,2.6vw,2.6rem);line-height:.9;
  font-variant-numeric:tabular-nums;color:var(--dxred)}
.dxthree h3{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.3rem,2.2vw,2.2rem);line-height:1.02;letter-spacing:-.01em;
  margin:0 0 clamp(8px,1vw,14px)}


/* ============================================================ THE SYSTEM
   The reference board this section is modelled on runs a numbered index down
   the page, one statement under each number and a short block of prose under
   that. The numbers are the only thing on this page that reads as furniture,
   and they earn it: they are what makes a long page navigable without a rule
   between every section. */
.dxsec{padding:clamp(60px,10vh,150px) var(--dxpad)}
body.pg-def .dxsec__n{display:block;font-family:var(--angular);font-weight:600;
  font-size:clamp(1.1rem,1.8vw,1.8rem);line-height:1;color:var(--dxred);
  margin:0 0 clamp(16px,2.4vh,34px)}
.dxsec__h{font-family:var(--angular);font-weight:600;
  font-size:clamp(2rem,5.6vw,5.4rem);line-height:.94;letter-spacing:-.026em;
  margin:0;max-width:18ch}
.dxsec__lede{margin:clamp(22px,3.4vh,48px) 0 0;font-family:var(--angular);
  font-weight:500;font-size:clamp(1.05rem,1.7vw,1.7rem);line-height:1.34;
  max-width:52ch}

/* the numbered index of what the system finds */
.dxfind{margin-top:clamp(46px,7vh,110px);
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(34px,5vw,80px) clamp(30px,5vw,90px)}
.dxfind__i{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:clamp(14px,1.8vw,28px);align-items:start}
body.pg-def .dxfind__n{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.05rem,1.5vw,1.5rem);line-height:1.1;
  font-variant-numeric:tabular-nums;color:var(--dxred)}
.dxfind__i h3{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.4rem,2.4vw,2.4rem);line-height:1.02;letter-spacing:-.018em;
  margin:0 0 clamp(10px,1.4vh,18px)}
.dxfind__i p{font-family:var(--angular);font-weight:500;
  font-size:clamp(1.05rem,1.5vw,1.5rem);line-height:1.32;margin:0;max-width:34ch}


/* ============================================================ THE DETECTOR
   The one place on this page where type sits on a picture, and it is there
   because it is not copy: every label is a readout of the box it is attached
   to, printed the way a tracker prints it. Drawing it anywhere but on the frame
   would break the only thing it is for, which is showing you what the model
   found and where. */
.dxdet{position:relative;overflow:hidden;background:#04070b;aspect-ratio:16/9;
  margin-top:clamp(40px,6vh,90px)}
.dxdet video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:grayscale(.72) brightness(.8) contrast(1.06)}
.dxdet__svg{position:absolute;inset:0;width:100%;height:100%;z-index:2}
.dxdet__fill{fill:#fff;fill-opacity:.05}
.dxdet__br{fill:none;stroke:#fff;stroke-width:calc(var(--hud,1) * 2px)}
.dxdet__dot{fill:var(--dxred)}
.dxdet__t1{fill:#fff;font-family:var(--angular);font-weight:600;
  font-size:calc(var(--hud,1) * 15px);letter-spacing:.02em}
.dxdet__t2{fill:#E0362E;font-family:var(--angular);font-weight:500;
  font-size:calc(var(--hud,1) * 12.5px);letter-spacing:.02em}
/* the frame's own corner marks and centre cross, drawn once */
.dxdet__frame{position:absolute;inset:clamp(10px,1.4vw,22px);z-index:3;
  pointer-events:none}
.dxdet__frame i{position:absolute;width:clamp(22px,2.2vw,34px);height:clamp(22px,2.2vw,34px);
  border:2px solid #fff;opacity:.9}
.dxdet__frame i:nth-child(1){top:0;left:0;border-right:0;border-bottom:0}
.dxdet__frame i:nth-child(2){top:0;right:0;border-left:0;border-bottom:0}
.dxdet__frame i:nth-child(3){bottom:0;left:0;border-right:0;border-top:0}
.dxdet__frame i:nth-child(4){bottom:0;right:0;border-left:0;border-top:0}
/* The live index of what is on screen this frame, and it sits UNDER the plate
   rather than in its top corner. In the corner it collided with the label on
   any track whose box reached the top left of the frame, which on an aerial
   pass is most of them: two runs of white type in the same place, both moving,
   neither readable. */
.dxdet__list{display:flex;flex-wrap:wrap;gap:4px clamp(20px,2.6vw,42px);
  margin-top:clamp(14px,2vh,26px);align-items:baseline}
.dxdet__list span{display:inline-flex;align-items:baseline;white-space:nowrap}
.dxdet__list b{font-family:var(--angular);font-weight:600;
  font-size:clamp(1rem,1.25vw,1.25rem);line-height:1.4;color:#fff}
body.pg-def .dxdet__list i{font-style:normal;font-family:var(--angular);font-weight:500;
  font-variant-numeric:tabular-nums;margin-left:.6em;
  font-size:clamp(1rem,1.25vw,1.25rem);line-height:1.4;color:var(--dxred)}
.dxdet__cap{margin-top:clamp(16px,2.4vh,30px);font-family:var(--angular);
  font-weight:500;font-size:clamp(1rem,1.25vw,1.25rem);line-height:1.42;
  max-width:60ch}


/* ============================================================ HOW IT ARRIVES */
.dxstep{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(34px,5vw,86px);margin-top:clamp(46px,7vh,110px)}
.dxstep__i h3{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.5rem,2.6vw,2.6rem);line-height:1;letter-spacing:-.018em;
  margin:clamp(12px,1.6vh,22px) 0 clamp(12px,1.6vh,20px)}
.dxstep__i p{font-family:var(--angular);font-weight:500;
  font-size:clamp(1.05rem,1.5vw,1.5rem);line-height:1.32;margin:0;max-width:32ch}
body.pg-def .dxstep__n{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.05rem,1.5vw,1.5rem);line-height:1;color:var(--dxred);
  font-variant-numeric:tabular-nums}


/* ============================================================ THE ALLIES */
.dxallies{padding:clamp(56px,9vh,130px) var(--dxpad);text-align:center}
.dxallies__h{margin-inline:auto;font-family:var(--angular);font-weight:600;
  /* One line at every width, so it is sized to fit rather than to wrap.
     Measured, not estimated: this run costs .45em of width per character, so
     twenty four characters is 10.8em, and at 8vw of a 1440 measure that is
     1244px inside a 1324px page. The first guess used .4em, which put the line
     65px past the right edge and gave the whole document a horizontal scroll. */
  font-size:clamp(1.3rem,7vw,6.8rem);line-height:1;letter-spacing:-.012em;
  margin:0;white-space:nowrap}
/* centred, and wrapping rather than filling a track grid: a fixed column count
   leaves a ragged last row hanging off the left, which reads as a mistake when
   everything above it is centred */
/* Held in from the page edges so the rows come out near enough the same
   length. Left at full width the first row ran the whole measure and the second
   was half of one, which under a centred heading reads as a mistake. */
.dxgrid{margin:clamp(36px,5.4vh,80px) auto 0;max-width:min(1080px,100%);
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(16px,2vw,30px) clamp(12px,1.6vw,24px)}
.dxflag{display:flex;align-items:center;justify-content:center;
  transition:opacity .5s var(--dxease),transform .5s var(--dxease)}
[data-dx] .dxgrid .dxflag{opacity:0;transform:translate3d(0,16px,0)}
[data-dx] .dxgrid.is-on .dxflag{opacity:1;transform:none}
.dxflag:hover{transform:translate3d(0,-3px,0)}
/* the only drawn line left on the page, and it belongs to the object rather
   than to the layout: a white flag with no edge floats off a black ground */
.dxflag svg{width:clamp(30px,3vw,40px);height:auto;display:block;
  box-shadow:0 0 0 1px #fff}
.dxsprite{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}


/* ============================================================ THE CLOSE */
.dxend{padding:clamp(60px,12vh,170px) var(--dxpad) clamp(44px,6vh,86px);
  text-align:center}
.dxend__l{font-family:var(--angular);font-weight:600;
  font-size:clamp(1.9rem,6.8vw,6.4rem);line-height:.94;letter-spacing:-.014em;
  margin:0 auto clamp(30px,4.4vh,60px);max-width:17ch}
.dxcta{display:inline-flex;align-items:center;
  font-family:var(--angular);font-weight:600;text-transform:uppercase;
  font-size:clamp(.94rem,1.06vw,1.06rem);letter-spacing:.16em;
  padding:19px 34px;border:1px solid #fff;color:#fff;
  transition:background .34s var(--dxease),color .34s var(--dxease)}
.dxcta:hover{background:#fff;color:#000}


/* ============================================================ MOTION
   The scroll owns the type. --t is one glyph's own progress through the
   uncover, written by defense.js every frame it changes: the glyph is clipped
   to nothing and parked half a line below itself at 0, and sitting on its own
   baseline in full at 1. The sweep left to right comes from each glyph getting
   a later share of the run's travel, not from a transition, which is why it
   runs backwards when you scroll back up.

   There is no transition on any of it. A per frame write against a transition
   is a smear that lags most of a second behind your hand. */
[data-dx] [data-dx-uncover]:not(.dxintro) .dxc{display:inline-block;
  clip-path:inset(-26% 0 calc((1 - var(--t,0)) * 118%) 0);
  transform:translate3d(0,calc((1 - var(--t,0)) * .46em),0);
  will-change:clip-path,transform}

/* The same gesture on a timer rather than on the scroll, for the runs that are
   already on screen when the page opens.

   A keyframe animation and not a transition. A transition needs the browser to
   have painted the from state at least once, and these glyphs are built by
   script and given their class in the same turn, so the from and the to state
   landed in one frame and every delay in the sequence was ignored: the opening
   arrived finished. An animation plays from its own first keyframe whatever was
   on screen before it, and `both` holds that keyframe through the delay. */
[data-dx] .dxintro .dxc{display:inline-block;
  clip-path:inset(-26% 0 118% 0);transform:translate3d(0,.46em,0)}
[data-dx][data-booted] .dxintro .dxc{animation:dxwrite .6s var(--dxease) both}
@keyframes dxwrite{
  from{clip-path:inset(-26% 0 118% 0);transform:translate3d(0,.46em,0)}
  to{clip-path:inset(-26% 0 -26% 0);transform:none}}
/* The figure is carried through the split untouched, so it is not written on
   with the glyphs around it. Left alone it sat on an empty line rerolling by
   itself before its own words had arrived. It waits for them. */
[data-dx] .dxintro [data-dx-roll]{opacity:0}
[data-dx][data-booted] .dxintro [data-dx-roll]{animation:dxfade .45s linear both}
@keyframes dxfade{from{opacity:0}to{opacity:1}}
.dxw{display:inline-block}

[data-dx] .dxlift{opacity:0;transform:translate3d(0,22px,0);
  transition:opacity .9s var(--dxease),transform .9s var(--dxease)}
[data-dx] .is-on .dxlift,[data-dx] .dxlift.is-on{opacity:1;transform:none}

/* The plates open rather than fade, and the wipe is a curtain drawn over the
   plate rather than a clip on the plate itself: an element clipped to zero
   height has no intersection rect, so the observer watching it for arrival
   never fires and the plate stays black at every scroll position. */
[data-dx] .dxopen{position:relative}
[data-dx] .dxopen::before{content:"";position:absolute;inset:0;z-index:9;
  background:#000;transform-origin:50% 100%;
  transition:transform 1.25s var(--dxease)}
[data-dx] .dxopen.is-on::before{transform:scaleY(0)}

/* The opening writes on one glyph at a time, line after line. It used to be a
   clip over each whole line, which arrives as four blocks rather than as
   writing. The per line offset is data-dx-delay in the markup. */

@media (prefers-reduced-motion:reduce){
  [data-dx] .dxopen::before{display:none}
  [data-dx] .dxgrid .dxflag{opacity:1;transform:none}
  [data-dx] .dxlift,[data-dx] .dxopen,
  [data-dx] [data-dx-uncover] .dxc,[data-dx] .dxintro .dxc{
    opacity:1;transform:none;clip-path:none;transition:none;animation:none}
}


/* ============================================================ NARROW */
@media (max-width:900px){
  .dxstack__l{margin-left:0;font-size:clamp(1.5rem,8.6vw,3.2rem)}
  .dxfind,.dxstep{grid-template-columns:1fr;gap:clamp(28px,5vh,44px)}
  .dxsec__h{max-width:none}
  .dxdet{aspect-ratio:4/3}

  .dxboard__head{grid-template-columns:1fr;gap:22px}
  .dxboard__head .dxsub{padding-top:0}
  .dxboard__plates{grid-template-columns:1fr}
  .dxflat{min-height:230px}
  .dxshot{min-height:260px}
  .dxthree{grid-template-columns:1fr;gap:26px}
  .dxscene{aspect-ratio:4/3}
  .dxcta{min-height:52px}
}
@media (max-width:640px){
  .dxduo__plate{height:46svh}
  .dxflag svg{width:32px}
}
