/*
| Blip24 — one stylesheet for the public site and the dashboard.
|
| An operations board in daylight. Paper, ink and hairlines; the only colour on
| the page is the colour of something breaking. A red square in a column of
| grey rows is the loudest thing on screen — which is how the product itself
| works — so nothing decorative is allowed any chroma, not even the brand.
|
| One typeface: Inter, self-hosted, with its optical-size axis on so large
| headings take the Display letterforms and body text keeps the text cut.
| Figures are tabular where they sit in columns. Monospace is for code only.
*/

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin-opsz-normal.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin-ext-opsz-normal.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: light;

  --paper: #FFFFFF;
  --wash: #F6F7F9;       /* sidebar, footer, table heads — the second surface */
  --panel: #FFFFFF;
  --raise: #F3F4F7;      /* hover */
  --line: #E2E5EA;
  --line-soft: #ECEEF1;
  --line-strong: #CDD2DA;

  --ink: #16181D;
  --dim: #565D69;
  /* Was #8A909B: 3.21:1 on paper, under AA for the small print it sets.
     4.90:1 on paper, 4.57:1 on --wash. */
  --faint: #6B717D;

  /* Chroma is reserved. Nothing decorative may use these. Each is dark enough
     to read as text on white, because a status is a word before it is a colour. */
  --ok: #148A47;
  --degraded: #A86F06;
  --partial: #C9560E;
  --major: #CF2F2A;
  --maint: #3354D1;
  --unknown: #9AA0AA;
  /* Every status by its own name too, so `var(--{status})` works for all six
     wherever a template sets a colour from a status. */
  --operational: var(--ok);
  --maintenance: var(--maint);

  --sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --code: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, monospace;
  /* Kept so older markup that asks for --mono gets the text face, not a typewriter. */
  --mono: var(--sans);

  /* The one dark surface: the night every page opens on, the footer it closes
     on, and the dashboard's chrome. A night sky, not a stand-in for black; the
     work itself — tables, boards, forms — always sits on paper. */
  --night: #0A0F1E;
  --mark-ink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 16' fill='none' stroke-linecap='round'%3E%3Cpath d='M1 15A10 10 0 0 1 21 15' stroke='%2316181D' stroke-opacity='.4' stroke-width='1.6'/%3E%3Cpath d='M5.5 15A5.5 5.5 0 0 1 16.5 15' stroke='%2316181D' stroke-opacity='.4' stroke-width='1.6'/%3E%3Cpath d='M11 15L17.5 7.4' stroke='%2316181D' stroke-width='1.6'/%3E%3Ccircle cx='14.6' cy='8.6' r='1.9' fill='%23148A47'/%3E%3C/svg%3E");
  --mark-night: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 16' fill='none' stroke-linecap='round'%3E%3Cpath d='M1 15A10 10 0 0 1 21 15' stroke='%23FFFFFF' stroke-opacity='.5' stroke-width='1.6'/%3E%3Cpath d='M5.5 15A5.5 5.5 0 0 1 16.5 15' stroke='%23FFFFFF' stroke-opacity='.5' stroke-width='1.6'/%3E%3Cpath d='M11 15L17.5 7.4' stroke='%23FFFFFF' stroke-width='1.6'/%3E%3Ccircle cx='14.6' cy='8.6' r='1.9' fill='%2334D27A'/%3E%3C/svg%3E");
  --mark-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 16' fill='none' stroke-linecap='round'%3E%3Cpath d='M1 15A10 10 0 0 1 21 15' stroke='%23FFFFFF' stroke-opacity='.5' stroke-width='1.6'/%3E%3Cpath d='M5.5 15A5.5 5.5 0 0 1 16.5 15' stroke='%23FFFFFF' stroke-opacity='.5' stroke-width='1.6'/%3E%3Cpath d='M11 15L17.5 7.4' stroke='%23FFFFFF' stroke-width='1.6'/%3E%3Ccircle cx='14.6' cy='8.6' r='1.9' fill='%23FF6A63'/%3E%3C/svg%3E");
  --night-dim: rgba(214, 222, 238, .72);
  /* .5 was 4.20:1 on --night; .58 is 5.3:1, with room for the slightly
     lighter night of a chip or the board's edge. */
  --night-faint: rgba(214, 222, 238, .58);
  --night-line: rgba(255, 255, 255, .1);
  /* The horizon: a hairline of sky-light wherever night meets paper. */
  --horizon-line: rgba(214, 226, 250, .14);
  /* Anything rising out of the night: a lit top edge where it crosses the
     horizon, and a shadow cast back into the dark — long on the home page's
     board, shorter where the next thing sits close below. */
  --rise-edge: 0 0 0 1px rgba(255, 255, 255, .08), 0 -1px 0 rgba(255, 255, 255, .6);
  --rise-cast: 0 24px 48px -26px rgba(10, 15, 30, .42), 0 6px 18px rgba(10, 15, 30, .08);
  --rise-cast-deep: 0 40px 80px -24px rgba(10, 15, 30, .5), 0 10px 28px rgba(10, 15, 30, .14);
  --ok-glow: #34D27A;
  /* Blip24's own light: warm, the colour of a lamp rather than a screen.
     It lives on the night — the beam, the primary action, this minute's mark —
     and on paper only as a highlighter stroke under a phrase. Never a glow,
     never a gradient, never a status. */
  --lamp: #FFBF5E;
  --lamp-hover: #FFCD80;
  --lamp-ink: #221605;
  --lamp-mark: rgba(255, 191, 94, .42);
  /* How far the first thing on a page rises into the night above it. */
  --tail: clamp(64px, 7vw, 96px);
  --wrap: 1200px;
  --gutter: 28px;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 72px; } /* under the sticky nav */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: -.006em;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }
a:hover { color: var(--ink); }

/* Fields draw their own ring (see forms) and keep their own corners: once the
   text-input rule stopped out-weighing everything, this rounded a focused box
   from 8px to 4px and unwelded an input group. */
:focus-visible:not(input, select, textarea) {
  outline: 2px solid var(--maint);
  outline-offset: 2px;
  border-radius: 4px;
}

::selection { background: var(--lamp-mark); }

code, pre, kbd, samp { font-family: var(--code); font-size: .9em; }

/* ── type ────────────────────────────────────────────────────────────────── */

h1, h2, h3, h4 { margin: 0; line-height: 1.08; text-wrap: balance; font-weight: 600; }

/* Semibold and tight, the way Inter's Display cut is drawn to be set. */
.d1, h1 { font-size: clamp(2.35rem, 4.4vw, 3.5rem); font-weight: 600; letter-spacing: -.034em; line-height: 1.04; }
.d2, h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); font-weight: 600; letter-spacing: -.028em; }
.d3, h3 { font-size: clamp(1.08rem, 1.5vw, 1.22rem); font-weight: 600; letter-spacing: -.018em; line-height: 1.25; }
.d4 { font-size: 1rem; font-weight: 600; letter-spacing: -.01em; line-height: 1.3; }

p { margin: 0; }
.lede { font-size: clamp(1.04rem, 1.35vw, 1.16rem); line-height: 1.55; color: var(--dim); max-width: 48ch; }
.body { color: var(--dim); max-width: 64ch; }
.body + .body { margin-top: .9em; }
/* Inside a stack the gap already separates them; the margin on top of it
   doubled every paragraph break on the home and pricing pages. */
.stack > .body + .body { margin-top: 0; }
.body a, .lede a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
/* A link that ends a section: a word and an arrow, no button chrome. */
/* A link that goes somewhere, said by its underline rather than an arrow
   appended to the words. */
.more {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; font-size: .92rem; font-weight: 560; color: var(--ink);
  text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 4px; text-decoration-thickness: 1px;
  transition: text-decoration-color .15s;
}
.more:hover { color: var(--ink); text-decoration-color: var(--ink); }
.body a:hover, .lede a:hover { text-decoration-color: var(--ink); }
.small { font-size: .86rem; }
.tiny { font-size: .78rem; }
.dim { color: var(--dim); }
.faint { color: var(--faint); }
/* A figure that should line up with the ones above and below it. Inter's
   tabular digits are ordinary digits, not a typewriter face. */
.mono { font-variant-numeric: tabular-nums; }

/* ── layout ──────────────────────────────────────────────────────────────── */

.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.stack { display: flex; flex-direction: column; }
.row { display: flex; flex-wrap: wrap; align-items: center; }
.between { justify-content: space-between; }
.baseline { align-items: baseline; }
.g1 { gap: 4px } .g2 { gap: 8px } .g3 { gap: 12px } .g4 { gap: 16px }
.g5 { gap: 20px } .g6 { gap: 24px } .g8 { gap: 32px }

section { padding-block: clamp(34px, 4vw, 60px); }
/* A divider is a hairline and nothing else. Its ends used to carry crosshair
   "registration marks", which said nothing about the content and are among
   the most recognisable tells of a generated landing page. */
.rule { border-top: 1px solid var(--line-soft); position: relative; }

/* ── nav ─────────────────────────────────────────────────────────────────── */

.nav {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--line-soft);
  transition: background .25s, border-color .25s;
}
/* Logo and sections on the left, as a site you use is laid out; the account
   on the right. Plain words, no capsule: where you are is a rule under the
   word, drawn on the nav's own bottom edge. */
