/* PostData — component layer
   Semantic classes, no utilities. Everything works as plain elements with
   classes; the dynamic parts are x-text / x-for targets.

   House rules enforced here:
   · No pills, no rounded chips, no gradient fills, no ambient animation.
   · The accent appears only on the primary action, the focus ring and live
     state. All other colour on a page is a data series.
   · A 2px edge always means "active": focus ring, chart stroke, checkmark.
   · --c-faint is never used for text (see tokens.css); anything readable and
     quiet uses --c-muted. */

/* =========================================================================
   1. Buttons
   <button class="btn btn--primary">  --secondary  --ghost
   states: :hover :focus-visible :active [disabled] .is-loading
   ========================================================================= */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--c-text);
  --btn-bd: var(--c-border-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  min-height: var(--tap);
  padding: 0 var(--sp-6);
  border: var(--bw) solid var(--btn-bd);
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: var(--fw-medium) var(--fs-sm) / 1 var(--font-mono);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              color var(--dur-2) var(--ease);
}
.btn--primary {
  --btn-bg: var(--c-accent);
  --btn-fg: var(--c-on-accent);
  --btn-bd: var(--c-accent);
  font-weight: var(--fw-semi);
}
.btn--primary:hover { --btn-bg: var(--c-accent-hover); --btn-bd: var(--c-accent-hover); }
.btn--primary:active { --btn-bg: var(--c-accent-active); --btn-bd: var(--c-accent-active); }

.btn--secondary { --btn-bg: var(--c-raised); }
.btn--secondary:hover { --btn-bg: var(--c-overlay); --btn-bd: var(--c-faint); }
.btn--secondary:active { --btn-bg: var(--c-surface); }

.btn--ghost { --btn-bd: transparent; --btn-fg: var(--c-text-dim); }
.btn--ghost:hover { --btn-bg: var(--c-raised); --btn-fg: var(--c-text); }

.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.38; cursor: not-allowed; }
/* Loading: the label is replaced by a mono ellipsis that steps, not a spinner. */
.btn.is-loading { pointer-events: none; color: transparent; position: relative; }
.btn.is-loading::after {
  content: "···";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--btn-fg);
  letter-spacing: 0.1em;
  animation: pd-ellipsis 900ms steps(3, end) infinite;
}
@keyframes pd-ellipsis { 0% { clip-path: inset(0 66% 0 0); } 33% { clip-path: inset(0 33% 0 0); } 66% { clip-path: inset(0 0 0 0); } }

/* Text link that acts: mono, no underline, arrow shifts on hover. */
.link-action {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font: var(--fw-medium) var(--fs-sm) / 1 var(--font-mono);
  text-decoration: none;
  color: var(--c-text);
}
.link-action::after { content: "→"; color: var(--c-faint); transition: translate var(--dur-2) var(--ease), color var(--dur-2) var(--ease); }
.link-action:hover { color: var(--c-accent-dim); }
.link-action:hover::after { translate: 3px 0; color: var(--c-accent); }

/* Micro-label: the pill's replacement. A rule, not a capsule. */
.kicker {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin: 0 0 var(--sp-6);
  font: var(--fw-medium) var(--fs-2xs) / 1 var(--font-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-muted);
}
.kicker::before { content: ""; width: 28px; height: var(--bw); background: var(--c-border-strong); flex: none; }

/* =========================================================================
   2. Live status
   <span class="status status--live"><i class="status__mark"></i>Live</span>
   Liveness is a number, not a heartbeat: the mark flashes for 160ms only when
   a sample actually lands (JS adds .is-tick), and .age counts the gap.
   ========================================================================= */
.status {
  --mark: var(--c-faint);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  font: var(--fw-medium) var(--fs-2xs) / 1 var(--font-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-muted);
}
.status__mark {
  flex: none;
  width: 6px; height: 6px;
  border-radius: var(--r-xs);
  background: var(--mark);
}
.status--live { --mark: var(--c-accent); color: var(--c-accent-dim); }
.status--live .status__mark.is-tick { animation: pd-tick var(--dur-tick) var(--ease) 1; }
@keyframes pd-tick {
  0%   { box-shadow: 0 0 0 0 var(--c-accent-line); }
  100% { box-shadow: 0 0 0 5px transparent; }
}
/* Connecting: hollow mark, no blink. */
.status--connecting { --mark: transparent; color: var(--c-text-dim); }
.status--connecting .status__mark { box-shadow: inset 0 0 0 var(--bw) var(--c-text-dim); }
.status--offline { --mark: transparent; }
.status--offline .status__mark { box-shadow: inset 0 0 0 var(--bw) var(--c-muted); }

