/* ==========================================================================
   BLACK ACE CARDS - PRE-LAUNCH
   One screen at every size. An index grid of the deck, with a nine-cell
   console cut out of it for the notification form.

   Dark-only by deliberate brand decision (see DESIGN.md: "black drench carries
   80% of the surface"). There is no light mode to ship here - the page is a
   sealed room.
   ========================================================================== */

/* --------------------------------------------------------------------------
   FONTS
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Archivo";
  src: url("../assets/fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 400 700;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../assets/fonts/jetbrains-mono-var.woff2") format("woff2-variations");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
/* The brand display face, from Dark Roast. One weight, one job: the console
   headline. It is a condensed blackletter, which is why it needs positive
   tracking where Archivo wanted negative - the apexes collide otherwise. Its
   internal ascender runs to 1200/1000 upem, so its line box is taller than its
   caps and the line-height here is doing more work than the number suggests.

   LICENSING: Zentras is Alit Design, released for PERSONAL USE ONLY. This page
   is commercial. See README before launch. */
@font-face {
  font-family: "Zentras";
  src: url("../assets/fonts/zentras-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Ground and surfaces. The page ground is true black. */
  --black:      #000000;
  --black-2:    #0E0E10;
  --black-3:    #1F1F24;

  /* Text. --ink-3 is the floor: 5.5:1 on --black, verified. */
  --ink:        #F4F2EE;
  --ink-2:      #A9A59D;
  --ink-3:      #8A8680;

  /* The one accent. Sampled from the logo. Nothing else is coloured. */
  --gold:       #C7A564;
  --gold-deep:  #8F7340;
  --gold-lit:   #EFD9A6;   /* the falloff either side of the sheen's core */
  --gold-flare: #FFF4DA;   /* the core of the sheen. Used nowhere else. */

  /* Structure. The rules are white, held back just enough that 1px lines on
     true black do not vibrate; the crosshairs are the monogram gold. Both are
     single tokens - dial --rule up or down in one place. */
  --rule:       rgb(255 255 255 / 0.30);
  --cross:      #C7A564;

  /* Crosshair geometry. --cross-clear is the black square that punches the
     rule back so the lines stop short of the mark instead of running through
     it; keep it larger than --cross-arm or the gap disappears. */
  --cross-arm:   13px;
  --cross-clear: 27px;

  --font-sans:  "Archivo", "Helvetica Neue", Arial, sans-serif;
  /* The fallback is the same stack as --font-sans on purpose: if Zentras is
     ever pulled for the licence, the headline lands back on Archivo rather
     than on whatever the OS calls a serif. */
  --font-display: "Zentras", "Archivo", "Helvetica Neue", Arial, sans-serif;
  --font-mono:  "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --r-ui:       2px;

  --pad:        clamp(14px, 2.4vw, 26px);
  --rowmin:     clamp(112px, 16.5vh, 184px);
  --ease:       cubic-bezier(0.16, 1, 0.3, 1);

  /* Load choreography. The frame draws itself, then everything else arrives
     in one move. The crosshairs are not in here: they are painted from the
     first frame, so the rules have marks to draw between. */
  --t-rule:      90ms;
  --t-art:      980ms;

  /* z-scale. Extend this, never invent a magic number. */
  --z-cross:    5;
  --z-plate:    6;   /* the cell index, above the artwork AND the crosshair */
  --z-grain:   40;
  --z-skip:    70;
}

/* --------------------------------------------------------------------------
   RESET / BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--black);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; height: auto; }
ul, dl, dd { margin: 0; padding: 0; list-style: none; }
h1, h2, p, dt, figure, figcaption { margin: 0; }

::selection { background: var(--gold); color: var(--black); }

:where(a, button, input):focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* Read by assistive tech, never painted. Carries the real string for text that
   has been split into per-character spans. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: fixed; top: 8px; left: 8px; z-index: var(--z-skip);
  padding: 10px 14px;
  background: var(--gold); color: var(--black);
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none;
  transform: translateY(-200%);
}
.skip:focus { transform: none; }

/* Fine grain, fixed so it never repaints while scrolling. */
.grain {
  position: fixed; inset: 0; z-index: var(--z-grain);
  pointer-events: none; opacity: 0.035;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/></filter>\
<rect width='160' height='160' filter='url(%23n)'/></svg>");
}

/* --------------------------------------------------------------------------
   SHELL
   -------------------------------------------------------------------------- */
.shell {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  /* No top padding: the grid's column rules have to reach the top edge and
     run off it, so nothing may sit above row 1. */
  padding: 0 clamp(12px, 1.6vw, 20px) clamp(12px, 1.6vw, 20px);
  gap: clamp(12px, 1.6vw, 20px);
}

/* --------------------------------------------------------------------------
   THE STATUS BAR - row 1 of the grid, not a header floating above it.
   Putting it in the grid is what lets the column rules start at the top of the
   window instead of below a gap, and it is why the clocks sit one per column.
   -------------------------------------------------------------------------- */
.bar { display: contents; }

.cell--bar { padding: clamp(9px, 1.1vw, 15px) var(--pad); }

/* Row 1 sits on the window edge, so its crosshairs would be sliced in half by
   it. The first full row of marks is the one under the bar. */
.cell--bar::after { display: none; }

.clock { display: grid; align-content: start; gap: 5px; }
.clock__pip { width: 7px; height: 7px; background: var(--gold); }

.clock__city {
  font-family: var(--font-sans);
  font-weight: 700;
  font-stretch: 74%;
  font-size: clamp(13px, 1.5vw, 17px);
  line-height: 1;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  color: var(--ink);
}

.clock__time {
  font-size: clamp(11px, 1.15vw, 13px);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--ink-2);
}
.clock__off { color: var(--ink-3); }


/* ==========================================================================
   THE INDEX GRID
   Desktop 6 x 3 = 18 cells. The console occupies the 3 x 3 block at columns
   2-4; seven card cells and two text cells fill the other nine.

   The rules are NOT borders. Each cell paints its own right and bottom rule as
   a one-pixel background bar sitting inside a transparent border, which is
   what lets them draw themselves open on load. The transparent borders stay so
   the geometry matches what real borders would have produced.
   ========================================================================== */
