/* ---- the front door ------------------------------------------------------ */

/* The documentation home is the one page a reader opens to be pointed
   somewhere rather than to find an answer, so it alone takes the principal
   face and a standfirst. It is recognised by what it carries - the numbered setup path -
   rather than by its name, so the type and the page it belongs to cannot drift
   apart. */

.vs-docs-article:has(.vs-docs-path) > section > h1 {
  font-family: var(--font-principal);
  font-synthesis: none;
  font-size: var(--doc-display);
  line-height: var(--doc-display-line-height);
  letter-spacing: var(--doc-display-tracking);
  font-weight: var(--doc-display-weight);
  text-wrap: balance;
}

.vs-docs-article:has(.vs-docs-path) > section > h1 + p {
  font-size: var(--ms-lead);
  line-height: var(--ms-lead-line-height);
  color: var(--color-ink-muted);
  /* The lead is prose read at a larger size, not a block; it keeps the same
     edge every other paragraph on the site does rather than a wider one of
     its own. */
  max-width: var(--docs-measure);
}

/* One plate, scored into zones - not a deck of separate cards.

   The comment this replaces argued the right thing and the code did the
   opposite: it said cards are raised paper on the mat rather than outlined
   boxes, because elevation is what separates a route you can take from the
   prose describing it, and then gave every card an outline and no elevation.
   With the stock tokens now in the shared layer the intent is finally
   available to state.

   The plate is the landing page's strongest composition and the answer to the
   deck: siblings meet at a cut edge with no gap between them, and a corner
   appears once, on the outside of the sheet, instead of four times per card.
   The seams are drawn as shadows rather than borders so they cost no layout -
   every cell casts a hairline up and to the left, and the sheet's own overflow
   clips the ones that would otherwise hang off the outer edge. That works
   whatever number of columns auto-fit lands on, which a border-between-siblings
   rule cannot do once the grid wraps. */

.vs-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 0;
  margin: var(--space-8) 0;
  overflow: hidden;
  background: var(--color-paper-raised);
  border-radius: var(--radius-cut);
  box-shadow: var(--stock-contact-plate);
}

.vs-card {
  padding: var(--space-fluid-sm);
  background: transparent;
  border: 0;
  border-radius: var(--radius-cut);
  box-shadow:
    calc(-1 * var(--border-hairline)) 0 0 var(--color-rule),
    0 calc(-1 * var(--border-hairline)) 0 var(--color-rule);
}

/* The product pair is the one place on the site where the two components are
   presented as peers, so they take the accent edge along the top of the sheet
   to mark them as the primary choice on the page. */

.vs-cards-products .vs-card {
  box-shadow:
    calc(-1 * var(--border-hairline)) 0 0 var(--color-rule),
    0 calc(-1 * var(--border-hairline)) 0 var(--color-rule),
    inset 0 var(--border-accent-edge) 0 var(--color-accent);
}

.vs-card > :first-child {
  margin-top: 0;
}

.vs-card > :last-child {
  margin-bottom: 0;
}

/* A card title is part of the document outline, not paragraph text dressed to
   resemble one. Its h3/h4 typography comes from the article heading scale; the
   card only owns the spacing and link treatment local to this composition. */

.vs-card > :is(h3, h4):first-child {
  margin-bottom: var(--space-1);
}

.vs-card > :is(h3, h4):first-child a {
  text-decoration: none;
}

.vs-card > :is(h3, h4):first-child a:hover {
  text-decoration: underline;
  text-decoration-color: currentColor;
}

/* The trailing link row is navigation within the card, at meta scale so it
   sits below the description rather than competing with it. */

.vs-card > p:last-child {
  font-size: var(--text-meta);
  color: var(--color-ink-muted);
}

/* ---- figures ----------------------------------------------------------- */

.vs-figure {
  margin: var(--space-4) 0;
}

/* Every image in the article, not only the ones wrapped in a figure. The rule
   below dresses `.vs-figure img`, and a bare `<p><img></p>` never matched it -
   which is how a vendored readme shipped an 880px screenshot with no width
   attribute and pushed the document 506px past a 390px viewport. Intrinsic
   width is the author's, so the ceiling belongs on the container. */

.vs-docs-article img {
  max-width: 100%;
  height: auto;
}

.vs-figure img {
  display: block;
  width: 100%;
  height: auto;
  border: var(--border-hairline) solid var(--color-rule);
  border-radius: var(--radius-cut);
  background: var(--color-paper-sunken);
}

.vs-figure figcaption {
  margin-top: var(--space-1);
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
  line-height: var(--text-meta-line-height);
}

.vs-docs-article .docutils.container.vs-cards {
  margin: var(--space-4) 0;
}

/* ---- the pipeline figure ----------------------------------------------- */

/* The one diagram on the site, on the page that defines the product's model.
   It is drawn from the token layer rather than given colours of its own, and
   every length inside it is an SVG user unit scaled by the viewBox, so it
   resizes with the column instead of being laid out twice. */

.vs-pipe {
  margin: var(--space-4) 0;
}

.vs-pipe-svg {
  display: block;
  width: 100%;
  height: auto;
}

