/* HonorBox theme: "stand".

   Monochrome. There is no accent hue anywhere in this theme, including the
   mark, and that is the point rather than an omission.

   The reasoning, because it will otherwise look like something was left out:
   a colour has to earn a place in both schemes or it does not have one. The
   accent here was a burnt red that carried the coin, the buy button, the
   price, focus rings and a rule. Five jobs is how an accent stops meaning
   anything, and the obvious replacement, a gold coin, measured 7.71:1 on ink
   and 2.53:1 on the cream the mark flips to at night. Pushing it to pass
   turned it brown. So the page spends its contrast on hierarchy instead, and
   the one heavy object left on it is the thing you are meant to press.

   The rules that follow:

   - The primary action is the only filled object on the page: solid ink on
     paper, solid paper at night. 19.80:1 and 20.12:1 against their grounds,
     where the old red button was 5.35:1. It cannot be missed and it never
     competes with anything, because there is nothing to compete with.
   - Everything else that needs an edge gets a hairline, never a fill.
   - Quiet surfaces sit one step off the ground, and the hairline carries the
     edge. Separation is space first, ground second, line last.
   - Scale carries hierarchy, not weight. The display face is set at 400 for
     headings; a headline stops shouting and starts reading.

   Palette, true neutral in both directions. Light: ground #ffffff, raised
   #fafafa, line #e5e5e5, ink #0a0a0a, muted #6e6e6e. Dark: ground #000000,
   raised #0f0f0f, line #1f1f1f, ink #fafafa, muted #a3a3a3.

   Measured (WCAG 2.1, against actual grounds rather than against white).
   Light: ink 19.80 on ground and 18.97 on raised, muted 5.10 and 4.89,
   primary button label 19.80, focus ring 19.80. Dark: ink 20.12 and 18.36,
   muted 8.33 and 7.60, primary button label 20.12, focus ring 20.12.

   Display face: Schibsted Grotesk (OFL, self-hosted in assets/fonts/) at 400
   and 500. Body is the system sans. Both colour schemes are first class; the
   theme mirrors rather than dims. */

@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./assets/fonts/schibsted-grotesk-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("./assets/fonts/schibsted-grotesk-latin-500-normal.woff2") format("woff2");
}

:root {
  color-scheme: light dark;
  --ground: #ffffff;
  --raised: #fafafa;   /* a surface resting on the ground, the hairline gives it its edge */
  --line: #e5e5e5;
  --edge: #d4d4d4;     /* the edge of a picture, which must survive a light screenshot */
  --ink: #0a0a0a;
  --ink-soft: #6e6e6e;
  --on-ink: #ffffff;   /* the label on the primary action */

  --display: "Schibsted Grotesk", "Avenir Next", "Segoe UI", system-ui, sans-serif;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* One vertical scale, used everywhere, so the page keeps a beat. */
  --gap-section: clamp(4.5rem, 11vw, 9rem);
  --gap-block: clamp(1.5rem, 3vw, 2.25rem);
  --pad-page: clamp(1.15rem, 4vw, 2.25rem);
  --measure: 62ch;
  --shell: 66rem;
  --radius: 10px;
  --pill: 999px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* Dark tokens apply in two situations: the reader's system asks for dark and
   they have not chosen otherwise, or they picked dark with the toggle. The two
   blocks below must stay byte identical in their declarations; scripts/test/
   theme-toggle.test.js fails the build if they drift, because a palette that
   is right in one path and stale in the other is the worst kind of bug to
   see, appearing only for readers whose setup differs from yours. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #000000;
    --raised: #0f0f0f;
    --line: #1f1f1f;
    --edge: #2e2e2e;
    --ink: #fafafa;
    --ink-soft: #a3a3a3;
    --on-ink: #000000;
  }
}
:root[data-theme="dark"] {
  --ground: #000000;
  --raised: #0f0f0f;
  --line: #1f1f1f;
  --edge: #2e2e2e;
  --ink: #fafafa;
  --ink-soft: #a3a3a3;
  --on-ink: #000000;
}