.grid {
  flex: 1;
  display: grid;
  align-content: stretch;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-template-rows: auto repeat(3, minmax(var(--rowmin), auto));

  /* Only the left rule. There is no rule across the very top: the column
     rules are meant to run off the edge of the window, and a horizontal line
     at y=0 would cap them. */
  border-left: 1px solid transparent;
  background-image: linear-gradient(var(--rule), var(--rule));
  background-origin: border-box;
  background-repeat: no-repeat;
  background-position: left top;
  background-size: 1px 100%;
}

.cell,
.console__box {
  position: relative;
  margin: 0;
  padding: var(--pad);
  min-width: 0;

  border-right: 1px solid transparent;
  border-bottom: 1px solid transparent;
  background-image:
    linear-gradient(var(--rule), var(--rule)),
    linear-gradient(var(--rule), var(--rule));
  background-origin: border-box;
  background-repeat: no-repeat;
  background-position: right top, left bottom;
  background-size: 1px 100%, 100% 1px;
}

/* The crosshair on each intersection. Its black square punches the rules back
   so they stop short of the mark, then two hard-stop bars draw the cross on
   top. Solid colour throughout - no gradation. */
.cell::after,
.console__box::after {
  content: "";
  position: absolute;
  top: calc(var(--cross-clear) / -2);
  left: calc(var(--cross-clear) / -2);
  width: var(--cross-clear);
  height: var(--cross-clear);
  z-index: var(--z-cross);
  pointer-events: none;

  background-color: var(--black);
  background-image:
    linear-gradient(var(--cross), var(--cross)),
    linear-gradient(var(--cross), var(--cross));
  background-repeat: no-repeat;
  background-size: var(--cross-arm) 1px, 1px var(--cross-arm);
  background-position: center center, center center;

}

/* ---- card cells ---------------------------------------------------------- */
.cell--card {
  display: grid;
  place-items: center;
  /* Query container so the window can be sized against the cell's shorter
     side. Sizing off width alone overflows the cell vertically at narrow
     desktop widths, where cells are wide and short. */
  container-type: size;
}

/* The index number, in the corner, like a plate on a case.

   It outranks two things that would otherwise cover it: the window, which grows
   over this corner on hover at some viewport sizes, and the crosshair's black
   square, which is painted above the rules to punch a gap in them and would
   take the digits with it. */
.cell__index {
  position: absolute; top: var(--pad); left: var(--pad);
  z-index: var(--z-plate);
  font-size: 10.5px; letter-spacing: 0.1em;
  color: var(--ink-3);
  pointer-events: none;
}

/* .shot holds the window and its plate code together. The code is taken out of
   flow so it can hang below the window without moving it.

   Nothing may clip this element: the plate sits outside its box and the window
   scales past its edge on hover, so any clip-path here shears both. */
.shot { position: relative; display: block; }

/* A window onto one detail of a card, never the whole card. The deck is the
   thing being withheld, so the grid only ever shows fragments of it, and no
   two cells frame the same part of their card.

   The box is always at full size and the WINDOW is what changes: at rest
   clip-path holds it nearly shut, and hover opens it. Because the artwork
   inside keeps its rendered size throughout, opening the window uncovers more
   of the design rather than magnifying the same sliver. The small scale on
   hover is there for weight, not for zoom. */
.crop {
  display: block;
  position: relative;
  overflow: hidden;
  /* Fallback for engines without container query units: cap against the row
     minimum so the window can never be taller than the cell. */
  width: min(74%, calc(var(--rowmin) - 2 * var(--pad) - 10px));
  aspect-ratio: 1;

  clip-path: inset(var(--shut, 21%));
  /* The deck prints on black stock, so at rest the veil is desaturation, not
     darkness: the gold goes to silver and the card sits back. Crushing the
     brightness the way a white-stock card could take would just erase it. */
  filter: grayscale(1) brightness(0.88) contrast(1.03);
  transition:
    clip-path 560ms var(--ease),
    transform 560ms var(--ease),
    filter    560ms var(--ease);
  /* Held so the composited layer survives the transform. Without it the
     browser re-rasterises once the scale settles and the edge visibly snaps by
     a pixel a beat after the card finishes opening. */
  will-change: transform;
}

/* Roughly 65% of the cell, measured against each axis separately. A flat
   percentage of the shorter side leaves the window tiny in the tall, narrow
   cells between 768 and 1024, so width takes the looser share and height stays
   the hard ceiling that keeps it inside the cell. */
@supports (width: 1cqmin) {
  /* The 200px ceiling keeps the window inside the source: 200 x 1.11 hover
     scale x 2 DPR = 444px, under the 494px the files are cropped to. */
  .crop { width: min(84cqw, 82cqh, 200px); }
}

/* The source files are pre-cropped to precisely the square this window
   reveals, so the artwork fills it rather than being an oversized card slid
   into place. Nothing of the card beyond this square is ever sent, which is
   the only way "the deck is withheld" survives someone opening the file URL.

   The artwork is inert to the pointer. That kills dragging it out, and it also
   means a right-click never targets an <img>, so the browser offers its normal
   page menu instead of "Save image as" - no contextmenu handler needed, and the
   rest of the page keeps a working right-click.

   Hover is unaffected: it is bound to .cell--card, and events pass straight
   through this element to it.

   This stops casual copying and nothing more. The file is still fetched to be
   drawn, so it remains in cache, in devtools, and at its own URL. See the note
   in README.md about what would actually reduce exposure. */
.crop img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;

  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;   /* no long-press save sheet on iOS */
}

/* The plate. It used to carry a production code in the deck's own grammar
   (rank, suit, artwork series); it now names the card outright, because a
   person hovering a fragment wants to know which card it is and a code makes
   them decode it first. */
.plate {
  position: absolute;
  top: calc(100% + 20px);
  left: 0;
  width: 100%;
  text-align: center;
  pointer-events: none;
}

