/* aurelioochoa.github.io
   Rhythmic cutlist: fourteen short acts, hard cuts, nothing pinned.
   Tokens and rules are documented in /DESIGN.md. */

/* Emotional carries both display and body: a hard-edged grotesque, one single cut.
   Its variable axis is TAGGED `wght` BUT IT IS NOT A WEIGHT AXIS. The stroke is identical
   at -1000 and +1000; what the axis actually does is deform the lowercase into quirky
   alternates, symmetrically around 0. Uppercase is unaffected at every value, which is why
   the all-caps display moments always looked right and every mixed-case heading looked
   broken: they were sitting at 800 or 900, i.e. near the top of the wonk dial.
   So: the face is declared as the single 400 it really is, and pinned to the clean cut
   with `font-variation-settings` on the body. There is no bold here to reach for, and
   nothing on this page claims one: emphasis is scale, case and the accent. */
/* Shipped as WOFF2, subsetted to the eight glyphs the face actually has (a e i l m n o t)
   and instanced to the single `wght 0` cut the body pins it to below. The variable axis is
   never animated here, so carrying its deltas cost 15KB to render the same eight shapes.
   1.7KB now, and it is the one font on the critical path. */
@font-face {
  font-family: "Emotional";
  src: url("../assets/fonts/emotional.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
/* Mimoid is a pixel-tech face. It is legible for one short uppercase word and nothing more,
   so it is used only on the three title cards, where that is exactly the job. Its axis is
   the same shape of lie: it runs from a melted blob at -100 to the clean pixel cut at +100,
   which is the one the cards hold. The melt is the site's one kinetic-type move (see the
   title-card entrance below), not a weight. */
/* Shipped as WOFF2, subsetted to uppercase, digits and the few marks it carries. It stays
   VARIABLE, because the melt below animates the axis; only the lowercase is gone, and the
   one rule that uses this face forces uppercase. 22KB, down from 50KB of gzipped TTF. */
@font-face {
  font-family: "Mimoid";
  src: url("../assets/fonts/mimoid.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../assets/fonts/jetbrains-mono.woff2") format("woff2");
  font-weight: 100 800;
  font-display: swap;
}

:root {
  /* One accent. It never varies, in any world. Deepened from #e4322b, which carried the
     label-sized copy that sits on it (the CV button, the fly button, the footer links) at
     4.12:1 and therefore failed AA for small text in three places. This red clears 4.5:1
     against the ink both ways round, and still clears 3:1 as large type on every ground. */
  --signal: #d62822;
  --signal-ink: #fff6f5;

  --code-ground: #0b0d14;
  --code-raise: #161a28;
  --code-ink: #e8eaf2;
  --code-soft: #8a90a8;
  --code-amber: #f0a32c;

  --sky-ground: #f2ede4;
  --sky-raise: #ffffff;
  --sky-ink: #14181f;
  --sky-soft: #5c6472;
  --sky-cyan: #3ac5d9;

  --kitchen-ground: #1a0f0c;
  --kitchen-raise: #2a1a14;
  --kitchen-ink: #f6ede4;
  --kitchen-soft: #a8907e;

  --card-ground: #08090d;
  --card-ink: #f2ede4;

  --display: "Emotional", "Helvetica Neue", system-ui, sans-serif;
  --body: "Emotional", system-ui, -apple-system, sans-serif;
  --label: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --card-face: "Mimoid", "Emotional", system-ui, sans-serif;

  --gutter: clamp(1.25rem, 5vw, 5.5rem);
  --chrome-h: 56px;

  /* Per-act ground, rebound by [data-ground]. */
  --ground: var(--code-ground);
  --ink: var(--code-ink);
  --soft: var(--code-soft);
  --raise: var(--code-raise);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  font-family: var(--body);
  /* Pins Emotional's mislabelled axis to its clean cut. Inherited, so it holds for every
     descendant, and nothing below this line sets a font-weight on Emotional. */
  font-variation-settings: "wght" 0;
  background: var(--code-ground);
  color: var(--code-ink);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: fixed; top: 0; left: 0; z-index: 100;
  transform: translateY(-120%);
  background: var(--signal); color: var(--signal-ink);
  padding: 0.85rem 1.25rem;
  font-family: var(--label); font-size: 0.78rem;
  letter-spacing: 0.18em; text-transform: uppercase;
  text-decoration: none;
}
.skip:focus { transform: translateY(0); }

:where(a, button, canvas):focus-visible {
  outline: 3px solid var(--signal);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------- chrome */

.chrome {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  height: var(--chrome-h);
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  padding: 0 var(--gutter);
  background: var(--card-ground);
  border-bottom: 2px solid rgb(255 255 255 / 0.14);
  color: var(--card-ink);
}
.chrome__mark { text-decoration: none; }
.chrome__name {
  font-family: var(--display);
  font-size: clamp(0.95rem, 2.2vw, 1.2rem);
  letter-spacing: -0.02em;
  text-transform: uppercase;
}
.chrome__right { display: flex; align-items: center; gap: clamp(0.6rem, 2vw, 1.4rem); }

.chrome__cv {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--signal);
  color: var(--signal-ink);
  padding: 0.6rem 0.9rem;
  white-space: nowrap;
  transition: transform 0.14s ease;
}
.chrome__cv:hover { transform: translateY(-1px); }
.chrome__cv:active { transform: translateY(1px); }

.langs { display: flex; gap: 0.15rem; }
.langs button {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  background: transparent;
  color: inherit;
  /* A control boundary, so it owes 3:1 against the chrome's ground rather than the 1.9:1
     that 0.22 alpha was giving it. */
  border: 2px solid rgb(255 255 255 / 0.46);
  padding: 0.42rem 0.5rem;
  cursor: pointer;
  line-height: 1;
}
.langs button[aria-current="true"] {
  background: var(--card-ink);
  color: var(--card-ground);
  border-color: var(--card-ink);
}

/* The two switches, in the same control grammar as the language buttons: a 2px boundary and
   no fill until they are on. Which state is drawn is decided by aria-pressed alone, so the
   icon and the accessibility tree cannot disagree about what the switch is saying. */
.switches { display: flex; gap: 0.3rem; }
.switch {
  display: grid; place-items: center;
  width: 34px; height: 34px;
  padding: 0;
  background: transparent;
  color: inherit;
  border: 2px solid rgb(255 255 255 / 0.46);
  cursor: pointer;
  line-height: 0;
  -webkit-tap-highlight-color: transparent;
}
.switch__icon { display: block; width: 18px; height: 18px; }
.switch[aria-pressed="true"] {
  background: var(--card-ink);
  color: var(--card-ground);
  border-color: var(--card-ink);
}
.switch[aria-pressed="true"] .switch__no,
.switch[aria-pressed="false"] .switch__yes { display: none; }
.switch:focus-visible { outline: 3px solid var(--signal); outline-offset: 2px; }

/* ----------------------------------------------------------- the drone */

/* The aircraft is in front of every ground and behind selected objects. It has to be
   visible the whole way down for the handover's own line ("it has been flying itself since
   you got here") to be true, so it never goes behind a world: what it passes behind is the
   chrome, the three plates and the shelf, and each of those opts in by carrying
   data-drone="behind" in the markup.

   The page's COPY is the third case and the interesting one. Headlines, body text and the
   title-card words carry data-drone="cross" instead, and the aircraft goes both over and
   under them: it dives through a headline behind the letterforms, and a while later it
   crosses the next one in front of them. Which way round is decided by its depth drift and
   by nothing else.

   None of that is done here. This canvas is one layer and it stays one layer: js/drone.js
   projects those elements' rects into its own depth buffer as quads that write depth and no
   colour, so the aircraft is clipped where it is behind one and the transparent canvas lets
   the real element through. Only opaque rectangles may take the "behind" path, because a
   depth quad covers the whole rect whether or not the element paints it; copy takes the
   "cross" path, where the element's own glyphs are rasterised onto the quad as a coverage
   mask and a fragment the text does not cover never writes depth at all.

   pointer-events: none is permanent, not a state. This is a fixed, full-viewport surface at
   z-index 50: any value but none turns it into a sheet of glass over every control on the
   page, which is exactly what drag-to-fly did to the CV link, the language toggle and the
   email CTA. The controls are the keyboard, and the affordance is the controller in act 10. */
.drone {
  position: fixed; inset: 0; z-index: 50;
  width: 100%; height: 100%;
  pointer-events: none;
  touch-action: none;
}

/* -------------------------------------------------------------- acts */

.act {
  position: relative; z-index: 10;
  background: var(--ground);
  color: var(--ink);
  padding: clamp(3.5rem, 9vh, 6rem) var(--gutter);
  min-height: 78svh;
  display: flex; flex-direction: column; justify-content: center;
}
.act:first-child { padding-top: calc(var(--chrome-h) + 3rem); }

[data-ground="code"]    { --ground: var(--code-ground);    --ink: var(--code-ink);    --soft: var(--code-soft);    --raise: var(--code-raise); }
[data-ground="sky"]     { --ground: var(--sky-ground);     --ink: var(--sky-ink);     --soft: var(--sky-soft);     --raise: var(--sky-raise); }
[data-ground="kitchen"] { --ground: var(--kitchen-ground); --ink: var(--kitchen-ink); --soft: var(--kitchen-soft); --raise: var(--kitchen-raise); }
[data-ground="card"]    { --ground: var(--card-ground);    --ink: var(--card-ink);    --soft: var(--card-ink);     --raise: var(--card-ground); }
[data-ground="card-light"] { --ground: var(--sky-cyan);    --ink: var(--card-ground); --soft: var(--card-ground);  --raise: var(--sky-raise); }
[data-ground="close"]   { --ground: var(--signal);         --ink: var(--signal-ink);  --soft: var(--signal-ink);   --raise: var(--signal); }

/* Entrance. Front-loaded so an act is legible in the first third of its own span.
   The .reveal class is set ON the act, so these are child selectors, not descendant ones.
   Everything is visible by default and only hidden once JS has confirmed it can reveal it
   again: content must never depend on an observer firing in order to be readable. */
:root[data-reveal="on"] .reveal > * { opacity: 0; transform: translateY(18px); }
:root[data-reveal="on"] .act.is-in > * {
  opacity: 1; transform: none;
  transition: opacity 0.34s ease, transform 0.42s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 70ms);
}

/* 1 ---------------------------------------------------------- name */

.act--name { min-height: 100svh; justify-content: flex-end; }
.act[id] { scroll-margin-top: var(--chrome-h); }
/* Keep the keyboard field luminous above and quiet underneath the name. */
.act--name::after {
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 20%, rgb(11 13 20 / 0.24) 50%, var(--code-ground));
}
.name__footer {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.25rem; margin-top: 2rem;
  border-top: 1px solid rgb(232 234 242 / 0.3); padding-top: 1rem;
}
.world-links { display: flex; gap: clamp(0.8rem, 2.5vw, 2.5rem); }
.world-links a {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--label); font-size: 0.8rem;
  text-decoration-thickness: 1px; text-underline-offset: 0.45em;
}
.world-links a:hover { color: var(--code-amber); }
@media (max-width: 520px) {
  .name__footer { align-items: flex-start; flex-direction: column; gap: 0.5rem; }
  .world-links { width: 100%; justify-content: space-between; }
}
.name {
  font-family: var(--display);
  font-size: clamp(3.2rem, 15.5vw, 13rem);
  line-height: 0.86;
  letter-spacing: -0.045em;
  text-transform: uppercase;
}
.name__line { display: block; overflow: hidden; }
.name__line .w { display: block; }
:root[data-reveal="on"] .act--name.is-in .name__line:nth-child(1) .w { animation: rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) both; }
:root[data-reveal="on"] .act--name.is-in .name__line:nth-child(2) .w { animation: rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.09s both; }
@keyframes rise { from { transform: translateY(102%); } to { transform: translateY(0); } }

