/*
 * telecraft.dev, structure only. Every value comes from `tokens.css` and
 * every bare element is already dressed by `base.css`, both of which are
 * copies of the console's (see `tools/vendored.json`). This is the only
 * stylesheet in this repository that is ours to edit.
 *
 * Three rules bind it. Nothing is fetched from another origin, ever
 * (ADR-0019, ADR-0045 §5). `tools/check-external-assets.mjs` fails the
 * build if it is. Hue is never load-bearing (ADR-0047 §5): no signal
 * colour and no severity colour appears here at all, which is the simplest
 * way to keep the rule on a page with no data on it. The only colour with a
 * job is the brand amber, which ADR-0047 §4 confines to exactly this
 * surface, and it is only ever set on --colour-bg, the one ground the
 * palette check measures it against (design-system.md, "Brand").
 *
 * The third rule is what the panels on this page are for. The page shows
 * the product rather than describing it, and every panel is built from the
 * console's own tokens and element sheet, so a reader meets the same
 * surface here that they meet in the demo. It follows that an outcome is
 * read off a drawn mark and its word, never off a colour: the marks below
 * are the seven state marks of design-system.md on the same 16-unit grid at
 * the same 1.75 stroke, drawn in ink, and each one keeps its label. Read
 * any of them in greyscale and it still says what it says.
 *
 * `base.css` deliberately carries no block rhythm and no heading sizes,
 * because a console panel and a marketing column want neither the same
 * margins nor the same scale step. This file sets both, from the scale.
 */

/* ---- The page ---- */

:root {
  /* Blueprint linework, which is the identity's own reference
     (identity.md: datum lines and hairlines, calipers, Blueprints as a
     domain object). The two rule tokens draw it, so the grid follows the
     theme without naming a colour of its own. */
  --grid-minor: var(--space-5);
  --grid-major: calc(var(--space-5) * 5);

  /* One measure for every prose column on the page, so the left datum is
     the same distance from the right ragged edge in every band. */
  --measure: 34rem;
  /* The band holds the measure plus the drafting margin, and the panels,
     tables and diagrams that carry the same argument in a picture. Prose is
     still capped at --measure inside it, so widening the band widens the
     drawings and never the line length. */
  --band-width: 56rem;
  /* The hero runs wider than a band because it is two columns: the words at
     the measure, and the panel beside them. */
  --hero-width: 72rem;
  /* The drafting margin the section numbers hang in, left of the datum. */
  --rail: 4.5rem;

  /* What sits on a brand fill. `tokens.css` carries `--brand` for both
     themes and is vendored from the console, so its twin lives here rather
     than as a fork of a file `tools/vendor.mjs check` holds byte for byte. */
  --brand-ink: var(--colour-bg);
}

:root[data-theme='light'] {
  /* The light brand is a dark bronze, so the ink on it goes the other way. */
  --brand-ink: #ffffff;
}

body {
  /* No `place-items: center` any more: this is a page with sections, and a
     column centred in the viewport cannot have a datum line down it. */
  min-height: 100%;
}

/* Block rhythm. Set once on the container rather than per element, which is
   what stops a heading and a paragraph disagreeing about the gap between
   them in two different bands.

   The reset and the rhythm are both child selectors, and deliberately so.
   `.band-body p` would be the obvious place to zero the browser's margins
   and it is one specificity point heavier than `.band-body > * + *`, so it
   would silently win and collapse the rhythm to nothing. Reset the same
   children the rhythm spaces, and the two rules tie; the later one wins on
   the elements it names. */

.band-body > *,
.band-words > * {
  margin: 0;
}

.band-body > * + *,
.band-words > * + * {
  margin-top: var(--space-4);
}

.band-body p {
  max-width: var(--measure);
}

.band-body h2 {
  /* A one-off clamp rather than a scale step. The scale stops at 22px
     because it was cut for interface text (design-system.md, "The scale");
     a section head in a marketing column is a display line, and adding a
     display step would be a change to the shared token layer and belongs
     upstream, not here. */
  font-size: clamp(var(--text-xl), 3.2vw, 1.75rem);
  letter-spacing: -0.01em;
}

.band-body h3 {
  font-size: var(--text-l);
}

/* A heading that opens a new beat wants more air above it than a paragraph
   does. This is the only place the rhythm varies. */
.band-body > h3,
.band-words > h3 {
  margin-top: var(--space-6);
}

/* The beat is marked as well as spaced, in the one colour this page may use
   where there is no data: a short rule in the drafting margin of the column,
   which is the same gesture the band number makes in the margin of the page. */
.band-body > h3::before,
.band-words > h3::before {
  content: '';
  display: block;
  width: 2rem;
  height: 2px;
  margin-bottom: var(--space-3);
  background: var(--brand);
}

/* ---- The chrome ----
   `.topbar`, `a.wordmark`, `nav.topnav` and the theme control are the shape
   the documentation site generates against. Both surfaces are the same site
   or neither is.

   The bar carries a select, so it centres rather than sitting on a baseline:
   a form control's baseline is its text's, which drops the box a reader sees
   below the words beside it. */