/* The name types in one character at a time, so the box has to hold its full
   width from the start - otherwise the line would grow outward from its centre
   and read as inflating rather than typing.

   The names are not all the same length, so each one carries its own --len in
   the markup. The font is monospaced, so one character is exactly `1ch` of
   advance plus the tracking that follows it, which makes the reservation exact
   rather than approximate. Uppercase here and title case in the markup: every
   other small label on the page is set this way, and the DOM keeps a form that
   reads naturally out loud. */
.plate__txt {
  display: inline-block;
  min-width: calc(var(--len, 14) * (1ch + 0.16em));
  text-align: left;
  white-space: pre;
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

/* The caret sits after whatever has been typed so far. */
.plate__txt::after {
  content: "";
  display: inline-block;
  width: 1ch;
  height: 1em;
  vertical-align: -0.15em;
  background: var(--gold);
  opacity: 0;
}
.cell--card:hover .plate__txt.is-typing::after { opacity: 1; }

/* Gated on .is-settled, which main.js adds once the entrance has finished. A
   pointer resting on a cell during the reveal would otherwise open the window
   over artwork that is still wiping in. */
@media (hover: hover) and (pointer: fine) {
  .is-settled .cell--card:hover .crop,
  .is-settled .cell--card:focus-within .crop {
    --shut: 0%;
    transform: scale(1.11);
    filter: grayscale(0) brightness(1.06) contrast(1);
  }

}

/* ---- text cells ---------------------------------------------------------- */
.cell--say { display: flex; align-items: flex-end; }

.say {
  font-family: var(--font-sans);
  font-weight: 700;
  font-stretch: 72%;
  font-size: clamp(15px, 1.35vw, 20px);
  line-height: 1.06;
  letter-spacing: 0.004em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* ---- desktop placement --------------------------------------------------- */
@media (min-width: 900px) {
  /* Row 1: one clock per column, then three open bays. */
  .bar > .cell:nth-child(1) { grid-column: 1; grid-row: 1; }
  .bar > .cell:nth-child(2) { grid-column: 2; grid-row: 1; }
  .bar > .cell:nth-child(3) { grid-column: 3; grid-row: 1; }
  .bar > .cell:nth-child(4) { grid-column: 4; grid-row: 1; }
  .bar > .cell:nth-child(5) { grid-column: 5; grid-row: 1; }
  .bar > .cell:nth-child(6) { grid-column: 6; grid-row: 1; }

  .cell--card[data-idx="01"] { grid-column: 1; grid-row: 2; }
  .cell--card[data-idx="02"] { grid-column: 1; grid-row: 3; }
  .cell--card[data-idx="03"] { grid-column: 1; grid-row: 4; }

  .console                   { grid-column: 2 / span 3; grid-row: 2 / span 3; }

  .cell--card[data-idx="04"] { grid-column: 5; grid-row: 2; }
  .cell--card[data-idx="05"] { grid-column: 5; grid-row: 3; }
  .cell--card[data-idx="06"] { grid-column: 5; grid-row: 4; }

  .cell--say-a               { grid-column: 6; grid-row: 2; }
  .cell--card[data-idx="07"] { grid-column: 6; grid-row: 3; }
  .cell--say-b               { grid-column: 6; grid-row: 4; }
}

/* ==========================================================================
   THE CONSOLE
   Nine cells of the grid, cut out and re-purposed. Its interior keeps the same
   rule-and-crosshair rhythm so it reads as replaced grid, not as a pasted-in
   card.
   ========================================================================== */
/* The console keeps the right and bottom rules it inherits as a .cell - they
   are the block's own edges, and nothing inside it is positioned to draw them.
   Interior boxes draw only the divisions between themselves. */
.console { padding: 0; display: grid; }

.console__pane {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: 1fr auto auto;
  min-width: 0;
}
.console__pane[hidden] { display: none; }

/* The issued pane is focused programmatically so a screen reader lands on the
   credential that just appeared. That is not keyboard navigation, so no ring. */
.console__pane--issued:focus { outline: none; }

.console__box--head   {
  grid-column: 1 / span 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.console__box--note   { grid-column: 3; display: flex; align-items: flex-end; }
.console__box--field  { grid-column: 1 / span 3; }
.console__box--action { grid-column: 1 / span 2; }
.console__box--status { grid-column: 3; display: flex; flex-direction: column; justify-content: center; gap: 6px; }

/* Interior boxes draw only the divisions between themselves. The rules along
   the console's own right and bottom edges belong to the console, so a box on
   one of those edges drops that background layer entirely - the borders are
   already transparent, so hiding the border would not have removed the line.

   --head   both: divides head|note, and row 1 from row 2
   --note   bottom only: its right side is the console's edge
   --field  bottom only: spans the full width
   --action right only: divides action|status; the console draws the bottom
   --status neither: the console draws both edges it touches
   --cred   bottom only: the divider above the reservation
   --ante   neither: the console draws the bottom */
.console__box--note,
.console__box--field,
.console__box--cred {
  background-image: linear-gradient(var(--rule), var(--rule));
  background-position: left bottom;
  background-size: 100% 1px;
}

.console__box--action {
  background-image: linear-gradient(var(--rule), var(--rule));
  background-position: right top;
  background-size: 1px 100%;
}

.console__box--status,
.console__box--ante { background-image: none; }

/* The issued pane keeps the same box grammar as the form, so its divider and
   its crosshair come from the same rules rather than a one-off border. */
.console__box--cred,
.console__box--ante { grid-column: 1; }

.console__head {
  font-family: var(--font-display);
  font-weight: 400;
  /* Zentras carries less width per character than condensed Archivo did, so
     the same clamp reads a size smaller. The ceiling comes up to hold the
     block at roughly the height it had. */
  font-size: clamp(30px, 4.7vw, 68px);
  /* Loose enough that the apexes of one line clear the descender-less caps of
     the next, tight enough that three lines still read as one block. */
  line-height: 1.0;
  letter-spacing: 0.012em;
  text-transform: uppercase;
  color: var(--ink);
  text-wrap: balance;
  margin: 0;
}

/* The maker's mark. Sits above the headline rather than beside it: the box is
   already giving the headline every pixel of its width, and the same
   mark-over-name relationship is what the credential uses lower down. */
.console__mark {
  display: block;
  width: auto;
  height: clamp(22px, 2.6vw, 34px);
  margin-bottom: clamp(8px, 1.1vw, 15px);
  flex: none;
  /* Same reason as the card art: nothing to drag out, and a right-click gets
     the page menu rather than "Save image as". */
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}

.note {
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* ---- the field ----------------------------------------------------------- */
.form { display: grid; gap: 8px; }

.form__label {
  font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-3);
}

.form__input {
  width: 100%;
  padding: 15px 16px;
  background: var(--black);
  border: 1px solid var(--black-3);
  border-radius: var(--r-ui);
  color: var(--ink);
  font: 400 15px/1.2 var(--font-mono);
  letter-spacing: 0.02em;
  transition: border-color 200ms var(--ease);
}
.form__input::placeholder { color: var(--ink-3); }
.form__input:hover { border-color: var(--gold-deep); }
.form__input:focus { border-color: var(--gold); outline-offset: 0; }
.form__input[aria-invalid="true"] { border-color: var(--gold); }

/* Honeypot. Off-screen, never displayed, never removed. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%; min-height: 52px;
  padding: 14px 22px;
  background: var(--gold);
  color: var(--black);
  border: 1px solid var(--gold);
  border-radius: var(--r-ui);
  font: 700 12px/1 var(--font-mono);
  letter-spacing: 0.18em; text-transform: uppercase; text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 180ms var(--ease), transform 120ms var(--ease);
}
.btn:hover { background: var(--ink); border-color: var(--ink); }
.btn:active { transform: scale(0.985); }
.btn[disabled] { opacity: 0.55; cursor: progress; }
.btn[hidden] { display: none; }

/* The one thing on this pane anyone has to decide about, so it is set larger
   than the form's button rather than the same size. */
.btn--ante {
  position: relative;      /* anchors the sheen ring */
  background: transparent;
  color: var(--gold);
  border-color: var(--gold-deep);
  width: auto;
  min-height: 60px;
  padding: 16px 32px;
  font-size: 14.5px;
  letter-spacing: 0.12em;
}

.btn--ante:hover { background: var(--gold); border-color: var(--gold); color: var(--black); }

/* A control that is present but not yet wired steps back through colour role,
   not opacity - dimming gold on black drops it to about 2.5:1, unreadable.

   Scoped away from .btn--ante on purpose. That button is a mock-up that keeps
   its live look, and an unscoped attribute selector here (0-2-0) would outrank
   the sheen's `color: transparent` (0-1-0) regardless of source order, painting
   the label solid and hiding the gradient it is clipped to. */
.btn[aria-disabled="true"]:not(.btn--ante) {
  color: var(--ink-2);
  border-color: var(--black-3);
  cursor: not-allowed;
}
.btn[aria-disabled="true"]:not(.btn--ante):hover {
  background: transparent;
  border-color: var(--black-3);
  color: var(--ink-2);
}

/* ---- status + helper ----------------------------------------------------- */
.status { font-size: 11.5px; line-height: 1.45; color: var(--gold); }
.status:empty { display: none; }

.helper {
  font-size: 10.5px; line-height: 1.5; letter-spacing: 0.04em;
  color: var(--ink-3);
}

/* ==========================================================================
   STATE 2 - THE CREDENTIAL AND THE ANTE
   ========================================================================== */
.console__pane--issued {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-content: start;
}

.cred {
  background: var(--black-2);
  border: 1px solid var(--gold-deep);
  border-radius: var(--r-ui);
  overflow: hidden;
}

/* Hazard stripes across the head of the label - the strip printed on the edge
   of a case or a controlled-goods carton. Hard stops, no gradation. */
.cred__tape {
  height: 14px;
  background-image: repeating-linear-gradient(
    -45deg,
    var(--ink)   0 7px,
    var(--black) 7px 15px
  );
}

.cred__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 16px;
  padding: 16px var(--pad);
  border-bottom: 1px solid var(--black-3);
}

.cred__mark { width: 16px; height: auto; margin-bottom: 10px; }

.cred__brand {
  font-family: var(--font-sans);
  font-weight: 700; font-stretch: 70%;
  font-size: clamp(21px, 2.5vw, 33px);
  line-height: 1; text-transform: uppercase; letter-spacing: 0.002em;
  color: var(--ink);
}
.cred__kind {
  font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--gold); margin-top: 5px;
}

.cred__field-key {
  font-size: 9.5px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-3);
}
.cred__body .cred__field-key { margin-top: 16px; }