/* Age counter — the actual liveness signal. */
.age {
  font: var(--fw-regular) var(--fs-xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--num-feat);
  color: var(--c-muted);
}
.age--stale { color: var(--c-text-dim); }

/* =========================================================================
   3. Metric tile — the device page's signature element
   Series colour is bound per tile: style="--tile-c: var(--s-1)".
   It appears twice (rule + sparkline) and nowhere else.
   ========================================================================= */
/* Wrapping flex, not grid: a metric count that doesn't fill the last row would
   leave grid cells empty, and an empty cell shows the seam colour — which reads
   as a lit tile. With flex the last row grows to fill instead. */
.tile-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--bw);                 /* hairline seams, not floating cards */
  background: var(--c-border);
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.tile {
  --tile-c: var(--c-faint);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  flex: 1 1 168px;
  min-width: 0;
  padding: var(--sp-5) var(--sp-6);
  background: var(--c-surface);
  transition: background var(--dur-2) var(--ease);
}
.tile:hover { background: var(--c-raised); }
/* The series colour reads as a 2px rule across the top of the tile. */
.tile__head { display: flex; align-items: flex-start; gap: var(--sp-4); min-width: 0; padding-top: var(--sp-1); position: relative; }
.tile__head::before {
  content: "";
  position: absolute;
  top: calc(var(--sp-5) * -1);
  left: 0;
  width: 18px;
  height: var(--bw-thick);
  background: var(--tile-c);
}
.tile__name {
  flex: 1;
  min-width: 0;
  font: var(--fw-medium) var(--fs-xs) / 1.3 var(--font-mono);
  color: var(--c-muted);
  display: -webkit-box;          /* 40-char names: 2 lines, then ellipsis */
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.tile__value { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }
.tile__num {
  font: var(--fw-medium) var(--fs-2xl) / var(--lh-tight) var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--num-feat);
  letter-spacing: var(--ls-tight);
  color: var(--c-text);
  overflow-wrap: anywhere;
}
/* A string value: smaller, green like a JSON string, quoted by CSS. */
.tile__num--string {
  font-size: var(--fs-lg);
  line-height: var(--lh-snug);
  letter-spacing: 0;
  color: var(--syn-string);
}
.tile__num--string::before, .tile__num--string::after { content: "\""; color: var(--c-muted); }
/* Unit slot. PostData stores no unit metadata, so nothing fills this today —
   kept for if per-device metadata ever lands. */
.tile__unit { font: var(--fw-regular) var(--fs-sm) / 1 var(--font-mono); color: var(--c-muted); }
.tile__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-top: auto;
  font: var(--fw-regular) var(--fs-2xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--c-muted);
}
.tile__delta { color: var(--c-muted); }
.tile__delta--up { color: var(--c-success); }
.tile__delta--down { color: var(--c-danger); }
.tile.is-stale { --tile-c: var(--c-border-strong); }
.tile.is-stale .tile__num { color: var(--c-text-dim); }
.tile.is-stale .spark { opacity: 0.4; }
.tile.is-empty .tile__num { color: var(--c-muted); }

/* Sparkline: hand-rolled inline SVG, stroke via currentColor. No uPlot. */
.spark { display: block; width: 100%; height: 22px; color: var(--tile-c); overflow: visible; }
.spark polyline { fill: none; stroke: currentColor; stroke-width: 1.5; vector-effect: non-scaling-stroke; }

/* Static loading block. No shimmer — a dark rectangle is honest and free. */
.placeholder {
  display: block;
  height: 0.75rem;
  border-radius: var(--r-xs);
  background: var(--c-raised);
}

/* =========================================================================
   4. Code block
   <figure class="code"><figcaption class="code__bar">…</figcaption>
     <pre class="code__body"><code>…</code></pre></figure>
   ========================================================================= */
