/* z-addr. One object on black, in two views, built to his product render.
   Every surface here is a rendered picture (tools/device-scene.html); CSS draws only text,
   hairlines, the screen's own light and the motion.

   ONE scale, used everywhere and nowhere else:
     12  labels and the small print under them, on the machine and off it
     14  secondary notes, the cost of a check
     15  body copy, line-height 1.55
     20  a value: the readings beside the machine, the name on the plate
     24  the heading of the sheet
     32  the one display element on the screen, the ZA01 moulded into the shell
   Two things are allowed to scale with the object instead of the scale, because they are
   printed ON it and have to fit its glass and its moulding: the readout (13 to 20) and the
   ZA01 pressed into the shell (20 to 32). Everything else is a step above, flat.
   ONE column: --page, capped at 1120, with the same gutter on both sides. Everything on the
   page sits inside it, the machine and the edge panel included, so nothing is left hanging
   off the viewport and the two panels line up with each other. */

@font-face { font-family: "Technor"; src: url("/fonts/Technor-Black.woff2") format("woff2"); font-weight: 900; font-display: swap; }
@font-face { font-family: "Technor"; src: url("/fonts/Technor-Bold.woff2") format("woff2"); font-weight: 700; font-display: swap; }
@font-face { font-family: "Technor"; src: url("/fonts/Technor-Medium.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "RX100"; src: url("/fonts/RX100-Regular.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Departure"; src: url("/fonts/DepartureMono-Regular.woff2") format("woff2"); font-weight: 400; font-display: swap; }

:root {
  --ink: #000000;
  --line: #2f2049;
  --lime: #d7ee52;
  --cream: #ecf3a6;
  --text: #a996ce;         /* 7.9:1 on black */
  --text-soft: #8d7cb0;    /* 5.6:1 on black: the smallest type the page ever sets */
  --text-strong: #cfc1ea;
  --lcd-ink: #16200a;      /* 7.1:1 on the glass */
  --lcd-dim: #2b400c;      /* 4.8:1 on the glass: dim, still readable */
  --ok: #8aa86a;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;
  --gutter: 20px;
  --page: min(1120px, 100vw - var(--gutter) * 2);
  --device: var(--page);
  --row: 48px;

  color-scheme: dark;
}

* { box-sizing: border-box; }
html { background: var(--ink); overflow-x: clip; }
body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: "Technor", system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
p { margin: 0; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 9;
  background: var(--lime); color: #000; padding: 10px 16px; font-weight: 700; font-size: 12px;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
}
.skip:focus { left: var(--s4); top: var(--s4); }
:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }

/* ---- the two views, stacked on black exactly as his picture stacks them ---- */
.view { padding: var(--s7) var(--gutter); display: grid; justify-items: center; }
.view-front { padding-top: clamp(24px, 5vh, 48px); }
.view-edge { padding-top: var(--s7); }
.stack { width: var(--page); display: grid; gap: var(--s5); justify-items: center; }

/* ---- the machine ---- */
.device {
  position: relative;
  width: var(--device);
  container-type: inline-size;
  animation: float 13s ease-in-out infinite;
  filter: drop-shadow(0 0 54px rgba(122, 76, 196, 0.26));
}
.device-front { aspect-ratio: var(--front-ratio); }
.device-edge { width: var(--page); aspect-ratio: var(--edge-ratio); animation-duration: 17s; }
.device-shell { position: absolute; inset: 0; width: 100%; height: 100%; }
@keyframes float {
  0%, 100% { transform: translateY(0); filter: drop-shadow(0 0 54px rgba(122, 76, 196, 0.26)); }
  50% { transform: translateY(-5px); filter: drop-shadow(0 0 74px rgba(138, 88, 214, 0.34)); }
}

/* the screen, laid on the glass rect measured out of the render */
.screen {
  position: absolute;
  left: var(--front-screen-l); top: var(--front-screen-t);
  width: var(--front-screen-w); height: var(--front-screen-h);
  padding: 2.4cqw 3cqw;
  display: flex; flex-direction: column; justify-content: center;
  overflow: hidden;
  font-family: "Departure", ui-monospace, monospace;
  color: var(--lcd-ink);
  /* the readout is a share of the machine, so a line always fits its glass. The unit has to
     be set HERE, inside the container: a cqw inside a custom property on :root resolves
     against the root and comes out three times too big. */
  --lcd-size: clamp(13px, 2.85cqw, 20px);
}
.screen-rows { display: grid; gap: 0.3em; width: 100%; }
.screen-glare {
  position: absolute; inset: 0; pointer-events: none;
  background:
    repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 0 1px, rgba(0, 0, 0, 0) 1px 4px),
    linear-gradient(118deg, rgba(255, 255, 255, 0.2) 0 13%, rgba(255, 255, 255, 0) 32%);
}
.screen.scanning .screen-glare::after {
  content: ""; position: absolute; left: 0; right: 0; height: 26%;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.34) 55%, rgba(255, 255, 255, 0) 100%);
  animation: sweep 1.15s linear infinite;
}
@keyframes sweep { from { top: -28%; } to { top: 102%; } }

