/* PredictiveX — Embed Walkthrough.
 *
 * Brand: PredictiveX V1 (Aug 2026) brand board. Palette, typefaces and the
 * mark are taken from it directly; every value below that is NOT on the board
 * is marked "derived" with the reason, so the boundary between brand and
 * interpretation stays visible.
 *
 * Two colour systems live in this app and they are deliberately separate:
 *   1. This file  — the PredictiveX product chrome.
 *   2. catalog.js — the nine T0 tokens the PUBLISHER sets for their own
 *      widget. Those are the customer's brand, not ours, and their defaults
 *      are untouched by this revision.
 */

/* Brand faces are loaded via <link> in index.html, not @import: @import
 * serialises the request behind this stylesheet, and Fontshare only honours
 * the first f[] parameter per request, so the two faces need separate URLs. */

:root {
  /* ---------------------------------------------- brand palette (verbatim) */
  --ultra-violet: #6c3bff;
  --violet-light: #a77bff;
  --violet-deep: #3f12ae;
  --signal-lime: #c8ff4d;
  --ink: #070609;
  --ink-raised: #150c33;
  --slate: #8b84a8;
  --white: #ffffff;

  /* ------------------------------------------------------ surface ramp
   * Derived. The board gives two darks — Ink and Ink Raised — which is not
   * enough steps for a UI with cards inside cards. The intermediate values
   * interpolate between them in the same violet-tinted hue so the ramp reads
   * as one family rather than grey with a violet card dropped into it. */
  --bg: #070609;          /* Ink, verbatim */
  --surface-1: #0d0a1c;   /* derived */
  --surface-2: #150c33;   /* Ink Raised, verbatim */
  --surface-3: #1d1244;   /* derived */
  --border: #241a4a;      /* derived */
  --border-strong: #33246b;/* derived */

  /* ------------------------------------------------------------------ text
   * --text is a hair off pure white: at 14px on near-black, #FFFFFF haloes.
   * Pure White is kept for headings, where the extra weight is wanted. */
  --text: #f3f0ff;        /* derived from White */
  --text-strong: #ffffff; /* White, verbatim */

  /* Three quiet text levels, all of which have to clear 4.5:1 — this UI puts
   * real content (help text, timestamps, insertion-point descriptors, key
   * names) at every one of them, so none of them qualify for the
   * "incidental text" exemption.
   *
   * Solving for the darkest tint in Slate's own hue that clears 4.5:1 on all
   * five surfaces returns Slate itself, at 4.51:1 against --border. Slate is
   * therefore the FLOOR, not the middle: it takes the faint level, and the
   * middle level is a derived lighter tint. The previous --text-faint was
   * #635c80 at 3.06:1 and was failing on 19 elements across the flow. */
  --text-muted: #aba6c0;  /* derived, Slate mixed 28% to White — 6.77:1 worst case */
  --text-faint: #8b84a8;  /* Slate, verbatim — 4.51:1 worst case */
  /* Disabled controls only. WCAG explicitly exempts them, and nothing that
   * carries meaning may use this. */
  --text-disabled: #504b60;

  /* ----------------------------------------------------------- accent
   * Ultra Violet is only 3.57:1 on Ink. That clears the 3:1 bar for UI
   * components and borders but FAILS the 4.5:1 bar for text, so the two uses
   * are separate tokens rather than one token used carelessly:
   *   --accent       fills and borders. White on it is 5.66:1.
   *   --accent-text  links and accented words. 6.66:1 on Ink. */
  --accent: #6c3bff;         /* Ultra Violet, verbatim */
  --accent-hover: #7f54ff;   /* derived */
  --accent-active: #5a27f0;  /* derived */
  --accent-quiet: #1a0f3d;   /* derived — tinted fill behind selected rows */
  --accent-text: #a77bff;    /* Violet Light, verbatim */
  --on-accent: #ffffff;      /* White on Ultra Violet = 5.66:1 */

  /* ------------------------------------------------------------- status
   * On the board, lime is the attention colour: the LIVE chip and the HIGH
   * badge. It is used here for live/positive and for the focus ring, and
   * nowhere else, so it keeps that meaning.
   * The board has no red and no amber. Both are required by this UI (the
   * contrast gate rejects, the clamp warns), so they are derived to sit at
   * the same high chroma and lightness as Signal Lime rather than pulled
   * from a generic palette. */
  --positive: #c8ff4d;    /* Signal Lime, verbatim — 17.20:1 on Ink */
  --negative: #ff5c8a;    /* derived — 6.63:1 on Ink */
  --warning: #ffb84d;     /* derived — 11.13:1 on Ink */
  --on-status: #070609;   /* Ink on lime = 17.20:1 */
  --focus-ring: #c8ff4d;  /* Signal Lime — highest-contrast option available */

  /* ------------------------------------------------------------ geometry
   * Derived. The board specifies no radius. The mark is built from straight
   * constant-width strokes with hard corners, so the UI stays tight rather
   * than rounded; 4px reads as intentional at this density without fighting
   * the mark. */
  --radius: 4px;
  --radius-lg: 8px;

  /* ------------------------------------------------------------------ type
   * Board: "Chillax Medium — wordmark, headlines, display." and "Sora Medium
   * — labels, data, odds. Tracked out 0.12-0.22em in caps." Body prose and
   * controls therefore take Sora; Chillax is reserved for display, which is
   * what keeps it feeling like a display face. */
  --font-display: "Chillax", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-sans: "Sora", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "JetBrains Mono", "SF Mono", "Cascadia Code", Consolas, "Liberation Mono", monospace;

  /* Caps tracking, from the board's 0.12-0.22em range. Tighter as size
     grows, which is how tracking actually has to work. */
  --track-caps: 0.2em;
  --track-caps-tight: 0.13em;

  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;

  --shell: 1400px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
  border-color: var(--border);
}

html {
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4,
p {
  margin: 0;
}

/* Board: "Chillax Medium — wordmark, headlines, display." Medium is 500, so
   headings sit at 500 and lean on size and tracking for hierarchy rather
   than on weight. Chillax is loaded at 500/600 only; asking for 660 as the
   old system did would make the browser synthesise a fake bold. */
h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--text-strong);
}

/* Board: "Sora Medium — labels, data, odds. Tracked out 0.12-0.22em in
   caps." One class, used everywhere a tracked-out cap label is wanted. */