.vs-pipe-stage rect {
  fill: var(--color-paper-raised);
  stroke: var(--color-rule-strong);
  stroke-width: 1;
}

.vs-pipe-name text {
  fill: var(--color-ink);
  font-family: var(--font-sans);
  font-weight: var(--text-body-strong-weight);
}

.vs-pipe-dir text {
  fill: var(--color-accent-text);
  font-family: var(--font-mono);
}

.vs-pipe-note text {
  fill: var(--color-ink-muted);
  font-family: var(--font-sans);
}

/* The arrow head is a filled triangle on the same path element as the shaft,
   so both take the stroke colour and neither can drift from the other. */

.vs-pipe-flow path {
  stroke: var(--color-rule-strong);
  stroke-width: 1.5;
  fill: var(--color-rule-strong);
}

/* The band is the feature tag: it runs the full width because that is the
   claim, that one tag spans all five stages rather than sitting under any of
   them. */

.vs-pipe-band rect {
  fill: var(--color-paper-sunken);
  stroke: var(--color-rule);
  stroke-width: 1;
}

.vs-pipe-band text {
  fill: var(--color-ink-muted);
  font-family: var(--font-sans);
}

/* Below the point where the shell drops to one column, the diagram is replaced
   by the same content as text.

   Not a preference: an SVG scales its labels with the column, and at 358px the
   viewBox maps 960 user units onto that width, so an 18-unit label renders at
   6.71px and a 19-unit one at 7.09px - 56 and 59 per cent of the floor the type
   scale declares for prose. Nothing in either diagram clears it, and no
   stylesheet can raise them: the sizes are presentation attributes on the text
   elements, and the only lever CSS has is the width, which is already 100%. An
   18-unit label needs a 640px column to reach 12px, which no phone offers.

   So the picture goes and the words stay. Exactly one of the two is in the
   document at any width - `display: none` removes the other from the
   accessibility tree as well as the page - so a screen reader gets the SVG's
   own description on a wide viewport and this list on a narrow one, never both
   and never neither. */

.vs-pipe-steps,
.vs-pipe-steps-band {
  display: none;
}

@media (max-width: 60rem) {
  .vs-pipe-svg {
    display: none;
  }

  .vs-pipe-steps {
    display: block;
    margin: 0;
    padding-left: var(--space-6);
    /* The figure it replaces takes the block column, not the prose measure;
       the fallback text keeps that same width rather than narrowing under it. */
    max-width: none;
  }

  .vs-pipe-steps li {
    margin-bottom: var(--space-3);
  }

  .vs-pipe-steps b {
    font-family: var(--font-sans);
    font-weight: var(--text-body-strong-weight);
  }

  .vs-pipe-steps code {
    margin-left: var(--space-2);
    color: var(--color-accent-text);
    font-family: var(--font-mono);
  }

  .vs-pipe-steps span {
    display: block;
    color: var(--color-ink-muted);
  }

  .vs-pipe-steps-band {
    display: block;
    margin: var(--space-4) 0 0;
    padding: var(--space-3);
    background: var(--color-paper-sunken);
    border: var(--border-hairline) solid var(--color-rule);
    color: var(--color-ink-muted);
  }
}

.vs-pipe figcaption {
  margin-top: var(--space-1);
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
  line-height: var(--text-meta-line-height);
}

/* ---- recorded sessions -------------------------------------------------- */

/* A cast is evidence sitting beside its own transcript, so it is framed like
   the other figure on the site rather than like a code block: the reader has
   already been given the text, and this is the same thing moving. The player
   paints its own ground from the palette baked into the recording, which is why
   nothing here sets a colour. */

.vs-cast {
  margin: var(--space-4) 0;
}

.vs-cast-player {
  border: var(--border-hairline) solid var(--color-rule);
  border-radius: var(--radius-cut);
  overflow: hidden;
}

.vs-cast figcaption {
  margin-top: var(--space-1);
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
  line-height: var(--text-meta-line-height);
}

/* Until the player mounts, the slot is an empty bordered box that reads as a
   broken image. Collapsed until asciinema gives it children, it is simply
   absent, which is the correct appearance when scripting is off and the
   transcript above is carrying the page. */

.vs-cast-player:empty {
  display: none;
}

/* The typed line is the part of a recording a reader can act on, so it reads as
   content rather than as a note about content: full ink, mono, on its own line
   above the caption prose. It is also the only part that survives with the
   player collapsed, which is the case this exists for. */

.vs-cast figcaption .vs-cast-run {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--color-ink);
}

/* The screenshot follows the theme control, not the operating system.
   It was a <picture> switching on prefers-color-scheme, which this theme never
   consults: the ground is chosen by `data-theme`, written in <head> from the
   reader's stored choice. So a reader on a light machine who picked dark got
   dark chrome around a light screenshot, and the site's one image was the one
   element its own control could not reach. Two images keyed off the same
   attribute as everything else fixes that. Both carry the same alt text rather
   than marking one decorative, because either may be the one that is showing,
   and the hidden one is not in the accessibility tree to fall back on. */

.vs-figure-dark {
  display: none;
}

[data-theme="dark"] .vs-figure-light {
  display: none;
}

[data-theme="dark"] .vs-figure-dark {
  display: block;
}