/* every line of the readout is set at one size and starts on the same left line: the only
   thing that changes between them is how dim the ink is, and even dim carries 4.5:1 */
.row { font-size: var(--lcd-size); line-height: 1.2; display: flex; gap: 0.6ch; align-items: baseline; min-width: 0; overflow: hidden; }
.row-wide { justify-content: space-between; }
.row b { font-weight: 400; white-space: nowrap; }
.row .dimmed { color: var(--lcd-dim); }
.row-extra { display: none; }
/* the readout drops what will not fit rather than pushing it out of the glass:
   under 400 the bar goes, under 900 the verdict word goes */
.row .bar { display: none; }
@media (min-width: 400px) { .row .bar { display: inline; } .row .verdict { display: none; } }
@media (min-width: 900px) { .row .verdict { display: inline; } }
.lcd-field {
  font: inherit; color: inherit; background: transparent; border: 0; padding: 0;
  flex: 1 1 auto; min-width: 0; width: 100%;
}
.lcd-field::placeholder { color: var(--lcd-dim); opacity: 1; }
.lcd-field:focus { outline: none; }
.lcd-field:focus-visible { outline: 2px solid var(--lcd-ink); outline-offset: 2px; }

/* the words printed on the shell */
.shell-brand {
  position: absolute; left: var(--front-brand-l); top: var(--front-brand-t);
  width: var(--front-brand-w); height: var(--front-brand-h);
  display: flex; align-items: center;
  font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
  color: var(--lime); white-space: nowrap;
}
.shell-brand .dot { padding: 0 0.5em; }
.shell-ghost {
  position: absolute; left: var(--front-ghost-l); top: var(--front-ghost-t);
  width: var(--front-ghost-w); height: var(--front-ghost-h);
  display: flex; align-items: center; justify-content: flex-end;
  font-size: clamp(20px, 4.4cqw, 32px); font-weight: 900; font-style: italic;
  letter-spacing: 0.02em; color: rgba(13, 6, 26, 0.44);
  text-shadow: 0 1px 0 rgba(197, 168, 240, 0.2);
}
/* the caption belongs to the key: it sits right under its rim, not adrift on the deck */
.shell-run-label {
  position: absolute; left: var(--front-run-l); top: calc(var(--front-run-t) + var(--front-run-h) + 0.5cqw);
  width: var(--front-run-w); text-align: center;
  font-size: 12px; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--cream);
}

/* the keys: rendered plates, pressed for real. One row, one centre line, one size.
   The deck spans the whole device so the keys can sit on their measured rects, so it must let
   every pointer through: without this it covers the screen and the address field cannot be
   clicked at all, while the keys themselves still work and the bug looks like nothing. */
.deck { position: absolute; inset: 0; pointer-events: none; }
.deck .key { pointer-events: auto; }
.key {
  font-family: "Technor", sans-serif; font-weight: 700; letter-spacing: 0.05em;
  color: var(--lime); background: transparent center / 100% 100% no-repeat;
  background-image: url("/img/key-capsule.png");
  border: 0; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: transform 0.08s ease-out, filter 0.18s ease-out, color 0.12s;
}
.deck .key {
  position: absolute; height: var(--front-key-h); width: var(--front-key-w); top: var(--front-key-t);
  font-size: 12px; padding: 0 0.6em;
}
.deck .key:nth-child(1) { left: var(--front-key0-l); }
.deck .key:nth-child(2) { left: var(--front-key1-l); }
.deck .key:nth-child(3) { left: var(--front-key2-l); }
.deck .key:nth-child(4) { left: var(--front-key3-l); }
.key[aria-selected="true"] { background-image: url("/img/key-capsule-lit.png"); color: #10140a; }
.key:hover { filter: brightness(1.14); }
.key:active { transform: translateY(1px) scale(0.985); }
.key-run {
  position: absolute; left: var(--front-run-l); top: var(--front-run-t);
  width: var(--front-run-w); height: var(--front-run-h);
  background: url("/img/key-round.png") center / 100% 100% no-repeat;
  border: 0; cursor: pointer; transition: transform 0.08s ease-out, filter 0.18s ease-out;
}
.key-run:hover { filter: brightness(1.2); }
.key-run:active { transform: scale(0.97); }
.key-run.busy { background-image: url("/img/key-round-lit.png"); }

/* ---- the readings in the black air, on the object's own edges ----
   Both columns carry the same three rows in the same shape: rule, label, bracket, value.
   The label and its bracket are always on two lines, so the rules line up across the machine
   and neither column can grow a hole the other one does not have. */
.facts { margin: 0; display: grid; gap: var(--s4); align-content: start; width: 100%; }
/* the rule belongs to the row, not around its words: a border here would make the fact a box
   and push every label 1 em off the container's own line */
.fact { position: relative; display: grid; gap: 2px; padding-top: var(--s3); }
.fact::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--line); }
.fact dt {
  display: grid; gap: 1px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--lime); line-height: 1.4;
}
.fact .sub { font-weight: 500; font-size: 12px; letter-spacing: 0.1em; color: var(--text-soft); white-space: nowrap; }
.fact dd {
  margin: 0; font-family: "RX100", ui-monospace, monospace; font-size: 20px; line-height: 1.35;
  color: var(--cream); font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}

