/* SVGink — image to SVG. One palette written out twice: a deep slate canvas
   with an indigo-to-cyan accent that carries the brand mark. Dark applies when
   the system asks for it or when the reader picks it in the header, and an
   explicit pick beats the system in both directions. */
:root {
  color-scheme: light;

  --bg: #ffffff;
  --bg-tint: #f5f6fa;
  --surface: #ffffff;
  --surface-2: #f3f5f9;
  --fg: #0d1117;
  --muted: #5b6472;
  --line: #e3e7ee;
  --line-strong: #cdd4df;
  --accent: #4f46e5;
  --accent-ink: #ffffff;
  --accent-2: #06b6d4;
  /* Gradient that carries text on top of it, so both ends stay dark enough for
     the ink colour. The brighter cyan is reserved for graphics. */
  --grad-a: #4f46e5;
  --grad-b: #7c3aed;
  --accent-soft: color-mix(in srgb, var(--accent) 10%, transparent);
  --error: #b3261e;
  --ok: #157347;
  --checker: #e6e9ef;
  --shadow-sm: 0 1px 2px rgb(13 17 23 / 6%), 0 1px 3px rgb(13 17 23 / 4%);
  --shadow-md: 0 6px 20px -8px rgb(13 17 23 / 18%), 0 2px 6px rgb(13 17 23 / 5%);
  --shadow-lg: 0 30px 60px -30px rgb(13 17 23 / 28%), 0 8px 24px -12px rgb(13 17 23 / 12%);
  --glow: color-mix(in srgb, var(--accent) 22%, transparent);
  --glow-2: color-mix(in srgb, var(--accent-2) 20%, transparent);
  --grid-line: color-mix(in srgb, var(--fg) 6%, transparent);

  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 24px;
  --shell: 74rem;
  --font: "Inter", "Segoe UI Variable Text", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* The dark half of the palette. It is written out twice because the two ways of
   asking for it cannot share a rule: a media query for the no-JS case and an
   attribute for the toggle. Picking light explicitly opts out of the query. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg: #0b0d12;
    --bg-tint: #0f1219;
    --surface: #14171f;
    --surface-2: #1a1e28;
    --fg: #e9ecf2;
    --muted: #98a2b3;
    --line: #252b37;
    --line-strong: #333b4a;
    --accent: #818cf8;
    --accent-ink: #0b0d12;
    --accent-2: #22d3ee;
    --grad-a: #818cf8;
    --grad-b: #67e8f9;
    --accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
    --error: #f2b8b5;
    --ok: #8cd7a5;
    --checker: #232936;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
    --shadow-md: 0 8px 24px -10px rgb(0 0 0 / 60%);
    --shadow-lg: 0 34px 70px -34px rgb(0 0 0 / 75%), 0 10px 30px -16px rgb(0 0 0 / 50%);
    --glow: color-mix(in srgb, var(--accent) 26%, transparent);
    --glow-2: color-mix(in srgb, var(--accent-2) 18%, transparent);
    --grid-line: color-mix(in srgb, var(--fg) 8%, transparent);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0b0d12;
  --bg-tint: #0f1219;
  --surface: #14171f;
  --surface-2: #1a1e28;
  --fg: #e9ecf2;
  --muted: #98a2b3;
  --line: #252b37;
  --line-strong: #333b4a;
  --accent: #818cf8;
  --accent-ink: #0b0d12;
  --accent-2: #22d3ee;
  --grad-a: #818cf8;
  --grad-b: #67e8f9;
  --accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
  --error: #f2b8b5;
  --ok: #8cd7a5;
  --checker: #232936;
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
  --shadow-md: 0 8px 24px -10px rgb(0 0 0 / 60%);
  --shadow-lg: 0 34px 70px -34px rgb(0 0 0 / 75%), 0 10px 30px -16px rgb(0 0 0 / 50%);
  --glow: color-mix(in srgb, var(--accent) 26%, transparent);
  --glow-2: color-mix(in srgb, var(--accent-2) 18%, transparent);
  --grid-line: color-mix(in srgb, var(--fg) 8%, transparent);
}

* {
  box-sizing: border-box;
}

/* The page toggles sections with the hidden attribute, and a class that sets a
   display value would otherwise win over it and leave them on screen. */
[hidden] {
  display: none !important;
}

html {
  scroll-behavior: smooth;
}

/* The header is sticky, so anything jumped to must clear it. */
[id] {
  scroll-margin-top: 5.75rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.6 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 650;
  text-wrap: balance;
}

h3 {
  line-height: 1.35;
}

