/* ==========================================================================
   err.css — the 404's room.

   THERE IS ALMOST NOTHING TO STYLE, because the page is a canvas. What is here
   is the four things CSS still has to decide: the paper the canvas sits on
   while it boots, the canvas filling the window, the blob canvas being kept
   ALIVE but out of sight, and one heading nobody sees.
   ========================================================================== */

/* THE PAPER, AND IT MATTERS FOR ONE FRAME. The canvas paints the red itself,
   so this only shows before the first draw — but without it that frame is the
   site's white and the page opens with a flash. --about-ground is the variable
   .about paints with; naming it rather than setting `background` directly is
   what keeps this page inside the same room as the other two. */
body[data-page="err"]{
  --about-ground: #e8271c;
  background: var(--about-ground);
}

/* THE ORDINARY POINTER. It was a crosshair, which is a cursor for a tool —
   this page has one thing to click and nothing to aim at. */
.err{ cursor: default; }

/* ---- THE GO HOME BARS -------------------------------------------------------
   Real links, above the canvas, one over every place a ticket's bar lands.
   js/err.js moves them with the camera and js/contact-cta.js gives them the
   site's scramble hover, the same one the bottom bar's buttons have.

   THEY ARE OUTSIDE THE LENS, and that is the trade. Everything else on the page
   is a texture the CRT shader warps; these are DOM, so they stay crisp and
   flat while the field bulges behind them. The alternative was a bar inside the
   picture, which cannot be clicked, cannot be hovered and cannot be read by
   anything. A button that works beats a button that matches. */
.err__bars{
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;   /* only the bars themselves take the pointer */
}
.err__go{
  position: absolute;
  top: 0; left: 0;
  width: 462px; height: 16px;
  transform-origin: 50% 50%;
  display: block;
  background: #000000;
  color: var(--about-ground, #e8271c);
  font: 7px/16px 'PP Supply Mono', ui-monospace, monospace;
  letter-spacing: 0;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0 0 0 10px;
  box-sizing: border-box;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
}
/* The site's hover, in the site's colour: the label descrambles and goes
   white, exactly as it does on the bottom bar. */
.err__go:hover{ color: #ffffff; }

/* The canvas is the page. Absolute rather than fixed on purpose: a fixed
   element makes its own stacking context, and .about is already the context
   everything here composites in. */
.err > canvas{
  position: absolute;
  inset: 0;
  display: block;
  width: 100%; height: 100%;
}

/* THE BLOBS, ALIVE AND UNSEEN — the same trick css/about.css plays on block
   02's figure, and for the same reason. js/work-cursor.js keeps drawing into
   this canvas and js/err.js uploads it as a texture every frame; the browser
   must not ALSO composite it, or the ground would appear twice, once warped by
   the lens and once flat on top of it.

   `visibility: hidden` and not `display: none`: a display-none canvas has no
   box, so it never gets sized, and what you would sample is a 300x150 default
   with nothing in it. Hidden keeps the size, the backing store and the draws —
   it only stops the compositor. */
/* !important, AND IT IS EARNED. js/work-cursor.js writes
   `canvas.style.visibility` INLINE on its own canvas every frame, so a plain
   rule here loses to it — measured: the canvas came back `visible`, composited
   itself on top of the field at its own z-index 1, and the ground appeared
   twice, once warped by the lens and once flat over the tickets. An inline
   declaration is beaten by an important one and by nothing else short of
   editing that module, which belongs to three other pages. */
body[data-page="err"] .work-blobs{ visibility: hidden !important; }

/* Read, never seen. The page's text lives in the texture, where no screen
   reader and no crawler can reach it, so it is said once here properly.
   Clipped rather than `display:none` — the latter is not read out either. */
.err__say{
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- THE STRIP -------------------------------------------------------------
   Figma 14783:21804, at that node's own numbers: 16px tall, full width, and one
   centred 191px group holding "//", the line, and "//" — 8, 18 and 183 from the
   group's left edge, which is a 10px gap either side of the text.

   THE NEGATIVE. `backdrop-filter: invert(1)` inverts whatever the strip is
   sitting on rather than painting a colour of its own, so it carries the field,
   the tickets, the blob and the lens's own grain through inverted — a true
   photographic negative of the page, sixteen pixels tall, wherever you put it.
   The red field comes back cyan, the black ink comes back white.

   IT HAS TO BE DOM, and that is not a preference. The invert must read the
   FINISHED picture, and inside the field shader the lens has not run yet — the
   strip would be inverting a half-drawn page. Above the canvas it inverts what
   is actually on screen.

   HORIZONTALLY IT IS CENTRED BY FLEX, NOT BY FIGMA'S NUMBER, and that is on
   purpose. That file's group sits at x=864.5 with a width of 191, which centres
   the BOX on 960 — but the box has 8px of slack on its left and none on its
   right, so the ink inside it lands 4px right of centre. Centring the ink is
   what "dead centre" means to the eye; copying the box would reproduce the
   slack instead.

   `--err-strip-y` is the only thing about it that is not Figma's: that file's
   frame puts it 615 down a 1080-tall page, which is not the middle, and the
   brief is the middle. 50%. */
.err__strip{
  position: absolute;
  left: 0; right: 0;
  /* DEAD CENTRE, and the -8px is what makes that true rather than nearly true.
     `top` places the strip's TOP edge, so at 50% the strip hangs eight pixels
     BELOW the middle of the window — half its own height. Subtracting that half
     puts the strip's own centre line on the page's centre line, and it also
     makes the dial mean what it says: err.stripY is where the middle of the
     strip sits, not where its top edge does. */
  top: calc(var(--err-strip-y, 50%) - 8px);
  height: 16px;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  -webkit-backdrop-filter: invert(1);
  backdrop-filter: invert(1);
}
.err__stripIn{
  display: flex;
  align-items: baseline;
  gap: 10px;
  color: var(--about-ground, #e8271c);
  font: 7px/16px 'PP Supply Mono', ui-monospace, monospace;
  text-transform: uppercase;
  white-space: nowrap;
}
/* 155px in the design, and it is the text that sets the group's 191 — so the
   width is left to the type rather than pinned, and the two // stay 10px off
   whatever it measures. */
.err__stripSay{ letter-spacing: 0; }
/* The dial's off state. Written as a data attribute rather than by removing
   the property so the strip keeps its place while you compare the two. */
.err__strip[data-neg="off"]{ -webkit-backdrop-filter: none; backdrop-filter: none; }
