/* portrayal.dev — landing page.
 * Palette and type come from brand/tokens.css; this file is layout only.
 */

* { box-sizing: border-box; }

/* The UA stylesheet gives [hidden] display:none at the lowest precedence, so any
   author rule that sets display - .stage-loading needs display:grid to center -
   silently outranks it and the element stays on screen. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--pt-ground);
  color: var(--pt-ink);
  font-family: var(--pt-font-body);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--pt-font-display);
  margin: 0;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
p { margin: 0; }

a { color: var(--pt-brass); text-underline-offset: 3px; }
a:hover { color: var(--pt-brass-light); }

code, .mono, pre {
  font-family: var(--pt-font-mono);
  font-variant-numeric: tabular-nums;
}
code { font-size: 0.875em; color: var(--pt-ink); }

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

.wrap { max-width: 1160px; margin: 0 auto; padding: 0 clamp(1.15rem, 4vw, 2.75rem); }

.skip {
  position: absolute; left: -9999px;
  background: var(--pt-surface); color: var(--pt-ink);
  padding: 0.6rem 1rem; border: 1px solid var(--pt-brass); border-radius: var(--pt-radius);
}
.skip:focus { left: 1rem; top: 1rem; z-index: 60; }

.eyebrow {
  font-family: var(--pt-font-mono);
  font-size: 0.76rem; font-weight: 500;
  letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--pt-ink-faint);
}
.eyebrow .on { color: var(--pt-brass); }

/* ── Masthead ───────────────────────────────────────────────────────── */
.masthead {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--pt-ground) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--pt-line-soft);
}
.masthead-in { display: flex; align-items: center; gap: 2rem; height: 58px; }
.brand { display: flex; align-items: center; gap: 0.55rem; text-decoration: none; color: var(--pt-ink); }
.brand-name { font-family: var(--pt-font-display); font-weight: 700; font-size: 1.05rem; letter-spacing: -0.035em; }
.tld { color: var(--pt-brass); }
.masthead-nav { margin-left: auto; display: flex; gap: 1.4rem; align-items: center; }
.masthead-nav a { color: var(--pt-ink-muted); text-decoration: none; font-size: 0.9rem; }
.masthead-nav a:hover { color: var(--pt-ink); }
.masthead-nav .gh {
  color: var(--pt-ink); border: 1px solid var(--pt-line);
  padding: 0.3rem 0.75rem; border-radius: var(--pt-radius);
}
.masthead-nav .gh:hover { border-color: var(--pt-brass); color: var(--pt-brass); }
/* The same credit in the masthead, after GitHub, on the pages that carry it in
 * the footer: small, so it sits in the nav's line rather than competing with
 * the wordmark, and down to the logo alone on a phone. */
.masthead-nav .nav-sponsor {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding-left: 1.2rem; border-left: 1px solid var(--pt-line);
  font-size: 0.78rem; line-height: 1.1;
}
.masthead-nav .nav-sponsor:hover { color: var(--pt-brass); }
.nav-sponsor img { display: block; height: 32px; width: auto; }
/* The full bar needs about 1010px, so it gives way in steps rather than
 * running off the right edge: first the "Sponsored by" words and some of the
 * spacing, then the links you can most easily do without (the index's own
 * sections, which are a scroll away, and the Components and Annotate tools,
 * which the index footer lists), and at 800px down to the phone bar below. */
@media (max-width: 1080px) {
  .masthead-in { gap: 1.25rem; }
  .masthead-nav { gap: 1rem; }
  .masthead-nav .nav-sponsor { padding-left: 1rem; }
  .masthead-nav .nav-sponsor-label { display: none; }
}
@media (max-width: 960px) {
  .masthead-nav > a:is([href="#how"], [href="#status"],
    [href="components.html"], [href="annotate.html"]) { display: none; }
}
@media (max-width: 800px) {
  .masthead-nav a:not(.gh):not(.nav-sponsor) { display: none; }
  /* room for the sponsor's logo beside GitHub on a phone */
  .masthead-in { gap: 0.75rem; }
  .masthead-nav { gap: 0.75rem; }
  .masthead-nav .nav-sep { display: none; }
  .masthead-nav .nav-sponsor { padding-left: 0; border-left: 0; }
  .masthead-nav .gh { padding: 0.3rem 0.55rem; }
  .masthead-nav .nav-sponsor img { height: 24px; }
  .masthead-nav .nav-sponsor-label { display: none; }
  /* the docs are one footer link away on a phone; the bar keeps Export */
  .masthead-nav .navmenu[data-docs] { display: none; }
}

/* ── Hero ───────────────────────────────────────────────────────────── */
.hero { padding: clamp(3.5rem, 9vw, 7rem) 0 clamp(2.5rem, 6vw, 4rem); }
.hero h1 {
  font-size: clamp(2.5rem, 7vw, 4.4rem);
  font-weight: 800; letter-spacing: -0.045em;
  margin: 1.15rem 0 1.4rem;
}
/* THE TAGLINE. Sits above the h1 as the framing the headline then answers.
   The second sentence is the one carrying the claim, so it gets the ink and
   the first is set back as the setup it is. */
.tagline { font-size: clamp(1.05rem, 1.9vw, 1.35rem); line-height: 1.45;
           color: var(--pt-ink-faint); margin: 0 0 0.9rem; max-width: 46ch; }
.tagline-turn { color: var(--pt-ink); }