.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-3) var(--space-5);
  background: var(--colour-chrome);
  border-bottom: 1px solid var(--colour-rule);
}

/* The word goes left and everything else goes right together, which
   `space-between` alone stops doing the moment the bar holds three things
   rather than two. */
.topnav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-inline-start: auto;
  font-size: var(--text-m);
}

/* Three drawn marks on one plate, which is the shape a bar can hold at any
   width the word could not. It is a radio group because there are three
   states and following the machine is one of them, and it is `hidden` in the
   markup and unhidden by `theme.js`, because a control that cannot act is
   worse than no control and the page resolves to a complete theme from the
   bare `:root` block without it.

   The console offers the same three states in a different shape, and that is
   settled rather than drifted. `console/src/chrome/ThemeControl.tsx` records
   why: a three segment control in its chrome was measured and did not fit,
   and the chrome compaction then moved the theme into the profile menu,
   beside the reader's name, because it is a preference about the reader
   rather than about what the numbers mean. Neither of those pressures exists
   on a page whose bar holds two links and no menu. Do not make one follow the
   other; both were measured where they sit.

   `fieldset` and `legend` bring a border, a padding and a float with them.
   All of it goes: what the elements are wanted for is the grouping and the
   name they give it. */
.theme-control {
  display: flex;
  gap: 2px;
  margin: 0;
  padding: 2px;
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
}

/* Off the screen, not out of the document: it is the group's name. */
.theme-control legend,
.theme-option > span {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.theme-option {
  position: relative;
  display: flex;
  cursor: pointer;
}

.theme-option input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* A plate, not a pill (identity.md): the smallest radius in the scale, and
   the mark drawn in the same hairline stroke as every other mark on the
   site. */
.theme-mark {
  box-sizing: content-box;
  width: 16px;
  height: 16px;
  padding: 4px;
  border-radius: var(--radius-1);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--colour-text-faint);
}

.theme-option:hover .theme-mark {
  color: var(--colour-text-muted);
}

/* The chosen one is filled and comes up to full strength, which is how every
   other settled state on these surfaces reads. */
.theme-option input:checked + .theme-mark {
  background: var(--colour-fill);
  color: var(--colour-chrome);
}

.theme-option input:focus-visible + .theme-mark {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: var(--focus-offset);
}

/* ---- The wordmark ----
   Wordmark-first: the mark supports the word and does not replace it
   (identity.md). `.wordmark` rather than `.mark`, which in this design
   system means a drawn state mark and nothing else.

   One class, three sizes. The chrome's is an anchor at interface size, the
   hero's is a nameplate above the headline, and the console mockup's is the
   same word at the size the real chrome sets it. The documentation site can
   emit `a.wordmark` into its own topbar and get the first of those without
   knowing about the other two. */

.wordmark {
  margin: 0;
  font-weight: var(--weight-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
}

a.wordmark:hover {
  text-decoration: underline;
  text-underline-offset: 0.3em;
}
/* The mark beside the word. The pack's clear space rule is two reading bands,
   three eighths of the mark's height, on every side; at 1em tall that is 6px
   and the gap here is 8. Its minimum is 12px tall, which `--text-m` clears. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.wordmark-mark {
  flex: none;
  /* Taller than the caps beside it, because the mark is measured from its
     datum and the wordmark is measured from its baseline. At `--text-m` this
     is 18px, comfortably over the pack's 12px floor for a bare mark. */
  height: 1.3em;
  width: auto;
}

/* The nameplate carries the pulse instead, which is the same measurement
   drawn long, so a second mark beside it would be the drawing twice. */
.wordmark-plate .wordmark-mark {
  display: none;
}

/* A nameplate, not a headline. The headline is the headline: the wordmark
   set at four rem was the largest thing on the page and the first sentence a
   reader had to get past to find out what the product does. */
.wordmark-plate {
  font-size: var(--text-l);
}

.wordmark-lead {
  color: var(--brand);
}

/* ---- The hero ----
   The blueprint sheet lives here and nowhere else. Drafting linework is a
   drawing surface, not wallpaper: tiled behind a page of prose it stops
   being measurement and becomes decoration, which identity.md rules out.
   It is drawn on a pseudo-element rather than the section so the mask that
   dissolves it into the page cannot reach the text.

   The hero is set to the left datum rather than centred, like every band
   below it. A centred column has no datum to hang from, and the datum is
   the identity. */

.hero {
  position: relative;
  overflow: hidden;
  padding: calc(var(--space-6) * 1.5) var(--space-5) calc(var(--space-6) * 2);
  border-bottom: 1px solid var(--colour-rule);
}

.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--colour-rule) 1px, transparent 1px),
    linear-gradient(90deg, var(--colour-rule) 1px, transparent 1px),
    linear-gradient(var(--colour-rule-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--colour-rule-soft) 1px, transparent 1px);
  background-size:
    var(--grid-major) var(--grid-major),
    var(--grid-major) var(--grid-major),
    var(--grid-minor) var(--grid-minor),
    var(--grid-minor) var(--grid-minor);
  background-position: center top;
  /* The sheet runs out before the section rule does, so the band below
     starts on clean ground. The prefixed copy is for Safari before 15.4,
     which shipped `mask-image` only behind `-webkit-`. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
}

.hero-inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  align-items: center;
  max-width: var(--hero-width);
  margin: 0 auto;
}

.hero-words > * {
  margin: 0;
}

.hero-words > * + * {
  margin-top: var(--space-4);
}

/* The nameplate: the word and the pulse on one baseline, which is where a
   maker's mark sits on an instrument. */
