/* ============================================================
   /hqz/ — spec sheet

   The page is laid out like the technical documentation its subject actually
   writes: a parameter sheet, not a portfolio. Dark because the work happens in
   terminals and on bench equipment — and the palette comes from a PCB
   (substrate, copper, silkscreen), not from "dark mode".

   Disciplines this file holds to:
     · zero border-radius, zero shadows — nothing here is a card
     · copper is the only chroma, and only on things that are live connections
     · mono appears only where the content is machine-readable
     · structure encodes data (project weight = note size, bar width = years)

   Sections: tokens · base · bar · hero · axis · band · timeline
             projects · repos · skills · contact · foot · motion · responsive
   ============================================================ */

/* ---------- tokens ---------- */
/* Contrast notes, measured against --board (#0B0F13):
     --silk   15.9:1   body text
     --silk-2 11.1:1   secondary
     --silk-3  6.8:1   tertiary — the previous value was .36 alpha, i.e. 2.99:1,
                       which failed WCAG AA outright. It carried the dates, the
                       note sizes, the repo URLs and the whole footer.
     --signal  9.5:1   accent
   Everything above 14px, so AA needs 4.5:1. All pass. */
:root {
  /* board */
  --board:    #0B0F13;
  --board-2:  #070A0D;
  --panel:    #141A20;
  --panel-2:  #1C242C;

  /* silkscreen */
  --silk:     #F2F6F9;
  --silk-2:   rgba(242, 246, 249, .86);
  --silk-3:   rgba(242, 246, 249, .68);

  /* signal — lit copper rather than raw copper: same family, pushed bright
     enough to carry as an accent on a dark board. */
  --signal:   #FFA234;
  --signal-2: rgba(255, 162, 52, .18);
  --signal-3: rgba(255, 162, 52, .36);

  --etch:     rgba(242, 246, 249, .15);
  --etch-2:   rgba(242, 246, 249, .08);

  /* Background glows. A glow this strong is paid for out of the text budget:
     the silk tokens are semi-transparent, so wherever the wash lands they get
     lighter and their contrast falls. The silk alphas below are set so that the
     worst case — silkscreen-3 sitting on the very peak of the warm glow —
     still clears 4.5:1. It measures 4.96:1.

     Raising --glow-warm further means raising --silk-3 with it. The pairs that
     hold: .34 needs .68, .38 needs .73, .42 needs .82. Past about .42 the
     tertiary tier stops being distinguishable from the secondary one and the
     hierarchy is gone, so that is the practical ceiling. */
  --glow-warm: rgba(255, 162, 52, .34);
  --glow-cool: rgba(52, 128, 176, .24);

  --measure: 66ch;                       /* prose line length */
  --page: 1120px;                        /* the document's page width */
  /* Left rail for the robot. Zero by default, so every rule below is a no-op
     until the viewport is wide enough to spare the room. */
  --rail: 0px;
  --pad: clamp(20px, 5vw, 56px);
  --band: clamp(64px, 8vw, 108px);

  --ease: cubic-bezier(.22, 1, .36, 1);

  --sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI',
          'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

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

/* A document has a page width.
 *
 * Without this every block was full-bleed with only inner padding, so on a wide
 * display the two ends of a table row — or the name and the nav in the bar —
 * sat a screen apart and the eye had to travel the whole way across to pair
 * them up. Constraining the blocks and centring them keeps the rules, the
 * table and the axis at a readable span, and lets the margins do the work the
 * padding alone could not. */
/* The rules run edge to edge; the text does not.
 *
 * Each block is full width so its border can span the viewport, and an inner
 * wrapper carries the page width. The copy stays on exactly the column it was
 * on — only the lines reach the edges. Without the wrapper the border stops
 * where the text stops, which is what made the rules look inset. */
.hero { max-width: var(--page); margin-inline: auto; }

.bar__in, .band__in, .foot__in {
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--pad);
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 72px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  /* The whole document starts after the rail. Everything is centred within the
     remaining width, so the copy stays on the same column it always was — it
     has simply moved right to make room. */
  padding-left: var(--rail);
  background: var(--board);
  color: var(--silk);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- background ----------
   A fixed layer behind everything: two soft glows over a vertical wash, with a
   canvas of slow-drifting points above that. It carries no information, so it
   is aria-hidden and pointer-events:none, and it never sits over the text —
   the content blocks are lifted onto z-index 1 below. */
.bg {
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
}
.bg::before {
  content: ''; position: absolute; inset: 0;
  /* Two washes that meet across the middle of the page. The centres sit off
     canvas so the visible area catches the shoulder of each glow rather than
     its hot spot — that is what lets the radius grow to well over the viewport
     without a bright blob appearing behind the text. */
  background:
    radial-gradient(135% 105% at 88% -12%, var(--glow-warm) 0%, transparent 78%),
    radial-gradient(115% 90%  at -12% 102%, var(--glow-cool) 0%, transparent 80%),
    linear-gradient(180deg, var(--board) 0%, var(--board-2) 100%);
}
.bg canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

main, .foot { position: relative; z-index: 1; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.02em; line-height: 1.2; }
p { margin: 0; }
ul, ol, dl, dd { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }

::selection { background: var(--signal); color: var(--board); }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 99;
  background: var(--signal); color: var(--board);
  padding: 11px 18px; font-weight: 500;
}
.skip:focus { left: 0; }