/* The holder's own address. Long addresses have no natural break points, so
   allow a break anywhere - but only where one is needed, which break-all does
   not do. */
.cred__holder {
  font-size: clamp(12px, 1.35vw, 15px);
  letter-spacing: 0.01em;
  color: var(--ink);
  overflow-wrap: anywhere;
  margin-top: 3px;
}

.cred__no { text-align: right; }
.cred__seq {
  font-family: var(--font-sans);
  font-weight: 700; font-stretch: 68%;
  font-size: clamp(34px, 4.6vw, 62px);
  line-height: 0.86;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.cred__meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-bottom: 1px solid var(--black-3);
}
.cred__meta-item {
  padding: 11px var(--pad);
  border-right: 1px solid var(--black-3);
  min-width: 0;
}
.cred__meta-item:last-child { border-right: 0; }
.cred__meta dt {
  font-size: 9.5px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-3);
}
.cred__meta dd {
  font-size: 11.5px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink); margin-top: 4px; overflow-wrap: anywhere;
}

/* Three tracks so the terms line is centred against the label itself, not
   against whatever space the barcode and the marks happen to leave. */
.cred__foot {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 14px;
  padding: 11px var(--pad) 13px;
}
.cred__terms { text-align: center; }
.cred__marks { justify-self: end; }

/* A data-matrix-style block, laid out from the access code the same way the
   barcode is. Not a scannable symbol - a printed mark that carries the
   holder's own code rather than a texture chosen to look busy. */
.cred__matrix {
  display: grid;
  grid-template-columns: repeat(13, 1fr);
  width: clamp(52px, 5.4vw, 74px);
  aspect-ratio: 1;
  margin: 11px 0 0 auto;
}
.cred__matrix i { display: block; aspect-ratio: 1; }
.cred__matrix i.on { background: var(--ink); }