.name__where {
  font-family: var(--label);
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--soft);
  margin: 0;
  /* Sized to the line itself: letter-spaced mono runs wider than its ch count, so a
     ch-based max-width wraps a short place name onto two lines. */
  width: fit-content;
  white-space: nowrap;
}

/* 2 -------------------------------------------------------- three */

.three { list-style: none; }
.three__item {
  font-family: var(--display);
  font-size: clamp(2.4rem, 10vw, 7.5rem);
  line-height: 0.95;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}
/* The one place the accent is ink, and it can be: at clamp(2.4rem, ...) this is large text,
   which owes 3:1 and clears it at 3.87:1 on the code ground. */
.three__item--turn { color: var(--signal); }
.three__note {
  margin-top: 2rem;
  font-size: clamp(1rem, 1vw + 0.8rem, 1.3rem);
  color: var(--soft);
  max-width: 30ch;
}

/* 3, 7, 11 -------------------------------------------- title cards */

.act--card {
  min-height: 62svh;
  align-items: flex-start; justify-content: center;
}
.card__word {
  font-family: var(--card-face);
  font-variation-settings: "wght" 100;   /* Mimoid's clean pixel cut, not a weight */
  font-size: clamp(4rem, 20vw, 15rem);
  line-height: 0.82;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}