p {
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

code {
  padding: 0.1em 0.35em;
  border-radius: 6px;
  background: var(--surface-2);
  font-family: ui-monospace, SFMono-Regular, "Cascadia Code", Consolas, monospace;
  font-size: 0.9em;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 1.5rem;
}

.shell--narrow {
  max-width: 48rem;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  padding: 0.7rem 1rem;
  border-radius: 0 0 var(--radius-sm) 0;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

.skip-link:focus {
  left: 0;
}

/* --- header ---------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px) saturate(160%);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 4.25rem;
}

.brand {
  /* One knob for the whole lockup. The mark and the wordmark both read this, so
     they cannot drift apart the way a hardcoded pair does - and a footer that
     wants a smaller lockup changes one value instead of two that disagree. */
  --brand-size: 1.15rem;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 650;
  letter-spacing: -0.02em;
}

.brand__mark {
  width: var(--brand-size);
  height: var(--brand-size);
  flex: none;
}

.brand__name {
  font-size: var(--brand-size);
  color: var(--fg);
}

/* The wordmark is two-tone: the keyword carries the brand accent, the rest stays
   the body colour. Both tokens resolve per theme and both clear AA at this size -
   the brighter cyan is left to graphics, for the reason the --grad-* comment gives. */
.brand__name__svg {
  color: var(--accent);
}

.site-nav {
  display: flex;
  gap: 1.5rem;
  margin-inline-start: auto;
  font-size: 0.95rem;
  color: var(--muted);
}

.site-nav a {
  padding-block: 0.25rem;
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}

.site-nav a:hover {
  color: var(--fg);
  border-bottom-color: var(--accent);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Icon-only and quiet: it sits beside the sign-up button without competing. */
.theme-toggle {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.theme-toggle:hover {
  border-color: var(--line-strong);
  background: var(--surface-2);
  color: var(--fg);
}

.theme-toggle svg {
  width: 1.15rem;
  height: 1.15rem;
}

/* The control shows the scheme it switches to, not the one in force. */
.theme-toggle__sun {
  display: none;
}

:root[data-theme="dark"] .theme-toggle__sun {
  display: block;
}

:root[data-theme="dark"] .theme-toggle__moon {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle__sun {
    display: block;
  }

  :root:not([data-theme="light"]) .theme-toggle__moon {
    display: none;
  }
}

/* Google Identity button.
   Google's branding guidelines fix the mark, the fill, the stroke, the type and
   the padding, so this renders Google's own pre-approved artwork at its
   specified 180x40 pill size instead of rebuilding the button from the `.button`
   primitives below. The icon-only 40x40 artwork takes over on narrow phones. */
.google-button {
  display: inline-flex;
  flex: none;
  border-radius: 999px;
}

.google-button__narrow {
  display: none;
}

.google-button__img {
  display: block;
  width: auto;
  height: 2.5rem;
}

/* Same arrangement as the theme toggle: every variant is in the DOM and the
   active theme decides which one is painted. */
.google-button__img--dark {
  display: none;
}

:root[data-theme="dark"] .google-button__img--light {
  display: none;
}

:root[data-theme="dark"] .google-button__img--dark {
  display: block;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .google-button__img--light {
    display: none;
  }

  :root:not([data-theme="light"]) .google-button__img--dark {
    display: block;
  }
}

/* The signed-in state of the same slot the sign-in pill occupies. It is a
   container rather than a link, because the only thing left to do to an account
   is to leave it. */
.account {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.3rem 0.3rem 0.3rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
}

.account__avatar {
  width: 1.85rem;
  height: 1.85rem;
  border-radius: 50%;
  background: var(--surface-2);
  object-fit: cover;
}

.account__who {
  display: grid;
  line-height: 1.15;
}

.account__tag {
  color: var(--muted);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The address is the identity, so it is the part that has to survive a narrow
   header; a long one is cut rather than allowed to push the pill off the bar. */
.account__name {
  max-width: 14rem;
  overflow: hidden;
  font-size: 0.82rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account__out {
  padding: 0.3rem 0.7rem;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--fg);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.account__out:hover {
  background: var(--accent-soft);
  color: var(--accent);
}

/* --- buttons --------------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.7rem 1.15rem;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--fg);
  font: inherit;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease,
    border-color 0.2s ease, color 0.2s ease;
}

.button--primary {
  background-image: linear-gradient(120deg, var(--grad-a), var(--grad-b));
  background-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-sm);
}

.button--primary:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 12px 26px -12px var(--glow), var(--shadow-md);
}

.button--ghost {
  border-color: var(--line-strong);
  background: transparent;
}

.button--ghost:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

button:disabled {
  opacity: 0.45;
  cursor: default;
}

/* --- hero ------------------------------------------------------------------ */

.hero {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}

/* Two soft light sources behind the fold, kept faint enough to read over. */
.hero::before {
  content: "";
  position: absolute;
  inset: -30% -10% 20%;
  background:
    radial-gradient(40rem 22rem at 12% 8%, var(--glow), transparent 70%),
    radial-gradient(34rem 20rem at 92% 30%, var(--glow-2), transparent 70%);
  filter: blur(6px);
  pointer-events: none;
}

/* The traced dragon from the sample set, kept as a ghost behind the fold: it is
   the product's own output, so the hero shows the artefact rather than an icon.
   Full-bleed behind the whole section, faded on the left so the copy column
   never has to compete with it. Only drawn once the hero has gone two-column,
   which also keeps the file off the phones that would not show it.
   The pseudo-element is the whole section, so `cover` is resolved against the
   hero's box: anything that changes the hero's height rescales the artwork.
   Keep the hero's height stable as the page settles. */
@media (min-width: 60.0625rem) {
  .hero::after {
    content: "";
    position: absolute;
    z-index: 0;
    inset: 0;
    background: url("demo/drawing-flowers.svg") center / cover no-repeat;
    opacity: 0.14;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 55%);
    mask-image: linear-gradient(90deg, transparent, #000 55%);
    pointer-events: none;
  }
}

/* The traced artwork is dark, so it needs a little more presence on a dark page. */
:root[data-theme="dark"] .hero::after {
  opacity: 0.22;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hero::after {
    opacity: 0.22;
  }
}

/* The converter is the point of the page, so its column is the wider one. */
.hero__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  align-items: center;
  gap: 3rem;
  padding-block: 4rem 4.5rem;
}

.hero__copy > * + * {
  margin-top: 1.25rem;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  font-size: 0.82rem;
  font-weight: 550;
  color: var(--muted);
}

.eyebrow__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.hero h1 {
  font-size: clamp(2.15rem, 4.4vw, 3.4rem);
}

.gradient-text {
  background-image: linear-gradient(100deg, var(--grad-a), var(--grad-b));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Clipped text would be invisible without background-clip support. */
@supports not (background-clip: text) {
  .gradient-text {
    color: var(--accent);
  }
}

.lede {
  max-width: 34rem;
  color: var(--muted);
  font-size: 1.05rem;
}

.checklist {
  display: grid;
  gap: 0.6rem;
  color: var(--muted);
  font-size: 0.95rem;
}

.checklist li {
  position: relative;
  padding-inline-start: 1.7rem;
}

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.15rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}

.checklist li::after {
  content: "";
  position: absolute;
  left: 0.34rem;
  top: 0.45rem;
  width: 0.42rem;
  height: 0.22rem;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding-top: 0.5rem;
}

/* Format strip. The chips are controls now, but the row keeps a single tab
   stop: arrow keys walk it, so the hero does not spend seven stops on it. */
.formats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.82rem;
  color: var(--muted);
}

.formats__note {
  margin-inline-end: 0.15rem;
}

.formats__note:last-child {
  margin-inline: 0.15rem 0;
}

.formats__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

/* The list items are wrappers with no look of their own; the button inside
   carries the pill, because the button is what answers a click. */
.formats__list li {
  display: flex;
}

.chip {
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: border-color 0.18s ease, background-color 0.18s ease, color 0.18s ease;
}

.chip:hover {
  border-color: var(--line-strong);
  color: var(--accent);
}

.chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.chip[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}

/* What the tracer does with the chosen format. Two lines are reserved so the
   answer does not shove the page around when it arrives. */
.formats__detail {
  min-height: 2.6rem;
  max-width: 34rem;
  margin-top: 0.7rem;
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.55;
}

/* On the hero the accepted-format block sits inside the dropzone, so it follows
   the zone's centred alignment and sits clear of the hint above it. */
.dropzone .formats {
  justify-content: center;
  margin-top: 1.1rem;
}

.hero__tool {
  scroll-margin-top: 6rem;
}

/* --- card & converter ------------------------------------------------------ */

.card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-md);
}

.converter {
  padding: 1.5rem;
  box-shadow: var(--shadow-lg);
}

.converter__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.1rem;
}

.converter__title {
  font-size: 1.05rem;
}

.converter__meta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: 0.82rem;
}

.converter__meta::before {
  content: "";
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--ok);
}

.converter__note {
  margin-top: 1.1rem;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
}

/* What is left of the free allowance, filled in from /api/me. Quieter than the
   status line below the dropzone, because it is standing information rather
   than the answer to something the visitor just did. */
.allowance {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.4;
}

/* The allowance arrives a moment after the first paint, so an empty one holds
   the space it is about to take. Without that the card grows by a row the
   instant `/api/me` answers and everything under it moves with it: the
   dropzone, and the hero box the traced background is scaled against, which
   re-derives `cover` and makes the artwork visibly resize mid-load.
   `visibility` rather than `display` so the reserved row stays a box while
   still being out of the accessibility tree. */
.allowance[hidden] {
  display: flex !important;
  visibility: hidden;
  /* One line of the allowance's own type, plus its padding and border: exactly
     the height it takes once `paintIdentity()` fills it in. */
  min-height: calc(0.82rem * 1.4 + 1rem + 2px);
}

/* Said in the allowance's own voice rather than in red: the allowance running
   out is the expected end of a free tier, not a fault. */
.allowance[data-tone="out"] {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: var(--accent-soft);
  color: var(--fg);
}