.lede { font-size: clamp(1.02rem, 1.6vw, 1.18rem); color: var(--pt-ink-muted); max-width: 60ch; }
.lede-2 { margin-top: 0.9rem; color: var(--pt-ink-faint); }
.cta-row { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2.25rem; }
.btn {
  display: inline-block; text-decoration: none;
  padding: 0.6rem 1.3rem; border-radius: var(--pt-radius);
  border: 1px solid var(--pt-line); color: var(--pt-ink);
  font-size: 0.94rem; font-weight: 500;
}
.btn:hover { border-color: var(--pt-brass); color: var(--pt-brass); }
.btn-primary { background: var(--pt-brass); border-color: var(--pt-brass); color: #0E1013; }
.btn-primary:hover { background: var(--pt-brass-light); border-color: var(--pt-brass-light); color: #0E1013; }

/* ── Sections ───────────────────────────────────────────────────────── */
.section { padding: clamp(3.5rem, 8vw, 6rem) 0; border-top: 1px solid var(--pt-line-soft); }
.section-head { display: flex; flex-direction: column; gap: 0.9rem; margin-bottom: 2.5rem; max-width: 70ch; }
.section-head h2 { font-size: clamp(1.7rem, 3.4vw, 2.35rem); font-weight: 700; }
.section-head .sub { color: var(--pt-ink-muted); max-width: 66ch; }
.section-head .sub strong { color: var(--pt-ink); font-weight: 600; }

/* ── Showcase ───────────────────────────────────────────────────────── */
.show {
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  background: var(--pt-surface);
  overflow: hidden;
}
.show-tabs {
  display: flex; gap: 0.15rem; overflow-x: auto;
  padding: 0 0.5rem; background: var(--pt-surface-2);
  border-bottom: 1px solid var(--pt-line);
}
.show-tabs button {
  flex: none;
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--pt-ink-muted); cursor: pointer;
  font: inherit; font-size: 0.9rem;
  padding: 0.8rem 1rem 0.7rem;
}
.show-tabs button:hover { color: var(--pt-ink); }
.show-tabs button[aria-selected="true"] { color: var(--pt-ink); border-bottom-color: var(--pt-brass); }

.show-body { display: grid; grid-template-columns: 108px minmax(0, 1fr) 330px; }
@media (max-width: 900px) { .show-body { grid-template-columns: 1fr; min-height: 0; } }

/* The stage ladder: a vertical column, so all seven names are legible at once.
   The buttons are .show-tabs' buttons turned on their side - the selected
   marker moves from the bottom edge to the right - so this is a layout change
   rather than a restyle. Below 900px it lies down again as a scrolling row. */
.stage-rail {
  display: flex; flex-direction: column;
  background: var(--pt-surface-2);
  border-right: 1px solid var(--pt-line);
  padding: 0.35rem 0;
}
.stage-rail button {
  flex: none; text-align: left;
  background: none; border: none; border-right: 2px solid transparent;
  color: var(--pt-ink-muted); cursor: pointer;
  font: inherit; font-size: 0.82rem; line-height: 1.25;
  padding: 0.6rem 0.65rem 0.6rem 0.75rem;
}
.stage-rail button:hover { color: var(--pt-ink); }
.stage-rail button.done { color: var(--pt-ink); }
.stage-rail button.done::after { content: " \2713"; color: var(--pt-brass); }
.stage-rail button[aria-selected="true"] { color: var(--pt-ink); border-right-color: var(--pt-brass); }
.stage-rail button:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: -2px; }
@media (max-width: 900px) {
  .stage-rail { flex-direction: row; overflow-x: auto; padding: 0 0.5rem;
                border-right: none; border-bottom: 1px solid var(--pt-line); }
  .stage-rail button { border-right: none; border-bottom: 2px solid transparent; white-space: nowrap; }
  .stage-rail button[aria-selected="true"] { border-bottom-color: var(--pt-brass); }
}

/* A spoke: the stage is a device's 2D face, and the breadcrumb names the way
   back. The ladder dims to say it is not where you are - but it stays
   clickable, because a rung is also a way out, and a dimmed control that
   silently ignores clicks is a trap. So no pointer-events here. */
.spoke-crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem;
  padding: 0.55rem 1.25rem; background: var(--pt-surface-2);
  border-bottom: 1px solid var(--pt-line);
}
.spoke-crumb[hidden] { display: none; }
.spoke-back {
  background: none; border: 1px solid var(--pt-brass-dim); border-radius: var(--pt-radius-sm);
  color: var(--pt-brass); font: inherit; font-size: 0.82rem;
  padding: 0.3rem 0.7rem; cursor: pointer;
}
.spoke-back:hover { border-color: var(--pt-brass); }
.spoke-back:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: 2px; }
.spoke-where { font-size: 0.78rem; color: var(--pt-ink-muted); }
.spoke-lenses { margin-left: auto; }
.spoke-open .stage-rail { opacity: 0.35; transition: opacity 0.15s; }
.spoke-open .stage-rail:hover, .spoke-open .stage-rail:focus-within { opacity: 0.7; }

/* Over the stage: the Front/Rear toggle on the left, the 2D/3D switch on the
   right. Same ground and ink as the ladder, so it reads as the ladder's bar.
   The switch is a segmented control; the selected half carries the brass the
   ladder uses for its selected rung. */
.show-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.4rem 1.25rem; background: var(--pt-surface-2);
  border-bottom: 1px solid var(--pt-line);
}
.stage-tabs { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.stage-tabs:empty { display: none; }
.stage-tabs button {
  background: none; border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  color: var(--pt-ink-muted); cursor: pointer;
  font: inherit; font-size: 0.78rem; padding: 0.25rem 0.6rem;
}
.stage-tabs button:hover { color: var(--pt-ink); }
.stage-tabs button[aria-selected="true"] { color: var(--pt-ink); border-color: var(--pt-brass); }
.stage-tabs button:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: -2px; }
.stage-mode {
  display: inline-flex; margin-left: auto;
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm); overflow: hidden;
}
.stage-mode button {
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--pt-ink-muted); cursor: pointer;
  font: inherit; font-size: 0.82rem; line-height: 1.25;
  padding: 0.3rem 0.8rem;
}
.stage-mode button + button { border-left: 1px solid var(--pt-line); }
.stage-mode button:hover { color: var(--pt-ink); }
.stage-mode button[aria-pressed="true"] { color: var(--pt-ink); background: var(--pt-surface);
                                          border-bottom-color: var(--pt-brass); }
.stage-mode button:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: -2px; }
@media (max-width: 900px) { .show-bar { padding: 0.4rem 0.75rem; } }

/* "Look closer", stages 1-3: a strip between the stage and its foot, one
   group per cast device with a button per lens. It used to sit at the foot of
   the side panel, below the panel's cut, where nobody found it. */
.look-strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.45rem 1.1rem;
  padding: 0.55rem 1.25rem; border-top: 1px solid var(--pt-line);
  background: var(--pt-surface);
}
.look-strip[hidden] { display: none; }
.look-label { margin: 0; font-size: 0.8rem; color: var(--pt-ink-muted); }
.look-label strong { color: var(--pt-brass); font-weight: 600; }
.look-dev { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
.look-dev > .mono { font-size: 0.76rem; color: var(--pt-ink); margin-right: 0.15rem; }
.look-dev button {
  background: var(--pt-surface-2); color: var(--pt-ink-muted);
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  font: inherit; font-size: 0.74rem; padding: 0.22rem 0.55rem; cursor: pointer;
}
.look-dev button:hover { border-color: var(--pt-brass-dim); color: var(--pt-ink); }
.look-dev button:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: 1px; }
@media (max-width: 900px) { .look-strip { padding: 0.5rem 0.75rem; } }