/* The one kinetic-type move on the page, and it is the card's job: the word arrives out of
   Mimoid's melt and resolves into the pixel cut as the world changes under the visitor.
   It punctuates the hard cut rather than decorating it, it happens three times, and it is
   the axis this face already ships instead of a second font or a library. */
@keyframes card-resolve {
  from { font-variation-settings: "wght" -80; }
  to   { font-variation-settings: "wght" 100; }
}
:root[data-reveal="on"] .act--card.is-in .card__word {
  animation: card-resolve 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.act--card-warm { --ground: #2a1a14; --ink: var(--kitchen-ink); }

/* The sky card's clouds. spite's CSS3DClouds, rebuilt in js/clouds.js: no WebGL, no fetched
   texture, no mouse look. Everything here is geometry; the sprites are drawn on a canvas and
   handed in as a data: URI. */
.clouds {
  perspective: 1200px;
  perspective-origin: 50% 46%;
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.34) 0%, rgb(255 255 255 / 0) 42%),
    linear-gradient(0deg, rgb(8 9 13 / 0.16) 0%, rgb(8 9 13 / 0) 46%);
}
.clouds__world {
  position: absolute; left: 50%; top: 50%;
  width: 0; height: 0;
  transform-style: preserve-3d;
}
.clouds__base {
  position: absolute;
  width: 20px; height: 20px;
  margin-left: -10px; margin-top: -10px;
  transform-style: preserve-3d;
}
.clouds__layer {
  position: absolute;
  width: 256px; height: 256px;
  margin-left: -128px; margin-top: -128px;
  max-width: none;   /* the global img rule would otherwise shrink these to the 0px base */
  will-change: transform;
}

/* 4, 12 --------------------------------------------- wide + plate */

.act--wide {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: center;
}
.act--wide-flip .plate { order: 2; }

.plate { border: 2px solid currentColor; overflow: hidden; }
.plate img { width: 100%; height: auto; }
.plate--bleed { border: 0; }

.prose h2 {
  font-family: var(--display);
  font-size: clamp(1.8rem, 4.4vw, 3.6rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  margin-bottom: 1.1rem;
  /* Acts 4 and 12 are column-constrained by their grid, but act 8 runs the full page and
     was setting its headline as one 1000px line while its own body sat at 40ch. A headline
     owes a measure too, and once it wraps it owes even lines rather than an orphan. */
  max-width: 20ch;
  text-wrap: balance;
}
.prose p {
  font-size: clamp(1rem, 0.9vw + 0.85rem, 1.28rem);
  line-height: 1.5;
  color: var(--soft);
  max-width: 40ch;
}

/* 5 --------------------------------------------------------- stack */

.stack { display: grid; gap: 0; }
.stack__row {
  display: grid;
  grid-template-columns: minmax(8rem, 15rem) 1fr;
  gap: clamp(0.5rem, 3vw, 2.5rem);
  padding: clamp(0.9rem, 2vh, 1.5rem) 0;
  border-top: 2px solid currentColor;
  align-items: baseline;
}
.stack__row:last-child { border-bottom: 2px solid currentColor; }
.stack dt {
  font-family: var(--label);
  font-size: 0.74rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--soft);
}
.stack dd {
  font-size: clamp(1rem, 1.1vw + 0.8rem, 1.5rem);
  line-height: 1.25;
}
/* The row he actually works in daily gets the accent, but as structure rather than as ink.
   No single red can clear 4.5:1 against both the near-white it carries as a button label
   and the near-black ground this row sits on: the two requirements pull in opposite
   directions, so one of the two uses has to stop being small text. The button keeps the
   red (it is the CTA), and this row states itself with a heavier accent rule and full-ink
   copy, which reads as more emphatic than the tinted text did and is legible besides. */
.stack__row--hot { border-top-color: var(--signal); border-top-width: 4px; }
.stack__row--hot dd { color: var(--ink); }

/* 6 ------------------------------------------------------- shipped */

/* Three items, three cells, and deliberately not three equal ones: act 10 further down is
   already an even trio of columns, and two even trios on one page is the same section
   twice. The longest-running client takes the tall cell and the other two stack beside it,
   which is a different layout family and also the true weighting of the three. */
.shipped {
  list-style: none;
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: clamp(1rem, 3vw, 2.5rem);
}
.shipped__item {
  background: var(--raise);
  border-top: 3px solid var(--signal);
  padding: clamp(1.1rem, 2.5vw, 1.8rem);
}
.shipped__item:first-child {
  grid-row: span 2;
  display: flex; flex-direction: column; justify-content: center;
}
.shipped__item h3 {
  font-family: var(--display);
  font-size: clamp(1.15rem, 1.8vw, 1.6rem);
  letter-spacing: -0.02em;
  line-height: 1.08;
  margin-bottom: 0.7rem;
}
.shipped__item p { color: var(--ink); opacity: 0.82; font-size: 1rem; line-height: 1.65; max-width: 52ch; }

/* 8 --------------------------------------------------------- field */

.act--field { padding-inline: 0; position: relative; overflow: hidden; }
.act--field .plate--bleed img { width: 100%; height: clamp(16rem, 44svh, 29rem); object-fit: cover; }
.prose--over {
  padding: clamp(1.5rem, 4vw, 3rem) var(--gutter) 0;
}

/* 9 ------------------------------------------------- THE HANDOVER */