.nav .wrap { display: flex; align-items: center; gap: 36px; height: 64px; }
.nav-links { display: flex; align-items: stretch; gap: 26px; height: 100%; }
.nav-links a {
  position: relative; display: flex; align-items: center;
  font-size: .88rem; font-weight: 500; color: var(--dim); transition: color .15s;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a[aria-current=page]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: currentColor;
}
.nav-links a[aria-current=page] { color: var(--ink); }
.nav-actions { display: flex; align-items: center; justify-content: flex-end; gap: 18px; margin-left: auto; }
.nav-signin { font-size: .9rem; font-weight: 500; color: var(--dim); white-space: nowrap; }
.nav-signin:hover { color: var(--ink); }

/* Over the night band. */
.nav.nav-night { background: var(--night); border-bottom-color: rgba(255, 255, 255, .06); }
/* The logo on any night surface: the nav over a band, the footer, the
   dashboard's sidebar. */
:is(.night, .nav-night) .logo { color: #fff; }
:is(.night, .nav-night) .logo .bars { background-image: var(--mark-night); }
.nav-night .nav-links a, .nav-night .nav-signin { color: rgba(222, 229, 243, .72); }
.nav-night .nav-links a:hover { color: #fff; }
.nav-night .nav-links a[aria-current=page] { color: #fff; }
.nav-night .nav-links a[aria-current=page]::after { background: var(--lamp); }
.nav-night .nav-signin:hover { color: #fff; }

/* The phone menu: two strokes that cross when open, and a sheet under the bar
   with the sections as full-width rows. Only below 860px, where the bar's own
   links give way to it. */
.nav-menu { display: none; }
.nav-menu summary {
  list-style: none; cursor: pointer; display: grid; place-items: center;
  width: 36px; height: 36px; margin-right: -8px; border-radius: 8px; color: var(--ink);
}
.nav-menu summary::-webkit-details-marker { display: none; }
.nav-burger { position: relative; width: 16px; height: 10px; }
.nav-burger i { position: absolute; left: 0; right: 0; height: 1.5px; border-radius: 1px; background: currentColor; transition: transform .2s ease, top .2s ease; }
.nav-burger i:first-child { top: 1px; }
.nav-burger i:last-child { top: 7.5px; }
.nav-menu[open] .nav-burger i { top: 4.25px; }
.nav-menu[open] .nav-burger i:first-child { transform: rotate(45deg); }
.nav-menu[open] .nav-burger i:last-child { transform: rotate(-45deg); }
.nav-sheet {
  position: absolute; left: 0; right: 0; top: 100%; display: flex; flex-direction: column;
  padding: 6px var(--gutter) 14px; background: var(--paper); border-bottom: 1px solid var(--line);
  box-shadow: 0 18px 30px -18px rgba(10, 15, 30, .28);
}
.nav-sheet a {
  display: flex; align-items: center; justify-content: space-between; padding: 13px 0;
  border-top: 1px solid var(--line-soft); font-size: .98rem; font-weight: 520; color: var(--ink);
}
.nav-sheet a:first-child { border-top: 0; }
.nav-sheet a .arr { color: var(--faint); }
.nav-sheet a[aria-current=page] { box-shadow: inset 2px 0 0 var(--ink); padding-left: 12px; }
/* Sign in moves into the sheet only where the bar has no room for it. */
.nav-sheet .sheet-signin { display: none; }
@media (max-width: 440px) { .nav-signin { display: none; } .nav-sheet .sheet-signin { display: flex; } }
.nav-night .nav-menu summary { color: #fff; }
.nav-night .nav-sheet { background: var(--night); border-bottom-color: rgba(255, 255, 255, .08); box-shadow: 0 18px 30px -18px rgba(0, 0, 0, .7); }
.nav-night .nav-sheet a { color: #fff; border-top-color: rgba(255, 255, 255, .07); }
.nav-night .nav-sheet a .arr { color: var(--night-faint); }
.nav-night .nav-sheet a[aria-current=page] { box-shadow: inset 2px 0 0 var(--lamp); }

.logo { display: inline-flex; align-items: center; gap: 9px; font-weight: 680; letter-spacing: -.025em; font-size: 1.08rem; color: var(--ink); white-space: nowrap; }
/* The mark: a radar's half-dome, two range rings, its sweep, and one vendor
   lit in the operational green. The markup still carries the three bars of
   the earlier mark; they are hidden and the mark is drawn from one SVG. */
.logo .bars { flex: none; width: 22px; height: 16px; background: var(--mark-ink) no-repeat center / contain; }
.logo .bars i, .msg .av .bars i { display: none; }

/* ── buttons ─────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font: inherit; font-size: .9rem; font-weight: 560; letter-spacing: -.005em; line-height: 18px;
  /* One height for every control: a field, a button and a segmented switch on
     the same row were 44, 32 and 38px tall, so no row of them ever lined up. */
  min-height: 38px; padding: 9px 16px; border-radius: 8px; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; transition: background .15s, border-color .15s, color .15s, box-shadow .15s, transform .08s;
}
.btn:active:not(:disabled, [aria-disabled=true]) { transform: translateY(1px); }
/* A lit top edge on the solid button, the way a key catches light: the only
   thing that tells it apart from a black rectangle at a glance. */
.btn-solid { background: var(--ink); color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), 0 1px 2px rgba(16, 20, 28, .18); }
.btn-solid:hover { background: #2C3038; color: #fff; }
.btn-line { border-color: var(--line); color: var(--ink); background: var(--paper); box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
.btn-line:hover { border-color: var(--line-strong); background: var(--wash); color: var(--ink); }
.btn-sm { min-height: 32px; padding: 6px 12px; font-size: .84rem; border-radius: 7px; }
/* A button that sits in a sentence, read as a link would be. */
.btn-text { font: inherit; color: var(--ink); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
.btn-text:hover { text-decoration-color: var(--ink); }
.btn:disabled, .btn[aria-disabled=true] { opacity: .45; cursor: not-allowed; box-shadow: none; }
/* An arrow that leans the way it points when you reach for it. */
.arr { display: inline-block; transition: transform .16s ease; }
:is(a, button):hover .arr { transform: translateX(3px); }

/* ── status: the only chroma ─────────────────────────────────────────────── */

/*
| A lamp and a word. The square is the indicator on a departures board; the
| word is in the status colour taken a step towards ink, so it reads as text.
| Disruptions get a faint wash of their colour; nothing else does.
*/
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .78rem; font-weight: 560; letter-spacing: -.003em; line-height: 1.4;
  padding: 2px 8px 2px 7px; border-radius: 6px;
  white-space: nowrap; --c: var(--unknown);
  color: color-mix(in srgb, var(--c) 84%, var(--ink));
  background: color-mix(in srgb, var(--c) 9%, var(--paper));
}
.pill::before { content: ""; width: 7px; height: 7px; border-radius: 2px; background: var(--c); flex: none; }
.pill-operational { --c: var(--ok); }
.pill-degraded    { --c: var(--degraded); }
.pill-partial     { --c: var(--partial); }
.pill-major       { --c: var(--major); background: color-mix(in srgb, var(--major) 10%, var(--paper)); }
.pill-maintenance { --c: var(--maint); }
.pill-unknown     { --c: var(--unknown); color: var(--dim); background: var(--wash); }

/* Operational, in a list.
|  The majority state must not compete with the exceptions. A board of forty
|  healthy vendors was forty green pills, which left a red one nothing to stand
|  against. The lamp stays green so the row is never confused with `unknown`;
|  the field of colour goes. */
.pill-quiet { color: var(--dim); background: transparent; font-weight: 500; padding-inline: 0; }
.pill-quiet::before { background: var(--ok); }

/* the 90-day strip: this category's one unmistakable visual, so it gets room */
.strip { display: flex; gap: 2px; align-items: stretch; height: 34px; }
.strip i { flex: 1 1 0; min-width: 2px; border-radius: 2px; background: var(--ok); opacity: .34; transition: opacity .12s; }
.strip:hover i { opacity: .24; }
.strip i:hover { opacity: 1 !important; }
.strip i.s-degraded    { background: var(--degraded);  opacity: .9 }
.strip i.s-partial     { background: var(--partial);   opacity: .95 }
.strip i.s-major       { background: var(--major);     opacity: 1 }
.strip i.s-maintenance { background: var(--maint);     opacity: .55 }
.strip i.s-unknown     { background: #E6E9EE;          opacity: 1 }
.strip-sm { height: 20px; gap: 1.5px; }
/* Row scale. 90 bars cannot fit here — each has a 2px minimum, so they
   overflowed the column and ran underneath the status pill. The row shows a
   shorter window instead of an illegible smear; the full 90 days lives on the
   vendor page where there is room for it. */
.strip-xs { height: 16px; gap: 1px; width: 130px; flex: none; }
.strip-xs i { min-width: 1px; border-radius: 1px; }
.strip-wrap { overflow-x: auto; }
/* Under a full-width strip: the first day, a hairline tick down from each
   month's first bar with its name beside it, and today at the end. */
.strip-scale { position: relative; height: 22px; font-size: .72rem; line-height: 1; color: var(--faint); font-variant-numeric: tabular-nums; }
.strip-scale span { position: absolute; white-space: nowrap; }
.ss-edge { top: 9px; left: 0; }
.ss-today { left: auto; right: 0; color: var(--dim); }
.ss-m { top: 0; height: 20px; padding: 9px 0 0 6px; border-left: 1px solid var(--line-strong); }

/* ── surfaces ────────────────────────────────────────────────────────────── */

.panel {
  background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(16, 20, 28, .04);
}
.panel-p { padding: 22px 24px; }
.sunken { background: var(--wash); border: 1px solid var(--line-soft); border-radius: 10px; }

/* ── the live board ──────────────────────────────────────────────────────── */

/* --cols is the board's one column track, read by its rows and by the column
   heads over them, so the two cannot drift apart. */
.board { --cols: minmax(0,1fr) 130px 150px 52px; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--panel); box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
.board-head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 18px; border-bottom: 1px solid var(--line); background: var(--wash);
  font-size: .86rem;
}
.board-rows { display: flex; flex-direction: column; }
.board-row {
  display: grid; grid-template-columns: var(--cols);
  align-items: center; gap: 18px;
  padding: 12px 18px; border-top: 1px solid var(--line-soft);
  transition: background .12s;
}
/* Grid items stretch by default, which turned every status pill into a 240px
   slab. Pills size to their text; only the timestamp hugs the right edge. */
.board-row > * { justify-self: start; min-width: 0; }
/* The name truncates inside its track; nowrap text sized to fit its content
   otherwise ran on underneath the pill beside it. */
.board-row > :first-child { max-width: 100%; }
.board-row > *:last-child { justify-self: end; }
.board-row:first-child { border-top: 0; }
.board-row:hover { background: var(--raise); }
.board-row .name { font-weight: 560; letter-spacing: -.01em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-row .when { font-size: .8rem; color: var(--faint); text-align: right; font-variant-numeric: tabular-nums; }
/* Where the name is a link the whole row is: a 1fr name track is mostly
   empty, and a pointer over the strip or the pill found nothing to click.
   The strip stays above the link so a day can still be pointed at. */
.board-row:has(> a.name) { position: relative; cursor: pointer; }
.board-row > a.name::after { content: ""; position: absolute; inset: 0; }
.board-row > .strip { position: relative; z-index: 1; }

.live-dot { width: 7px; height: 7px; border-radius: 2px; background: var(--ok); flex: none; position: relative; }
.live-dot::after {
  content: ""; position: absolute; inset: -4px; border-radius: 4px;
  border: 1px solid var(--ok); opacity: .45; animation: ping 2.4s ease-out infinite;
}
@keyframes ping { 0% { transform: scale(.6); opacity: .6 } 100% { transform: scale(1.5); opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .live-dot::after { animation: none; opacity: .3 } }

/* An open incident on the live page: who, what they said, how bad, how long.
   Fixed side columns so the lamps line up down the whole list. */
.inc-row {
  display: grid; grid-template-columns: minmax(0,1fr) 172px 88px; align-items: center; gap: 6px 18px;
  padding: 12px 18px; border-top: 1px solid var(--line-soft); transition: background .12s;
}
.inc-row:first-child { border-top: 0; }
.inc-row:hover { background: var(--raise); }
.inc-row > * { justify-self: start; min-width: 0; max-width: 100%; }
.inc-row > *:last-child { justify-self: end; }
.inc-row .nm { font-weight: 560; letter-spacing: -.01em; max-width: 100%; }
.inc-row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inc-row .t:hover { color: var(--ink); }
.inc-row .when { font-size: .8rem; color: var(--faint); text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 640px) {
  .inc-row { grid-template-columns: minmax(0,1fr) auto; padding-inline: 14px; }
  .inc-row .when { display: none; }
  .inc-row .t { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; }
}

/* ── data tables ─────────────────────────────────────────────────────────── */

.tbl-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
table { border-collapse: collapse; width: 100%; font-size: .88rem; min-width: 600px; }
th, td { text-align: left; padding: 11px 16px; border-top: 1px solid var(--line-soft); vertical-align: middle; }
thead th {
  border-top: 0; border-bottom: 1px solid var(--line); background: var(--wash);
  font-size: .78rem; font-weight: 560; color: var(--dim); white-space: nowrap;
}
tbody tr:hover td { background: var(--raise); }
td.n, th.n { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* A short table that should fit its column rather than scroll inside it. The
   600px floor is right for a wide ledger and wrong for three columns in half a
   page, where it cut the credit column off at "You ge". */
table.fit { min-width: 0; }
/* A long header ("Entry price, billed monthly") may wrap in a table meant to
   fit: unwrapped, the header alone held the table wider than the phone. */
table.fit thead th { white-space: normal; }
/* Except over a column of figures, which is narrow by design: a three-word
   head over it stacked one word to a line. */
table.fit thead th.n { white-space: nowrap; }
/* The first column takes what is left and truncates inside it. Without this,
   one unbreakable 60-character vendor name set the column's width and pushed
   the status off the screen all over again. */
table.fit td:first-child { width: 100%; max-width: 0; }
table.fit td:first-child .with-mark { max-width: 100%; }
/* Where the second column carries the text — vendor, then what happened. */
table.fit-2 td:first-child { width: auto; max-width: none; }
table.fit-2 td:first-child > a { display: block; max-width: min(30vw, 240px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.fit-2 td:nth-child(2) { width: 100%; max-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  /* On a phone the floor scrolled the answer off the screen: the directory
     and every vendor page showed a column of names and no status. A table
     keeps its name and its status, and `.hide-sm` marks what can go. */
  .hide-sm { display: none; }
  th, td { padding: 10px 12px; }
}
td .lnk { font-weight: 560; letter-spacing: -.01em; }
td .lnk:hover, .board-row:has(> a.name):hover .nm-text, a.spot-who:hover .d3 { text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
/* Our own row, marked in ink rather than the maintenance blue it used to be
   tinted with: colour on this site means a vendor's status. */
tr.mine td { background: var(--wash); }
tr.mine:hover td { background: var(--raise); }
tr.mine td:first-child { box-shadow: inset 2px 0 0 var(--ink); }

/* ── stats ───────────────────────────────────────────────────────────────── */

/* Each cell draws its own right and bottom rule and the frame clips the ones
   on the outer edge. The old 1px gap showed the grid's grey background
   through, so a row that did not fill — four stats on a phone — ended in a
   grey slab where the missing cell would have been. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); background: var(--panel); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.stat { padding: 16px 18px; display: flex; flex-direction: column; gap: 4px; min-width: 0; box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line); }
.stat .v { font-size: clamp(1.25rem, 2.1vw, 1.55rem); font-weight: 600; letter-spacing: -.03em; line-height: 1.1; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.stat .k { font-size: .8rem; color: var(--dim); }
/* The unit after a figure: smaller, lighter, tucked against it. */
.u { font-size: .62em; font-weight: 500; letter-spacing: 0; color: var(--faint); margin-left: .06em; }

/* ── feature rows (not a card grid) ──────────────────────────────────────── */

.feat { display: grid; grid-template-columns: minmax(0,.42fr) minmax(0,.58fr); gap: clamp(24px,5vw,64px); align-items: start; padding-block: clamp(32px,4vw,48px); border-top: 1px solid var(--line-soft); }
.feat:first-of-type { border-top: 0; padding-top: 0; }
.feat-t { display: flex; flex-direction: column; gap: 10px; }

/* ── a real alert, not a description of one ──────────────────────────────── */

.chat { background: var(--paper); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
.chat-bar { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--line-soft); background: var(--wash); color: var(--dim); font-size: .8rem; }
.chat-bar .dots { display: flex; gap: 5px; }
.chat-bar .dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
.msg { display: grid; grid-template-columns: 34px 1fr; gap: 11px; padding: 16px 14px; }
.msg .av { width: 34px; height: 34px; border-radius: 8px; background: var(--ink); display: grid; place-items: center; }
/* The alert's avatar: the mark with its vendor lit red, as in the alert. */
.msg .av .bars { width: 19px; height: 14px; background: var(--mark-alert) no-repeat center / contain; }
.msg-body { min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.msg-who { display: flex; align-items: baseline; gap: 8px; }
.msg-who b { font-size: .88rem; font-weight: 640; letter-spacing: -.01em; }
.msg-who span { font-size: .74rem; color: var(--faint); }
.msg-card { border-left: 3px solid var(--major); background: var(--wash); border-radius: 0 8px 8px 0; padding: 12px 14px; display: flex; flex-direction: column; gap: 9px; }
.msg-card .t { font-weight: 600; letter-spacing: -.01em; font-size: .9rem; }
.msg-facts { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 7px 18px; }
.msg-facts div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.msg-facts .fk { font-size: .72rem; color: var(--faint); }
.msg-facts .fv { font-size: .8rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── pricing ─────────────────────────────────────────────────────────────── */

.cols2 { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: clamp(20px,4vw,52px); align-items: start; }

.cols3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px,1fr)); gap: 14px; }

/* Four self-serve tiers across; the MSP plan sits apart because it is a
   different buyer, not a bigger version of the same one. Previously all five
   shared one grid and the fifth orphaned onto its own row. */
.plans { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 12px; }
.plan { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 22px; display: flex; flex-direction: column; gap: 14px; box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
.plan.pick { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink), 0 8px 24px rgba(16, 20, 28, .06); }
/* Not scoped to .plan: the MSP row is a wide panel, and its price rendered as
   body text beside four plans set in display figures. */
.amt { font-size: 2.1rem; font-weight: 650; letter-spacing: -.04em; line-height: 1; }
.amt span { font-size: .84rem; font-weight: 500; color: var(--faint); letter-spacing: 0; }
.plan ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; font-size: .87rem; color: var(--dim); }
.plan li { display: grid; grid-template-columns: 14px 1fr; gap: 8px; }
.plan li::before { content: ""; width: 5px; height: 5px; margin-top: .6em; border-radius: 1px; background: var(--line-strong); }
.plan .btn { margin-top: auto; }
.plan-wide { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.1fr) auto; gap: clamp(18px,3vw,40px); align-items: center; }
@media (max-width: 860px) { .plan-wide { grid-template-columns: minmax(0,1fr); align-items: start; gap: 18px; } }

/* ── forms ───────────────────────────────────────────────────────────────── */

label { font-size: .82rem; font-weight: 560; color: var(--ink); }

/*
| Matched by exclusion rather than by listing the types we remembered to write.
|
| `input[type=text]` matches the ATTRIBUTE, not the resolved type, so a bare
| `<input>` — which is most of them in the dashboard — matched nothing here and
| fell through to the browser's own control. Naming what is not a text box is a
| list that cannot go stale.
|
| The list sits inside :where() so it adds no specificity. Written as nine
| :not() clauses it weighed (0,9,1), which silently beat every rule meant to
| refine a field — :focus, :hover, [readonly], :disabled, the input-group's
| welded corners, [aria-invalid] — so a focused text box never showed its ring.
*/
input:not(:where([type=checkbox], [type=radio], [type=file], [type=range], [type=color], [type=submit], [type=button], [type=reset], [type=image])),
select,
textarea {
  font: inherit; font-size: .92rem; width: 100%;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-strong); border-radius: 8px; padding: 9px 12px;
  box-shadow: 0 1px 2px rgba(16, 20, 28, .04);
  transition: border-color .14s, box-shadow .14s, background .14s;
}

/* Firefox dims placeholders a second time on top of the colour. */
::placeholder { color: var(--faint); opacity: 1; }

input:not(:where([type=checkbox], [type=radio], [type=file], [type=range], [type=color], [type=submit], [type=button], [type=reset], [type=image])),
select { height: 38px; padding-block: 0; line-height: normal; }
/* The paste-a-list box and any other multi-line field: grows downward only. */
textarea { min-height: 96px; line-height: 1.5; resize: vertical; }

input:hover:not(:disabled):not(:focus), select:hover:not(:disabled):not(:focus), textarea:hover:not(:disabled):not(:focus) { border-color: #B7BEC9; }

/*
| The ring hugs the control instead of the offset outline the rest of the page
| uses: on a field the offset ring reads as a second border and fights the
| first. It fires on :focus, not :focus-visible, because a clicked field should
| show it too.
*/
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--maint);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--maint) 16%, transparent);
}

/* An error, which is the one place a field may carry status colour. After
   :focus and out-weighing :hover, so the red holds while the field is pointed
   at or being corrected. */
:is(input, select, textarea)[aria-invalid=true],
:is(input, select, textarea)[aria-invalid=true]:hover:not(:disabled):not(:focus) { border-color: var(--major); }
:is(input, select, textarea)[aria-invalid=true]:focus {
  border-color: var(--major);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--major) 16%, transparent);
}

input[readonly] { color: var(--dim); background: var(--wash); }
input[readonly]:focus { border-color: var(--line-strong); box-shadow: none; }
input:disabled, select:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; background: var(--wash); }
input[type=checkbox], input[type=radio] { accent-color: var(--ink); width: 15px; height: 15px; }

/* The native arrow cannot be told to match the rest of the control, so it is
   replaced rather than lived with. */
select {
  appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23565D69' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px;
}

/* A field welded to its buttons: one control, not three that happen to touch. */
.input-group { display: flex; align-items: stretch; min-width: 0; }
.input-group > :first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group > :last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.input-group > :not(:first-child):not(:last-child) { border-radius: 0; }
.input-group > * + * { margin-left: -1px; }
.input-group > input { flex: 1; min-width: 0; }
.input-group > .btn { flex: none; position: relative; }
.input-group > .btn:hover, .input-group > :focus { z-index: 1; }

/* One choice out of three, so it is one control rather than three buttons that
   have to be read together to work out which is on. */
.seg { display: inline-flex; padding: 3px; gap: 2px; background: var(--wash); border: 1px solid var(--line); border-radius: 9px; }
.seg button {
  font: inherit; font-size: .82rem; font-weight: 540;
  padding: 5px 13px; border-radius: 6px; border: 0; cursor: pointer;
  background: none; color: var(--dim); transition: background .14s, color .14s, box-shadow .14s;
}
.seg button:hover { color: var(--ink); }
.seg button[aria-pressed=true] { background: var(--paper); color: var(--ink); box-shadow: 0 1px 2px rgba(16, 20, 28, .1), 0 0 0 1px var(--line); }
.seg .c { font-size: .76rem; color: var(--faint); margin-left: 3px; }

/* Chrome paints autofilled fields with its own tint, which reads as a
   different control. The inset shadow is the only way to override it. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 1000px var(--paper) inset;
  caret-color: var(--ink);
  transition: background-color 9999s ease-out;
}

/* ── skip link ───────────────────────────────────────────────────────────
   Off-screen until a keyboard lands on it, then the first thing on the page. */
.skip-link {
  position: absolute; left: 12px; top: -100px; z-index: 100;
  padding: 8px 14px; border-radius: 7px; background: var(--ink); color: #fff;
  font-size: .86rem; font-weight: 560; text-decoration: none;
}
.skip-link:focus { top: 12px; }
main:focus { outline: none; }

/* ── chips ───────────────────────────────────────────────────────────────── */

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* Wraps. With nowrap a long component name ("Amazon Elastic Compute Cloud —
   us-east-1 (N. Virginia)") set the chip's width, and a component page at
   375px scrolled sideways to 489. A chip that fits still sits on one line;
   one that cannot breaks inside itself instead of widening the page. The
   dashboard's filter row, which scrolls on phones, uses button.chip with
   flex: none, so it keeps its one-line chips. */
.chip {
  font-size: .82rem; font-weight: 520; padding: 5px 11px; border-radius: 7px;
  border: 1px solid var(--line); color: var(--dim); background: var(--paper);
  white-space: normal; overflow-wrap: anywhere; max-width: 100%;
}
:is(a, button).chip:hover { border-color: var(--line-strong); color: var(--ink); }
/* `aria-current` is for the links in the public catalogue, which really do
   navigate; `aria-pressed` is for the dashboard's filter buttons, which do not
   go anywhere and were claiming to be the current page. */
.chip[aria-current=page],
.chip[aria-pressed=true] { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip .c { font-size: .76rem; color: var(--faint); }
/* Opacity .6 over --dim came to #9A9EA5, 2.7:1 on paper: the category counts
   on the home page failed contrast. */

/* A long list kept closed: the vendor page's remaining component pages. */
.all-pages summary { cursor: pointer; color: var(--dim); width: fit-content; }
.all-pages summary:hover { color: var(--ink); }
.all-pages[open] summary { margin-bottom: 10px; }

/* ── pagination ──────────────────────────────────────────────────────────── */
/* Laravel's default links are Tailwind markup; with no Tailwind on the page
   they rendered as two 80px chevrons and a run of bare numbers. */
.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 20px; }
.pager-pages { display: flex; flex-wrap: wrap; gap: 4px; }
.pager-pages a, .pager-pages span {
  min-width: 34px; height: 34px; padding: 0 10px; border-radius: 8px;
  display: inline-grid; place-items: center; font-size: .86rem; font-weight: 520;
  color: var(--dim); font-variant-numeric: tabular-nums;
}
.pager-pages a { border: 1px solid var(--line); background: var(--paper); }
.pager-pages a:hover { border-color: var(--line-strong); color: var(--ink); }
.pager-pages [aria-current=page] { background: var(--ink); color: #fff; border: 1px solid var(--ink); }
.pager-pages .gap { color: var(--faint); min-width: 20px; padding: 0; }
.pager-pages .off { color: var(--line-strong); border: 1px solid var(--line-soft); }
@media (max-width: 640px) { .pager-pages .num:not([aria-current]) { display: none; } }

/* ── tabs ────────────────────────────────────────────────────────────────── */
/* Sections of one page, not filters: an underline on a hairline, the way a
   document's own navigation reads, rather than a row of solid chips. */
.tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.tabs a { padding: 2px 0 11px; margin-bottom: -1px; font-size: .88rem; font-weight: 520; color: var(--dim); white-space: nowrap; border-bottom: 2px solid transparent; transition: color .15s, border-color .15s; }
.tabs a:hover { color: var(--ink); }
.tabs a[aria-current=page] { color: var(--ink); border-bottom-color: var(--ink); }

/* ── vendor marks ────────────────────────────────────────────────────────── */
/*
| Softened until the row is hovered. The palette reserves chroma for failure,
| and thousands of brand marks at full strength would out-shout the one red
| square that has to carry meaning.
*/
.vmark {
  width: 18px; height: 18px; flex: none; border-radius: 5px;
  object-fit: contain; background: var(--paper);
  filter: saturate(.35); transition: filter .16s;
}
tr:hover .vmark, .mon:hover .vmark, .pick:hover .vmark, .board-row:hover .vmark, .vmark-lg { filter: none; }

/* The initial on the vendor's own colour, tinted towards paper so any brand
   colour — pale yellow or near-black — gives a readable letter. */
.vmark-none {
  display: inline-grid; place-items: center;
  font-size: .64rem; font-weight: 640;
  color: var(--dim); background: var(--wash); box-shadow: inset 0 0 0 1px var(--line);
  text-transform: uppercase; filter: none;
}
.vmark-none[style*="--brand"] {
  background: color-mix(in srgb, var(--brand) 16%, var(--paper));
  color: color-mix(in srgb, var(--brand) 72%, var(--ink));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 22%, transparent);
}

/* The mark and the name are one thing; truncation still belongs to the name. */
.with-mark { display: inline-flex; align-items: center; gap: 9px; min-width: 0; vertical-align: middle; }
.with-mark > .nm-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.with-mark:has(> .vmark-lg) { gap: 14px; }

/* On a vendor's own page the mark is the subject, not a row bullet. */
.vmark-lg { width: 44px; height: 44px; border-radius: 11px; box-shadow: 0 0 0 1px var(--line), 0 1px 2px rgba(16, 20, 28, .06); }
.vmark-lg.vmark-none { font-size: 1.2rem; }

/* The vendor's own colour, used once, on the page that is about them. Set as
   --brand inline; falls back to the neutral line so a vendor with no published
   mark still gets the rule rather than a gap. */
/* A signature under the name, as wide as the name: stretched across the
   masthead, it ran on into the dome's beam. */
.brand-rule { border-bottom: 2px solid var(--brand, var(--line)); padding-bottom: 12px; align-self: flex-start; }

/* ── the night band ──────────────────────────────────────────────────────── */
/*
| The hour the product exists for. A slow sweep over a quiet sky and the one
| thing it found — the only dark surface on the site, and the only motion that
| is not a response to something you did. The light is white, not a brand hue:
| the single colour in the scene is the status of the vendor it caught.
*/

/* The sky is flat. The only light in it comes from somewhere: the beam, and
   the source it sweeps from on the horizon. A general glow washed across the
   band read as a template's aurora rather than a night — and the hero kept a
   smaller one at the horizon until this was made flat too. */
.night { position: relative; overflow: hidden; color: #fff; background: var(--night); }
.night-hero {
  padding-block: clamp(56px, 7vw, 88px) 0;
  background: var(--night);
}
/* The headline on the left at full width of its column; what it means and
   what to do beside it, sitting on the headline's last line. */
.night-copy {
  position: relative; z-index: 2; display: grid; align-items: start; text-align: left;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 28px clamp(40px, 6vw, 88px);
}
/* The lede's first line sits on the headline's cap height, not its top edge. */
.night-side { display: flex; flex-direction: column; gap: 20px; padding-top: clamp(.4rem, 1.1vw, 1rem); }
@media (max-width: 900px) {
  .night-copy { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .night-side { padding-bottom: 0; }
}


/* One colour. The second sentence used to be set a step greyer, the
   accent-the-second-line move every generated headline makes; the sentence
   does the work on its own. */
.night-h1 { font-size: clamp(2.7rem, 6.2vw, 5.2rem); font-weight: 620; letter-spacing: -.045em; line-height: .98; text-wrap: balance; max-width: 11ch; }
/* A term's expansion beside it on a glossary heading: secondary, set back. */
.h-dim { color: #8C96AB; }
.night-lede { font-size: clamp(1.02rem, 1.25vw, 1.14rem); line-height: 1.6; color: var(--night-dim); max-width: 46ch; }
.night-cta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; }
.btn-lg { min-height: 44px; padding: 11px 22px; font-size: .95rem; border-radius: 9px; }
/* The second way in, as a link: it goes somewhere to look, not something to
   commit to. */
.night-link { font-size: .92rem; font-weight: 520; color: var(--night-dim); text-decoration: underline; text-decoration-color: rgba(255, 255, 255, .25); text-underline-offset: 4px; transition: color .15s, text-decoration-color .15s; }
.night-link:hover { color: #fff; text-decoration-color: rgba(255, 255, 255, .7); }

/* ── the scope's side panels ─────────────────────────────────────────────── */
/* Either side of the board where it crosses the horizon: labels on hairlines,
   figures in white, the way an instrument annotates its display. Only where
   there is room beside the board; below that the board says it alone. */
.stage-wrap { position: relative; }
.wing {
  position: absolute; top: 20px; width: calc((min(100vw, var(--wrap)) - 820px) / 2 - var(--gutter) - 22px);
  margin: 0; color: var(--night-dim); font-size: .74rem; font-variant-numeric: tabular-nums;
}
.wing-l { left: var(--gutter); }
.wing-r { right: var(--gutter); }
.wing-rows { margin: 8px 0 0; display: flex; flex-direction: column; gap: 6px; }
.wing-rows > div { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.wing dd { margin: 0; font-weight: 600; color: #fff; white-space: nowrap; }
.wing dd .u { color: var(--night-faint); }
.wing-h { display: block; padding-top: 7px; border-top: 1px solid var(--horizon-line); color: var(--night-faint); }
.wing-log { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.wing-log .wl-row { display: grid; grid-template-columns: auto minmax(0, 1fr) 7px; align-items: center; gap: 9px; color: var(--night-dim); }
.wing-log a.wl-row:hover { color: #fff; }
.wing-log time { color: var(--night-faint); }
.wl-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1180px) { .wing { display: none; } }

/* No highlighter strokes on paper: phrases marked up in the lamp's colour
   read as a template's emphasis, and the sentences stand without them. */
mark { color: inherit; background: none; }
.night-note { font-size: .8rem; color: var(--night-faint); }
/* The primary action in the dark is lit by the lamp: the one warm thing in
   the band besides the beam. */
.night .btn-solid, .nav-night .btn-solid {
  background: var(--lamp); color: var(--lamp-ink);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35), inset 0 -1px 0 rgba(90, 50, 0, .18), 0 1px 2px rgba(0, 0, 0, .3);
}
.night .btn-solid:hover, .nav-night .btn-solid:hover { background: var(--lamp-hover); color: var(--lamp-ink); }
.night .btn-line { background: rgba(255, 255, 255, .04); border-color: rgba(255, 255, 255, .16); color: #fff; box-shadow: none; }
.night .btn-line:hover { background: rgba(255, 255, 255, .09); border-color: rgba(255, 255, 255, .28); color: #fff; }

/* A half-dome on the horizon, drawn as an instrument: range rings, spokes and
   a bezel of ticks. The grid is SVG and never moves; the beam and everything
   it lights is a canvas over it (public/js/radar.js). */
.radar { position: relative; width: min(860px, calc(100% - 32px)); aspect-ratio: 2 / 1; margin: clamp(32px, 4.4vw, 52px) auto 0; overflow: hidden; }
.radar-grid, .radar canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.radar-grid {
  fill: none; stroke: rgba(186, 202, 234, .1); stroke-width: 1;
  /* Thinner toward the rim, so the instrument dissolves into the sky rather
     than stopping at a line. The ellipse is the dome: half the width, all the
     height, centred on the horizon. */
  -webkit-mask-image: radial-gradient(50% 100% at 50% 100%, #000 45%, rgba(0, 0, 0, .55) 100%);
          mask-image: radial-gradient(50% 100% at 50% 100%, #000 45%, rgba(0, 0, 0, .55) 100%);
}
.radar-grid * { vector-effect: non-scaling-stroke; }
.radar-grid .rim { stroke: rgba(186, 202, 234, .22); }
.radar-grid .spoke { stroke: rgba(186, 202, 234, .07); }
.radar-grid .tick { stroke: rgba(186, 202, 234, .17); }
.radar-grid .tick.major { stroke: rgba(186, 202, 234, .34); }
.radar-grid .q { fill: rgba(206, 218, 242, .3); stroke: none; }
.radar-grid .rim-in { stroke: rgba(186, 202, 234, .12); }
/* A vendor in trouble keeps its status colour between sweeps (the still
   frame and a page without the script show it too); the rest are cold. */
.radar-grid .q-degraded { fill: color-mix(in srgb, var(--degraded) 80%, #fff); }
.radar-grid .q-partial { fill: color-mix(in srgb, var(--partial) 80%, #fff); }
.radar-grid .q-major { fill: color-mix(in srgb, var(--major) 80%, #fff); }
.radar-grid .q-maintenance { fill: color-mix(in srgb, var(--maint) 70%, #fff); }
/* What each sector holds, along the inside of the rim: the radar's legend
   is its own bezel. */
.radar-grid .sector {
  fill: rgba(206, 218, 242, .5); stroke: none; text-anchor: middle;
  font-family: var(--sans); font-size: 14.5px; font-weight: 500; letter-spacing: .01em;
}
@media (max-width: 640px) { .radar-grid .sector { font-size: 22px; } }
/* Bearings on the bezel, the way a real scope is read: three digits, north
   at 000, set small and tabular just inside the major ticks. */
.radar-grid .brg {
  fill: rgba(206, 218, 242, .42); stroke: none; text-anchor: middle; dominant-baseline: middle;
  font-family: var(--sans); font-size: 15px; font-weight: 500; letter-spacing: .04em; font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) { .radar-grid .brg { display: none; } }
/* The horizon: a line, brighter where the source sits on it. */
.radar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(214, 226, 250, .24) 18%, rgba(255, 214, 150, .7) 50%, rgba(214, 226, 250, .24) 82%, transparent);
}

/* The still frame, for a page without the script: the beam paused just past
   the vendor it found, its trail fading behind it. A square twice the dome's
   height, centred on the horizon, so its middle is the dome's centre. */
.radar-sweep {
  position: absolute; left: 0; top: 0; width: 100%; height: 200%; border-radius: 50%;
  background: conic-gradient(from 0deg,
    transparent 0deg 280deg,
    rgba(255, 200, 128, .03) 300deg,
    rgba(255, 200, 128, .08) 344deg,
    rgba(255, 206, 140, .2) 358deg,
    rgba(255, 228, 186, .6) 359.7deg,
    transparent 360deg);
  transform: rotate(49deg);
}
.radar.is-live .radar-still { display: none; }

/* The one it found. It glows between sweeps, because an outage does not stop
   being true while the beam is elsewhere, and pings each time the beam
   crosses it. */
.blip-hot {
  position: absolute; left: var(--x); top: var(--y); z-index: 2;
  width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
  --lit: color-mix(in srgb, var(--c) 78%, #fff);
  /* A flat ring, not a bloom: the blurred halo was a glow by another name. */
  --rest: 0 0 0 4px color-mix(in srgb, var(--c) 24%, transparent);
  background: var(--lit); box-shadow: var(--rest);
}
.blip-hot.is-hit { animation: blip-hit 1.6s ease-out; }
@keyframes blip-hit {
  0%   { background: #fff; box-shadow: 0 0 0 7px color-mix(in srgb, var(--c) 40%, transparent); }
  100% { box-shadow: var(--rest); }
}
/* The ping is drawn at its final size and scaled up from a point, so its line
   stays a hairline instead of thickening as it grows. */
.blip-ping {
  position: absolute; left: 50%; top: 50%; width: 88px; height: 88px; margin: -44px 0 0 -44px;
  border-radius: 50%; border: 1px solid var(--lit); opacity: 0; transform: scale(.1);
}
.blip-hot.is-hit .blip-ping { animation: blip-ping 1.9s cubic-bezier(.16, .7, .3, 1); }
@keyframes blip-ping {
  0%   { opacity: .9; transform: scale(.1); }
  100% { opacity: 0; transform: scale(1); }
}
.blip-hot.tag-left .blip-tag { left: auto; right: 18px; }
.blip-tag {
  position: absolute; left: 18px; top: 50%; transform: translateY(-50%); white-space: nowrap;
  font-size: .78rem; font-weight: 520; color: #fff; padding: 3px 9px; border-radius: 6px;
  background: rgba(10, 15, 30, .9); border: 1px solid color-mix(in srgb, var(--c) 50%, transparent);
  backdrop-filter: blur(4px);
}

/* Where the beam comes from: the lamp itself, a flat half-disc sitting on
   the horizon directly above the board. It was a radial bloom half the
   dome wide — a glow, which the lamp is never allowed to be. */
.radar-source {
  position: absolute; left: 50%; bottom: 0; width: 14px; height: 14px; transform: translate(-50%, 50%);
  border-radius: 50%; background: var(--lamp);
  pointer-events: none;
}

/* The board sits on the horizon: night above its top edge, daylight from its
   middle down — a hard edge, two flat colours. The source's light used to be
   carried on past the section edge as a glow. */
.stage {
  padding-block: 0 clamp(20px, 3vw, 36px);
  background: linear-gradient(180deg, var(--night) 0 150px, var(--paper) 150px);
}
/* The home board is the one with the long shadow and no border: it has the
   whole page below it. An inner page's card keeps its hairline, because the
   half on paper still needs an edge. */
.board-lifted { max-width: 820px; margin: 0 auto; position: relative; border-color: transparent; box-shadow: var(--rise-edge), var(--rise-cast-deep); }
.lift, .lift-cards > * { position: relative; box-shadow: var(--rise-edge), var(--rise-cast); }
/* The recommended plan keeps its ring where it crosses into the dark. */
.lift-cards > .plan.pick { box-shadow: 0 0 0 1px var(--ink), 0 0 0 4px rgba(255, 255, 255, .12), var(--rise-cast); }
.stage-body { padding: 22px 24px; display: flex; flex-direction: column; gap: 18px; }
/* The card's figures, under a hairline: three facts about the outage on
   screen, label over figure, without the boxed stat-tile treatment. */
.card-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--line); }
.card-facts > div { padding: 14px 24px 16px; display: flex; flex-direction: column-reverse; gap: 2px; min-width: 0; }
.card-facts > div + div { border-left: 1px solid var(--line-soft); }
.card-facts dt { font-size: .76rem; color: var(--dim); }
.card-facts dd { margin: 0; font-size: 1.12rem; font-weight: 600; letter-spacing: -.02em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-facts .of { font-size: .78rem; font-weight: 500; letter-spacing: 0; color: var(--faint); }
@media (max-width: 560px) {
  .card-facts { grid-template-columns: minmax(0, 1fr); }
  .card-facts > div { flex-direction: row-reverse; justify-content: space-between; align-items: baseline; padding: 10px 18px; }
  .card-facts > div + div { border-left: 0; border-top: 1px solid var(--line-soft); }
}
.facts-line { max-width: 62ch; }

/* ── the masthead ────────────────────────────────────────────────────────── */
/*
| Every other page opens in the same night as the home page, smaller and
| still: the heading on the left, the dome standing on the horizon to the
| right, its beam held where the home page's would be mid-sweep. The only
| animation on the site stays the one on the home page.
|
| When the section after it has a `.lift` card, the night carries on past the
| horizon by --tail and that section rises into it, the way the live board
| does on the home page. The page marks the card; nothing else to declare.
*/
.masthead {
  --horizon: 0px;
  padding-block: clamp(40px, 5vw, 64px) calc(clamp(64px, 6.6vw, 84px) + var(--horizon));
}
.masthead:has(+ section :is(.lift, .lift-cards)) { --horizon: var(--tail); }
.masthead-copy { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 18px; }
/* The copy keeps to the left and leaves the dome its sky. */
.masthead-copy > :not(.chips) { max-width: min(700px, 100%); }
.masthead-copy .lede { max-width: 56ch; }
/* The horizon, faint across the full width; the dome lights its own stretch. */
.masthead::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: var(--horizon); height: 1px; z-index: 1;
  background: var(--horizon-line);
}
/* The hour scale stands on the horizon, the width of the page's content. */
.masthead-ruler { position: absolute; left: 0; right: 0; bottom: var(--horizon); z-index: 2; }

.dome {
  position: absolute; z-index: 1; pointer-events: none;
  right: max(-40px, calc(50% - var(--wrap) / 2 - 20px));
  bottom: var(--horizon, 0px); height: min(320px, calc(100% - var(--horizon, 0px) - 36px));
  aspect-ratio: 2 / 1; overflow: hidden;
}
/* Held, the beam's leading edge is a hard line the moving one never shows. The
   still dome keeps the trail and lets the edge go soft, so it reads as where
   the light has just been rather than a blade across the heading. */
.dome .radar-sweep {
  background: conic-gradient(from 0deg,
    transparent 0deg 290deg,
    rgba(255, 200, 128, .025) 310deg,
    rgba(255, 200, 128, .065) 346deg,
    rgba(255, 206, 140, .13) 358.6deg,
    rgba(255, 228, 186, .26) 359.8deg,
    transparent 360deg);
}
.dome .radar-source { width: 10px; height: 10px; }
@media (max-width: 940px) {
  .dome { right: -18%; opacity: .5; }
  /* Narrow, the dome sits behind the copy, and a lit blip landed on a button
     where it read as a smudge rather than a vendor. The pill says it anyway. */
  .dome .blip-hot { display: none; }
}


/* ── the hour scale ──────────────────────────────────────────────────────── */
/*
| Twenty-four hours of UTC along the horizon, with this minute marked. The
| product exists for the hours on the left of it, so the scale is drawn the way
| an instrument's is: fifteen-minute ticks, hours taller, every third hour
| numbered. No glow; the only bright thing on it is now.
*/
.ruler { position: relative; height: 34px; }
.ruler-ticks { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: 10px; overflow: visible; }
.ruler-ticks path { fill: none; stroke-width: 1; vector-effect: non-scaling-stroke; shape-rendering: crispEdges; }
.ruler-ticks .t15 { stroke: rgba(214, 226, 250, .12); }
.ruler-ticks .t60 { stroke: rgba(214, 226, 250, .24); }
.ruler-ticks .t180 { stroke: rgba(214, 226, 250, .42); }
.ruler-h, .ruler-now {
  position: absolute; bottom: 14px; transform: translateX(-50%);
  font-size: .68rem; line-height: 1; letter-spacing: .02em; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ruler-h { color: rgba(214, 222, 238, .42); }
.ruler-h.is-first { transform: none; }
/* An hour beside the mark would run into it and be read as the time: the
   nearest go at every width, the next ones on a phone. nav.js keeps these
   in step as the mark moves. */
.ruler-h.is-near { display: none; }
@media (max-width: 640px) { .ruler-h.is-close { display: none; } }
.ruler-now { color: var(--lamp); font-weight: 560; }
.ruler-now b { font-weight: 600; }
.ruler-now span { color: var(--night-faint); font-weight: 500; }
/* The label sits 14px above the horizon; its line runs from 3px under it
   down to the horizon, and the point of the hour rests on the line. */
.ruler-now::after {
  content: ""; position: absolute; left: 50%; top: calc(100% + 3px); width: 1px; height: 11px; background: var(--lamp);
}
.ruler-now::before {
  content: ""; position: absolute; left: calc(50% - 2px); top: calc(100% + 12px); width: 5px; height: 5px;
  border-radius: 50%; background: var(--lamp);
}
/* Hung from a horizon above it instead of standing on one: the footer's,
   so the page opens and closes on the same line. */
.ruler-hung .ruler-ticks { top: 0; bottom: auto; transform: scaleY(-1); }
.ruler-hung :is(.ruler-h, .ruler-now) { top: 14px; bottom: auto; }
.ruler-hung .ruler-now::after { top: auto; bottom: calc(100% + 3px); }
.ruler-hung .ruler-now::before { top: auto; bottom: calc(100% + 12px); }

/* ── in the night ────────────────────────────────────────────────────────── */
/* The same components, relit: text steps toward the sky, lines become light
   on dark, and a status keeps its hue — brighter, because on a night sky the
   lamp is the only thing that should glow. */
.night .lede, .night .body, .night .dim { color: var(--night-dim); }
.night .faint { color: var(--night-faint); }
.night .body a, .night .lede a { color: #fff; text-decoration-color: rgba(255, 255, 255, .3); }
.night .body a:hover, .night .lede a:hover { text-decoration-color: #fff; }
/* The page-wide a:hover is ink, which on the sky is invisible. */
.night a:hover { color: #fff; }
.night .crumbs { color: var(--night-faint); }
.night .crumbs a:hover { color: #fff; }
.night .crumbs a + a::before { color: rgba(255, 255, 255, .22); }

.night .pill {
  color: color-mix(in srgb, var(--c) 42%, #fff);
  background: color-mix(in srgb, var(--c) 20%, var(--night));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 32%, transparent);
}
.night .pill::before { background: color-mix(in srgb, var(--c) 78%, #fff); }
.night .pill-quiet { background: transparent; box-shadow: none; color: var(--night-dim); }
.night .pill-unknown { color: var(--night-dim); background: rgba(255, 255, 255, .06); box-shadow: inset 0 0 0 1px var(--night-line); }

.night .chip { background: color-mix(in srgb, #fff 4%, var(--night)); border-color: rgba(255, 255, 255, .12); color: var(--night-dim); }
.night .chip:hover { border-color: rgba(255, 255, 255, .26); color: #fff; }
.night .chip[aria-current=page], .night .chip[aria-pressed=true] { background: #fff; border-color: #fff; color: var(--ink); }

/* A dark brand colour would vanish on the sky, so the rule is lifted toward
   white; a light one barely changes. */
.night .brand-rule { border-bottom-color: color-mix(in srgb, var(--brand, #5B6475) 78%, #fff); }
.night .vmark-lg { box-shadow: 0 0 0 1px rgba(255, 255, 255, .16), 0 8px 24px rgba(0, 0, 0, .35); }
.night .note { border-left-color: rgba(255, 255, 255, .18); }
.night .note-lead { border-left-color: #fff; }

/* The section after the masthead starts on the horizon when it lifts, and a
   little closer than a section's full padding when it does not. */
.masthead + section:has(:is(.lift, .lift-cards)) { margin-top: calc(-1 * var(--tail)); padding-top: 0; position: relative; z-index: 3; }
.masthead + section:not(:has(:is(.lift, .lift-cards))):not(.rule) { padding-top: clamp(30px, 3.6vw, 48px); }

/* ── capability grid ─────────────────────────────────────────────────────── */
/* Lines between the cells and none around them: the 1px gap shows the grid's
   background through, so the dividers are exactly as long as the cells. */
.caps { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 1px; background: var(--line); position: relative; }
.cap { background: var(--paper); padding: 28px 26px 30px; display: flex; flex-direction: column; gap: 8px; }
.cap .d4 { font-size: .98rem; }

/* ── capability figures ──────────────────────────────────────────────────── */
/* Each capability shown by a small piece of the product doing it: the app's
   own vocabulary — lamps, strips, tabular figures, hairlines — at figure
   size, all on one baseline so the row of eight reads as one drawing. */
.cap-fig {
  height: 76px; margin-bottom: 16px; display: flex; flex-direction: column; justify-content: flex-end;
  font-size: .74rem; line-height: 1.35; color: var(--dim); font-variant-numeric: tabular-nums;
}
/* Only the one figure that is literally code is set in a code face; request
   lines and labels are data, set in the text face with tabular figures. */
.fig-code { font-family: var(--code); font-size: .68rem; letter-spacing: 0; }
.cap-fig code { font-family: var(--sans); font-size: .74rem; font-weight: 500; }
/* The importer's review: what was pasted, faint; what it matched, in ink. */
.ledger.match .in { color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lampq { width: 7px; height: 7px; border-radius: 2px; background: var(--c); flex: none; }

/* Detection against admission, on a ten-minute axis. */
.lead { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 9px 10px; }
.lead-t { color: var(--ink); font-weight: 560; }
.lead-bar { position: relative; height: 12px; }
.lead-bar::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--line); }
.lead-bar i { position: absolute; top: 50%; left: var(--at); right: 0; height: 4px; transform: translateY(-50%); border-radius: 2px 0 0 2px; background: var(--ink); }
.lead-bar.is-late i { background: var(--line-strong); }
.lead-bar .gap { position: absolute; top: 50%; left: var(--from); width: calc(var(--at) - var(--from)); border-top: 1px dashed var(--faint); }
.lead-bar .gap b { position: absolute; left: 50%; top: 5px; transform: translateX(-50%); font-size: .66rem; font-weight: 560; color: var(--ink); white-space: nowrap; }

.fig-strip { display: flex; flex-direction: column; gap: 8px; }
.fig-cap { font-size: .7rem; color: var(--faint); }

.fig-rows { display: flex; flex-direction: column; gap: 6px; }
.fr { display: grid; grid-template-columns: 7px minmax(0, 1fr) auto 50px; align-items: center; gap: 8px; }
.fr code { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr b { font-weight: 560; color: var(--ink); }
.fr-n { text-align: right; }
.cap-fig .u { font-size: .88em; margin-left: 1px; }
/* A status page in miniature: the one figure that is a page, so it is framed. */
.fig-page { border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; gap: 5px; box-shadow: 0 1px 2px rgba(16, 20, 28, .04); }
.fig-page .fr { grid-template-columns: minmax(0, 1fr) auto; }
.fig-page .fr-head b { font-size: .7rem; }
.fig-page .fr-name { color: var(--ink); }
.fig-page .pill { font-size: .66rem; padding-block: 0; }

/* A day from midnight, quiet hours hatched the way a drawing marks what is
   excluded, and the one change inside them held for the morning. */
.day { display: flex; flex-direction: column; gap: 5px; }
.day-bar { position: relative; height: 14px; border-radius: 4px; background: var(--paper); box-shadow: inset 0 0 0 1px var(--line); overflow: hidden; }
.day-quiet { position: absolute; top: 0; bottom: 0; background: repeating-linear-gradient(135deg, rgba(22, 24, 29, .1) 0 1px, transparent 1px 5px); }
.day-held { position: absolute; top: 3px; bottom: 3px; width: 3px; margin-left: -1.5px; border-radius: 1px; background: var(--degraded); }
.day-axis { position: relative; height: 11px; }
.day-axis span { position: absolute; transform: translateX(-50%); font-size: .64rem; line-height: 1; color: var(--faint); }
.day-axis span:first-child { transform: none; }
.day-note { font-size: .7rem; }

/* A ledger: label, dotted leader, figure. */
.ledger { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: baseline; gap: 5px 8px; }
.leader { border-bottom: 1px dotted var(--line-strong); transform: translateY(-4px); }
.ledger b { font-weight: 560; color: var(--ink); text-align: right; }
.ledger .owed { font-weight: 650; }

/*
| The rate card: the plans as one table read across, because they differ only
| in quantities. Under 760px a plan is its own block, name and price on one
| line, the quantities in two columns under it, its button full width.
*/
.rates { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); box-shadow: 0 1px 2px rgba(16, 20, 28, .04); overflow: hidden; }
.rate-card { min-width: 0; font-size: .92rem; }
.rate-card :is(th, td) { padding: 15px 18px; }
.rate-card thead th { padding-block: 10px; }
.rate-card tbody tr:hover > * { background: var(--raise); }
.rate-card .rc-plan { font-size: 1rem; font-weight: 600; letter-spacing: -.01em; color: var(--ink); white-space: nowrap; }
.rc-price { white-space: nowrap; }
.rc-amt { display: block; font-size: 1.45rem; font-weight: 650; letter-spacing: -.035em; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--ink); }
.rc-per { margin-left: 2px; font-size: .8rem; font-weight: 500; letter-spacing: 0; color: var(--faint); }
.rc-alt { display: block; margin-top: 4px; font-size: .76rem; color: var(--faint); }
.rate-card td.n { font-size: .95rem; font-weight: 520; color: var(--ink); }
.rate-card .rc-go { width: 1%; text-align: right; white-space: nowrap; }
.rc-go .btn { min-width: 11.4em; } /* one width, so the column lines up */
.rc-notes { max-width: 92ch; margin-top: -8px; }
.rc-more { padding-top: 6px; }
.rc-more .body.small { max-width: 52ch; }
/* On a phone the quantities are said as one sentence: five labelled rows a
   plan ran the section past two thousand pixels. */
.rate-card .rc-sum { display: none; }
@media (max-width: 900px) {
  .rate-card thead { display: none; }
  .rate-card, .rate-card tbody { display: block; }
  .rate-card tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 20px; padding: 18px; border-top: 1px solid var(--line-soft); }
  .rate-card tr:first-child { border-top: 0; }
  .rate-card :is(th, td) { display: block; padding: 0; border: 0; }
  .rate-card tbody tr:hover > * { background: none; }
  .rate-card .rc-plan { padding-top: 3px; }
  .rate-card .rc-price { text-align: right; }
  .rate-card td.n { display: none; }
  .rate-card .rc-sum { display: block; grid-column: 1 / -1; max-width: 46ch; font-size: .88rem; color: var(--dim); }
  .rate-card .rc-go { grid-column: 1 / -1; width: auto; padding-top: 8px; }
  .rc-go .btn { width: 100%; }
}
@media (min-width: 561px) and (max-width: 900px) {
  /* Room for the button beside the sentence rather than under it. */
  .rate-card tr { grid-template-columns: minmax(0, 1fr) auto auto; align-items: start; }
  .rate-card .rc-price { grid-column: 2; }
  .rate-card .rc-go { grid-column: 3; grid-row: 1 / span 2; align-self: center; padding-top: 0; }
  .rate-card .rc-sum { grid-column: 1 / 3; }
  .rc-go .btn { width: auto; }
}

/* Read by screen readers, not shown. */
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.team { display: flex; flex-direction: column; gap: 12px; }
.faces { display: flex; }
.faces i {
  width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; margin-left: -4px;
  font-style: normal; font-size: .58rem; font-weight: 600; letter-spacing: .02em; color: var(--dim);
  background: var(--wash); box-shadow: 0 0 0 2px var(--paper), inset 0 0 0 1px var(--line);
}
.faces i:first-child { margin-left: 0; }
.faces i:last-child { background: var(--paper); color: var(--faint); }
.team-rule { display: inline-flex; align-items: center; gap: 8px; }

.fig-code { margin: 0; line-height: 1.75; color: var(--dim); white-space: pre; overflow: hidden; }
.fig-code .c-m, .fig-code .c-k { color: var(--ink); }
.fig-code .c-m { font-weight: 600; }
.fig-code .c-p { color: var(--faint); }
.fig-code .c-s { color: color-mix(in srgb, var(--ok) 82%, var(--ink)); } /* --ok itself is 4.4:1 at this size */

/* The live board's column heads: what each column is, said once. */
.board-cols {
  display: grid; grid-template-columns: var(--cols); gap: 18px;
  padding: 9px 18px; background: var(--wash); border-bottom: 1px solid var(--line);
  font-size: .72rem; font-weight: 520; color: var(--faint);
}
.board-cols > :last-child { text-align: right; }
.board-cols + .board-row { border-top: 0; }
@media (max-width: 860px) {
  .board-cols { gap: 10px 14px; }
  .board-cols > :nth-child(2), .board-cols > :nth-child(4) { display: none; }
}
@media (max-width: 960px) { .caps { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 560px) { .caps { grid-template-columns: minmax(0,1fr); } .cap { padding: 22px 4px; } }

.icon { flex: none; }

/* A run of short facts about one thing. Spaced, not dotted: the dot between
   every pair was template chrome, and the gap does the same job silently. */
.facts { display: inline-flex; flex-wrap: wrap; align-items: baseline; column-gap: 14px; row-gap: 2px; }

/* ── breadcrumbs ─────────────────────────────────────────────────────────── */

.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: .86rem; color: var(--dim); }
.crumbs a:hover { color: var(--ink); }
.crumbs a + a::before { content: "/"; margin-right: 8px; color: var(--line-strong); }

/* ── notes / callouts ────────────────────────────────────────────────────── */

.kv { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.kv .k { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kv .pill { flex: none; }

.note { border-left: 2px solid var(--line); padding: 2px 0 2px 18px; display: flex; flex-direction: column; gap: 7px; }
.note-lead { border-left-color: var(--ink); }

/* A short list of pages, each with one fact beside it: a hairline between
   rows, the title truncating before the fact does. */
.links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.links li { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 9px 0; border-top: 1px solid var(--line-soft); font-size: .88rem; }
.links li:first-child { border-top: 0; padding-top: 0; }
.links a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
.links a:hover { text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
.links span { flex: none; font-size: .8rem; color: var(--faint); font-variant-numeric: tabular-nums; }

.checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.checks li { display: grid; grid-template-columns: 16px 1fr; gap: 11px; color: var(--dim); font-size: .9rem; }
.checks li::before { content: ""; width: 7px; height: 7px; margin-top: .52em; border-radius: 2px; background: var(--line-strong); }
.checks.on li::before { background: var(--ok); }
.checks.off li::before { background: var(--unknown); opacity: .6; }

/* ── footer ──────────────────────────────────────────────────────────────── */

/*
| The page ends in the night it opened on. The horizon runs along the top edge,
| lit from the middle like the one under the home page's dome, and the one line
| of colour below it is the catalogue's real state this minute.
*/
.foot { margin-top: clamp(56px, 7vw, 96px); padding-block: 0 34px; color: var(--night-dim); }
.foot::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--horizon-line); }
.foot > .wrap { position: relative; z-index: 1; }
.foot .ruler { margin-bottom: clamp(36px, 4.4vw, 56px); }
/* A page that is only its heading (an error, a one-line confirmation) runs
   straight into the footer: one night, the heading's horizon and hour scale
   between them, rather than two dark bands with a strip of paper caught
   between. The layout knows when there is no body and says so. */
.foot-joined { margin-top: 0; padding-top: clamp(40px, 5vw, 64px); }
.foot-joined::before { display: none; }

.foot-cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px 32px; }
.foot-h { font-size: clamp(1.7rem, 3.2vw, 2.5rem); font-weight: 600; letter-spacing: -.036em; line-height: 1.08; max-width: 18ch; color: #fff; }

/* Right now, across the catalogue: a lamp per state that has anyone in it. */
.pulse {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 26px;
  margin-top: clamp(32px, 4vw, 44px); padding: 13px 18px;
  border: 1px solid rgba(255, 255, 255, .08); border-radius: 12px; background: rgba(255, 255, 255, .025);
  font-size: .85rem; color: var(--night-dim); transition: border-color .15s, background .15s;
}
.pulse:hover { border-color: rgba(255, 255, 255, .18); background: rgba(255, 255, 255, .045); }
.pulse-now { display: inline-flex; align-items: center; gap: 11px; color: #fff; font-weight: 560; }
.pulse-now .live-dot { background: var(--ok-glow); }
.pulse-now .live-dot::after { border-color: var(--ok-glow); }
.pulse-lamps { display: flex; flex-wrap: wrap; gap: 6px 20px; flex: 1; min-width: 0; }
.lamp { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.lamp::before { content: ""; width: 7px; height: 7px; border-radius: 2px; flex: none; background: color-mix(in srgb, var(--c) 80%, #fff); }
.lamp b { color: #fff; font-weight: 600; font-variant-numeric: tabular-nums; }
.pulse-go { color: #fff; font-weight: 540; white-space: nowrap; }
/* On a phone the label heads the list rather than floating beside five rows. */
@media (max-width: 560px) { .pulse { flex-direction: column; align-items: flex-start; padding: 14px 16px; } }

/* The columns keep their own distance from whatever is above them. That was
   the pulse's bottom margin, so on a catalogue with no status yet (no pulse)
   the headline sat directly on the link columns. */
.foot-grid { display: grid; grid-template-columns: 1.3fr repeat(5, minmax(0,1fr)); gap: 28px; padding-top: 4px; margin-top: clamp(40px, 5vw, 56px); }
/* h3 under the footer's h2; these were h4s, a skipped level. */
.foot-grid h3 { font-size: .82rem; font-weight: 600; letter-spacing: 0; line-height: 1.3; color: #fff; margin-bottom: 12px; }
.foot ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.foot ul a { font-size: .86rem; transition: color .15s; }
.foot-base { margin-top: 40px; padding-top: 20px; border-top: 1px solid rgba(255, 255, 255, .08); display: flex; flex-wrap: wrap; gap: 12px 24px; justify-content: space-between; font-size: .8rem; color: var(--night-faint); }

/* ── overflow discipline ─────────────────────────────────────────────────── */
/*
| A grid or flex track sized `1fr` still has `min-width: auto`, so it refuses to
| shrink below its content's minimum — one long unbreakable string in a
| collapsed column made the whole document wider than the phone it was on, and
| every container inherited the overflow. `minmax(0, 1fr)` and an explicit
| `min-width: 0` on children are what actually let a column get narrow.
*/
.stack > *, .cols2 > *, .cols3 > *, .feat > *, .plan-wide > * { min-width: 0; }

/* Long unbroken strings — DNS records, URLs, selectors — wrap instead of
   pushing the page sideways. Inside a sunken block `.mono` really is code. */
.sunken .mono { font-family: var(--code); font-size: .84rem; letter-spacing: 0; }
.sunken .mono, .msg-facts .fv, code, pre { overflow-wrap: anywhere; }

/* ── responsive ──────────────────────────────────────────────────────────── */

@media (max-width: 1020px) {
  .plans { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .cols2 { grid-template-columns: minmax(0,1fr); }
  .foot-grid { grid-template-columns: repeat(3, minmax(0,1fr)); }
}
@media (max-width: 860px) {
  .nav-links { display: none; }
  .nav-menu { display: block; }
  .feat { grid-template-columns: minmax(0,1fr); gap: 18px; }
  .board { --cols: minmax(0,1fr) auto; }
  .board-row { gap: 10px 14px; }
  .board-row > .stack, .board-row .when { display: none; }
}
@media (max-width: 640px) {
  :root { --gutter: 16px; }
  .d1, h1 { font-size: clamp(2rem, 8.5vw, 2.5rem); }
  .plan-wide .checks { display: none; }
  .plans { grid-template-columns: minmax(0,1fr); }
  .foot-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .nav .wrap { gap: 16px; }
  .nav-actions { gap: 12px; }
  /* No room beside a blip on a phone; the card under the radar names it. */
  .blip-tag { display: none; }
  .msg-facts { grid-template-columns: 1fr; }
  .stats { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .stat { padding: 14px 16px; }
}