/* Native widgets, scrollbars and form controls read `color-scheme`, not our
   custom properties, so an explicit choice has to narrow it or the scrollbar
   keeps the system's colour while the page changes underneath it. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* Theme switch as a circular reveal. The browser's default is to cross-fade
   the two snapshots, which reads as a muddy flicker between two grounds this
   far apart, so both animations are cancelled and the script drives a growing
   circle on the incoming layer instead. Stacking matters: the new theme sits
   above the old one and is clipped, so the old page is what shows through
   until the circle covers it.

   Browsers without view transitions ignore all of this and switch instantly,
   which is a fine outcome rather than a broken one. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
  }
  ::view-transition-old(root) { z-index: 0; }
  ::view-transition-new(root) { z-index: 1; }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1rem, 0.97rem + 0.16vw, 1.05rem);
  line-height: 1.65;
  font-synthesis-weight: none;
}

main { max-width: var(--shell); margin: 0 auto; padding: 0 var(--pad-page) var(--gap-section); }

/* Type scale. Size carries the hierarchy and weight stays out of it: headings
   are set at 400, the lightest weight the family ships. The headline was 700
   at 4.25rem, which is what "shouting" looks like when you measure it. At 400
   it can stay large and still read as a sentence rather than a claim. */
h1, h2, h3 { font-family: var(--display); font-weight: 400; line-height: 1.12; letter-spacing: -0.02em; }
h1 {
  font-size: clamp(2.35rem, 1.5rem + 3.6vw, 3.75rem);
  line-height: 1.04; margin: 0 0 0.4em; text-wrap: balance; letter-spacing: -0.032em;
}
h2 {
  font-size: clamp(1.6rem, 1.25rem + 1.6vw, 2.25rem);
  margin: 0 0 0.6em; text-wrap: balance; letter-spacing: -0.028em;
}
h3 { font-size: clamp(1.05rem, 1rem + 0.28vw, 1.18rem); margin: 0 0 0.35em; font-weight: 500; }

a { color: var(--ink); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }
a:focus-visible, summary:focus-visible {
  outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 3px;
}
::selection { background: var(--ink); color: var(--ground); }
.muted { color: var(--ink-soft); }

.skip-link {
  position: absolute; left: 1rem; top: -3.5rem; z-index: 20;
  background: var(--raised); color: var(--ink); font-weight: 600; font-size: 0.9rem;
  border: 1.5px solid var(--ink);
  padding: 0.55rem 1rem; border-radius: 5px; text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 1rem; }

/* header: no rule under it. The chrome is the quietest thing on the page and a
   hairline across the full width only re-states an edge the layout already
   makes obvious. */
.site-head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem 1.5rem;
  flex-wrap: wrap;
  max-width: var(--shell); margin: 0 auto;
  padding: 1.6rem var(--pad-page) 0.75rem;
}
.brand {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--display); font-weight: 500; font-size: 1.1rem;
  color: var(--ink); text-decoration: none; letter-spacing: -0.02em;
}
.brand-mark { color: var(--ink); width: 22px; height: 22px; flex-shrink: 0; }
.site-nav { display: flex; gap: 0.3rem 1.5rem; flex-wrap: wrap; }
.site-nav a {
  color: var(--ink-soft); text-decoration: none; font-size: 0.92rem; font-weight: 500;
  padding: 0.2rem 0; border-bottom: 1.5px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.site-nav a:hover { color: var(--ink); }

/* Theme toggle. Hidden by default and revealed by the script that gives it
   behaviour, so a reader with JS blocked never sees a control that does
   nothing. The two glyphs live in one button and swap on the resolved theme,
   which keeps a single focus target and one accessible name to maintain. */
.head-actions { display: flex; align-items: center; gap: 1.5rem; }
.theme-toggle {
  display: none;
  align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 999px;
  color: var(--ink-soft); cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--line); }