/* The peak. It gets the largest span of any act by a visible margin, while staying under
   the cutlist's 1.4vh ceiling: the rest of the hold lives in the drone's chrome layer,
   which outlives this act rather than pinning it. */
.act--handover {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
  align-items: center;
  /* Grid tracks stretch by default, and .act's flex `justify-content: center` means nothing
     here. Without this the peak's 124svh is divided between the rows and the phone gets a
     screen of empty bone between the headline and the controller. */
  align-content: center;
  gap: clamp(2rem, 5vw, 4.5rem);
  min-height: 124svh;
}
.handover__copy { display: flex; flex-direction: column; align-items: flex-start; gap: 1.2rem; }
.handover__head {
  font-family: var(--display);
  font-size: clamp(2.6rem, 9vw, 7rem);
  line-height: 0.92;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}
.handover__body {
  font-size: clamp(1.05rem, 1.1vw + 0.85rem, 1.45rem);
  color: var(--soft);
  max-width: 26ch;
}
.handover__fallback {
  font-family: var(--label);
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--soft);
}
.handover__fallback[hidden] { display: none; }

/* ---------------------------------------------------- the controller */

/* Every offset below is a percentage of the ART, not of the box: the line art is cropped to
   its own ink bounding box by build/controller.py, so the screen and the power button sit at
   fixed fractions of it and stay put at any width. The numbers came off the alpha channel,
   not off a guess. See DESIGN.md. */
.rc {
  position: relative;
  width: min(560px, 100%);
  aspect-ratio: 1264 / 988;
  justify-self: end;
  container-type: inline-size;   /* the ring below is sized in cqw, off the art's own width */
  --rc-hold: 0;
}
.rc[hidden] { display: none; }
.rc__art { width: 100%; height: 100%; object-fit: contain; }

/* The screen. Dark glass until the controller is on, then the cyan backlight comes up:
   DESIGN.md sanctions sky-cyan as light rather than as a second accent. */
.rc__screen {
  position: absolute;
  left: 19.3%; right: 19.4%; top: 44.4%; bottom: 16%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 0.5rem;
  padding: 0.6rem;
  overflow: hidden;
  background: #0d1620;
  transition: background 0.5s ease, box-shadow 0.5s ease;
}
.rc[data-rc-state="on"] .rc__screen {
  background: radial-gradient(120% 140% at 50% 30%, #145566 0%, #0b2733 70%, #081b24 100%);
  box-shadow: inset 0 0 0 1px rgba(58, 197, 217, 0.5), 0 0 34px rgba(58, 197, 217, 0.28);
}

.rc__line { display: none; flex-direction: column; align-items: center; gap: 0.35rem; text-align: center; }
.rc[data-rc-state="off"]    .rc__line[data-rc-when="off"],
.rc[data-rc-state="primed"] .rc__line[data-rc-when="primed"],
.rc[data-rc-state="on"]     .rc__line[data-rc-when="on"] { display: flex; }

/* The aircraft's own power-on, which outlasts the button press: self test, warm up, link.
   js/rc.js mirrors the drone's state onto data-rc-link and it settles on "gnss", so the
   linked readout is what the screen shows for the rest of the page. */
.rc[data-rc-state="on"]:not([data-rc-link="gnss"]) .rc__line[data-rc-when="on"] { display: none; }
.rc[data-rc-state="on"]:not([data-rc-link="gnss"]) .rc__line[data-rc-when="linking"] { display: flex; }

.rc__state {
  font-family: var(--label);
  font-size: clamp(0.6rem, 1.5vw, 0.82rem);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--sky-cyan);
}
.rc__cue {
  font-family: var(--label);
  font-size: clamp(0.52rem, 1.15vw, 0.66rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #7f95a3;
  text-wrap: balance;
}
.rc[data-rc-state="on"] .rc__cue { color: #cfe9ef; }

/* The hold, drawn once. --rc-hold runs 0 to 1 and is the only thing js/rc.js animates. */
.rc__track {
  width: 62%; height: 4px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}
.rc[data-rc-state="off"] .rc__track, .rc[data-rc-state="on"] .rc__track { visibility: hidden; }
.rc__fill {
  display: block; height: 100%;
  width: calc(var(--rc-hold) * 100%);
  background: var(--signal);
}

/* The power button, bottom right, where it is on the real airframe's controller. The ring
   itself is only about 30px across at this width, which is under every sane touch target, so
   the hit area is a 48px square centred on it and the ::before draws the ring at its true
   size. Highlighted while the controller is off, because that is the one thing on this page
   a visitor has to find. */
.rc__power {
  position: absolute;
  left: 86.75%; top: 89.88%;
  transform: translate(-50%, -50%);
  width: max(48px, 7.2%); height: max(48px, 9.2%);
  padding: 0; border: 0; background: none;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  display: grid; place-items: center;
}
/* The ring is sized against the controller, not the viewport, so it tracks the art. */
.rc__power::before {
  content: "";
  width: 5.38cqw; height: 5.38cqw;
  min-width: 26px; min-height: 26px;
  border-radius: 50%;
  border: 3px solid var(--signal);
  background: rgba(214, 40, 34, calc(0.12 + var(--rc-hold) * 0.7));
  box-shadow: 0 0 0 0 rgba(214, 40, 34, 0.55);
}
.rc[data-rc-state="off"] .rc__power::before { animation: rc-pulse 1.9s ease-out infinite; }
.rc[data-rc-state="on"] .rc__power::before { background: var(--signal); }
.rc__power:focus-visible { outline: 3px solid var(--signal); outline-offset: 3px; }

@keyframes rc-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(214, 40, 34, 0.55); }
  70%  { box-shadow: 0 0 0 16px rgba(214, 40, 34, 0); }
  100% { box-shadow: 0 0 0 0 rgba(214, 40, 34, 0); }
}

/* The spray, on the dial the real controller sprays with: right shoulder, under the stick,
   at the same fractions of the art as the power button uses. It is quiet until the
   controller is on and it does not pulse while it is off, because the power button is the
   one thing on this page a visitor has to find and two competing rings find nothing. */