.code {
  margin: 0 0 var(--sp-6);
  background: var(--c-surface);
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.code__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding: 0 var(--sp-3) 0 var(--sp-6);
  min-height: 34px;
  border-bottom: var(--bw) solid var(--c-border);
  background: var(--c-bg);
}
.code__label {
  font: var(--fw-medium) var(--fs-2xs) / 1 var(--font-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-muted);
}
.code__copy {
  margin-left: auto;
  min-height: 26px;
  padding: 0 var(--sp-4);
  border: var(--bw) solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--c-muted);
  font: var(--fw-medium) var(--fs-2xs) / 1 var(--font-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.code__copy:hover { color: var(--c-text); background: var(--c-raised); }
.code__copy.is-copied { color: var(--c-accent-dim); }
.code:not(:has(.code__bar)) { position: relative; }
.code:not(:has(.code__bar)) .code__copy {
  position: absolute;
  top: var(--sp-3); right: var(--sp-3);
  opacity: 0;
  background: var(--c-raised);
  border-color: var(--c-border);
}
.code:hover .code__copy, .code__copy:focus-visible { opacity: 1; }

.code__body {
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: var(--sp-5) var(--sp-6);
  white-space: pre;               /* curl lines scroll; wrapping them is worse */
}
.code--wrap .code__body { white-space: pre-wrap; overflow-wrap: anywhere; }

.tok-comment { color: var(--syn-comment); font-style: italic; }
.tok-key     { color: var(--syn-key); }
.tok-string  { color: var(--syn-string); }
.tok-number  { color: var(--syn-number); }
.tok-punct   { color: var(--syn-punct); }
.tok-func    { color: var(--syn-func); font-weight: var(--fw-medium); }
.tok-url     { color: var(--syn-url); }

/* =========================================================================
   5. Device card — explore grid
   Hairline seams again: the grid is one ruled sheet, not scattered cards.
   ========================================================================= */
/* Same reason as .tile-row: flex so a partial last row grows to fill. */
.device-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--bw);
  background: var(--c-border);
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.device-card {
  display: flex;
  flex-direction: column;
  flex: 1 1 268px;
  gap: var(--sp-5);
  padding: var(--sp-6);
  background: var(--c-surface);
  text-decoration: none;
  color: inherit;
  transition: background var(--dur-2) var(--ease);
}
.device-card:hover { background: var(--c-raised); }
.device-card:hover .device-card__name { color: var(--c-accent-dim); }
.device-card:focus-visible { outline-offset: -2px; }
.device-card__head { display: flex; align-items: baseline; gap: var(--sp-5); min-width: 0; }
.device-card__name {
  flex: 1;
  min-width: 0;
  font: var(--fw-medium) var(--fs-md) / var(--lh-snug) var(--font-mono);
  color: var(--c-text);
  overflow-wrap: anywhere;
  transition: color var(--dur-2) var(--ease);
}
/* Metric preview: a key/value ladder. Never JSON.stringify. */
.device-card__metrics {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-3) var(--sp-5);
  margin: 0;
  padding-top: var(--sp-5);
  border-top: var(--bw) solid var(--c-border-faint);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
}
.device-card__metrics dt { color: var(--c-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.device-card__metrics dd {
  margin: 0;
  text-align: right;
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--num-feat);
}
.device-card__more { grid-column: 1 / -1; color: var(--c-muted); font-size: var(--fs-2xs); }
.device-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-top: auto;
  font: var(--fw-regular) var(--fs-2xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--c-muted);
}
/* Idle for over a day: everything dims one step. No badge, no dashed border. */
.device-card--stale { background: var(--c-bg); }
.device-card--stale .device-card__name { color: var(--c-text-dim); }
.device-card--stale .device-card__metrics dd { color: var(--c-muted); }

/* Empty state, shared by explore and charts. */
.empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-9) var(--sp-7);
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-lg);
  color: var(--c-muted);
}
.empty__title { font: var(--fw-medium) var(--fs-md) / 1.3 var(--font-mono); color: var(--c-text); }
.empty p { font-size: var(--fs-sm); max-width: 52ch; margin: 0; }

/* =========================================================================
   6. Chart block — uPlot host
   Exact uPlot option values live in /js/uplot-theme.js.
   ========================================================================= */