.hero-plate {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.hero h1 {
  max-width: 22ch;
  font-size: clamp(1.9rem, 5.6vw, 3rem);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  /* One voice, not two tones: the three sentences are the same kind of
     statement, and the shape of the product is that they are three. */
}

/* A line each, at every width. Left to the wrap the three beats fell one and
   two, which reads as a sentence that ran out rather than a list of three
   steps, and the list is what the page is about. */
.h1-beat {
  display: block;
}

/* ---- The pulse ----
   A telemetry-signal motif, which identity.md holds from the naming
   decision. It carries no state and no reading, so it is safe ground for
   the brand amber: there is nothing here for a hue to be load-bearing
   about. It sits beside the wordmark now rather than under it, at the size
   a nameplate wants.

   It is drawn the moment the page is, and nothing stages it. A line that
   draws itself in over a few seconds and then hands over to `pulse.js`
   reads as the page loading twice, which is worse than the flourish was
   worth. The geometry in the markup is the geometry the script starts from,
   so a reader never sees the changeover. */

.pulse {
  display: block;
  width: min(15rem, 45vw);
  height: auto;
}

.pulse polyline {
  fill: none;
  stroke: var(--brand);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- The words in the hero ---- */

.lede {
  max-width: var(--measure);
  font-size: var(--text-l);
  line-height: var(--leading-normal);
  color: var(--colour-text-muted);
}

/* Two calls to action in the two tones the system already has, and the tones
   are structural rather than chromatic (design-system.md, "The primitive
   layer"): primary is a solid fill of the ink, quiet is underlined text.
   Two bordered plates side by side would be the paired marketing buttons
   identity.md rejects, and would also claim that both destinations matter
   equally. The demo is the thing worth opening first. */

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-5);
}

.action {
  display: inline-block;
  padding: var(--space-2) 0;
  font-size: var(--text-m);
  font-weight: var(--weight-medium);
  text-underline-offset: 0.25em;
}

.action:hover {
  text-decoration-thickness: 2px;
}

.action-lead {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-1);
  background: var(--brand);
  color: var(--brand-ink);
  text-decoration: none;
}

/* The fill is the accent, and a hover that lightened it would need a second
   value per theme. The plate holds and the underline does the work, which
   is also the one signal `base.css` gives a link, given back to a link that
   had traded it for a plate. */
.action-lead:hover,
.action-lead:focus-visible {
  text-decoration: underline;
}

.status {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  max-width: var(--measure);
  font-size: var(--text-s);
  color: var(--colour-text-muted);
}

/* A stamp, not a pill: the largest radius in the system is 8px because the
   identity is datum lines and hairlines (design-system.md, "Space, radius,
   focus, motion, elevation"). Set in the mono face, which is where the
   second family belongs: a stamp reads as an instrument's stamp, and the
   words are a status rather than prose. */
.stamp {
  flex: none;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--colour-text-muted);
}

/* Two columns once there is room for the measure and a panel beside it. The
   panel leads on a narrow screen only in the source order sense: it comes
   after the words either way, so a reader on a phone meets the claim before
   the evidence. */
@media (min-width: 62rem) {
  .hero-inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 25rem);
    gap: var(--space-6);
  }
}

/* ---- Panels ----
   The product's own surface, built from the product's own tokens: a plate,
   a head that names what is in it, a body, and where there is one, a foot
   carrying the cost or the verdict. Nothing here is a screenshot. A
   screenshot is right in one theme, at one width, on one release, and this
   is right in every combination of the three because it is the same
   stylesheet the console is dressed by. */

.panel {
  margin: 0;
  background: var(--colour-surface);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-2);
  box-shadow: var(--elevation-1);
  overflow: hidden;
}

.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--colour-chrome);
  border-bottom: 1px solid var(--colour-rule);
}

.panel-title {
  font-family: var(--font-mono);
  font-size: var(--text-s);
  font-weight: var(--weight-medium);
  color: var(--colour-text);
}

.panel-body {
  padding: var(--space-4);
}

.panel-body > * {
  margin: 0;
}

.panel-body > * + * {
  margin-top: var(--space-4);
}

.panel-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--colour-rule);
  background: var(--colour-surface-raised);
}

/* A note under a drawing is the drawing's caption, so it takes the muted
   ink every caption on this page takes, and it keeps the panel's measure
   rather than the page's. */
.panel-note,
.panel-cost {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-s);
  line-height: var(--leading-snug);
  color: var(--colour-text-muted);
}

.band-body .panel-note,
.band-body .panel-cost {
  max-width: none;
}

.panel-hero {
  /* The hero panel is the only one that may outgrow its column, because on
     a narrow screen it is the whole width there is. */
  max-width: 100%;
}

