/* observant.computer — night-watch print design */

/* The two faces, served from this origin. Google Fonts blocks the first
   paint until its stylesheet answers, so a network that accepts the
   connection and then stalls left the reader on a white screen. Serving
   them here also keeps the reader's address off a third party. Latin
   subset only; this page is English, and regular weight only, because
   nothing on the page asks for a heavier one.

   The eight hex characters in each file name are the first eight of the
   file's own sha256. nginx sends these three files with a one year
   immutable Cache-Control, so a returning reader never revalidates them;
   without the hash a replaced face would never reach that reader. Replace
   a face and the name changes with it, which is a new URL and a new fetch.
   Recompute with `sha256sum` and edit this file and the preloads in
   index.html together. */
@font-face{
  font-family:'Instrument Serif';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/instrument-serif-400.60c06664.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Instrument Serif';
  font-style:italic;
  font-weight:400;
  font-display:swap;
  src:url('fonts/instrument-serif-400-italic.6ee678c3.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/ibm-plex-mono-400.c36f509c.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  --paper:#F4EDD9;
  --paper-deep:#EAE0C4;
  --cobalt:#1632B5;
  --cobalt-deep:#0F2387;
  --ink:#0D1020;
  --serif:"Instrument Serif", Georgia, "Times New Roman", serif;
  --mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Three body sizes, not five. hero-sub and section-sub shared one clamp;
     step p and plate li shared one fixed size; every small note (section
     note, fineprint, the legal date, the footer note) already agreed on
     12.5px and now says so once. */
  --text-lg:clamp(15px,1.3vw,17px);
  --text-md:15px;
  --text-sm:12.5px;
  /* Two vertical rhythms, not five. The sections read 110/120/90/110/120;
     110 and 120 were never a deliberate pair, so one rhythm now covers
     both and the ephemeral strip keeps the one genuinely tighter measure. */
  --rhythm:112px;
  --rhythm-tight:90px;
  /* Tone, as colour, not opacity. `.js .reveal.in{opacity:1}` owns the
     fade-in and wins any specificity fight over a plain single-class rule,
     so an element that is both `.reveal` and toned by `opacity` lost its
     tone the moment it revealed. Two dim levels, ink on paper and paper on
     cobalt, so hierarchy survives the animation. */
  --ink-70:rgba(13,16,32,.70);
  --ink-80:rgba(13,16,32,.80);
  --paper-70:rgba(244,237,217,.70);
  --paper-80:rgba(244,237,217,.80);
}

*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--sans);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
a{color:inherit;text-decoration:none;}
:focus-visible{outline:2px solid var(--paper);outline-offset:3px;}
.paper :focus-visible,.paper-deep :focus-visible,footer :focus-visible,
#nav.scrolled :focus-visible,.scroll-hint:focus-visible{
  outline-color:var(--ink);
}
/* The hero has both grounds under it: cobalt sky and paper plain. A ring of
   each colour reads on both. */
.hero :focus-visible{
  outline-color:var(--ink);
  box-shadow:0 0 0 3px var(--paper);
}
.wrap{max-width:1120px;margin:0 auto;padding:0 28px;}
.narrow{max-width:780px;}
.center{text-align:center;}
/* inset-inline-start, not left. In a right-to-left document the inline
   start is the right edge, so left:-9999px became real scrollable overflow
   and the document measured 11279 pixels wide. */
.skip{
  position:absolute;inset-inline-start:-9999px;top:0;z-index:100;
  background:var(--paper);color:var(--ink);
  font-family:var(--mono);font-size:13px;padding:12px 18px;
}
.skip:focus{inset-inline-start:0;}

/* ---------- nav ---------- */
#nav{
  position:fixed;top:0;left:0;right:0;z-index:50;
  color:var(--paper);
  border-bottom:1px solid transparent;
  transition:background .25s ease,color .25s ease,border-color .25s ease;
}
.nav-inner{
  max-width:1120px;margin:0 auto;padding:16px 28px;
  display:flex;align-items:center;gap:26px;
}
.brand{
  display:inline-flex;align-items:center;gap:10px;min-height:24px;
  font-family:var(--serif);font-size:22px;line-height:1;white-space:nowrap;
}
.mark{width:20px;height:20px;flex:none;}
.tld{opacity:.62;}
.nav-links{
  display:flex;align-items:center;flex-wrap:wrap;
  margin-left:auto;gap:26px;font-size:14px;
}
.nav-links a{
  white-space:nowrap;opacity:.82;
  display:inline-flex;align-items:center;min-height:24px;
}
.nav-links a:hover{opacity:1;}
.nav-links a.nav-cta{opacity:1;}
.nav-cta{
  font-family:var(--mono);font-size:12.5px;letter-spacing:.02em;
  padding:8px 14px;border:1px solid currentColor;border-radius:2px;
  white-space:nowrap;
}
#nav.scrolled{
  background:rgba(244,237,217,.93);
  color:var(--ink);
  border-bottom-color:rgba(13,16,32,.14);
}