/* In 2D the device on the drawing is the button (spoke-host.js's
   wireDevices). The outline is drawn by the browser round the group's box. */
.pt-device { cursor: pointer; }
.pt-device:hover, .pt-device:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: 1px; }

.show-stage {
  position: relative;
  /* An explicit height, and each stage layer absolutely positioned to fill it.
     Laying the layers out as grid rows lets their content size the row - the
     rack elevation grew the stage to 1534px and it never came back - and leaves
     height:100% on the SVG indefinite, which is what broke zoom in the first
     place. Absolute inset makes both dimensions definite. */
  height: 520px;
  background: var(--pt-ground);
}
.stage-2d, .stage-3d, .stage-rack {
  position: absolute;
  inset: clamp(1rem, 3vw, 2.25rem);
}
.stage-2d svg, .stage-rack svg { width: 100%; height: 100%; display: block; }
/* svg2d.js's highlight: a box drawn over one element of a composed drawing. */
/* Non-scaling, so it reads at any zoom: a millimeter stroke on a 7 mm bore in
   a meter-tall rack is sub-pixel. */
.pt-hl { fill: none; stroke: var(--pt-brass); stroke-width: 2.5px; vector-effect: non-scaling-stroke; }
.pt-hl.pt-hl-inner { stroke-width: 1.5px; stroke-dasharray: 3 2; }
.stage-3d canvas { display: block; border-radius: var(--pt-radius-sm); }
.stage-loading {
  position: absolute; inset: 0; margin: 0; z-index: 2;
  /* Opaque: building a 3D scene is visible work, and watching a rack assemble
     itself piece by piece reads as a glitch rather than as progress. */
  background: var(--pt-ground);
  display: grid; place-items: center; pointer-events: none;
  color: var(--pt-ink-faint); font-family: var(--pt-font-mono);
  font-size: 0.8rem; text-align: center;
}
@media (max-width: 900px) {
  .show-stage { height: 360px; }
}

.show-side {
  border-left: 1px solid var(--pt-line);
  background: var(--pt-surface);
  min-width: 0;
  /* The panels are different heights - a seven-step runbook against a two-line
     readout. Scroll the panel instead of resizing the stage, so the drawing
     does not jump every time you change tab. */
  max-height: 520px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
@media (max-width: 900px) { .show-side { max-height: 340px; } }
@media (max-width: 900px) { .show-side { border-left: none; border-top: 1px solid var(--pt-line); } }
.side-panel { padding: 1.15rem 1.25rem; display: flex; flex-direction: column; gap: 1rem; }

/* THE INSPECT LENS IS EXPLORER, IN A FRAME (spoke-host.js openFrame). While it
   is open it takes the stage's and the side panel's place. .show-body is a
   GRID (rail | stage | panel), not a flex row, so the frame spans the last two
   columns rather than flexing into them; below 900px the grid is one column
   and the frame is simply the row after the rail. */
.show-body.frame-open #stageHost,
.show-body.frame-open .show-side { display: none; }
.lens-frame { grid-column: 2 / span 2; min-width: 0; min-height: 560px; position: relative; }
.lens-frame[hidden] { display: none; }
@media (max-width: 900px) { .lens-frame { grid-column: auto; } }
.lens-cover { position: absolute; inset: 0; margin: 0; display: grid; place-items: center;
              background: var(--pt-ground); color: var(--pt-ink-muted);
              font-family: var(--pt-font-mono); font-size: 0.8rem; }
.lens-frame iframe { display: block; width: 100%; height: 100%; min-height: 560px;
                     border: 0; background: var(--pt-ground); }

.panel-title {
  font-family: var(--pt-font-mono); font-size: 0.7rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--pt-ink-faint);
}
.panel-lead { font-size: 0.88rem; color: var(--pt-ink-muted); line-height: 1.55; }
/* One line from the rail (stage-rail.js notice), atop the stage it lands on. */
.panel-notice { font-size: 0.88rem; color: var(--pt-ink-muted); line-height: 1.55;
                margin: 0; padding-left: 0.75rem; border-left: 2px solid var(--pt-brass); }

.readout { min-height: 108px; }

.bay-set { display: grid; gap: 0.35rem; }
.bay-set-name { margin: 0.4rem 0 0; font-size: 0.8rem; font-weight: 600; color: var(--pt-brass); }
.bay-set .ctl-row { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); align-items: center; gap: 0.6rem; font-size: 0.8rem; }
.bay-set .ctl-row select { min-width: 0; }
.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.3rem 0.85rem; font-size: 0.8rem; }
.kv dt { font-family: var(--pt-font-mono); color: var(--pt-ink-faint); }
.kv dd { margin: 0; font-family: var(--pt-font-mono); color: var(--pt-ink); overflow-wrap: anywhere; }
.kv dd.hi { color: var(--pt-brass); }

.ctl { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ctl button {
  background: var(--pt-surface-2); color: var(--pt-ink-muted);
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  font: inherit; font-size: 0.78rem; padding: 0.32rem 0.65rem; cursor: pointer;
}
.ctl button:hover { border-color: var(--pt-brass-dim); color: var(--pt-ink); }
.ctl button[aria-pressed="true"] { border-color: var(--pt-brass); color: var(--pt-brass); }

.steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; counter-reset: s; }
.steps li {
  display: grid; grid-template-columns: 1.4rem minmax(0, 1fr); gap: 0.65rem;
  padding: 0.55rem 0.65rem; border: 1px solid transparent; border-radius: var(--pt-radius-sm);
  font-size: 0.86rem; line-height: 1.6; color: var(--pt-ink-muted); cursor: pointer;
}
.steps li > span { min-width: 0; }
.steps li::before {
  counter-increment: s; content: counter(s);
  font-family: var(--pt-font-mono); font-size: 0.72rem; color: var(--pt-ink-faint);
  text-align: right;
}
.steps li:hover { border-color: var(--pt-line); }
.steps li[aria-current="step"] { border-color: var(--pt-brass); color: var(--pt-ink); background: var(--pt-brass-wash); }
.steps li[aria-current="step"]::before { color: var(--pt-brass); }
.steps code { color: var(--pt-brass); white-space: nowrap; font-size: 0.9em; }