/* ---- Chips ----
   The console's chip, in the one variant this page needs: muted ink, one or
   two words, set never to wrap. Tone reinforces the words inside it and
   never replaces them, which on a surface with no severity colour at all
   means there is exactly one tone. */

.chip {
  flex: none;
  padding: 0 var(--space-2);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  font-size: var(--text-xs);
  line-height: 1.6;
  white-space: nowrap;
  color: var(--colour-text-muted);
}

.chip-mono {
  font-family: var(--font-mono);
}

/* ---- Marks ----
   The seven state marks and the five domain marks share one 16-unit grid at
   1.75 stroke with round caps and joins, and inherit `currentColor`
   (design-system.md, "Marks and icons"). Unicode is not used for any of
   them: Atkinson carries a tick and a solid triangle but no cross, so a
   verdict drawn from the typeface would render its two halves from two
   different faces at two different weights.

   Solid where the outline would close up at 16px, which is why the advisory
   triangle and the Service dot are filled and the rest are not. */

.mark {
  flex: none;
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Aligned to the text it sits beside rather than to the line box, so a
     mark in a sentence sits on the same optical line as the words. */
  vertical-align: -0.18em;
}

.mark-solid path {
  fill: currentColor;
}

/* ---- The three readings ----
   One geometry, three strokes. The difference is dash pattern, never
   colour: these are three readings of one estate, and every row states its
   own name next to its line, so nothing is read off a hue (ADR-0047 §5).
   The same list appears twice on the page, once inside the hero panel as
   the thing being read and once in band 02 with each source explained. */

.readings {
  margin: 0;
  padding: 0;
  list-style: none;
}

.band-body > .readings-explained,
.band-words > .readings-explained {
  margin-top: var(--space-5);
  border-top: 1px solid var(--colour-rule);
}

.reading {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--colour-rule-soft);
}

.readings-explained .reading {
  padding: var(--space-4) 0;
  border-bottom-color: var(--colour-rule);
}

.reading:last-child {
  border-bottom: 0;
}

.readings-explained .reading:last-child {
  border-bottom: 1px solid var(--colour-rule);
}

.reading-name {
  margin: 0;
  font-weight: var(--weight-semibold);
}

.reading-source {
  margin: 0;
  font-size: var(--text-m);
  color: var(--colour-text-muted);
}

/* Inside the hero panel the third column is the reading itself, which is
   data, so it is set in the mono face and lines up. */
.reading-value {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
}

/* What the lines are, said once, under the three rows it is said about. The
   drawing is a reading and a reader is owed its units. */
.readings-key {
  max-width: var(--measure);
  margin: var(--space-4) 0 0;
  font-size: var(--text-m);
  color: var(--colour-text-muted);
}

.trace {
  display: block;
  width: 100%;
  max-width: 15rem;
  /* Height from the viewBox, so a narrower column draws a smaller trace
     rather than letterboxing one inside a fixed band. */
  height: auto;
}

/* The datum the reading deviates from. It shows through the gaps in the
   dashed and dotted strokes, which is what makes a trace read as an
   instrument's rather than as a squiggle (identity.md: datum lines and
   hairlines). */
.trace-datum {
  stroke: var(--colour-rule);
  stroke-width: 1;
}

.trace polyline {
  fill: none;
  stroke: var(--colour-text-muted);
  stroke-width: 2;
  stroke-linejoin: round;
  /* The same trace is drawn at 240 user units into anything from a 90px
     column inside a panel to a 240px one in a band. A stroke that scales
     with it thins to under a pixel in the narrow case and the reading stops
     being legible, so the stroke is held at its authored width and only the
     geometry scales. */
  vector-effect: non-scaling-stroke;
}

/* Intended is drawn: a continuous line, because it is one authored
   document at one commit. */
.trace-intended polyline {
  stroke-linecap: round;
}

/* Effective is reported: the collector answers when asked, in segments. */
.trace-effective polyline {
  stroke-dasharray: 11 5;
}

/* Observed is sampled: discrete arrivals over a window, which is what the
   reading actually is. */
.trace-observed polyline {
  stroke-dasharray: 0.5 5;
  stroke-linecap: round;
}

/* ---- The verdict ----
   A mark, its word, and the sentence it stands for, in that order and never
   without all three. The word is the identifier the API and the CLI print,
   so it is set in the mono face and spelled exactly as they spell it. */

.verdict {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin: 0;
}

.verdict .mark {
  align-self: center;
}

.verdict-name {
  font-family: var(--font-mono);
  font-size: var(--text-s);
  font-weight: var(--weight-medium);
}

.verdict-said {
  font-size: var(--text-s);
  color: var(--colour-text-muted);
}

.verdict-owner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
}

/* ---- Two panels side by side ----
   Used where the argument is a comparison, and only there. Two panels that
   are not being compared are a card grid, which this page does not have. */

.band-body > .two-up {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-5);
}

.band-body > .two-up-note {
  margin-top: var(--space-4);
}