.capline {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Numbers must not reflow as they tick. */
.num,
.facts b,
.facts strong {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

a {
  color: var(--accent-text);
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 3px;
}

::selection {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-thumb {
  background: var(--surface-3);
  border-radius: 6px;
}
::-webkit-scrollbar-track {
  background: transparent;
}

.nums {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: var(--on-accent);
  padding: 10px 14px;
  border-radius: var(--radius);
  font-weight: 650;
}
.skip:focus {
  left: 12px;
  top: 12px;
}

/* ============================================================ top bar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--s4);
  height: 54px;
  padding: 0 var(--s5);
  /* Near-opaque rather than translucent: backdrop-filter is a progressive
     enhancement and content must never read through the bar without it. */
  background: color-mix(in srgb, var(--bg) 97%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
/* A single hairline in the mark's gradient, so the brand reads even above
   the fold on a page with no hero image. */
.topbar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--violet-light) 22%, transparent 55%);
  pointer-events: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  flex: 0 0 auto;
  /* The lockup itself is 21 px tall, which is under the 24 x 24 CSS-pixel
     floor in WCAG 2.5.8. Padding rather than a min-height so the mark keeps
     its optical position on the topbar baseline. */
  padding: 3px 0;
}

/* The lockup, reproduced from predictivex-lockup-ink.svg rather than
   approximated. In that file the wordmark is set at font-size 200 with the
   baseline at y=150, and the X box is 150 tall with its bottom edge at
   y=158 — 8px past the baseline. As em fractions that is exactly a 0.75em
   box hung 0.04em below the baseline, which is what the two values below
   are. Setting it in live text instead of shipping the SVG's <text> element
   means it inherits colour (the supplied file is hard-coded to #0B0818 ink
   and would be invisible on this dark shell) and stays selectable. */
.wordmark {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--text-strong);
  white-space: nowrap;
}
.wordmark .xmark {
  height: 0.75em;
  width: 0.75em;
  vertical-align: -0.04em;
  margin-left: 0.035em;
}

/* The generic .tag class further down this file carries a surface fill,
   border and asymmetric padding. Reset the box explicitly rather than
   partially overriding it, which is what left the product tag with 10px of
   left padding against 7px of right. */
.brand .tag {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 9.5px;
  line-height: 1;
  letter-spacing: var(--track-caps-tight);
  text-transform: uppercase;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 5px calc(9px - var(--track-caps-tight)) 4px 9px;
  margin: 0;
}

.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: 0 0 auto;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Tracked-out caps put a full letter-space after the last character. In a
     bordered pill that reads as lopsided padding and, in the top bar, it was
     also 1.6px of real width — enough to push the document into horizontal
     scroll at 375px. Right padding is reduced by exactly one letter-space to
     cancel it. */
  padding: 4px calc(10px - var(--track-caps-tight)) 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--track-caps-tight);
  text-transform: uppercase;
  white-space: nowrap;
}
.chip .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--positive);
  flex: 0 0 auto;
}
.chip.demo .dot {
  background: var(--warning);
}

/* ============================================================ layout */

.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s6) var(--s5) var(--s7);
}