.rc__spray {
  position: absolute;
  left: 93.35%; top: 77.73%;
  transform: translate(-50%, -50%);
  width: max(44px, 6.6%); height: max(44px, 8.4%);
  padding: 0; border: 0; background: none;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  display: grid; place-items: center;
}
.rc__spray[disabled] { cursor: default; }
.rc__spray::before {
  content: "";
  width: 4.4cqw; height: 4.4cqw;
  min-width: 22px; min-height: 22px;
  border-radius: 50%;
  border: 3px solid currentColor;
  color: rgba(127, 149, 163, 0.5);
  transition: color 0.2s ease, background-color 0.2s ease;
}
.rc[data-rc-state="on"] .rc__spray::before { color: var(--sky-cyan); }
/* Spraying is a red RING with the booms' own red behind it, not a second solid red dot:
   the power button is solid red once it is on, and two of those find nothing. */
.rc[data-rc-spray="on"] .rc__spray::before {
  color: var(--signal);
  background-color: rgba(214, 40, 34, 0.34);
  animation: rc-spray 1.1s ease-out infinite;
}
.rc__spray:focus-visible { outline: 3px solid var(--signal); outline-offset: 3px; }

@keyframes rc-spray {
  0%   { box-shadow: 0 0 0 0 rgba(214, 40, 34, 0.5); }
  70%  { box-shadow: 0 0 0 12px rgba(214, 40, 34, 0); }
  100% { box-shadow: 0 0 0 0 rgba(214, 40, 34, 0); }
}

/* 10 --------------------------------------------------------- creds */

.creds {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: clamp(1rem, 3vw, 2.5rem);
}
.creds li { border-top: 2px solid currentColor; padding-top: 1rem; }
.creds b {
  display: block;
  font-family: var(--display);
  font-size: clamp(1.15rem, 2.2vw, 1.9rem);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin-bottom: 0.45rem;
}
.creds span {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--soft);
}

/* 13 -------------------------------------------------------- skull */

/* The skull moved into the closing act, so it no longer centres a whole section of its
   own: he signs the page, badge on the left and what he is doing there on the right. Stacked
   he cost the act a whole extra row of height for no reading gain. */
.close__sign {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(0.9rem, 2.5vw, 1.8rem);
}
.close__sign-text { display: flex; flex-direction: column; gap: 0.5rem; }
.act--close .skull {
  /* Smaller than it was when it had a section to itself: the closing act already carries a
     headline, a CTA and the link row, and the cutlist caps every act under the peak. */
  width: clamp(4.5rem, 9vw, 6.5rem);
  /* Its badge is signal-red-on-dark by default, which is invisible on the signal-red close.
     On this ground the ring takes the ink instead. */
  border-color: var(--signal-ink);
  background: var(--card-ground);
}
.act--close .skull__credit { text-align: left; }
.skull {
  width: clamp(9rem, 26vw, 14rem);
  aspect-ratio: 1;
  border-radius: 50%;   /* the one round thing on the page, and it is round by nature */
  overflow: hidden;
  background: var(--kitchen-raise);
  border: 3px solid var(--signal);
  display: grid; place-items: center;
}
.skull img { width: 100%; height: 100%; object-fit: contain; }
/* The badge is a link, so it has to say so on hover and it has to keep its shape while it
   does: the border is already the ring, so the tell is the ring thickening inward rather
   than a scale that would break the circle out of the row it sits in. */
.skull { transition: box-shadow 0.16s ease; }
.skull:hover, .skull:focus-visible { box-shadow: inset 0 0 0 4px currentColor; }

/* The fact he says, and the press that gives another one. It is one button and not a line
   with a control beside it: the whole bubble is the target, which is what makes it worth
   pressing on a phone, and the label under it is what says it is pressable at all.

   The facts are written to two lines or fewer at every width in all three languages, and
   `min-height` below holds exactly those two, so pressing for another one never moves the
   credit under it. A longer fact added later breaks that, and the length is the thing to
   fix, not the floor. */
.skull__fact {
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 2px solid currentColor;
  padding: 0.7rem 0.9rem;
  cursor: pointer;
  display: grid;
  gap: 0.45rem;
  max-width: 42ch;
  transition: background-color 0.14s ease, color 0.14s ease;
}
/* A display of any kind beats the hidden attribute's own display:none, so the attribute
   has to be restated here or the empty bubble shows until a language is applied. Same
   restatement the fallback and the controller already need, for the same reason. */
.skull__fact[hidden] { display: none; }
.skull__fact-text {
  font-size: clamp(0.86rem, 0.5vw + 0.74rem, 1rem);
  line-height: 1.4;
  min-height: 2.8em;
}
.skull__fact-more {
  font-family: var(--label);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* The close's --soft IS its ink, so a label that steps down here has to do it itself. */
  opacity: 0.72;
}
/* Ink and ground swapped, which is what the CTA in the same act already does on press. */
.skull__fact:hover { background: var(--signal-ink); color: var(--signal); }

.skull__credit {
  font-family: var(--label);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--soft);
}

/* 14 -------------------------------------------------------- close */

/* The page's focus ring is the accent, and this act's ground is the accent, so every link,
   the CTA and the copy control were being ringed in red on red: a keyboard visitor could
   not see where they were standing in the one act that asks them to do something. On this
   ground the ring takes the ink, which is the same swap the jazzskull's badge already
   makes for the same reason. */
[data-ground="close"] :where(a, button):focus-visible { outline-color: var(--signal-ink); }

/* The act that asks for something, and the only one that was not using its own page. The
   headline held the left third of a 1440px viewport and the other two thirds were bare red,
   under a lone mailto that never showed the address it would open. So the act is a grid
   now: the headline keeps the full span it always deserved, the ask and the answers sit
   side by side under it, and the signature runs the width as one bar rather than two loose
   blocks stacked at the foot. */
.act--close {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  column-gap: clamp(2rem, 6vw, 5.5rem);
  row-gap: clamp(1.8rem, 5vh, 3.4rem);
  align-content: center;
  min-height: 92svh;
}
.close__head {
  grid-column: 1 / -1;
  font-family: var(--display);
  font-size: clamp(2.6rem, 10vw, 8rem);
  line-height: 0.9;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}