/* Bars are generated from the access code - it is that string encoded, not a
   decorative texture. */
.cred__barcode { display: flex; align-items: flex-end; gap: 2px; height: 32px; }
.cred__barcode i { display: block; height: 100%; background: var(--ink); }

/* Package handling marks, set the way they are actually printed: bare
   silhouettes on a baseline, not glyphs in boxes. Their widths differ because
   the symbols differ, which is what makes the row read as real. */
.cred__marks {
  display: flex; align-items: flex-end;
  gap: clamp(10px, 1.1vw, 16px);
}
.cred__marks img {
  height: clamp(19px, 1.9vw, 26px);
  width: auto;
  display: block;
}

.cred__terms {
  font-size: 9.5px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}

/* ---- the reservation ----------------------------------------------------- */
.ante { padding: 0; }



.ante__head {
  font-family: var(--font-sans);
  font-weight: 700; font-stretch: 70%;
  font-size: clamp(19px, 2.1vw, 27px);
  line-height: 1.02; text-transform: uppercase; letter-spacing: 0.002em;
  color: var(--ink);
  margin-top: 9px;
  text-wrap: balance;
}

/* The one thing a reader hesitates over, so it is set apart from the legal
   fine print underneath and given the accent to itself. */
.ante__refund {
  margin-top: 11px;
  font-size: 12.5px;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: var(--gold);
}

.ante__act {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 12px 18px;
  margin-top: 18px;
}
.ante__fine {
  flex: 1 1 220px;
  font-size: 10.5px; line-height: 1.5; color: var(--ink-3);
}

/* ==========================================================================
   FOOT
   The reference sets its logotype enormous, but its page scrolls. This one is
   a single screen and the field has to stay in view, so the wordmark signs the
   page rather than filling it, and the control notice sits alongside.
   ========================================================================== */
.foot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: end;
  gap: clamp(18px, 3vw, 48px);
}

.foot__wordmark-band { display: flex; align-items: flex-end; }
.foot__wordmark {
  height: clamp(34px, 5.4vh, 60px);
  width: auto;
  opacity: 0.62;
}

.foot__legal {
  font-size: 10px;
  line-height: 1.6;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  max-width: 92ch;
}
/* Its own narrow column, stacked, so it never adds a line to the legal text
   beside it: on a laptop that one line is the difference between fitting the
   window and scrolling. */
.foot__links {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-size: 10px;
  line-height: 1.6;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.foot__links a { color: var(--ink-2); text-underline-offset: 2px; }
.foot__links a:hover { color: var(--gold); }
.foot__links a:focus-visible { outline: 1px solid var(--gold); outline-offset: 2px; }

/* ==========================================================================
   MOTION
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {

  /* ---- the load ---------------------------------------------------------
     The page arrives as an empty frame. The crosshairs and the foot (the
     wordmark and the control notice) are simply present from the first paint:
     they are the page's registration, not part of the reveal. Then the rules
     draw themselves between the crosshairs, and only once they have finished
     does everything else wipe up from its own bottom edge.

     Every revealed element shares one delay and one duration, so the page
     resolves in a single move rather than a cascade. Only the rules carry a
     stagger, which is what makes the frame read as being drawn across. */

  .grid {
    background-size: 1px 0;
    transition: background-size 760ms var(--ease) var(--t-rule);
  }
  .is-ready .grid { background-size: 1px 100%; }

  .cell, .console__box {
    background-size: 1px 0, 0 1px;
    transition: background-size 560ms var(--ease) calc(var(--t-rule) + var(--ld, 0ms));
  }
  .is-ready .cell,
  .is-ready .console__box { background-size: 1px 100%, 100% 1px; }

  .console__box--note,
  .console__box--field,
  .console__box--cred { background-size: 0 1px; }
  .is-ready .console__box--note,
  .is-ready .console__box--field,
  .is-ready .console__box--cred { background-size: 100% 1px; }

  .console__box--action { background-size: 1px 0; }
  .is-ready .console__box--action { background-size: 1px 100%; }

  .clock__pip, .clock__city, .clock__time,
  .crop img, .say,
  .console__mark, .console__head, .note, .form,
  .console__pane--form .btn, .helper,
  .cred, .ante {
    clip-path: inset(100% 0 0 0);
    transition: clip-path 640ms var(--ease) var(--t-art);
  }
  /* The cell index plates belong to the content, not the frame, so they wait
     with everything else. Pseudo-elements cannot go inside :is(). */
  .cell__index {
    clip-path: inset(100% 0 0 0);
    transition: clip-path 640ms var(--ease) var(--t-art);
  }
  .is-ready .cell__index { clip-path: inset(0 0 0 0); }

  .is-ready :is(.clock__pip, .clock__city, .clock__time,
  .crop img, .say,
  .console__mark, .console__head, .note, .form,
  .console__pane--form .btn, .helper,
  .cred, .ante) { clip-path: inset(0 0 0 0); }

  /* ---- the flicker ------------------------------------------------------
     The signature ambient motion: characters drop out individually, the way a
     panel does when something upstream is drawing power. Each character has
     its own delay and period, so the string shimmers rather than blinking as
     one block.

     Deliberately slow. Two dips across about a second is roughly two flashes
     per second, under the WCAG 2.3.1 three-per-second threshold, and it is one
     character at a time rather than a full-field luminance change. The field,
     the button, the status line and the holder's own address never flicker. */
  @keyframes flicker {
    0%,   88%    { opacity: 1; }
    88.6%        { opacity: 0.14; }
    89.9%        { opacity: 1; }
    91.3%        { opacity: 0.44; }
    92.6%, 100%  { opacity: 1; }
  }
  /* Words hold together; only the characters inside them flicker. */
  .fk-w { display: inline-block; white-space: nowrap; }
  .fk-c {
    display: inline-block;
    animation: flicker var(--dur, 8s) var(--fd, 0s) infinite steps(1, end);
  }

  /* ---- the foil sheen ---------------------------------------------------
     Light travelling across a stamped plate, on the type and on the rule
     around it, so the button reads as one object catching the light.

     The loop is seamless because the gradient TILES and the animation moves it
     by exactly one tile width. The band's first and last colour stops are the
     same, so tile meets tile invisibly and the sweep has no restart: animating
     a single non-repeating gradient from one edge to the other is what caused
     the hard cut, because the end state and the start state were different
     pictures. 90deg keeps the tile seam perfectly vertical - an angled band
     would step at each repeat. */
  @keyframes plateSheen {
    from { background-position: 0 0; }
    to   { background-position: -460px 0; }
  }

  @supports ((background-clip: text) or (-webkit-background-clip: text)) {
    .btn--ante {
      background-image: linear-gradient(
        90deg,
        var(--gold)       0 42%,
        var(--gold-lit)   46%,
        var(--gold-flare) 50%,
        var(--gold-lit)   54%,
        var(--gold)       58% 100%
      );
      background-size: 460px 100%;
      background-repeat: repeat;
      -webkit-background-clip: text;
      background-clip: text;
      color: transparent;
      animation: plateSheen 5s linear infinite;
    }

    /* Hover fills the button solid, so the sheen stands down and the label
       goes back to real ink against it. */
    .btn--ante:hover,
    .btn--ante:focus-visible {
      background-image: none;
      -webkit-background-clip: border-box;
      background-clip: border-box;
      color: var(--black);
      animation: none;
    }
  }

  /* The same light crossing the rule around the type. The gradient is painted
     over the border and masked down to that ring. It rests on --gold-deep,
     exactly the border underneath, so the no-mask fallback is seamless. */
  @supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
    .btn--ante::before {
      content: "";
      position: absolute;
      inset: -1px;           /* absolute insets sit on the padding box, so this
                                reaches back out over the 1px border */
      pointer-events: none;
      padding: 1px;
      background-image: linear-gradient(
        90deg,
        var(--gold-deep)  0 42%,
        var(--gold)       46%,
        var(--gold-flare) 50%,
        var(--gold)       54%,
        var(--gold-deep)  58% 100%
      );
      background-size: 460px 100%;
      background-repeat: repeat;
      -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
      -webkit-mask-composite: xor;
              mask-composite: exclude;
      animation: plateSheen 5s linear infinite;
    }
    .btn--ante:hover::before,
    .btn--ante:focus-visible::before { opacity: 0; }
  }

  /* ---- form out, credential in -----------------------------------------
     The console does not cut between states. The form's boxes lift away in
     sequence, then the credential prints itself in from the top edge and the
     ante rises under it. */
  @keyframes paneOut { to { opacity: 0; transform: translateY(-12px); } }
  .console__pane--form.is-leaving .console__box {
    animation: paneOut 320ms var(--ease) both;
    animation-delay: calc(var(--i, 0) * 55ms);
  }
  .console__pane--form.is-leaving { pointer-events: none; }

  @keyframes credPrint {
    from { clip-path: inset(0 0 100% 0); opacity: 0; }
    to   { clip-path: inset(0 0 0 0);    opacity: 1; }
  }
  @keyframes anteRise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
  }
  .console__pane--issued.is-entering .cred { animation: credPrint 680ms var(--ease) 90ms both; }
  .console__pane--issued.is-entering .ante { animation: anteRise  520ms var(--ease) 430ms both; }
}