@media (min-width: 48rem) {
  .band-body > .two-up {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Where the two panels carry different amounts of content, the shorter
     one still reaches the same foot, so the pair reads as a comparison
     rather than as two things that happen to be adjacent. */
  .two-up-tall .panel {
    display: flex;
    flex-direction: column;
  }

  .two-up-tall .panel-body {
    flex: 1;
  }
}

/* ---- Code inside a panel ----
   An excerpt is quoted material, so it sits on the page's ground rather
   than the panel's, the way a quotation sits inside a plate. */

.excerpt,
.terminal,
.commands {
  margin: 0;
  overflow-x: auto;
  padding: var(--space-3);
  background: var(--colour-bg);
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-1);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
}

/* The commands in a plate are meant to be typed, so they keep the larger of
   the two sizes. */
.commands {
  font-size: var(--text-s);
}

/* A terminal tells two things apart: what you type and what comes back. The
   difference is ink, not colour, and the prompt character is part of the
   line rather than a decoration in front of it. */
.term-cmd {
  color: var(--colour-text);
}

.term-out {
  color: var(--colour-text-muted);
}

.band-body .terminal {
  margin-top: var(--space-4);
}

/* ---- The console mockup ----
   The chrome, the environment lens and one row of the shelf, at the size a
   column allows. It is the shape a reader will recognise when they open the
   demo, and it is drawn from the same tokens, so it cannot disagree with the
   real one about a colour or a face. It is deliberately not interactive: a
   mockup that responds to a click is a promise it cannot keep. */

.console {
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  overflow: hidden;
  background: var(--colour-bg);
}

.console-chrome {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-2) var(--space-3);
  background: var(--colour-chrome);
  border-bottom: 1px solid var(--colour-rule);
}

.console-mark {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.console-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
}

.console-tab {
  font-size: var(--text-xs);
  color: var(--colour-text-faint);
}

/* The active Workspace is a solid fill of the ink, which is what the accent
   is in this system: contrast, not hue. */
.console-tab-on {
  padding: 0 var(--space-2);
  border-radius: var(--radius-1);
  background: var(--colour-fill);
  color: var(--colour-on-fill);
}

.console-lens {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--colour-rule-soft);
}

.console-meta {
  font-size: var(--text-xs);
  color: var(--colour-text-faint);
}

.console-shelf {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3);
  list-style: none;
}

.console-card {
  padding: var(--space-3);
  background: var(--colour-surface);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
}

.console-card > * {
  margin: 0;
}

.console-card-name {
  margin-bottom: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}

/* Every band row carries its mark and its lane name, and neither ever
   appears without the other (ADR-0047 §5). */
.console-card-band {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
}

.console-card-band .mark {
  width: 12px;
  height: 12px;
}

/* ---- The deployment tabs ----
   Two shapes of the same release, so they are two views of one section
   rather than two sections. Both panels are in the document and both are
   readable with no script at all: `assets/tabs.js` unhides the tab list and
   takes over the choice, and until it runs the reader has both, each under
   its own heading. */

.band-body > .tabs {
  margin-top: var(--space-5);
}

.tablist {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  border-bottom: 1px solid var(--colour-rule);
}

.tab {
  padding: var(--space-2) var(--space-4);
  margin-bottom: -1px;
  background: none;
  border: 1px solid transparent;
  border-bottom-color: var(--colour-rule);
  border-radius: var(--radius-1) var(--radius-1) 0 0;
  font: inherit;
  font-size: var(--text-m);
  font-weight: var(--weight-medium);
  color: var(--colour-text-muted);
  cursor: pointer;
}

.tab:hover {
  color: var(--colour-text);
}

/* The selected tab is the plate the panel below it hangs from, which is why
   its bottom edge is missing: the tab and its panel are one surface. */
.tab[aria-selected='true'] {
  background: var(--colour-surface);
  border-color: var(--colour-rule);
  border-bottom-color: var(--colour-surface);
  color: var(--colour-text);
}

.tabpanel {
  padding-top: var(--space-5);
}

.tabpanel > * {
  margin: 0;
}

.tabpanel > * + * {
  margin-top: var(--space-4);
}

.tabpanel p {
  max-width: var(--measure);
}

/* Each panel carries its own heading for the reader who has no script. Once
   the tab list is showing, the tab is the heading, and two of them would be
   the same word twice. */
[data-tabs-ready] .tabpanel-title {
  display: none;
}

.tabpanel-more {
  font-size: var(--text-m);
}

/* ---- The topology diagram ----
   The same drawing twice with one boundary moved, because that is the only
   difference between the two shapes. Drawn in HTML rather than as an SVG so
   every label stays real text at a real size: a 640-unit viewBox scaled into
   a 360px viewport renders a 12px label at under 7px, and the labels are the
   content.

   The telemetry path is a strip of its own with its own rule around it,
   because the first principle is that Telecraft is not on it, and a diagram
   that draws the platform next to the path has already lost the argument. */

.topology {
  display: grid;
  gap: var(--space-3);
  margin: 0;
}

.topo-zone {
  padding: var(--space-4);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-2);
}

.topo-zone > * + * {
  margin-top: var(--space-3);
}

/* The customer's boundary is drawn solid and the project's is drawn dashed,
   so which side of the line a box is on is read off the line and not off a
   colour. */
.topo-zone-ours {
  border-style: dashed;
  background: var(--colour-surface);
}

