/* Demo Locker brochure.
   Palette, font stack and TUI idiom are copied from packages/web/src/index.css
   so the page and the app read as the same object. Deliberately a copy, not a
   shared build — this page is one static file with no pipeline. If the app's
   accent or font changes, update it here too. */

/* --accent has to be a registered property to be animatable: an unregistered
   custom property has no type, so the browser can only flip it between
   keyframes rather than interpolate through them. With a <color> syntax the
   whole page cross-fades. */
@property --accent {
  syntax: "<color>";
  inherits: true;
  initial-value: #3f6;
}

:root {
  --bg: #0a0a0a;
  --fg: #ffffff;
  --fg-dim: #ccc;
  --accent: #3f6;
  /* derived rather than animated alongside --accent, so the tint can never
     drift out of step with the colour it is a tint of. */
  --accent-dim: color-mix(in srgb, var(--accent) 8%, transparent);
  --border: #282828;
  --font: "Berkeley Mono", "JetBrains Mono", "Fira Code", "SF Mono", monospace;

  /* Every accented thing on the page reads var(--accent) — logo, links, the
     [copy] buttons, the list markers, the prompt-box tint, ::selection — so
     animating the one variable moves all of them in step. 4s per colour across
     the seven accents the app ships, plus a wrap back to the first so the loop
     has no visible seam. Was 6s (42s total); DL asked for faster 2026-08-03.
     The keyframe stops are percentages, so this one number is the only knob —
     changing it keeps every colour's share of the loop equal. */
  animation: accent-cycle 28s linear infinite;
}

/* Ordered around the hue wheel, NOT in the app's ACCENTS order. Registered
   custom properties interpolate in sRGB, and a straight line in sRGB between
   two near-opposite hues runs through grey — the app's order puts amber (32deg)
   next to blue (207deg), and the logo spent a second of every loop a muddy
   khaki. Sorted by hue the largest gap is 87deg (gold to green, through
   yellow-green), so the cycle stays saturated the whole way round. Keep any new
   accent in hue order for the same reason.

   The wheel is entered at green (DL's pick, 2026-07-30) rather than at the
   lowest hue: the list is rotated, not re-sorted, so every adjacent pair — and
   therefore every interpolation — is the one hue order already vetted. The
   brochure resting on green while the app still defaults to gold is deliberate;
   don't "resync" them. */