.intro {
  max-width: 62ch;
  margin-bottom: var(--s5);
}
.eyebrow {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* A short rule after the eyebrow, in the mark's gradient. This is the only
   place the gradient appears outside the logo itself. */
.eyebrow::after {
  content: "";
  height: 1px;
  width: 64px;
  background: linear-gradient(90deg, var(--accent), transparent);
}
h1 {
  font-size: clamp(27px, 3.1vw, 37px);
  line-height: 1.06;
  letter-spacing: -0.028em;
  margin-bottom: 10px;
  text-wrap: balance;
}
.lede {
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.6;
  max-width: 64ch;
}

/* In a five-step wizard the marketing hero is only doing work on step 1.
   Past that it was pushing the thing you actually came to use down by a
   quarter of the viewport, so it condenses: the lede goes, the headline
   drops to a caption. Driven off the stepper's own aria-current via :has(),
   so there is no new state to keep in sync. */
.intro {
  transition: margin-bottom 0.22s ease;
}
.intro h1 {
  transition: font-size 0.22s ease, margin-bottom 0.22s ease;
}
.shell:not(:has(#stepper [data-go="1"][aria-current="step"])) .intro {
  margin-bottom: var(--s5);
}
.shell:not(:has(#stepper [data-go="1"][aria-current="step"])) .intro .lede {
  display: none;
}
.shell:not(:has(#stepper [data-go="1"][aria-current="step"])) .intro h1 {
  font-size: clamp(19px, 2vw, 23px);
  letter-spacing: -0.02em;
  margin-bottom: 0;
  /* Not --text-muted: dimming the h1 below its own eyebrow inverts the
     hierarchy. It gets smaller, not quieter. */
  color: var(--text);
}
.shell:not(:has(#stepper [data-go="1"][aria-current="step"])) .intro .eyebrow {
  margin-bottom: 8px;
}
@media (prefers-reduced-motion: reduce) {
  .intro,
  .intro h1 {
    transition: none;
  }
}

/* two-column work area: steps on the left, sticky preview on the right */
.work {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 560px;
  gap: var(--s5);
  align-items: start;
}

/* ============================================================ stepper */

.stepper {
  display: flex;
  gap: 2px;
  margin-bottom: var(--s5);
  padding: 4px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow-x: auto;
}
.stepper button {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  min-height: 44px; /* touch target */
  padding: 8px 11px;
  background: transparent;
  border: 0;
  border-radius: 3px;
  color: var(--text-faint);
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: var(--track-caps-tight);
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  text-align: left;
  transition: background 0.15s, color 0.15s;
}
.stepper button:hover:not(:disabled) {
  color: var(--text);
  background: var(--surface-2);
}
.stepper button:disabled {
  cursor: not-allowed;
  color: var(--text-disabled);
  opacity: 1;
}
.stepper button[aria-current="step"] {
  background: var(--surface-3);
  color: var(--text-strong);
}
/* Squared rather than circular: the mark is built from hard-cornered
   constant-width strokes, and round badges fought it. */
.stepper .n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  border: 1px solid var(--border-strong);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.stepper button[aria-current="step"] .n {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
/* Lime is the board's attention/confirmed colour — same role as its LIVE
   chip and HIGH badge. Cleared steps get it; nothing else in the stepper. */
.stepper button.done .n {
  background: color-mix(in srgb, var(--positive) 18%, transparent);
  border-color: color-mix(in srgb, var(--positive) 55%, transparent);
  color: var(--positive);
}
.stepper button.done:not([aria-current="step"]) {
  color: var(--text-muted);
}
.stepper .lbl {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============================================================ panels */

.panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.panel + .panel {
  margin-top: var(--s3);
}
.panel-hd {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  padding: 10px var(--s4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.panel-hd h2,
.panel-hd h3 {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text-strong);
  letter-spacing: -0.005em;
}
.panel-hd .hint {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--text-faint);
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* On narrow screens the hint gets its own line at full width instead of
   being squeezed to an ellipsis next to the heading. */
@media (max-width: 560px) {
  .panel-hd {
    flex-wrap: wrap;
    align-items: flex-start;
  }
  .panel-hd .hint {
    margin-left: 0;
    flex: 1 1 100%;
    white-space: normal;
  }
}
.panel-bd {
  padding: 14px var(--s4);
}
.panel-bd > * + * {
  margin-top: 14px;
}

.step {
  display: none;
}
.step.active {
  display: block;
  animation: rise 0.24s cubic-bezier(0.22, 0.8, 0.3, 1);
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.step-title {
  font-size: 17.5px;
  font-weight: 650;
  letter-spacing: -0.018em;
  color: var(--text-strong);
  margin-bottom: 6px;
}
.step-sub {
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.55;
  max-width: 64ch;
  margin-bottom: 13px;
}

/* ============================================================ dropzone */

.drop {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  padding: 26px var(--s5);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-1);
  transition: border-color 0.18s, background 0.18s;
}
.drop.over {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface-1));
}
.drop svg {
  color: var(--text-faint);
}
.drop.over svg {
  color: var(--accent-text);
}
.drop b {
  color: var(--text-strong);
  font-size: 14.5px;
  font-weight: 620;
}
.drop p {
  color: var(--text-muted);
  font-size: 12.5px;
  max-width: 44ch;
}
.drop input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

/* ============================================================ file card */

.filecard {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.filecard .ic {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 5px;
  background: color-mix(in srgb, var(--positive) 16%, transparent);
  color: var(--positive);
  flex: 0 0 auto;
}
.filecard .nm {
  font-weight: 620;
  color: var(--text-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filecard .mt {
  font-size: 12px;
  color: var(--text-muted);
}
.filecard .grow {
  min-width: 0;
  flex: 1 1 auto;
}

/* ======================================================== sample picker */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.samples {
  margin-top: var(--s4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  overflow: hidden;
}
.samples-head {
  padding: 10px var(--s4);
  border-bottom: 1px solid var(--border);
}
.samples-title {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.samples-hint {
  display: block;
  margin-top: 5px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 62ch;
}

.samplelist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.samplelist li {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px var(--s4);
  border-bottom: 1px solid var(--border);
  transition: background 0.15s ease;
}
.samplelist li:hover {
  background: var(--surface-3);
}
.samplelist li:last-child {
  border-bottom: 0;
}
.samplelist .grow {
  min-width: 0;
  flex: 1 1 auto;
}
.samplelist .nm {
  display: block;
  font-weight: 620;
  color: var(--text-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.samplelist .mt {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A 3-stripe chip that previews each sample page's palette, so the choice is
   visual rather than a wall of identical rows. */
.samplelist .sw {
  display: flex;
  flex-direction: column;
  width: 26px;
  height: 30px;
  flex: 0 0 auto;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  overflow: hidden;
}
.samplelist .sw i {
  display: block;
  flex: 1 1 auto;
}
.sw-rundown i:nth-child(1) {
  background: #faf9f7;
  flex-grow: 2;
}
.sw-rundown i:nth-child(2) {
  background: #a8342b;
}
.sw-rundown i:nth-child(3) {
  background: #e6e2db;
}
.sw-baseline i:nth-child(1) {
  background: #12161c;
}
.sw-baseline i:nth-child(2) {
  background: #0b6b5e;
}
.sw-baseline i:nth-child(3) {
  background: #ffffff;
  flex-grow: 2;
}
.sw-curve i:nth-child(1) {
  background: #c9a227;
}
.sw-curve i:nth-child(2) {
  background: #14161a;
  flex-grow: 2;
}
.sw-curve i:nth-child(3) {
  background: #1b1e24;
}

/* Two of these are <a download>, one is a <button> that builds a Blob, so the
   rule has to reset button chrome as well as link chrome. */
/* 2px/3px of vertical padding lifts these from 20px to 25px, clearing the
   WCAG 2.5.8 floor. The underline is a border-bottom, so the bottom pad
   also buys the text a little breathing room from the rule. (#47) */
.samplelist .dl {
  flex: 0 0 auto;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 2px 0 3px;
  cursor: pointer;
}
.samplelist .dl:hover {
  color: var(--accent-text);
  border-bottom-color: var(--accent);
}
.samplelist .dl:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.samples-foot {
  margin: 0;
  padding: 9px var(--s4);
  border-top: 1px solid var(--border);
  background: var(--surface-1);
  font-size: 12px;
  line-height: 1.55;
  color: var(--text-faint);
}

@media (max-width: 560px) {
  /* This used to wrap the row and push .dl to order 3, which orphaned
     "Download" alone on a second line while "Load" stayed up on the first —
     the two actions for one row reading as two unrelated things. There is
     enough width for both if the text is allowed to truncate, which it
     already does, so the row stays on one line and matches the rhythm of the
     live-page rows above it. */
  .samplelist li {
    flex-wrap: nowrap;
    gap: 9px;
  }
  .samplelist .grow {
    flex: 1 1 0;
    min-width: 0;
  }
  .samplelist .dl {
    flex: 0 0 auto;
  }
  .samplelist .btn {
    flex: 0 0 auto;
  }
  /* Side by side, "SAMPLE PAGES" wrapped to two lines and its descriptor
     wrapped to two more, so the label read as four ragged lines. Stack them:
     the label keeps one line, the descriptor gets the width it needs. */
  .grouplbl {
    display: block;
    padding-bottom: 8px;
  }
  .grouplbl span {
    display: block;
    margin-top: 2px;
  }
}

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.fact {
  background: var(--surface-1);
  padding: var(--s3) var(--s3);
}
.fact .v {
  font-size: 19px;
  font-weight: 650;
  color: var(--text-strong);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.fact .l {
  font-size: 11px;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.055em;
  font-weight: 600;
  margin-top: 3px;
}

/* ============================================================ widget cards */

/* Both grids hold exactly four cards. auto-fit gave 3 + 1, which left an
   orphan on its own row beside two empty cells and read like a bug. A fixed
   2x2 is a deliberate composition and gives each card room for its
   description. */
.wgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
}
@media (max-width: 720px) {
  .wgrid {
    grid-template-columns: minmax(0, 1fr);
  }
}
.wcard {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--s4);
  text-align: left;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: border-color 0.16s, background 0.16s, transform 0.16s;
}
.wcard:hover {
  border-color: var(--border-strong);
  background: var(--surface-2);
  transform: translateY(-1px);
}
.wcard[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-1));
}
.wcard .top {
  display: flex;
  align-items: center;
  gap: 8px;
}
.wcard .nm {
  font-weight: 650;
  color: var(--text-strong);
  font-size: 14px;
  letter-spacing: -0.01em;
}
.wcard .bl {
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.5;
}
.wcard .tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: auto;
  padding-top: 4px;
}
.wcard .check {
  position: absolute;
  top: 10px;
  right: 10px;
  display: none;
  color: var(--accent-text);
}
.wcard[aria-pressed="true"] .check {
  display: block;
}

.tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: 3px;
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.tag.acc {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  color: var(--accent-text);
}
.tag.warn {
  background: color-mix(in srgb, var(--warning) 14%, transparent);
  border-color: color-mix(in srgb, var(--warning) 40%, transparent);
  color: var(--warning);
}
.tag.mute {
  background: transparent;
}

/* locked upsell cards */
.wcard.locked {
  cursor: not-allowed;
  opacity: 0.6;
}
.wcard.locked:hover {
  transform: none;
  border-color: var(--border);
  background: var(--surface-1);
}
.wcard.locked .nm {
  color: var(--text-muted);
}

/* ============================================================ form */

.fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--s4) var(--s4);
}
.field {
  min-width: 0;
}
.field.wide {
  grid-column: 1 / -1;
}
.field label,
.field .flabel {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12px;
  font-weight: 620;
  color: var(--text);
  margin-bottom: 6px;
}
.field .val {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}
.field .help {
  font-size: 11.5px;
  color: var(--text-faint);
  line-height: 1.45;
  margin-top: 5px;
}

input[type="text"],
input[type="number"],
input[type="url"],
select,
textarea {
  width: 100%;
  padding: 7px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font: inherit;
  font-size: 13px;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="url"]:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
select {
  cursor: pointer;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-faint) 50%),
    linear-gradient(135deg, var(--text-faint) 50%, transparent 50%);
  background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 28px;
}
textarea {
  resize: vertical;
  min-height: 58px;
  line-height: 1.5;
}

/* 24px, not 20, because WCAG 2.5.8 measures the whole control as the
   target and 20 was under the AA floor (#47). The thumb is still 14px,
   so nothing looks different — the extra 4px is hit area, absorbed by
   the track being vertically centred. */
input[type="range"] {
  width: 100%;
  height: 24px;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--surface-3);
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -5px; /* (4px track - 14px thumb) / 2 */
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
}
input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--surface-3);
}
input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
}

/* segmented control */
.seg {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  width: 100%;
}
.seg button {
  flex: 1 1 0;
  padding: 6px 8px;
  background: transparent;
  border: 0;
  border-radius: 3px;
  color: var(--text-muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.14s, color 0.14s;
}
.seg button:hover {
  color: var(--text);
}
.seg button[aria-pressed="true"] {
  background: var(--surface-3);
  color: var(--text-strong);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* toggle */
.tog {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  font-size: 12.5px;
  color: var(--text);
  font-weight: 600;
}
.tog input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.tog .track {
  width: 34px;
  height: 19px;
  border-radius: 999px;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  transition: background 0.16s, border-color 0.16s;
  flex: 0 0 auto;
}
.tog .track::after {
  content: "";
  display: block;
  width: 13px;
  height: 13px;
  margin: 2px;
  border-radius: 50%;
  background: var(--text-muted);
  transition: transform 0.16s, background 0.16s;
}
.tog input:checked + .track {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  border-color: var(--accent);
}
.tog input:checked + .track::after {
  transform: translateX(15px);
  background: var(--accent);
}
.tog input:focus-visible + .track {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* multi-select chips */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chips button {
  padding: 4px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.14s;
}
.chips button:hover {
  color: var(--text);
  border-color: var(--border-strong);
}
.chips button[aria-pressed="true"] {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: var(--accent);
  color: var(--accent-text);
}

/* colour field */
.cfield {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cfield input[type="color"] {
  width: 34px;
  height: 32px;
  padding: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  cursor: pointer;
  flex: 0 0 auto;
}
.cfield input[type="text"] {
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
}
.cfield .ratio {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 7px;
  border-radius: 4px;
  white-space: nowrap;
}
.ratio.pass {
  background: color-mix(in srgb, var(--positive) 16%, transparent);
  color: var(--positive);
}
.ratio.fail {
  background: color-mix(in srgb, var(--negative) 16%, transparent);
  color: var(--negative);
}

/* ============================================================ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 34px;
  padding: 7px 14px;
  border-radius: 5px;
  border: 1px solid transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 620;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, color 0.15s, opacity 0.15s;
}
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-hover);
}
.btn-ghost {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
.btn-ghost:hover:not(:disabled) {
  background: var(--surface-3);
  border-color: var(--border-strong);
}
.btn-quiet {
  background: transparent;
  color: var(--text-muted);
  padding-inline: 8px;
}
.btn-quiet:hover:not(:disabled) {
  color: var(--text);
}
.btn-sm {
  min-height: 28px;
  padding: 4px 10px;
  font-size: 12px;
}

.actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.actions .spacer {
  flex: 1 1 auto;
}
.actions .note {
  font-size: 12px;
  color: var(--text-faint);
}

/* ============================================================ callouts */

.callout {
  display: flex;
  gap: 10px;
  padding: var(--s3) var(--s4);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-muted);
}
.callout svg {
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--text-faint);
}
.callout b {
  color: var(--text-strong);
  font-weight: 620;
}
.callout.lock {
  background: color-mix(in srgb, var(--warning) 7%, var(--surface-1));
  border-color: color-mix(in srgb, var(--warning) 28%, transparent);
}
.callout.lock svg {
  color: var(--warning);
}
.callout.good {
  background: color-mix(in srgb, var(--positive) 7%, var(--surface-1));
  border-color: color-mix(in srgb, var(--positive) 26%, transparent);
}
.callout.good svg {
  color: var(--positive);
}
.callout.bad {
  background: color-mix(in srgb, var(--negative) 8%, var(--surface-1));
  border-color: color-mix(in srgb, var(--negative) 30%, transparent);
}
.callout.bad svg {
  color: var(--negative);
}

/* lock / allow-deny lists */
.locklist {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.locklist li {
  display: flex;
  gap: 9px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-muted);
}
.locklist li svg {
  flex: 0 0 auto;
  margin-top: 2px;
}
.locklist.deny li svg {
  color: var(--negative);
}
.locklist.allow li svg {
  color: var(--positive);
}
.locklist.hard li svg {
  color: var(--warning);
}

.split2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s4);
}

/* ============================================================ code */

.code {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
}
.code-hd {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 7px 10px 7px 12px;
  background: var(--surface-3);
  border-bottom: 1px solid var(--border);
}
.code-hd .t {
  font-size: 12px;
  font-weight: 620;
  color: var(--text);
}
.code-hd .spacer {
  flex: 1 1 auto;
}
.code pre {
  margin: 0;
  padding: 12px;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.65;
  color: var(--text-muted);
  tab-size: 2;
}
.code pre .k {
  color: var(--accent-text);
}
.code pre .a {
  color: #f6b23c;
}
.code pre .s {
  color: #2bd17e;
}
.code pre .c {
  color: var(--text-faint);
  font-style: italic;
}

/* ============================================================ clamp log */

.log {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  max-height: 168px;
  overflow-y: auto;
}
.log-row {
  display: flex;
  gap: 9px;
  align-items: baseline;
  padding: 7px 11px;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.5;
  animation: rise 0.2s ease-out;
}
.log-row:last-child {
  border-bottom: 0;
}
.log-row .tm {
  color: var(--text-faint);
  flex: 0 0 auto;
}
.log-row .lv {
  flex: 0 0 auto;
  font-weight: 700;
}
.log-row .lv.clamp {
  color: var(--warning);
}
.log-row .lv.reject {
  color: var(--negative);
}
.log-row .lv.ok {
  color: var(--positive);
}
.log-row .lv.info {
  color: var(--accent-text);
}
.log-row .ms {
  color: var(--text-muted);
  min-width: 0;
}
.log-empty {
  padding: 20px 12px;
  text-align: center;
  color: var(--text-faint);
  font-size: 12px;
}

/* ============================================================ preview rail */

.rail {
  position: sticky;
  top: 70px;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  max-height: calc(100vh - 88px);
}

.frame-wrap {
  flex: 1 1 auto;
  min-height: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-1);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.frame-bar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 7px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
}
.frame-bar .lights {
  display: flex;
  gap: 5px;
  flex: 0 0 auto;
}
.frame-bar .lights i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
}
.frame-bar .url {
  flex: 1 1 auto;
  min-width: 0;
  padding: 3px 9px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vp {
  display: flex;
  gap: 1px;
  flex: 0 0 auto;
}
.vp button {
  width: 28px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--text-faint);
  cursor: pointer;
}
.vp button:hover {
  color: var(--text);
  background: var(--surface-3);
}
.vp button[aria-pressed="true"] {
  color: var(--accent-text);
  background: var(--surface-3);
  border-color: var(--border-strong);
}