.legend { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.78rem; color: var(--pt-ink-muted); }
.legend span { display: flex; align-items: center; gap: 0.5rem; }
.legend i { width: 10px; height: 10px; border-radius: 2px; flex: none; }

.show-foot {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: baseline;
  padding: 0.7rem 1.25rem; border-top: 1px solid var(--pt-line);
  background: var(--pt-surface-2);
}
.show-hint { font-size: 0.8rem; color: var(--pt-ink-muted); }
.show-nav { font-size: 0.72rem; color: var(--pt-ink-faint); letter-spacing: 0.02em; }
.show-src { margin-left: auto; font-size: 0.72rem; color: var(--pt-ink-faint); }

/* Overlay drawn into the compiled SVG's own millimeter space. */
/* Rings sit on the faceplate, which is dark gray in both page themes, so they
   use fixed values rather than theme tokens. Halo first, brass on top. */
.pt-ring-halo      { fill: none; stroke: #06080A; stroke-width: 1.9; opacity: 0.85; }
.pt-ring           { fill: none; stroke: #F0C68C; stroke-width: 0.85; }
.pt-ring-soft-halo { fill: none; stroke: #06080A; stroke-width: 1.5; opacity: 0.7; }
.pt-ring-soft      { fill: none; stroke: #E3BC84; stroke-width: 0.7; }
.pt-leader { fill: none; stroke: var(--pt-brass); stroke-width: 0.5; }
.pt-dot { fill: var(--pt-brass); }
.pt-label { font-family: var(--pt-font-mono); font-size: 7px; font-weight: 500; fill: var(--pt-ink); }
.pt-label-sub { font-family: var(--pt-font-mono); font-size: 5px; fill: var(--pt-ink-muted); }
.pt-dim { fill: none; stroke: var(--pt-ink-faint); stroke-width: 0.35; }

/* ── Cards ──────────────────────────────────────────────────────────── */
.cards { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.card {
  border: 1px solid var(--pt-line); border-left: 2px solid var(--pt-brass);
  border-radius: 0 var(--pt-radius) var(--pt-radius) 0;
  background: var(--pt-surface); padding: 1.35rem 1.5rem;
  display: flex; flex-direction: column; gap: 0.7rem;
}
.card h3 { font-size: 1.08rem; font-weight: 600; }
.card p { font-size: 0.9rem; color: var(--pt-ink-muted); }
.card-kicker { color: var(--pt-brass) !important; font-size: 0.85rem !important; }

/* ── How ────────────────────────────────────────────────────────────── */
.how-grid { display: grid; gap: clamp(1.5rem, 4vw, 3rem); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
@media (max-width: 860px) { .how-grid { grid-template-columns: 1fr; } }
.how-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1.5rem; counter-reset: h; }
.how-steps li { display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: 1rem; }
/* The step's content is one wrapped element, so it lands in the second column.
   Left as bare siblings, an <h3> and a <p> become two grid items and the
   paragraph wraps back under the counter, one word wide. */
.how-steps li > div { min-width: 0; }
.how-steps li::before {
  counter-increment: h; content: counter(h, decimal-leading-zero);
  font-family: var(--pt-font-mono); font-size: 0.78rem; color: var(--pt-brass); padding-top: 0.25rem;
}
.how-steps h3 { font-size: 1.05rem; font-weight: 600; margin-bottom: 0.35rem; }
.how-steps p { font-size: 0.9rem; color: var(--pt-ink-muted); }

.how-code { display: flex; flex-direction: column; gap: 0.75rem; }
.code-block { border: 1px solid var(--pt-line); border-radius: var(--pt-radius); background: var(--pt-surface); overflow: hidden; }
.code-cap { padding: 0.5rem 0.9rem; border-bottom: 1px solid var(--pt-line-soft); font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--pt-ink-faint); }
.code-block pre { margin: 0; padding: 0.9rem; overflow-x: auto; font-size: 0.78rem; line-height: 1.6; color: var(--pt-ink-muted); }
.code-arrow { text-align: center; color: var(--pt-brass); font-size: 1.1rem; }
.how-note { font-size: 0.84rem; color: var(--pt-ink-faint); }

/* ── Status ─────────────────────────────────────────────────────────── */
.status-grid { display: grid; gap: clamp(1.5rem, 4vw, 3rem); grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr); }
@media (max-width: 860px) { .status-grid { grid-template-columns: 1fr; } }
.prose { display: flex; flex-direction: column; gap: 1rem; max-width: 62ch; color: var(--pt-ink-muted); }
.prose strong { color: var(--pt-ink); font-weight: 600; }
.facts { margin: 0; display: flex; flex-direction: column; gap: 0.7rem; }
.facts div { display: flex; flex-direction: column; gap: 0.1rem; padding-bottom: 0.7rem; border-bottom: 1px solid var(--pt-line-soft); }
.facts dt { font-family: var(--pt-font-mono); font-size: 0.7rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--pt-ink-faint); }
.facts dd { margin: 0; font-size: 0.88rem; color: var(--pt-ink); }

/* ── Footer ─────────────────────────────────────────────────────────── */
.footer { border-top: 1px solid var(--pt-line-soft); padding: 2.5rem 0 3.5rem; }
.footer-in { display: flex; flex-wrap: wrap; gap: 0.6rem 2rem; align-items: center; font-size: 0.82rem; color: var(--pt-ink-faint); }
.footer-brand { display: flex; align-items: center; gap: 0.5rem; font-family: var(--pt-font-display); font-weight: 700; color: var(--pt-ink); letter-spacing: -0.03em; }
.footer-in p { max-width: 48ch; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* ── 3D dimension overlay ───────────────────────────────────────────────
   An SVG layer over the canvas, redrawn each frame from projected scene
   coordinates. Screen-space so the text stays upright and legible at any
   camera angle. */
.dim-layer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
.dim-witness { fill: none; stroke: var(--pt-brass); stroke-width: 1; opacity: 0.5; }
.dim-line    { fill: none; stroke: var(--pt-brass); stroke-width: 1.2; }
.dim-head    { fill: var(--pt-brass); stroke: none; }
.dim-text {
  font-family: var(--pt-font-mono); font-size: 12px; font-weight: 500;
  fill: var(--pt-brass-light);
  paint-order: stroke; stroke: #0A0C0E; stroke-width: 3px; stroke-linejoin: round;
}

/* Bay occupant pickers on the Rack tab. */
.bay-select {
  background: var(--pt-surface-2); color: var(--pt-ink);
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  font: inherit; font-family: var(--pt-font-mono); font-size: 0.76rem;
  padding: 0.3rem 0.4rem; width: 100%;
}
.bay-select:hover { border-color: var(--pt-brass-dim); }

/* ── Export control ─────────────────────────────────────────────────────
   Top-right of the stage. What comes out is the state on screen: the current
   frame, the applied states, the swapped modules, the drawn callouts. */
.stage-export {
  position: absolute; top: 0.85rem; right: 0.85rem; z-index: 3;
  display: flex; align-items: center; gap: 0.3rem;
}
.stage-export:empty { display: none; }
.stage-export button {
  display: inline-flex; align-items: center; gap: 0.38rem;
  background: color-mix(in srgb, var(--pt-surface) 88%, transparent);
  color: var(--pt-ink-muted);
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  font: inherit; font-family: var(--pt-font-mono);
  font-size: 0.72rem; letter-spacing: 0.04em;
  padding: 0.34rem 0.6rem; cursor: pointer;
  backdrop-filter: blur(6px);
}
.stage-export button:hover { border-color: var(--pt-brass); color: var(--pt-brass); }
.stage-export button[disabled] { opacity: 0.6; cursor: progress; }
.stage-export svg { width: 12px; height: 12px; flex: none; }

/* ── What comes out ─────────────────────────────────────────────────────
   The outputs are the argument of the How section: one authored source,
   several writers over it. Given their own row so the fan-out reads. */
.outputs-head { margin: clamp(2.5rem, 5vw, 3.5rem) 0 1.25rem; }
.outputs {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.out {
  border: 1px solid var(--pt-line); border-top: 2px solid var(--pt-brass);
  border-radius: 0 0 var(--pt-radius) var(--pt-radius);
  background: var(--pt-surface); padding: 1.2rem 1.35rem;
  display: flex; flex-direction: column; gap: 0.6rem;
}
.out h3 { font-size: 1rem; font-weight: 600; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
.out p { font-size: 0.86rem; color: var(--pt-ink-muted); }
.tag {
  font-family: var(--pt-font-mono); font-size: 0.62rem; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--pt-brass); border: 1px solid var(--pt-brass-dim);
  border-radius: 2px; padding: 0.1rem 0.35rem;
}

/* ── Schema browser ─────────────────────────────────────────────────────
   Rendered from the schema documents at runtime. Reuses the showcase shell so
   the two pages read as one site. */
.schema-head {
  padding: 1.5rem clamp(1.1rem, 3vw, 1.75rem) 1.25rem;
  border-bottom: 1px solid var(--pt-line);
  background: var(--pt-surface-2);
  display: flex; flex-direction: column; gap: 0.5rem;
}
.sc-title { font-size: 1.2rem; font-weight: 600; }
.sc-id { font-size: 0.72rem; color: var(--pt-ink-faint); overflow-wrap: anywhere; }
.sc-counts { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.35rem; }

.schema-body { padding: 1.5rem clamp(1.1rem, 3vw, 1.75rem) 2rem; }
.sc-loading { color: var(--pt-ink-faint); font-family: var(--pt-font-mono); font-size: 0.82rem; }

.sc-section-label {
  font-family: var(--pt-font-mono); font-size: 0.7rem;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--pt-ink-faint);
  margin: 2rem 0 0.9rem;
}
.sc-section-label:first-child { margin-top: 0; }

.sc-props { display: flex; flex-direction: column; gap: 0.15rem; }
.sc-prop {
  padding: 0.7rem 0 0.75rem 0.9rem;
  border-left: 1px solid var(--pt-line-soft);
  display: flex; flex-direction: column; gap: 0.4rem;
}
.sc-prop:hover { border-left-color: var(--pt-brass-dim); }

.sc-prop-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
.sc-name { font-family: var(--pt-font-mono); font-size: 0.86rem; color: var(--pt-ink); }
.sc-name.is-req { color: var(--pt-brass); }
.sc-type {
  font-family: var(--pt-font-mono); font-size: 0.7rem;
  color: var(--pt-ink-muted); background: var(--pt-surface-2);
  border: 1px solid var(--pt-line-soft); border-radius: 2px; padding: 0.05rem 0.35rem;
}
.sc-req {
  font-family: var(--pt-font-mono); font-size: 0.6rem; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--pt-brass);
}
.sc-desc { font-size: 0.86rem; color: var(--pt-ink-muted); max-width: 74ch; line-height: 1.6; }

.sc-meta { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.sc-chip, .sc-enum {
  font-family: var(--pt-font-mono); font-size: 0.68rem;
  border: 1px solid var(--pt-line); border-radius: 2px; padding: 0.1rem 0.4rem;
  color: var(--pt-ink-muted); overflow-wrap: anywhere;
}
.sc-chip-k { color: var(--pt-ink-faint); margin-right: 0.35rem; }
.sc-enum { color: var(--pt-brass); border-color: var(--pt-brass-dim); background: var(--pt-brass-wash); }

.sc-ref { font-size: 0.78rem; color: var(--pt-ink-faint); font-family: var(--pt-font-mono); }
.sc-ref-link { color: var(--pt-brass); }

.sc-nest > summary, .sc-raw > summary {
  cursor: pointer; font-family: var(--pt-font-mono); font-size: 0.7rem;
  letter-spacing: 0.06em; color: var(--pt-ink-faint); padding: 0.2rem 0;
}
.sc-nest > summary:hover, .sc-raw > summary:hover { color: var(--pt-brass); }
.sc-nest[open] > summary { color: var(--pt-ink-muted); }
.sc-nest > .sc-props { margin-top: 0.4rem; }

.sc-oneof { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.2rem; }
.sc-branch { border-left: 2px solid var(--pt-brass-dim); padding-left: 0.8rem; }
.sc-branch-head {
  font-family: var(--pt-font-mono); font-size: 0.7rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--pt-brass);
}

.sc-note { color: var(--pt-ink-faint); font-style: italic; }
.sc-note-k {
  font-family: var(--pt-font-mono); font-style: normal; font-size: 0.66rem;
  letter-spacing: 0.08em; text-transform: uppercase; margin-right: 0.3rem;
}
.sc-raw pre {
  margin: 0.35rem 0 0; padding: 0.7rem 0.85rem; overflow-x: auto;
  background: var(--pt-surface-2); border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius-sm);
  font-family: var(--pt-font-mono); font-size: 0.72rem; color: var(--pt-ink-muted);
}

.sc-def { padding-top: 1.1rem; border-top: 1px solid var(--pt-line-soft); margin-top: 1.1rem; }
.sc-def:target { background: var(--pt-brass-wash); border-radius: var(--pt-radius-sm); padding: 1.1rem 0.9rem 0.9rem; }
.sc-def-name { font-size: 1rem; font-weight: 600; display: flex; align-items: baseline; gap: 0.6rem; margin-bottom: 0.45rem; }
.sc-def-name code { font-family: var(--pt-font-mono); color: var(--pt-brass); }
.sc-code {
  font-family: var(--pt-font-mono); font-size: 0.86em;
  color: var(--pt-brass); background: var(--pt-brass-wash);
  border-radius: 2px; padding: 0.05em 0.28em;
  overflow-wrap: anywhere;
}

/* The sponsor credit (roc-ops/portrayal-site#25). RocNet publishes its logo in
 * two variants, white for dark grounds and its primary colors for light ones,
 * so both are served from brand/sponsor/ and switched by the same rules
 * tokens.css uses for the page: dark by default, light by the system setting
 * unless data-theme says dark, or by data-theme="light". */
.footer-sponsor { display: inline-flex; align-items: center; gap: 0.6rem; color: var(--pt-ink-muted); text-decoration: none; }
.footer-sponsor:hover { color: var(--pt-brass); }
.footer-sponsor img { display: block; height: 36px; width: auto; }
.footer-sponsor .on-light, .nav-sponsor .on-light { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) :is(.footer-sponsor, .nav-sponsor) .on-dark { display: none; }
  :root:not([data-theme="dark"]) :is(.footer-sponsor, .nav-sponsor) .on-light { display: block; }
}
:root[data-theme="light"] :is(.footer-sponsor, .nav-sponsor) .on-dark { display: none; }
:root[data-theme="light"] :is(.footer-sponsor, .nav-sponsor) .on-light { display: block; }
.footer-tools { display: flex; flex-wrap: wrap; gap: 0.4rem; color: var(--pt-line); }
.footer-tools a { color: var(--pt-ink-muted); text-decoration: none; }
.footer-tools a:hover { color: var(--pt-brass); }

