/* ==========================================================================
   $VEND — design tokens
   --------------------------------------------------------------------------
   Two languages are fused here and it is worth naming both, because the whole
   look is the seam between them.

   FROM THE BRIEF — the machine is a physical object in a dark studio.
     Near-black page, a single lit object, electric blue as the only glowing
     colour, bone for the shell, deep blue for the panels, gold strictly as an
     accent that never becomes a surface.

   FROM THE HOUSE STYLE — chunky borders, hard offset shadows, a pixel display
     face, generous radii on flat panels, and buttons that physically depress.

   WHAT THAT MEANS CONCRETELY
     · Every edge on the machine is a drawn line (2px ink), not a gradient.
     · Depth is a hard offset shadow — `0 5px 0 var(--ink)` — the sticker
       language, not a blur. Blur is reserved for GLOW, which is a light source
       and therefore belongs only to the neon.
     · The pixel face (Pixelify Sans) carries the marquee, the screen, the price
       pills and the big numbers. Body copy stays a system sans, because pixel
       type at paragraph length is a legibility cost with no payoff.
     · Radii are deliberately SMALLER on the machine (4–14px) than on flat UI
       panels (16–20px). A vending machine is a moulded box; a card is not.

   THE ONE RULE THAT MATTERS MOST
     Glow stays on the machine. The page around it is black and quiet. A neon
     strip is not text and needs no contrast check; glowing TEXT does, so every
     glowing string on this site is paired with a solid fallback colour and is
     checked in tests/contrast.test.js.
   ========================================================================== */

:root {
  /* ---- the studio ------------------------------------------------------ */
  --ink:        #0B0B0C;   /* linework, panel interior, the page itself */
  --ink-2:      #131316;   /* one step up, for recesses */
  --ink-3:      #1C1C21;   /* two steps up, for flat UI on black */

  /* ---- the machine ----------------------------------------------------- */
  --bone:       #F2EFE6;   /* the shell */
  --bone-2:     #E4DFD1;   /* shell shading */
  --bone-3:     #D3CCBA;   /* shell shadow line */
  --deep:       #0A1F52;   /* the body panels */
  --deep-2:     #081640;   /* panel interior */
  --deep-3:     #06102E;   /* the recess behind the glass */

  /* ---- the neon -------------------------------------------------------- */
  --blue:       #1B5CFF;
  --blue-hi:    #3D8BFF;
  --blue-lo:    #0A2E8F;
  --blue-ink:   #6FA8FF;   /* readable blue TEXT on black */
  --blue-wash:  rgba(27, 92, 255, .14);

  /* ---- the accent ------------------------------------------------------ */
  --gold:       #F5B524;
  --gold-hi:    #FFD264;
  --gold-ink:   #F5B524;   /* gold as text on black — passes at large sizes only */
  --gold-deep:  #A8760A;

  /* ---- the flat UI ----------------------------------------------------- */
  /*
   * THE BRAND IS BLACK AND WHITE, and these are what the UI chrome uses.
   *
   * They are deliberately SEPARATE names from the machine's `--blue*` and
   * `--gold*` above. The machine is the one object on the page that keeps its
   * colour — its neon, its screen, its panels, the pack art — so redefining
   * those tokens would drain the colour out of the vending machine itself,
   * which is the one thing that must not change.
   *
   * Everything outside the machine — the shell, the buttons, the plates, the
   * chips, the boards, the toasts — is drawn from here.
   *
   * State is carried by BRIGHTNESS AND WEIGHT rather than hue. That is not a
   * compromise: the toasts already lead with a glyph (! ✓ •) and the chips
   * already say what they mean in words, so colour was only ever repeating
   * information the reader already had.
   */
  --ui-fg:     #F2EFE6;                    /* UI text at its brightest */
  --ui-fg-2:   #B9B6AD;                    /* secondary text */
  --ui-fg-3:   #86847D;                    /* muted text */
  --ui-line:   #2C2C34;                    /* hairlines */
  --ui-line-2: #3B3B45;                    /* raised edges */
  --ui-fill:   #191920;                    /* a raised surface */
  --ui-fill-2: #22222A;                    /* one step up */
  --ui-wash:   rgba(242, 239, 230, .085);  /* the active / hover tint */
  --ui-edge:   rgba(242, 239, 230, .34);   /* the active border */
  --ui-lo:     #6E6C66;                    /* the hard shadow under a light button */
  --ui-ink:    #0B0B0C;                    /* text on a light UI surface */

  /* ---- states ---------------------------------------------------------- */
  --ok:         #2FD37A;
  --warn:       #FFB020;
  --bad:        #FF4D3D;
  /* Lifted from #6B7280, which measured 4.07:1 on the page and 4.21:1 on the
     screen — under AA for the small type it is used on. See
     tests/contrast.test.js, which is what caught it. */
  --sold:       #767D8C;

  /* ---- text on the machine -------------------------------------------- */
  --on-bone:    #0B0B0C;
  --on-bone-2:  #4A463B;
  --on-deep:    #E8EEFF;
  --on-deep-2:  #9FB2D8;
  --on-ink:     #F2EFE6;
  --on-ink-2:   #9A9689;

  /* ---- type ------------------------------------------------------------ */
  --px:   "Pixelify Sans", ui-monospace, "Cascadia Mono", Consolas, monospace;
  --font: ui-sans-serif, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  /* ---- geometry -------------------------------------------------------- */
  --line:      2px;        /* the machine's linework */
  --line-thin: 1.6px;      /* the flat UI's linework, from the house style */
  --r-xs:      4px;
  --r-sm:      7px;
  --r-md:      11px;
  --r-lg:      16px;
  --r-xl:      20px;

  /* The shell's sidebar. `--side-w` is read by the grid track AND by the
     sidebar itself, so the collapsed rail only has to change one number. */
  --side-w:    246px;
  --side-w-rail: 78px;
  --hairline:  #1E1E24;    /* the flat UI's separator, same as the topbar's */

  /* ---- depth ----------------------------------------------------------- */
  /* Hard offsets, no blur. `--sh-*` are the machine's; the UI ones are shorter. */
  --sh-1: 0 2px 0 var(--ink);
  --sh-2: 0 4px 0 var(--ink);
  --sh-3: 0 6px 0 var(--ink);
  --sh-4: 0 9px 0 var(--ink);
  --sh-ui-1: 0 2px 0 rgba(0, 0, 0, .5);
  --sh-ui-2: 0 4px 0 rgba(0, 0, 0, .55);
  --sh-ui-3: 0 6px 0 rgba(0, 0, 0, .6);

  /* ---- glow (light, not decoration) ----------------------------------- */
  --glow-sm:  0 0 6px rgba(61, 139, 255, .55);
  --glow-md:  0 0 14px rgba(61, 139, 255, .45), 0 0 34px rgba(27, 92, 255, .28);
  --glow-lg:  0 0 22px rgba(61, 139, 255, .55), 0 0 60px rgba(27, 92, 255, .34);
  --glow-text: 0 0 8px rgba(61, 139, 255, .6);

  /* ---- motion ---------------------------------------------------------- */
  --fast: 90ms;
  --mid:  180ms;
  --slow: 340ms;
  --ease: cubic-bezier(.2, .7, .3, 1);
  --ease-drop: cubic-bezier(.34, 1.5, .5, 1);   /* the dispense bounce */

  color-scheme: dark;
}