/* ==========================================================================
   MOBILE - one screen, no scrolling.

   Everything is sized against dvh so the page fits the window rather than the
   window fitting the page. Two rows of the deck bracket the console; the two
   text cells and the seventh card are the editorial cut that buys the room,
   since the console's own headline already carries their message.
   ========================================================================== */
@media (max-width: 899px) {
  :root {
    --pad: clamp(10px, 1.7vh, 17px);
    --cross-arm: 11px;
    --cross-clear: 23px;
  }

  .shell {
    height: 100dvh;
    min-height: 0;
    padding: 0 clamp(9px, 1.4vh, 15px) clamp(9px, 1.4vh, 15px);
    gap: clamp(8px, 1.3vh, 15px);
  }

  .cell--bar { padding: clamp(6px, 0.9vh, 11px) var(--pad); }
  .clock { gap: 3px; }
  .clock__pip { width: 6px; height: 6px; }
  .clock__city { font-size: clamp(10.5px, 1.6vh, 13px); font-stretch: 66%; }
  .clock__time { font-size: clamp(9px, 1.3vh, 11px); letter-spacing: 0.01em; }
  /* Clocks, deck row, console, deck row. The seal used to take a band of its
     own above the clocks; with it gone the clocks are the top row and the
     console keeps what is left. */
  .grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows:
      auto
      clamp(62px, 11.2vh, 92px) minmax(0, 1fr) clamp(62px, 11.2vh, 92px);
  }

  .bar > .cell:nth-child(1) { grid-column: 1; grid-row: 1; }
  .bar > .cell:nth-child(2) { grid-column: 2; grid-row: 1; }
  .bar > .cell:nth-child(3) { grid-column: 3; grid-row: 1; }
  .cell--bar-void { display: none; }

  .cell--card[data-idx="01"] { grid-column: 1; grid-row: 2; }
  .cell--card[data-idx="02"] { grid-column: 2; grid-row: 2; }
  .cell--card[data-idx="03"] { grid-column: 3; grid-row: 2; }

  .console                   { grid-column: 1 / span 3; grid-row: 3; min-height: 0; }

  .cell--card[data-idx="04"] { grid-column: 1; grid-row: 4; }
  .cell--card[data-idx="05"] { grid-column: 2; grid-row: 4; }
  .cell--card[data-idx="06"] { grid-column: 3; grid-row: 4; }

  /* The editorial cut that buys the room to fit one screen. */
  .cell--card[data-idx="07"],
  .cell--say { display: none; }

  /* Cells are short and narrow here, so the fragment drops to the bottom and
     leaves the index label a clear band along the top. Centred, it sat
     directly on top of the "(01)". */
  /* Card cells run their own tighter padding here: the shared --pad would eat
     most of a 74px row and leave the fragment barely visible. */
  .cell--card { align-items: end; padding: 7px 8px; }
  .cell__index { font-size: 9.5px; top: 7px; left: 8px; }

  /* No hover on touch, so the windows sit open from the start and the plate
     codes have nothing to reveal them. They stay desaturated - that part is
     the theme, not the interaction. */
  .crop {
    --shut: 0%;
    width: min(78%, calc(var(--rowmin) - 2 * var(--pad) - 12px));
  }
  @supports (width: 1cqmin) {
    .crop { width: min(86cqw, 74cqh); }
  }
  .plate { display: none; }

  /* The console stacks: every box full width, in reading order, each sized so
     the column fits the row it was given. */
  /* The headline absorbs the slack, so the field stays close to the button it
     belongs to instead of floating in the middle of an over-tall row. */
  .console__pane {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto auto auto auto;
    align-content: stretch;
  }
  .console__box { border-right-color: transparent; }
  .console__box--head,
  .console__box--note,
  .console__box--field,
  .console__box--action,
  .console__box--status { grid-column: 1; }
  /* Column flow now, so the vertical centring the row layout got from
     align-items has to come from justify-content instead. */
  .console__box--head { justify-content: center; }
  .console__box--field { display: flex; align-items: center; }
  .console__box--action { background-image: none; padding-bottom: 0; }
  .console__box--status { padding-top: clamp(7px, 1vh, 11px); }

  /* Height is the scarce axis on a phone, not width, so both of these are
     sized against vh. The console had no vertical slack before the mark
     existed - a 375x667 window fit to the pixel - so the mark is paid for
     partly by its own size and partly by a couple of points off the headline,
     rather than by either one alone. */
  .console__mark {
    height: clamp(15px, 2.3vh, 26px);
    margin-bottom: clamp(4px, 0.7vh, 9px);
  }
  .console__head { font-size: clamp(22px, 4.35vh, 36px); }
  .note { font-size: clamp(9.5px, 1.35vh, 11px); letter-spacing: 0.07em; }
  .form { gap: clamp(5px, 0.8vh, 8px); }
  .form__label { font-size: 9.5px; }
  .form__input { padding: clamp(11px, 1.7vh, 15px) 14px; font-size: clamp(14px, 2vh, 16px); }
  .btn { min-height: clamp(42px, 6vh, 52px); font-size: 11.5px; letter-spacing: 0.14em; }
  .helper { font-size: clamp(9px, 1.25vh, 10.5px); }

  /* Once the credential is issued the deck steps back and the console takes
     the whole grid. It is the only way the label and the ante fit a phone
     without scrolling, and the vault taking over the screen is the right read
     at that moment anyway. */
  [data-state="issued"] .cell--card { display: none; }
  [data-state="issued"] .console { grid-row: 3 / -1; grid-column: 1 / -1; }

  .console__pane--issued { grid-template-rows: auto auto; align-content: center; }

  .console__box--cred,
  .console__box--ante { padding: clamp(8px, 1.2vh, 14px) var(--pad); }
  .cred__matrix { width: clamp(40px, 6.4vh, 58px); margin-top: 8px; }
  .mark { width: 21px; height: 21px; }
  .mark svg { width: 13px; height: 13px; }
  .mark--type { font-size: 10.5px; }
  .cred__tape { height: clamp(9px, 1.3vh, 13px); }
  .cred__body { padding: clamp(10px, 1.5vh, 16px) var(--pad); gap: 12px; }
  .cred__mark { width: 14px; margin-bottom: 7px; }
  .cred__brand { font-size: clamp(17px, 2.9vh, 26px); }
  .cred__kind { font-size: 9px; margin-top: 4px; letter-spacing: 0.16em; }
  .cred__body .cred__field-key { margin-top: clamp(8px, 1.3vh, 15px); }
  .cred__holder { font-size: clamp(11px, 1.6vh, 13px); }
  .cred__seq { font-size: clamp(26px, 4.4vh, 40px); }
  /* Three across, not stacked - stacking the meta costs about 100px of height
     the phone does not have. */
  .cred__meta-item { padding: clamp(7px, 1.1vh, 11px) clamp(8px, 1.4vw, 12px); }
  .cred__meta dt { font-size: 8px; letter-spacing: 0.1em; }
  .cred__meta dd { font-size: clamp(9px, 1.3vh, 11px); letter-spacing: 0; margin-top: 3px; }
  .cred__foot { padding: clamp(7px, 1vh, 11px) var(--pad); }
  .cred__barcode { height: clamp(18px, 2.6vh, 28px); }
  .cred__terms { font-size: 8.5px; letter-spacing: 0.12em; }

  .ante__head { font-size: clamp(15px, 2.4vh, 21px); margin-top: 6px; }
  .ante__refund { margin-top: clamp(8px, 1.2vh, 13px); font-size: clamp(10.5px, 1.5vh, 12px); }
  .ante__act { margin-top: clamp(10px, 1.5vh, 16px); gap: 9px 14px; }
  .ante__fine { font-size: clamp(9px, 1.25vh, 10.5px); flex-basis: 100%; }
  .btn--ante { width: 100%; min-height: clamp(46px, 6.4vh, 56px); font-size: 13px; padding: 14px 20px; }

  .foot { grid-template-columns: minmax(0, 1fr); gap: clamp(6px, 1vh, 11px); align-items: start; }
  .foot__wordmark { height: clamp(22px, 3.4vh, 34px); }
  .foot__legal {
    font-size: clamp(7.5px, 1.05vh, 9px);
    line-height: 1.5;
    letter-spacing: 0.04em;
  }
}