/* ---------- bar ----------
   The plate and its rule reach the left edge even though the bar's content
   starts after the rail. Without that, `body { padding-left }` clips them at
   the rail and the bar reads as though its left end has been cut off — which is
   exactly how it was reported. The same "lines run edge to edge" rule the rest
   of the page follows.

   Drawn as a pseudo-element rather than by widening the bar itself, so
   .bar__in keeps centring against the same column as every other block and no
   layout arithmetic has to be unpicked. */
.bar {
  position: sticky; top: 0; z-index: 20;
}
.bar::before {
  content: '';
  position: absolute;
  inset: 0 0 -1px calc(-1 * var(--rail));
  background: var(--board);
  border-bottom: 1px solid var(--etch);
  pointer-events: none;
  /* z-index: -1 is load-bearing, not tidiness.
     A positioned pseudo-element paints in layer 6; the bar's copy is in-flow
     content, which paints in layer 3 — so without this the opaque plate lands
     on top of the text and covers it. The links stay clickable (the plate is
     pointer-events:none) which is what makes it look like the words vanished
     rather than the bar broke.
     -1 puts it in layer 2, still inside the bar's own stacking context (the bar
     is sticky with z-index 20), so it can never slip behind anything else. */
  z-index: -1;
}
.bar__in {
  display: flex; align-items: baseline; gap: 20px 28px; flex-wrap: wrap;
  padding-block: 15px;
}
.bar__id { display: flex; align-items: baseline; gap: 12px; }
.bar__name { font-weight: 600; font-size: 1rem; letter-spacing: -.02em; }
.bar__handle { font-family: var(--mono); font-size: .875rem; color: var(--silk-3); }