.chart {
  background: var(--c-surface);
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.chart__head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-6);
  flex-wrap: wrap;
  padding: var(--sp-5) var(--sp-6);
  border-bottom: var(--bw) solid var(--c-border);
}
.chart__title {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin: 0;
  font: var(--fw-medium) var(--fs-sm) / 1 var(--font-mono);
  color: var(--c-text);
}
/* Series marker is a 2px rule, matching the tile and the legend. */
.chart__swatch { flex: none; width: 14px; height: var(--bw-thick); background: var(--chart-c, var(--s-1)); }
.chart__unit { color: var(--c-muted); font-weight: var(--fw-regular); }
.chart__readout {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: var(--sp-5);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--num-feat);
}
.chart__readout-value { font-size: var(--fs-lg); font-weight: var(--fw-medium); color: var(--c-text); }
.chart__readout-time { font-size: var(--fs-2xs); color: var(--c-muted); }
.chart__body { position: relative; padding: var(--sp-5) var(--sp-6) var(--sp-4); background: var(--c-sunken); }

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-7);
  padding: var(--sp-4) var(--sp-6);
  background: var(--c-sunken);
  border-top: var(--bw) solid var(--c-border-faint);
}
.legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 24px;
  padding: var(--sp-2) 0;
  border: 0;
  background: none;
  cursor: pointer;
  font: var(--fw-regular) var(--fs-2xs) / 1 var(--font-mono);
  color: var(--c-muted);
  transition: color var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.legend-item::before { content: ""; width: 14px; height: var(--bw-thick); background: var(--legend-c, var(--c-muted)); }
.legend-item:hover { color: var(--c-text); }
.legend-item[aria-pressed="false"] { opacity: 0.42; }
.legend-item[aria-pressed="false"]::before { background: var(--c-faint); }
.legend-item__value { color: var(--c-text-dim); font-variant-numeric: tabular-nums; }

/* Loading / no-data live inside the plot area and keep its ruling, so an
   empty chart still reads as a chart. */
.chart__state {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--sp-3);
  min-height: 190px;
  padding: var(--sp-6) 0 var(--sp-5) 46px;
  background-image: repeating-linear-gradient(to top, var(--c-border-faint) 0 1px, transparent 1px 44px);
  font: var(--fw-regular) var(--fs-sm) / 1.4 var(--font-mono);
  color: var(--c-text-dim);
}
.chart__state small { color: var(--c-muted); font-size: var(--fs-xs); }

/* =========================================================================
   7. Time-span selector
   <div class="span-select" role="group"><button aria-pressed="true">24h</button>…
   ========================================================================= */
.span-select {
  display: inline-flex;
  border: var(--bw) solid var(--c-border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.span-select__opt {
  min-height: 34px;
  min-width: 48px;
  padding: 0 var(--sp-5);
  border: 0;
  border-left: var(--bw) solid var(--c-border);
  background: var(--c-surface);
  color: var(--c-muted);
  font: var(--fw-medium) var(--fs-xs) / 1 var(--font-mono);
  cursor: pointer;
  transition: background var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.span-select__opt:first-child { border-left: 0; }
.span-select__opt:hover { background: var(--c-raised); color: var(--c-text); }
.span-select__opt[aria-pressed="true"] {
  background: var(--c-overlay);
  color: var(--c-text);
  box-shadow: inset 0 -2px 0 var(--c-accent);
}
.span-select__opt[disabled] { color: var(--c-faint); cursor: not-allowed; }
.span-select__opt[disabled]:hover { background: var(--c-surface); color: var(--c-faint); }
@media (max-width: 719px) {
  .span-select { display: flex; }
  .span-select__opt { flex: 1; min-height: 42px; }
}

/* =========================================================================
   8. Nav & footer
   ========================================================================= */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--c-bg);
  border-bottom: var(--bw) solid var(--c-border);
}
.site-nav__inner { display: flex; align-items: stretch; gap: var(--sp-6); min-height: var(--nav-h); }
.site-nav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  font: var(--fw-medium) var(--fs-md) / 1 var(--font-mono);
  color: var(--c-text);
  text-decoration: none;
  letter-spacing: var(--ls-tight);
}
/* The mark is the only logo: a 2px amber rule, the same shape as a series rule. */
.site-nav__mark { width: 14px; height: var(--bw-thick); background: var(--c-accent); }
.site-nav__links { display: flex; align-items: stretch; gap: 0; margin: 0 0 0 auto; padding: 0; list-style: none; }
.site-nav__links li { margin: 0; display: flex; }
.site-nav__link {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--sp-5);
  font: var(--fw-medium) var(--fs-sm) / 1 var(--font-mono);
  color: var(--c-muted);
  text-decoration: none;
  box-shadow: inset 0 -1px 0 transparent;
  transition: color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.site-nav__link:hover { color: var(--c-text); box-shadow: inset 0 -1px 0 var(--c-border-strong); }