.topo-zone-name {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--colour-text-faint);
}

.topo-path {
  padding: var(--space-3);
  background: var(--colour-bg);
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-1);
}

.topo-path > * + * {
  margin-top: var(--space-2);
}

.topo-path-name,
.topo-path-note,
.topo-reads,
.topo-store-note {
  margin: 0;
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
  color: var(--colour-text-muted);
}

.topo-path-name {
  font-family: var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--colour-text-faint);
}

/* The flow is a row of plates joined by hairlines. The join is a border on
   the gap rather than a glyph, because Atkinson carries no arrow and a
   fallback face drawing one would be the only character on the page set in
   a typeface nobody chose. */
.topo-flow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.topo-node {
  flex: none;
  padding: var(--space-2) var(--space-3);
  background: var(--colour-surface);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  font-size: var(--text-xs);
  color: var(--colour-text);
}

.topo-node + .topo-node {
  position: relative;
  margin-left: var(--space-4);
}

.topo-node + .topo-node::before {
  content: '';
  position: absolute;
  top: 50%;
  right: 100%;
  width: var(--space-4);
  border-top: 1px solid var(--colour-rule);
}

/* The Instance is the subject of the drawing, so it takes the fill the
   system uses for the one thing on a surface that is selected. */
.topo-node-lead {
  background: var(--colour-fill);
  border-color: var(--colour-fill);
  color: var(--colour-on-fill);
  font-weight: var(--weight-medium);
}

/* Below the width where two plates and the hairline between them fit, the
   flow stacks. Wrapped, the hairline that joins two nodes ends up hanging
   off the left of the row below, which reads as a stray rule rather than as
   a join. */
@media (max-width: 30rem) {
  .topo-flow {
    flex-direction: column;
    align-items: stretch;
  }

  .topo-node + .topo-node {
    margin-left: 0;
    margin-top: var(--space-4);
  }

  .topo-node + .topo-node::before {
    top: auto;
    right: auto;
    bottom: 100%;
    left: var(--space-4);
    width: 0;
    height: var(--space-4);
    border-top: 0;
    border-left: 1px solid var(--colour-rule);
  }
}

.topo-node-store {
  font-family: var(--font-mono);
}

.topo-store {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

/* ---- Facts ----
   A row per question a reader has about a deployment shape, with the
   question in the mono face on the left. Rows on hairlines rather than
   cards: the two lists are meant to be read against each other, and a card
   grid puts them in parallel instead of in register. */

.facts {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--colour-rule);
}

.facts li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-1);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--colour-rule-soft);
  font-size: var(--text-m);
  color: var(--colour-text-muted);
}

.facts-value {
  color: var(--colour-text-muted);
}

.facts-key {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--colour-text-faint);
}

@media (min-width: 40rem) {
  .facts li {
    grid-template-columns: 9rem minmax(0, 1fr);
    gap: var(--space-4);
    align-items: baseline;
  }
}

/* ---- The bands ----
   A drafting sheet's margin: the section number hangs in the margin and the
   content sits behind a datum line that runs the height of the band. The
   rule is the alignment, which is why it is a border on the body rather
   than a decorative element beside it, so nothing can drift off it. */

.band {
  padding: calc(var(--space-6) * 1.5) var(--space-5);
  border-bottom: 1px solid var(--colour-rule);
}

/* Alternating grounds, so the page has a beat a reader can feel at the
   scroll bar rather than one flat field seven sections long. One step of
   the surface scale, which is the same step a panel takes off the page. */
.band:nth-of-type(even) {
  background: var(--colour-surface);
}

/* A panel on the raised ground would disappear into it, so on those bands
   it takes the page's own ground instead. */
.band:nth-of-type(even) .panel {
  background: var(--colour-bg);
}

.band-last {
  border-bottom: 0;
}

/* Every single-column grid on this page names `minmax(0, 1fr)` rather than
   leaving the track implicit. An implicit `auto` track takes its base size
   from its content's minimum, and the command block's longest flag line is
   wider than a 360px viewport, which pushed the whole band 72px past the
   right edge until the track was told it may be narrower than its content.
   The block scrolls inside its own bounds instead, which is what
   `.commands` was given `overflow-x` for. */
.band-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
  max-width: var(--band-width);
  margin: 0 auto;
}

.band-split > * {
  margin: 0;
}

.band-index {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-s);
  letter-spacing: 0.12em;
  color: var(--brand);
}

/* One breakpoint for the whole page, and 48rem is where the drafting margin
   plus the readings' three columns still leave the prose a usable measure.
   Three different breakpoints meant a 768px window that had the wide
   readings row and not the datum line it should hang from. */
@media (min-width: 48rem) {
  .band-inner {
    grid-template-columns: var(--rail) minmax(0, 1fr);
    gap: 0;
  }

  .band-index {
    /* Aligned to the cap of the h2 beside it rather than its box, which is
       what makes the number read as a datum label and not a heading. */
    padding-top: 0.35em;
  }

  .band-body {
    border-left: 1px solid var(--colour-rule);
    padding-left: var(--space-5);
  }

  .reading {
    grid-template-columns: 7rem 15rem minmax(0, 1fr);
    align-items: center;
    gap: var(--space-4);
  }
}