/* Very narrow phones. The plate head stays in two columns at every width:
   stacking it reads better in isolation but costs about 125px of height, and
   on a 320px screen the issued state has none to give. Everything here shrinks
   instead. */
@media (max-width: 360px) {
  .cred__seq { font-size: clamp(22px, 3.6vh, 30px); }
  .cred__matrix { width: clamp(30px, 4.6vh, 44px); margin-top: 6px; }
  .cred__body { gap: 9px; }
  .cred__marks { gap: 7px; }
  .cred__marks img { height: 16px; }
  .cred__barcode { height: 15px; }

  .ante__head { font-size: clamp(14px, 2.2vh, 18px); }
  .ante__refund { font-size: 10.5px; }
  .ante__fine { font-size: 8.5px; line-height: 1.42; }
  .btn--ante { min-height: 42px; font-size: 11.5px; letter-spacing: 0.08em; }
}

/* ==========================================================================
   SHORT PHONES - the issued state carries the most content of any view, so on
   a short window it gives up padding rather than the screen's one-page rule.
   ========================================================================== */
@media (max-width: 899px) and (max-height: 720px) {
  .console__box--cred,
  .console__box--ante { padding: 8px var(--pad); }
  .cred__body { padding: 9px var(--pad); gap: 10px; }
  .cred__mark { margin-bottom: 5px; }
  .cred__body .cred__field-key { margin-top: 8px; }
  .cred__meta-item { padding: 6px 9px; }
  .cred__foot { padding: 6px var(--pad) 8px; }
  .cred__barcode { height: 17px; }

  .ante__head { margin-top: 4px; }
  .ante__refund { margin-top: 8px; }
  .ante__act { margin-top: 9px; gap: 7px 12px; }

  .foot__legal { line-height: 1.42; }
}