.theme-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.js .theme-toggle { display: inline-flex; }
.theme-toggle svg { width: 17px; height: 17px; display: block; }
/* Show the destination, not the current state: in light you see the moon you
   are heading towards. */
.theme-toggle .icon-moon { display: block; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
}

/* footer: an index, not a paragraph */
.site-foot {
  border-top: 1px solid var(--line);
  color: var(--ink-soft); font-size: 0.875rem;
  max-width: var(--shell); margin: 0 auto;
  padding: 2.25rem var(--pad-page) 3.5rem;
}
.foot-links { max-width: 52rem; line-height: 2.4; margin: 0 0 1.1rem; color: var(--line); }
.foot-links a, .foot-legal a { color: var(--ink-soft); text-decoration: none; }
.foot-links a { padding: 0.15rem 0.25rem; }
.foot-links a:hover, .foot-legal a:hover { color: var(--ink); text-decoration: underline; }
.foot-legal { margin: 0; font-size: 0.83rem; }

/* hero: centred, and mostly air. It used to open with a 128px mark floating
   above the headline, a monospace kicker in caps and a monospace subline
   under the buttons. Four things competing before the reader reached the
   product. It is a headline, a line of explanation and one button now. */
.hero {
  text-align: center;
  padding: clamp(3.5rem, 10vw, 7.5rem) 0 clamp(1rem, 3vw, 2rem);
}
.hero h1 { max-width: 20ch; margin-inline: auto; }
.lede {
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.2rem); line-height: 1.6;
  color: var(--ink-soft); max-width: 58ch; margin: 0 auto 2.5rem; text-wrap: pretty;
}
.hero-ctas { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; align-items: center; }

/* Motion. One gesture, used once: content rises 8px and fades in as it
   arrives, and never moves again. Everything else is a 180ms colour change.
   Content is visible by default and the observer adds the effect, so a
   scripting failure or an unsupported browser leaves a readable page rather
   than a blank one. */