@keyframes accent-cycle {
  0%      { --accent: #3f6; } /* 135deg green  */
  14.286% { --accent: #6cf; } /* 200deg cyan   */
  28.571% { --accent: #4af; } /* 207deg blue   */
  42.857% { --accent: #a6f; } /* 267deg purple */
  57.143% { --accent: #f6a; } /* 333deg pink   */
  71.429% { --accent: #f80; } /*  32deg amber  */
  85.714% { --accent: #fc0; } /*  48deg gold   */
  100%    { --accent: #3f6; }
}

/* A page-wide colour cycle with no off switch is exactly what this query is
   for. Holding the initial green leaves every accent rule working untouched. */
@media (prefers-reduced-motion: reduce) {
  :root {
    animation: none;
  }
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  background: var(--bg);
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

::selection {
  background: var(--accent);
  color: var(--bg);
}

a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

a:hover,
a:focus-visible {
  border-bottom-color: var(--accent);
}

.wrap {
  max-width: 880px;
  margin: 0 auto;
  padding: 2.5rem 1.625rem 4rem;
}

/* NB `container-type: inline-size` makes this element's width independent of
   its contents, so it must never sit in a content-sized box — as a flex item it
   would collapse to zero and take the art with it. That shipped in the app once.
   Safe here because it's in a plain block context, but the guard is cheap. */
.ascii-fit {
  container-type: inline-size;
  width: 100%;
  min-width: 0;
}

/* The wordmark is an <svg> of cells now, not a <pre> of block characters, so
   this is a width rather than a font-size. See the comment in index.html.

   What that removed: a `clamp(4px, 100cqw / (cols * 0.61), 11px)` font-size, a
   4px floor, and a note about measuring 265px of art into a 268px box. Every
   one of those was managing the same defect — keeping the glyph cell large
   enough that mismatched font advances were not obvious — and none of them
   could fix it, because the cause was font metrics. An SVG has no glyphs to
   mis-advance, scales exactly at any width, and cannot clip its own art.

   738.1px is the old 11px cap expressed as a width: 110 columns x the 0.61
   advance x 11px. Keeping it means the mark is the same size it has always
   been on a desktop.

   --fit is the fraction of the container the art may occupy. At 1 it spans
   edge to edge, which on a phone lets the logo dominate the page; the mobile
   query below pulls it in. margin-inline centres what is left over. */
.ascii-fit > .ascii-logo {
  --fit: 1;
  display: block;
  width: min(calc(100% * var(--fit)), 738.1px);
  height: auto;
  margin-inline: auto;
}

.ascii-logo {
  color: var(--accent);
  user-select: none;
}

.tagline {
  margin-top: 1.75rem;
  font-size: 15px;
  line-height: 1.7;
}

.tagline em {
  color: var(--accent);
  font-style: normal;
}

section {
  margin-top: 3rem;
}

.label {
  color: var(--fg-dim);
  font-size: 11px;
  font-weight: normal;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.25rem;
}

/* Lead line under "hear one". Matches the tagline's measure rather than the
   list styles, since it is prose and not an item. */
.hear-lead {
  color: var(--fg-dim);
  line-height: 1.7;
  margin-bottom: 0.75rem;
}

ul.plain {
  list-style: none;
}

ul.plain > li {
  padding: 0.35rem 0;
}

/* the either/or under the recommendation — indented, dimmed, dashes in the
   accent so it reads as a nested aside. */
ul.sub {
  list-style: none;
  margin-top: 0.35rem;
  color: var(--fg-dim);
}

ul.sub li {
  padding-left: 1.5rem;
  text-indent: -1.5rem;
}

ul.sub li::before {
  content: "- ";
  color: var(--accent);
}

ul.plain strong {
  color: var(--accent);
  font-weight: normal;
}

.cta-lead {
  color: var(--fg-dim);
  margin-bottom: 0.9rem;
}

/* the hinge between the ways in — reads as another lead-in to the box below
   it, so it matches .cta-lead rather than sitting centred between them. */
.or {
  color: var(--fg-dim);
  margin: 1.75rem 0 0.9rem;
}

/* trailing note under the last CTA. Smaller and dimmer than .or so it reads
   as a footnote to the docker box, not as a fourth way in. */
.fineprint {
  color: var(--fg-dim);
  font-size: 12px;
  line-height: 1.7;
  margin-top: 0.9rem;
}

/* The demo video, and the caption under it.

   No `alt` exists on <video>, so the description is a real <figcaption>:
   visible to everyone, and useful copy rather than an accessibility tax. */
.demo {
  margin: 3rem 0 0;
}

.demo-video {
  display: block;
  width: 100%;
  max-width: 700px;
  height: auto;
  border: 1px solid var(--border);
  background: var(--bg);
}

.demo figcaption {
  color: var(--fg-dim);
  font-size: 12px;
  line-height: 1.7;
  margin-top: 0.75rem;
  max-width: 700px;
}

/* --- install tabs ---------------------------------------------------------
   Radios drive this, not JS and not the ARIA tabs pattern: arrow keys, focus
   and the checked state all come from the browser, and it still works with
   copy.js blocked.

   The inputs are visually hidden but NOT `display:none` — a display-none radio
   is not focusable, which would take the keyboard support that is the whole
   reason for choosing radios. */
.tab-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.tabs {
  display: flex;
  gap: 1.25rem;
  margin-bottom: 0.9rem;
  flex-wrap: wrap;
}

.tabs label {
  color: var(--fg-dim);
  font-size: 13px;
  cursor: pointer;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}

.tabs label:hover {
  color: var(--fg);
}

#m-agent:checked ~ .tabs label[for="m-agent"],
#m-npx:checked ~ .tabs label[for="m-npx"],
#m-docker:checked ~ .tabs label[for="m-docker"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Focus lives on the hidden input, so the ring has to be painted on the label
   it controls — otherwise keyboard users move through an invisible control. */
#m-agent:focus-visible ~ .tabs label[for="m-agent"],
#m-npx:focus-visible ~ .tabs label[for="m-npx"],
#m-docker:focus-visible ~ .tabs label[for="m-docker"] {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Panels: only the checked one is laid out. Hidden ones are display:none so
   their text is out of the accessibility tree and out of find-in-page, rather
   than three snippets stacked invisibly on top of each other. */
.panel,
.panel-note {
  display: none;
}

#m-agent:checked ~ .prompt-box .p-agent,
#m-npx:checked ~ .prompt-box .p-npx,
#m-docker:checked ~ .prompt-box .p-docker {
  display: block;
}

#m-agent:checked ~ .n-agent,
#m-npx:checked ~ .n-npx,
#m-docker:checked ~ .n-docker {
  display: block;
}

/* the copyable prompt — the main event on this page */
.prompt-box {
  border: 1px solid var(--border);
  background: var(--accent-dim);
  padding: 1rem;
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
}

.prompt {
  font-family: var(--font);
  color: var(--fg);
  font-size: 13px;
  line-height: 1.7;
  white-space: pre-wrap;
  word-break: break-word;
  flex: 1 1 22ch;
  min-width: 0;
}

.btn {
  font-family: var(--font);
  font-size: 13px;
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
  flex: 0 0 auto;
}

.btn:hover,
.btn:focus-visible {
  color: var(--fg);
}

.btn[data-copied="true"] {
  color: var(--fg);
}

footer {
  margin-top: 4rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.colophon {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* the icon is the whole link, so it gets no underline rule — the hover cue is
   the color flip instead. line-height:0 keeps the svg from riding above the
   text baseline it sits next to. */
.icon-link {
  border-bottom: none;
  line-height: 0;
  flex: 0 0 auto;
}

.icon-link:hover,
.icon-link:focus-visible {
  color: var(--fg);
  border-bottom: none;
}

@media (max-width: 640px) {
  .wrap {
    padding: 1.75rem 1rem 3rem;
  }
  .tagline {
    font-size: 14px;
  }
  .prompt-box {
    gap: 0.75rem;
  }
  .ascii-fit > .ascii-logo {
    --fit: 0.85;
  }
}