.notice {
  margin-bottom: 1rem;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.84rem;
  line-height: 1.45;
}

.notice[data-kind="error"] {
  border-left-color: var(--error);
  color: var(--error);
}

.dropzone {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 0.4rem;
  min-height: 19rem;
  padding: 3.5rem 1.75rem;
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.2s ease;
}

.dropzone:hover,
.dropzone[data-dragging] {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.dropzone:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

.dropzone[data-dragging] {
  transform: scale(1.01);
}

.dropzone[data-busy] {
  cursor: progress;
  opacity: 0.6;
}

.dropzone__icon {
  width: 2.4rem;
  height: 2.4rem;
  margin-bottom: 0.5rem;
  color: var(--accent);
}

.dropzone-title {
  font-size: 1.05rem;
  font-weight: 600;
}

.hint {
  color: var(--muted);
  font-size: 0.92rem;
}

.pick {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.pick input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.pick:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Conversion runs for tens of seconds with no measurable fraction done, so the
   server reports its ordered steps and the page eases the bar toward them. The
   bar and its percentage are the whole readout. */
.progress {
  margin-top: 1.1rem;
}

.progress-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.progress-track {
  flex: 1;
  overflow: hidden;
  height: 6px;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--line);
}

.progress-bar {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background-image: linear-gradient(90deg, var(--accent), var(--accent-2));
}

.progress-percent {
  min-width: 3.5ch;
  color: var(--accent);
  font-size: 0.88rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.status {
  min-height: 1.35rem;
  margin-top: 0.9rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.status:empty {
  min-height: 0;
  margin-top: 0;
}

.status[data-kind="error"] {
  color: var(--error);
}

.status[data-kind="ok"] {
  color: var(--ok);
}

.result {
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
}

.preview {
  display: grid;
  place-items: center;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background-color: #fff;
  background-image:
    linear-gradient(45deg, var(--checker) 25%, transparent 25%),
    linear-gradient(-45deg, var(--checker) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--checker) 75%),
    linear-gradient(-45deg, transparent 75%, var(--checker) 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}

.preview img {
  max-width: 100%;
  max-height: 52vh;
  object-fit: contain;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.actions .button {
  flex: 1;
}

/* Takes the download's place when the file is waiting on an account. It occupies
   the same row, so the box does not change shape under the trace - only what it
   offers does. */
.result__signin {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.result__note {
  margin: 0;
  flex: 1 1 14rem;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
}

/* --- stats band ------------------------------------------------------------ */

.stats {
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}

/* Hairline gaps between cells instead of cell borders, so the row reads as one
   band rather than four boxes. `margin: 0` because `dl` is not in the reset and
   its default margin would otherwise collapse out and skew the band. */
.stats__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  margin: 0;
  gap: 1px;
  background: var(--line);
  border-inline: 1px solid var(--line);
}

.stat {
  padding: 1.6rem 1.35rem;
  background: var(--bg);
}

.stat dt {
  color: var(--muted);
  font-size: 0.82rem;
}

.stat__value {
  margin: 0.35rem 0 0;
  font-size: clamp(1.7rem, 3.2vw, 2.15rem);
  font-weight: 680;
  letter-spacing: -0.03em;
  line-height: 1;
}

.stat__unit {
  margin-inline-start: 0.15rem;
  font-size: 0.5em;
  font-weight: 650;
  letter-spacing: 0;
  color: var(--muted);
}

/* The signed-in visitor's own jobs, newest first. The section that holds this is
   only revealed once we know who is asking, so the markup starts hidden and the
   empty state is a sentence rather than a blank box. */
.traces {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
  margin: 1.6rem 0 0;
  padding: 0;
  list-style: none;
}

.trace {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.trace__main {
  flex: 1 1 auto;
  min-width: 0;
}

/* A filename is whatever the visitor called it, so the row has to survive an
   absurd one without the actions sliding out of reach. */
.trace__name {
  margin: 0;
  overflow: hidden;
  font-size: 0.9rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.trace__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.55rem;
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.76rem;
}

.trace__status {
  font-weight: 600;
}

.trace__status[data-status="done"] {
  color: var(--ok);
}

.trace__status[data-status="failed"] {
  color: var(--error);
}

.trace__actions {
  display: flex;
  flex-shrink: 0;
  gap: 0.4rem;
}

.trace__action {
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 550;
  cursor: pointer;
  text-decoration: none;
}

.trace__action:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.trace__action--danger:hover {
  border-color: var(--error);
  color: var(--error);
}

/* In the place a Download link would be, for a result that has been swept. */
.trace__note {
  align-self: center;
  color: var(--muted);
  font-size: 0.76rem;
}

.traces__empty {
  margin-top: 1.4rem;
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- sections -------------------------------------------------------------- */

.section {
  padding-block: 5rem;
  border-bottom: 1px solid var(--line);
}

.section--muted {
  background: var(--bg-tint);
}

/* The stats band above already closes the gap, so the viewer section opens
   tighter than the rest. */
#demo {
  padding-block-start: 3rem;
}

.section__head {
  max-width: 38rem;
  margin-bottom: 2.75rem;
}

.section__head h2 {
  font-size: clamp(1.6rem, 2.6vw, 2.15rem);
}

.section__head p {
  margin-top: 0.85rem;
  color: var(--muted);
}

/* The features head runs the full shell: the ledger and the canvas below it
   already start at that edge, so a narrower head had nothing to align to. */
.section__head--wide {
  max-width: none;
}

/* Article pages cap the prose at 44rem, so a 38rem head left the title and
   standfirst ending 96px short of the body underneath them. Match the two, and
   give the article an h1 size: no rule set one anywhere, so a post title fell
   back to the browser's 2em and barely outranked the in-article h2. */
.shell--narrow .section__head:has(h1) {
  max-width: 44rem;
}

.section__head:has(h1) > h1 {
  font-size: clamp(1.8rem, 2.8vw, 2.6rem);
}

/* The standfirst opens the piece, so it takes the body colour instead of the
   muted tone the section subtitles use. The meta line below it stays muted. */
.shell--narrow .section__head:has(h1) > h1 + p {
  color: var(--fg);
  font-size: 1.08rem;
}

/* --- features: an annotated specimen --------------------------------------- */

/* Two columns that explain each other: a ledger of claims a reader can walk
   down, and the traced file those claims describe. Pointing at, focusing or
   tapping a claim lights up the matching mark on the canvas, and the marks
   answer back, so the picture and the words explain each other. */
.spec {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  /* The ledger opens up to the height of the canvas beside it, so the two
     columns finish together instead of leaving a hole under the last claim. */
  align-items: stretch;
  gap: 3.25rem;
}

.claims {
  display: grid;
  align-content: space-between;
  border-top: 1px solid var(--line);
}

.claim {
  position: relative;
  padding: 1.4rem 0.75rem 1.4rem 2.75rem;
  border-bottom: 1px solid var(--line);
  /* The row is the control, so the whole strip is a tap target. */
  cursor: pointer;
}

.claim:hover .claim__no,
.claim:focus-visible .claim__no {
  color: var(--accent);
}

/* The mark in the ledger that pairs with the one on the canvas. */
.claim::before {
  content: "";
  position: absolute;
  inset-block: 1rem;
  inset-inline-start: 0;
  width: 2px;
  border-radius: 2px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.claim.is-active::before {
  opacity: 1;
}

.claim__no {
  position: absolute;
  inset-inline-start: 0.75rem;
  top: 1.55rem;
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  transition: color 0.2s ease;
}

.claim.is-active .claim__no {
  color: var(--accent);
}

.claim h3 {
  font-size: 1.05rem;
}

.claim p {
  max-width: 32rem;
  margin-top: 0.45rem;
  color: var(--muted);
  font-size: 0.94rem;
}

/* The artefact: a file header, a canvas the artwork sits on, a readout. */
.spec__figure {
  margin: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.spec__bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  font-size: 0.78rem;
}

.spec__file {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 600;
}

.spec__tag {
  margin-inline-start: auto;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--bg-tint);
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
}

/* Reveals the file as the tracer actually wrote it: every one of the 337 path
   outlines drawn over the art, which is the difference between a picture and a
   set of shapes you can edit. Ships hidden, because without JS it is a control
   for nothing. */
.spec__paths {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-family: inherit;
  font-size: 0.7rem;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.18s ease, background-color 0.18s ease, color 0.18s ease;
}

.spec__paths:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.spec__paths[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}

.spec__paths:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Graph paper, with the traced file sitting on it like an artboard. The
   percentages are the artwork's box inside the 806x650 mark overlay.

   The canvas is the frame and the scroll container; the stage inside it is the
   part that grows. Zooming therefore moves the artwork and never the page, the
   grid stays put with the frame, and the mark overlay scales with the artwork
   because it is anchored to the stage. */
.spec__canvas {
  position: relative;
  overflow: auto;
  aspect-ratio: 806 / 650;
  background-color: var(--surface-2);
  background-image: linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 1.35rem 1.35rem;
  overscroll-behavior: contain;
}

/* Scrollable regions need a tab stop to be usable from the keyboard. */
.spec__canvas:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -4px;
}

/* Width only: aspect-ratio then derives the height, so the artboard holds the
   proportions of the file it shows at every zoom level. */
.spec__stage {
  position: relative;
  width: var(--spec-zoom, 100%);
  aspect-ratio: 806 / 650;
}

.spec__art {
  position: absolute;
  left: 11.1665%;
  top: 13.8462%;
  display: block;
  width: 77.6675%;
  height: 72.3077%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
}

/* The outline sheet the tracer wrote, laid over the art. It repeats the art's
   box to the pixel — transparent border included, so the two stay welded at
   every zoom level — and is only ever a thin overlay, never a target. */
.spec__paths-art {
  position: absolute;
  left: 11.1665%;
  top: 13.8462%;
  display: block;
  width: 77.6675%;
  height: 72.3077%;
  border: 1px solid transparent;
  pointer-events: none;
  transition: opacity 0.22s ease;
}

.spec[data-paths="off"] .spec__paths-art {
  opacity: 0;
}

/* The mark sheet is transparent too, so it must not swallow clicks aimed at the
   page behind it; the pins take them back and work like their claim. */
.spec__marks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Marks are white with a dark halo (see #spec-ink), so they stay legible over
   any artwork. Only the pins are always on; the geometry appears with its
   claim, and on touch and in print the pins alone still index the figure. */
.mark__detail {
  opacity: 0;
  transition: opacity 0.22s ease;
}

.spec[data-active="1"] .mark[data-mark="1"] .mark__detail,
.spec[data-active="2"] .mark[data-mark="2"] .mark__detail,
.spec[data-active="3"] .mark[data-mark="3"] .mark__detail {
  opacity: 1;
}

.mark__ring,
.mark__box,
.mark__handle,
.mark__leader {
  fill: none;
  stroke: #fff;
  stroke-width: 2.6;
}

.mark__ring,
.mark__box {
  stroke-dasharray: 6 5;
}

.mark__handle,
.mark__leader {
  stroke-width: 2;
}

.mark__node {
  fill: var(--accent);
  stroke: #fff;
  stroke-width: 2;
}

.mark__pin {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.2s ease;
  pointer-events: auto;
  cursor: pointer;
}

.spec[data-active="1"] .mark[data-mark="1"] .mark__pin,
.spec[data-active="2"] .mark[data-mark="2"] .mark__pin,
.spec[data-active="3"] .mark[data-mark="3"] .mark__pin {
  transform: scale(1.16);
}

.mark__pin circle {
  fill: var(--accent);
  stroke: #fff;
  stroke-width: 2.2;
}

.mark__pin text {
  fill: var(--accent-ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 15px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}

/* The proof that the file is geometry: a zoom control over the canvas. Vector
   edges are redrawn at every level, so they stay hard where a bitmap would go
   soft. The slider is visible here, so it is styled rather than hidden. */
.spec__zoom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem 0.75rem;
  padding: 0.75rem 1rem 0.85rem;
  border-top: 1px solid var(--line);
}

.spec__zoom-label {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.spec__zoom-range {
  flex: 1 1 8rem;
  min-width: 0;
  height: 1.1rem;
  margin: 0;
  background: none;
  cursor: ew-resize;
  -webkit-appearance: none;
  appearance: none;
}

.spec__zoom-range::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: 999px;
  background: var(--line-strong);
}

.spec__zoom-range::-webkit-slider-thumb {
  width: 0.85rem;
  height: 0.85rem;
  margin-top: -0.375rem;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  -webkit-appearance: none;
  appearance: none;
}

.spec__zoom-range::-moz-range-track {
  height: 2px;
  border-radius: 999px;
  background: var(--line-strong);
}

.spec__zoom-range::-moz-range-thumb {
  width: 0.85rem;
  height: 0.85rem;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
}

.spec__zoom-range:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 999px;
}

.spec__zoom-value {
  min-width: 3.4rem;
  color: var(--fg);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  font-weight: 650;
}

.spec__zoom-fit {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg);
  font-family: inherit;
  font-size: 0.7rem;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.18s ease, color 0.18s ease;
}

.spec__zoom-fit:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.spec__zoom-fit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.spec__zoom-hint {
  flex: 1 1 100%;
  color: var(--muted);
  font-size: 0.78rem;
}

/* Real numbers from the file the figure shows. */
.spec__readout {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--line);
}

.spec__cell {
  padding: 0.8rem 1rem 0.9rem;
  border-inline-start: 1px solid var(--line);
  transition: background-color 0.2s ease;
}

/* This cell is the fourth claim's mark, so it answers a click the same way. */
.spec__cell[data-mark] {
  cursor: pointer;
}

.spec__cell[data-mark]:hover dt {
  color: var(--accent);
}

.spec__cell:first-child {
  border-inline-start: 0;
}

.spec__cell dt {
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.spec__cell dd {
  margin: 0.35rem 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

.spec__zero {
  color: var(--accent);
  font-weight: 650;
}

.spec[data-active="4"] .spec__cell[data-mark="4"] {
  background: var(--accent-soft);
}

.spec[data-active="4"] .spec__cell[data-mark="4"] dt {
  color: var(--accent);
}

/* --- how it works: the reader's route, then the tracer's ------------------- */

/* Three numbered columns between hairlines: a route through the tool, light
   enough that it does not compete with the stages underneath. */
.flow {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-block: 1px solid var(--line);
}

.flow__step {
  padding: 1.6rem 1.5rem 1.75rem;
  border-inline-start: 1px solid var(--line);
}

.flow__step:first-child {
  padding-inline-start: 0;
  border-inline-start: 0;
}

.flow__step:last-child {
  padding-inline-end: 0;
}

.flow__no {
  display: grid;
  place-items: center;
  width: 1.85rem;
  height: 1.85rem;
  margin-bottom: 0.85rem;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--accent);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  font-weight: 650;
}

.flow__step h3 {
  font-size: 1rem;
}

.flow__step p {
  margin-top: 0.5rem;
  color: var(--muted);
  font-size: 0.93rem;
}

/* The four stages of a trace, side by side. */
.rail {
  margin-top: 3.25rem;
}

.rail__intro h3 {
  font-size: 1.1rem;
}

.rail__intro p {
  margin-top: 0.4rem;
  color: var(--muted);
  font-size: 0.93rem;
}

.rail__track {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.75rem;
  margin-top: 1.75rem;
}

/* The rail loop adds one emphasis at a time: the stage the loop is sitting on
   is drawn at full strength, and the drawings of the others step back. Their
   words keep their contrast. With no JS, or with reduced motion asked for,
   there is no data-active, nothing dims, and the list still reads as plain
   documentation. */
.rail__track[data-active] .stage__art {
  opacity: 0.42;
  transition: opacity 0.3s ease, border-color 0.3s ease, background-color 0.3s ease;
}

.rail__track[data-active] .stage[aria-current="true"] .stage__art {
  opacity: 1;
  border-color: var(--accent);
  background: var(--accent-soft);
}

.rail__track[data-active] .stage[aria-current="true"] .stage__no {
  color: var(--accent);
}

.stage__art {
  display: grid;
  place-items: center;
  height: 6.5rem;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--accent);
}

.stage__art svg {
  /* The frame follows the viewBox, so a drawing fills it instead of being
     letterboxed inside a taller box. */
  height: 4.75rem;
  width: auto;
  max-width: 100%;
}

.stage .stage__no {
  margin-top: 1.05rem;
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
}

/* h4 is not part of the heading reset, so it is styled in full here. */
.stage h4 {
  margin: 0.3rem 0 0;
  font-size: 1rem;
  font-weight: 650;
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.stage p {
  margin-top: 0.5rem;
  color: var(--muted);
  font-size: 0.92rem;
}

/* Stage diagrams: one vocabulary of strokes, filled regions and handles.
   Each drawing carries its own scale to reach a common frame, so the strokes
   opt out of it and their widths are stated in screen pixels. */
.dg-line,
.dg-faint,
.dg-handle,
.dg-dash,
.dg-seam,
.dg-mark {
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.dg-line {
  stroke-width: 1.3px;
}

.dg-faint {
  stroke-width: 1.15px;
  opacity: 0.32;
}

.dg-handle {
  stroke-width: 1px;
  opacity: 0.75;
}

.dg-dash {
  stroke-width: 1px;
  stroke-dasharray: 2.9 2.9;
  opacity: 0.5;
}

.dg-grid {
  fill: none;
  stroke: currentColor;
  stroke-width: 0.75px;
  opacity: 0.22;
  vector-effect: non-scaling-stroke;
}

.dg-mark {
  stroke-width: 1.15px;
}

.dg-seam {
  stroke-width: 1.75px;
}

.dg-fill {
  fill: color-mix(in srgb, currentColor 16%, transparent);
}

.dg-fill-2 {
  fill: color-mix(in srgb, currentColor 32%, transparent);
}

.dg-dot {
  fill: currentColor;
}

.dg-lens {
  fill: var(--surface);
  stroke: currentColor;
  stroke-width: 1.15px;
  vector-effect: non-scaling-stroke;
}

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.5rem;
  counter-reset: step;
}

.step {
  position: relative;
  padding: 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.step__index {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  margin-bottom: 1rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.95rem;
  font-weight: 650;
}

.step h3 {
  font-size: 1.05rem;
}

.step p {
  margin-top: 0.6rem;
  color: var(--muted);
  font-size: 0.94rem;
}

/* --- faq: heading above the ledger ----------------------------------------- */

.qa {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2.25rem;
}

.qa__head h2 {
  font-size: clamp(1.6rem, 2.6vw, 2.15rem);
}

/* --- faq ------------------------------------------------------------------- */

.faq {
  display: grid;
  gap: 0.75rem;
}

.faq__item {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.faq__item summary {
  padding: 1.1rem 3rem 1.1rem 1.25rem;
  position: relative;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.faq__item summary::-webkit-details-marker {
  display: none;
}

/* Plus that turns into a minus: the vertical bar collapses when open. */
.faq__item summary::before,
.faq__item summary::after {
  content: "";
  position: absolute;
  right: 1.35rem;
  top: 50%;
  width: 0.85rem;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  transition: transform 0.2s ease;
}

.faq__item summary::after {
  transform: rotate(90deg);
}

.faq__item[open] summary::after {
  transform: rotate(0deg);
}

.faq__item[open] summary {
  border-bottom: 1px solid var(--line);
}

.faq__item p {
  padding: 1.1rem 1.25rem 1.3rem;
  color: var(--muted);
  font-size: 0.94rem;
}

/* The home page's list drops the boxes for hairlines, so nine questions read as
   one ledger rather than nine panels. */
.faq--ledger {
  gap: 0;
}

.faq--ledger .faq__item {
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.faq--ledger .faq__item:last-child {
  border-bottom: 0;
}

.faq--ledger .faq__item summary {
  padding: 1.15rem 3rem 1.15rem 2.85rem;
}

.faq--ledger .faq__item[open] summary {
  border-bottom: 0;
}

.faq--ledger .faq__item p {
  padding: 0 3rem 1.4rem 2.85rem;
}

.faq__no {
  position: absolute;
  inset-inline-start: 0;
  top: 1.3rem;
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.74rem;
  letter-spacing: 0.06em;
}

.faq__item[open] .faq__no {
  color: var(--accent);
}

/* --- tools: one lead card, then a list ------------------------------------- */

/* Four equal cards read as inventory. One worked example beside a plain list
   says which of these is the one most people want. */
.tools {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  gap: 1.75rem;
}

.tool-lead {
  display: grid;
  padding: 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.2s ease, transform 0.2s ease;
}

.tool-lead:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
}

.tool-lead__tag,
.tool-row__tag {
  color: var(--muted);
  font-size: 0.72rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.tool-lead h3 {
  margin-top: 0.45rem;
  font-size: 1.25rem;
}

.tool-lead p {
  margin-top: 0.55rem;
  color: var(--muted);
  font-size: 0.94rem;
}

/* The two halves of the trade, shown on the checkerboard the transparency in a
   PNG is meant to land on. */
.tool-lead__pair {
  display: grid;
  /* minmax(0, ...) both sides: a replaced element's intrinsic width would
     otherwise take the whole row and leave the second shot a sliver. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1px;
  margin-top: 1.35rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--line);
  overflow: hidden;
}

.tool-lead__shot {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 8 / 5;
  min-height: 0;
  overflow: hidden;
  background-color: var(--surface);
  background-image: linear-gradient(45deg, var(--checker) 25%, transparent 25%, transparent 75%, var(--checker) 75%),
    linear-gradient(45deg, var(--checker) 25%, transparent 25%, transparent 75%, var(--checker) 75%);
  background-position: 0 0, 0.5rem 0.5rem;
  background-size: 1rem 1rem;
}

.tool-lead__shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.tool-lead__shot em {
  position: absolute;
  inset-block-end: 0.5rem;
  inset-inline-start: 0.5rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-size: 0.66rem;
  font-style: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.tool-list {
  display: grid;
  border-top: 1px solid var(--line);
}

.tool-row {
  position: relative;
  display: grid;
  gap: 0.35rem;
  padding: 1.15rem 2.75rem 1.25rem 0;
  border-bottom: 1px solid var(--line);
}

.tool-row h3 {
  font-size: 1.05rem;
}

.tool-row p {
  color: var(--muted);
  font-size: 0.92rem;
}

.tool-row__go {
  position: absolute;
  inset-inline-end: 0.6rem;
  top: 50%;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: -0.575rem;
  color: var(--muted);
  transition: color 0.2s ease, transform 0.2s ease;
}

.tool-row:hover .tool-row__go {
  color: var(--accent);
  transform: translateX(3px);
}

/* --- closing call to action ------------------------------------------------ */

/* The band is the drawing surface again: a dark canvas with the same grid the
   specimen sits on, so the page ends inside the tool rather than on a banner. */
.closer {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) auto;
  align-items: center;
  gap: 2.5rem;
  padding: 3rem;
  border-radius: var(--radius-lg);
  background-color: #0e1420;
  background-image: linear-gradient(rgb(255 255 255 / 6%) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 6%) 1px, transparent 1px);
  background-size: 1.5rem 1.5rem;
  box-shadow: var(--shadow-lg);
  color: #eef2f8;
}

.closer h2 {
  color: #f4f7fb;
  font-size: clamp(1.5rem, 2.7vw, 2.05rem);
}

.closer p {
  max-width: 32rem;
  margin-top: 0.7rem;
  color: #a9b5c6;
}

.closer__facts {
  display: grid;
  gap: 0.45rem;
  margin-top: 1.35rem;
}

.closer__facts li {
  position: relative;
  padding-inline-start: 1.45rem;
  color: #cbd5e3;
  font-size: 0.9rem;
}

.closer__facts li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.72em;
  width: 0.85rem;
  height: 1px;
  background: var(--accent-2);
}

.closer__act {
  display: grid;
  justify-items: center;
  gap: 0.85rem;
}

/* The band is dark in both themes, so the button steps back to plain white. */
.closer .button--primary {
  background-image: none;
  background-color: #fff;
  color: #0e1420;
  box-shadow: 0 14px 34px -16px rgb(0 0 0 / 70%);
}

.closer__formats {
  margin: 0;
  color: #7d8ba1;
  font-size: 0.76rem;
  letter-spacing: 0.03em;
}

/* --- before / after compare ------------------------------------------------ */

.compare {
  margin: 0;
}

/* One custom property drives the figure: the traced layer is clipped at --pos
   and the divider sits on the same line. The stage keeps one fixed 16/9 shape
   for every sample, so picking a picture cannot change the viewer's height.
   Supply artwork at that same shape and it lands edge to edge, uncropped. */
.compare__stage {
  position: relative;
  /* A definite width keeps the frame flush with the bar and filmstrip. */
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 40rem;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  box-shadow: var(--shadow-lg);
  cursor: ew-resize;
}

.compare__stage:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.compare__img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* Fills the frame edge to edge: a 16/9 source lands uncropped, anything
     other than that is centre-cropped like the filmstrip tiles. */
  object-fit: cover;
}

.compare__after {
  position: absolute;
  inset: 0;
  clip-path: inset(0 0 0 var(--pos, 52%));
}

.compare__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos, 52%);
  width: 2px;
  margin-left: -1px;
  background: rgb(255 255 255 / 88%);
  box-shadow: 0 0 0 1px rgb(13 17 23 / 22%);
  pointer-events: none;
}

/* The knob is fixed light-on-dark in both schemes: it reads as a control over
   artwork of any colour. */
.compare__divider svg {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2.9rem;
  height: 2.9rem;
  padding: 0.75rem;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgb(255 255 255 / 94%);
  color: #0d1117;
  box-shadow: 0 10px 24px -10px rgb(13 17 23 / 55%), inset 0 0 0 1px rgb(13 17 23 / 10%);
}

.compare__tag {
  position: absolute;
  top: 0.9rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  background: rgb(13 17 23 / 76%);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.compare__tag--before {
  left: 0.9rem;
}

.compare__tag--after {
  right: 0.9rem;
}

/* Shown in place of the traced layer when a sample has no SVG beside it yet. */
.compare__tag--pending {
  right: 0.9rem;
  display: none;
  background: rgb(255 255 255 / 92%);
  color: #0d1117;
}

.compare__tag.is-hidden {
  opacity: 0;
}

/* The artwork's action corner: the traced file is a click away from the picture
   it belongs to instead of somewhere below the strip. The row sits above the
   invisible slider in the stack, which is what makes it clickable at all.
   Deliberately small: the artwork is the subject here, so the pills read as a
   corner label rather than as the page's primary buttons. */
.compare__actions {
  position: absolute;
  right: 0.75rem;
  bottom: 0.75rem;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem;
}

/* Copying the source and reading it are steps before the download, so they are
   quieter pills than the filled one beside them. They use the surface token
   rather than a literal white: they sit on top of the artwork in both themes,
   so they have to be the palette's own panel, not a light-mode assumption. The
   `:focus-within` ring on the stage supplies the accent; the pill only needs a
   ring that separates it from the picture. */
.compare__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  color: var(--fg);
  font-family: inherit;
  font-size: 0.62rem;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.18s ease, color 0.18s ease, background-color 0.18s ease;
}

.compare__action:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--surface);
}