/* ---------- hero ---------- */
.hero{
  position:relative;
  min-height:100vh;
  min-height:100svh;
  color:var(--paper);
  overflow:hidden;
}
#sky{position:absolute;inset:0;width:100%;height:100%;display:block;}
/* The fallback picture reframes itself, so it holds at any window shape.
   The plain stretches, and the moon and the watcher keep their own size.
   The watcher stands on the crest, at the centre of the frame. */
.sky-fallback{
  position:absolute;inset:0;overflow:hidden;background:var(--cobalt);
  --fb-drop:0%;   /* how far the horizon drops on a tall screen */
  --ryl:818;      /* the ellipse radius, in viewBox units */
  --crest:80;     /* the height of the crest, as a percentage of the hero */
}
.fb-plain{
  position:absolute;inset:0;width:100%;height:100%;
  transform:translateY(var(--fb-drop));
}
/* The shader grows its radius on a tall screen, which flattens the plain.
   The ellipse follows, band by band. Its crest always sits at 80, so cy is
   always ry plus 80. These steps serve a reader with no JavaScript; when
   main.js runs it replaces them with the shader's own arithmetic. */
/* One number sets the crest for the plain and for the watcher, so the two
   cannot drift apart. The viewBox is 100 units tall and the element fills
   the hero, so one unit is one percent. */
.fb-plain ellipse{
  ry:calc(var(--ryl) * 1px);
  cy:calc((var(--ryl) + var(--crest)) * 1px);
}
.js .fb-plain{transform:none;}
.js .fb-plain ellipse{ry:var(--fb-ry,818px);cy:var(--fb-cy,898px);}
.js .fb-moon{
  left:var(--fb-moon-x,80%);height:var(--fb-moon-h,11%);
  display:var(--moon-display,block);
}
/* The element is 300 units tall and the person fills the top 100, so at a
   height of 18% the person is 6% of the hero, the shader's own S value.
   His feet sit a third of the way down the box. */
.js .fb-figure{
  left:var(--fb-fig-x,50%);top:var(--fb-fig-y,70%);height:18%;
}
/* The scroll hint is anchored to the hero, and the hero can be taller than
   the window. main.js hides it when the hero does not fit. */
.js .scroll-hint{display:var(--hint,inline-flex);}
/* The moon centre and the moon size match uMoon and uMR in main.js, so
   both heroes put the same moon in the same place. */
.fb-moon{
  position:absolute;left:80%;top:17.5%;
  height:11%;aspect-ratio:1;width:auto;
  transform:translate(-50%,-50%);
}
.fb-figure{
  position:absolute;left:50%;top:calc(var(--crest) * 1% + var(--fb-drop));
  height:18%;width:auto;
  transform:translate(-50%,-33.333%);
}
/* A middling laptop width. The aspect-ratio ladder above never fires here
   (the window is landscape, so the ladder treats it exactly like a wide
   desktop), but the headline keeps more of its width at 900-1200px than
   the moon's vw-scaled position assumes, and the crescent's halo reached
   under "infrastructure" at 1024px. Shrink the moon and move it right for
   this one width band; main.js's scene() carries the same two numbers, so
   the WebGL hero and this fallback agree. */
@media (min-width:900px) and (max-width:1200px) and (min-aspect-ratio:1/1){
  .fb-moon{left:83%;height:10%;}
}
/* A tall screen wants a lower horizon, the same as the WebGL hero. One
   number moves the plain and the watcher together. The ladder puts the
   crest at 80%, not the 73.5% the script picks. The script sets the crest
   from the measured copy; the ladder cannot measure, so it sits low enough
   that the watcher clears the button row at every window it serves. */
