/* BuildAPI — hand-authored CSS design system (replaces Tailwind build). */

:root {
  --background: oklch(0.99 0.01 250);
  --foreground: oklch(0.145 0 0);
  --card: oklch(1 0 0);
  --primary: oklch(0.51 0.21 264);
  --primary-foreground: oklch(1 0 0);
  --secondary: oklch(0.72 0.22 315);
  --muted: oklch(0.95 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --border: oklch(0.92 0.01 250);
  --destructive: oklch(0.577 0.245 27.325);
  --radius: 0.625rem;
  /* Every rectangle that holds information — a dashboard card, a marketing
   * card, a bento tile — shares this. There were three values for the same
   * idea (12px, 16px, 22px) across a hundred and thirty places, so the shape
   * of the product changed depending which page you were on. */
  --card-radius: 18px;
  --max-width: 1152px;
}

* , *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  background: var(--background);
  color: var(--foreground);
  line-height: 1.5;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
.container { max-width: var(--max-width); margin: 0 auto; padding: 0 24px; }
.icon { width: 18px; height: 18px; }

/* --- Buttons ---
 *
 * The variants carry the base box themselves, not only .btn.
 *
 * They were modifiers: .btn set the padding, radius and font, and
 * .btn-primary added a colour on top. Every place that wrote
 * class="btn-primary" on its own — the suspend and resume buttons, both
 * download buttons, the installations screens — got a coloured rectangle with
 * no padding, no radius and no font weight, which is what an unstyled button
 * looks like. .btn-secondary was worse: it was written in eight places and
 * defined in none, so those controls had no styling at all.
 *
 * Listing the variants here fixes all of them at once and means the next one
 * written without .btn still comes out looking like a button. */
.btn,
.btn-primary, .btn-secondary, .btn-danger, .btn-ghost, .btn-outline {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 10px; padding: 10px 20px; font-size: 0.875rem; font-weight: 600;
  border: 1px solid transparent; transition: background .15s, opacity .15s; white-space: nowrap;
  cursor: pointer; text-decoration: none; font-family: inherit; line-height: 1.2;
}
.btn-primary { background: var(--primary); color: var(--primary-foreground); }
.btn-primary:hover { opacity: .9; }
/* The quieter half of a pair — "Suspend" beside "Resume", "Back" beside a
 * primary action. Reads as a button, never competes with the one next to it. */
.btn-secondary { background: var(--muted); color: var(--foreground); border-color: var(--border); }
.btn-secondary:hover { background: var(--border); }
/* Blocking an installation, and the other actions somebody should not reach for
 * absent-mindedly. Written in the admin screens and, like .btn-secondary,
 * defined nowhere until a test went looking. */
.btn-danger { background: var(--destructive); color: var(--primary-foreground); }
.btn-danger:hover { opacity: .9; }
.btn-ghost { background: transparent; color: var(--foreground); }
.btn-ghost:hover { background: var(--muted); }
.btn-outline { background: transparent; border-color: var(--border); color: var(--foreground); }
.btn-outline:hover { background: var(--muted); }
.btn-lg { height: 48px; padding: 0 32px; font-size: 1rem; }
.btn .icon { width: 16px; height: 16px; flex-shrink: 0; }
.btn-lg .icon { width: 18px; height: 18px; }

/* --- Header --- */
/* ---- The site header ----------------------------------------------------
 *
 * Two states, and which one you get depends on what is under the header.
 *
 * SOLID is the default and the safe one: a surface, a hairline, dark text.
 * Every page that opens on light content gets it from the first paint.
 *
 * OVERLAY is for a page whose first section is a dark hero. The header has no
 * background at all there, so the hero runs edge to edge behind it and there
 * is no seam across the top of the page — then it turns solid the moment the
 * page moves. That is the whole effect.
 *
 * Overlay is FIXED rather than sticky, and that is the part that makes it
 * work. A sticky header still occupies its own row in the layout, so a
 * transparent one does not reveal the hero — it reveals the page background
 * ABOVE the hero, which is a white band exactly where the seam used to be.
 * Taken out of the flow, the hero starts at the top of the viewport and the
 * header floats over it.
 *
 * It is gated on html.js, and that gate is not a nicety. Nothing but script
 * adds .scrolled, so without script an overlay header would stay transparent
 * for the entire page — white text on white content, all the way down. No
 * script, no overlay, solid header, everything readable. */
.site-header {
  position: sticky; top: 0; z-index: 50; background: rgba(255,255,255,.92);
  backdrop-filter: blur(8px); border-bottom: 1px solid var(--border);
  /* backdrop-filter is deliberately NOT transitioned.
   *
   * Turning it off has to be instant, because "off" is the only value that
   * stops the header being a filtering ancestor — and a filtering ancestor
   * suppresses the menu panel's own blur entirely. Animated, the header spends
   * the transition at blur(0.07px): visually nothing, still an ancestor with a
   * filter, so the glass panel below it renders flat for the whole 300ms and
   * then pops. Frozen mid-transition it never blurs at all.
   *
   * Everything else here animates; this one switches. */
  transition: background-color .3s ease, border-color .3s ease, box-shadow .3s ease;
}

/* Lifted off the page once it has moved. On a solid header this is the only
 * thing that changes, and it is what stops the bar reading as painted onto
 * the content scrolling beneath it.
 *
 * One custom property, set in both scrolled states, because the overlay rule
 * below has to clear the shadow at the top of the page and lands at the SAME
 * specificity as this one — so written as two box-shadow declarations, the
 * later one won and the overlay header scrolled down with no shadow at all.
 * A variable cannot lose that argument. */
:root { --header-lift: 0 1px 2px rgba(15,23,42,.04), 0 10px 30px rgba(15,23,42,.07); }
.site-header { box-shadow: none; }
.site-header.scrolled { box-shadow: var(--header-lift); }

.js .site-header--overlay {
  position: fixed; left: 0; right: 0; top: 0;
  background: transparent; border-bottom-color: transparent;
  backdrop-filter: none; box-shadow: none;
}
.js .site-header--overlay.scrolled {
  background: rgba(255,255,255,.94); border-bottom-color: var(--border);
  backdrop-filter: blur(12px);
  box-shadow: var(--header-lift);
  color: var(--foreground);
}

/* Over a dark hero every piece of the bar has to invert, and .nav-toggle
 * needs no rule of its own — it is built out of currentColor, so it follows
 * whatever the header text is doing. */
.js .site-header--overlay:not(.scrolled) { color: #fff; }
/* Including while the menu is open, because the bar stays dark there now. */
.js .site-header--overlay.nav-is-open:not(.scrolled) { color: #fff; }
.js .site-header--overlay:not(.scrolled) .brand,
.js .site-header--overlay:not(.scrolled) .brand span:last-child { color: #fff; }
.js .site-header--overlay:not(.scrolled) .nav-link,
.js .site-header--overlay:not(.scrolled) .nav-dropdown-trigger,
.js .site-header--overlay:not(.scrolled) .btn-ghost { color: rgba(255,255,255,.82); }
.js .site-header--overlay:not(.scrolled) .nav-link:hover,
.js .site-header--overlay:not(.scrolled) .nav-dropdown-trigger:hover,
.js .site-header--overlay:not(.scrolled) .btn-ghost:hover { color: #fff; }

/* The open menu is a white panel. Hanging it off a transparent bar leaves it
 * floating with a gap of hero showing through where the header should be, so
 * an open menu makes the header solid whatever the scroll position is. */
/* An open menu makes the bar solid — but on an overlay page solid means the
 * SAME SMOKED GLASS the panel is made of, not white.
 *
 * It was white first, and that was wrong twice: a white bar sitting on a
 * smoked panel is two materials pretending to be one component, and the flip
 * dragged the logo and every label through a colour change on the way. Same
 * tint, same blur, one surface — and because the header keeps its white
 * foreground, the close cross stays legible without a rule of its own. */
.js .site-header--overlay.nav-is-open {
  background: rgba(10,15,32,.92);
  border-bottom-color: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: none;
}

/* An uploaded logo is a picture and cannot be recoloured, so when the operator
 * has given us a light-background one AND a dark-background one, both are in
 * the markup and the header shows whichever matches the state it is in. The
 * pair is only rendered when the two files actually differ — see header.php. */
.brand-main, .brand-alt { display: flex; align-items: center; gap: 8px; }
.brand-alt { display: none; }
/* Gated on the header saying it HAS a second lockup. Without that gate this
 * hides the only brand there is on every install that has not uploaded a pair
 * — which is most of them, and which is exactly what it did: the logo
 * disappeared from the home page entirely. */
.js .site-header--overlay.has-alt-logo:not(.scrolled) .brand-main { display: none; }
.js .site-header--overlay.has-alt-logo:not(.scrolled) .brand-alt { display: flex; }

/* ---- The bar on a phone -------------------------------------------------
 *
 * On a wide screen the overlay header is genuinely transparent and the logo
 * has the whole hero behind it. On a phone it does not: the lockup is smaller,
 * it sits directly on the darkest part of the gradient, and the brand's own
 * blue is close enough in luminance to that navy to disappear into it. Both
 * uploaded logos are correct for their slots — the dark-background one is
 * already the variant in use here — so this is not a wrong file, it is a
 * wordmark with nothing behind it.
 *
 * A thin sheet of smoked glass gives it something to sit on. Light enough that
 * the hero still reads through it, dark enough that the logo separates. Phones
 * only, so the desktop header keeps the clean edge-to-edge hero. */
@media (max-width: 1023px) {
  .js .site-header--overlay:not(.scrolled):not(.nav-is-open) {
    background: rgba(10,15,32,.40);
    -webkit-backdrop-filter: blur(16px) saturate(170%);
    backdrop-filter: blur(16px) saturate(170%);
    border-bottom-color: rgba(140,160,255,.12);
  }
}

/* Every animation here is decoration over a state that is already correct, so
 * turning them off costs nothing but the flourish. */
@media (prefers-reduced-motion: reduce) {
  .site-header,
  .nav-toggle,
  .nav-toggle-bar,
  .mobile-nav a,
  .mobile-cta-btn { transition: none !important; }
  .mobile-nav.open,
  .mobile-nav.open .mobile-nav-group > a,
  .mobile-nav.open .mobile-cta-btn,
  .nav-toggle[aria-expanded="true"] .nav-toggle-box,
  .nav-toggle[aria-expanded="true"] .nav-toggle-bar { animation: none !important; }
}
.site-nav { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; max-width: var(--max-width); margin: 0 auto; }
.brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 1.125rem; }
.brand-mark { width: 32px; height: 32px; background: var(--primary); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; font-size: .875rem; }
/* An uploaded logo replaces the letter mark entirely. Height-capped so any
   aspect ratio the operator uploads sits on the header baseline. */
.brand-logo { height: 32px; width: auto; max-width: 200px; object-fit: contain; display: block; }
.nav-links { display: none; align-items: center; gap: 32px; }
.nav-link { font-size: .875rem; }
.nav-link:hover { color: var(--primary); }
.nav-dropdown { position: relative; }
.nav-dropdown-trigger { display: inline-flex; align-items: center; gap: 4px; font-size: .875rem; background: none; border: none; color: inherit; }
.nav-dropdown-trigger:hover { color: var(--primary); }
.nav-dropdown-menu {
  display: none; position: absolute; top: calc(100% + 8px); left: 0; min-width: 190px;
  background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 6px;
  box-shadow: 0 10px 30px rgba(0,0,0,.08); z-index: 60;
}
.nav-dropdown.open .nav-dropdown-menu { display: block; }
.nav-dropdown-item { display: block; padding: 8px 12px; border-radius: 6px; font-size: .875rem; }
.nav-dropdown-item:hover { background: var(--muted); }
.nav-cta { display: flex; align-items: center; gap: 10px; }

/* ---- The hamburger ------------------------------------------------------
 * 44x44, which is the smallest a finger reliably hits. It was a 30x20 icon
 * with no background and no border, and on the dark home hero that is grey
 * lines on grey — "zero hamburger" was a fair description.
 *
 * It is a glass chip now, made of currentColor so it inverts with the header
 * without a second rule anywhere. */
.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 44px; padding: 0;
  background: color-mix(in oklch, currentColor 8%, transparent);
  border: 1px solid color-mix(in oklch, currentColor 18%, transparent);
  border-radius: 14px; color: inherit; cursor: pointer;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  transition: background .18s ease, border-color .18s ease,
              transform .22s cubic-bezier(.22,1.5,.36,1), box-shadow .22s ease;
}
.nav-toggle:hover { background: color-mix(in oklch, currentColor 14%, transparent); }
/* Bounce. The press is answered before anything else happens. */
.nav-toggle:active { transform: scale(.92); }
.nav-toggle[aria-expanded="true"] {
  background: color-mix(in oklch, var(--primary) 18%, transparent);
  border-color: color-mix(in oklch, var(--primary) 45%, transparent);
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--primary) 12%, transparent);
}
.nav-toggle-box { position: relative; width: 18px; height: 12px; display: block; }
.nav-toggle-bar {
  position: absolute; left: 0; width: 100%; height: 2px; border-radius: 2px;
  background: currentColor;
  transition: transform .44s cubic-bezier(.2,1.6,.35,1),
              opacity .2s ease,
              top .44s cubic-bezier(.2,1.6,.35,1);
}
/* Each bar leaves a beat after the one above it, which is what makes the
 * morph read as three lines moving rather than one shape flipping. */
.nav-toggle-bar:nth-child(1) { top: 0;  transition-delay: 0ms; }
.nav-toggle-bar:nth-child(2) { top: 5px; transition-delay: 55ms; }
.nav-toggle-bar:nth-child(3) { top: 10px; transition-delay: 110ms; }

/* Bars to a cross. The same button closes the menu, and nothing else on screen
 * says so. */
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(1) { top: 5px; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(2) { opacity: 0; transform: scaleX(.4); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(3) { top: 5px; transform: rotate(-45deg); }

/* The sizzle.
 *
 * Two separate things, deliberately kept off the transform the bars are
 * already transitioning on — an animation and a transition fighting over the
 * same property is how one of them silently wins.
 *
 * The shake lives on the BOX, so the bars keep their rotation while the whole
 * glyph jitters; the flare lives on `filter`, which nothing else touches. */
.nav-toggle[aria-expanded="true"] .nav-toggle-box { animation: sizzleShake .5s cubic-bezier(.3,1.4,.4,1); }
@keyframes sizzleShake {
  0%   { transform: translateX(0) }
  18%  { transform: translateX(-1.6px) }
  36%  { transform: translateX(1.6px) }
  54%  { transform: translateX(-1.1px) }
  72%  { transform: translateX(.7px) }
  100% { transform: translateX(0) }
}
.nav-toggle[aria-expanded="true"] .nav-toggle-bar { animation: sizzleFlare .52s ease; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(2) { animation-duration: .58s; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(3) { animation-duration: .64s; }
@keyframes sizzleFlare {
  0%   { filter: none }
  28%  { filter: drop-shadow(0 0 7px currentColor) }
  60%  { filter: drop-shadow(0 0 2px currentColor) }
  100% { filter: none }
}

/* ---- The mobile menu: liquid glass ---------------------------------------
 *
 * A frosted panel that floats over the page rather than pushing it down, with
 * the bar above it made of the same material. Two palettes, because one does
 * not work twice: over the dark hero the glass is smoked, and on an ordinary
 * light page it is frosted white. Both are the same construction — a tint, a
 * blur, a hairline, and a highlight along the top edge, which is what stops
 * "translucent" reading as "washed out".
 *
 * THE ONE RULE THIS FILE KEEPS. Open is a DISPLAY change, never the end of an
 * animation. Anything gated on the animation clock does not run when the clock
 * is stopped — a hidden tab, a background pane — and this codebase has been
 * bitten by that three times, once here: a menu that reported itself open,
 * locked the page behind it, and showed nothing at all. So the panel is
 * visible the instant the class lands, and every animation below is decoration
 * layered on top with NO fill mode. Skip them all and the menu still works. */
.mobile-nav { display: none; }

.mobile-nav.open {
  display: block;
  /* Floating, not in the flow. The panel used to push the page down, which on
   * a fixed header meant the two disagreed about where the menu began.
   *
   * It fills the screen. A capped panel stopped after the last link and left a
   * band of hero showing beneath it, so the menu read as a dropdown that had
   * run out rather than as the screen you are now on. top:100% is the bottom
   * of the bar, and the bar is at the top of the viewport in both the fixed
   * and the sticky case, so viewport height minus bar height reaches exactly
   * the bottom edge.
   *
   * --header-h is measured and written by main.js rather than guessed at: the
   * bar is padding plus whatever the logo's aspect ratio makes it, so a
   * hardcoded number is wrong for any operator who uploads a taller one.
   *
   * dvh, not vh: vh on a phone is the height with the browser chrome
   * RETRACTED, which would run the last rows under the address bar. The body
   * is locked while the menu is open so the chrome cannot move underneath it,
   * which is what makes dvh stable here rather than jumpy. */
  position: absolute; top: 100%; left: 0; right: 0;
  height: calc(100vh - var(--header-h, 72px));
  height: calc(100dvh - var(--header-h, 72px));
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  /* Opaque enough to read WITHOUT the blur, because the blur is the one part
   * of this that a browser is allowed to refuse. Where it is supported the
   * tint drops back and the hero shows through as a soft wash — see the
   * @supports block below. */
  background: var(--glass-solid, rgba(255,255,255,.96));
  border-top: 1px solid var(--glass-edge, rgba(15,23,42,.08));
  box-shadow: 0 26px 60px rgba(2,6,23,.22);
}
/* The highlight that makes it read as glass rather than as a flat wash. */
.mobile-nav.open::before {
  content: ''; position: sticky; top: 0; display: block; height: 1px;
  background: linear-gradient(90deg, transparent, var(--glass-sheen, rgba(255,255,255,.9)), transparent);
}

@supports ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .mobile-nav.open {
    /* .82 let a headline behind the panel stay legible through it, which
     * reads as a rendering fault rather than as glass. Dark content needs
     * less help than light content here: a dark tint hides dark text, a
     * light tint has to work against everything the page is already showing. */
    background: var(--glass-tint, rgba(255,255,255,.90));
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    backdrop-filter: blur(22px) saturate(180%);
  }
}

/* NESTED BACKDROP FILTERS DO NOT COMPOSITE, and this cost an hour.
 *
 * The panel is a child of the header. A backdrop-filter samples what is behind
 * the element — but when an ANCESTOR is already filtering, the child is
 * sampling a backdrop its parent has painted, and the browser simply drops the
 * child's filter. Computed style still reports blur(22px); nothing blurs. The
 * menu was a flat 72% tint over a sharp headline, which is unreadable, and it
 * looked like the tint was too weak rather than like the blur was gone.
 *
 * Proved by removing the header's filter and watching the panel's appear.
 *
 * So exactly one of the two filters at a time: while the menu is open the bar
 * stops filtering and carries a matching tint instead, and the panel — the
 * part with text over busy content — does the blurring. */
.site-header.nav-is-open {
  -webkit-backdrop-filter: none; backdrop-filter: none;
  background: rgba(255,255,255,.97);
}

/* Smoked glass over the dark hero, where a white panel would be a slab. */
.js .site-header--overlay {
  --glass-tint: rgba(10,15,32,.72);
  --glass-solid: rgba(10,15,32,.96);
  --glass-edge: rgba(140,160,255,.18);
  --glass-sheen: rgba(150,175,255,.55);
}

/* The panel slides down and settles. No fill mode: an animation that never
 * runs leaves the panel at its normal styles, which are the visible ones. */
.mobile-nav.open { animation: navPanelIn .42s cubic-bezier(.22,1.2,.36,1); }
@keyframes navPanelIn {
  from { opacity: 0; transform: translateY(-14px) scaleY(.97); transform-origin: top center; }
}

.mobile-nav-inner { padding: 16px 20px 26px; }
.mobile-cta-row { display: flex; gap: 10px; margin-bottom: 6px; }
.mobile-cta-btn { flex: 1; justify-content: center; }
.mobile-nav-group { padding-top: 14px; margin-top: 12px; border-top: 1px solid var(--glass-edge, var(--border)); }
.mobile-nav-group:first-of-type { border-top: 0; margin-top: 4px; }
.mobile-nav-heading {
  font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted-foreground); margin: 0 0 4px;
}
.mobile-nav a {
  display: block; padding: 12px 12px; font-size: .95rem; color: var(--foreground);
  border-radius: 12px; position: relative;
  transition: background .2s ease, color .2s ease, transform .16s cubic-bezier(.22,1.4,.36,1);
}
.mobile-nav a:hover { color: var(--primary); background: var(--glass-hover, rgba(15,23,42,.05)); }
/* Bounce on press. Short, springy, and on the element the finger is actually
 * on, so the menu answers a tap before the next page has begun to load. */
.mobile-nav a:active { transform: scale(.97); }
.mobile-cta-btn:active { transform: scale(.96); }

/* Over the dark hero the rows need their own hover and a little light. */
.js .site-header--overlay .mobile-nav a { color: #e8ecff; }
.js .site-header--overlay .mobile-nav a:hover {
  color: #fff; background: rgba(120,140,255,.14);
  box-shadow: 0 0 22px rgba(120,140,255,.18);
}
.js .site-header--overlay .mobile-nav-heading { color: #93a0cf; }

/* ---- The cascade ---------------------------------------------------------
 *
 * Staggered by DURATION, not by delay, and that is the whole trick.
 *
 * A delay needs animation-fill-mode: backwards to hold a row hidden until its
 * turn — and a row held at opacity 0 by a fill mode is a row that stays
 * invisible for ever if the clock never starts. That is the "open and empty"
 * failure this file already carries a warning about.
 *
 * Every row therefore starts at the same instant and simply takes longer the
 * further down it is. There is no fill mode, nothing is held anywhere, and the
 * effect reads as a fluid cascade rather than a queue. Stop the clock and every
 * row is exactly where it should be. */
.mobile-nav.open .mobile-nav-group > a,
.mobile-nav.open .mobile-cta-btn {
  animation: navRowIn calc(260ms + (var(--i, 0) * 42ms)) cubic-bezier(.22,1.1,.36,1);
}
@keyframes navRowIn {
  from { opacity: 0; transform: translateY(-10px) translateX(-6px); }
}

/* The menu is open: stop the page behind it scrolling under the finger. */
body.nav-open { overflow: hidden; }

/* The header used to scroll the whole page sideways.
 *
 * The desktop nav switched on at 768px, but measured end to end it needs about
 * 880px — brand 114 + links 465 + the two CTA buttons 253 + 48 of padding. So
 * between 768 and 880 the nav simply overflowed its container, and because
 * .site-nav does not wrap, the document grew wider than the viewport and every
 * page scrolled left-to-right. An uploaded logo makes it worse: .brand-logo
 * allows 200px, which pushes the requirement past 1000px.
 *
 * 1024 is the breakpoint that actually fits, with room for the widest logo the
 * uploader accepts. Below it, the hamburger.
 */
@media (min-width: 1024px) {
  /* Brand and links read as one group, actions as another.
   *
   * space-between across three children spread them evenly instead: the brand
   * ended at 200px and the links did not start until 336, so the bar had a
   * hole in it and the spacing looked accidental rather than chosen. */
  .nav-links { display: flex; margin-right: auto; margin-left: 36px; gap: 26px; }
  .nav-toggle { display: none; }
  .site-nav { justify-content: flex-start; }
}

/* The same overflow, one size down. On a phone the two CTA buttons are 253px
 * on their own; with the brand and the nav padding that is over 400px against
 * a 375px screen. They move into the menu, which is where the rest of the
 * navigation already lives — the header keeps the brand and the toggle. */
@media (max-width: 639px) {
  .nav-cta > .btn { display: none; }
}
@media (min-width: 640px) {
  .mobile-nav .mobile-cta { display: none; }
}
.mobile-nav .mobile-cta { font-weight: 700; color: var(--primary); }

/* Nothing in the bar may push the row wider than the bar itself. */
.site-nav { gap: 12px; flex-wrap: nowrap; }
.brand { min-width: 0; }
.brand > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-cta { flex-shrink: 0; }

/* --- Hero --- */
.hero { position: relative; padding: 80px 24px; overflow: hidden; }
.hero-bg { position: absolute; inset: 0; z-index: -1; }
.hero-blob { position: absolute; width: 384px; height: 384px; border-radius: 999px; filter: blur(90px); opacity: .2; }
.hero-blob-a { top: 0; left: 25%; background: radial-gradient(circle, var(--primary), transparent 70%); }
.hero-blob-b { bottom: 0; right: 25%; background: radial-gradient(circle, #ef4444, transparent 70%); }
.hero-inner { max-width: 1000px; margin: 0 auto; }
.hero h1 { font-size: clamp(2.5rem, 6vw, 5.5rem); font-weight: 900; line-height: 1.05; margin: 0 0 32px; }
.hero-row { display: flex; flex-direction: column; gap: 24px; margin-top: 40px; }
.hero-copy { font-size: 1.125rem; color: var(--muted-foreground); max-width: 640px; margin: 0; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 16px; }

@media (min-width: 768px) {
  .hero-row { flex-direction: row; justify-content: space-between; align-items: flex-start; }
}

/* --- Section header --- */
.section { padding: 96px 24px; }
.section-header { text-align: center; max-width: 640px; margin: 0 auto 64px; }
.section-header h2 { font-size: clamp(2rem, 4vw, 2.75rem); font-weight: 700; margin: 0 0 20px; }
.section-header p { color: var(--muted-foreground); font-size: 1.125rem; margin: 0; }

/* --- Features grid --- */
.features-grid { display: grid; grid-template-columns: 1fr; gap: 16px; max-width: var(--max-width); margin: 0 auto; }
.feature-card { position: relative; background: #fff; border: 1px solid #e5e7eb; border-radius: 16px; padding: 32px; transition: box-shadow .2s; }
.feature-card:hover { box-shadow: 0 10px 30px rgba(0,0,0,.06); }
.feature-icon-wrap { display: inline-flex; padding: 12px; background: #eff6ff; border-radius: 10px; margin-bottom: 16px; color: var(--primary); }
.feature-icon-wrap .icon { width: 28px; height: 28px; }
.feature-title-row { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.feature-title-row h3 { font-size: 1.375rem; font-weight: 700; margin: 0; }
.feature-badge { font-size: .7rem; background: #f3f4f6; color: #4b5563; padding: 4px 10px; border-radius: 999px; }
.feature-card p { color: #4b5563; font-size: .9rem; line-height: 1.6; margin: 0; }

@media (min-width: 900px) {
  .features-grid { grid-template-columns: repeat(4, 1fr); }
}

/* --- Industry section --- */
.industry-section { position: relative; background: #020817; color: #fff; overflow: hidden; padding: 96px 24px; }
.industry-glow { position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 100%; height: 100%; background: radial-gradient(circle at 50% -20%, #1e3a8a 0%, transparent 60%); opacity: .5; }
.industry-eyebrow { color: var(--primary); font-size: .75rem; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; display: block; margin-bottom: 16px; }
.industry-section h2 { font-size: 2.25rem; font-weight: 700; margin: 0 0 16px; }
.industry-section > .container > p { color: #94a3b8; max-width: 640px; margin: 0 0 64px; }
.industry-grid { position: relative; z-index: 1; display: grid; grid-template-columns: 1fr; gap: 24px; }
.industry-card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 16px; padding: 40px; transition: background .2s; }
.industry-card:hover { background: rgba(255,255,255,.1); }
.industry-icon-wrap { width: 48px; height: 48px; background: #fff; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: #000; margin-bottom: 32px; }
.industry-card h3 { font-size: 1.5rem; font-weight: 600; margin: 0 0 8px; }
.industry-card p { color: #94a3b8; line-height: 1.6; margin: 0; }

@media (min-width: 768px) {
  .industry-grid { grid-template-columns: 1fr 1fr; }
}

/* --- Testimonials --- */
.testimonials-section { padding: 96px 0; background: #fff; overflow: hidden; }
.testimonial-row { display: flex; gap: 24px; width: max-content; }
.testimonial-row.scroll-left { animation: scroll-left 60s linear infinite; }
.testimonial-row.scroll-right { animation: scroll-right 70s linear infinite; }
.testimonial-row:hover { animation-play-state: paused; }
.testimonial-track { display: flex; flex-direction: column; gap: 32px; margin-top: 16px; }
@keyframes scroll-left { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes scroll-right { from { transform: translateX(-50%); } to { transform: translateX(0); } }
.testimonial-card { width: 380px; flex-shrink: 0; background: #f3f4f6; padding: 20px; border-radius: 12px; display: flex; flex-direction: column; justify-content: space-between; gap: 24px; }
.testimonial-card .quote-icon { color: #000; margin-bottom: 16px; }
.testimonial-card p { color: #6b7280; font-size: .875rem; line-height: 1.6; margin: 0; }
.testimonial-footer { display: flex; align-items: center; gap: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb; }
.testimonial-avatar { width: 48px; height: 48px; border-radius: 999px; overflow: hidden; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,.1); flex-shrink: 0; }
.testimonial-avatar img { width: 100%; height: 100%; object-fit: cover; }
.testimonial-name { font-weight: 700; font-size: .875rem; color: #0f172a; margin: 0; }
.testimonial-role { font-size: .75rem; color: #64748b; margin: 0; }

/* --- FAQ --- */
.faq-section { padding: 96px 24px; background: #fff; }
.faq-grid { max-width: var(--max-width); margin: 0 auto; display: grid; grid-template-columns: 1fr; gap: 64px; }
.faq-eyebrow { color: var(--primary); font-size: .875rem; font-weight: 700; display: block; margin-bottom: 16px; }
.faq-grid h2 { font-size: 2.5rem; font-weight: 700; color: #0f172a; margin: 0 0 24px; }
.faq-grid > div:first-child > p { color: #64748b; font-size: 1.125rem; margin: 0 0 40px; max-width: 420px; }
.faq-list { border-top: 1px solid #f1f5f9; }
.faq-item { border-bottom: 1px solid #f1f5f9; padding: 24px 0; cursor: pointer; }
.faq-item-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.faq-item-head h3 { font-size: 1.125rem; font-weight: 600; color: #1e293b; margin: 0; }
.faq-item:hover .faq-item-head h3 { color: var(--primary); }
.faq-item-head .icon { color: #94a3b8; flex-shrink: 0; }
.faq-icon-minus { display: none; }
.faq-item.open .faq-icon-plus { display: none; }
.faq-item.open .faq-icon-minus { display: block; }
.faq-answer { max-height: 0; overflow: hidden; transition: max-height .25s ease; }
.faq-item.open .faq-answer { max-height: 240px; }
.faq-answer p { padding-top: 16px; color: #64748b; line-height: 1.6; margin: 0; }

@media (min-width: 1024px) {
  .faq-grid { grid-template-columns: 1fr 1fr; }
}

/* --- CTA final --- */
.cta-section { position: relative; padding: 128px 24px; overflow: hidden; background: #fff; text-align: center; }
.cta-grid-bg {
  position: absolute; inset: 0; z-index: 0; opacity: .03;
  background-image: linear-gradient(#000 1px, transparent 1px), linear-gradient(90deg, #000 1px, transparent 1px);
  background-size: 40px 40px;
}
.cta-glow { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); z-index: 0; width: 800px; height: 600px; background: rgba(199,210,254,.4); filter: blur(120px); border-radius: 999px; }
.cta-section .container { position: relative; z-index: 1; }
.cta-section h2 { font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 700; color: #0f172a; margin: 0 0 24px; }
.cta-section > .container > p { max-width: 640px; margin: 0 auto 40px; color: #64748b; font-size: 1.125rem; line-height: 1.6; }
.cta-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 24px; }
.link-cta { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--primary); }
.link-cta:hover { text-decoration: underline; }

/* --- Footer --- */
.site-footer { background: var(--foreground); color: #fff; padding: 64px 24px; }
.footer-grid { display: grid; grid-template-columns: 1fr; gap: 48px; max-width: var(--max-width); margin: 0 auto 48px; }
.footer-grid h4 { font-weight: 600; margin: 0 0 16px; }
.footer-grid ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.footer-grid a { color: #9ca3af; font-size: .875rem; }
.footer-grid a:hover { color: #fff; }
.footer-tagline { color: #9ca3af; font-size: .875rem; margin: 0; }
.footer-bottom { max-width: var(--max-width); margin: 0 auto; border-top: 1px solid #374151; padding-top: 32px; display: flex; flex-direction: column; align-items: center; gap: 24px; }
.footer-social { display: flex; gap: 24px; }
.footer-social a { color: #9ca3af; }
.footer-social a:hover { color: #fff; }

@media (min-width: 768px) {
  .footer-grid { grid-template-columns: repeat(4, 1fr); }
  .footer-bottom { flex-direction: row; justify-content: space-between; }
}

/* --- Generic page shell (used by inner marketing pages) --- */
.page-hero { padding: 72px 24px 48px; text-align: center; }
.page-hero h1 { font-size: clamp(2.25rem, 5vw, 3.5rem); font-weight: 800; margin: 0 0 20px; }
.page-hero p { color: var(--muted-foreground); font-size: 1.125rem; max-width: 640px; margin: 0 auto; }
.card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; }

/* --- Inner marketing pages --- */
.mk-hero { padding: 80px 24px; text-align: center; }
.mk-hero h1 { font-size: clamp(2.25rem, 5vw, 3.75rem); font-weight: 700; margin: 0 0 24px; line-height: 1.1; }
.mk-hero p { color: var(--muted-foreground); font-size: 1.125rem; max-width: 640px; margin: 0 auto 40px; }
.mk-section { padding: 64px 24px; }
.mk-section.white { background: #fff; }
.mk-section.gray { background: #f8fafc; }
.mk-section.dark { background: #0f172a; color: #fff; }
.mk-wrap { max-width: 1152px; margin: 0 auto; }
.mk-wrap-sm { max-width: 768px; margin: 0 auto; }
.mk-wrap-xs { max-width: 640px; margin: 0 auto; }
.mk-center { text-align: center; }
.mk-h2 { font-size: clamp(1.75rem, 3vw, 2.25rem); font-weight: 700; margin: 0 0 16px; }
.mk-h2.center { text-align: center; }
.mk-lead { color: var(--muted-foreground); font-size: 1rem; line-height: 1.7; }
.mk-lead.center { text-align: center; margin-left: auto; margin-right: auto; max-width: 640px; }
.mk-grid { display: grid; gap: 24px; }
.mk-grid.cols-2 { grid-template-columns: 1fr; }
.mk-grid.cols-3 { grid-template-columns: 1fr; }
.mk-grid.cols-4 { grid-template-columns: 1fr; }
@media (min-width: 640px) { .mk-grid.cols-2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) {
  .mk-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
  .mk-grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) { .mk-grid.cols-4 { grid-template-columns: repeat(4, 1fr); } }

.mk-card { background: var(--card, #fff); border: 1px solid var(--border); border-radius: var(--card-radius); padding: 32px; transition: box-shadow .2s, border-color .2s; }
.mk-card:hover { box-shadow: 0 10px 30px rgba(0,0,0,.06); }
.mk-card.dark { background: #1e293b; border-color: #334155; }
.mk-icon-box { display: inline-flex; padding: 12px; background: #eff6ff; border-radius: 12px; color: var(--primary); margin-bottom: 16px; }
.mk-icon-box .icon { width: 28px; height: 28px; }
.mk-card h3 { font-size: 1.25rem; font-weight: 700; margin: 0 0 8px; }
.mk-card p { color: #475569; font-size: .9rem; line-height: 1.6; margin: 0; }
.mk-badge { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border-radius: 999px; font-size: .875rem; font-weight: 600; margin-bottom: 24px; }
.mk-badge.blue { background: #eff6ff; color: #1d4ed8; }
.mk-badge.green { background: #ecfdf5; color: #047857; }
.mk-dot { width: 8px; height: 8px; border-radius: 999px; background: currentColor; }
.mk-dot.pulse { animation: mk-pulse 1.5s ease-in-out infinite; }
@keyframes mk-pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
.mk-stat { text-align: center; padding: 32px; border-radius: 16px; background: #f8fafc; border: 1px solid #f1f5f9; }
.mk-stat .val { font-size: 2.25rem; font-weight: 700; color: var(--primary); }
.mk-stat.dark .val { color: #fff; }
.mk-stat .label { font-size: .875rem; font-weight: 600; color: #334155; margin-top: 4px; }
.mk-stat.dark .label { color: #94a3b8; }
.mk-stat .sub { font-size: .75rem; color: #64748b; margin-top: 4px; }
.mk-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.mk-list li { display: flex; align-items: flex-start; gap: 8px; font-size: .9rem; color: #475569; }
.mk-list .icon { color: #10b981; flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; }
.mk-tag { display: inline-block; padding: 6px 12px; background: #f1f5f9; color: #475569; font-size: .75rem; font-weight: 500; border-radius: 8px; }
.mk-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.mk-cta-actions { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; }
.mk-code { background: #0f172a; color: #e2e8f0; padding: 24px; border-radius: 12px; font-family: 'SFMono-Regular', Consolas, monospace; font-size: .85rem; overflow-x: auto; }
.mk-code pre { margin: 0; white-space: pre; }
.mk-row-link { display: block; border: 1px solid var(--border); border-radius: 12px; padding: 24px; transition: border-color .2s, box-shadow .2s; }
.mk-row-link:hover { border-color: #cbd5e1; box-shadow: 0 4px 12px rgba(0,0,0,.05); }
.mk-status-row { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; border-bottom: 1px solid #f1f5f9; }
.mk-status-row:last-child { border-bottom: none; }
.mk-status-ok { display: inline-flex; align-items: center; gap: 8px; color: #059669; font-size: .875rem; font-weight: 500; }
.mk-pill { padding: 4px 12px; border-radius: 999px; font-size: .75rem; font-weight: 600; }
.mk-pill.green { background: #ecfdf5; color: #047857; }
.mk-timeline-item { border-bottom: 1px solid var(--border); padding: 40px 0; }
.mk-timeline-item:first-child { padding-top: 0; }
.mk-inline-form { display: flex; flex-direction: column; gap: 12px; max-width: 448px; margin: 0 auto; }
@media (min-width: 640px) { .mk-inline-form { flex-direction: row; } }
.mk-inline-form input { flex: 1; padding: 12px 16px; border-radius: 10px; border: 1px solid var(--border); font-size: .875rem; }
.mk-mb-0 { margin-bottom: 0; }
.mt-2 { margin-top: 16px; } .mt-4 { margin-top: 32px; } .mb-4 { margin-bottom: 32px; }

/* --- Forms (auth pages) --- */
.auth-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; background: var(--muted); }
.auth-card { width: 100%; max-width: 440px; background: #fff; border: 1px solid var(--border); border-radius: 16px; padding: 40px; box-shadow: 0 20px 50px rgba(0,0,0,.06); }
.auth-card h1 { font-size: 1.5rem; font-weight: 700; margin: 0 0 8px; }
.auth-card .subtitle { color: var(--muted-foreground); font-size: .9rem; margin: 0 0 32px; }
.form-group { margin-bottom: 18px; }
.form-group label { display: block; font-size: .8rem; font-weight: 600; margin-bottom: 6px; }
.form-group input, .form-group select, .form-group textarea {
  width: 100%; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--border);
  font-size: .9rem; background: #fff; color: var(--foreground);
}
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { outline: 2px solid var(--primary); outline-offset: 1px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-error { background: rgba(220,38,38,.08); border: 1px solid rgba(220,38,38,.25); color: #b91c1c; padding: 10px 14px; border-radius: 8px; font-size: .85rem; margin-bottom: 18px; }
.form-success { background: rgba(22,163,74,.08); border: 1px solid rgba(22,163,74,.25); color: #15803d; padding: 10px 14px; border-radius: 8px; font-size: .85rem; margin-bottom: 18px; }
.form-warning { background: rgba(180,83,9,.08); border: 1px solid rgba(180,83,9,.28); color: #92400e; padding: 10px 14px; border-radius: 8px; font-size: .85rem; margin-bottom: 18px; }
/* Neither good news nor bad: work is under way and there is nothing to do.
   Without its own style this fell through to .form-success and told people a
   domain had connected when it had not. */
.form-info { background: rgba(37,99,235,.07); border: 1px solid rgba(37,99,235,.25); color: #1d4ed8; padding: 10px 14px; border-radius: 8px; font-size: .85rem; margin-bottom: 18px; }
.form-footer { margin-top: 24px; text-align: center; font-size: .875rem; color: var(--muted-foreground); }
.form-footer a { color: var(--primary); font-weight: 600; }
.btn-block { width: 100%; }

/* --- Auth page layout (with site header/footer) --- */
.auth-wrap { display: flex; align-items: flex-start; justify-content: center; padding: 48px 16px; }
.auth-inner { width: 100%; max-width: 448px; }
.auth-inner.wide { max-width: 672px; }
.auth-head { text-align: center; margin-bottom: 48px; }
.auth-head h1 { font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; margin: 0 0 16px; }
.auth-head p { color: var(--muted-foreground); font-size: 1.05rem; margin: 0; }
.auth-box { background: #fff; border: 1px solid var(--border); border-radius: 12px; padding: 40px; }
.auth-alt { margin-top: 32px; text-align: center; font-size: .875rem; color: var(--muted-foreground); }
.auth-alt a { color: var(--primary); font-weight: 500; }
.trust-badges { margin-top: 48px; display: flex; align-items: center; justify-content: center; gap: 32px; flex-wrap: wrap; }
.trust-badges .tb { text-align: center; }
.trust-badges .tb .v { font-size: 1.5rem; font-weight: 700; }
.trust-badges .tb .l { font-size: .75rem; color: var(--muted-foreground); }
.trust-sep { height: 32px; width: 1px; background: var(--border); }
.pw-field { position: relative; display: flex; align-items: center; }
.pw-field input { padding-right: 40px; }
.pw-toggle { position: absolute; right: 12px; background: none; border: none; color: var(--muted-foreground); display: flex; padding: 0; }
.pw-toggle:hover { color: var(--foreground); }
.field-hint { font-size: .75rem; color: var(--muted-foreground); margin-top: 4px; }
.field-error { font-size: .78rem; color: #dc2626; margin-top: 4px; }
.form-group input.invalid, .form-group select.invalid { border-color: #dc2626; }
.wizard-step { display: none; }
.wizard-step.active { display: block; }
.wizard-head { margin-bottom: 32px; }
.wizard-head h2 { font-size: 1.5rem; font-weight: 700; margin: 0 0 8px; }
.wizard-head p { color: var(--muted-foreground); font-size: .875rem; margin: 0; }
.checkbox-row { display: flex; align-items: flex-start; gap: 10px; }
.checkbox-row input { width: auto; margin-top: 3px; }
.checkbox-row label { margin: 0; font-weight: 400; }
/* The links inside a consent row have to LOOK like links.
 *
 * "I agree to the Terms of Service and Privacy Policy" rendered them in the
 * same colour and weight as the sentence around them, so the two things a
 * person is being asked to agree to were indistinguishable from the asking.
 * Somebody ticking that box could not tell there was anything to read. */
.checkbox-row a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.checkbox-row a:hover { text-decoration-thickness: 2px; }
.otp-input { letter-spacing: .5em; text-align: center; font-size: 1.5rem !important; font-family: var(--mono, monospace); }
.btn-row { display: flex; gap: 16px; }
.btn-row .btn { flex: 1; }

/* Monthly / yearly switch above the plan cards. Shared by the public pricing
   page (light) and the customer billing page (dark) so the control cannot
   drift between the place a price is advertised and the place it is charged. */
.plan-toggle { display: inline-flex; align-items: center; gap: 4px; padding: 4px; border-radius: 999px; }
.plan-toggle-opt { display: inline-flex; align-items: center; gap: 7px; border: 0; cursor: pointer;
  padding: 9px 20px; border-radius: 999px; font-size: .9rem; font-weight: 600; background: transparent;
  color: inherit; transition: background .15s ease, color .15s ease; font-family: inherit; }
.plan-toggle-badge { font-size: .68rem; font-weight: 700; padding: 3px 7px; border-radius: 999px;
  text-transform: uppercase; letter-spacing: .03em; white-space: nowrap; }

/* The selected side is a filled pill that slides. It used to be a flat colour
 * swap, which reads as two buttons where one happens to be shaded; the movement
 * is what makes it read as one control with a position. */
.plan-toggle-opt.is-on { box-shadow: 0 6px 16px -6px color-mix(in oklch, var(--primary) 70%, transparent); }

.plan-toggle-light { background: #f1f5f9; border: 1px solid var(--border); }
.plan-toggle-light .plan-toggle-opt { color: var(--muted-foreground); }
.plan-toggle-light .plan-toggle-opt.is-on {
  color: var(--primary-foreground);
  background: linear-gradient(to top, var(--primary), color-mix(in oklch, var(--primary) 76%, white));
}
.plan-toggle-light .plan-toggle-badge { background: color-mix(in oklch, var(--primary) 14%, white); color: var(--foreground); }
.plan-toggle-light .plan-toggle-opt.is-on .plan-toggle-badge {
  background: color-mix(in oklch, white 82%, transparent); color: var(--foreground);
}

.plan-toggle-dark { background: #0f1729; border: 1px solid #1e293b; }
.plan-toggle-dark .plan-toggle-opt { color: #94a3b8; }
.plan-toggle-dark .plan-toggle-opt.is-on {
  color: var(--primary-foreground);
  background: linear-gradient(to top, var(--primary), color-mix(in oklch, var(--primary) 76%, white));
}
.plan-toggle-dark .plan-toggle-badge { background: rgba(255,255,255,.18); color: #fff; }

@media (max-width: 420px) {
  .plan-toggle { width: 100%; }
  .plan-toggle-opt { flex: 1; justify-content: center; padding: 9px 10px; font-size: .84rem; }
}

/* ---------- Documentation & Help Centre ---------- */

/* Two columns on desktop with a sticky contents rail; one column on mobile,
   where the rail becomes a plain list above the text rather than disappearing. */
.docs-layout { display: grid; grid-template-columns: 1fr; gap: 32px; align-items: start; }
@media (min-width: 900px) { .docs-layout { grid-template-columns: 220px minmax(0, 1fr); gap: 48px; } }

.docs-nav { position: static; }
@media (min-width: 900px) { .docs-nav { position: sticky; top: 96px; } }
.docs-nav-title { font-size: .72rem; text-transform: uppercase; letter-spacing: .07em; color: #94a3b8;
  font-weight: 700; margin: 0 0 10px; }
.docs-nav a { display: block; padding: 6px 10px; border-radius: 7px; font-size: .84rem; color: #475569;
  text-decoration: none; border-left: 2px solid transparent; }
.docs-nav a:hover { background: #f1f5f9; color: #0f172a; border-left-color: var(--primary); }
.docs-nav-cta { margin-top: 22px; padding: 14px; border: 1px solid #e2e8f0; border-radius: 12px; background: #f8fafc; }
.docs-nav-cta p { font-size: .74rem; color: #64748b; margin: 8px 0 0; text-align: center; }

.docs-body { min-width: 0; }
.docs-body h2 { font-size: 1.6rem; font-weight: 800; margin: 52px 0 14px; padding-top: 12px;
  scroll-margin-top: 96px; border-top: 1px solid #e2e8f0; }
.docs-body h2:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.docs-body h3 { font-size: 1.05rem; font-weight: 700; margin: 28px 0 8px; }
.docs-body p { color: #475569; line-height: 1.75; margin: 0 0 14px; }
.docs-body code { background: #f1f5f9; padding: 2px 6px; border-radius: 5px; font-size: .86em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #0f172a; }
.docs-body .mk-code { margin: 0 0 16px; }
.docs-list { margin: 0 0 16px; padding-left: 20px; color: #475569; line-height: 1.75; }
.docs-list li { margin-bottom: 8px; }
.docs-dim { color: #94a3b8; font-size: .85em; }

/* Callouts: the paragraphs worth stopping at rather than skimming past. */
.docs-note { border-left: 3px solid var(--primary); background: #f8fafc; padding: 12px 16px;
  border-radius: 0 8px 8px 0; font-size: .9rem; margin: 0 0 18px !important; }

/* Wide tables scroll inside their own box rather than widening the page. */
.docs-table-wrap { overflow-x: auto; margin: 0 0 18px; border: 1px solid #e2e8f0; border-radius: 10px; }
.docs-table { width: 100%; border-collapse: collapse; font-size: .87rem; min-width: 480px; }
.docs-table th { text-align: left; padding: 10px 14px; background: #f8fafc; color: #475569;
  font-weight: 700; font-size: .76rem; text-transform: uppercase; letter-spacing: .04em;
  border-bottom: 1px solid #e2e8f0; white-space: nowrap; }
.docs-table td { padding: 11px 14px; border-bottom: 1px solid #f1f5f9; color: #475569; vertical-align: top; line-height: 1.6; }
.docs-table tr:last-child td { border-bottom: 0; }

.docs-cta { margin-top: 46px; padding: 22px 24px; border: 1px solid #e2e8f0; border-radius: 14px; background: #f8fafc; }
.docs-cta h3 { margin: 0 0 6px; font-size: 1.05rem; font-weight: 700; }
.docs-cta p { margin: 0; color: #64748b; font-size: .9rem; }

/* Help Centre */
.hc-jump { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 34px; }
.hc-jump a { display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;
  border: 1px solid #e2e8f0; background: #fff; font-size: .82rem; color: #475569; text-decoration: none; }
.hc-jump a:hover { border-color: var(--primary); color: var(--primary); }
.hc-jump a span { font-size: .7rem; font-weight: 700; background: #f1f5f9; color: #64748b;
  padding: 1px 7px; border-radius: 999px; }
.hc-group { font-size: 1.35rem; font-weight: 800; margin: 42px 0 14px; scroll-margin-top: 96px; }
.hc-tag { display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .7rem; background: #eef2ff; color: #4338ca; padding: 2px 8px; border-radius: 999px; margin-bottom: 10px; }
.faq-item-body .mk-code { margin: 12px 0; }
.faq-item-body ul { padding-left: 20px; line-height: 1.7; color: #475569; }

/* ---------------------------------------------------------------------------
   Blog
   --------------------------------------------------------------------------- */
.bl-back{display:inline-flex;align-items:center;gap:7px;font-size:.85rem;font-weight:600;color:#64748b;text-decoration:none}
.bl-back:hover{color:var(--primary)}
.bl-filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:28px}
.bl-filter{display:inline-flex;align-items:center;gap:7px;padding:7px 14px;border:1px solid #e2e8f0;border-radius:999px;
  background:#fff;font-size:.82rem;font-weight:600;color:#475569;text-decoration:none}
.bl-filter span{color:#94a3b8;font-weight:500}
.bl-filter:hover{border-color:#cbd5e1}
.bl-filter.active{background:var(--primary);border-color:var(--primary);color:#fff}
.bl-filter.active span{color:rgba(255,255,255,.75)}
/* min(310px,100%): a bare 310px floor is wider than the content box on a
   320px phone, so the track overflowed the page instead of collapsing. */
.bl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(310px,100%),1fr));gap:22px}
.bl-card{display:flex;flex-direction:column;background:#fff;border:1px solid #e2e8f0;border-radius:16px;overflow:hidden;
  text-decoration:none;transition:transform .16s ease,box-shadow .16s ease,border-color .16s ease}
.bl-card:hover{transform:translateY(-3px);box-shadow:0 16px 34px rgba(15,23,42,.09);border-color:#cbd5e1}
.bl-card img{width:100%;height:168px;object-fit:cover;display:block;background:#f1f5f9}
.bl-card-body{padding:18px 20px 22px;display:flex;flex-direction:column;gap:9px;flex:1}
.bl-card-body h2{font-size:1.06rem;line-height:1.35;font-weight:700;color:#0f172a;margin:0}
.bl-card-body p{font-size:.87rem;line-height:1.55;color:#64748b;margin:0;flex:1}
.bl-card-meta{display:flex;align-items:center;gap:7px;font-size:.78rem;color:#94a3b8}
.bl-pager{display:flex;gap:6px;justify-content:center;margin-top:34px;flex-wrap:wrap}
.bl-pager a{min-width:38px;height:38px;display:inline-flex;align-items:center;justify-content:center;border:1px solid #e2e8f0;
  border-radius:9px;background:#fff;color:#475569;font-size:.85rem;font-weight:600;text-decoration:none}
.bl-pager a.active{background:var(--primary);border-color:var(--primary);color:#fff}
.bl-title{font-size:2.35rem;line-height:1.16;font-weight:800;letter-spacing:-.025em;color:#0f172a;margin:14px 0 12px}
.bl-lead{font-size:1.12rem;line-height:1.6;color:#475569;margin:0 0 18px}
.bl-byline{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:.86rem;color:#64748b;
  padding-bottom:22px;border-bottom:1px solid #e2e8f0}
.bl-cover{width:100%;height:auto;border-radius:14px;margin:26px 0 10px;display:block;background:#f1f5f9}
.bl-body{font-size:1.03rem;line-height:1.75;color:#334155}
.bl-body h2{font-size:1.42rem;font-weight:700;color:#0f172a;margin:38px 0 12px;letter-spacing:-.015em}
.bl-body h3{font-size:1.12rem;font-weight:700;color:#0f172a;margin:28px 0 10px}
.bl-body p{margin:0 0 18px}
.bl-body ul,.bl-body ol{margin:0 0 18px;padding-left:22px}
.bl-body li{margin-bottom:8px}
.bl-body code{background:#f1f5f9;padding:2px 6px;border-radius:5px;font-size:.88em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:#0f172a}
.bl-body blockquote{margin:0 0 20px;padding:14px 20px;border-left:4px solid var(--primary);background:#f8fafc;
  border-radius:0 10px 10px 0;color:#334155;font-size:1.02rem}
.bl-body blockquote p:last-child{margin-bottom:0}
.bl-body pre.mk-code{margin:0 0 22px}
.bl-nav{display:flex;justify-content:space-between;gap:18px;margin-top:46px;padding-top:26px;border-top:1px solid #e2e8f0}
.bl-nav a{display:flex;flex-direction:column;gap:4px;max-width:46%;text-decoration:none}
.bl-nav a.right{text-align:right;margin-left:auto}
.bl-nav span{font-size:.76rem;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;font-weight:600}
.bl-nav strong{font-size:.94rem;color:#0f172a;font-weight:600;line-height:1.4}
.bl-nav a:hover strong{color:var(--primary)}

/* ---------------------------------------------------------------------------
   Community
   --------------------------------------------------------------------------- */
.cm-avatar{display:inline-flex;align-items:center;justify-content:center;border-radius:50%;font-weight:700;flex-shrink:0}
.cm-layout{display:grid;grid-template-columns:212px 1fr;gap:32px;align-items:start}
.cm-side{position:sticky;top:96px}
.cm-search{display:flex;gap:6px;margin-bottom:22px}
.cm-search input{flex:1;min-width:0;font-size:.85rem}
.cm-search button{padding:8px 11px;flex-shrink:0}
.cm-side-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;font-weight:700;margin:0 0 8px}
.cm-cat{display:block;padding:7px 11px;border-radius:8px;font-size:.87rem;color:#475569;text-decoration:none;margin-bottom:2px}
.cm-cat:hover{background:#f1f5f9;color:#0f172a}
.cm-cat.active{background:var(--primary);color:#fff;font-weight:600}
.cm-toolbar{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:18px}
.cm-sorts{display:flex;gap:4px;background:#f1f5f9;padding:4px;border-radius:10px}
.cm-sort{padding:6px 13px;border-radius:7px;font-size:.83rem;font-weight:600;color:#64748b;text-decoration:none}
.cm-sort.active{background:#fff;color:#0f172a;box-shadow:0 1px 3px rgba(15,23,42,.09)}
.cm-threads{display:flex;flex-direction:column;gap:10px}
.cm-thread{display:flex;gap:14px;align-items:center;padding:16px 18px;background:#fff;border:1px solid #e2e8f0;
  border-radius:13px;text-decoration:none;transition:border-color .16s ease,box-shadow .16s ease}
.cm-thread:hover{border-color:#cbd5e1;box-shadow:0 8px 20px rgba(15,23,42,.06)}
.cm-thread-main{flex:1;min-width:0}
.cm-thread-main h2{font-size:1rem;font-weight:650;color:#0f172a;margin:0 0 5px;line-height:1.4}
.cm-thread:hover .cm-thread-main h2{color:var(--primary)}
.cm-thread-sub{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:.79rem;color:#94a3b8}
.cm-thread-stats{display:grid;grid-template-columns:auto auto;gap:1px 7px;text-align:right;flex-shrink:0;
  font-size:.74rem;color:#94a3b8;align-items:baseline}
.cm-thread-stats strong{font-size:.92rem;color:#334155;font-weight:700}
.cm-thread-title{font-size:1.85rem;line-height:1.25;font-weight:800;letter-spacing:-.02em;color:#0f172a;margin:16px 0 10px}
.cm-thread-meta{font-size:.85rem;color:#64748b;padding-bottom:20px;border-bottom:1px solid #e2e8f0;margin-bottom:8px}
.cm-post{padding:22px 0;border-bottom:1px solid #f1f5f9}
.cm-post.first{background:#f8fafc;border:1px solid #e2e8f0;border-radius:14px;padding:22px 24px;margin:18px 0 10px}
.cm-post-head{display:flex;align-items:center;gap:11px;margin-bottom:12px}
.cm-post-head strong{font-size:.92rem;color:#0f172a}
.cm-post-time{font-size:.78rem;color:#94a3b8}
.cm-post-head .mk-tag{margin-left:auto}
.cm-post-body{font-size:.97rem;line-height:1.72;color:#334155}
.cm-post-body p{margin:0 0 13px}
.cm-post-body p:last-child{margin-bottom:0}
.cm-post-body pre.mk-code{margin:0 0 14px}
.cm-reply,.cm-ask{margin-top:30px;padding:24px;background:#fff;border:1px solid #e2e8f0;border-radius:14px}
.cm-reply h2,.cm-ask h2{font-size:1.12rem;font-weight:700;color:#0f172a;margin:0 0 6px}
.cm-hint{font-size:.84rem;color:#64748b;line-height:1.6;margin:0 0 16px}
.cm-reply textarea,.cm-ask textarea,.cm-ask input,.cm-ask select{width:100%;margin-bottom:14px;font-size:.92rem}
.cm-ask .field-label{display:block;margin-bottom:5px}
.cm-signin{padding:24px;display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:30px}
.cm-signin p{margin:0;flex:1;min-width:250px;font-size:.9rem;color:#475569;line-height:1.6}
@media (max-width:860px){
  .cm-layout{grid-template-columns:1fr}
  .cm-side{position:static}
  .cm-thread-stats{display:none}
  .bl-title{font-size:1.85rem}
}

/* Legal pages ------------------------------------------------------------- */
.lg-layout{display:grid;grid-template-columns:210px 1fr;gap:44px;align-items:start}
.lg-toc{position:sticky;top:96px}
.lg-toc-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;font-weight:700;margin:0 0 9px}
.lg-toc a{display:block;padding:5px 0;font-size:.85rem;color:#64748b;text-decoration:none;line-height:1.45}
.lg-toc a:hover{color:var(--primary)}
.lg-body{max-width:760px;font-size:1rem;line-height:1.75;color:#334155}
.lg-section{margin-bottom:38px;scroll-margin-top:96px}
.lg-section h2{font-size:1.34rem;font-weight:700;color:#0f172a;margin:0 0 12px;letter-spacing:-.015em}
.lg-section h3{font-size:1.02rem;font-weight:700;color:#0f172a;margin:22px 0 8px}
.lg-section p{margin:0 0 14px}
.lg-section ul{margin:0 0 16px;padding-left:22px}
.lg-section li{margin-bottom:7px}
.lg-section code{background:#f1f5f9;padding:2px 6px;border-radius:5px;font-size:.88em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.lg-callout{background:#f8fafc;border:1px solid #e2e8f0;border-left:4px solid var(--primary);
  border-radius:0 12px 12px 0;padding:18px 22px;margin-bottom:16px}
.lg-table{width:100%;border-collapse:collapse;margin:8px 0 16px;font-size:.92rem}
.lg-table th,.lg-table td{text-align:left;padding:10px 12px;border-bottom:1px solid #e2e8f0;vertical-align:top}
.lg-table th{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:#64748b;font-weight:700}
.lg-table td code{font-size:.85em}
.lg-table-wrap{overflow-x:auto}
@media (max-width:860px){.lg-layout{grid-template-columns:1fr;gap:24px}.lg-toc{position:static}}
.footer-legal{display:inline-flex;gap:16px;margin-left:18px}
.footer-legal a{color:inherit;opacity:.72;text-decoration:none}
.footer-legal a:hover{opacity:1;text-decoration:underline}
@media (max-width:640px){.footer-legal{display:flex;margin:10px 0 0}}

/* API reference ----------------------------------------------------------- */
.ar-layout{display:grid;grid-template-columns:212px 1fr;gap:40px;align-items:start}
.ar-nav{position:sticky;top:96px;max-height:calc(100vh - 120px);overflow-y:auto}
.ar-nav-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;font-weight:700;margin:0 0 8px}
.ar-nav a{display:block;padding:4px 0;font-size:.84rem;color:#64748b;text-decoration:none;line-height:1.45}
.ar-nav a:hover{color:var(--primary)}
.ar-body{max-width:820px;min-width:0}
.ar-body>section{margin-bottom:36px;scroll-margin-top:96px}
.ar-body h2{font-size:1.42rem;font-weight:700;color:#0f172a;margin:0 0 12px;letter-spacing:-.015em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.ar-body section:not([id$="verify"]):not([id="botcheck"]):not([id="content"]) h2{font-family:inherit}
.ar-body h3{font-size:1.04rem;font-weight:700;color:#0f172a;margin:26px 0 10px}
.ar-body p{margin:0 0 14px;line-height:1.72;color:#334155}
.ar-body ul{margin:0 0 16px;padding-left:22px}
.ar-body li{margin-bottom:8px;line-height:1.65;color:#334155}
.ar-body code{background:#f1f5f9;padding:2px 6px;border-radius:5px;font-size:.87em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:#0f172a}
.ar-baseurl{display:flex;align-items:center;gap:14px;flex-wrap:wrap;background:#0f172a;border-radius:11px;
  padding:14px 18px;margin:0 0 18px}
.ar-baseurl span{font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;font-weight:700}
.ar-baseurl code{background:transparent;color:#e2e8f0;font-size:.92rem;padding:0}
/* The scroll lives on a wrapper, not on the table.
 *
 * display:block on a <table> makes it shrink to the container, but thead and
 * tbody keep table layout and render at their own intrinsic width regardless —
 * so overflow-x:auto here clipped nothing and the page scrolled sideways
 * instead. A plain wrapper with the overflow, and a table left as a table, is
 * what actually contains it. */
.ar-table-wrap{max-width:100%;overflow-x:auto;margin:0 0 20px}
.ar-table{width:100%;border-collapse:collapse;margin:0;font-size:.87rem;min-width:420px}
.ar-table thead th{text-align:left;padding:9px 12px;border-bottom:2px solid #e2e8f0;font-size:.72rem;
  text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;font-weight:700;white-space:nowrap}
.ar-table td{padding:11px 12px;border-bottom:1px solid #f1f5f9;vertical-align:top;color:#475569;line-height:1.6}
.ar-table td:first-child{white-space:nowrap}
.ar-type{color:#94a3b8;font-size:.82rem;white-space:nowrap}
.ar-req{font-size:.76rem;color:#94a3b8;white-space:nowrap}
.ar-req.yes{color:#b45309;font-weight:600}
.ar-two{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:18px}
.ar-two .mk-code{margin:0}
.ar-label{font-size:.76rem;text-transform:uppercase;letter-spacing:.06em;font-weight:700;color:#94a3b8;margin:0 0 7px}
.ar-label.ok{color:#15803d}
.ar-label.no{color:#b91c1c}
.ar-code{display:inline-block;padding:2px 8px;border-radius:6px;font-size:.78rem;font-weight:700;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.ar-code.c2{background:#f0fdf4;color:#15803d}
.ar-code.c4{background:#fef2f2;color:#b91c1c}
.ar-code.c5{background:#fffbeb;color:#92400e}
.ar-callout{border-radius:0 11px 11px 0;padding:15px 20px;margin:0 0 18px;border-left:4px solid var(--primary);background:#f8fafc}
.ar-callout.warn{border-left-color:#f59e0b;background:#fffbeb}
.ar-callout p{margin:0;font-size:.92rem;color:#334155}
@media (max-width:940px){
  .ar-layout{grid-template-columns:1fr;gap:22px}
  .ar-nav{position:static;max-height:none}
  .ar-two{grid-template-columns:1fr}
}

/* Bot protection section -------------------------------------------------- */
.bp-head{max-width:720px}
.bp-head .mk-tag{margin-bottom:14px}
.bp-head code{background:#f1f5f9;padding:2px 6px;border-radius:5px;font-size:.88em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.bp-detail{display:grid;grid-template-columns:1fr 1fr;gap:38px;align-items:start;margin-top:40px;
  padding-top:36px;border-top:1px solid #e2e8f0}
.bp-detail h3{font-size:1.14rem;font-weight:700;color:#0f172a;margin:0 0 14px}
.bp-note{font-size:.87rem;color:#64748b;line-height:1.65;margin:16px 0 20px}
.bp-code .mk-code{margin:0}
@media (max-width:860px){.bp-detail{grid-template-columns:1fr;gap:26px}}

/* ---------------------------------------------------------------------------
   Interactive states
   ---------------------------------------------------------------------------
   Everything clickable, given the states it was missing: focus for keyboard
   users, a press, and a disabled appearance that looks disabled.

   All of it derives from --primary, which the admin's brand colour overrides at
   runtime, so a change of brand colour carries through hover, press and focus
   without anything here being edited. Nothing below alters the resting
   appearance of a control — these are the states that did not exist.
   --------------------------------------------------------------------------- */

/* The catch-all. Every focusable thing gets a ring, whether or not anyone
   remembered to name its class here — a control added next year is covered by
   this rule on the day it is written. The named selectors below only adjust the
   radius where a square ring would look wrong on a rounded control. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* One ring, one shape, everywhere. Applied on :focus-visible rather than :focus
   so a mouse click does not leave a ring behind, while Tab always shows one.
   Buttons carry `border: 1px solid transparent`, so a browser default outline
   was landing on the border edge and was near-invisible on the primary fill. */
.btn:focus-visible,
.icon-btn:focus-visible,
.tab-btn:focus-visible,
.bl-filter:focus-visible,
.cm-cat:focus-visible,
.cm-sort:focus-visible,
.plan-toggle-opt:focus-visible,
.bl-pager a:focus-visible,
a.mk-row-link:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 10px;
}

/* A press should be felt. 1px is enough to read as a button going down and
   small enough not to reflow anything around it. */
.btn:active:not([disabled]):not(.disabled),
.icon-btn:active:not([disabled]),
.bl-filter:active,
.cm-cat:active,
.plan-toggle-opt:active {
  transform: translateY(1px);
}

.btn { cursor: pointer; }

/* The grey box iOS paints over a tapped control is not part of anyone's design,
   and it is inert on a mouse, so it is suppressed everywhere rather than only
   inside the touch query — where two controls were still being missed. */
a, button, summary, label, [role="button"],
.btn, .icon-btn, .tab-btn, .bl-filter, .cm-cat, .cm-sort,
.plan-toggle-opt, .nav-dropdown-item, .faq-item, .bl-pager a {
  -webkit-tap-highlight-color: transparent;
}
.btn, .icon-btn { transition: background .15s, color .15s, border-color .15s, opacity .15s, transform .08s ease; }

/* Hover by darkening the background rather than fading the whole control.
   `opacity: .9` faded the label along with the fill, which reads as the button
   going inactive at the moment it is being pointed at. color-mix keeps the text
   at full strength and works whatever format the brand colour arrives in. */
@supports (color: color-mix(in oklab, red, blue)) {
  .btn-primary:hover:not([disabled]):not(.disabled) {
    background: color-mix(in oklab, var(--primary) 88%, black);
    opacity: 1;
  }
  .btn-primary:active:not([disabled]):not(.disabled) {
    background: color-mix(in oklab, var(--primary) 80%, black);
  }
  .btn-outline:hover:not([disabled]):not(.disabled) {
    border-color: color-mix(in oklab, var(--primary) 40%, var(--border));
    color: var(--primary);
    background: color-mix(in oklab, var(--primary) 6%, transparent);
  }
}

/* Disabled has to look disabled. Fourteen places in the dashboard render a
   button with the attribute when the viewer lacks permission, and without this
   they were indistinguishable from working ones — you click, and nothing
   happens, with no way to tell whether it failed or was never allowed. */
.btn[disabled],
.btn.disabled,
.icon-btn[disabled],
button[disabled] {
  opacity: .5;
  cursor: not-allowed;
  pointer-events: none;
  transform: none !important;
}
/* pointer-events:none removes the tooltip too, so keep the wrapper hoverable
   where a page explains WHY a control is unavailable. */
.btn-disabled-wrap { display: inline-block; cursor: not-allowed; }
.btn-disabled-wrap > .btn[disabled] { pointer-events: none; }

/* Touch: a 44px target is the smallest a finger hits reliably, and the grey
   flash iOS paints over a tapped control is not part of anyone's design. */
@media (hover: none) and (pointer: coarse) {
  .btn, .icon-btn, .tab-btn, .bl-filter, .cm-cat, .cm-sort { min-height: 44px; }
  .btn { padding-top: 12px; padding-bottom: 12px; }
  /* No hover state on a touch device: it sticks after the tap and the control
     stays looking hovered until something else is touched. */
  .btn-primary:hover { background: var(--primary); }
}

/* Links inside prose get the same focus treatment, since they are clickable too. */
.mk-section a:focus-visible,
.lg-body a:focus-visible,
.bl-body a:focus-visible,
.cm-post-body a:focus-visible,
.footer-legal a:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Somebody who has asked their system for less motion has asked for it here. */
@media (prefers-reduced-motion: reduce) {
  .btn, .icon-btn, .bl-card, .cm-thread { transition: none !important; }
  .btn:active, .icon-btn:active, .bl-filter:active, .cm-cat:active,
  .plan-toggle-opt:active { transform: none !important; }
  .bl-card:hover { transform: none !important; }
}
.bl-card-meta .cm-avatar { margin-right: -2px; }
.bl-card-author { font-weight: 600; color: #475569; }

/* --- Pricing cards -------------------------------------------------------
 *
 * Every card the same height, with the call to action pinned to the bottom
 * edge rather than floating wherever that tier's feature list happens to end.
 * Four plans in a three-column grid used to leave Enterprise alone on a second
 * row, reading as an afterthought instead of the top of the ladder; the grid is
 * cols-4 now, which is already 1 / 2 / 4 across the breakpoints.
 */
.pricing-grid .mk-card { display: flex; flex-direction: column; height: 100%; }
.pricing-grid .plan-features { margin-bottom: 20px; }
/* Takes the slack, so every CTA lines up across the row. */
.pricing-grid .plan-cta { margin-top: auto; }

/* Collapsed overflow. The attribute is added by script, so with JavaScript off
 * the full list simply renders — a card that hides what you get behind a button
 * that cannot work would be worse than a long card. */
.plan-features[data-collapsed="1"] li[data-overflow] { display: none; }

.plan-more {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0 0 18px;
  margin: -4px 0 0;
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  color: var(--primary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.plan-more:hover { text-decoration: underline; }
.plan-more::after { content: "▾"; font-size: .7em; line-height: 1; }
.plan-more[aria-expanded="true"]::after { content: "▴"; }

/* The billing page's plan cards use the same collapse, and their footer
 * control is whichever of these the tier renders. */
.plan-grid .d-card > .btn:last-child,
.plan-grid .d-card > span.btn:last-child { margin-top: auto; }

/* A grid item's default min-width is auto, so it refuses to shrink below its
 * content's intrinsic minimum. The snippet in .bp-code is one long unbreakable
 * <script src="..."> line, which held these columns at 416px on a 375px screen
 * and scrolled the whole page sideways. min-width:0 lets the column shrink;
 * the code block scrolls inside its own box instead, which is where a long
 * line belongs. */
.bp-detail > * { min-width: 0; }
.bp-code .mk-code, .bp-code pre { overflow-x: auto; max-width: 100%; }

/* --- Narrow screens: let grid and flex children actually shrink -----------
 *
 * A grid or flex child defaults to min-width:auto, which means it refuses to
 * go narrower than its own content's minimum. One long word, one wide table or
 * one code line then holds the whole column open, the column holds the page
 * open, and every page on the site scrolls sideways. Setting min-width:0 lets
 * the column collapse; the thing that is genuinely too wide then scrolls
 * inside its own box, which is where the scrolling belongs.
 */
.cm-layout > *, .ar-layout > *, .ar-body > * { min-width: 0; }
.ar-table { max-width: 100%; }
/* A code sample is the usual offender: it must scroll, not stretch. */
.ar-body pre, .ar-body .mk-code, .cm-main pre { max-width: 100%; overflow-x: auto; }

/* .ar-two holds two JSON samples side by side. Its children default to
 * min-width:auto, so the widest line of JSON — which never wraps — set the
 * column width and pushed the page past the viewport on a phone. The columns
 * may now shrink; the sample scrolls inside its own dark box, which is already
 * what .mk-code is built to do. */
.ar-two > * { min-width: 0; }
.ar-two .mk-code, .ar-body .mk-code { max-width: 100%; overflow-x: auto; }

/* --- About: the decision chain and the stated limits -------------------- */
.about-chain { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.about-chain li { display: flex; gap: 14px; align-items: flex-start; padding: 16px 18px;
  background: #fff; border: 1px solid var(--border); border-radius: 12px; min-width: 0; }
.about-chain strong { display: block; font-size: .97rem; color: #0f172a; }
.about-chain p { margin: 5px 0 0; font-size: .88rem; color: #475569; line-height: 1.65; }
.about-step { flex: 0 0 28px; width: 28px; height: 28px; border-radius: 8px; background: var(--primary);
  color: #fff; font-weight: 700; font-size: .8rem; display: flex; align-items: center; justify-content: center; }

.about-limit { padding: 18px 20px; border-left: 3px solid #f59e0b; background: #fffbeb;
  border-radius: 0 12px 12px 0; margin-bottom: 14px; }
.about-limit h3 { font-size: .97rem; font-weight: 700; margin: 0 0 6px; color: #78350f; }
.about-limit p { margin: 0; font-size: .88rem; color: #713f12; line-height: 1.7; }

/* Article cover + its photographer credit. The credit is a licence condition
   for stock photography, not an optional flourish, so it sits with the image. */
.bl-cover-fig { margin: 0 0 26px; }
.bl-cover-fig .bl-cover { margin-bottom: 0; }
.bl-credit { font-size: .76rem; color: #94a3b8; margin-top: 8px; text-align: right; }
.bl-credit a { color: #64748b; text-decoration: underline; }

/* ===========================================================================
 * Plan cards
 * ---------------------------------------------------------------------------
 * One design, used by the public pricing page and the customer billing page.
 * They were two separate lots of inline styles that had already drifted — a
 * card on one page and a card on the other agreed about almost nothing — which
 * is the same drift the price list itself was fixed for once.
 *
 * The accent is var(--primary), not a hardcoded blue. An operator can rebrand
 * this install, and a pricing page that stays indigo when everything else has
 * turned green is the most visible place for that to show.
 * ======================================================================== */

.plans-section { position: relative; }

/* The glow behind the cards. Decorative only, so it is inert to pointers and
 * sits under everything; a mix-blend-mode alone would tint the text. */
.plans-glow {
  position: absolute; inset-inline: 10%; top: 0; height: 100%;
  z-index: 0; pointer-events: none;
  background: radial-gradient(circle at 50% 30%, color-mix(in oklch, var(--primary) 55%, transparent) 0%, transparent 70%);
  opacity: .22;
}
.plans-section > *:not(.plans-glow) { position: relative; z-index: 1; }

/* A word in the heading, ringed. */
.plans-mark {
  border: 1px dashed var(--primary);
  background: color-mix(in oklch, var(--primary) 12%, white);
  padding: 2px 12px; border-radius: 14px; display: inline-block; line-height: 1.15;
}

/* ---- The grid ---- */
.plans-grid { display: grid; gap: 16px; align-items: stretch; }
@media (min-width: 780px)  { .plans-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1120px) { .plans-grid { grid-template-columns: repeat(var(--plan-cols, 3), 1fr); } }

/* ---- The card ---- */
.plan-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--card, #fff); border: 1px solid var(--border);
  border-radius: 16px; padding: 26px 24px;
  transition: transform .25s cubic-bezier(.2,.7,.2,1), box-shadow .25s;
}
.plan-card:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -24px rgba(15, 23, 42, .35); }
.plan-card.is-popular {
  border-color: transparent;
  box-shadow: 0 0 0 2px var(--primary), 0 20px 50px -28px color-mix(in oklch, var(--primary) 70%, transparent);
  background: color-mix(in oklch, var(--primary) 5%, var(--card, #fff));
}
.plan-card.is-current { box-shadow: 0 0 0 2px var(--primary); }

.plan-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.plan-name { font-size: 1.6rem; font-weight: 700; color: var(--foreground); margin: 0 0 6px; line-height: 1.15; }
.plan-badge {
  flex: 0 0 auto; background: var(--primary); color: var(--primary-foreground);
  padding: 4px 12px; border-radius: 999px; font-size: .72rem; font-weight: 700; white-space: nowrap;
}
.plan-badge-quiet { background: var(--muted); color: var(--muted-foreground); }
.plan-desc { color: var(--muted-foreground); font-size: .86rem; margin: 0 0 18px; line-height: 1.6; }

.plan-price { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; margin-bottom: 4px; }
.plan-price-amount { font-size: 2.4rem; font-weight: 700; color: var(--foreground); line-height: 1; }
.plan-price-period { color: var(--muted-foreground); font-size: .9rem; }
.plan-price-note { font-size: .78rem; font-weight: 600; margin: 2px 0 0; }
.plan-notes { margin: 8px 0 0; }

/* ---- The button ---- */
.plan-cta {
  display: block; width: 100%; text-align: center; margin: 20px 0 0;
  padding: 14px 18px; border-radius: 12px; font-size: 1rem; font-weight: 600;
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  transition: transform .12s ease, box-shadow .2s, opacity .2s;
}
.plan-cta-primary {
  color: var(--primary-foreground);
  background: linear-gradient(to top, var(--primary), color-mix(in oklch, var(--primary) 78%, white));
  border-color: color-mix(in oklch, var(--primary) 80%, white);
  box-shadow: 0 10px 24px -10px color-mix(in oklch, var(--primary) 75%, transparent);
}
.plan-cta-dark {
  color: #fff;
  background: linear-gradient(to top, #0f172a, #475569);
  border-color: #334155;
  box-shadow: 0 10px 24px -12px rgba(15, 23, 42, .7);
}
.plan-cta:hover:not([disabled]) { opacity: .93; }
.plan-cta:active:not([disabled]) { transform: translateY(1px); }
.plan-cta[disabled] { opacity: .5; cursor: not-allowed; box-shadow: none; }

/* ---- Feature lists ---- */
.plan-feats { list-style: none; padding: 0; margin: 22px 0 0; display: flex; flex-direction: column; gap: 10px; }
.plan-feats li { display: flex; align-items: flex-start; gap: 11px; font-size: .86rem; color: var(--muted-foreground); }
.plan-feats .icon { width: 18px; height: 18px; flex: 0 0 18px; color: var(--foreground); margin-top: 1px; }

.plan-includes { margin-top: auto; padding-top: 18px; border-top: 1px solid var(--border); }
.plan-includes-title { font-size: .88rem; font-weight: 600; color: var(--foreground); margin: 18px 0 12px; }
.plan-includes ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
/* The fold control sits with the list it folds. */
.plan-includes .plan-more { margin-top: 12px; }
.plan-includes li { display: flex; align-items: flex-start; gap: 11px; font-size: .86rem; color: var(--muted-foreground); }
.plan-tick {
  flex: 0 0 22px; width: 22px; height: 22px; border-radius: 999px;
  display: grid; place-content: center;
  background: color-mix(in oklch, var(--primary) 10%, white);
  border: 1px solid var(--primary);
}
.plan-tick .icon { width: 13px; height: 13px; color: var(--primary); }

/* ---- Reveal ----
 * Blur-up, staggered by --i. Scroll-math drives it, not IntersectionObserver:
 * IO does not fire inside a hidden preview pane and would leave the whole price
 * list invisible, which is the worst possible thing for this particular page to
 * do. There is a timeout safety net in the script for the same reason. */
.plan-reveal { opacity: 0; transform: translateY(-14px); filter: blur(9px); }
.plan-reveal.in {
  opacity: 1; transform: none; filter: blur(0);
  transition: opacity .5s ease, transform .5s ease, filter .5s ease;
  transition-delay: calc(var(--i, 0) * 90ms);
}

@media (prefers-reduced-motion: reduce) {
  .mobile-nav { transition: none; }
  .mobile-nav.open .mobile-nav-group > a, .mobile-nav.open .mobile-cta-btn { animation: none; }
  .nav-toggle, .nav-toggle-bar { transition: none; }
  .plan-reveal, .plan-reveal.in { opacity: 1; transform: none; filter: none; transition: none; }
  .plan-card, .plan-cta { transition: none; }
  .plan-card:hover { transform: none; }
}

/* ===========================================================================
 * Stat bento
 * ---------------------------------------------------------------------------
 * One block for showing a number, used by the marketing pages and the
 * dashboard. There were two — .stat-card and .mk-stat — with different radii,
 * type scales and colours for the same job, so the product changed shape as a
 * customer moved from the site into their account.
 *
 * Uneven on purpose. A grid of identical boxes gives every figure the same
 * weight, which is how a page displays eight numbers and says nothing.
 * ======================================================================== */
.bento { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 720px)  { .bento { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.bento-tile {
  border-radius: var(--card-radius); padding: 22px 24px; position: relative; overflow: hidden;
  background: var(--card, #fff); border: 1px solid var(--border);
  display: flex; flex-direction: column; justify-content: center;
}
@media (min-width: 720px) {
  .bento-tile { grid-column: span var(--span, 3); grid-row: span var(--rows, 1); }
}

/* ---- The lead tile ---- */
.bento-lead {
  background: var(--primary); border-color: transparent; color: var(--primary-foreground);
  padding: 34px 32px; justify-content: space-between; gap: 26px;
}
/* Diagonal weave, faded towards the bottom-left so the sentence stays legible.
 * mask-image is the whole effect; without it the stripes run under the text. */
.bento-weave {
  position: absolute; inset: 0; pointer-events: none; opacity: .3;
  background: repeating-linear-gradient(45deg,
    color-mix(in oklch, var(--primary-foreground) 55%, transparent) 0 1px, transparent 1px 10px);
  -webkit-mask-image: radial-gradient(ellipse 80% 55% at 100% 0%, #000 60%, transparent 105%);
          mask-image: radial-gradient(ellipse 80% 55% at 100% 0%, #000 60%, transparent 105%);
}
.bento-lead > * { position: relative; z-index: 1; }
.bento-pill {
  display: inline-block; padding: 5px 12px; border-radius: 999px;
  background: color-mix(in oklch, var(--primary-foreground) 14%, transparent);
  color: color-mix(in oklch, var(--primary-foreground) 78%, transparent);
  font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .12em;
  margin-bottom: 18px;
}
.bento-figure {
  margin: 0; font-size: clamp(2.6rem, 7vw, 3.9rem); line-height: 1;
  letter-spacing: -.03em; font-weight: 700; color: var(--primary-foreground);
}
.bento-note {
  margin: 0; max-width: 30ch; font-size: .84rem; line-height: 1.55;
  color: color-mix(in oklch, var(--primary-foreground) 72%, transparent);
}

/* ---- The smaller tiles ---- */
.bento-row { display: flex; align-items: center; gap: 16px; width: 100%; }
.bento-body { min-width: 0; flex: 1; }
.bento-label {
  margin: 0 0 2px; font-size: .66rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .1em; color: var(--muted-foreground);
}
.bento-value { margin: 0; font-size: 1.7rem; font-weight: 700; letter-spacing: -.02em; color: var(--foreground); line-height: 1.15; }
.bento-sub { margin: 3px 0 0; font-size: .76rem; color: var(--muted-foreground); }
.bento-delta { margin: 4px 0 0; font-size: .74rem; font-weight: 600; }
.bento-delta.is-up { color: #059669; }
.bento-delta.is-down { color: #dc2626; }

.bento-icon {
  flex: 0 0 42px; width: 42px; height: 42px; border-radius: 999px;
  display: grid; place-content: center; font-size: 1.15rem; font-weight: 700;
  background: var(--background); color: var(--foreground);
  box-shadow: 0 1px 3px rgba(15, 23, 42, .1);
}
.bento-icon .icon { width: 19px; height: 19px; }

.bento-plain { background: var(--muted); }
/* A tile that goes somewhere says so on hover, and keeps the text colour it
 * would have had — an anchor inheriting link blue would repaint the figure. */
a.bento-link { text-decoration: none; color: inherit; transition: transform .18s ease, box-shadow .18s ease; }
a.bento-link:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -20px rgba(15, 23, 42, .45); }
@media (prefers-reduced-motion: reduce) { a.bento-link, a.bento-link:hover { transition: none; transform: none; } }
.bento-compact { text-align: center; align-items: center; }
.bento-compact .bento-value { font-size: 1.5rem; }
.bento-compact .bento-label { margin: 4px 0 0; }

/* Shape, not data. No axis, no scale — it says rising or flat and nothing more,
 * which is why it is aria-hidden rather than given a label it cannot honour. */
.bento-spark { display: flex; align-items: flex-end; gap: 3px; height: 34px; flex: 0 0 auto; }
.bento-spark i { display: block; width: 5px; border-radius: 999px; background: var(--foreground); opacity: .85; }

/* ---- On a dark section ---- */
.bento-dark .bento-tile { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.1); }
.bento-dark .bento-plain { background: rgba(255,255,255,.06); }
.bento-dark .bento-value, .bento-dark .bento-figure { color: #fff; }
.bento-dark .bento-label, .bento-dark .bento-sub { color: #94a3b8; }
.bento-dark .bento-spark i { background: #fff; }
.bento-dark .bento-icon { background: rgba(255,255,255,.1); color: #fff; box-shadow: none; }
.bento-dark .bento-lead { background: var(--primary); border-color: transparent; }