/* ==========================================================================
   Reset
   ========================================================================== */

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

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--on-ink);
  font-family: var(--font);
  font-size: 14.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; padding: 0; }

:focus-visible {
  outline: 2.5px solid var(--ui-fg);
  outline-offset: 2px;
  border-radius: 6px;
}

* { scrollbar-width: thin; scrollbar-color: #2A2A33 transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: #2A2A33;
  border-radius: 99px;
  border: 3px solid transparent;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: #3D3D4A; background-clip: content-box; }

/* ==========================================================================
   Type primitives
   ========================================================================== */

.px { font-family: var(--px); letter-spacing: .01em; }
.mono { font-family: var(--mono); font-variant-ligatures: none; }

.t-xs  { font-size: 10.5px; }
.t-sm  { font-size: 12px; }
.t-md  { font-size: 13.5px; }
.t-lg  { font-size: 15.5px; }

.eyebrow {
  font-family: var(--px);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--on-ink-2);
}

.dim  { color: var(--on-ink-2); }
.dim2 { color: #6E6A5E; }
.nowrap { white-space: nowrap; }
.trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.num { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   The studio backdrop
   --------------------------------------------------------------------------
   A single soft spotlight from above, a faint floor, everything else black. The
   machine must be the only lit object on the page, so this is deliberately
   almost nothing — if the backdrop reads as a gradient, it is too strong.
   ========================================================================== */

.studio {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(60% 42% at 50% -6%, rgba(61, 139, 255, .13) 0%, transparent 70%),
    radial-gradient(120% 60% at 50% 108%, rgba(27, 92, 255, .07) 0%, transparent 72%),
    var(--ink);
}

/* A barely-there grain, so the black is a surface rather than a void. */
.studio::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: .035;
  background-image:
    repeating-conic-gradient(from 0deg at 50% 50%, #fff 0deg 0.6deg, transparent 0.6deg 1.2deg);
  background-size: 3px 3px;
}

/* ==========================================================================
   Surfaces
   --------------------------------------------------------------------------
   `.plate` is the flat UI's container — the registry, the leaderboard, the docs.
   It is NOT used on the machine, which has its own moulded panels.
   ========================================================================== */

.plate {
  background: var(--ink-2);
  border: var(--line-thin) solid #26262E;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-ui-2);
  min-width: 0;
}

.plate-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  row-gap: 8px;
  padding: 12px 15px;
  border-bottom: var(--line-thin) solid #26262E;
  min-height: 50px;
}

.plate-title {
  font-family: var(--px);
  font-size: 17px;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

.plate-body { padding: 15px; }

.dash { border-top: var(--line-thin) dashed #2C2C36; }

/* ==========================================================================
   Buttons
   --------------------------------------------------------------------------
   Every button depresses by its own shadow height, so the click reads as a
   physical press rather than a colour change.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  white-space: nowrap;
  user-select: none;
  font-weight: 700;
  border: var(--line-thin) solid transparent;
  border-radius: var(--r-md);
  transition: transform var(--fast) ease, box-shadow var(--fast) ease,
              filter var(--fast), background var(--fast), color var(--fast);
}
.btn:active { transform: translateY(3px); }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }

/* The primary action: a light button with dark text. On a black page that is the
   loudest thing available without introducing a hue. */
.btn-blue {
  background: var(--ui-fg);
  color: var(--ui-ink);
  border-color: var(--ink);
  box-shadow: 0 5px 0 0 var(--ui-lo), 0 5px 0 2px var(--ink);
}
.btn-blue:hover:not(:disabled) { filter: brightness(1.06); }
.btn-blue:active:not(:disabled) { box-shadow: 0 2px 0 0 var(--ui-lo), 0 2px 0 2px var(--ink); }

.btn-ghost {
  background: var(--ink-3);
  color: var(--on-ink);
  border-color: #30303A;
  box-shadow: var(--sh-ui-2);
}
.btn-ghost:hover:not(:disabled) { background: #23232B; }
.btn-ghost:active:not(:disabled) { box-shadow: var(--sh-ui-1); }

/* The secondary accent: outlined rather than filled, so it reads as the quieter
   of the two without needing a different colour to say so. */
.btn-gold {
  background: var(--ink-3);
  color: var(--ui-fg);
  border-color: var(--ui-line-2);
  box-shadow: var(--sh-ui-2);
}
.btn-gold:hover:not(:disabled) { background: var(--ui-fill-2); border-color: var(--ui-edge); }
.btn-gold:active:not(:disabled) { box-shadow: var(--sh-ui-1); }

.btn-sm { height: 32px; padding: 0 12px; font-size: 12.5px; border-radius: 9px; }
.btn-md { height: 38px; padding: 0 15px; font-size: 13.5px; }
.btn-lg { height: 46px; padding: 0 20px; font-size: 15px; border-radius: 13px; }
.btn-block { width: 100%; }

/* ==========================================================================
   Chips
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 9px;
  background: var(--ink-3);
  border: var(--line-thin) solid #30303A;
  border-radius: 8px;
  color: var(--on-ink);
  font-size: 12px;
  font-weight: 650;
  white-space: nowrap;
}
.chip-xs { height: 21px; padding: 0 7px; font-size: 10.5px; border-radius: 6px; }
/*
 * Five chips, five treatments, no hue — and they stay distinguishable because
 * each one is a different WEIGHT rather than a different colour:
 *
 *   ok    solid light, dark text — the strongest, for "this was verified"
 *   gold  a tinted fill — a status worth noticing
 *   blue  an outline — a status that is only claimed, not confirmed
 *   bad   a brighter outline — a problem
 *   sold  the faintest — absence
 */
.chip-blue { background: transparent; border-color: var(--ui-line-2); color: var(--ui-fg-2); }
.chip-gold { background: var(--ui-wash); border-color: var(--ui-edge); color: var(--ui-fg); }
.chip-ok   { background: var(--ui-fg); border-color: var(--ui-fg); color: var(--ui-ink); }
.chip-bad  { background: transparent; border-color: rgba(242, 239, 230, .55); color: var(--ui-fg); }
.chip-sold { background: transparent; border-color: var(--ui-line); color: var(--ui-fg-3); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.dot { width: 7px; height: 7px; border-radius: 99px; background: var(--on-ink-2); flex: none; }
.dot-ok   { background: var(--ok); }
.dot-bad  { background: var(--bad); }
.dot-live { background: var(--ok); animation: pulse 1.8s infinite; }

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .4; transform: scale(.8); }
}

/* ==========================================================================
   Motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}