/* ---- the sheet the open mode prints ---- */
.sheet { padding: 0 var(--gutter) var(--s6); display: grid; justify-items: center; }
.sheet-inner { width: var(--page); display: grid; gap: var(--s5); }
.sheet h2 { margin: 0; font-size: 24px; line-height: 1.2; font-weight: 900; color: var(--cream); }
.sheet .lead { max-width: 70ch; color: var(--text); font-size: 15px; }
.sheet .lead strong { color: var(--text-strong); font-weight: 700; }

.rows { display: grid; }
.line {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s1) var(--s5);
  align-items: baseline; padding: var(--s3) 0;
}
.line::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--line); }
.line:last-child::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line); }
.line-name { font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-strong); }
.line-why { grid-column: 1 / -1; font-size: 15px; line-height: 1.55; color: var(--text); max-width: 84ch; }
.line-cost {
  font-family: "RX100", ui-monospace, monospace; font-size: 14px; color: var(--lime);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.line.passed .line-cost { color: var(--ok); }
.line.failed .line-name::before { content: "fail "; color: var(--lime); }
.line.passed .line-name::before { content: "pass "; color: var(--ok); }

/* a field and the paragraph that explains it stand side by side on a wide window, so neither
   of them leaves half the column empty */
.field { display: grid; gap: var(--s2); }
.field label { font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--lime); }
.field input {
  font-family: "RX100", ui-monospace, monospace; font-size: 15px; color: var(--cream);
  background: #0b0716; border: 1px solid var(--line); border-radius: 8px;
  padding: 0 var(--s4); min-height: var(--row); width: 100%; min-width: 0;
}
.field input::placeholder { color: var(--text-soft); }
.note { font-size: 14px; line-height: 1.55; color: var(--text); max-width: 80ch; }

/* A wide key is the same moulded capsule, nine sliced so its round ends keep their shape at
   any width: the slice (146 of 548) is the pad plus the full corner radius, and the border
   width is that slice scaled to the key's own height (146 x 48 / 296 = 24). Stretching the
   plate instead squashes both ends into ovals, which is only visible at 2x and is exactly
   what the audit is for. */
.key-wide {
  min-height: var(--row); padding: 0 var(--s5); font-size: 12px; gap: 0.7em;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--cream);
  text-decoration: none; justify-self: start;
  background-image: none;
  border: 0 solid transparent;
  border-image: url("/img/key-capsule.png") 0 146 fill / 0 24px / 0 stretch;
}
.key-wide[aria-selected="true"] { border-image-source: url("/img/key-capsule-lit.png"); }
/* a key that is not live yet still has to be readable: it is dimmed, not switched off */
.key-wide[disabled], .key-wide[aria-disabled="true"] { opacity: 0.74; cursor: not-allowed; }
.key-row { display: flex; flex-wrap: wrap; gap: var(--s2); }

/* ---- the edge view: the ports read, and the plate carries the coin ---- */
.port { position: absolute; display: grid; align-items: center; justify-items: start; padding: 0 1.1cqw; }
.port-in { left: var(--edge-port0-l); top: var(--edge-port0-t); width: var(--edge-port0-w); height: var(--edge-port0-h); }
.port-out { left: var(--edge-port1-l); top: var(--edge-port1-t); width: var(--edge-port1-w); height: var(--edge-port1-h); }
.port-label {
  position: absolute; bottom: calc(100% + 1cqw); left: 0; white-space: nowrap;
  font-size: 12px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--cream); display: grid; gap: 1px; line-height: 1.35;
}
.port-label .sub { font-size: 12px; font-weight: 500; letter-spacing: 0.1em; color: var(--lime); }
.port-value {
  font-family: "RX100", ui-monospace, monospace; font-size: 15px;
  color: var(--cream); font-variant-numeric: tabular-nums; white-space: nowrap; line-height: 1;
}
.plate {
  position: absolute;
  left: var(--edge-plate-l); top: var(--edge-plate-t);
  width: var(--edge-plate-w); height: var(--edge-plate-h);
  padding: 1.3cqw 1.6cqw;
  display: grid; align-content: center; gap: 0.7cqw;
}
.plate-title {
  font-size: 20px; font-weight: 900; color: var(--cream);
  display: flex; align-items: baseline; gap: 0.8em; flex-wrap: wrap; line-height: 1.2;
}
.plate-state {
  font-size: 12px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--lime);
}
.mint {
  font-family: "RX100", ui-monospace, monospace; font-size: 14px;
  line-height: 1.5; color: var(--cream); overflow-wrap: anywhere; user-select: all;
  background: rgba(7, 3, 14, 0.52); border-radius: 6px; padding: 9px var(--s4);
}
.plate-keys { display: grid; grid-template-columns: 1fr 1fr; gap: 0.7cqw; }
.plate .key-wide { padding: 0 var(--s4); justify-self: stretch; }