@media (max-aspect-ratio:1/1){
  .sky-fallback{--fb-drop:1.5%;--ryl:302;}
  .fb-moon{left:80.9%;height:10.58%;}
}
@media (max-aspect-ratio:7/8){
  .sky-fallback{--fb-drop:4.6%;--ryl:236;}
  .fb-moon{left:82.7%;height:10.02%;}
}
@media (max-aspect-ratio:3/4){
  .sky-fallback{--fb-drop:7.6%;--ryl:167;}
  .fb-moon{left:84.5%;height:9.46%;}
}
@media (max-aspect-ratio:5/8){
  .sky-fallback{--fb-drop:10.5%;--ryl:79;}
  .fb-moon{left:86.2%;height:8.14%;}
}
/* The hero is one centred column. The watcher stands on the crest at the
   same centre line, so the rings run out symmetrically under the text. */
.hero-inner{
  position:relative;z-index:2;width:100%;text-align:center;
  padding-top:max(15vh,135px);
  padding-bottom:230px;
}
h1{
  font-family:var(--serif);font-weight:400;
  font-size:clamp(40px,5.2vw,76px);
  line-height:1.0;letter-spacing:-.01em;
  max-width:26ch;margin:0 auto;
  text-wrap:balance;
}
h1 em{font-style:italic;}
.hero-sub{
  max-width:44ch;margin:24px auto 0;
  font-size:var(--text-lg);opacity:.88;
  text-wrap:balance;
}
.hero-cta{
  display:flex;gap:14px;flex-wrap:wrap;
  justify-content:center;margin-top:34px;
}
/* Equal widths, so the gap between the two buttons sits on the centre line
   under the watcher. Two labels of different length put it 40px off. The
   two share a row from 380px up; under 520px the halves are tight, so the
   type steps down to keep each label on one line. */
@media (min-width:380px){
  .hero-cta .btn{flex:1 1 0;max-width:236px;text-align:center;}
}
@media (min-width:380px) and (max-width:519px){
  .hero-cta .btn{font-size:11.5px;padding:13px 10px;}
}

/* ---------- buttons ---------- */
.btn{
  display:inline-block;font-family:var(--mono);font-size:13px;letter-spacing:.02em;
  padding:13px 22px;border-radius:2px;border:1px solid transparent;
  transition:transform .15s ease,background .15s ease,border-color .15s ease;
}
.btn:hover{transform:translateY(-1px);}
.btn-paper{background:var(--paper);color:var(--ink);border-color:rgba(13,16,32,.55);}
.btn-paper:hover{background:#FFF8E6;}
.btn-ghost{
  border-color:rgba(244,237,217,.62);color:var(--paper);
  background:rgba(15,35,135,.80);
}
.btn-ghost:hover{border-color:var(--paper);}
.scroll-hint{
  position:absolute;left:50%;bottom:20px;transform:translateX(-50%);
  z-index:2;color:var(--ink);opacity:.6;
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;
}
.scroll-hint svg{width:18px;height:18px;}

/* ---------- sections ---------- */
.section{padding:var(--rhythm) 0;}
/* Section content: a heading column and a prose column at desktop width, so
   the three prose sections (how, findings, ephemeral) commit to one
   composition instead of each drifting to its own left-aligned width. */
.section-grid > .wrap{display:grid;gap:32px;}
/* A grid track sizes to its content's min-width by default, and the install
   command's <pre> does not wrap. Without this the track grew to fit the
   command instead of scrolling it, and the whole page gained a horizontal
   scrollbar. */
.section-grid .section-head,.section-grid .section-body{min-width:0;}
@media (min-width:1024px){
  .section-grid > .wrap{grid-template-columns:minmax(220px,340px) 1fr;column-gap:64px;}
  .section-grid .section-head{padding-top:2px;}
  /* The product screenshot is the one artefact that outranks the 1fr body
     track: at 658px wide nothing inside it was readable. It spans both
     columns instead, so it gets the whole wrap. */
  #findings .product-shot{grid-column:1/-1;margin-top:8px;}
}
/* The paper sections carry a faint grain, so the hero's grained foot meets
   a matching ground instead of a flat one. The ::after sits under .wrap. */
.paper,.paper-deep{position:relative;color:var(--ink);}
.paper{background:var(--paper);}
.paper-deep{background:var(--paper-deep);}
.paper::after,.paper-deep::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.045;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.paper > .wrap,.paper-deep > .wrap{position:relative;z-index:1;}
.cobalt{
  position:relative;
  background:linear-gradient(180deg,var(--cobalt) 0%,var(--cobalt-deep) 100%);
  color:var(--paper);
}
.cobalt::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.08;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.cobalt > .wrap{position:relative;z-index:1;}

/* Still used by the legal pages' page-category label ("Privacy policy",
   "Terms of service"). The landing page's own four section eyebrows were
   deleted: the h2 in each section already carries the heading's weight. */
.label{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cobalt);margin-bottom:22px;
}
h2{
  font-family:var(--serif);font-weight:400;
  font-size:clamp(34px,4.6vw,58px);
  line-height:1.05;letter-spacing:-.01em;
  max-width:22ch;
}
.section-note{
  margin-top:10px;max-width:52ch;
  font-family:var(--mono);font-size:var(--text-sm);color:var(--ink-70);
}
.section-sub{
  margin-top:18px;max-width:52ch;
  font-size:var(--text-lg);color:var(--ink-80);
}
.cobalt .section-note{color:var(--paper-70);}
.cobalt .section-sub{color:var(--paper-80);}
.section-grid .section-sub{margin-top:0;}