@media (prefers-reduced-motion: no-preference) {
  .js-reveal { opacity: 0; transform: translateY(8px); }
  .js-reveal.is-in {
    opacity: 1; transform: none;
    transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
    transition-delay: var(--reveal-delay, 0ms);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* No transforms, no reveals, nothing left mid-transition. */
  .js-reveal, .js-reveal.is-in { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* buttons. The primary one is the only filled object on the page, so it does
   not need a colour to be found: solid ink on paper, solid paper at night,
   19.80:1 and 20.12:1 against their grounds. The old red managed 5.35:1 and
   spent a hue to do it. Everything secondary is a hairline and no fill, which
   is the whole hierarchy: one thing is filled, and that is the thing to press. */
.btn {
  display: inline-block; padding: 0.72rem 1.4rem; border-radius: var(--pill);
  font-weight: 500; text-decoration: none;
  border: 1px solid var(--line); color: var(--ink); background: transparent;
  font-size: 0.97rem; line-height: 1.35;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), opacity 0.18s var(--ease);
}
.btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.btn-buy {
  background: var(--ink); color: var(--on-ink); border-color: var(--ink); font-weight: 500;
}
/* Hover lightens rather than recolours. There is no second brand colour to
   move to, and opacity keeps the label's contrast proportional instead of
   landing it on some in-between grey. */
.btn-buy:hover { opacity: 0.86; }
.btn-ghost:hover { border-color: var(--ink); }
.btn-big { padding: 0.85rem 1.75rem; font-size: 1.02rem; }
.btn-disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------- products ---------- */

.products {
  display: grid; gap: 1rem;
  grid-template-columns: 1fr;
  margin-top: var(--gap-section);
}
.product-card {
  --card-pad: clamp(1.5rem, 3.5vw, 2.5rem);
  display: flex; flex-direction: column;
  padding: var(--card-pad);
  border-radius: 12px;
}
.pc-head { margin-bottom: 0.2rem; }
.badge {
  display: inline-block; font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--ink-soft); border: 1px solid var(--line);
  border-radius: 3px; padding: 0.2rem 0.55rem; margin-bottom: 0.9rem;
}
.product-card h3 { margin: 0; font-size: clamp(1.3rem, 1.15rem + 0.6vw, 1.55rem); }
.product-card h3 a { color: inherit; text-decoration: none; }
.product-card h3 a:hover { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.pc-tagline { color: var(--ink-soft); margin: 0.55rem 0 0; text-wrap: pretty; }
/* Feature lines, not bullets, and no markers at all. Eight markered items read
   as a pitch deck; the same eight set as plain lines with room between them
   read as a specification, which is what a person who will open the source
   before buying is actually here for. */
.pc-features { list-style: none; padding: 0; margin: 1.5rem 0 1.75rem; }
.pc-features li {
  margin: 0; padding: 0.55rem 0;
  font-size: 0.94rem; line-height: 1.55; color: var(--ink-soft);
}
.pc-buy {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.9rem 1.25rem; flex-wrap: wrap;
  margin-top: auto; padding-top: 1.4rem;
}
.price {
  font-family: var(--display); font-size: 1.55rem; font-weight: 800;
  line-height: 1.2; letter-spacing: -0.02em; color: var(--ink);
}
.price small {
  font-family: var(--sans); font-size: 0.72rem; font-weight: 400; color: var(--ink-soft);
  display: block; margin-top: 0.25rem; letter-spacing: 0.01em;
}

/* The flagship card. It used to be a reversed slab, the one inverted surface
   on the site. A slab that dark is a second heavy object arguing with the
   button inside it, and it is most of what made the page look like it was
   trying something. It is a card now: a hairline, a radius, and room. The
   layout it had was good and is kept, wide and shallow rather than a tall
   block, so the identity and the buy action hold a left rail and the
   specification runs beside them. */
.product-card.flagship {
  background: var(--raised);
  border: 1px solid var(--line);
  padding: clamp(1.75rem, 4vw, 3.25rem);
  border-radius: var(--radius);
}
.product-card.flagship h3 {
  font-size: clamp(1.5rem, 1.2rem + 1.3vw, 2rem); letter-spacing: -0.025em;
}
.product-card.flagship .pc-tagline { max-width: 34ch; }
/* A hairline between spec lines. Inside a specification a rule is structure,
   not decoration: it says these are separate claims. */
.product-card.flagship .pc-features li + li { border-top: 1px solid var(--line); }
.product-card.flagship .pc-buy { padding-top: 1.6rem; }

@media (min-width: 56rem) {
  .product-card.flagship {
    display: grid;
    grid-template-columns: minmax(0, 21rem) minmax(0, 1fr);
    /* auto/1fr, not auto/auto. The spec column spans both rows and is always
       the taller one, and with two auto rows the grid split its surplus
       between them, opening a hole in the middle of the left rail: between
       what the product is and what it costs, which is the one place a reader
       must not be interrupted. Giving row 2 the surplus puts the slack under
       the button instead, where it reads as air. */
    grid-template-rows: auto 1fr;
    column-gap: clamp(2.5rem, 6vw, 5rem);
    align-items: start;
  }
  .product-card.flagship .pc-head { grid-column: 1; grid-row: 1; margin: 0; }
  .product-card.flagship .pc-buy {
    grid-column: 1; grid-row: 2; align-self: start;
    margin-top: 1.75rem; padding-top: 1.6rem;
    border-top: 1px solid var(--line);
    justify-content: flex-start; gap: 1.5rem;
  }
  .product-card.flagship .pc-features {
    grid-column: 2; grid-row: 1 / span 2; margin: 0;
  }
}

/* companion: a quieter row than the flagship, on the same hairline system */
.product-card.companion {
  flex-direction: row; align-items: center; gap: 0.5rem 1.5rem;
  padding: 1.25rem var(--card-pad);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.product-card.companion .badge,
.product-card.companion .pc-features { display: none; }
.product-card.companion .pc-head { flex: 1; min-width: 14rem; margin: 0; }
.product-card.companion h3 { font-size: 1.12rem; }
.product-card.companion .pc-tagline { font-size: 0.88rem; margin-top: 0.3rem; max-width: 52ch; }
.product-card.companion .pc-buy {
  margin: 0; padding: 0;
  align-self: center; align-items: center; flex-wrap: nowrap; gap: 1.15rem;
}
.product-card.companion .price { font-size: 1.12rem; }
.product-card.companion .btn { padding: 0.5rem 1rem; font-size: 0.9rem; }

/* ---------- home sections ---------- */
/* Space separates them. No rule across the top: at this heading size the
   heading is the divider. */
.steps, .compare, .faq, .showcase {
  margin-top: var(--gap-section);
}

/* Each band opens the same way: a heading, then its standfirst under it, both
   centred, then the content left-aligned beneath. The heading and standfirst
   used to sit side by side as a two-column masthead, which is a device, and
   it only existed above 56rem so the page had two different section grammars
   depending on the window. One grammar now, at every width.
   The content itself stays left-aligned. Centred body text is harder to read,
   and none of this is decoration. */
.steps > h2, .compare > h2, .faq > h2, .showcase > h2 { text-align: center; margin-bottom: 0.35em; }
.compare > .muted, .showcase > .muted, .steps > .muted, .faq > .muted {
  font-size: 0.98rem; max-width: 56ch; margin: 0 auto; text-align: center;
}

/* steps: the numerals set the order, quietly */
.steps-list {
  counter-reset: step; list-style: none; padding: 0; margin: 2.25rem 0 0;
  display: grid; gap: 2.25rem 3rem; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.steps-list li { counter-increment: step; }
.steps-list li::before {
  content: counter(step, decimal-leading-zero);
  display: block; margin-bottom: 0.85rem; padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-weight: 400;
  color: var(--ink-soft); font-size: 0.9rem; letter-spacing: 0.14em; line-height: 1;
}
.steps-list h3 { margin: 0 0 0.4rem; font-size: 1.08rem; }
.steps-list p { margin: 0; color: var(--ink-soft); font-size: 0.93rem; line-height: 1.6; max-width: 54ch; }

/* guide index rows, and the docs index, share one row treatment */
.steps-list:has(h3 a), .doc-list {
  grid-template-columns: 1fr; gap: 0; margin-top: 1.75rem;
  display: grid; list-style: none; padding: 0; counter-reset: none;
}
.steps-list li:has(h3 a), .doc-list li {
  position: relative; padding: 1.3rem 2.75rem 1.4rem 0;
  border-top: 1px solid var(--line);
}
.steps-list li:has(h3 a):last-child, .doc-list li:last-child { border-bottom: 1px solid var(--line); }
.steps-list li:has(h3 a)::before, .doc-list li::before { display: none; }
.steps-list li:has(h3 a)::after, .doc-list li::after {
  content: "\2192"; position: absolute; top: 50%; right: 0.4rem;
  font-family: var(--mono); font-size: 0.95rem; color: var(--ink-soft);
  transform: translateY(-50%);
  transition: color 0.15s ease, transform 0.15s ease;
}
.steps-list li:has(h3 a) p, .doc-list li p { max-width: 64ch; margin: 0; color: var(--ink-soft); font-size: 0.93rem; line-height: 1.6; }
.doc-list h3 { margin: 0 0 0.35rem; font-size: 1.08rem; }
.steps-list h3 a, .doc-list h3 a { color: var(--ink); text-decoration: none; }
.steps-list h3 a::after, .doc-list h3 a::after { content: ""; position: absolute; inset: 0; }
.steps-list li:has(h3 a):hover::after, .doc-list li:hover::after { color: var(--ink); transform: translate(3px, -50%); }
.steps-list li:has(h3 a):hover h3 a, .doc-list li:hover h3 a { color: var(--ink); }

/* compare table: hairlines, and our column marked by ground rather than bold */
/* The table is wider than a phone, so it scrolls. Row labels have to stay put
   or a scrolled reader sees "0%" with no idea which row it is, and the overflow
   has to be visible because macOS hides overlay scrollbars at rest. */
.table-scroll {
  overflow-x: auto; margin-top: 1.6rem;
  background:
    linear-gradient(to right, var(--ground) 30%, transparent) left / 3rem 100% no-repeat local,
    linear-gradient(to left, var(--ground) 30%, transparent) right / 3rem 100% no-repeat local,
    linear-gradient(to right, rgba(0,0,0,0.13), transparent) left / 1.1rem 100% no-repeat scroll,
    linear-gradient(to left, rgba(0,0,0,0.13), transparent) right / 1.1rem 100% no-repeat scroll;
}
.table-scroll:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.table-scroll table { min-width: 40rem; }
.table-scroll th:first-child, .table-scroll td:first-child {
  position: sticky; left: 0; background: var(--ground);
}
table { border-collapse: collapse; width: 100%; font-size: 0.92rem; }
th {
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-soft); font-weight: 500;
}
th, td { text-align: left; padding: 0.8rem 0.9rem; border-bottom: 1px solid var(--line); vertical-align: top; }
th { border-bottom: 1px solid var(--ink); }
td { color: var(--ink-soft); }
td.rowhead { font-weight: 600; color: var(--ink); }
td.num { font-family: var(--mono); }
tr.attn td { font-weight: 600; background: var(--raised); color: var(--ink); }
.compare th:last-child, .compare td:last-child { background: var(--raised); }
.compare th:last-child { color: var(--ink); }
.compare td:last-child { color: var(--ink); font-weight: 600; }

/* faq */
.faq details { border-bottom: 1px solid var(--line); }
.faq details:first-of-type { border-top: 1px solid var(--line); }
.faq summary {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: 1.15rem 0.15rem; font-weight: 600; font-size: 1rem; cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; font-family: var(--mono); font-size: 1.1rem; font-weight: 400;
  color: var(--ink-soft); flex-shrink: 0; line-height: 1;
  transition: transform 0.2s ease; transform-origin: center;
}
.faq summary:hover { color: var(--ink-soft); }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq p { color: var(--ink-soft); margin: 0 0 1.4rem; max-width: var(--measure); padding: 0 0.15rem; font-size: 0.96rem; }
/* Wraps on purpose. A seller writes this label, so it can be any length, and
   nowrap turned a long one into horizontal overflow on a phone. */
.faq-more { color: var(--ink); }

/* note: the page's one piece of plain speech. It used to be 0.94rem grey in a
   tinted box, which is the typography of fine print, on the paragraph that
   tells you when NOT to buy this. Set at reading size in full ink it becomes a
   statement, which is the only thing that paragraph was ever worth. */
.note { margin: var(--gap-section) 0 0; }
.note p {
  margin: 0; font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.22rem); line-height: 1.6;
  max-width: 46ch;
  color: var(--ink);
  padding: 0 0 0 clamp(1.1rem, 2.5vw, 1.6rem);
  border-left: 2px solid var(--line);
}
.note p a { color: var(--ink); }

/* showcase. Five sold themes, five deliberately different palettes, laid out
   in a row. Unframed they read as the page changing its mind five times: the
   owner's word for it was chaos, and that is a fair description of five
   full-bleed colour blocks with nothing holding them.
   Nothing here restyles a theme. Each preview goes into an identical neutral
   mat instead, so the frame is the constant and the colour inside it is
   visibly the sample rather than the site. This is what a shop does with
   paint chips, and it is the whole fix.
   Two columns, not three. Three put five tiles in a 3+2 grid with a hole in
   the corner, and shrank a screenshot of a page to a third of a column, which
   is too small to read the thing being sold. */
.showcase-grid {
  --showcase-gap: clamp(1.1rem, 2.5vw, 1.6rem);
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.3rem, 3vw, 1.9rem) var(--showcase-gap);
  margin-top: 2.25rem;
}
/* An odd last tile centres at the width of the others rather than stretching
   across the row. Keyed on the item count, not on there being five themes, so
   a seller who ships four or six never meets the seam either. */
.showcase-grid > :last-child:nth-child(odd) {
  grid-column: 1 / -1;
  width: calc(50% - (var(--showcase-gap) / 2));
  justify-self: center;
}
.showcase figure {
  margin: 0; height: 100%;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(0.5rem, 1.2vw, 0.7rem);
  transition: border-color 0.18s var(--ease);
}
.showcase img {
  display: block; width: 100%; height: auto;
  border-radius: 5px;
  /* The preview's own edge, because a light theme's screenshot on a light mat
     would otherwise bleed into it. */
  box-shadow: 0 0 0 1px var(--edge);
}
.showcase figcaption {
  font-size: 0.86rem; color: var(--ink-soft);
  margin-top: 0.7rem; padding: 0 0.25rem 0.2rem;
  transition: color 0.18s var(--ease);
}
.showcase-link { text-decoration: none; display: block; height: 100%; }
.showcase-link:hover figure, .showcase-link:focus-visible figure { border-color: var(--ink); }
.showcase-link:hover figcaption { color: var(--ink); }
.showcase-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; border-radius: var(--radius); }

