/*
  Discover NanoFlashing.

  Format taken from gore-tex.com/technology/membrane, read and measured 2026-08-28:
  a logo alone, a large title, a small scroll line, then one idea per screen, each
  carrying one piece of media and a short paragraph. Their whole page runs 512 words.
  Their ground is black; Yin asked for white with black ink, so the format is borrowed
  and the colour is ours.

  Every section fits one screen. Yin: "Many screen fail to show without scroll, I need
  to scroll up scroll down to see the full information." So each piece of media is
  capped by the height left to it and takes its width from its own aspect ratio, rather
  than taking the stage width and pushing its section past the fold.
*/

@font-face {
  font-family: Anton;
  src: url("/assets/fonts/Anton-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

:root {
  --paper: #ffffff;
  --ink: #000000;
  --ink-soft: #4a5257;
  --accent: #1b7bff;
  --hair: #6b7780;
  --rule: rgba(0, 0, 0, .12);

  --type: Montserrat, Arial, sans-serif;
  --display: Anton, Impact, sans-serif;

  /* Yin, 2026-08-28: "font size is not the same as our landing page header."
     This is the landing page's own header scale, matched exactly. */
  --display-size: clamp(30px, calc(18px + 2vw), 50px);

  --stage: min(100% - clamp(72px, 15vw, 300px), clamp(1240px, 62vw, 1560px));
  --pad: clamp(56px, 7vh, 88px);

  /* One spacing scale. Before this the gaps between a heading, its words and its
     picture measured 22, 26, 32, 36, 44 and 48 across five screens, which is what
     "unaligned" looks like when you measure it. Two values now, used everywhere. */
  --gap-head: 24px;   /* heading to words */
  --gap-media: 48px;  /* words to picture */

  /* One measure for centred body copy. In ch this was resolving against each
     element's own font-size, so the same token gave 882px on a 19.5px paragraph and
     720px on its 16px wrapper. In px it is one width everywhere. */
  --measure: 880px;
  --ease: cubic-bezier(.22, .61, .36, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/*
  Yin: "I like the landing page effect, scroll 1 page, scroll 1 page, not like now."
  One screen per scroll. The footer is left out of it, so the page ends normally rather
  than snapping into a panel that is not a screen.
*/
html {
  scroll-behavior: smooth;
  scroll-snap-type: y mandatory;
}
.dn-section {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
/* The footer must be a snap target too. Without it, mandatory snapping had nothing to
   land on down there and threw the page back to the last section every time. */
footer { scroll-snap-align: start; }

/* A snapped section sits flush with the top of the window, and the site header floats
   over that top edge. Every section reserves the header's height so nothing lands
   underneath it. */
:root { --header: 84px; }

/*
  The wave field sits behind everything, fixed, so the page scrolls over it rather than
  dragging it along. Its own ground is white, so it is the page ground as well.
*/
/*
  Yin, 2026-08-28: "The ocean background effect, shall only appear at the first screen,
  screen after don't need ocean background effect." So it is not fixed to the window any
  more; it belongs to the opening section and scrolls away with it.
*/
.dn-open { position: relative; }
#dn-ocean {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
.dn-open .dn-inner { position: relative; z-index: 1; }
main, footer { position: relative; z-index: 1; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--type);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.dn-skip { position: absolute; left: -9999px; }
.dn-skip:focus {
  left: 16px; top: 16px; z-index: 99;
  padding: 10px 14px; background: var(--ink); color: var(--paper);
}

/* ---- the shared shapes ------------------------------------------------- */

.dn-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: calc(var(--pad) + var(--header)) 0 var(--pad);
}

.dn-inner { width: var(--stage); margin: 0 auto; }

h1, h2 {
  font-family: var(--display);
  font-size: var(--display-size);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: 0;
  color: var(--ink);
  text-wrap: balance;
}

.dn-body {
  font-size: 19.5px;
  font-weight: 450;
  line-height: 1.62;
  color: var(--ink);
}

/* the landing page's scroll cue, matched: Montserrat, not a mono */
.dn-mono {
  font-family: var(--type);
  font-size: 13.5px;
  font-weight: 650;
  letter-spacing: .12em;
  color: var(--ink);
  opacity: .9;
}

.dn-film { display: block; width: 100%; height: auto; background: var(--paper); }
.dn-figure { position: relative; width: 100%; margin: 0 auto; }

/* ---- 1. opening -------------------------------------------------------- */

.dn-open { text-align: center; }
.dn-open img { width: 260px; height: auto; margin: 0 auto 48px; display: block; }
.dn-open .dn-mono { margin-top: 36px; }

/* ---- 2. what it is ----------------------------------------------------- */

/*
  Yin asked for this paragraph on two lines. At 19.5px the sentence needs about 86
  characters a line to break in two, so the measure is set wide rather than at the
  62 a single column would take.
*/
.dn-what { text-align: center; }
.dn-what h2 { margin-bottom: var(--gap-head); }
.dn-what .dn-body {
  max-width: min(100%, var(--measure));
  margin: 0 auto var(--gap-media);
  text-align: center;
  text-wrap: pretty;
}

/* the film keeps its 1920 by 822 ratio and is capped by the room left on the screen */
.dn-what .dn-stage {
  max-width: min(100%, calc((100vh - 390px) * 1920 / 822));
  margin: 0 auto;
}

/*
  The film's own last rows carry the studio floor, which ended in a hard grey band
  against the page. Yin: "there is a weird black line below the video." The bottom
  edge is faded so it dissolves instead.
*/
.dn-what .dn-film {
  -webkit-mask-image: linear-gradient(to bottom, #000 95%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 95%, transparent 100%);
}

/* ---- the pollutant pop-overs, carried over from the v24 page ------------ */

.dn-stage { position: relative; container-type: inline-size; }
.dn-specimens { position: absolute; z-index: 3; inset: 0; pointer-events: none; }

.dn-specimen {
  position: absolute;
  left: var(--sx);
  display: flex;
  width: max-content;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  opacity: 0;
  transform: translate(-50%, 8px);
  transition: opacity .42s var(--ease), transform .42s var(--ease);
}
/* they latch open and stay open, so the frozen last frame keeps all five */
.dn-specimen.is-in { opacity: 1; transform: translate(-50%, 0); }

.dn-specimen--top { top: 2%; }
.dn-specimen--bottom { bottom: 11%; }

.dn-specimen video {
  display: block;
  width: min(5.4cqw, 112px);
  height: min(5.4cqw, 112px);
  border-radius: 50%;
  background: var(--paper);
  object-fit: cover;
}

.dn-specimen span {
  display: block;
  color: var(--ink);
  font: 600 clamp(14.5px, 1.16cqw, 19px)/1.3 var(--type);
  text-align: center;
  max-width: 11cqw;
  white-space: normal;
  overflow-wrap: break-word;
  text-wrap: balance;
}

/* the leaders run straight down into the plume, and straight up from below */
.dn-specimen--top::after,
.dn-specimen--bottom::before {
  width: 1px;
  background: var(--hair);
  content: "";
}
.dn-specimen--top::after { height: 5.8cqw; }
.dn-specimen--bottom::before { height: 3.4cqw; }

/*
  The order matters and a pseudo-element has none by default, which put the leader
  ABOVE the name on the top row: line, then label, then circle, with the line running
  up into empty white instead of down into the plume. Every one of the three parts is
  ordered explicitly now.

  Top row reads name, circle, then the line dropping into the smoke.
  Bottom row reads the line rising into the smoke, circle, then name.
*/
.dn-specimen--top span   { order: 1; }
.dn-specimen--top video  { order: 2; }
.dn-specimen--top::after { order: 3; }

.dn-specimen--bottom::before { order: 1; }
.dn-specimen--bottom video   { order: 2; }
.dn-specimen--bottom span    { order: 3; }

/* ---- 3. attract. capture. destroy. ------------------------------------- */

/*
  Yin: "the writing shall be on top of the animation, animation and writing shall
  center, like gore tex." A centred stack, the same shape as section two.
*/
.dn-acd { text-align: center; }
.dn-acd h2 { margin-bottom: var(--gap-head); }
.dn-acd .dn-body { max-width: min(100%, var(--measure)); margin: 0 auto var(--gap-media); text-wrap: pretty; }
.dn-acd .dn-figure { max-width: min(520px, calc(100vh - 530px)); }

/*
  Yin, twice: the charge animation has a black border.

  The file has none. Every edge row and column of the encoded frame measures 255, and
  so does every edge of the composited page in a headless capture, so it is not in
  anything I can photograph here. What IS wrong is the frame size. It was 1080 square,
  and 1080 is not a multiple of 16, so the encoder pads to 1088 and the decoder crops
  back. On some hardware decode paths that seam shows as a dark fringe. It is now 1024
  and 704, both multiples of 16.

  This clip is the second line of defence: it removes the outermost pixel of the
  element, so no decoder edge can reach the page whatever the path.
*/
.dn-acd .dn-film {
  clip-path: inset(1px);
}

/* ---- 4. it does not fade ----------------------------------------------- */

/*
  This was the only left-aligned screen on a page where every other screen is centred.
  It is centred now like the rest.

  The chart is not given a width. It is given whatever height the screen has left after
  the heading, the words and the caption are paid for, and it takes its width from that.
  Guessing at a formula for the leftover space was wrong four times in a row, because a
  narrower chart makes the text columns taller, which changes the answer. Letting the
  box absorb the remainder is exact at every size, with no arithmetic to get wrong.
*/
.dn-fade { text-align: center; }
.dn-fade .dn-inner {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 2 * var(--pad) - var(--header));
}
.dn-fade h2 { margin-bottom: var(--gap-head); flex: 0 0 auto; }

/* Yin: "Can you don't split it into 2 column". One column, one measure, like the
   rest of the page. */
.dn-fade .dn-cols {
  width: min(100%, var(--measure));
  margin: 0 auto var(--gap-media);
  text-align: left;
  flex: 0 0 auto;
}
.dn-fade .dn-body + .dn-body { margin-top: 18px; }

.dn-chart {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.dn-chart svg { display: block; flex: 1 1 auto; min-height: 0; width: auto; max-width: 100%; }
.dn-chart figcaption {
  flex: 0 0 auto;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  max-width: none;
  width: 100%;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-soft);
  text-align: left;
}
.dn-chart b { color: var(--ink); font-weight: 600; }

/*
  This screen carries twice the words of any other, so on a short window it is the one
  that runs longest. Below 880px tall its body steps down a size. That is not an
  inconsistency: at any given window every paragraph on the page matches every other.
*/
@media (max-height: 880px) {
  .dn-fade .dn-body { font-size: 17.5px; line-height: 1.5; }
}

/* ---- 5. it stays clean ------------------------------------------------- */

/*
  Yin: "I want to dumb it down, back to It stays clean, then say minimal writing to say
  stay clean." The film is the argument. The words only say what is being looked at.
*/
.dn-clean { text-align: center; }
.dn-clean h2 { margin-bottom: var(--gap-head); }
.dn-clean .dn-body { max-width: min(100%, var(--measure)); margin: 0 auto var(--gap-media); text-wrap: pretty; }
.dn-clean .dn-figure { max-width: min(760px, calc((100vh - 380px) * 1280 / 720)); }

/*
  The fungi film is a photographed studio scene on a grey backdrop, measured at
  196,196,202. It cannot be lifted to white without blowing out the slab, so its edges
  are feathered and it reads as a photograph on the page rather than a grey rectangle
  cut into it. This is what the fungi page itself does.
*/
.dn-clean .dn-film {
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 13%, #000 87%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 13%, #000 87%, transparent 100%);
  mask-composite: intersect;
}

/* ---- 6. close ---------------------------------------------------------- */

.dn-close { text-align: center; }
.dn-close img { width: 340px; height: auto; margin: 0 auto; display: block; }

/* ---- phone and tablet -------------------------------------------------- */

@media (max-width: 900px) {
  :root { --stage: calc(100% - 40px); --pad: 64px; }

  .dn-section { min-height: 0; }

  .dn-what .dn-stage,
  .dn-acd .dn-figure,
  .dn-clean .dn-figure,
  .dn-chart { max-width: 100%; }

  .dn-what .dn-body,
  .dn-acd .dn-body,
  .dn-clean .dn-body { max-width: none; margin-bottom: 28px; }

  .dn-fade .dn-cols { grid-template-columns: minmax(0, 1fr); row-gap: 22px; }

  .dn-open img { width: 180px; margin-bottom: 32px; }
  .dn-close img { width: 210px; }

  /* the pop-overs come out of the picture and sit under it in one row */
  .dn-specimens {
    position: static;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
    margin-top: 16px;
  }
  .dn-specimen,
  .dn-specimen--top,
  .dn-specimen--bottom {
    position: static; top: auto; bottom: auto; left: auto;
    width: auto; min-width: 0; transform: translateY(8px); gap: 6px;
  }
  .dn-specimen.is-in { transform: translateY(0); }
  .dn-specimen--top span,
  .dn-specimen--bottom span { order: 3; }
  .dn-specimen--top video,
  .dn-specimen--bottom video { order: 2; width: 46px; height: 46px; }
  .dn-specimen--top::after { display: none; }
  .dn-specimen--bottom::before,
  .dn-specimen--top::before { width: 1px; height: 12px; order: 1; background: var(--hair); content: ""; }
  .dn-specimen span { max-width: 100%; font-size: 11.5px; line-height: 14px; }

  .dn-chart { overflow-x: auto; }
  .dn-chart svg { min-width: 600px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .dn-specimen { transition: none; }
}


/*
  The filtration chart was unreadable on a phone.

  It is one 840 unit SVG in a flex column with width:auto and a flexible height,
  so on a narrow short box it collapsed. Codex, judging the live render at 390:
  "the performance chart is about 80x30 CSS px, so the axes, labels and data are
  unreadable" -- worse than the same chart was on the five contaminant decks,
  where it rendered at 358 by 154.

  The phone gets the chart the decks now use: authored at viewBox 0 0 342 258 so
  one unit is one CSS pixel, the two series names lifted out of the plot into a
  stacked key, three markers instead of five, and no label under 12 CSS pixels.
  Same data, same two curves, same colours. The desktop chart is untouched.
*/
.dn-chart-phone { display: none; }

@media (pointer: coarse) and (max-width: 880px) and (orientation: portrait) {
  /*
    Under 900px this page already gave the chart "min-width: 600px" inside an
    overflow-x: auto figure, which is the old answer to a wide chart on a narrow
    screen: make the reader swipe. The phone chart does not need that, and the
    600px floor was what made it 600 wide and 453 tall inside a figure that had
    collapsed to 38, so it was clipped to a sliver. The floor goes, and the figure
    sizes to the chart instead of to whatever room is left.
  */
  .dn-chart {
    overflow: visible;
    flex: 0 0 auto;
    height: auto;
  }

  /*
    Every screen on this page caps its content to one window height. The chart
    screen's content is now taller than that, because a readable chart is 264px
    where the squeezed one was 38, so the cap cut it off at the signup boundary.
    Only the screen that holds the chart is released, and only on a phone, where
    a section running past one screen is ordinary.
  */
  .dn-inner:has(> .dn-chart) {
    max-height: none;
  }

  .dn-chart .dn-chart-wide { display: none; }

  .dn-chart .dn-chart-phone {
    display: block;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    height: auto;
    flex: 0 0 auto;
  }
}