/* ── Export menu ───────────────────────────────────────────────────────────
 * One nav item instead of one per format. Two exports already crowded the bar
 * and there are more coming - Visio, Lucidchart - so the bar cannot grow a slot
 * each time.
 *
 * <details> rather than a scripted menu: it opens on click and on Enter, closes
 * on Escape, and is reachable by keyboard and screen reader without a line of
 * JavaScript. A <select> would have been fewer characters and the wrong element
 * - these are destinations, not a value.
 */
.navmenu { position: relative; }
.navmenu > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: 0.25rem; color: var(--pt-ink-muted);
}
/* `flex`, not `inline-flex`: an inline box sits on a line in the <details>,
   whose strut is the details' own 1rem, so the label's baseline dropped a
   pixel or two below the links beside it. */
/* The bar sizes its links with rules that match `a`, so a <summary> falls back
   to 1rem and sits visibly larger than everything beside it. */
.masthead-nav .navmenu > summary { font-size: 0.9rem; }
.navmenu > summary::-webkit-details-marker { display: none; }
.navmenu > summary::after {
  content: ''; width: 0; height: 0; margin-top: 1px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor; opacity: 0.7;
}
.navmenu > summary:hover { color: var(--pt-ink); }
.navmenu[data-here] > summary { color: var(--pt-brass); }
.navmenu[open] > summary { color: var(--pt-ink); }
.navmenu-items {
  position: absolute; right: 0; top: calc(100% + 0.5rem); z-index: 60;
  min-width: 13rem; padding: 0.3rem;
  background: var(--pt-surface); border: 1px solid var(--pt-line);
  border-radius: 8px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  display: flex; flex-direction: column;
}
.navmenu-items a {
  display: block; padding: 0.4rem 0.55rem; border-radius: 5px;
  color: var(--pt-ink-muted); text-decoration: none; font-size: 0.82rem;
  white-space: nowrap;
}
.navmenu-items a:hover { background: var(--pt-surface-2); color: var(--pt-ink); }
.navmenu-items a[aria-current="page"] { color: var(--pt-brass); }
.navmenu-items .hint {
  display: block; font-size: 0.7rem; color: var(--pt-ink-faint); margin-top: 0.05rem;
}