/* ---------- steps ---------- */
.steps{list-style:none;margin-top:64px;}
.section-grid .steps{margin-top:0;}
.step{
  display:grid;grid-template-columns:90px 1fr;gap:24px;
  padding:38px 0;border-top:1px solid rgba(13,16,32,.18);
}
/* The install command's <pre> does not wrap, so without this the second
   track grew to its content width instead of letting the command scroll. */
.step > div{min-width:0;}
.step:last-child{border-bottom:1px solid rgba(13,16,32,.18);}
.step-no{
  font-family:var(--mono);font-size:13px;letter-spacing:.1em;
  color:var(--cobalt);padding-top:6px;
}
.step h3{
  font-family:var(--serif);font-weight:400;font-size:24px;
  margin-bottom:10px;
}
.step p{max-width:58ch;font-size:var(--text-md);opacity:.85;}

/* The install command. Real, copyable, in the same mono the rest of the
   page's code voice already uses. Copy sat inside the box at flex:none
   width and the token half of the command sat behind it, 45% hidden. The
   button now sits above the box instead of over the text, and the text
   wraps in place rather than running off the edge. */
.install-cmd{position:relative;margin-top:40px;}
.install-cmd pre{
  margin:0;padding:14px 16px;overflow-wrap:anywhere;
  border:1px solid rgba(13,16,32,.3);border-radius:2px;
  background:rgba(13,16,32,.04);
}
.install-cmd code{
  display:block;font-family:var(--mono);font-size:12.5px;color:var(--ink);
  white-space:pre-wrap;overflow-wrap:anywhere;
}
.install-cmd .copy-btn{
  position:absolute;right:0;bottom:calc(100% + 8px);
  font-family:var(--mono);font-size:11.5px;letter-spacing:.04em;
  padding:5px 12px;border:1px solid rgba(13,16,32,.3);border-radius:2px;
  background:rgba(13,16,32,.06);color:var(--ink);cursor:pointer;
}
.install-cmd .copy-btn:hover{background:rgba(13,16,32,.12);}

/* ---------- findings plate ---------- */
/* One real screenshot, not four re-typeset findings. A cream card with a
   bordered caption strip read as a browser chrome frame around the shot,
   not as a plate in the print. The frame belongs to the image alone; the
   caption sits below it, unframed, on the section's own cobalt ground. */
.product-shot img{
  display:block;width:100%;height:auto;
  border:1px solid var(--ink);border-radius:2px;
}
.product-shot figcaption{
  margin-top:10px;font-family:var(--mono);font-size:var(--text-sm);
  color:var(--paper-70);
}

/* ---------- ephemeral strip ---------- */
.ephemeral{padding:var(--rhythm-tight) 0;}
.ephemeral h2{max-width:26ch;}