.compare__action:focus-visible {
  outline: 2px solid var(--surface);
  outline-offset: 2px;
}

.compare__action[aria-expanded="true"] {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--surface);
}

/* The one action that actually produces the file, so it is the filled one. It
   borrows `.button--primary`'s recipe rather than a flat `--accent` fill: the
   gradient is what the palette uses to mark the primary action everywhere else
   on the page, and here it is the only thing distinguishing this pill from the
   two quieter ones beside it. */
.compare__download {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem 0.3rem 0.5rem;
  border-radius: 999px;
  background-image: linear-gradient(120deg, var(--grad-a), var(--grad-b));
  background-color: var(--accent);
  color: var(--accent-ink);
  font-size: 0.65rem;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.compare__download svg {
  width: 0.9rem;
  height: 0.9rem;
}

.compare__download:hover {
  transform: translateY(-1px);
  box-shadow: 0 12px 26px -12px var(--glow), var(--shadow-md);
}

.compare__download:focus-visible {
  outline: 2px solid var(--surface);
  outline-offset: 2px;
}

/* Full-bleed and invisible: the native slider already gives us pointer drag,
   click-to-jump and the arrow keys, so none of that is reimplemented here. */
.compare__range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  -webkit-appearance: none;
  appearance: none;
}

/* --- the viewer is one panel ----------------------------------------------- */