.bar__nav { display: flex; gap: 22px; margin-left: auto; }
.bar__nav a {
  font-size: .875rem; color: var(--silk-2);
  border-bottom: 1px solid transparent; padding-bottom: 1px;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.bar__nav a:hover { color: var(--silk); border-bottom-color: var(--signal); }

.bar__lang {
  font-family: var(--mono); font-size: .875rem; color: var(--silk-2);
  border: 1px solid var(--etch); padding: 4px 11px;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.bar__lang:hover { color: var(--signal); border-color: var(--signal); }

/* ---------- hero ---------- */
.hero {
  padding: clamp(56px, 9vw, 128px) var(--pad) clamp(48px, 6vw, 84px);
}
/* role and location are separated by space, not by a glyph — a middle dot
   between two facts is a stock meta-string and says nothing */
.hero__role {
  display: flex; flex-wrap: wrap; gap: 2px 24px;
  font-family: var(--mono); font-size: .875rem; color: var(--silk-3);
  margin-bottom: clamp(22px, 3vw, 36px);
}
.hero__place { color: var(--silk-3); }
.hero__place::before {
  content: ''; display: inline-block;
  width: 5px; height: 5px; margin-right: 9px;
  transform: translateY(-2px);
  background: var(--signal);
}
.hero__claim {
  max-width: 22ch;                      /* short lines, set large */
  font-size: clamp(1.85rem, 4.6vw, 3.4rem);
  font-weight: 600;
  letter-spacing: -.035em;
  line-height: 1.16;
}

/* ---------- axis ----------
   The one bold element. Two bands on a shared 2014–2026 scale, so the ratio
   between nine years of hardware and three of agents is read before the copy
   is. Widths come from content.mjs via --l/--w; nothing here is hand-tuned. */
.axis { margin: clamp(40px, 6vw, 72px) 0 0; }
.axis__track {
  position: relative;
  height: clamp(30px, 4vw, 46px);
  background: var(--panel);
  border: 1px solid var(--etch);
}
.axis__span {
  position: absolute; top: 0; bottom: 0;
  left: var(--l);
  width: var(--w);
  /* the load moment: each band grows to its real width once, in order, then
     stops. Declarative, so it also runs with JavaScript disabled. */
  animation: axisGrow 1s var(--ease) both;
}
.axis__span--device { background: var(--silk-3); animation-delay: .1s; }
.axis__span--agent  { background: var(--signal); animation-delay: .55s; }

@keyframes axisGrow { from { width: 0; } }

.axis figcaption {
  display: flex; flex-wrap: wrap; gap: 10px 28px;
  justify-content: space-between; align-items: baseline;
  padding-top: 15px;
}
.axis__legend { display: flex; flex-wrap: wrap; gap: 10px 28px; }
.axis__key {
  display: inline-flex; align-items: baseline; gap: 9px;
  font-size: .9rem; color: var(--silk-2);
}
.axis__keytext em {
  font-style: normal; font-size: .875rem; color: var(--silk-3);
  margin-left: 9px;
}
.axis__key b { font-family: var(--mono); font-weight: 400; color: var(--silk-3); font-size: .875rem; }
.axis__swatch { width: 22px; height: 9px; flex: none; transform: translateY(1px); }
.axis__key--device .axis__swatch { background: var(--silk-3); }
.axis__key--agent  .axis__swatch { background: var(--signal); }
.axis__label { font-family: var(--mono); font-size: .875rem; color: var(--silk-3); }

/* ---------- band (section) ---------- */
.band {
  padding-top: var(--band);
  border-top: 1px solid var(--etch-2);
}
.band--last { padding-bottom: var(--band); }
.band__title {
  font-size: 1.05rem; font-weight: 500; color: var(--silk);
  margin-bottom: 10px;
}
.band__note {
  max-width: var(--measure);
  font-size: .9rem; color: var(--silk-3);
  margin-bottom: clamp(30px, 4vw, 50px);
}

/* ---------- timeline ----------
   A real sequence, so it gets a real rail. The node sits on the line; the
   dates are the anchors, in mono because they are data. */
.tl { position: relative; }
.tl__row {
  position: relative;
  display: grid; grid-template-columns: 168px minmax(0, 1fr);
  gap: 28px;
  padding: 26px 0 26px 30px;
  border-top: 1px solid var(--etch-2);
}
.tl__row:first-child { border-top: none; }
.tl__row::after {
  content: ''; position: absolute; left: 4px; top: 0; bottom: 0;
  width: 1px; background: var(--etch);
}
.tl__row:first-child::after { top: 34px; }
.tl__row:last-child::after { bottom: auto; height: 34px; }
.tl__node {
  position: absolute; left: 0; top: 32px;
  width: 9px; height: 9px; background: var(--signal);
}
.tl__when {
  font-family: var(--mono); font-size: .875rem; color: var(--silk-3);
  padding-top: 3px;
}
.tl__what h3 { font-size: 1.05rem; font-weight: 600; margin-bottom: 5px; }
.tl__role { font-size: .9rem; color: var(--signal); margin-bottom: 10px; }
.tl__desc { max-width: var(--measure); font-size: .93rem; color: var(--silk-2); }
.tl__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 13px; }
.tl__tags li {
  font-size: .875rem; color: var(--silk-3);
  border: 1px solid var(--etch); padding: 2px 9px;
}

/* ---------- projects ----------
   Three tiers, assigned by how many bytes of notes exist for the project.
   .pr--lead gets a paragraph and an excerpt, .pr--mini a compact two-column
   line. The tier shows only in the layout — no byte count is printed, because
   how much has been written up is a property of the page, not of the project. */
.cats { display: grid; gap: clamp(38px, 5vw, 62px); }
.cat__name {
  font-size: 1rem; font-weight: 500; color: var(--silk);
  padding-bottom: 12px; margin-bottom: 4px;
  border-bottom: 1px solid var(--etch);
}
.cat__list { display: grid; }

.pr { padding: 20px 0; border-top: 1px solid var(--etch-2); }
.pr:first-child { border-top: none; }

.pr__head { margin-bottom: 8px; }
.pr__name { font-size: 1rem; font-weight: 600; letter-spacing: -.01em; word-break: break-word; }
.pr__name--plain { color: var(--silk); }

/* the name carries the repo link, so no address line is needed below */
a.pr__name {
  display: inline-flex; align-items: baseline; gap: 7px;
  color: var(--silk);
  border-bottom: 1px solid transparent;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
a.pr__name svg {
  width: 12px; height: 12px; flex: none;
  fill: none; stroke: var(--silk-3); stroke-width: 2;
  transition: stroke .18s var(--ease), transform .18s var(--ease);
  transform: translateY(1px);
}
a.pr__name:hover { color: var(--signal); border-bottom-color: var(--signal); }
a.pr__name:hover svg { stroke: var(--signal); transform: translate(2px, -1px); }

.pr__desc { max-width: var(--measure); font-size: .93rem; color: var(--silk-2); }
.pr__excerpt {
  max-width: var(--measure);
  margin-top: 12px; padding-left: 15px;
  border-left: 2px solid var(--etch);
  font-size: .9rem; color: var(--silk-3); line-height: 1.65;
}

/* the lead tier carries the most weight: larger name, more air */
.pr--lead { padding: 28px 0; }
.pr--lead .pr__name { font-size: 1.22rem; }
.pr--lead .pr__desc { font-size: .96rem; color: var(--silk-2); }

/* the mid tier is present but quiet */
.pr--mid .pr__name { font-size: 1rem; }

/* the mini tier reads as a two-column list line: name against description */
.pr--mini {
  display: grid;
  grid-template-columns: minmax(0, 215px) minmax(0, 1fr);
  gap: 4px 22px; align-items: baseline;
  padding: 12px 0;
}
.pr--mini .pr__head { margin-bottom: 0; }
.pr--mini .pr__name { font-size: .93rem; }
.pr--mini .pr__desc { font-size: .875rem; color: var(--silk-3); max-width: none; }

/* ---------- repos ---------- */
.repos__wrap { border: 1px solid var(--etch); }
.repos { font-size: .93rem; }
.repos__group th {
  text-align: left; font-weight: 500; font-size: .875rem; color: var(--silk-3);
  padding: 12px 18px 10px;
  background: var(--panel);
  border-bottom: 1px solid var(--etch);
}
.repos tbody + tbody .repos__group th { border-top: 1px solid var(--etch); }
.repos th[scope="row"] { font-weight: 400; text-align: left; padding: 11px 18px; }
.repos th[scope="row"] a {
  font-family: var(--mono); font-size: .875rem;
  border-bottom: 1px solid transparent;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.repos tr:hover th[scope="row"] a { color: var(--signal); border-bottom-color: var(--signal); }

/* ---------- skills ---------- */
.sgs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 30px clamp(24px, 4vw, 52px);
}
.sg h3 {
  font-size: .875rem; font-weight: 500; color: var(--silk-3);
  padding-bottom: 10px; margin-bottom: 12px;
  border-bottom: 1px solid var(--etch);
}
.sg li { font-size: .9rem; color: var(--silk-2); padding: 5px 0; }

/* ---------- contact ---------- */
.contact { display: grid; gap: 0; max-width: 560px; }
.contact > div {
  display: grid; grid-template-columns: 88px minmax(0, 1fr);
  gap: 18px; align-items: baseline;
  padding: 15px 0; border-top: 1px solid var(--etch-2);
}
.contact > div:first-child { border-top: none; }
.contact dt { font-size: .875rem; color: var(--silk-3); }
.contact dd { font-size: .98rem; }
.contact a { border-bottom: 1px solid var(--etch); transition: color .18s var(--ease), border-color .18s var(--ease); }
.contact a:hover { color: var(--signal); border-bottom-color: var(--signal); }

/* ---------- foot ---------- */
.foot {
  padding-block: 26px 40px;
  border-top: 1px solid var(--etch);
  font-size: .875rem; color: var(--silk-3);
}
.foot__in {
  display: flex; flex-wrap: wrap; gap: 10px 28px;
  justify-content: space-between; align-items: baseline;
}
.foot__mcp code {
  font-family: var(--mono); font-size: .875rem;
  color: var(--signal); border-bottom: 1px solid var(--signal-2);
}
.foot__meta { display: inline-flex; flex-wrap: wrap; gap: 3px 22px; }
.foot__meta a { border-bottom: 1px solid var(--etch); }
.foot__meta a:hover { color: var(--signal); border-bottom-color: var(--signal); }

/* ---------- motion ----------
   Deliberately almost nothing. The axis is the one orchestrated entrance; a
   per-section fade-and-slide would be decoration that draws attention to the
   scaffolding rather than the content, and is the default on generated pages. */

/* ---------- cursor trail ----------
   Seven squares that spawn near the pointer, drift a little and fade out, so
   moving the mouse leaves a short dissipating trail. The squares are created
   and driven by script; this is only the layer they live on.

   Additive, never load-bearing: the native cursor is left alone (no
   `cursor: none`), the layer is pointer-events:none, and it is removed entirely
   for touch, coarse pointers and reduced motion. */
.cursor {
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none; opacity: 0;
  transition: opacity .3s var(--ease);
}
body.has-cursor .cursor { opacity: 1; }
.cursor span {
  position: absolute; top: 0; left: 0;
  display: block; opacity: 0;
  background: var(--signal);
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce), (max-width: 767.98px) {
  html { scroll-behavior: auto; }
  .axis__span { animation: none; }
  .cursor { display: none; }
  /* a continuously repainting canvas is the first thing to cost battery on a
     phone, and the plan requires motion to stand down at this width */
  .bg canvas { display: none; }
  .bg::before {
    background:
      radial-gradient(150% 60% at 80% -8%, var(--glow-warm) 0%, transparent 76%),
      linear-gradient(180deg, var(--board) 0%, var(--board-2) 100%);
  }
}

/* ---------- the short version ----------
   A config block a visitor can hand straight to whatever AI they already have
   open. The copy control sits in the corner of the block rather than under it,
   so the thing being copied and the button that copies it read as one object. */
.snippet {
  position: relative;
  max-width: 640px;
  border: 1px solid var(--etch);
  background: var(--panel);
}
.snippet__copy {
  position: absolute; top: 0; right: 0;
  font: inherit; font-size: .875rem; font-weight: 500;
  color: var(--silk-3); background: transparent;
  border: 0; border-left: 1px solid var(--etch); border-bottom: 1px solid var(--etch);
  padding: 7px 18px; cursor: pointer;
  transition: color .18s var(--ease), background .18s var(--ease);
}
.snippet__copy:hover { color: var(--signal); background: var(--signal-2); }
.snippet__copy.is-done { color: var(--signal); }
.snippet__copy.is-done::after { content: ' ✓'; }

.snippet__code {
  margin: 0;
  padding: 20px 22px;
  padding-right: 104px;            /* clear of the button */
  font-family: var(--mono); font-size: .875rem; line-height: 1.75;
  color: var(--silk-2);
  overflow-x: auto;
}

.intro__alt {
  margin-top: 18px; max-width: var(--measure);
  font-size: .9rem; color: var(--silk-3);
}
.intro__alt code {
  display: inline-block; margin-top: 5px;
  font-family: var(--mono); font-size: .875rem; color: var(--silk-2);
  background: var(--panel); border: 1px solid var(--etch);
  padding: 4px 9px;
  overflow-wrap: anywhere;
}

.intro__asks { margin-top: clamp(26px, 3.5vw, 42px); }
.intro__asks-title { font-size: .9rem; color: var(--silk-3); margin-bottom: 10px; }
.intro__asks li {
  position: relative;
  font-size: .95rem; color: var(--silk-2);
  padding: 9px 0 9px 18px;
  border-top: 1px solid var(--etch-2);
  max-width: var(--measure);
}
.intro__asks li:first-child { border-top: none; }
.intro__asks li::before {
  content: ''; position: absolute; left: 0; top: 17px;
  width: 6px; height: 6px; background: var(--signal);
}

/* ---------- the robot rail ----------
   A fixed column down the left, holding the robot. Nothing else lives there, so
   the whole rail can be pointer-events:none except the canvas itself. */
.robot {
  position: fixed;
  /* start below the bar, so the robot centres in the space that is actually
     visible rather than sliding behind the header on a short window */
  left: 0; top: 56px; bottom: 0;
  width: var(--rail);
  z-index: 5;
  display: none;
  pointer-events: none;
}
.robot__inner {
  position: absolute; left: 0; top: 50%;
  width: 100%;
  transform: translateY(-50%);
  will-change: transform;
}
.robot canvas {
  display: block; width: 100%; height: auto;
  pointer-events: auto;    /* the only clickable thing in the rail */
}

/* ---------- runner strip ----------
   The one part of the page that is not a document. Full-bleed, 100px tall and
   deliberately bright — it reads as a window cut into the dark page rather than
   a section of it. Everything inside is drawn on the canvas; there is no image
   file, so nothing extra is fetched and nothing resamples at any zoom. */
.game {
  position: relative;
  border-top: 1px solid var(--etch);
  /* a window, not a page: it stays edge to edge even when the rail is reserved */
  margin-left: calc(-1 * var(--rail));
  width: calc(100% + var(--rail));
}
.game__cv {
  display: block; width: 100%; height: 100px;
  /* a tap has to jump without the page scrolling or double-tap-zooming —
     `manipulation` still allows the drag that scrolls the page past the strip */
  touch-action: manipulation;
}

/* the scrim and the controls share one layer, shown whenever the run is not
   in progress: before the first start and after a crash */
.game__ui {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 8px 22px;
  padding: 0 var(--pad);
  background: rgba(6, 14, 22, .40);
}
.game__ui[hidden] { display: none; }

.game__btn {
  font: inherit; font-size: .9rem; font-weight: 600; letter-spacing: -.01em;
  color: #0B0F13; background: #fff;
  border: 0; padding: 9px 24px; cursor: pointer;
  transition: transform .3s var(--spring), background .2s var(--ease);
}
.game__btn:hover { background: var(--signal); transform: translateY(-1px) scale(1.04); }
.game__btn:active { transform: scale(.97); }

.game__score {
  font-family: var(--mono); font-size: .875rem; color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, .55);
}
.game__hint {
  font-size: .875rem; color: rgba(255, 255, 255, .86);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .55);
}