/* ---------- prose ---------- */
/* A centred reading column. It used to be a two-column grid above 64rem with
   the h1 in a left rail, which is the same masthead device the home sections
   were using, and it gave a doc a different shape from the page that links to
   it. A document is a column of text; that is the conventional answer and it
   is the right one. */
.prose { max-width: 44rem; margin: clamp(2.5rem, 5vw, 4rem) auto 0; }
.prose p, .prose ul, .prose ol, .prose blockquote { max-width: var(--measure); }
.prose h1 { font-size: clamp(1.95rem, 1.4rem + 2.2vw, 2.85rem); }
.prose h1 + p { font-size: 1.08rem; color: var(--ink-soft); max-width: 52ch; }
.prose h2 {
  font-size: clamp(1.3rem, 1.15rem + 0.7vw, 1.65rem);
  margin: 2.75em 0 0.75em; padding-top: 1.4em; border-top: 1px solid var(--line);
}
.prose h1 + p + h2, .prose > h2:first-child { border-top: none; padding-top: 0; margin-top: 1.8em; }
.prose h3 { margin: 2em 0 0.5em; }
.prose p { margin: 0 0 1.15em; }
.prose li { margin: 0.4rem 0; }
.prose li::marker { color: var(--ink-soft); }

.product-page { max-width: 44rem; margin: clamp(2rem, 5vw, 3.25rem) auto 0; }
/* The one place a kicker survives: it names the store above a product's
   title, which is wayfinding rather than decoration. Set as quiet text, not
   as monospace capitals. */