/* Stage, name bar and filmstrip share a frame: only the stage's top corners and
   the strip's bottom corners are rounded, so the three read as one widget. */
.compare--stacked .compare__stage {
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* Chrome around the artwork rather than part of it, so it follows the page
   palette: light ink on a light bar, dark ink on a dark one. */
.compare__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.9rem;
  padding: 0.7rem 0.95rem;
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--surface);
  color: var(--fg);
}

.compare__name {
  font-size: 0.95rem;
  font-weight: 600;
}

.compare__mode {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

/* Bitmap weight next to vector weight: the reason the download is small, shown
   beside the name of the sample it was measured on. */
.compare__sizes {
  color: var(--muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

/* The traced file itself, a scrollable band between the viewer and the strip.
   Long lines wrap rather than force the page sideways; the band is capped so a
   600 KB file cannot push the gallery off the screen. */
.compare__source {
  max-height: 16rem;
  margin: 0;
  padding: 0.85rem 1rem;
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--bg-tint);
  color: var(--fg);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem;
  line-height: 1.6;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}


/* --- demo gallery ---------------------------------------------------------- */

/* The gallery is the filmstrip of the viewer: every sample is a button, and the
   one on screen is marked. */
.gallery {
  display: grid;
}

/* The strip is welded to the foot of the viewer, so the tiles read as the
   filmstrip of one gallery rather than a separate list of pictures. A narrow
   minimum keeps the count per row high on every screen. */
.gallery--thumbs {
  grid-template-columns: repeat(auto-fit, minmax(min(6rem, 100%), 1fr));
  gap: 0.55rem;
  padding: 0.75rem;
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: var(--surface-2);
}

/* Reset the button down to a plain block, then let the art fill the tile. */
.thumb {
  display: block;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* Square and cropped: one shape for every tile whatever the shape of the artwork
   inside it, so the strip stays an even row of squares. */
.thumb img {
  display: block;
  width: 100%;
  /* Needed for `aspect-ratio` to win: the markup carries width/height attributes
     for the layout hint, and a definite height would override the ratio. */
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  opacity: 0.68;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}

.thumb:hover img,
.thumb:focus-visible img {
  border-color: var(--line-strong);
  opacity: 1;
}

.thumb:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.thumb.is-active img {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow-md);
  opacity: 1;
}

/* --- a sample with no traced SVG ------------------------------------------- */

/* Nothing to reveal, so the slider, the divider, the traced layer and the
   download all step aside and the badge takes their place. */
.compare--pending .compare__after,
.compare--pending .compare__divider,
.compare--pending .compare__range,
.compare--pending .compare__download,
.compare--pending .compare__action,
.compare--pending .compare__tag--after {
  display: none;
}

.compare--pending .compare__tag--pending {
  display: block;
}

.compare--pending .compare__stage {
  cursor: default;
}

/* --- closing call to action ------------------------------------------------ */

.cta-band {
  padding-block: 4.5rem;
}

.cta {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 2rem;
  padding: 2.75rem;
  border-radius: var(--radius-lg);
  background-image: linear-gradient(115deg, var(--grad-a), var(--grad-b));
  box-shadow: 0 30px 60px -30px var(--glow);
}

.cta h2 {
  color: var(--accent-ink);
  font-size: clamp(1.5rem, 2.6vw, 2rem);
}

.cta p {
  max-width: 34rem;
  margin-top: 0.7rem;
  /* Full `--accent-ink`, no alpha: the lighter end of the light palette's
     gradient only clears 4.5:1 against pure white. Size and weight carry the
     de-emphasis instead. */
  color: var(--accent-ink);
}

/* The band is the gradient, so the button in it has to step back to plain. */
.cta .button--primary {
  background-image: none;
  background-color: var(--surface);
  color: var(--fg);
  box-shadow: var(--shadow-md);
}

/* --- footer ---------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--bg-tint);
  padding-block: 3rem 2.25rem;
}

.site-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
}

.site-footer__brand .brand {
  --brand-size: 1.05rem;
}

.site-footer__brand p {
  max-width: 20rem;
  margin-top: 0.75rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.site-footer__title {
  font-size: 0.78rem;
  font-weight: 650;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

.site-footer__col ul {
  display: grid;
  gap: 0.55rem;
  margin-top: 1rem;
  font-size: 0.92rem;
}

.site-footer__col p {
  max-width: 20rem;
  margin-top: 1rem;
  color: var(--muted);
  font-size: 0.92rem;
}

.site-footer__col a:hover {
  color: var(--accent);
}

.site-footer__base {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.85rem;
}

/* --- responsive ------------------------------------------------------------ */

@media (max-width: 60rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 2.5rem;
    padding-block: 3.5rem 4rem;
  }

  .lede {
    max-width: none;
  }

  .spec,
  .tools,
  .cta {
    grid-template-columns: minmax(0, 1fr);
  }

  .spec,
  .tools {
    gap: 2.25rem;
  }

  .rail__track {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2rem 1.75rem;
  }

  .flow {
    grid-template-columns: minmax(0, 1fr);
  }

  .flow__step {
    padding-inline: 0;
    border-inline-start: 0;
    border-top: 1px solid var(--line);
  }

  .flow__step:first-child {
    border-top: 0;
  }

  .closer {
    grid-template-columns: minmax(0, 1fr);
    justify-items: start;
    padding: 2.25rem;
  }

  .closer__act {
    justify-items: start;
  }

  .cta {
    justify-items: start;
    padding: 2rem;
  }

  .site-footer__top {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 2rem;
  }

  .site-footer__brand {
    grid-column: 1 / -1;
  }

  /* The header is at its fullest just above the point where the nav drops out:
     brand, five nav links, the theme toggle and the sign-up pill. The icon-only
     artwork frees about 140px there, which is what stops the nav wrapping to
     three lines. The accessible name still comes from the image's alt text. */
  .google-button__wide {
    display: none;
  }

  .google-button__narrow {
    display: block;
  }
}

@media (max-width: 46rem) {
  .site-nav {
    display: none;
  }

  /* The nav normally pushes the actions right; with it hidden they take over. */
  .header-actions {
    margin-inline-start: auto;
  }

  .section {
    padding-block: 3.5rem;
  }

  #demo {
    padding-block-start: 2.25rem;
  }

  .stats__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .stat {
    padding: 1.25rem 1.1rem;
  }

  .spec__readout {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .spec__cell:nth-child(odd) {
    border-inline-start: 0;
  }

  .spec__cell:nth-child(n + 3) {
    border-top: 1px solid var(--line);
  }

  .spec__cell dt {
    font-size: 0.66rem;
  }

  .spec__cell dd {
    font-size: 0.8rem;
  }

  .cta-band {
    padding-block: 3.5rem;
  }

  .cta {
    padding: 1.6rem;
  }

  .site-footer__top {
    grid-template-columns: minmax(0, 1fr);
  }

  .dropzone {
    min-height: 15rem;
    padding: 2.5rem 1.25rem;
  }

  .compare__tag {
    top: 0.6rem;
    padding: 0.25rem 0.55rem;
    font-size: 0.68rem;
  }

  .compare__tag--before {
    left: 0.6rem;
  }

  .compare__tag--after {
    right: 0.6rem;
  }

  .compare__divider svg {
    width: 2.5rem;
    height: 2.5rem;
    padding: 0.65rem;
  }

  .rail__track {
    gap: 1.5rem;
  }

  /* The name beside the avatar is the first thing to go when the header runs
     out of room; the avatar and the sign-out pill still say who and how. */
  .account__who {
    display: none;
  }

  .account {
    padding: 0.3rem;
  }

  /* A history row is a name on top of its own actions once the two would be
     fighting for the same line. */
  .trace {
    flex-wrap: wrap;
    gap: 0.7rem;
  }

  .trace__actions {
    width: 100%;
  }

  .trace__action {
    flex: 1 1 auto;
    text-align: center;
  }

  /* Side by side, the three demo actions would reach past the middle of the
     stage; stacked in the corner they stay clear of the divider handle. The
     stack is the tap target on a phone, so the pills share one width and one
     row height, and the type comes back up a step — the desktop sizes above
     are sized for a mouse, not for a thumb. */
  .compare__actions {
    right: 0.6rem;
    bottom: 0.6rem;
    flex-direction: column;
    align-items: stretch;
    gap: 0.35rem;
  }

  .compare__action,
  .compare__download {
    justify-content: center;
    min-height: 2.25rem;
    font-size: 0.7rem;
  }

  .compare__download svg {
    width: 1rem;
    height: 1rem;
  }
}

/* Narrowest phones: the wordmark no longer fits beside the brand and the theme
   toggle. The sign-up pill has already dropped to its icon-only artwork at the
   60rem breakpoint above, so only the gap tightens here. */
@media (max-width: 32rem) {
  .site-header__inner {
    gap: 0.75rem;
  }
}

/* --- scroll reveal --------------------------------------------------------- */

/* Progressive enhancement with a hard floor: the base state is visible, and the
   hidden state only exists once the head script has confirmed JS is running and
   the visitor has not asked for reduced motion. A failed script therefore cannot
   leave a section invisible. */
@media (prefers-reduced-motion: no-preference) {
  html.js .reveal {
    opacity: 0;
    transform: translateY(1.25rem);
    transition: opacity 0.6s ease, transform 0.6s ease;
  }

  html.js .reveal.is-visible {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .button,
  .claim,
  .claim::before,
  .claim__no,
  .mark__detail,
  .mark__pin,
  .spec__cell,
  .tool-lead,
  .tool-row__go,
  .dropzone,
  .compare__tag,
  .compare__download,
  .thumb img,
  .post-card,
  .chip,
  .compare__action,
  .rail__track[data-active] .stage__art,
  .spec__zoom-fit,
  .spec__paths,
  .spec__paths-art,
  .faq__item summary::after {
    transition: none;
  }

  .button--primary:hover:not(:disabled),
  .compare__download:hover,
  .tool-lead:hover,
  .post-card:hover,
  .dropzone[data-dragging] {
    transform: none;
  }

  .tool-row:hover .tool-row__go {
    transform: none;
  }
}

/* --- inner pages ----------------------------------------------------------- */

/* The format landing pages and the blog reuse the home page's hero shell, so
   only what is genuinely new lives here: the article measure and the blog
   index cards. */

/* The trail sits first under the sticky header, and it used to start on the
   header's hairline with nothing above it, which read as chrome rather than as
   the top of the page. It now owns the space above, and the section below gives
   up most of its own top padding so the pair keeps the gap it had. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  padding-block-start: 1.5rem;
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--muted);
}

.crumbs li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
}

/* A chevron drawn from two borders rather than a "/" glyph: it points the way
   the trail is read, and it lives inside the crumb that follows it, so a wrap
   can never leave a separator stranded at the start of a line. */
.crumbs li + li::before {
  content: "";
  flex: none;
  width: 0.3em;
  height: 0.3em;
  border-right: 1.5px solid color-mix(in srgb, var(--muted) 55%, transparent);
  border-bottom: 1.5px solid color-mix(in srgb, var(--muted) 55%, transparent);
  transform: rotate(-45deg);
}

/* The links stay quiet until the pointer arrives, and the tint needs room to
   read as a pill. The negative margins hand that padding back to the layout, so
   the first crumb still starts on the shell edge and the gaps stay even. */
.crumbs a {
  padding: 0.3rem 0.55rem;
  margin: -0.3rem -0.55rem;
  border-radius: 999px;
  color: var(--muted);
  text-decoration: none;
  transition: color 140ms ease, background-color 140ms ease;
}

.crumbs a:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

.crumbs a:focus-visible {
  outline-offset: 1px;
  border-radius: 999px;
}

/* The page you are on is a label rather than a link, so it takes the body
   colour and a little more weight. It is also the one crumb that can be a whole
   post title: unclipped it wrapped to three lines on a phone and pushed the h1
   below it into reading the same sentence a second time. One clipped line, and
   the full title is where it belongs, on the h1. */
.crumbs [aria-current="page"] {
  min-width: 0;
  color: var(--fg);
  font-weight: 600;
}

.crumbs__current {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A phone has less height to spend, so the trail gives back some of its headroom
   under the sticky header. */
@media (max-width: 46rem) {
  .crumbs {
    padding-block-start: 1.25rem;
  }
}

/* The crumb is the only thing above an article, so the section below it does
   not need its full 5rem of top padding as well. */
main > .shell:has(> .crumbs) + .section {
  padding-block-start: 2rem;
}

.preview canvas {
  max-width: 100%;
  max-height: 52vh;
}

/* Pickers for the SVG rasteriser, which has no server round trip and therefore
   no progress bar to fill the space the converter leaves empty. */
.field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.1rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.field select {
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--fg);
  font: inherit;
}

.field input[type='color'] {
  width: 3.1rem;
  height: 2.35rem;
  padding: 0.15rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}

.field select:focus-visible,
.field input[type='color']:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--accent-soft);
  outline-offset: 1px;
}