/* The masthead has two kinds of link: three that move you around this page, and
   three that open a tool. Ordered Showcase / How it works / Status, then
   Explorer / Export / Schema, the run reads as two groups - this just makes the
   seam visible rather than leaving it to word order. */
.masthead-nav .nav-sep {
  width: 1px; align-self: stretch; margin: 0.15rem 0.25rem;
  background: var(--pt-line);
}

/* ── the hero reveal ──────────────────────────────────────────────────────
   A flat PNG and the compiled SVG of the same drawing, in the same place,
   with a divider between them. Both layers are sized from the same aspect so
   the two halves line up exactly; if they ever drift, the drawing changed and
   the export did not, which is worth seeing rather than hiding. */
.reveal { margin: 1.1rem 0 1.1rem; max-width: 72rem; }
/* DRAGGING A DIVIDER IS NOT SELECTING TEXT. The faceplate carries real
   silkscreen - port numbers, model, legends - and a drag across it was
   sweeping all of it into a selection, which looked like the page had come
   apart. The pointer is here to move the divider. */
/* The strip under the device holds the two labels, "a picture" and
   "addressable". There is no floating callout, so no well above or below. */
.reveal-stage { --split: 50%; position: relative; touch-action: none;
                padding: 0 0 2rem; cursor: ew-resize;
                user-select: none; -webkit-user-select: none; }
/* Holds only the drawing. An overlay pinned to the stage would be offset
   from the image by the stage's padding, which holds the labels. */