/* Inside a panel the three columns arrive earlier, because the panel is
   narrower than a band and the rows are shorter. */
@media (min-width: 30rem) {
  .panel .reading {
    grid-template-columns: 5.5rem minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3);
  }

  .panel .trace {
    max-width: 100%;
  }
}

/* ---- The library ----
   Rows on hairlines, not a shelf of cards. A term and what it is, at the
   density of a reference rather than a feature list. */

.band-body > .lexicon,
.band-words > .lexicon {
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--colour-rule);
}

.entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--colour-rule-soft);
}

.entry-name {
  margin: 0;
  font-size: var(--text-m);
}

.entry-what {
  margin: 0;
  max-width: var(--measure);
  color: var(--colour-text-muted);
}

@media (min-width: 48rem) {
  .entry {
    grid-template-columns: 10rem minmax(0, 1fr);
    column-gap: var(--space-4);
  }

  /* Under the description it belongs to, not under the term. Auto placement
     put it in the term's column, where it wrapped at ten rem and read as a
     second definition of the word above it. */
  .entry-cost {
    grid-column: 2;
  }
}

/* ---- Wide bands, split in two ----
   The hero puts the words beside the thing they describe, and a band that
   has something to show wants the same shape. `.band-wide` takes the hero's
   measure and `.band-split` divides it: prose on the left at the same
   measure it always had, and the panel on the right.

   Which bands get it is a rule and not a judgement: a band that shows
   something takes the wide measure, and a band that is only words keeps the
   reading measure it always had. So 01 to 05 are wide and 06 and 07 are
   not.

   A second breakpoint, and the only one on the page besides 48rem. The
   split cannot open at 48rem because two columns out of 43.5rem of body
   leave the prose at about twenty characters, which is not a measure. It
   opens at 60rem, where the narrower half is still wider than the panel is
   on a phone, and the columns are set in fractions from there so a laptop
   and a wide display both divide what they have rather than one of them
   being sized for the other. */

.band-wide {
  max-width: var(--hero-width);
}

.band-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
}

.entry-cost {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  font-size: var(--text-s);
  color: var(--colour-text-muted);
}

.verdicts {
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 60rem) {
  .band-split {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    /* Two steps of the scale, because the gutter between a column of prose
       and a panel is the widest gap on the page and the scale stops at 32px. */
    gap: calc(var(--space-6) * 1.5);
  }

  /* The panel sits at the head of its column and travels with the words.
     It was pinned, which held it in place for the length of the prose and
     read as a panel that had come loose from the paragraph it belonged to. */
  .band-demo {
    align-self: start;
  }
}

/* ---- The live panels ----
   `assets/demos.js` builds these; the markup they replace is the same content
   standing still. Everything here is a control or a state, so it is drawn the
   way the console draws controls and states: a hairline, a mono label, and no
   colour carrying a meaning a word does not also carry (ADR-0047 §5). */

.demo-live {
  display: grid;
  gap: var(--space-4);
}

.demo-live[hidden],
.panel-cost[hidden] {
  display: none;
}

.demo-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

.demo-label {
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--colour-text-faint);
}

.demo-choice {
  display: inline-flex;
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  overflow: hidden;
}

.demo-opt,
.toggle-opt {
  padding: 2px var(--space-2);
  border: 0;
  background: none;
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
  cursor: pointer;
}

.demo-choice .demo-opt + .demo-opt,
.toggle-opts .toggle-opt + .toggle-opt {
  border-left: 1px solid var(--colour-rule);
}

/* The chosen one is filled rather than tinted, which is the one way this
   page marks a control as on and the only one that survives greyscale. */
.demo-opt[aria-pressed='true'],
.toggle-opt[aria-pressed='true'] {
  background: var(--colour-fill);
  color: var(--colour-on-fill);
}

.demo-bar > .demo-opt {
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  padding: 2px var(--space-3);
}

.demo-opt[disabled] {
  color: var(--colour-text-faint);
  cursor: default;
}

/* ---- The lanes ---- */

.lanes,
.claims,
.cohorts,
.lane-parts,
.palette-parts,
.cohort-nodes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.lanes {
  display: grid;
  gap: var(--space-3);
}

.lane {
  display: grid;
  gap: var(--space-2);
}

.lane-name,
.cohort-name,
.claims-head {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--colour-text-faint);
}

.lane-parts,
.palette-parts,
.cohort-nodes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.part,
.node {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: 1px var(--space-2);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  background: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--colour-text);
  white-space: nowrap;
}

.part {
  cursor: pointer;
}

/* A part in a lane can be taken out, and the glyph is what says so. The
   `+` and a barred entry are not removable, so neither carries it. */
.lane-parts .part:not(.part-add)::after {
  content: '\00d7';
  color: var(--colour-text-faint);
}

.part:hover,
.part:focus-visible {
  border-color: var(--colour-text-muted);
}

.part-add {
  border-style: dashed;
  color: var(--colour-text-muted);
}

.part-barred {
  border-style: dotted;
  color: var(--colour-text-faint);
  cursor: not-allowed;
}