.site-nav__link[aria-current="page"] { color: var(--c-text); box-shadow: inset 0 -2px 0 var(--c-accent); }
@media (max-width: 559px) {
  .site-nav__inner { min-height: 46px; }
  .site-nav__link { padding: 0 var(--sp-4); font-size: var(--fs-xs); }
}

.site-footer {
  margin-top: var(--sp-11);
  border-top: var(--bw) solid var(--c-border);
  padding: var(--sp-7) 0 var(--sp-8);
  font-size: var(--fs-xs);
  color: var(--c-muted);
}
.site-footer__inner { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-5) var(--sp-8); }
.site-footer__links { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6); margin: 0; padding: 0; list-style: none; }
.site-footer__links li { margin: 0; }
.site-footer__links a { color: var(--c-muted); text-decoration: none; }
.site-footer__links a:hover { color: var(--c-text); text-decoration: underline; }
/* Attribution and the public-data warning sit together at the trailing edge. */
.site-footer__by {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--c-muted);
}
.site-footer__by a { color: var(--c-text-dim); text-decoration: none; }
.site-footer__by a:hover { color: var(--c-accent-dim); text-decoration: underline; }
.site-footer__note { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--c-muted); }
/* On a phone the footer stacks; nothing should push to a trailing edge. */
@media (max-width: 639px) {
  .site-footer__by { margin-left: 0; }
}

/* =========================================================================
   9. Section heading + FAQ
   ========================================================================= */
.section-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-6);
  margin-bottom: var(--sp-7);
  padding-bottom: var(--sp-4);
  border-bottom: var(--bw) solid var(--c-border);
}
.section-head__index {
  font: var(--fw-medium) var(--fs-2xs) / 1 var(--font-mono);
  letter-spacing: var(--ls-wide);
  color: var(--c-muted);
}
.section-head h2 { margin: 0; font: var(--fw-medium) var(--fs-xl) / 1 var(--font-mono); letter-spacing: var(--ls-tight); }
.section-head__aside { margin-left: auto; font: var(--fw-regular) var(--fs-xs) / 1 var(--font-mono); color: var(--c-muted); }

.faq { border-top: var(--bw) solid var(--c-border); }
.faq details { margin: 0; }
.faq summary { font-family: var(--font-mono); font-size: var(--fs-sm); }

/* =========================================================================
   10. Hero
   The graticule is the one background treatment on the site.
   ========================================================================= */
.hero { position: relative; padding: var(--sp-9) 0 var(--sp-8); overflow: hidden; }
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--c-border-faint) 0 1px, transparent 1px),
    linear-gradient(to bottom, var(--c-border-faint) 0 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: linear-gradient(160deg, #000 0%, transparent 62%);
  pointer-events: none;
}
.hero > * { position: relative; }
.hero h1 {
  margin: 0 0 var(--sp-5);
  max-width: 20ch;
  font: var(--fw-medium) clamp(2.375rem, 5.2vw, 3.5rem) / 1.05 var(--font-mono);
  letter-spacing: -0.045em;
}
.hero h1 em { font-style: normal; color: var(--c-muted); }
.hero__sub { max-width: 54ch; margin-bottom: var(--sp-7); font-size: var(--fs-lg); color: var(--c-text-dim); }
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-5); }
.hero__grid { display: grid; gap: var(--sp-8); align-items: start; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .hero { padding: var(--sp-10) 0 var(--sp-9); }
  .hero__grid { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: var(--sp-9); }
}

/* Key/value spec list — docs, device meta. */
.spec { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-4) var(--sp-7); margin: 0; font-size: var(--fs-sm); }
.spec dt { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--c-muted); }
.spec dd { margin: 0; font-family: var(--font-mono); color: var(--c-text); font-variant-numeric: tabular-nums; }