.reveal-frame { position: relative; }
/* THE STUDIO, behind the 2D hero as behind the 3D one (feedback: black DAC
   cables vanished on the dark page). A backdrop layer, not the drawings: the
   mask fades only the gray panel's edges into the page, never the device.
   --hero-studio must equal hero-3d.js's STUDIO; check-contract.py pins it. */
.reveal-frame { --hero-studio: #7D848C; }
.reveal-frame::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--hero-studio);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
                      linear-gradient(to bottom, #000 78%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
              linear-gradient(to bottom, #000 78%, transparent);
  mask-composite: intersect; }
.reveal-stage * { user-select: none; -webkit-user-select: none; }
.reveal-png { -webkit-user-drag: none; user-drag: none; }
/* THE FRAME IS THE FACE PLUS ROOM FOR ITS CABLES: 44 mm each side, where
   they fade out, and 48 below, where they hang (hero-reveal.js FACE, SIDE,
   BELOW). The rich SVG's viewBox is that frame; the PNG gets the same room as
   padding - a padding percentage is of the WIDTH, so 44/524 each side and
   48/524 below - which keeps the face at the same place in both, and the
   split on the same millimeter of it. */
.reveal-media { position: relative; aspect-ratio: 524 / 135.7; }
.reveal-png { display: block; width: 100%; height: 100%; object-fit: contain;
              box-sizing: border-box; padding: 0 calc(100% * 44 / 524) calc(100% * 48 / 524); }
.reveal-rich { position: absolute; inset: 0;
               clip-path: inset(0 0 0 var(--split)); }
.reveal-rich svg { display: block; width: 100%; height: 100%; }

/* Wide enough to read as the seam between two pictures rather than as a stray
   hairline in the drawing - the faceplate is full of 1px lines and the divider
   was competing with them. */
.reveal-handle { position: absolute; top: 0; bottom: 0;
  left: var(--split); width: 6px; background: var(--pt-brass);
  transform: translateX(-3px); z-index: 4; pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35); }
.reveal-grip { position: absolute; top: 50%; left: 50%; width: 1.7rem;
  height: 1.7rem; margin: -.85rem 0 0 -.85rem; border-radius: 50%;
  background: var(--pt-brass); box-shadow: 0 2px 10px rgba(0, 0, 0, .45); }
.reveal-grip::before, .reveal-grip::after {
  content: ''; position: absolute; top: 50%; width: 0; height: 0;
  border: .3rem solid transparent; }
.reveal-grip::before { left: .18rem; border-right-color: #16181c;
                       margin-top: -.3rem; }
.reveal-grip::after { right: .18rem; border-left-color: #16181c;
                      margin-top: -.3rem; }
/* The real control, kept for keyboard and screen readers; the handle above is
   what it looks like.
   POINTER-EVENTS OFF, or it is an invisible slider lying across the whole
   drawing - which is exactly what it was. It swallowed every hover before it
   could reach a port, and a click on a port moved the divider instead of
   answering, because the pointer never touched the port at all. The mouse is
   served by the stage's own drag handler; this element is here for the
   keyboard, which pointer-events does not affect. */
.reveal-range { position: absolute; inset: 0; width: 100%;
  opacity: 0; margin: 0; cursor: ew-resize; pointer-events: none; }
.reveal-range:focus-visible { opacity: .001; outline: 2px solid var(--pt-brass);
                              outline-offset: 3px; }

.reveal-tags { position: absolute; left: 0; right: 0; bottom: .4rem;
  display: flex; justify-content: space-between;
  font-size: .74rem; letter-spacing: .06em; text-transform: uppercase;
  font-family: ui-monospace, monospace; }
.reveal-tag.left { color: var(--pt-ink-faint); }
.reveal-tag.right { color: var(--pt-brass); }

/* The hero's cables (hero-reveal.js): hovering, focusing or tapping one
   lights it and dims the rest, and the caption line under the frame names
   it. The cables sit in the SVG, under the divider's handle. */
#hero-reveal [data-hero-cable] { cursor: pointer; transition: opacity 120ms; outline: none; }
#hero-reveal .cables-focus [data-hero-cable]:not(.cable-on) { opacity: .22; }
.hero-caption { font: 500 .8rem/1.4 var(--pt-font-mono); color: var(--pt-ink-muted);
                margin: -.4rem 0 1.1rem; min-height: 1.4em; letter-spacing: .01em; }

/* The same switch in 3D (hero-3d.js): the 2D frame's aspect, the still image
   (the compiled front face, padded as the PNG is) until the scene is built,
   and a one-line note in its place of 3D when there is no WebGL. */
.tagline-3d { margin-top: 1.6rem; }
/* THE EDGES ARE THE PAGE. The scene is one even dark; a mask fades the stage
   to transparent at the sides and softly at the bottom, so the page's own
   ground shows through in either theme - no color is read in JS, and nothing
   goes stale on a theme change. It is also the cables' side fade.
   touch-action pan-y until the panel is clicked (ruling R4): a swipe over it
   scrolls the page. hero-3d.js switches it to none while it is held. */
.hero-3d { position: relative; max-width: 72rem; aspect-ratio: 524 / 135.7;
           margin: .8rem 0 1.1rem; overflow: hidden; }