/* Expand control. Label is hidden on the narrow rail and appears once the
   preview is promoted to the overlay, where there is room for it. */
.zoom {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 7px;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  color: var(--text-muted);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  transition: color 0.16s, background 0.16s, border-color 0.16s;
}
.zoom:hover:not(:disabled) {
  color: var(--text-strong);
  background: var(--surface-3);
}
.zoom:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.zoom .zl {
  display: none;
}
.zoom .i-close {
  display: none;
}

/* ---------------------------------------------- expanded preview overlay */

.frame-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(8, 10, 14, 0.74);
}
.frame-scrim[hidden] {
  display: none;
}

/* The wrap is promoted in place rather than reparented — moving the iframe in
   the DOM would reload it and lose the rendered page. */
body.zoomed {
  overflow: hidden;
}
/* A sticky element creates a stacking context, which would trap the promoted
   wrap's z-index inside the rail and let the scrim paint over the frame's own
   controls. Dropping sticky for the duration lets the wrap stack against the
   root; the rail's height is pinned in JS so nothing shifts. */
body.zoomed .rail {
  position: static;
}
body.zoomed .frame-wrap {
  position: fixed;
  inset: 20px;
  z-index: 61;
  max-height: none;
  border-color: var(--border-strong);
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.55);
}
body.zoomed .frame-bar {
  padding: 9px 12px;
}
body.zoomed .zoom {
  color: var(--text-strong);
}
body.zoomed .zoom .zl {
  display: inline;
}
body.zoomed .zoom .i-open {
  display: none;
}
body.zoomed .zoom .i-close {
  display: inline;
}
body.zoomed .frame-stage {
  min-height: 0;
}
body.zoomed .frame-stage iframe {
  min-height: 0;
}
@media (max-width: 640px) {
  body.zoomed .frame-wrap {
    inset: 0;
    border-radius: 0;
    border: 0;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .frame-scrim {
    animation: scrim-in 0.16s ease-out;
  }
}
@keyframes scrim-in {
  from {
    opacity: 0;
  }
}

/* Scales with the viewport so the rail still fits under its sticky max-height
   on a short laptop screen instead of spilling past the fold. */
.frame-stage {
  flex: 1 1 auto;
  min-height: clamp(360px, 52vh, 560px);
  display: flex;
  justify-content: center;
  background: #2a2f3a;
  overflow: hidden;
  padding: 0;
}
.frame-stage iframe[hidden] {
  display: none;
}
/* No page loaded yet — keep the stage in the app's own surface colour rather
   than showing an empty browser-chrome grey. */
.frame-stage:has(iframe[hidden]) {
  background: var(--surface-1);
}
.frame-stage iframe {
  width: 100%;
  height: 100%;
  min-height: clamp(360px, 52vh, 560px);
  border: 0;
  background: #fff;
  display: block;
  transition: width 0.24s cubic-bezier(0.22, 0.8, 0.3, 1);
}
/* The frame is laid out at its true target width and then scaled to fit the
   stage, so the previewed document really does reflow at 768 / 375 px
   instead of being clamped back to the stage width. `--vp-w` and
   `--vp-scale` are set on the stage by fitViewport() in app.js; the
   fallbacks here keep the frame sane if scripting has not run yet.

   flex-shrink is disabled because the box is deliberately allowed to be
   wider than its flex container — the stage already clips, and the scale
   brings the painted result back inside. transform-origin is `top center`
   so the scaled result stays centred on the same axis the unscaled box was
   centred on, which means no horizontal jump when switching widths. */
.frame-stage[data-vp="tablet"] iframe,
.frame-stage[data-vp="mobile"] iframe {
  flex: 0 0 auto;
  width: var(--vp-w, 100%);
  max-width: none;
  height: calc(100% / var(--vp-scale, 1));
  min-height: calc(clamp(360px, 52vh, 560px) / var(--vp-scale, 1));
  transform: scale(var(--vp-scale, 1));
  transform-origin: top center;
}

.rail-empty {
  margin: auto;
  padding: var(--s7) var(--s4);
  text-align: center;
  color: var(--text-muted);
}
.rail-empty svg {
  color: var(--text-faint);
  margin-bottom: 10px;
}
.rail-empty b {
  display: block;
  color: var(--text-strong);
  font-size: 14px;
  margin-bottom: 4px;
}
.rail-empty p {
  font-size: 12.5px;
  max-width: 34ch;
  margin: 0 auto;
}

/* isolated widget swatch preview */
.swatch {
  padding: var(--s5) var(--s4);
  background: repeating-conic-gradient(
      from 0deg at 50% 50%,
      rgba(255, 255, 255, 0.018) 0deg 90deg,
      transparent 90deg 180deg
    )
    0 0 / 16px 16px;
  text-align: center;
  min-height: 130px;
  max-height: 40vh;
  overflow: auto;
}
#swatchPanel {
  flex: 0 0 auto;
}
/* text-align is inherited and crosses the shadow boundary, so reset it on the
   host: the panel centres the widget, the widget keeps its own alignment. */