.kicker {
  font-size: 0.92rem; color: var(--ink-soft); margin: 0 0 0.6em; letter-spacing: 0;
}
.product-page .kicker { margin-bottom: 0.7em; }
.product-page .lede { margin-left: 0; margin-right: 0; text-wrap: pretty; }
.product-page .prose { margin-top: 0; max-width: 44rem; display: block; }
.product-page .prose > * { max-width: none; }
.product-page .prose p, .product-page .prose ul, .product-page .prose ol { max-width: var(--measure); }

/* The standalone buy block on a product page is the flagship card again, at
   the size the page can carry. Same object, same meaning, so the site has one
   way of saying "this is the thing you pay for". */
.pc-buy.standalone {
  margin: 2.25rem 0 2.75rem; padding: 1.35rem 1.5rem;
  border-radius: 12px;
  background: var(--raised); border: 1px solid var(--line);
}

.pc-buy.standalone:not(:has(.price)) {
  background: none; border: none; padding: 0;
  justify-content: flex-start; margin: 2.75rem 0 0.5rem;
}

.prose pre {
  background: var(--raised); border: 1px solid var(--line); border-radius: 6px;
  padding: 1rem 1.1rem; overflow-x: auto; font-size: 0.85rem; line-height: 1.6;
}
.prose code { font-family: var(--mono); font-size: 0.9em; }
.prose p code, .prose li code, .prose td code {
  background: var(--raised); border: 1px solid var(--line);
  border-radius: 3px; padding: 0.08em 0.32em; color: var(--ink);
  /* A long unbroken identifier in inline code has no wrap opportunity and
     widens the whole page on a phone (the docs carry things like
     `checkout.session.async_payment_succeeded`, 374px at 390px wide). Block
     code keeps its own horizontal scroll; only inline code breaks. */
  overflow-wrap: anywhere;
}
.prose blockquote {
  border-left: 2px solid var(--line); margin: 1.4rem 0; padding: 0.2rem 1.2rem;
  color: var(--ink-soft);
}