/* ---------- responsive ---------- */
/* The rail only exists where there is width to spare. Below this the robot is
   not rendered at all — the container stays display:none and the script bails
   out on the zero-width host. */
@media (min-width: 1280px) {
  :root { --rail: clamp(200px, 17vw, 300px); }
  .robot { display: block; }
}

@media (max-width: 900px) {
  .bar__nav { order: 3; width: 100%; margin-left: 0; gap: 18px; }
}

@media (max-width: 767.98px) {
  :root { --band: clamp(48px, 8vw, 68px); }

  .hero { padding-top: 44px; }
  .hero__claim { max-width: none; }

  .axis figcaption { flex-direction: column; gap: 12px; }
  .axis__legend { flex-direction: column; gap: 8px; }

  .tl__row { grid-template-columns: minmax(0, 1fr); gap: 8px; padding-left: 26px; }
  .tl__when { padding-top: 0; order: -1; }

  .pr--mini { grid-template-columns: minmax(0, 1fr); gap: 4px; }


  .contact > div { grid-template-columns: minmax(0, 1fr); gap: 3px; }

  /* no space bar on a phone; the tap target is self-evident */
  .game__hint { display: none; }
  .game__ui { gap: 8px 16px; }

  .foot__in { flex-direction: column; }
}

@media (max-width: 420px) {
  .bar__handle { display: none; }
}