.risk { margin: var(--s6) auto 0; max-width: 78ch; text-align: center; font-size: 14px; color: var(--text-soft); }

/* ---- the phone is its own composition: the machine first, its keys at thumb size ---- */
@media (max-width: 899px) {
  .view { padding: var(--s6) var(--gutter); }
  .device-front { order: -1; margin-bottom: 122px; }
  .deck {
    position: absolute; inset: auto 0 auto 0; top: calc(100% + 14px);
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding-right: 74px;
  }
  .deck .key {
    position: static; left: auto; top: auto;
    width: auto; height: var(--row); min-width: 0;
    font-size: 14px; padding: 0 var(--s4); letter-spacing: 0.08em;
  }
  .key-run { left: auto; right: 0; top: calc(100% + 34px); width: 58px; height: 58px; }
  .shell-run-label { display: none; }
  /* the six readings become ONE two column grid under the machine: the two lists hand their
     children to the stack (display: contents) so no column is left with a hole in it */
  .stack {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4) var(--s5);
    justify-items: stretch;
  }
  .device-front { grid-column: 1 / -1; justify-self: center; }
  .facts { display: contents; }
}

/* Below 1000 the edge stops being a picture with text on it and becomes the same thing
   unfolded: the view, its two readings, its plate. Over the picture at this size the plate
   would hold type under 12px or spill off the plastic. */
@media (max-width: 999px) {
  /* the edge view unfolds: the picture, then the two ports as readings, then the plate.
     Laid over the picture they would land on top of it, because the picture itself is the
     one absolutely positioned thing in the box. */
  .device-edge {
    aspect-ratio: auto; height: auto;
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s5); align-items: start;
  }
  .device-edge .device-shell { position: static; grid-column: 1 / -1; width: 100%; height: auto; }
  /* a static port is a reading in a column, so it drops the rect it wore on the picture:
     left over width and height from the render would squeeze its value out of its own box */
  .port {
    position: static; width: auto; height: auto;
    display: grid; justify-items: start; gap: 2px; padding-top: var(--s3);
  }
  .port::before { content: ""; display: block; height: 1px; background: var(--line); width: 100%; margin-bottom: var(--s2); }
  .port-label { position: static; }
  .plate { position: static; grid-column: 1 / -1; width: 100%; height: auto; padding: 0; display: grid; gap: var(--s3); }
  .mint { padding: var(--s3) var(--s4); }
  .plate-keys { grid-template-columns: 1fr; gap: var(--s2); }
}

/* the smallest phone pays for its gutter in type: a narrower margin keeps the readout and
   the bracket under each label on one line, which is what they are for */
@media (max-width: 374px) {
  :root { --gutter: 14px; }
  .stack { gap: var(--s4); }
}

@media (min-width: 900px) {
  :root { --gutter: 32px; --device: min(50vw, 680px); }
  .view { padding: var(--s7) var(--gutter); }
  /* the machine and the two columns are one composition inside the page column: the columns
     take what the machine leaves, evenly, and never touch the edge of the window */
  .stack {
    grid-template-columns: minmax(0, 1fr) var(--device) minmax(0, 1fr);
    justify-content: center; align-items: start; gap: var(--s6);
  }
  .facts-left { text-align: right; }
  .row-extra { display: flex; }
  .field {
    grid-template-columns: minmax(260px, 420px) minmax(0, 1fr);
    gap: var(--s2) var(--s6); align-items: start;
  }
  .field label { grid-column: 1 / -1; }
  .field input { grid-column: 1; }
  .field .note { grid-column: 2; grid-row: 2 / span 2; }
}

@media (prefers-reduced-motion: reduce) {
  .device { animation: none; }
  .screen.scanning .screen-glare::after { animation: none; }
  * { transition-duration: 1ms !important; }
}