.prose {
  max-width: 44rem;
  font-size: 1.03rem;
  line-height: 1.75;
}

.prose > * + * {
  margin-top: 1.15rem;
}

.prose h2 {
  margin-top: 2.75rem;
  font-size: clamp(1.35rem, 2.2vw, 1.7rem);
}

.prose h3 {
  margin-top: 2rem;
  font-size: 1.12rem;
}

.prose ul,
.prose ol {
  display: grid;
  gap: 0.5rem;
  padding-inline-start: 1.35rem;
  list-style: disc;
}

.prose ol {
  list-style: decimal;
}

.prose li::marker {
  color: var(--muted);
}

.prose a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.prose code {
  padding: 0.15em 0.4em;
  border-radius: 6px;
  background: var(--surface-2);
  font-size: 0.9em;
}

.prose blockquote {
  padding-inline-start: 1.1rem;
  border-inline-start: 3px solid var(--line-strong);
  color: var(--muted);
}

.prose figure {
  margin-top: 1.75rem;
}

.prose figure img {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.prose figcaption {
  margin-top: 0.6rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.post-list {
  display: grid;
  gap: 1rem;
  margin-top: 2.5rem;
}

/* Cards side by side where the list is a set of peers rather than a reading order. */
.post-list--row {
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.post-card {
  display: block;
  padding: 1.5rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 0.2s ease, transform 0.2s ease;
}

.post-card:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
}

.post-card h2,
.post-card h3 {
  font-size: 1.15rem;
}

.post-card p {
  margin-top: 0.5rem;
  color: var(--muted);
  font-size: 0.94rem;
}

.post-card__meta {
  display: block;
  margin-bottom: 0.6rem;
  color: var(--muted);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