.hero-3d-stage { position: absolute; inset: 0; touch-action: pan-y;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
                      linear-gradient(to bottom, #000 78%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
              linear-gradient(to bottom, #000 78%, transparent);
  mask-composite: intersect; }
.hero-3d-hint { position: absolute; right: 10%; top: .5rem; margin: 0; pointer-events: none;
                font: 500 .7rem/1.3 var(--pt-font-mono); letter-spacing: .04em;
                color: #9AA1A8; opacity: .75; }
.hero-3d-hint[hidden] { display: none; }
.hero-3d-stage canvas { display: block; }
.hero-3d-still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
                 box-sizing: border-box; padding: 0 calc(100% * 44 / 524) calc(100% * 48 / 524); }
.hero-3d-still[hidden] { display: none; }
.hero-3d-note { position: absolute; left: 0; right: 0; bottom: .3rem; margin: 0; text-align: center;
                font: 500 .74rem/1.3 var(--pt-font-mono); color: var(--pt-ink-muted); }

/* Stages 4-5: cables that hang (patch-view.js / cables.js). Hover or a plan
 * row lights one cable and dims the rest; stage 5 ghosts what its filters
 * leave out. */
[data-cables] [data-cable] { cursor: pointer; transition: opacity 120ms; }
.cable-focus [data-cable]:not(.on), .cable-focus [data-cable-tag]:not(.on) { opacity: 0.18; }
[data-cable].ghost, [data-cable-tag].ghost { opacity: 0.12; }
.cable-tag rect { fill: var(--pt-surface); stroke: var(--pt-line); }
.cable-tag text { font: 500 11px var(--pt-font-mono); fill: var(--pt-ink); }
.cable-tag { cursor: pointer; }
/* An unplugged lead's tag: dashed, and a button only when a stage fixes it. */
.cable-tag.not-connected rect { stroke-dasharray: 3 2; }
.cable-tag.not-connected:not([data-fix-stage]) { cursor: default; }
.cable-tag[data-fix-stage]:hover rect { stroke: var(--pt-ink); }
.cable-legend { display: flex; flex-wrap: wrap; gap: 0.3rem 0.9rem; font-size: 0.78rem; color: var(--pt-ink-muted); }
.cable-legend .swatch { display: inline-block; width: 14px; height: 4px; border-radius: 2px; margin-right: 0.35rem; vertical-align: middle; }
.plan-list { display: grid; gap: 0.5rem; font-size: 0.76rem; }
.plan-list ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.2rem; }
.plan-list .plan-group { margin: 0 0 0.15rem; font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--pt-ink-muted); }
.plan-list button { all: unset; cursor: pointer; font-family: var(--pt-font-mono); color: var(--pt-ink-muted); }
.plan-list button:hover { color: var(--pt-brass); }
.plan-list button:focus-visible { outline: 2px solid var(--pt-brass); outline-offset: 2px; }
.plan-list .loose { color: var(--pt-warn); }

/* ── kit page ─────────────────────────────────────────────────────────────
   The embedder's guide: a sticky contents list beside the sections, and each
   recipe as code over its live result. */
.kit-h1 { font-size: clamp(2rem, 4.5vw, 2.9rem); font-weight: 800; letter-spacing: -0.04em; }
.kit-layout { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 2.5rem; margin-top: 2rem; }
.kit-toc { position: sticky; top: 76px; align-self: start; display: flex; flex-direction: column; gap: 0.45rem;
  font-size: 0.88rem; }
.kit-toc a { color: var(--pt-ink-muted); text-decoration: none; border-left: 2px solid transparent; padding-left: 0.6rem; }
.kit-toc a:hover { color: var(--pt-ink); }
.kit-toc a[aria-current] { color: var(--pt-brass); border-left-color: var(--pt-brass); }
.kit-sec { margin-bottom: 3.2rem; scroll-margin-top: 76px; }
.kit-sec h2 { font-size: 1.45rem; letter-spacing: -0.02em; margin: 0 0 0.6rem; }
.kit-sec > p { color: var(--pt-ink-muted); max-width: 44rem; }
.kit-ex { margin: 1.2rem 0 0; border: 1px solid var(--pt-line); border-radius: var(--pt-radius); overflow: hidden;
  background: var(--pt-surface); }
.kit-ex pre { margin: 0; padding: 1rem 1.1rem; overflow-x: auto; font: 0.8rem/1.55 var(--pt-font-mono);
  background: var(--pt-surface-2); border-bottom: 1px solid var(--pt-line); max-height: 28rem; }
.kit-ex code { font: inherit; color: var(--pt-ink); }
.tk-c { color: var(--pt-ink-faint); font-style: italic; }
.tk-s { color: var(--pt-good); }
.tk-n { color: var(--pt-brass-light); }
.tk-k { color: var(--pt-brass); }
.kit-result { padding: 1rem; min-height: 5rem; }
.kit-result.busy { opacity: 0.55; }
.kit-result svg { display: block; width: 100%; height: auto; }
.kit-result output { display: block; margin-top: 0.6rem; font: 0.85rem var(--pt-font-mono); color: var(--pt-ink-muted); }
.kit-fail { color: var(--pt-warn); font-size: 0.9rem; }
.kit-frame { width: 100%; height: 34rem; border: 0; display: block; }
.kit-bar { display: flex; gap: 0.5rem; justify-content: flex-end; padding: 0.5rem 0.75rem;
  border-top: 1px solid var(--pt-line); }
.kit-bar button { font: 0.8rem var(--pt-font-body); color: var(--pt-ink); background: transparent;
  border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm); padding: 0.25rem 0.7rem; cursor: pointer; }
.kit-bar button:hover { border-color: var(--pt-brass); color: var(--pt-brass); }
@media (max-width: 820px) {
  .kit-layout { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .kit-toc { position: static; flex-direction: row; flex-wrap: wrap; gap: 0.4rem 1rem; }
  .kit-toc a { border-left: 0; padding-left: 0; }
}
.kit-facts { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1rem; margin: 0; font-size: 0.9rem; }
.kit-facts dt { color: var(--pt-ink-faint); font: 0.72rem var(--pt-font-mono); text-transform: uppercase; letter-spacing: 0.08em; padding-top: 0.2rem; }
.kit-facts dd { margin: 0; color: var(--pt-ink); }
.kit-result .kit-build, .kit-result p > button { font: 0.85rem var(--pt-font-body); color: var(--pt-ink);
  background: var(--pt-surface-2); border: 1px solid var(--pt-line); border-radius: var(--pt-radius-sm);
  padding: 0.45rem 0.9rem; cursor: pointer; }
.kit-result .kit-build:hover, .kit-result p > button:hover { border-color: var(--pt-brass); color: var(--pt-brass); }
.kit-snippet { margin: 0.8rem 0; padding: 0.9rem 1rem; background: var(--pt-surface-2); border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius); overflow-x: auto; font: 0.8rem/1.55 var(--pt-font-mono); }
.kit-note { border-left: 2px solid var(--pt-brass); padding-left: 0.8rem; }

/* ── Contributing ───────────────────────────────────────────────────── */
/* The paid-request note sits under the two request cards, not inside one:
   it applies to either route. */
.contrib-paid { margin-top: 2rem; }
.how-steps h3 code { font-size: 0.8em; color: var(--pt-brass); overflow-wrap: anywhere; }
.contrib-list { margin: 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: 0.6rem; }