.close__ask { align-self: center; }
.close__line {
  font-size: clamp(1.05rem, 1vw + 0.85rem, 1.45rem);
  line-height: 1.4;
  max-width: 26ch;
  text-wrap: balance;
  margin-bottom: clamp(1.4rem, 3vh, 2.2rem);
}

/* The link is what occupies the space and the frame is laid over it, centred on it, rather
   than stacked above it. Stacked, the frame's own bloom padding left about 120px of empty
   red between the line and the CTA for as long as the scene took to arrive, and then the
   act moved when it did. Overlaid, the pill lands where the link already was. */
.close__go { position: relative; display: inline-block; }
.close__cta {
  font-family: var(--label);
  font-size: 0.86rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--signal-ink);
  color: var(--signal);
  padding: 1.15rem 1.9rem;
  min-height: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 10.5rem;
  transition: transform 0.14s ease;
}
.close__cta:hover { transform: translateY(-2px); }
.close__cta:active { transform: translateY(1px); }

/* The answers. Rows and rules, which is how act 5 and act 11 state their data: this page
   has no card component and does not need one to say three things.

   `currentColor` is the close ground's ink, so the rules come out near-white on the red the
   same way they come out near-white on the code ground. */
.close__card { align-self: center; display: grid; gap: 0; }
.close__row {
  display: grid;
  /* The label column is fixed so the three rows align on one edge, and it is fixed at what
     the longest of them needs rather than at what English needs: "SCHREIB AUF" is half as
     wide again as "WRITE IN", and the 8rem that fit it was taking the room the address and
     its control needed beside it, so German wrapped KOPIEREN onto a line of its own. */
  grid-template-columns: minmax(5rem, 7rem) minmax(0, 1fr);
  gap: clamp(0.5rem, 2vw, 1.5rem);
  padding: clamp(0.75rem, 1.8vh, 1.2rem) 0;
  border-top: 2px solid currentColor;
  align-items: baseline;
}
.close__row:last-child { border-bottom: 2px solid currentColor; }
/* The address is the row the act exists for. Elsewhere on the page that row takes the
   accent rule; here the accent is the ground, so what is left to say it with is weight. */