/* ==========================================================================
   VERY SHORT WINDOWS - below about 660px tall the deck rows are what give,
   because the console and the control notice cannot compress any further and
   the one-screen rule outranks the size of the fragments.
   ========================================================================== */
@media (max-width: 899px) and (max-height: 660px) {
  .shell { gap: 6px; }

  /* The maker's mark is the newest thing in the console and the only purely
     decorative one, so it gives first: smaller here, gone below 600 where the
     form genuinely cannot hold it. Same editorial logic as the seventh card
     and the two text cells, which mobile drops for the same reason. */
  .console__mark { height: 14px; margin-bottom: 4px; }
  .grid {
    grid-template-rows:
      auto
      clamp(50px, 9.4vh, 70px) minmax(0, 1fr) clamp(50px, 9.4vh, 70px);
  }
  .foot__legal { line-height: 1.4; }
}

/* ==========================================================================
   THE SHORTEST WINDOWS - under about 600px tall the console needs every pixel
   the headline, the field, the button and the helper take, and there is no
   padding left to give. The mark comes out rather than the page scrolling.
   ========================================================================== */
@media (max-width: 899px) and (max-height: 600px) {
  .console__mark { display: none; }
}

/* ==========================================================================
   NARROW AND SHORT TOGETHER (about 320x640, the smallest window still worth
   supporting). The issued state is the tallest thing this page renders, so at
   this size everything that is padding gives up a few pixels rather than the
   one-screen rule giving up.
   ========================================================================== */
@media (max-width: 360px) and (max-height: 680px) {
  .shell { gap: 5px; }

  /* The form state overruns too, by about 15px: at this width the headline and
     the helper each take one more line than they do at 375, and the console's
     1fr row has no slack left to absorb them, so the page picks up a short
     scroll. The boxes give the padding back. The action box keeps its zero
     bottom - the button already carries that gap. */
  .console__pane--form .console__box { padding-top: 7px; padding-bottom: 7px; }
  .console__pane--form .console__box--action { padding-bottom: 0; }
  .console__pane--form .console__box--status { padding-top: 5px; }
  .console__box--cred,
  .console__box--ante { padding: 6px var(--pad); }
  .cred__body { padding: 7px var(--pad); }
  .cred__meta-item { padding: 5px 8px; }
  .cred__foot { padding: 5px var(--pad) 6px; }
  .ante__refund { margin-top: 6px; }
  .ante__act { margin-top: 7px; }
  .cred__tape { height: 8px; }
  .foot__legal { line-height: 1.36; }
}

/* ==========================================================================
   SHORT DESKTOP WINDOWS - a laptop at 1024x768, or any window under about
   860px tall. The issued state is the tallest thing the console renders, and
   on the desktop grid it only gets half the page's width, so it compresses
   here rather than pushing the page into a scroll.

   The form state needs the same treatment now. Its headline is sized off vw
   while the room for it comes from vh, so a wide short window (1280x800 is the
   worst of them) gets the largest type in the least space: five lines of it,
   plus the mark above. Both give here.
   ========================================================================== */
@media (min-width: 900px) and (max-height: 860px) {
  .console__mark {
    height: clamp(20px, 2.6vh, 28px);
    margin-bottom: clamp(7px, 1vh, 12px);
  }
  .console__head { font-size: clamp(28px, min(4.7vw, 6.4vh), 54px); }

  .console__box--cred,
  .console__box--ante { padding: clamp(12px, 1.7vh, 18px) var(--pad); }

  .cred__tape { height: 11px; }
  .cred__body { padding: 11px var(--pad); }
  .cred__mark { margin-bottom: 6px; }
  .cred__body .cred__field-key { margin-top: 10px; }
  .cred__seq { font-size: clamp(28px, 3.4vw, 42px); }
  .cred__matrix { width: clamp(44px, 4.4vw, 58px); margin-top: 7px; }
  .cred__meta-item { padding: 7px var(--pad); }
  .cred__foot { padding: 7px var(--pad) 9px; }
  .cred__barcode { height: 24px; }
  .cred__marks img { height: 20px; }

  .ante__head { margin-top: 6px; }
  .ante__refund { margin-top: 9px; }
  .ante__act { margin-top: 11px; }
  .btn--ante { min-height: 50px; padding: 13px 26px; }
}

/* ==========================================================================
   SHORT DESKTOP WINDOWS, SHORTER STILL - under about 740px tall on the desktop
   grid the console is being asked to fit its five lines of headline into three
   rows that no longer have the height for them.

   NOTE: this band does not fully fit and did not before the headline changed
   either - 1024x640 and 1280x700 both scrolled on the previous build. The real
   fix is to let the stacked layout take over on short windows as well as
   narrow ones, which is a layout decision rather than a tuning one. See
   README. What this tier does is keep the compression honest so the overflow
   is no worse than it was.
   ========================================================================== */
@media (min-width: 900px) and (max-height: 740px) {
  .console__mark {
    height: clamp(14px, 2.1vh, 22px);
    margin-bottom: clamp(5px, 0.8vh, 9px);
  }
  .console__head { font-size: clamp(24px, min(4.7vw, 5.3vh), 44px); }
}