.palette {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px dashed var(--colour-rule);
  border-radius: var(--radius-1);
}

.demo-empty {
  font-size: var(--text-xs);
  color: var(--colour-text-faint);
}

/* ---- The claims ---- */

.claims {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--colour-rule);
}

.claim {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
}

.claim .mark {
  margin-top: 2px;
}

.claim-name {
  font-family: var(--font-mono);
  color: var(--colour-text);
}

.claim-said {
  grid-column: 2;
  color: var(--colour-text-muted);
}

.demo-foot {
  margin: 0;
}

.demo-reset {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

/* ---- The rollout ---- */

.cohorts {
  display: grid;
  gap: var(--space-3);
}

.cohort {
  display: grid;
  gap: var(--space-2);
}

.node {
  color: var(--colour-text-faint);
}

.node-version {
  color: inherit;
}

/* A collector that has reported the new version is filled, the way every
   other settled state on this page is. */
.node-done {
  border-color: var(--colour-text-muted);
  color: var(--colour-text);
}

@media (prefers-reduced-motion: no-preference) {
  .node {
    transition: border-color 200ms ease, color 200ms ease;
  }
}

/* ---- The cross ---- */

.toggle {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
}

.toggle-name {
  flex: none;
  width: 6rem;
  font-family: var(--font-mono);
  color: var(--colour-text-muted);
}

.toggle-opts {
  display: inline-flex;
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
  overflow: hidden;
}

/* ---- Severity and signal, borrowed from the console ----
   Two sets of colour that already exist in `tokens.css`, contrast verified
   on both grounds and separated under simulated deuteranopia and
   protanopia. Neither carries anything on its own: a severity tone always
   sits on a mark whose shape says the same thing, and a lane hue never
   appears without its lane name beside it, which is the rule ADR-0047 §5
   extends to this site by name.

   The site used to have neither, on the reasoning that a marketing page has
   no state to report. It does: every panel on it reports one, and reporting
   it in the ink of the product is more honest than reporting it in grey. */

.mark-ok {
  color: var(--severity-ok);
}

.mark-advisory {
  color: var(--severity-advisory);
}

.mark-violation {
  color: var(--severity-violation);
}

/* ---- Signal lanes ---- */

[data-lane='traces'] {
  --lane: var(--signal-traces);
}

[data-lane='logs'] {
  --lane: var(--signal-logs);
}

[data-lane='metrics'] {
  --lane: var(--signal-metrics);
}

.lane {
  padding-left: var(--space-3);
  border-left: 2px solid var(--lane);
}

.lane-name,
.console-card-band[data-lane] {
  color: var(--lane, var(--colour-text-faint));
}

/* ---- The principles ----
   Short declaratives on hairlines. No icons: a glyph beside each of these
   would be decoration, and every mark in this system is a mapping from a
   state (design-system.md, "Marks and icons").

   One column, like the readings and the parts above it. Two columns fitted,
   and read as a different page: five items in two columns also leaves the
   fifth alone on a row, which makes a deliberate list look like one that ran
   out. */

.principles {
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--colour-rule);
}

.principles li {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--colour-rule);
  max-width: var(--measure);
  font-size: var(--text-m);
  color: var(--colour-text-muted);
}

.principles strong {
  display: block;
  font-weight: var(--weight-semibold);
  color: var(--colour-text);
  color: var(--colour-text);
}

/* ---- The two plates ----
   A plate here frames something the reader can act on (a console to open, a
   command to run) which is what keeps two of them from reading as a card
   grid.

   Stacked at every width, and never side by side. Two columns would put
   them in parallel when they are in sequence: the heading says see it, then
   run it. It also gives the command block the full measure, so a
   39-character flag line lands whole instead of scrolling on a laptop. */

.band-body > .plates {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-5);
}

.plate {
  padding: var(--space-5);
  background: var(--colour-surface);
  border: 1px solid var(--colour-rule);
  border-left: 2px solid var(--brand);
  border-radius: var(--radius-2);
}

/* Reset and rhythm on the same children, for the reason given at the top of
   this file: a descendant selector would outweigh the `+` rule. */
.plate > * {
  margin: 0;
}

.plate > * + * {
  margin-top: var(--space-3);
}

.plate h3 {
  font-size: var(--text-l);
}

/* The plate is as wide as the band so the command block is, but its prose
   keeps the same measure as every other paragraph on the page. */
.plate p {
  font-size: var(--text-m);
  color: var(--colour-text-muted);
}

.plate-link {
  font-family: var(--font-mono);
  font-size: var(--text-l);
  font-weight: var(--weight-medium);
  color: var(--colour-text);
}

/* ---- The foot ---- */

.foot {
  max-width: var(--band-width);
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) calc(var(--space-6) * 1.5);
  border-top: 1px solid var(--colour-rule);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
}

/* The licence, stated rather than abbreviated: source-available is not open
   source, and the difference is the reader's to weigh before they invest an
   afternoon. */
.licence-note {
  margin: 0 0 var(--space-5);
  max-width: var(--measure);
  font-size: var(--text-s);
}

.colophon {
  margin: 0;
  letter-spacing: 0.04em;
}