.swatch > * {
  max-width: 100%;
  text-align: left;
}

/* ============================================================ slots */

.slots {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  max-height: 268px;
  overflow-y: auto;
}
.slot {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 10px var(--s3);
  background: var(--surface-1);
  border: 0;
  border-left: 2px solid transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.14s;
}
.slot:hover {
  background: var(--surface-2);
}
.slot[aria-pressed="true"] {
  background: color-mix(in srgb, var(--accent) 9%, var(--surface-1));
  border-left-color: var(--accent);
}
.slot .ix {
  flex: 0 0 auto;
  width: 20px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.slot .tx {
  flex: 1 1 auto;
  min-width: 0;
}
.slot .tx .p {
  display: block;
  font-size: 12.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.slot .tx .w {
  display: block;
  font-size: 11px;
  color: var(--text-faint);
  font-family: var(--font-mono);
  margin-top: 1px;
}
.slot .pick {
  flex: 0 0 auto;
  color: var(--accent-text);
  display: none;
}
.slot[aria-pressed="true"] .pick {
  display: block;
}

/* ============================================================ summary */

.kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 7px var(--s4);
  font-size: 12.5px;
}
.kv dt {
  color: var(--text-faint);
  white-space: nowrap;
}
.kv dd {
  margin: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  overflow-wrap: anywhere;
}

/* ============================================================ toast */

.toasts {
  position: fixed;
  bottom: 18px;
  right: 18px;
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 14px;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 550;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  animation: toastin 0.24s cubic-bezier(0.22, 0.8, 0.3, 1);
  max-width: 340px;
}
.toast.ok svg {
  color: var(--positive);
}
.toast.err svg {
  color: var(--negative);
}
.toast.warn svg {
  color: var(--warning);
}
@keyframes toastin {
  from {
    opacity: 0;
    transform: translateX(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ============================================================ footer */

.foot {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s5);
  border-top: 1px solid var(--border);
  display: flex;
  gap: var(--s4);
  flex-wrap: wrap;
  align-items: center;
  font-size: 12px;
  color: var(--text-faint);
}
.foot .spacer {
  flex: 1 1 auto;
}

/* ============================================================ responsive */

@media (max-width: 1080px) {
  .work {
    grid-template-columns: minmax(0, 1fr);
  }
  .rail {
    position: static;
    max-height: none;
    order: -1;
  }
  .frame-stage,
  .frame-stage iframe {
    min-height: clamp(320px, 60vh, 620px);
  }

  /* The rail is hoisted above the wizard on narrow screens so edits and
     their result stay in one view. That only helps once there is something
     to look at: before a page is loaded it buried step 1 under a full screen
     of empty placeholder. While the empty state is showing, the rail drops
     back below the steps and stops reserving the height. */
  .rail:has(#railEmpty:not([hidden])) {
    order: 1;
  }
  .rail:has(#railEmpty:not([hidden])) .frame-stage,
  .rail:has(#railEmpty:not([hidden])) .frame-stage iframe {
    min-height: 200px;
  }
}

@media (max-width: 640px) {
  .topbar {
    padding: 0 var(--s4);
    gap: var(--s2);
  }
  /* The scope note is the less urgent of the two chips; "sample data" is the
     one a viewer needs. Below 460px there is only room for one. */
  .topbar-right .chip:last-child {
    display: none;
  }
  .brand .tag {
    display: none;
  }
  .shell {
    padding: var(--s5) var(--s4) var(--s6);
  }
  /* Five bare numbers clustered at the left edge read as unfinished. Only
     the current step keeps its label, and it takes the leftover width, so
     the row fills the bar and still says where you are. */
  .stepper {
    gap: 4px;
  }
  .stepper .lbl {
    display: none;
  }
  .stepper button {
    justify-content: center;
    flex: 0 0 auto;
    padding: 8px 10px;
  }
  .stepper button[aria-current="step"] {
    flex: 1 1 auto;
    justify-content: flex-start;
  }
  .stepper button[aria-current="step"] .lbl {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .panel-bd,
  .drop {
    padding: var(--s3);
  }
  .drop {
    padding-block: var(--s5);
  }
  .fields {
    grid-template-columns: 1fr;
  }
  .actions {
    flex-direction: column-reverse;
    align-items: stretch;
  }
  .actions .btn {
    width: 100%;
  }
  .actions .spacer {
    display: none;
  }
}

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

/* ======================================================================
   Additions: URL import, multiple placements, plain-English summary,
   locked-card request dialog, placement highlight toggle.
   ====================================================================== */

/* ------------------------------------------------------------ step 1: URL */

.urlbox {
  padding: var(--s4);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.urlbox .flabel {
  margin-bottom: 7px;
}
.urlrow {
  display: flex;
  gap: var(--s2);
  align-items: stretch;
}
.urlrow input[type="url"] {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 11px;
}
.urlrow .btn {
  flex: 0 0 auto;
  white-space: nowrap;
}
.urlbox .help {
  margin-top: 10px;
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.6;
}
.urlbox .help code {
  font-size: 12px;
  color: var(--text);
}
.urlnote {
  margin-top: 9px;
  padding: 8px 10px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
  color: var(--text);
  font-size: 12.5px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.urlnote.bad {
  background: color-mix(in srgb, var(--negative) 7%, var(--surface-2));
  border-color: color-mix(in srgb, var(--negative) 30%, var(--border));
}

/* Inline text button used for quick-picks and cross-step jumps. */
.lnk {
  padding: 0;
  background: none;
  border: 0;
  color: var(--accent-text);
  font: inherit;
  font-size: inherit;
  font-weight: 550;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-underline-offset: 2px;
  cursor: pointer;
}
.lnk:hover {
  text-decoration-color: var(--accent-text);
}

/* "or" divider between the two ways in. */
.orbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin: var(--s4) 0;
  color: var(--text-faint);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.orbar::before,
.orbar::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}

/* --------------------------------------------------- explanatory panel note */

.panel-note {
  padding: 11px var(--s4) 0;
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.55;
  max-width: 66ch;
}
.panel-note:last-child {
  padding-bottom: var(--s4);
}

/* ------------------------------------------- step 2: locked cards + strip */

.wcard .why {
  color: var(--text-faint);
  font-size: 12px;
  line-height: 1.5;
}
.wcard.locked {
  cursor: default;
}
.wcard.locked:hover {
  transform: none;
  background: var(--surface-1);
  border-color: var(--border);
}
.wcard .cardft {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: auto;
  padding-top: 11px;
  border-top: 1px solid var(--border);
}
.askbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid color-mix(in srgb, var(--accent) 46%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  color: var(--accent-text);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.askbtn svg {
  flex: 0 0 auto;
  transition: transform 0.16s ease;
}
.askbtn:hover {
  background: color-mix(in srgb, var(--accent) 17%, transparent);
  border-color: var(--accent);
}
.askbtn:hover svg {
  transform: translateX(2px);
}
.askbtn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.askeddone {
  margin-left: auto;
}
.askeddone {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--positive);
  font-size: 11.5px;
  font-weight: 550;
}
.askeddone svg {
  flex: 0 0 auto;
}

.placedstrip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 7px 10px;
  margin-bottom: var(--s4);
  padding: 10px var(--s3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 12.5px;
  color: var(--text-muted);
}
.placedstrip b {
  color: var(--text-strong);
  font-weight: 600;
}
.placedstrip .lnk {
  margin-left: auto;
  font-size: 12px;
}
.pchip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 8px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
  font-size: 12px;
  font-weight: 550;
}
.pchip .ix {
  color: var(--text-faint);
  font-weight: 450;
  font-size: 11px;
}

/* ------------------------------------------------ step 5: placement list */

.plist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.plist:empty {
  display: none;
}
.prow {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 11px var(--s3);
  background: var(--surface-1);
}
.prow + .prow {
  border-top: 1px solid var(--border);
}
.prow.on {
  background: color-mix(in srgb, var(--accent) 7%, var(--surface-1));
  box-shadow: inset 2px 0 0 var(--accent);
}
.prow .pn {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-muted);
}
.prow.on .pn {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.prow .pmain {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.prow .pt {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-strong);
}
.prow .ps {
  color: var(--text-faint);
  font-size: 11.5px;
  overflow-wrap: anywhere;
}
.prow .pacts {
  flex: 0 0 auto;
  display: flex;
  gap: 5px;
}
#addAnother {
  margin-top: var(--s3);
}

/* Slots already occupied by another widget. */
.slot.taken {
  opacity: 0.62;
  cursor: not-allowed;
}
.slot.taken:hover {
  background: color-mix(in srgb, var(--negative) 5%, var(--surface-1));
}
.slot .tk {
  color: var(--accent-text);
  font-weight: 550;
}

/* ---------------------------------------- step 5: plain-English summary */

.sumlede {
  color: var(--text);
  font-size: 13.5px;
  line-height: 1.6;
  max-width: 62ch;
}
.sumcard {
  padding: var(--s3) var(--s4) var(--s4);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.sumcard + .sumcard {
  margin-top: var(--s3);
}
.sumcard.theme {
  background: var(--surface-2);
}
.sumhd {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.sumhd b {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text-strong);
}
.sumhd .sn {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  font-weight: 700;
}
.sumhd .tag {
  margin-left: auto;
}
.sumwhere {
  margin-top: 5px;
  color: var(--text-faint);
  font-size: 12px;
  overflow-wrap: anywhere;
}
.sumcard ul {
  list-style: none;
  padding: 0;
  margin-top: 11px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sumcard li {
  position: relative;
  padding-left: 15px;
  color: var(--text);
  font-size: 13px;
  line-height: 1.6;
  max-width: 68ch;
}
.sumcard li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 60%, var(--border-strong));
}

/* ------------------------------------------------- developer details toggle */

.devdet {
  border-top: 1px solid var(--border);
  padding-top: var(--s3);
}
.devdet > summary {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 0;
  color: var(--text-muted);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.devdet > summary::-webkit-details-marker {
  display: none;
}
.devdet > summary:hover {
  color: var(--text-strong);
}
.devdet > summary .caret {
  flex: 0 0 auto;
  color: var(--text-faint);
  transition: transform 0.16s;
}
.devdet[open] > summary .caret {
  transform: rotate(90deg);
}
.devdet > summary .hint {
  margin-left: auto;
  color: var(--text-faint);
  font-size: 11.5px;
  font-weight: 400;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.devbd {
  padding-top: var(--s3);
}

/* -------------------------------------------------- placement highlight btn */

.hl {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 7px;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  color: var(--text-muted);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  transition: color 0.16s, background 0.16s, border-color 0.16s;
}
.hl:hover:not(:disabled) {
  color: var(--text-strong);
  background: var(--surface-3);
}
.hl:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.hl[aria-pressed="true"] {
  color: var(--accent-text);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border-strong));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.hl .zl {
  display: none;
}
body.zoomed .hl .zl {
  display: inline;
}

/* --------------------------------------------------------- request dialog */

.askdlg {
  width: min(620px, calc(100vw - 32px));
  max-height: min(88vh, 860px);
  padding: 0;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg, 10px);
  box-shadow: 0 28px 70px -14px rgba(0, 0, 0, 0.72);
  color: var(--text);
}
.askdlg::backdrop {
  background: rgba(4, 8, 16, 0.66);
}
.askform {
  display: flex;
  flex-direction: column;
  max-height: inherit;
}
.ask-hd {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
  padding: var(--s4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.ask-hd h2 {
  margin-top: 4px;
  font-size: 18px;
  font-weight: 650;
  letter-spacing: -0.012em;
  color: var(--text-strong);
}
.ask-hd .btn {
  flex: 0 0 auto;
  margin-left: auto;
}
.ask-sub {
  margin-top: 5px;
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.6;
  max-width: 56ch;
}
.ask-bd {
  padding: var(--s4);
  overflow-y: auto;
}
.ask-bd > * + * {
  margin-top: var(--s4);
}
.ask-ft {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--surface-2);
  border-top: 1px solid var(--border);
}
.ask-ft .spacer {
  flex: 1 1 auto;
}
.ask-ft .note {
  color: var(--text-faint);
  font-size: 11.5px;
}

@media (max-width: 720px) {
  /* A two-column dl crushes the value column at phone widths; stack instead. */
  .kv {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .kv dt {
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .kv dd {
    margin-bottom: 9px;
  }
  .kv dd:last-child {
    margin-bottom: 0;
  }

  .urlrow {
    flex-direction: column;
  }
  .urlrow .btn {
    justify-content: center;
  }
  .prow {
    flex-wrap: wrap;
  }
  .prow .pacts {
    width: 100%;
    padding-left: 34px;
  }
  .placedstrip .lnk {
    margin-left: 0;
  }
  .ask-hd {
    flex-direction: column;
  }
  .ask-hd .btn {
    margin-left: 0;
  }
}

/* The request payload contains a free-text message, so wrap rather than clip. */
#askPayload {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------- heavy-page weight notice
   Step 1, shown only when the imported document is large enough that a
   preview rebuild is genuinely expensive. Informational, not an error: the
   page still works, it just costs more, and the user gets to decide. */
.weightbox {
  margin-top: var(--s3);
  padding: var(--s3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
}
.weightbox[hidden] {
  display: none;
}
.weightbox .wn {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.62;
  color: var(--text-muted);
  max-width: 62ch;
}
#trimMain[hidden] {
  display: none;
}

/* The shared .btn-sm sizing is 28px tall, which is fine for a control sitting
   in a dense toolbar but not for one that appears on its own in a notice a
   user reads on a phone. 44px is the minimum comfortable touch target. */
#trimMain {
  min-height: 44px;
  padding-block: 10px;
}

/* The notice is a paragraph of real prose, not a label, so it gets a reading
   measure. At 1440px it otherwise ran to ~85 characters a line. */
.weightbox .wn {
  max-width: 68ch;
}

/* =========================================================== start-from block
   Step 1 used to offer two separate ways in: three quick-pick URLs buried in
   the address box, and a samples list below the dropzone. They are the same
   idea — "I do not have a file, give me something to work with" — so they are
   one block now, below the dropzone, split into live pages and bundled
   samples. Both groups reuse .samplelist, so row rhythm and the 24px target
   floor (#44, #47) hold for the new rows without a second implementation. */

.grouplbl {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  padding: 9px var(--s4) 7px;
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.grouplbl span {
  font-weight: 400;
  letter-spacing: 0.01em;
  text-transform: none;
  font-size: 11.5px;
  color: var(--text-faint);
}
.grouplbl + .samplelist li:last-child {
  border-bottom: 1px solid var(--border);
}
.samples > .samplelist:last-of-type li:last-child {
  border-bottom: 0;
}

/* Live rows get a glyph rather than a palette swatch: there is no brand to
   preview, and a globe says "we go and fetch this" in a way a colour cannot. */
.livelist .gl {
  display: grid;
  place-items: center;
  width: 26px;
  height: 30px;
  flex: 0 0 auto;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--surface-1);
  color: var(--text-faint);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.livelist .gl svg {
  width: 15px;
  height: 15px;
}
.livelist li:hover .gl {
  color: var(--accent-text);
  border-color: var(--accent);
}

/* The refusal demo is a feature, not a hazard, so it is marked in warning
   amber rather than negative red: nothing has gone wrong when you click it. */
.livelist .row-guard .gl {
  color: var(--warning);
  border-color: color-mix(in srgb, var(--warning) 45%, var(--border-strong));
}
.livelist .row-guard:hover .gl {
  color: var(--warning);
  border-color: var(--warning);
}

/* ================================================================== motion
   Budget: entrance on the two places where several things arrive at once,
   and pressure feedback on things you click. Everything is under 380 ms and
   nothing loops. Anything longer or repeating reads as decoration on a tool
   people use for real work. */

@media (prefers-reduced-motion: no-preference) {
  /* Widget cards and start-from rows arrive in sequence rather than as a
     block, so the eye is led down the list. Capped at six: past that the
     last item waits long enough to feel like lag rather than choreography. */
  .step.active .wgrid > *,
  .step.active .samples .samplelist > li {
    animation: stagger-in 0.34s both cubic-bezier(0.22, 0.8, 0.3, 1);
  }
  .step.active .wgrid > *:nth-child(1),
  .step.active .samples .samplelist > li:nth-child(1) { animation-delay: 0.02s; }
  .step.active .wgrid > *:nth-child(2),
  .step.active .samples .samplelist > li:nth-child(2) { animation-delay: 0.06s; }
  .step.active .wgrid > *:nth-child(3),
  .step.active .samples .samplelist > li:nth-child(3) { animation-delay: 0.1s; }
  .step.active .wgrid > *:nth-child(4),
  .step.active .samples .samplelist > li:nth-child(4) { animation-delay: 0.14s; }
  .step.active .wgrid > *:nth-child(n + 5),
  .step.active .samples .samplelist > li:nth-child(n + 5) { animation-delay: 0.18s; }

  /* New validation lines slide in from the left edge of the log, so a value
     being clamped is visible even if you were looking at the input. */
  .log-row {
    animation: log-in 0.24s cubic-bezier(0.22, 0.8, 0.3, 1);
  }

  /* Pressure feedback. 0.985 is deliberately almost imperceptible — enough
     to confirm the hit, not enough to look like a toy. */
  .btn:active:not(:disabled),
  .wcard:active,
  .slot:active,
  .chips button:active,
  .seg button:active {
    transform: scale(0.985);
  }
  .btn,
  .wcard,
  .slot {
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease,
      transform 0.1s ease;
  }
}

@keyframes stagger-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes log-in {
  from {
    opacity: 0;
    transform: translateX(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ------------------------------------------------ coarse-pointer targets
   The preview chrome is deliberately small on a mouse: it is instrument
   labelling, not primary action, and a 44 px row of it would compete with
   the page being previewed. On a touch screen there is no hover to aim
   with and no cursor precision, so the same controls are grown to the
   44 x 44 recommendation. Width stays auto-ish so the bar does not wrap. */
@media (pointer: coarse) {
  .vp button {
    width: 40px;
    height: 40px;
  }
  .zoom,
  .hl {
    height: 40px;
    padding: 0 12px;
  }
  .btn-sm {
    min-height: 40px;
  }
  .brand {
    padding: 10px 0;
  }
}

@media (pointer: coarse) {
  /* Same controls as #44, second pass: the ones #47 caught. Touch gets
     40px targets without inflating the mouse layout. */
    .samplelist .dl { padding: 10px 0 11px; }
  input[type="range"] { height: 40px; }
}


/* VENDORED ADDITION: a visible route back to the main site. The logo alone is not enough — this
   page is reached from the marketing site and needs an obvious way home that does not rely on the
   visitor guessing that the wordmark is a link. Styled off the existing .chip so it belongs in the
   topbar, but underlined on hover so it reads as navigation rather than status. */
.topbar-home{
  display:inline-flex;align-items:center;gap:6px;
  padding:6px 12px;border-radius:999px;
  border:1px solid var(--line);
  color:var(--slate);text-decoration:none;
  font-family:'Sora',ui-sans-serif,system-ui,sans-serif;
  font-size:12px;font-weight:600;letter-spacing:.01em;
  transition:color .15s ease,border-color .15s ease,background-color .15s ease;
}
.topbar-home:hover{color:var(--white);border-color:var(--ultraviolet);background:rgba(108,59,255,.12);}
.topbar-home:focus-visible{outline:2px solid var(--ultraviolet);outline-offset:2px;}