.close__row--hot { border-top-width: 5px; }
.close__card dt {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.close__card dd {
  font-size: clamp(0.98rem, 0.7vw + 0.8rem, 1.24rem);
  line-height: 1.3;
  /* The address is one long unbreakable token beside a Copy control: they wrap as a pair
     at the widths where they have to, instead of the address pushing the control out. */
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.5rem 0.9rem;
}
.close__addr {
  font-family: var(--label);
  font-size: 0.86em;
  letter-spacing: 0.01em;
  /* An address is one word to a line breaker and the column is narrower than it is. */
  word-break: break-all;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

/* Secondary to the filled CTA by construction: outlined where that one is solid, and it
   inverts into the same solid on hover and on the copy itself. */
.close__copy {
  font-family: var(--label);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: inherit;
  background: none;
  border: 2px solid currentColor;
  padding: 0.42rem 0.7rem;
  min-height: 32px;
  cursor: pointer;
  flex: none;
  transition: background-color 0.14s ease, color 0.14s ease;
}
.close__copy:hover { background: var(--signal-ink); color: var(--signal); }
.close__copy[data-done] { background: var(--signal-ink); color: var(--signal); }

/* 14 --------------------------------------------------- the signature */

/* The badge and the line on one side, where else to find him on the other, under one rule.
   Stacked as two blocks they read as the page signing off twice, and on a phone the second
   of them was pushed to the bottom of the act across a screen of empty red. */
.close__foot {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(1rem, 4vw, 3rem);
  border-top: 2px solid currentColor;
  padding-top: clamp(1.1rem, 2.5vh, 1.7rem);
  margin-top: clamp(0.5rem, 2vh, 1.5rem);
}

.close__links {
  list-style: none;
  display: flex; flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.4rem 1.5rem;
  font-family: var(--label);
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.close__links a { text-decoration-thickness: 2px; text-underline-offset: 4px; }

.noscript {
  padding: 1rem var(--gutter);
  background: var(--code-amber);
  color: var(--card-ground);
  font-size: 0.95rem;
}

/* ------------------------------------------------------- responsive */

@media (max-width: 860px) {
  .act--wide { grid-template-columns: 1fr; }
  .act--wide-flip .plate { order: 0; }
  /* The peak stacks: copy, then the controller under it, still the largest act on the page. */
  .act--handover { grid-template-columns: minmax(0, 1fr); gap: clamp(1.6rem, 6vw, 2.6rem); }
  .rc { justify-self: start; width: min(460px, 100%); }
  .stack__row { grid-template-columns: 1fr; gap: 0.3rem; }
  .act--name { min-height: 92svh; }
  .shipped { grid-template-columns: 1fr; }
  .shipped__item:first-child { grid-row: auto; display: block; }
  /* The close stacks in its reading order: the ask, then the answers, then the signature.
     The foot drops its own top margin here: stacked, the row gap above it is already the
     separation, and the two together read as the act having ended twice. */
  .act--close { grid-template-columns: minmax(0, 1fr); }
  .close__foot { grid-template-columns: minmax(0, 1fr); align-items: start; margin-top: 0; }
  .close__links { justify-content: flex-start; }
}

@media (max-width: 520px) {
  :root { --chrome-h: 52px; }
  /* Five controls and a wordmark in 390px. The wordmark is one line at every width, which
     is what the two switches cost: without these the chrome had the room and now it does
     not, so the gaps and the paddings give it back rather than the name breaking in half. */
  .chrome { gap: 0.5rem; padding: 0 0.7rem; }
  .chrome__name { white-space: nowrap; font-size: 0.82rem; }
  .chrome__right { gap: 0.45rem; }
  .chrome__cv { padding: 0.45rem 0.5rem; font-size: 0.62rem; letter-spacing: 0.12em; }
  .langs { gap: 0.1rem; }
  .langs button { padding: 0.34rem 0.3rem; font-size: 0.62rem; letter-spacing: 0.08em; }
  .switch { width: 28px; height: 28px; }
  .switch__icon { width: 15px; height: 15px; }
  .switches { gap: 0.2rem; }
  .act { min-height: 72svh; }
  /* Restated after the blanket .act rule above, which would otherwise win on source order
     and cost the peak the largest-span property it is supposed to hold at every width. */
  .act--name { min-height: 92svh; }
  /* The peak has to stay the longest act at every width, and on a phone that is a number
     rather than a consequence: the close is 1.15 screens of content here, so the act that
     is supposed to dominate has to be given the room to. Raised from 118svh when the skull
     got his fact back, which is the maintenance G7 exists to force. 118 still technically
     cleared the close after the change, by 25px, which passes a comparison without reading
     as a peak to anyone actually scrolling. */
  .act--handover { min-height: 126svh; }
  .act--close { min-height: 88svh; }
  /* At this width the badge and its line side by side leave the line four words wide. */
  .close__sign { grid-template-columns: minmax(0, 1fr); gap: 0.9rem; }
  /* The address is 28 characters of monospace, and the label column beside it at 5.5rem
     leaves it too little to break into. Label over value from here down. */
  .close__row { grid-template-columns: minmax(0, 1fr); gap: 0.35rem; }
}

/* The narrowest phones still in the wild. Six things in the chrome do not fit at 320px on
   the sizes above, and the one that gives is never the wordmark: everything else steps down
   one notch instead. */
@media (max-width: 360px) {
  .chrome { gap: 0.35rem; padding: 0 0.5rem; }
  .chrome__name { font-size: 0.74rem; }
  .chrome__right { gap: 0.35rem; }
  .chrome__cv { padding: 0.4rem 0.42rem; font-size: 0.58rem; }
  .langs button { padding: 0.3rem 0.26rem; font-size: 0.58rem; }
  .switch { width: 26px; height: 26px; }
  .switch__icon { width: 14px; height: 14px; }
}

@media (prefers-reduced-motion: reduce) {
    :root[data-reveal="on"] .reveal > *,
  :root[data-reveal="on"] .act.is-in > * {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  :root[data-reveal="on"] .act--name.is-in .name__line .w { animation: none; }
  /* The melt collapses to the resolved cut, which is the frame the card is meant to land on. */
  :root[data-reveal="on"] .act--card.is-in .card__word {
    animation: none;
    font-variation-settings: "wght" 100;
  }
  .chrome__cv, .close__cta { transition: none; }
  /* The power-on ritual is an interaction, not decoration, so it still runs: what stops is
     the ring's pulse and the screen's fade. The two-second hold is the point of it. */
  .rc__power::before { animation: none; }
  .rc__spray::before { animation: none; transition: none; }
  .rc__screen { transition: none; }
}

/* ------------------------------------------------------- ThreeUI islands */

/* A ThreeUI scene used as an act's ground. Every one of these components renders a
   sandboxed iframe or its own canvas at 100% of its host, so the host is what sizes it:
   absolutely filling the act, behind the act's own copy.

   The scenes are style-isolated by construction (an iframe this stylesheet cannot reach),
   which is why there is nothing here but geometry and stacking. */
.act__scene {
  position: absolute; inset: 0;
  z-index: 0;
  width: 100%; height: 100%;
  border: 0;
  overflow: hidden;
  pointer-events: none;   /* the copy above stays selectable, links stay clickable */
}
/* A scene is either a host this page draws a canvas into, or a whole document in a frame.
   Both fill the act the same way. */
.act__scene canvas { display: block; width: 100%; height: 100%; }
iframe.act__scene { display: block; }

/* The act's own content, lifted above its scene.
   This has to cover every child, not just the ones tagged by hand: .act__scene is a
   POSITIONED element, and a positioned element paints above static in-flow content whatever
   the source order says. Tagging only some children is how an act ends up showing its
   picture and hiding its prose behind the scene. */
.act:has(.act__scene) > *:not(.act__scene) { position: relative; z-index: 1; }
.act__fore { position: relative; z-index: 1; }

/* An act carrying a scene owns its stacking context and clips it. */
.act:has(.act__scene) { isolation: isolate; overflow: hidden; }

/* 7 ------------------------------------------------------------ the shelf */

/* The Complete Shelf is a whole document in an iframe, so it is sized here and styled
   nowhere: this stylesheet cannot reach inside it, and the scene brings its own ground.
   It is given a viewport-relative height and a hard border because the act it sits in is
   still an act, and the cutlist caps every act at 1.4 viewport-heights. */
.act--shelf { padding-left: 0; padding-right: 0; }
.shelf {
  display: block;
  width: 100%;
  height: min(64svh, 620px);
  border: 0;
  border-top: 2px solid var(--code-raise);
  border-bottom: 2px solid var(--code-raise);
  background: var(--code-ground);
}

.shelf__intro { padding: 0 var(--gutter) clamp(1.5rem, 4vh, 2.5rem); }
.shelf__intro h2 {
  font-family: var(--display);
  font-size: clamp(2rem, 5.5vw, 3.6rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.shelf__intro p { margin-top: 0.9rem; color: var(--soft); max-width: 44ch; }

/* The liquid metal CTA replaces the plain link only once it has actually mounted, so the
   link is what is there without JavaScript, and there is never one of each. */
/* The button's own stage reserves roughly 90px on every side for the bloom to spill into,
   so the frame is sized to the stage, not to the pill. Cut to the pill's own size, all that
   shows is a corner of the empty pool. */
.close__metal {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(340px, 86vw); height: 240px;
  border: 0; display: block;
  background: transparent;
}
/* The plain link is what a visitor without JavaScript gets, so it is only hidden once the
   frame has actually been given a document to load. Hidden rather than removed: it is what
   reserves the pill's space, and `visibility` also takes it out of the tab order, so the
   one CTA is never two stops. */
.close__metal[src] ~ .close__cta { visibility: hidden; }

.plate { position: relative; }

/* A scene that sits behind a title card rather than behind body copy. The card's word is the
   act, and the banner is ivory at full strength: at parity the two large light shapes read as
   one muddle, so the scene is pushed back until it is ground again. */
.act__scene--behind { opacity: 0.42; }

/* An elemental mark washed into the act's own ground instead of laid over it.

   Every elemental-marks document paints its body #060708 and glows on top of that, which is
   fine on the two dark worlds and was a blackout on the bone one: act 11 covered a #F2EDE4
   act with near-black and then rendered the sky world's near-black ink on it, so his DGAC
   certification, his airframes and his languages sat at about 1.1:1 and could not be read at
   all. Screen leaves black untouched, so the ground comes back at full strength, the ink
   keeps the whole of its contrast, and the mark is left as a bloom on the paper.

   The scene is decorative, the copy is not, and the scene is what gives way.

   Opacity was the other candidate and it is the wrong one. It composites the frame's black
   ground too, so at the strength that tames the glow it also drags the bone halfway to grey
   and takes the copy's contrast down with it. */
.act__scene--wash { mix-blend-mode: screen; }

/* Effects are attached to the table rules and kitchen edges, never to duplicate titles.
   Canvases ignore pointers; semantic text and the static rules remain without JS. */
.stack { position: relative; }
.stack__row { position: relative; border-top-color: rgb(142 199 255 / 0.12); }
.stack__row:last-child { border-bottom-color: rgb(142 199 255 / 0.12); }
.stack__row--hot { border-top-width: 2px; }
.stack__row dt { transition: color 180ms ease, text-shadow 180ms ease; }
.stack__row.is-charged dt {
  color: #e1f6ff;
  text-shadow: 0 0 6px rgb(104 170 255 / 0.6), 0 0 22px rgb(68 128 230 / 0.35);
}
.section-effect { position: absolute; display: block; pointer-events: none; }
.section-effect--lightning { left: 0; top: -18px; width: 100%; height: calc(100% + 36px); }
.kitchen-heat { position: relative; }
.kitchen-heat.plate { border-color: rgb(240 163 44 / 0.22); }
.section-effect--fire { bottom: 0; left: 0; width: 100%; height: clamp(110px, 15vw, 170px); }
.kitchen-heat--copy { padding-bottom: 34px; border-bottom: 1px solid rgb(240 163 44 / 0.22); }
.kitchen-heat--copy .section-effect--fire { bottom: -1px; height: 65px; opacity: 0.8; }
@media (prefers-reduced-motion: reduce) {
  .section-effect { display: none; }
  .stack__row dt { transition: none; }
  .stack__row.is-charged dt { text-shadow: none; }
}

/* Navigation and contact share the site's ruled, square-edged visual language. */
:root { --chrome-h: 68px; }
.chrome { gap: 1.5rem; border-bottom-width: 1px; }
.chrome__mark { flex-shrink: 0; padding-block: 12px; }
.chrome__nav { display: flex; align-items: center; gap: clamp(1rem, 2vw, 2rem); }
.chrome__nav a {
  display: inline-flex; align-items: center; min-height: 44px;
  font: 0.72rem var(--label); color: var(--code-soft);
  text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent;
}
.chrome__nav a:hover, .chrome__nav a:focus-visible {
  color: var(--card-ink); border-bottom-color: var(--signal);
}
.chrome__right { gap: 1rem; }
.chrome__cv { min-height: 40px; display: inline-flex; align-items: center; letter-spacing: 0.1em; }
.chrome .langs { gap: 0; border: 1px solid rgb(255 255 255 / 0.46); padding: 3px; }
.chrome .langs button { border: 0; min-height: 30px; }
.chrome .switch { border-width: 1px; width: 34px; height: 34px; }
.chrome .switches { gap: 5px; }
.close__row { border-top-width: 1px; border-top-color: rgb(255 246 245 / 0.45); }
.close__row--hot { grid-template-columns: 1fr; gap: 0.75rem; border-top-width: 3px; border-top-color: currentColor; }
.close__row--hot dd { justify-content: space-between; }
.close__addr { font-size: clamp(0.8rem, 1.05vw, 1rem); text-decoration-thickness: 1px; text-underline-offset: 6px; }
.close__copy { min-height: 40px; border-width: 1px; }
.close__foot { border-top-width: 1px; border-top-color: rgb(255 246 245 / 0.5); column-gap: 3rem; row-gap: 1.3rem; }
.close__links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 2rem; }
.close__links a { display: flex; align-items: center; min-height: 44px; text-decoration: none; border-bottom: 1px solid rgb(255 246 245 / 0.4); }
.close__links a:hover { border-bottom-color: currentColor; }
.close__end { grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.close__signature { font: 1rem var(--display); text-transform: uppercase; letter-spacing: -0.02em; }
.close__end a { display: inline-flex; align-items: center; min-height: 44px; font: 0.68rem var(--label); text-underline-offset: 5px; }
.act--close .skull__fact { border-width: 1px; }
@media (max-width: 1180px) {
  :root { --chrome-h: 100px; }
  .chrome { display: grid; grid-template-columns: auto 1fr; grid-template-rows: 58px 42px; gap: 0 1rem; }
  .chrome__right { justify-self: end; }
  .chrome__nav { grid-column: 1 / -1; grid-row: 2; justify-content: space-between; border-top: 1px solid rgb(255 255 255 / 0.12); }
  .chrome__nav a { min-height: 40px; }
}
@media (max-width: 520px) {
  :root { --chrome-h: 96px; }
  .chrome { grid-template-rows: 54px 42px; gap: 0 0.4rem; padding-inline: 0.7rem; }
  .chrome__right { gap: 0.4rem; }
  .chrome__cv { min-height: 32px; padding-inline: 0.4rem; }
  .chrome .langs { padding: 2px; }
  .chrome .langs button { min-height: 28px; padding-inline: 0.26rem; }
  .chrome .switch { width: 28px; height: 30px; }
  .chrome .switches { gap: 3px; }
  .chrome__nav a { font-size: 0.68rem; }
  .act--close { row-gap: 1.5rem; padding-block: 3rem; }
  .close__sign { grid-template-columns: 52px minmax(0, 1fr); gap: 0.7rem; }
  .act--close .skull { width: 52px; border-width: 1px; }
  .skull__fact { padding: 0.55rem; }
  .skull__credit { font-size: 0.57rem; letter-spacing: 0.08em; }
  .close__links { gap: 0 1.5rem; }
}
@media (max-width: 360px) {
  .chrome { padding-inline: 0.45rem; }
  .chrome__right { gap: 0.28rem; }
  .chrome .switch { width: 25px; }
  .chrome .langs button { padding-inline: 0.2rem; }
  .close__signature { font-size: 0.85rem; }
}