/* ---------- pricing ---------- */
.plates{
  display:grid;grid-template-columns:1fr 1fr;gap:20px;
  margin-top:56px;max-width:860px;
}
.plate{
  border:1px solid rgba(13,16,32,.3);
  border-radius:3px;padding:36px 34px;
  background:rgba(244,237,217,.55);
}
.plate-hl{background:var(--cobalt);color:var(--paper);border-color:var(--cobalt-deep);}
.plate-hl .plate-note,.plate-hl li{opacity:.9;}
.plate-hl .amount span{opacity:.7;}
.tier{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--cobalt);margin-bottom:14px;
}
.plate-hl .tier{color:#B9C4F2;}
.amount{
  font-family:var(--serif);font-size:52px;line-height:1;
}
.amount span{font-size:17px;opacity:.6;}
.plate-note{font-size:13.5px;opacity:.65;margin:8px 0 24px;}
.plate ul{list-style:none;}
.plate li{
  padding:9px 0;font-size:var(--text-md);
  border-top:1px solid rgba(13,16,32,.15);
}
.plate-hl li{border-top-color:rgba(244,237,217,.25);}

/* ---------- access ---------- */
/* Centre these only in the centred panel. Without the prefix the rule
   indented the sub-paragraph in every left aligned section. The heading
   needs it too: its own max-width box was left aligned, so the text sat
   69px left of the centre line at 1280px wide. */
.access h2,.access .section-sub{margin-left:auto;margin-right:auto;}
.access-cta{margin-top:44px;}
/* .fineprint only ever sits on the cobalt access section, so it takes the
   paper tone directly instead of the ink/cobalt pair section-note carries. */
.fineprint{margin-top:18px;font-size:var(--text-sm);color:var(--paper-70);}

/* ---------- footer ---------- */
footer{
  border-top:1px solid rgba(13,16,32,.18);
  padding:44px 0 56px;
}
.footer-row{
  display:flex;align-items:center;justify-content:space-between;
  gap:28px;flex-wrap:wrap;
}
.brand-ink{color:var(--ink);font-size:20px;}
.footer-note{
  font-size:var(--text-sm);opacity:.68;max-width:44ch;
}
.footer-links{display:flex;gap:20px;font-size:13.5px;}
.footer-links a{
  opacity:.7;display:inline-flex;align-items:center;min-height:24px;
}
.footer-links a:hover{opacity:1;}

/* ---------- legal pages ---------- */
/* terms.html and privacy.html are long prose, which the landing page
   itself has none of. They carry the same nav, ground, and footer, and
   they load no script, so their nav ships the .scrolled class in the
   markup: the page is paper from the first pixel, and a paper nav on a
   paper ground would be invisible until a reader scrolled. */
.legal{padding:150px 0 110px;}
.legal-head{border-bottom:1px solid rgba(13,16,32,.18);padding-bottom:36px;}
/* The page title is an h1, so a screen reader lands on a real heading.
   The bare h1 rule is the hero's: 76px, centred, capped at 26ch. Reset it
   to the h2 scale these pages set the rest of their headings in. */
.legal-head h1{
  font-family:var(--serif);font-weight:400;
  font-size:clamp(34px,4.6vw,58px);
  line-height:1.05;letter-spacing:-.01em;
  max-width:22ch;margin:0;
}
.legal-updated{
  font-family:var(--mono);font-size:var(--text-sm);letter-spacing:.06em;
  text-transform:uppercase;opacity:.6;margin-top:16px;
}
/* The draft warning. A reader must not mistake these pages for reviewed
   legal text, so it sits above the first clause with an even ink rule and
   a filled ground, not a coloured accent stripe. */
.legal-draft{
  margin-top:28px;padding:18px 20px;
  border:1px solid rgba(13,16,32,.4);
  background:rgba(13,16,32,.04);
  font-size:14.5px;max-width:72ch;
}
.legal-body{margin-top:8px;}
.legal-body h2{
  font-size:clamp(24px,2.6vw,34px);margin-top:56px;
}
.legal-body h3{
  font-family:var(--mono);font-size:13px;letter-spacing:.06em;
  text-transform:uppercase;opacity:.7;margin-top:34px;
}
.legal-body p{margin-top:16px;max-width:72ch;font-size:15.5px;}
.legal-body ul,.legal-body ol{margin-top:16px;max-width:72ch;padding-left:22px;}
.legal-body li{margin-top:8px;font-size:15.5px;}
.legal-body a{text-decoration:underline;text-underline-offset:3px;}
.legal-body code{font-family:var(--mono);font-size:13.5px;}
/* A wide table must scroll inside its own box, never widen the page. */
.legal-table{overflow-x:auto;margin-top:20px;max-width:72ch;}
.legal-table table{border-collapse:collapse;min-width:520px;font-size:14.5px;}
.legal-table th,.legal-table td{
  text-align:left;padding:10px 16px 10px 0;vertical-align:top;
  border-bottom:1px solid rgba(13,16,32,.16);
}
.legal-table th{
  font-family:var(--mono);font-size:12px;letter-spacing:.06em;
  text-transform:uppercase;opacity:.7;font-weight:400;
}
/* A decision James has not made yet. It reads as a hole on purpose. */
.legal-todo{
  margin-top:16px;padding:14px 18px;max-width:72ch;
  border:1px dashed rgba(22,50,181,.6);color:var(--cobalt-deep);
  font-family:var(--mono);font-size:13px;line-height:1.7;
}
@media (max-width:900px){
  .legal{padding:120px 0 80px;}
}

/* ---------- reveal ---------- */
/* Content is visible by default. The .js class is set by main.js, so a
   page without JavaScript never hides anything. */
.js .reveal{
  opacity:0;transform:translateY(16px);
  transition:opacity .7s ease,transform .7s ease;
}
.js .reveal.in{opacity:1;transform:none;}

/* ---------- responsive ---------- */
@media (max-width:900px){
  /* The nav wraps on its own. The markup order and the painted order stay
     the same, so a Tab never jumps back up a row. */
  .nav-inner{flex-wrap:wrap;row-gap:12px;gap:14px;padding:14px 20px;}
  .nav-links{margin-left:auto;gap:16px;font-size:12.5px;}
  .nav-cta{margin-left:auto;font-size:11.5px;padding:7px 11px;}
  /* The wrapped nav is taller, and the moon hangs below it, so the hero
     copy starts lower. */
  .hero-inner{padding-top:max(23vh,196px);padding-bottom:180px;}
  .step{grid-template-columns:1fr;gap:8px;padding:30px 0;}
  .plates{grid-template-columns:1fr;}
}

/* The nav gained a fifth link, Sign in. Findings leaves below 520px, because
   it is the only nav link that goes to a section a reader reaches by
   scrolling anyway; Sign in is the only way a returning customer gets into
   the dashboard from this page.

   Measured on the built image, nav height and the number of rows the brand
   and the links occupy together:

     width   5 links (no rule)   4 links (shipped)
     519     1 row, 63px         1 row, 63px
     500     2 rows, 101px       1 row, 63px
     430     2 rows, 101px       1 row, 63px
     421     2 rows, 101px       2 rows, 101px
     390     2 rows, 87px        1 row, 55px
     320     2 rows, 88px        2 rows, 88px

   So the rule buys back 38px from 430 to 500 and 32px at 390, and buys
   nothing at 519, 421, or 320. The nav is never taller than it was before
   Sign in existed, at any width measured.

   The 421px band is not this rule's doing: it is 101px with four links and
   with five, and it was 101px before Sign in existed. It sits just above the
   420px query below, where the fonts have not shrunk yet. */
@media (max-width:519px){
  /* .nav-links a already sets display:inline-flex, and that selector is one
     class plus one element. A bare .nav-drop loses the specificity fight and
     the link stays on screen. Match the link selector and add the class. */
  .nav-links a.nav-drop{display:none;}
}

/* A small phone: a shorter nav, and more room under the moon. */
@media (max-width:420px){
  .nav-inner{padding:12px 16px;gap:10px;row-gap:8px;}
  .brand{font-size:19px;}
  .nav-links{gap:12px;font-size:12px;}
  .nav-cta{font-size:10.5px;padding:6px 9px;}
  .hero-inner{padding-top:max(25vh,210px);}
}
/* Below 380px the nav takes three rows at the default size, which costs
   the hero 36 pixels. Two rows fit at 11px. A reader who sets a minimum
   font size of 12px gets three rows again; the hero copy still clears the
   taller nav, because its top padding comes from the measured nav height. */
@media (max-width:379px){
  .nav-inner{padding:12px 14px;gap:8px;}
  .brand{font-size:18px;}
  .nav-links{gap:6px;font-size:11px;}
  .nav-cta{font-size:11px;padding:6px 6px;letter-spacing:0;}
}

/* A short window, at any width: shrink the hero copy, so the copy and the
   horizon both fit above the fold. A phone held sideways lands here too.
   The heading keeps its character cap here, or it grows to one long line
   and runs under the moon and the nav. */
@media (max-height:620px){
  /* The nav wraps to two or three rows on a narrow window, so the copy
     starts below whatever height the nav actually has. */
  .hero-inner{
    padding-top:max(78px, calc(var(--nav-h,62px) + 18px));
    padding-bottom:170px;
  }
  h1{font-size:clamp(30px,4.4vw,58px);max-width:min(26ch,76%);}
  .hero-sub{margin-top:16px;}
  .hero-cta{margin-top:22px;}
}
/* A small short window: keep both buttons on one row, above the fold. */
@media (max-height:620px) and (max-width:520px){
  .hero-cta{gap:10px;flex-wrap:nowrap;}
  .btn{font-size:11px;padding:10px 12px;white-space:nowrap;}
}
/* The ladder cannot measure the nav or the heading, so it drops the moon
   on any short window. The script makes the finer decision. */
@media (max-height:760px){ .fb-moon{display:none;} }
/* The ladder pins the crest to a fixed height, and on a short window the
   button row reaches past it. Drop the crest instead. The shorter case
   comes last, so it wins where both match. */
@media (max-height:760px) and (max-width:960px){ .sky-fallback{--crest:86;} }
@media (max-height:430px) and (min-width:431px){ .sky-fallback{--crest:88;} }
/* Under about 380px the two buttons cannot share a row at any type size. */
@media (max-height:620px) and (max-width:379px){
  .hero-cta{flex-wrap:wrap;}
}
/* A very short window, at any width: pull every gap in, so both buttons
   stay above the fold. */
@media (max-height:430px){
  .hero-inner{padding-bottom:110px;}
  h1{font-size:clamp(28px,3.4vw,44px);}
  .hero-sub{margin-top:12px;}
  .hero-cta{margin-top:16px;}
}
/* Guarded on width, so it never overrides the smaller button above. */
@media (max-height:430px) and (min-width:521px){
  .btn{font-size:12px;padding:9px 14px;}
}
/* The smallest short window cannot hold the whole hero above the fold. Pull
   in as far as the copy allows, and let the rest scroll. */
@media (max-height:430px) and (max-width:379px){
  h1{font-size:26px;}
  .hero-sub{font-size:13px;}
  .hero-inner{padding-bottom:90px;}
}
/* A phone that is a little too short: pull the gaps in, so the second
   button clears the fold. */
@media (max-width:900px) and (min-height:431px) and (max-height:760px){
  .hero-sub{margin-top:18px;}
  .hero-cta{margin-top:24px;}
  .hero-inner{padding-bottom:164px;}
}
/* A small phone that is a little too short: a smaller heading buys the
   second button its room. */
@media (max-width:379px) and (min-height:621px) and (max-height:760px){
  h1{font-size:clamp(38px,7.2vw,102px);}
}

/* A small phone between 431px and 620px tall. The moon is already out of
   frame at this size, so the copy only has to clear the nav. This block
   comes last, so it wins over the wider phone block above. */
@media (max-width:379px) and (min-height:431px) and (max-height:620px){
  .hero-inner{padding-top:max(78px, calc(var(--nav-h,62px) + 18px));}
  h1{font-size:26px;}
  .hero-sub{margin-top:12px;}
  .hero-cta{margin-top:16px;}
}

/* ---------- print ---------- */
@media print{
  #nav,.scroll-hint,#sky,.sky-fallback{display:none !important;}
  body,.hero,.cobalt,.paper,.paper-deep,.plate,.plate-hl,.access,footer{
    background:#fff !important;color:#000 !important;
  }
  .hero{min-height:0;}
  .hero-inner{padding:24px 28px 32px;}
  .plate-hl .tier{color:#000 !important;}
  .plate-hl li{border-top-color:rgba(0,0,0,.3) !important;}
  .cobalt::after,.paper::after,.paper-deep::after{display:none;}
  .btn,.hero-cta,.access-cta,.fineprint{display:none;}
  .section{padding:24px 0;}
  .plate-hl{border-color:#000 !important;}
  .js .reveal{opacity:1;transform:none;transition:none;}
}

/* Windows high contrast forces the text to system colours and paints an
   opaque box behind it. It cannot force a canvas, so the print stayed in
   full colour under white text panels. Drop the print instead. */
@media (forced-colors: active){
  #sky,.sky-fallback{display:none;}
  .hero{color:CanvasText;}
  .btn-ghost{background:transparent;}
}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto;}
  .js .reveal{transition:none;transform:none;opacity:1;}
  .js .scroll-hint,.scroll-hint{display:none;}
}