/* trust page: the public ledger prints like every other table */
.ledger-total { font-size: 1.05rem; }
.ledger-total strong { font-family: var(--display); font-weight: 500; color: var(--ink); }
.trust h1 + p { color: var(--ink-soft); }

.prose figure { margin: 1.6rem 0; }
.prose img, .gallery img {
  /* height:auto is load-bearing, not cosmetic. These images now carry real
     width/height attributes so the browser can reserve their box; without
     height:auto the explicit height is honoured literally while max-width
     clamps the width, and a 1200x630 panel renders as a stretched portrait. */
  max-width: 100%; height: auto; border-radius: 6px; display: block;
  box-shadow: 0 0 0 1px var(--edge);
}
.gallery {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: 1rem; margin: 1.75rem 0;
}
/* Same seam the showcase band had, and the same rule for it: an odd last tile
   takes the full row rather than sitting stranded at half width. auto-fit means
   the column count changes with the container, so this is keyed on the item
   count alone and holds at every width and every gallery length. */
.gallery > :last-child:nth-child(odd) { grid-column: 1 / -1; }
.prose .table-scroll { max-width: none; }

/* ---------- small screens ---------- */
/* One column below tablet: a screenshot of a whole page at half of a phone is
   not a preview of anything. The centred-orphan rule has to be undone too, or
   the last tile keeps a half-width cap with nothing beside it. */
@media (max-width: 44rem) {
  .showcase-grid { grid-template-columns: 1fr; }
  .showcase-grid > :last-child:nth-child(odd) { width: auto; justify-self: stretch; }
}
@media (max-width: 40rem) {
  .product-card.companion { flex-direction: column; align-items: stretch; }
  .product-card.companion .pc-head { min-width: 0; }
  .product-card.companion .pc-tagline { max-width: none; }
  .product-card.companion .pc-buy {
    margin-top: 1rem; padding: 1rem 0 0;
    align-self: auto; flex-wrap: wrap;
    border-top: 1px solid var(--line);
  }
}
@media (max-width: 35rem) {
  .hero-ctas .btn, .pc-buy .btn { width: 100%; text-align: center; }
  .pc-buy .price { width: 100%; }
  .pc-buy.standalone { align-items: flex-start; flex-direction: column; }
}

/* Print: prefers-color-scheme still reports dark under print media, so a
   dark-mode reader's saved PDF rendered near-white ink on unprinted white
   (~2.6:1). Pin the light palette for paper regardless of screen scheme. */
@media print {
  :root {
    --ground: #ffffff; --raised: #ffffff; --line: #d4d4d4; --edge: #b8b8b8;
    --ink: #0a0a0a; --ink-soft: #6e6e6e;
    --on-ink: #ffffff;
  }
  body { background: #fff; color: var(--ink); }
  .site-nav, .skip-link { display: none; }
  /* A scroll reveal must never reach paper. Anything the observer has not
     got to yet is still at opacity 0, and printing is exactly the case where
     nothing scrolls, so every section below the fold would print blank. */
  .js-reveal, .js-reveal.is-in { opacity: 1 !important; transform: none !important; }
  .product-card.flagship, .pc-buy.standalone { border: 1px solid var(--line); }
}
