/* Quant Intelligence — design system
   Dark, futuristic, glass + glow. Two accent hues: cyan (Alden) / violet (QuantMind). */

:root {
  --bg: #05060a;
  --bg-elevated: #0b0d16;
  --surface: rgba(255, 255, 255, 0.04);
  --surface-border: rgba(255, 255, 255, 0.09);
  --text: #eef1fb;
  --text-dim: #9aa3c0;
  --text-faint: #7880a6;   /* was #5c6389 (3.5:1); WCAG 2.2 AA needs 4.5:1 for normal text, this is 4.8-5.2:1 on our backgrounds */

  --cyan: #3b78ff;

  /* The UI accent, themed per product. Defaults to cyan for the company
     site and Alden; quantmind.css overrides it to violet, so the shared
     stylesheet doesn't have to know which product it is rendering.
     --accent-rgb exists because rgba() needs the channels separately. */
  --accent: #3b78ff;
  --accent-rgb: 59, 120, 255;
  --accent-soft: rgba(var(--accent-rgb), 0.15);
  --violet: #a76bff;
  --violet-soft: rgba(167, 107, 255, 0.15);

  /* Destructive actions. var(--danger) was a pale salmon — desaturated enough
     that on a violet-tinted card it read warm rather than red, which is
     the wrong signal on a delete confirmation. */
  --danger: #dc2626;
  --danger-rgb: 220, 38, 38;
  --danger-soft: #f08a8a;
  /* Danger as TEXT, which is a different job from danger as a border or a
     fill. #dc2626 is a light-theme red — GitHub Primer ships #d1242f for the
     same purpose, on a white page — and on this ground it measures 4.19:1,
     under the 4.5:1 WCAG AA needs for body text. This measures 6.04:1 and
     still reads as an alert rather than a pink. The admin's Revoke button
     had already worked around it with a hardcoded #ff8f8f; this replaces
     that so both follow one token. */
  --danger-text: #f85149;
  /* One definition per product, here rather than inside each product's
     own stylesheet, because the company pages market both and need both
     without loading either. The product stylesheets read from these. */
  /* VARIANT X1 — the accent itself into near-black. Trying options one at
     a time; swap this line for the next when asked. */
  --grad-alden: linear-gradient(135deg, #2f6cf5 0%, #050b1f 100%);
  --grad-quantmind: linear-gradient(135deg, #4a238f 0%, #1d0f3a 100%);

  /* The company's own gradient — the Quant Intelligence mark, used where
     a control belongs to neither product. */
  --grad: linear-gradient(120deg, var(--accent), var(--violet));
  /* The company name's gradient, violet into cyan, with the colours fixed
     so a product page that redefines --accent and --grad still shows it. */
  --grad-company: linear-gradient(120deg, #a76bff, #3b78ff);
  /* Ink on a primary button. Near-black works on the cyan end of the
     company gradient; a product whose gradient is dark needs white. */
  --btn-ink: #050608;

  --radius: 18px;
  --radius-sm: 10px;
  --maxw: 1160px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  background: var(--bg);
  /* Where scrollbars always show (macOS set to "Always", Windows), the
     bar took 15px on the right and every page sat 7.5px left of centre,
     jumping sideways against short pages with no bar. Reserving the same
     space on both edges keeps content on the true centre everywhere.
     Overlay scrollbars (phones, default macOS) reserve nothing. */
  scrollbar-gutter: stable both-edges;
  /* Dark scrollbars on the dark pages (they drew white where macOS or
     Windows always shows them). Inherited by every scrolling box. */
  scrollbar-color: #2c3149 var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: "Space Grotesk", "Inter", system-ui, sans-serif;
  font-weight: 600;
  line-height: 1.15;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1em; color: var(--text-dim); }

a { color: inherit; text-decoration: none; }

.container {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 28px;
}

/* ---------- Nav ---------- */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease), backdrop-filter 0.3s var(--ease);
  border-bottom: 1px solid transparent;
  /* permanent scrim so the wordmark stays legible over any hero background,
     not only once .scrolled adds the full blurred bar */
  background: linear-gradient(rgba(5, 6, 10, 0.65), rgba(5, 6, 10, 0));
}

.nav.scrolled {
  background: rgba(5, 6, 10, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--surface-border);
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: "Space Grotesk", sans-serif;
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
}

.nav-mark {
  height: 40px;
  width: auto;
  display: block;
}

.nav-links {
  display: flex;
  /* Reading order. This was row-reverse, so the markup order and the
     rendered order disagreed — marketing navs read right-to-left while
     the portal read left-to-right, and the same site had two opposite
     directions depending on which page you were on. */
  flex-direction: row;

  /* Centred against the page, not against the space left over. Flexing
     the group only centres it within its own share of the row, which
     lands off-centre whenever the brand and the right-hand controls are
     different widths — which they always are. */
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  justify-content: center;
  align-items: center;
  gap: 34px;
  font-size: 0.92rem;
  color: var(--text-dim);
}

.nav-links a {
  transition: color 0.2s var(--ease);
}

.nav-links a:hover { color: var(--text); }

.nav-cta {
  padding: 9px 18px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text) !important;
  font-size: 0.88rem;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.nav-cta:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.nav-toggle {
  display: none;
  background: none;
  border: none;
  color: var(--text);
  font-size: 1.4rem;
  cursor: pointer;
}

/* ---------- Products nav dropdown ---------- */
/* Replaces a plain #anchor link (which either scrolled down the current
   page or, on any page besides index.html, pointed at a #products id
   that doesn't exist there) with a real product picker. */

.nav-dropdown {
  position: relative;
}

/* Pill styling itself comes from the shared .nav-cta class (added
   alongside .nav-dropdown-toggle in the markup) — same bubble as the
   Contact button, no caret, so the two read as the same control. This
   rule only adds what .nav-cta doesn't already cover: it's a <button>,
   not an <a>. */
.nav-dropdown-toggle {
  display: inline-flex;
  align-items: center;
  font: inherit;
  cursor: pointer;
}

.nav-dropdown-toggle:hover { color: var(--text); }

.nav-dropdown-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 160px;
  margin: 0;
  padding: 6px;
  list-style: none;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: rgba(11, 13, 22, 0.97);
  backdrop-filter: blur(14px);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  z-index: 300;
}

.nav-dropdown-menu.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* The Products menu sits centred under its button. The account menu keeps
   its own placement. */
.nav-dropdown:not(.nav-account-menu) > .nav-dropdown-menu { left: 50%; transform: translate(-50%, -6px); }
.nav-dropdown:not(.nav-account-menu) > .nav-dropdown-menu.open { transform: translate(-50%, 0); }

/* The account menu hangs from an avatar, not from a row of pills, so it is
   centred under it rather than aligned to one edge. One rule, so the live
   product pages and the admin surface behave identically: they were
   left-aligned and right-aligned respectively, which read as two different
   controls wearing the same clothes. */
.nav-account-dropdown {
  left: 50%;
  right: auto;
  transform: translateX(-50%) translateY(-6px);
}

.nav-account-dropdown.open,
.admin-account:hover .nav-account-dropdown,
.admin-account:focus-within .nav-account-dropdown {
  transform: translateX(-50%) translateY(0);
}

.nav-dropdown-menu li a {
  display: block;
  padding: 9px 12px;
  border-radius: 6px;
  font-size: 0.88rem;
  color: var(--text-dim);
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}

.nav-dropdown-menu li a:hover { background: var(--surface); color: var(--text); }

/* The admin header links moved into this menu, and they carried the
   you-are-here marker with them. Without a rule for it the marker would
   still be in the markup and invisible on screen. */
.nav-dropdown-menu li a[aria-current="page"] {
  color: var(--text);
  background: var(--surface);
}

/* ---------- Language switcher (custom UI, driven by Google Website Translator) ---------- */

/* The menu hangs off this, so it stays the positioning context even
   though the pill itself is placed absolutely against the nav. */
.lang-menu { position: absolute; }

/* Google's own widget renders a bare native <select> that's barely
   stylable — keep it in the DOM (so it still works) but visually
   hidden, and drive it entirely from our own pill + menu below. */
.goog-hidden-host,
#google_translate_element {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* The container's own clipping above isn't enough on its own: a native
   <select> can render at its full intrinsic content width (however
   many px "français"/"Deutsch"/etc. need) regardless of a 1px-wide
   overflow:hidden ancestor — confirmed via a real browser check, where
   the select rendered a full, clickable, fully visible dropdown well
   outside its container's box. Nothing here ever reads or writes this
   select anymore (translation is driven entirely by the googtrans
   cookie Google reads on its own — see js/translate-widget.js /
   js/translate-widget-ar.js), so it's safe to collapse the select
   itself directly rather than only relying on its container. */
.goog-hidden-host select,
#google_translate_element select {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  min-width: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  border: 0 !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  opacity: 0 !important;
}

.lang-toggle {
  display: flex;
  align-items: center;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}

.lang-toggle:hover { border-color: var(--text-dim); color: var(--text); }
.lang-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 150px;
  margin: 0;
  padding: 6px;
  list-style: none;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: rgba(11, 13, 22, 0.97);
  backdrop-filter: blur(14px);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  z-index: 300;
}

.lang-menu.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.lang-menu li {
  padding: 9px 12px;
  border-radius: 6px;
  font-size: 0.88rem;
  color: var(--text-dim);
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}

.lang-menu li:hover { background: var(--surface); color: var(--text); }
.lang-menu li[aria-selected="true"] { color: var(--accent); }

/* Google's translate banner shoves the whole page down and adds a
   branded top bar — suppress it so layout/nav stay untouched. Also
   covers the floating "translating…" spinner and word-hover tooltip,
   which Google appends directly to <body> (not inside an iframe, so
   the .skiptranslate iframe rule below never touched them) — left
   visible, the spinner reads as permanently stuck since our own pill
   is the only language control meant to be on screen. */
.goog-te-banner-frame.skiptranslate,
.goog-te-banner-frame,
.goog-te-balloon-frame,
.goog-te-spinner-pos,
.goog-te-menu-frame,
#goog-gt-tt,
#goog-gt-vt,
.VIpgJd-yAWNEb-L7lbkb,
.VIpgJd-suEOdc,
.goog-tooltip,
.goog-tooltip:hover {
  display: none !important;
  visibility: hidden !important;
}

/* Hovering translated text for a second makes Google paint a blue box
   over the phrase and offer to show the original. It reads as the page
   selecting itself at random, and it only appears in a translated
   language, which is exactly where we least want the seams showing.

   These wrap REAL PAGE TEXT, so they must never be display:none — that
   deletes the sentence. Neutralise the decoration and leave the text.
   `.goog-text-highlight` is the legacy widget, the VIpgJd <font> is the
   current one; both ship depending on which build Google serves. */
.goog-text-highlight,
font.VIpgJd-yAWNEb-VIpgJd-fmcmS-sn54Q,
.VIpgJd-yAWNEb-VIpgJd-fmcmS-sn54Q {
  background: none !important;
  background-color: transparent !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  color: inherit !important;
  cursor: inherit !important;
}

.skiptranslate iframe {
  display: none !important;
}

/* Catch-all: any top-level element Google injects straight into body
   carries "skiptranslate" — belt-and-suspenders alongside the named
   selectors above in case Google adds new chrome under the same
   convention. */
body > .skiptranslate {
  display: none !important;
}

/* Full-viewport cover shown for the brief window between picking a
   language and the reloaded page's translation being ready (see
   js/translate-widget.js / js/translate-widget-ar.js). Some of
   Google's own UI (the native .goog-te-combo <select> especially)
   escapes ordinary clip/overflow hiding — it can end up positioned so
   that it's laid out outside #google_translate_element's clipped box
   entirely, which the named selectors above can't reach. Physically
   covering the viewport is the only way to guarantee nothing Google
   injects is ever visible, instead of chasing individual selectors. */
.lang-switch-overlay {
  position: fixed;
  inset: 0;
  z-index: 99999;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.lang-switch-overlay .lang-switch-spinner {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid var(--surface-border);
  border-top-color: var(--accent);
  animation: lang-switch-spin 0.8s linear infinite;
}

@keyframes lang-switch-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .lang-switch-overlay .lang-switch-spinner {
    animation: none;
  }
}

body {
  top: 0 !important;
  position: static !important;
}

@media (max-width: 860px) {
  .nav-lang { margin-inline-start: auto; margin-inline-end: 10px; }
}

/* ---------- Hero ---------- */

/* Standalone full-viewport intro page: logo, wordmark, and the company
   copy that used to live on the ring's hero card — its own "page"
   before the tornado, in normal document flow (not fixed/sticky). At
   rest the logo is static (no idle spin); js/logo-intro.js drives a
   scroll-linked rotateY + fade scoped to this element's own scroll-out
   (see there), so it only spins as the user actually scrolls from this
   page into the tornado page, not continuously. */
.tornado-logo-above {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* The text sits at the bottom of the section and the logo takes the
     space above it, so the logo is centred between the nav and the text
     at any viewport height. Centring the whole stack instead only lined
     up at the one height a fixed margin was tuned for. */
  justify-content: flex-end;
  gap: 18px;
  /* 81px of nav + the same 18px gap that sits under the slot, so the
     space above the logo and the space below it come out equal. */
  /* The bottom padding lifts the whole block, logo and text together, by
     the same amount at every height. 150px is the lift, and it is the
     ceiling: the lift is measured from the bottom, so a larger one puts
     the logo under the nav on a short window. Measured at 150px the logo
     clears the nav by 22px on an 800px viewport, and on anything shorter
     the block is already pinned by the top padding and stops moving. */
  padding: 99px 28px 150px;
  text-align: center;
  overflow: hidden;
  perspective: 600px;
  /* No background of its own — body now carries one continuous
     background spanning this section + .hero below it (see body{}),
     so there's no per-section boundary for a seam to appear at. */
}

/* Sized to the logo, not to the leftover space. When it absorbed the
   slack the gap under the logo grew with the window, pushing the text
   further away on taller screens. Now the gap is the same everywhere and
   the slack sits above the logo instead. */
.logo-slot {
  flex: 0 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin-bottom: 42px;
}

.tornado-logo-above img {
  height: 300px;
  max-height: 100%;
  width: auto;
  display: block;
  will-change: transform, opacity;
}

.tornado-logo-above h1 {
  margin: 0;
}

.tornado-logo-above p {
  max-width: 620px;
  color: var(--text-dim);
  font-size: 0.98rem;
  line-height: 1.7;
  margin: 0;
}

@media (max-width: 900px) {
  .tornado-logo-above { padding: 120px 24px 48px; }
  .tornado-logo-above img { height: 210px; }
}

.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 108px 28px 64px;
  overflow: hidden;
  /* This same .hero class is shared by alden/index.html and
     quantmind/index.html, which have no logo-intro page before them —
     they need this background themselves. On the main site's
     index.html specifically, body.has-logo-intro (see body{} and the
     rule right below) supplies one continuous background spanning
     both pages instead, so .hero's own copy is cancelled there —
     otherwise .hero's opaque-ish background would repaint over body's
     right at the page 1/page 2 boundary, the exact seam this was
     meant to fix. */
  background:
    radial-gradient(60% 50% at 50% 28%, rgba(var(--accent-rgb), 0.16), transparent 70%),
    radial-gradient(50% 45% at 72% 62%, rgba(167, 107, 255, 0.12), transparent 70%),
    linear-gradient(
      to right,
      transparent 0,
      transparent calc(50% - 1px),
      rgba(255, 255, 255, 0.035) 50%,
      transparent calc(50% + 1px),
      transparent 100%
    ),
    var(--bg);
  background-size: 100% 100%, 100% 100%, 64px 64px, 100% 100%;
}

/* Main site's index.html only (body carries the class) — one
   continuous background spanning the logo-intro page + .hero combined
   (200vh, both min-height:100vh), not repeated, so there's no
   per-section boundary for a seam to appear at. .hero's own background
   above is cancelled here so it can't repaint over this. Below 200vh
   it's just the plain --bg fill from body{}, same as before. */
body.has-logo-intro {
  background:
    radial-gradient(60% 50% at 50% 28%, rgba(var(--accent-rgb), 0.16), transparent 70%),
    radial-gradient(50% 45% at 72% 62%, rgba(167, 107, 255, 0.12), transparent 70%),
    linear-gradient(
      to right,
      transparent 0,
      transparent calc(50% - 1px),
      rgba(255, 255, 255, 0.035) 50%,
      transparent calc(50% + 1px),
      transparent 100%
    ),
    var(--bg);
  background-repeat: no-repeat, no-repeat, repeat, no-repeat;
  background-size: 100% 200vh, 100% 200vh, 64px 64px, 100% 100%;
}

body.has-logo-intro .hero {
  background: none;
}

.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 22px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 20px;
}

.hero-eyebrow .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}

.tornado-logo-above h1 {
  font-size: clamp(2rem, 5vw, 3.4rem);
}

/* The company name runs blue to violet, the same as in the header, so
   blue sits on the Q side in both. */
.tornado-logo-above h1 .grad {
  background: linear-gradient(120deg, #3b78ff, #a76bff);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero-actions {
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  /* The label is a flex item, so text-align does nothing here. Without
     this, any button stretched wider than its text (the pricing cards
     stretch theirs to card width) leaves the label against the left
     padding. It only became visible when "Start 7-day free trial" was
     replaced by the much shorter "Subscribe". */
  justify-content: center;
  gap: 8px;
  padding: 13px 26px;
  border-radius: 999px;
  font-size: 0.95rem;
  font-weight: 500;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), border-color 0.25s var(--ease), background 0.25s var(--ease);
}

.btn-primary {
  background: var(--grad);
  color: var(--btn-ink);
  font-weight: 600;
  box-shadow: 0 0 0 rgba(var(--accent-rgb), 0);
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px -8px rgba(var(--accent-rgb), 0.55);
}

.btn-ghost {
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text);
}

.btn-ghost:hover {
  border-color: var(--text-dim);
  transform: translateY(-2px);
}

/* .btn-primary casts a glow from under itself on hover and .btn-ghost only
   lifted, so a pair of them read as one button being live and the other
   inert. Ghost buttons now take the same light.

   Destructive actions are excluded deliberately. A glow is an invitation,
   and closing an account or revoking a licence should not be inviting. The
   two "Close my account" buttons carry .btn-danger for this reason: without
   it they are plain ghost buttons and this rule would light them up like a
   Continue. */
.btn-ghost:hover:not(.btn-danger):not(.cust-danger) {
  box-shadow: 0 10px 30px -8px rgba(var(--accent-rgb), 0.55);
}

@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}

.scroll-cue {
  position: absolute;
  bottom: 34px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--text-faint);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  z-index: 2;
  animation: fade-cue 2.4s ease-in-out infinite;
}

.scroll-cue .line {
  width: 1px;
  height: 34px;
  background: linear-gradient(var(--text-faint), transparent);
}

@keyframes fade-cue {
  0%, 100% { opacity: 0.35; transform: translate(-50%, 0); }
  50% { opacity: 1; transform: translate(-50%, 6px); }
}

/* ---------- Section shared ---------- */

section {
  position: relative;
  padding: 92px 0;
}

/* The docs subnav is a single row of links, not a section of content, so
   the full section rhythm below it pushed the page's real content
   (usually a search box) a long way down for no reason. */
.subnav-row {
  padding-top: 0;
  padding-bottom: 40px;
}

/* The subnav already provides the separation, so the section under it
   starts close instead of adding a second full gap on top. */
.subnav-row + section {
  padding-top: 24px;
}

.section-head {
  max-width: 620px;
  margin: 0 auto 64px;
  text-align: center;
}

.section-tag {
  display: inline-block;
  color: var(--accent);
  font-size: 0.95rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.section-head h2 {
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
}

.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}

.reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

/* Legal text is never animated, and never depends on JavaScript to be
   readable. A terms or privacy page that renders blank because a script
   was blocked, or because an observer never fired, is unreadable at the
   exact moment someone is checking what they agreed to. Everywhere else
   a fade-in is decoration; here it is a way to lose the document. */
.legal .reveal,
.legal.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Same reason, and a worse consequence. The consent box injects the legal
   pages' own markup, which carries .reveal blocks that start invisible and
   are revealed by an IntersectionObserver. That observer ran at page load,
   long before this content existed, so it never sees it: the last document
   sat at opacity 0 for its full 1607px, reading as a wall of empty scroll.

   The customer was ticking a box to say they had read a document that was
   not on screen, and the consent hash covers textContent, so the stored
   record claimed they had read it. Never animate anything inside here. */
.consent-doc .reveal,
.consent-doc.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Someone who has asked the system for less motion should not have to
   rely on an IntersectionObserver to see the page at all. */
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------- Products ---------- */

.products {
  background: linear-gradient(180deg, transparent, rgba(var(--accent-rgb), 0.03) 40%, transparent);
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px;
}

.product-card {
  position: relative;
  padding: 42px 36px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015));
  overflow: hidden;
  /* overflow:hidden alone does not clip ::before, because that element
     carries filter: blur(60px) and a filtered box is not always clipped to
     an ancestor's border-radius. The glow sits at top:-60% right:-30%, so
     what escaped was exactly one corner: the top-right rendered square while
     the other three stayed round. clip-path clips on the geometry rather
     than the paint, so it holds for filtered children too. */
  clip-path: inset(0 round var(--radius));
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease);
}

.product-card::before {
  content: "";
  position: absolute;
  top: -60%;
  right: -30%;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  filter: blur(60px);
  opacity: 0.35;
  z-index: 0;
  transition: opacity 0.4s var(--ease);
}

.product-card.alden::before { background: var(--accent); }
.product-card.quantmind::before { background: var(--violet); }

.product-card:hover {
  transform: translateY(-6px);
  border-color: rgba(255, 255, 255, 0.18);
}

.product-card:hover::before { opacity: 0.55; }

.product-card > * { position: relative; z-index: 1; }

.product-icon {
  width: 46px;
  height: 46px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 22px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

.product-icon svg { width: 22px; height: 22px; }

.product-card.alden .product-icon svg { color: var(--accent); }
.product-card.quantmind .product-icon svg { color: var(--violet); }

.product-card h3 {
  font-size: 1.5rem;
  margin-bottom: 8px;
}

.product-tag {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 18px;
  display: block;
}

.product-card p { margin-bottom: 26px; }

.product-links {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--text);
  border-bottom: 1px solid var(--surface-border);
  padding-bottom: 2px;
  transition: border-color 0.2s var(--ease), gap 0.2s var(--ease), color 0.2s var(--ease);
}

.product-card.alden .link-arrow.primary { color: var(--accent); border-color: var(--accent); }
.product-card.quantmind .link-arrow.primary { color: var(--violet); border-color: var(--violet); }

/* The accent variant. This existed only inside .product-card, so every
   other `link-arrow primary` on the site — ten of them — rendered
   identically to a plain one: white text on a faint grey rule, which
   reads as a caption rather than a link. */
.link-arrow.primary {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.45);
}

.link-arrow.primary:hover { border-color: var(--accent); }

.link-arrow:hover { gap: 10px; }

.link-arrow svg { width: 14px; height: 14px; }

/* ---------- Approach / About teaser ---------- */

/* ---------- Single unified tornado: hero + all product cards ---------- */

.tornado-stage {
  position: relative;
  width: min(1500px, 88vw);
  height: min(72vh, 700px);
  max-width: none;
  flex: 1 1 auto;
  min-width: 0;
  perspective: 1800px;
  overflow: visible;
}

/* #tornado-info lives as a direct child of <body>, NOT nested inside
   .hero/#tornado-section — GSAP applies a transform to .hero alongside
   position:fixed while it's pinned, and a transformed ancestor creates
   a new containing block that traps position:fixed descendants,
   resolving them against that ancestor instead of the real viewport.
   Being a body-level sibling avoids that entirely. position:fixed here
   is visually equivalent to the old position:absolute-inside-.hero
   during the pinned scroll phase, since .hero fills the viewport while
   pinned — but now it holds regardless of any ancestor's transform. */
.tornado-info {
  position: fixed;
  left: clamp(20px, 4vw, 64px);
  top: 50%;
  transform: translateY(-50%);
  max-width: 340px;
  z-index: 150;
}

/* The first setDimensions()/layout() pass runs before webfonts resolve, so it
   positions the panel from fallback-font measurements and can leave it in
   view. spiral.js adds .is-ready once it has laid out against the real fonts.
   !important because layout() writes opacity and visibility inline. */
.tornado-info:not(.is-ready) {
  opacity: 0 !important;
  visibility: hidden !important;
}

.tornado-info h4 {
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 8px;
}

.tornado-info h4:empty {
  display: none;
}

.tornado-info p {
  font-family: "Inter", sans-serif;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 0;
}

@media (max-width: 1100px) {
  /* Below desktop width, show the info panel pinned to the bottom of
     the viewport, centered, card-styled — cards are heading-only now,
     so this text is the only place the detail copy lives. The stage
     also shrinks a bit so cards can't orbit into the panel's space.
     (The deeper 900px breakpoint below still owns the full
     static-card-list mobile fallback separately.) */
  .tornado-stage {
    height: min(48vh, 460px);
  }
  .tornado-info {
    /* No transform for centering — JS never writes a transform on this
       element at all (text/opacity only, see spiral.js), but avoiding
       transform-based centering here keeps that guarantee cheap to
       verify by inspection. left/right + auto margins center it with
       zero dependency on transform. */
    left: 16px;
    right: 16px;
    top: auto;
    bottom: 24px;
    transform: none;
    width: auto;
    margin: 0 auto;
    max-width: 500px;
    text-align: center;
    padding: 20px 24px;
    border-radius: var(--radius);
    border: 1px solid var(--surface-border);
    /* Solid elevated surface, not the translucent card gradient — the
       card gradient reads fine floating inside the .hero 3D scene, but
       this panel is a body-level fixed overlay over arbitrary page
       content and needs real contrast on its own, same as .form-panel
       uses elsewhere for standalone card-style overlays. */
    background: var(--bg-elevated);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
  }
}

.spiral-ring {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  will-change: transform;
}

.spiral-card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(300px, 66vw);
  will-change: transform, opacity, filter;
  transform: translate(-50%, -50%);
  transform-style: preserve-3d;
}

/* Front (text) and back (blank) faces of the card, each hidden when
   rotated away from the viewer, so text never renders mirrored as the
   card turns edge-on and shows its back during the exit spiral. */
.spiral-card-face,
.spiral-card-back {
  padding: 20px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02));
  backdrop-filter: blur(6px);
  text-align: center;
  backface-visibility: hidden;
}

.spiral-card-back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
}

.spiral-badge {
  display: inline-block;
  font-size: 0.7rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
  margin-bottom: 12px;
  border: 1px solid var(--surface-border);
}

.spiral-badge.alden { color: var(--accent); border-color: rgba(var(--accent-rgb), 0.35); }
.spiral-badge.quantmind { color: var(--violet); border-color: rgba(167, 107, 255, 0.35); }

.spiral-card h3 { font-size: 1.5rem; margin-bottom: 10px; }
.spiral-card h4 { font-size: 1.25rem; margin-bottom: 10px; }
.spiral-card p { font-size: 0.98rem; margin: 0 0 14px; }
.spiral-card p:last-child { margin-bottom: 0; }
/* A card whose one line is its title: no separate heading, the grey text
   carries it, set larger. Its longer explanation sits in the info panel. */
.spiral-card p.spiral-card-lead { font-size: 1.28rem; line-height: 1.45; }
.spiral-card h3:last-child,
.spiral-card h4:last-child { margin-bottom: 0; }
.spiral-card .link-arrow { justify-content: center; }

@media (max-width: 900px) {
  .hero { min-height: auto; padding: 120px 24px 60px; flex-direction: column; }
  .tornado-stage {
    width: 100%;
    max-width: 100%;
    height: auto;
    perspective: none;
  }
  .spiral-ring {
    position: static;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .spiral-card,
  .spiral-card:has(> h3) {
    position: static;
    width: 100%;
    transform: none !important;
    opacity: 1 !important;
    filter: none !important;
  }
  /* spiral.js's own JS (setDimensions/layout/updateInfoPanelOutOfRange)
     bails out entirely below this same width (isMobile(), <=900) —
     "CSS handles the static mobile fallback" per its own comment — so
     nothing ever hides this panel again once scrolled past the cards
     above. Cards are static and heading-only in this stacked layout, so
     the panel has nothing left to be "the detail copy for" anyway; the
     1100px breakpoint's fixed-bottom treatment was only ever meant for
     the 900-1100px band where the JS positioning system is still
     running. Left visible, this rendered as a permanent empty-looking
     box sitting on top of (and inside) the page footer. */
  .tornado-info {
    display: none;
  }
}

/* ---------- CTA band ---------- */

.cta-band {
  text-align: center;
  padding: 100px 28px;
}

/* The closing call to action reads as the end of the last section rather
   than a section of its own, so the transition into it is tighter than the
   normal rhythm. This was a QuantMind-only override; it applies to both
   products now so the two homepages end the same way. */
section:has(+ .cta-band) { padding-bottom: 60px; }
.cta-band { padding-top: 50px; }

.cta-panel {
  max-width: 720px;
  margin: 0 auto;
  padding: 64px 44px;
  border-radius: 28px;
  border: 1px solid var(--surface-border);
  background: radial-gradient(120% 140% at 50% 0%, rgba(var(--accent-rgb), 0.1), transparent 60%), var(--bg-elevated);
  position: relative;
  overflow: hidden;
}

/* The sign-in card on the company homepage belongs to both products, so it
   wears both accents. Without this it inherits the root default, which is
   Alden's cyan, and a shared card ends up looking like Alden's. Cyan enters
   from the left and violet from the right, in the same order as the two
   buttons inside it, so the panel reads as the pair rather than as a blend
   of nothing in particular. Scoped to this one card: .cta-panel is reused
   on alden/download.html and blog/index.html, where a second product's
   colour would be wrong. */
.cta-panel.cta-both {
  background:
    radial-gradient(95% 130% at 0% 0%, rgba(59, 120, 255, 0.15), transparent 62%),
    radial-gradient(95% 130% at 100% 0%, rgba(167, 107, 255, 0.15), transparent 62%),
    var(--bg-elevated);
}

/* Just this one button. --grad-quantmind runs violet at the top left to
   near black at the bottom right, which on this card put the dark end
   against the panel's violet glow on the right and the bright end next to
   Alden's button on the left. The stops are swapped here so it leans black
   on the left and opens into violet on the right, away from Alden and into
   its own side of the card. The token itself is untouched, because every
   QuantMind page uses it as it stands. */
.cta-panel.cta-both .btn-primary[data-product="quantmind"] {
  background: linear-gradient(135deg, #1d0f3a 0%, #4a238f 100%);
}

/* Both sign-in buttons the same width, on this card only. They size to
   their text by default, so "QuantMind sign in" came out 189px against
   Alden's 151px and the pair read as a primary and a secondary rather than
   as two equals. 190px is the wider one's natural width, so the shorter
   grows to meet it and neither is truncated. The pair was already centred;
   only the widths differed. */
.cta-panel.cta-both .hero-actions .btn { min-width: 190px; }

.cta-panel h2 {
  font-size: clamp(1.7rem, 3vw, 2.2rem);
  margin-bottom: 14px;
}

.cta-panel p {
  max-width: 460px;
  margin: 0 auto 30px;
}

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--surface-border);
  padding: 56px 0 40px;
}

.footer-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 40px;
  margin-bottom: 40px;
}

.footer-brand {
  max-width: 320px;
}

.footer-brand .nav-brand { margin-bottom: 14px; }

.footer-brand p { font-size: 0.9rem; }

.footer-cols {
  display: flex;
  gap: 64px;
  flex-wrap: wrap;
}

.footer-col h5 {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0 0 16px;
  font-weight: 600;
}

.footer-col a {
  display: block;
  color: var(--text-dim);
  font-size: 0.92rem;
  margin-bottom: 12px;
  transition: color 0.2s var(--ease);
}

.footer-col a:hover { color: var(--text); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 0.82rem;
  color: var(--text-faint);
}

/* This divider separates the footer's link columns from the copyright bar,
   so it is only correct when those columns are actually present. No page
   currently renders .footer-top, which is why every page was showing two
   parallel lines with nothing between them — the real footer edge is
   .site-footer's border-top above. */
.footer-top + .footer-bottom {
  padding-top: 28px;
  border-top: 1px solid var(--surface-border);
}

.footer-bottom-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
}

.footer-bottom-links a { color: var(--text-faint); }

/* Phones: the links sit centred, close enough to share one line where
   they fit; where they don't (QuantMind has seven), the rows are centred
   so no link is left alone on the edge. */
@media (max-width: 520px) {
  .footer-bottom { justify-content: center; text-align: center; }
  /* Inline rather than flex, so text-wrap: balance can even out the rows
     when they wrap (four and three, not five and two or six and one). */
  .footer-bottom-links { display: block; text-align: center; text-wrap: balance; font-size: 0.78rem; line-height: 2; }
  .footer-bottom-links a { display: inline-block; margin: 0 4px; }
}
.footer-bottom-links a:hover { color: var(--text-dim); }

/* Two-floor footer (2026-11-05): logo + "Contact us" left, "Get in touch"
   + round icon links right; a line; copyright, legal links, Manage cookies. */
.footer-top { align-items: center; margin-bottom: 32px; }
.footer-brand { display: grid; gap: 12px; justify-items: start; }
.footer-brand .footer-logo { margin-bottom: 0; }
/* The nav's own wordmark, at a footer size: same display and the same
   letter-to-mark proportions, so the Q sits tight against "uant…". */
.footer-logo.qi-brand, .footer-logo.qm-brand { display: block; font-size: 22px; line-height: 1; }
.footer-logo .qi-q, .footer-logo .qm-q { display: inline; height: 1.1em; width: auto; vertical-align: -0.12em; }
.footer-logo.nav-brand-group { display: inline-flex; align-items: center; gap: 10px; }
.footer-logo .nav-mark-link { width: auto; height: auto; }
.footer-logo .nav-mark { width: 34px; height: 34px; object-fit: contain; }
.footer-logo .nav-brand { font-size: 22px; }
.footer-contact { display: inline-flex; align-items: center; gap: 7px; color: var(--text-dim); font-size: 0.9rem; border-bottom: 1px solid var(--surface-border); padding-bottom: 2px; }
.footer-contact:hover { color: var(--text); border-color: var(--text-dim); }
.footer-contact svg { width: 15px; height: 15px; flex: none; }
.footer-touch { display: grid; gap: 12px; justify-items: end; }
.footer-touch-label { font-size: 0.74rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-faint); }
.footer-social-icons { gap: 10px; }
.footer-social-icons a { width: 38px; height: 38px; justify-content: center; border-radius: 50%; border: 1px solid var(--surface-border); background: var(--surface); color: var(--text-dim); }
.footer-social-icons a:hover { color: var(--text); border-color: var(--text-dim); }
.footer-social-icons svg { width: 17px; height: 17px; }
.footer-cookies { display: inline-flex; align-items: center; gap: 6px; }
.footer-cookies svg { width: 15px; height: 15px; }
@media (max-width: 520px) {
  .footer-top { flex-direction: column; align-items: center; text-align: center; }
  .footer-brand, .footer-touch { justify-items: center; }
}

/* ---------- Simple/legal pages ---------- */

.page-hero {
  padding: 160px 28px 60px;
  text-align: center;
}

.page-hero h1 { font-size: clamp(2rem, 4vw, 2.8rem); }
.page-hero p { max-width: 560px; margin: 0 auto; }

.legal {
  max-width: 760px;
  margin: 0 auto;
  padding: 0 28px 120px;
}

.legal h2 {
  font-size: 1.3rem;
  margin-top: 44px;
  margin-bottom: 14px;
}

/* Sub-clause heading inside a numbered section — clearly subordinate to
   the section's <h2> so the numbering still reads as the outline. */
.legal h3 {
  font-size: 1.02rem;
  margin-top: 30px;
  margin-bottom: 10px;
  color: var(--text);
}

.legal p, .legal li { color: var(--text-dim); font-size: 0.96rem; }
.legal ul { padding-left: 20px; }
.legal .updated {
  color: var(--text-faint);
  font-size: 0.85rem;
  margin-bottom: 40px;
  display: block;
}

.about-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 48px;
  align-items: start;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 28px 120px;
}

.about-grid .stat {
  padding: 26px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  margin-bottom: 16px;
}

.about-grid .stat strong {
  display: block;
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.7rem;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  margin-bottom: 4px;
}

.about-grid .stat span {
  font-size: 0.85rem;
  color: var(--text-faint);
}

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    padding: 16px 28px 22px;
    background: rgba(5, 6, 10, 0.96);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--surface-border);
  }
  .nav-links.open { display: flex; }
  /* Plain links go full-width; pills must NOT — width:100% here also hit
     .nav-cta (which is an <a>), stretching the Contact/Sign up bubbles
     across the whole menu and pushing their text left, because 10px 0
     overrode the pill's own 9px 18px padding. */
  .nav-links > a:not(.nav-cta) { padding: 10px 0; width: 100%; }
  .nav-dropdown:not(.nav-account-menu) { align-self: flex-start; margin-top: 6px; }
  /* Flattening applies to menus that live INSIDE the collapsed nav list,
     like the company Products menu. The account menu sits outside it, in
     the header bar itself, so it stays a real popover: flattened, it
     spilled its items across the brand as unstyled text. */
  .nav-dropdown-menu:not(.nav-account-dropdown) { position: static; margin-top: 8px; opacity: 1; visibility: visible; transform: none; transition: none; display: none; border: none; background: none; backdrop-filter: none; padding: 0 0 0 14px; }
  .nav-dropdown-menu:not(.nav-account-dropdown).open { display: block; }
  /* Every pill — the <a class="nav-cta"> variants and the <button>
     dropdown toggle — gets identical sizing and centred text, so they
     read as one control family instead of three slightly different
     shapes. */
  .nav-cta,
  .nav-dropdown-toggle {
    align-self: flex-start;
    margin-top: 6px;
    width: auto;
    justify-content: center;
    text-align: center;
  }
  .nav-toggle { display: block; }
  .product-grid { grid-template-columns: 1fr; }
  .approach-grid { grid-template-columns: 1fr; }
  .about-grid { grid-template-columns: 1fr; }
  .footer-top { flex-direction: column; }
}

@media (max-width: 520px) {
  .hero-actions { flex-direction: column; align-items: stretch; }
  .btn { justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-eyebrow, .hero h1, .hero-sub, .hero-actions { animation: none; opacity: 1; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .scroll-cue { animation: none; }
  .tornado-logo-above img { animation: none; }
}

/* ---------- Pricing ---------- */

/* Monthly/annual toggle. The annual figures are the source of truth in the
   markup (data-annual); JS only swaps which one is displayed, so the page
   still shows complete, correct pricing with JS disabled. */
.billing-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 0 0 44px;
  font-size: 0.92rem;
  color: var(--text-dim);
}

/* Shared by the pricing toggle and the portal's auto-confirm switch. One
   definition, two users: a second copy would drift the moment either moved. */
.billing-switch,
.ui-switch {
  position: relative;
  width: 52px;
  height: 28px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  cursor: pointer;
  padding: 0;
  transition: border-color 0.2s var(--ease);
}

.billing-switch::after,
.ui-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--text-dim);
  transition: transform 0.22s var(--ease), background 0.22s var(--ease), box-shadow 0.22s var(--ease);
}

.billing-switch[aria-pressed="true"]::after,
.ui-switch[aria-pressed="true"]::after {
  transform: translateX(24px);
  background: var(--accent);
}

.billing-switch:focus-visible,
.ui-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.billing-toggle .is-active { color: var(--text); }

.billing-save {
  font-size: 0.78rem;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid rgba(var(--accent-rgb), 0.35);
  background: var(--accent-soft);
  color: var(--accent);
}

/* ---------- Pricing: Personal / Business tabs ---------- */
/* Both products' pricing pages. Colours come from --accent, so Alden gets
   cyan and QuantMind violet from the same rules. */
.plan-audience {
  display: flex;
  width: fit-content;
  margin: 0 auto 44px;
  padding: 5px;
  gap: 4px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

.plan-audience-tab {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 170px;
  padding: 11px 22px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.3s var(--ease), color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.plan-audience-tab svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}
.plan-audience-tab:hover { color: var(--text); }
.plan-audience-tab.is-active {
  color: var(--text);
  background: var(--accent-soft);
  border-color: rgba(var(--accent-rgb), 0.35);
}
.plan-audience-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 480px) {
  .plan-audience { width: 100%; }
  .plan-audience-tab { min-width: 0; flex: 1; padding: 11px 12px; }
}

/* ---------- Pricing: Monthly | Annual, inside each paid card ---------- */
/* Small, on the right, under the tagline and just above the price. */
.card-billing {
  display: flex;
  gap: 2px;
  width: fit-content;
  margin: 14px 0 12px auto;
  padding: 2px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

/* A plan with no period to choose (Free) carries an invisible copy, so its
   price stays level with its neighbours'. */
.card-billing-empty { visibility: hidden; }

.card-billing-opt {
  appearance: none;
  padding: 3px 8px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--text-faint);
  font: inherit;
  font-size: 0.7rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.card-billing-opt:hover { color: var(--text-dim); }
.card-billing-opt.is-active { background: rgba(255, 255, 255, 0.09); color: var(--text); }
.card-billing-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Cards keep the width they had before the tabs: Alden's four tiers sat at
   258px, QuantMind's three at 352px. Without this, two or three cards
   stretch to fill a row that used to hold more. */
.price-grid.price-panel { grid-template-columns: repeat(auto-fit, 258px); justify-content: center; }
.price-grid.price-panel.quantmind-panel { grid-template-columns: repeat(auto-fit, 352px); }
@media (max-width: 980px) {
  .price-grid.price-panel, .price-grid.price-panel.quantmind-panel { grid-template-columns: 1fr; }

  /* Stacked one per row there is nothing to line up with, so the spacing
     that kept prices level across a row (the tagline's fixed height, the
     invisible pill on Free) only leaves gaps. */
  .price-panel .price-card { padding: 28px 24px; }
  .price-panel .price-card .price-tagline { min-height: 0; margin-bottom: 16px; }
  .price-panel .card-billing-empty { display: none; }
  .price-panel .card-billing { margin: 0 0 12px auto; }
  .price-panel .price-annual-note { min-height: 0; margin-bottom: 20px; }
  .price-panel .price-annual-note:empty { display: none; }
}

/* ---------- Pricing: seats, inside a per-seat card ---------- */
.card-seats {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 18px 0 0;
  padding: 12px 0;
  border-top: 1px solid var(--surface-border);
  border-bottom: 1px solid var(--surface-border);
}
.card-seats-label { font-size: 0.92rem; color: var(--text); }
.seat-stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--surface-border);
  border-radius: 12px;
  overflow: hidden;
}
.seat-step {
  appearance: none;
  width: 34px;
  height: 34px;
  border: 0;
  background: var(--surface);
  color: var(--text);
  font-size: 1.05rem;
  cursor: pointer;
}
.seat-step:hover { background: var(--accent-soft); }
.seat-step:disabled { color: var(--text-faint); cursor: default; background: none; }
.seat-count {
  min-width: 44px;
  height: 34px;
  line-height: 34px;
  border-inline: 1px solid var(--surface-border);
  text-align: center;
}
.card-seats-total { margin: 12px 0 18px; font-size: 0.88rem; color: var(--text-dim); }
.card-seats-total strong { color: var(--text); font-size: 1rem; }

/* Back link, used for every back link inside a page (not the nav).
   Touch screens get a square arrow box with the label beside it, since
   there is no hover to reveal anything. A mouse or trackpad gets the box
   alone, which opens to show the label on hover. Chosen by what the
   device can do rather than by width, so a touch laptop or an iPad with a
   trackpad gets the right one. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9rem;
  color: var(--text-dim);
}
.back-link-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text);
}
.back-link svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.back-link-label { white-space: nowrap; }
[dir="rtl"] .back-link svg { transform: scaleX(-1); }

@media (hover: hover) and (pointer: fine) {
  .back-link {
    gap: 0;
    height: 38px;
    padding: 0 11px;
    border-radius: 10px;
    border: 1px solid rgba(var(--accent-rgb), 0.25);
    background: rgba(255, 255, 255, 0.04);
    backdrop-filter: blur(8px);
    color: var(--text);
    overflow: hidden;
    transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
  }
  .back-link-box { width: auto; height: auto; border: 0; background: none; }
  .back-link-label {
    max-width: 0;
    opacity: 0;
    transition: max-width 0.3s var(--ease), opacity 0.2s var(--ease), margin 0.3s var(--ease);
  }
  .back-link:hover,
  .back-link:focus-visible { border-color: rgba(var(--accent-rgb), 0.7); box-shadow: 0 0 18px rgba(var(--accent-rgb), 0.3); }
  .back-link:hover .back-link-label,
  .back-link:focus-visible .back-link-label { max-width: 160px; opacity: 1; margin-left: 7px; margin-right: 3px; }
}

/* Checkout: one price card, centred, with the order summary and the
   button that opens Paddle's checkout. */
.checkout { max-width: 440px; margin: 0 auto; }
.checkout-back { margin: 0 0 14px; }
.checkout-card { text-align: center; }
/* Hidden until checkout.js has filled in the chosen plan, so the default
   plan and price in the markup never flash first. */
.checkout:not(.is-ready) .checkout-card { visibility: hidden; }
.checkout-card .card-billing { margin: 4px auto 12px; }
.checkout-card .card-seats { justify-content: center; gap: 16px; }
.checkout-card .card-seats { flex-wrap: wrap; }
.checkout-seat-line { flex-basis: 100%; margin: 10px 0 0; font-size: 0.88rem; color: var(--text-dim); }
.checkout-product {
  margin: 0 0 4px;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.checkout-summary { margin: 18px 0 0; padding-top: 16px; border-top: 1px solid var(--surface-border); }
.card-seats:not([hidden]) + .checkout-summary { border-top: 0; padding-top: 0; }
/* The amount sits beside its label, not under it. */
.checkout-summary div { display: flex; justify-content: center; align-items: baseline; gap: 10px; }
.checkout-summary dt { font-size: 0.92rem; color: var(--text-dim); }
.checkout-summary dd { margin: 0; font-size: 1.1rem; font-weight: 600; color: var(--text); }
.checkout-renewal { margin: 16px 0 0; font-size: 0.82rem; line-height: 1.6; color: var(--text-dim); }
.checkout-card .checkout-pay { margin-top: 22px; }

.price-grid {
  display: grid;
  /* Fits three or four tiers without a separate rule per page: the cards
     wrap to two rows rather than being squeezed below a readable width. */
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 24px;
  align-items: start;
}

@media (max-width: 980px) {
  .price-grid { grid-template-columns: 1fr; max-width: 460px; margin: 0 auto; }
}

.price-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 34px 30px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

.price-card.featured {
  border-color: rgba(var(--accent-rgb), 0.4);
  background: linear-gradient(160deg, rgba(var(--accent-rgb), 0.07), rgba(255, 255, 255, 0.015));
}

.price-badge {
  position: absolute;
  top: -11px;
  left: 30px;
  font-size: 0.72rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid rgba(var(--accent-rgb), 0.4);
  background: var(--bg-elevated);
  color: var(--accent);
}

.price-card h2 {
  font-size: 1.15rem;
  margin: 0 0 6px;
}

.price-card .price-tagline {
  font-size: 0.88rem;
  color: var(--text-dim);
  margin: 0 0 22px;
  min-height: 40px;
}

.price-amount {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 4px;
}

.price-amount .price-value {
  font-family: "Space Grotesk", sans-serif;
  font-size: 2.1rem;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.price-amount .price-period {
  font-size: 0.9rem;
  color: var(--text-faint);
}

.price-annual-note {
  font-size: 0.82rem;
  color: var(--text-faint);
  min-height: 20px;
  margin-bottom: 24px;
}

.price-card .btn { width: 100%; text-align: center; }

.price-features {
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 0.9rem;
  color: var(--text-dim);
}

.price-features li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  line-height: 1.5;
}

.price-features svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-top: 3px;
  color: var(--accent);
}

.price-card.quantmind .price-features svg,
.price-card.quantmind .price-badge { color: var(--violet); }
.price-card.quantmind.featured {
  border-color: rgba(167, 107, 255, 0.4);
  background: linear-gradient(160deg, rgba(167, 107, 255, 0.07), rgba(255, 255, 255, 0.015));
}
.price-card.quantmind .price-badge { border-color: rgba(167, 107, 255, 0.4); }
.billing-switch.quantmind[aria-pressed="true"]::after { background: var(--violet); }

.price-note {
  max-width: 620px;
  margin: 56px auto 0;
  text-align: center;
  font-size: 0.9rem;
  color: var(--text-faint);
  line-height: 1.7;
}

.price-note a { color: var(--text-dim); border-bottom: 1px solid var(--surface-border); }

/* ---------- Support tickets + dashboard ---------- */
/* Shared by both products. Colour accents come from the product's own
   stylesheet via .quantmind / default cyan, same as .price-card. */

.console-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--surface-border);
  margin-bottom: 32px;
  overflow-x: auto;
}

.console-tab {
  padding: 12px 18px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.92rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.console-tab:hover { color: var(--text); }

.console-tab[aria-selected="true"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.console-panel[hidden] { display: none; }

/* ---------- API pages: two-level menu (2026-11-05) ----------
   Top row: section pills, the same pill as the site nav, lit the same way
   when current. Second row: the pages inside the section, smaller, lit the
   same way. Section and page light up together. Credit sits at the right
   of the top row on every API page. */
.api-nav {
  display: grid;
  gap: 14px;
  margin-bottom: 18px;
}
/* Centred rows of pills, the docs pages' layout. */
.api-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.api-row[hidden] { display: none; }
.api-row-plans { padding-bottom: 14px; border-bottom: 1px solid var(--surface-border); }
.api-row-plans .api-tab { padding: 10px 26px; font-size: 0.95rem; }
.api-row-sub { padding-top: 14px; border-top: 1px solid var(--surface-border); }
/* Credit of the account being viewed, under the menu on the right. */
.api-context {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 18px;
}
.api-context[data-plan="org"] [data-credit],
.api-context[data-plan="individual"] [data-org-credit] { display: none; }
.api-tab, .api-subtab {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  white-space: nowrap;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.api-tab { padding: 9px 18px; font-size: 0.9rem; }
.api-subtab { padding: 6px 14px; font-size: 0.82rem; }
.api-tab:hover, .api-subtab:hover { color: var(--text); border-color: var(--text-dim); }
.api-tab[aria-current="page"], .api-subtab[aria-current="page"] {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.32);
}

.credit-pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 6px 6px 6px 16px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
}
.credit-pill-label {
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.credit-pill-value {
  font-family: "Space Grotesk", sans-serif;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.credit-pill.is-low { border-color: rgba(255, 196, 107, 0.4); }
.credit-pill.is-low .credit-pill-value { color: #ffc46b; }
.credit-pill.is-empty { border-color: rgba(var(--danger-rgb), 0.45); }
.credit-pill.is-empty .credit-pill-value { color: var(--danger-text); }

.project-bar[hidden] { display: none; }

/* Usage meter */
.usage-meter {
  margin: 18px 0 6px;
  height: 8px;
  border-radius: 999px;
  background: var(--surface);
  overflow: hidden;
}

.usage-meter-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  /* One tone, not --grad. The gradient runs accent-to-violet across the
     full width of the bar, so a fill that stops at 62% shows only the first
     part of it — and its own end is a different colour from its start. On a
     decorative bar that is fine; on a spend meter the length IS the reading,
     and two tones invite the eye to measure the wrong edge. */
  background: var(--accent);
  transition: width 0.4s var(--ease);
}

.usage-legend {
  display: flex;
  justify-content: space-between;
  font-size: 0.82rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* Ticket list */
.ticket-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px 20px;
  padding: 18px 26px;
  border-bottom: 1px solid var(--surface-border);
}

.ticket-row:last-child { border-bottom: none; }

.ticket-subject {
  font-weight: 500;
  color: var(--text);
  font-size: 0.96rem;
}

.ticket-meta {
  font-size: 0.82rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.portal-status.open { color: #4fb0ff; border-color: rgba(var(--accent-rgb), 0.35); background: rgba(var(--accent-rgb), 0.1); }
.portal-status.pending { color: #fbbf24; border-color: rgba(251, 191, 36, 0.35); background: rgba(251, 191, 36, 0.1); }
.portal-status.closed { color: var(--text-faint); }

/* Forms inside portal panels */
.portal-form {
  padding: 26px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.portal-form label {
  display: block;
  font-size: 0.86rem;
  color: var(--text-dim);
  margin-bottom: 7px;
}

.portal-form input[type="text"],
.portal-form select,
.portal-form textarea {
  width: 100%;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 0.94rem;
}

.portal-form textarea { min-height: 150px; resize: vertical; }

.portal-form input:focus,
.portal-form select:focus,
.portal-form textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.portal-form .form-note { margin: 0; }

/* Code samples */
.code-block {
  margin: 20px 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  overflow: hidden;
}

.code-block-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--surface-border);
  font-size: 0.78rem;
  color: var(--text-faint);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.code-block pre {
  margin: 0;
  padding: 18px;
  overflow-x: auto;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.86rem;
  line-height: 1.65;
  color: var(--text);
}

.code-block code { font-family: inherit; }

/* Footer community icons */
.footer-social {
  display: flex;
  align-items: center;
  gap: 14px;
}

.footer-social a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--text-faint);
  font-size: 0.82rem;
  transition: color 0.2s var(--ease);
}

.footer-social a:hover { color: var(--text-dim); }
.footer-social svg { width: 17px; height: 17px; }


/* ---------- Nav: consistent pills + current-page state ---------- */

/* .nav-cta sits on both <a> and <button>; without explicit inline-flex
   centring the two render their labels differently at some widths. */
.nav-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.2;
}

/* Current page, set via aria-current="page" by js/nav-active.js. It
   compares resolved URLs rather than href strings, since the same page
   is reached by different relative paths depending on depth. Nothing
   marked the current page before at all, which is why a nav item could
   look "selected" on a page it didn't belong to: that was the browser
   holding :focus on the last thing clicked, with no real active state
   to contradict it. */
.nav-links a[aria-current="page"] { color: var(--text); }

.nav-links a.nav-cta[aria-current="page"] {
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* Keyboard focus stays visible; a mouse click doesn't leave a stuck ring. */
.nav-links a:focus { outline: none; }
.nav-links a:focus-visible,
.nav-toggle:focus-visible,
.nav-dropdown-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- Tickets: signed-out lookup + chat ---------- */

.ticket-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--surface-border);
  margin-bottom: 28px;
}

.ticket-tab {
  padding: 12px 18px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.94rem;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.ticket-tab:hover { color: var(--text); }
.ticket-tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); }
.ticket-panel[hidden] { display: none; }

.signed-in-note {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  margin-bottom: 24px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  font-size: 0.9rem;
  color: var(--text-dim);
}

.signed-in-note a { color: var(--accent); }

/* Support chat */
.chat-panel {
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 460px;
}

.chat-log {
  flex: 1;
  padding: 24px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: 460px;
}

.chat-msg {
  max-width: 78%;
  padding: 12px 16px;
  border-radius: 14px;
  font-size: 0.93rem;
  line-height: 1.55;
}

.chat-msg.bot {
  align-self: flex-start;
  background: var(--bg-elevated);
  border: 1px solid var(--surface-border);
  color: var(--text-dim);
  border-bottom-left-radius: 4px;
}

.chat-msg.user {
  align-self: flex-end;
  background: var(--accent-soft);
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  color: var(--text);
  border-bottom-right-radius: 4px;
}

.chat-msg.system {
  align-self: center;
  background: none;
  border: none;
  color: var(--text-faint);
  font-size: 0.84rem;
  text-align: center;
  max-width: 100%;
}

.chat-form {
  display: flex;
  gap: 10px;
  padding: 16px 20px;
  border-top: 1px solid var(--surface-border);
}

.chat-form input {
  flex: 1;
  padding: 11px 14px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 0.93rem;
}

.chat-form input:focus { outline: none; border-color: var(--accent); }

.chat-form button {
  padding: 11px 20px;
  border-radius: 999px;
  border: none;
  background: var(--grad);
  color: #06111a;
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
}

.chat-form button:disabled { opacity: 0.5; cursor: default; }

.chat-typing { display: inline-flex; gap: 4px; }
.chat-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-faint);
  animation: chat-bounce 1.2s infinite ease-in-out;
}
.chat-typing span:nth-child(2) { animation-delay: 0.15s; }
.chat-typing span:nth-child(3) { animation-delay: 0.3s; }

@keyframes chat-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .chat-typing span { animation: none; }
}

/* ---------- hidden-attribute correctness ---------- */
/* A class that sets `display` outranks the plain [hidden] UA rule, so
   any component styled with display:flex/grid ignores the attribute and
   renders anyway. This bit the signed-in note, which appeared for
   signed-out visitors. Global, because every panel toggled via `hidden`
   in this codebase has the same exposure. */
[hidden] { display: none !important; }

/* Form fields: cover every input type actually used, not just text.
   email/password/number were falling through to browser defaults and
   rendering as small white boxes on the dark panels. */
.portal-form { text-align: start; }

.portal-form input[type="text"],
.portal-form input[type="email"],
.portal-form input[type="password"],
.portal-form input[type="number"],
.portal-form input[type="search"],
.portal-form select,
.portal-form textarea {
  width: 100%;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 0.94rem;
}

.portal-form input::placeholder,
.portal-form textarea::placeholder { color: var(--text-faint); }

.portal-form input:focus,
.portal-form select:focus,
.portal-form textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.portal-panel-head h2 { text-align: start; }

/* Hint text inside portal forms is plain text, not a panel. The product
   stylesheets give .form-note a border and background, which reads as
   an input field when it sits directly under a real one — confusing on
   a form where the next thing genuinely is an input. */
.portal-form .form-note {
  padding: 0;
  border: none;
  background: none;
  margin-bottom: 0;
  font-size: 0.84rem;
}

/* ---------- Capability cards: level 1 + level 2 ---------- */
/* Level 1 states the capability. Level 2 holds the caveats and the
   detail that would otherwise bloat the headline claim — kept visible
   but visually subordinate, so the card answers "what does it do?"
   first and "what are the edges?" second. */

.cap-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  align-items: start;
}

@media (max-width: 1020px) { .cap-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 680px)  { .cap-grid { grid-template-columns: 1fr; } }

.cap-card {
  padding: 30px 28px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  height: 100%;
}

.cap-card > h3 {
  font-size: 1.08rem;
  margin: 0 0 10px;
  line-height: 1.3;
}

.cap-card > p {
  color: var(--text-dim);
  font-size: 0.93rem;
  line-height: 1.6;
  margin: 0;
}

.cap-detail {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--surface-border);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.cap-sub {
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: rgba(255, 255, 255, 0.02);
}

.cap-sub h4 {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 5px;
  letter-spacing: 0;
}

.cap-sub p {
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--text-faint);
  margin: 0;
}

/* ---------- Nav: one bubble treatment for every link ---------- */
/* Every nav item sits in a visible bubble at rest — same border, same
   fill, same size — so no single item reads as "selected" just by
   having a background. The current page is the only state that changes
   both the frame and the label. */

.nav-links > a,
.nav-dropdown-toggle {
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.88rem;
  line-height: 1.2;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease), color 0.2s var(--ease);
}

.nav-links > a:hover,
.nav-dropdown-toggle:hover {
  color: var(--text);
  border-color: var(--text-faint);
}

/* .nav-cta gets no separate resting treatment. Giving it its own fill
   made it the only filled item in the bar, so it read as permanently
   selected next to the others — the confusion the current-page state
   is meant to own. */
.nav-links > a.nav-cta,
.nav-dropdown-toggle.nav-cta {
  border-color: var(--surface-border);
  background: var(--surface);
  color: var(--text-dim) !important;
}

.nav-links > a.nav-cta:hover,
.nav-dropdown-toggle.nav-cta:hover {
  color: var(--text) !important;
  border-color: var(--text-faint);
}

/* Current page: frame and label both lift. */
.nav-links a[aria-current="page"],
.nav-links a.nav-cta[aria-current="page"] {
  color: var(--accent) !important;
  border-color: var(--accent);
  background: var(--accent-soft);
  /* The same glow the tag pill's dot carries. Both were already the
     identical hex; the dot only looked brighter because it had a halo
     and the flat pill didn't, so the selected item read as duller than
     the decoration next to it. */
  box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.32);
}

@media (max-width: 860px) {
  /* Stacked menu: every pill keeps its bubble at its natural width,
     rather than plain links going full-bleed and pills not. */
  .nav-links > a,
  .nav-links > a:not(.nav-cta),
  .nav-dropdown-toggle {
    width: auto;
    align-self: flex-start;
    padding: 9px 16px;
    text-align: center;
    justify-content: center;
  }
}

/* ---------- Portal chrome ---------- */
/* The signed-in portal is a different surface from the marketing site,
   so it doesn't wear the marketing nav. Solid bar rather than the
   translucent scrim, product name marked as a portal, and links that
   are portal destinations rather than sales pages. */

.nav.portal-nav {
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--surface-border);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.portal-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  margin-left: 10px;
  border-radius: 6px;
  /* Lit like the current-page pill, permanently: inside the portal it
     marks where you are, which is what that treatment means. */
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.32);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  font-family: "Space Grotesk", sans-serif;
}

/* The account link is an icon-led pill: a round avatar-style mark with the
   label beside it, so it reads the way people expect an account control to
   look without becoming an unlabelled icon-only button. */
/* The account button is the one control in the portal header that isn't a
   destination — it's who you're signed in as. It gets the far-right
   corner and a shape nothing else on the site uses: a large circle with
   the label stacked underneath, so it reads as an avatar rather than as
   another nav item competing with Home and Sign out. */
.nav-account {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  margin-inline-start: 18px;
  color: var(--text-dim);
  line-height: 1;
  text-align: center;
}

.nav-account svg {
  width: 48px;
  height: 48px;
  padding: 11px;
  border-radius: 50%;
  /* A touch lighter than the panel borders: this ring sits on the dark
     nav scrim, where --surface-border nearly disappears. */
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: var(--bg);
  flex: none;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease),
              background 0.2s var(--ease);
}

.nav-account span {
  font-size: 0.92rem;
  white-space: nowrap;
}

.nav-account:hover { color: var(--text); }
.nav-account:hover svg { border-color: var(--text-dim); background: var(--surface); }

.nav-account[aria-current="page"] { color: var(--accent); }

.nav-account[aria-current="page"] svg {
  border-color: var(--accent);
  color: var(--accent);
}

/* ---------- The account menu ----------

   Sign out used to sit in the nav as a pill that became "Portal" on
   marketing pages: one slot, two meanings, depending on where you were
   standing. Both options now live under the account icon, so each control
   says one thing.

   Opens on hover, and on focus for keyboards. main.js also wires the
   click, which is what touch devices get, since they have no hover. */
/* The wrapper is as wide as the menu it opens, with the button centred
   inside it. That sits the button dead centre over the dropdown while the
   menu itself stays anchored to the nav's right edge, so it can never open
   off-screen. Centring the menu under the button instead would push it
   past the viewport on narrow windows. */
.nav-account-menu {
  position: relative;
  margin-inline-start: 18px;
  /* Wide enough for the longest item, "Manage subscription", on one line.
     At 176px it wrapped mid-word, which reads as a typo rather than as a
     wrap. The wrapper and the menu share this width so the button stays
     centred over it. */
  min-width: 212px;
  display: flex;
  justify-content: center;
}

/* Same corner as every other page. It clears the account control on its
   own: the menu wrapper is wide, but the button inside it is centred, so
   the wrapper's right-hand half is empty space the pill sits above. */
.portal-nav .nav-lang,
.nav.has-account .nav-lang { inset-inline-end: 6px; }

/* .nav-account is a <button> here, not an <a>. */
.nav-account-menu .nav-account {
  margin-inline-start: 0;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
  /* .nav-dropdown-toggle sets inline-flex; the icon stacks over its label. */
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}

/* It hangs off the right edge of the nav, so it opens leftwards. */
.nav-account-dropdown {
  left: 0;
  right: 0;
  min-width: 0;
}

/* Without this the 10px gap between button and menu breaks the hover the
   moment the pointer crosses it. */
.nav-account-dropdown::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: -12px;
  height: 12px;
}

.nav-account-menu:hover .nav-account-dropdown,
.nav-account-menu:focus-within .nav-account-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.nav-account-menu:hover .nav-account,
.nav-account-menu:focus-within .nav-account { color: var(--text); }

.nav-account-menu:hover .nav-account svg,
.nav-account-menu:focus-within .nav-account svg {
  border-color: var(--text-dim);
  background: var(--surface);
}

/* Signing out is the one destructive item in the menu. */
.nav-account-dropdown li a { white-space: nowrap; }
.nav-account-dropdown a[data-action="sign-out"] { color: var(--text-dim); }
.nav-account-dropdown a[data-action="sign-out"]:hover {
  /* Was a pale pink fill with the deep red on top: dark-on-light inside a
     dark menu, measuring 2.00:1 — the least readable state on the site, and
     it appeared only once the pointer was on it. A low red tint over the
     menu's own ground keeps the destructive signal and puts the text back
     above AA at 5.32:1. */
  background: rgba(var(--danger-rgb), 0.14);
  color: var(--danger-text);
}


/* Beside a 48px circle with a label under it, a horizontal pill reads as
   a different kind of object, so in the portal it takes the same vertical
   shape — glyph over label — a size down.

   Taken out of flow deliberately. In flow it pushed the account button
   off the corner it was placed on; floated into the top-right it sits
   above and outboard of the circle, clearing both the circle and the
   label beneath it, and the account button's position is untouched. */
.nav-lang {
  position: absolute;
  top: 8px;
  inset-inline-end: 6px;
  margin: 0;
}

/* The same capsule, turned on its side. Everything is inherited from
   .lang-toggle — border, radius, background, type size, gap, icon — and
   only the axis and the padding change, with the original's 7px/12px
   transposed to 12px/7px so the proportions carry over instead of being
   invented. Scaled down a touch at the end, since it now sits beside the
   account button rather than among the nav links. */
.lang-toggle {
  flex-direction: column;
  gap: 2px;
  padding: 3px 4px 5px;
  border-radius: 999px;
  font-size: 0.65rem;
  line-height: 1;
}

.lang-toggle svg { width: 13px; height: 13px; }


/* Tags read larger across the site — they were small enough next to a
   page heading to look incidental rather than deliberate. */
.hero-eyebrow { font-size: 0.92rem; font-weight: 500; }
.section-tag { font-weight: 600; margin-bottom: 14px; }

/* Section headings that label a group ("Common topics", "Still need
   help?") use the same treatment as a page tag, so the labelling
   language is consistent rather than one being a tag and the other a
   plain heading. */
.section-head > h2.as-tag {
  font-family: "Inter", sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0;
}

.cap-card, .cap-card h3, .cap-card p, .cap-sub { text-align: start; }

/* ---------- FAQ / help accordion ---------- */
/* Built on native <details>/<summary>: keyboard support, screen-reader
   semantics and open/close all work with no JS, so the content is
   readable even if the search script never loads. */

.help-search {
  position: relative;
  max-width: 620px;
  margin: 0 auto 44px;
}

.help-search input {
  width: 100%;
  padding: 15px 20px 15px 50px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
}

.help-search input::placeholder { color: var(--text-faint); }
.help-search input:focus { outline: none; border-color: var(--accent); }

.help-search svg {
  position: absolute;
  left: 19px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--text-faint);
  pointer-events: none;
}

.help-group { margin-bottom: 44px; }

.help-group > h2 {
  font-family: "Inter", sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 18px;
}

.help-item {
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  margin-bottom: 12px;
  overflow: hidden;
}

.help-item > summary {
  list-style: none;
  cursor: pointer;
  padding: 20px 56px 20px 24px;
  position: relative;
  font-family: "Space Grotesk", sans-serif;
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--text);
  line-height: 1.4;
  transition: color 0.2s var(--ease);
}

.help-item > summary::-webkit-details-marker { display: none; }
.help-item > summary:hover { color: var(--accent); }
.help-item > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Chevron, rotated when open. */
.help-item > summary::after {
  content: "";
  position: absolute;
  right: 24px;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -6px;
  border-right: 2px solid var(--text-faint);
  border-bottom: 2px solid var(--text-faint);
  transform: rotate(45deg);
  transition: transform 0.25s var(--ease), border-color 0.2s var(--ease);
}

.help-item[open] > summary::after {
  transform: rotate(-135deg);
  margin-top: -2px;
  border-color: var(--accent);
}

.help-answer {
  padding: 0 24px 22px;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.65;
}

.help-answer p { margin: 0 0 0.9em; }
.help-answer p:last-child { margin-bottom: 0; }
.help-answer a { color: var(--accent); border-bottom: 1px solid rgba(var(--accent-rgb), 0.4); }

.help-empty {
  text-align: center;
  color: var(--text-faint);
  padding: 40px 20px;
  font-size: 0.95rem;
}

@media (prefers-reduced-motion: reduce) {
  .help-item > summary::after { transition: none; }
}

/* Questions and answers read left-aligned even inside centred sections —
   centred multi-line questions are noticeably harder to scan down. */
.help-item > summary,
.help-answer,
.help-group > h2,
.help-search { text-align: start; }
.help-group > h2 { text-align: start; }

/* Single ticket call-to-action on the support pages: one wide, centred
   card rather than a narrow column left over from a two-card grid. */
.cap-grid.ticket-cta {
  grid-template-columns: minmax(0, 760px);
  justify-content: center;
}

.cap-grid.ticket-cta .cap-card { padding: 38px 40px; }
.cap-grid.ticket-cta .cap-card > h3 { font-size: 1.25rem; }
.cap-grid.ticket-cta .cap-card > p { font-size: 0.98rem; max-width: 62ch; }

/* ---------- Dashboard: project switcher + rate table ---------- */

.project-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 16px 20px;
  margin-bottom: 26px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

/* Credit and billing belong to the account, not a project, so the
   project selector steps aside on the Billing tab. */
#portal-content:has(#tab-billing:not([hidden])) .project-bar { display: none; }

.project-bar label {
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.project-bar select {
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 0.92rem;
  min-width: 220px;
}

.project-bar select:focus { outline: none; border-color: var(--accent); }
.project-bar .project-spacer { margin-inline-start: auto; }

.rate-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.rate-table th {
  text-align: start;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  font-weight: 600;
  padding: 0 0 12px;
  border-bottom: 1px solid var(--surface-border);
}

.rate-table td {
  padding: 15px 0;
  border-bottom: 1px solid var(--surface-border);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.rate-table tr:last-child td { border-bottom: none; }
.rate-table td:last-child { text-align: end; }

.key-reveal {
  margin-top: 18px;
  padding: 18px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--accent-rgb), 0.4);
  background: var(--accent-soft);
}

.key-reveal p { margin: 0 0 10px; color: var(--text); font-size: 0.9rem; }

.key-reveal code {
  display: block;
  padding: 12px 14px;
  border-radius: 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--surface-border);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.88rem;
  color: var(--text);
  word-break: break-all;
}

/* Portal content is data, not marketing copy — it must not inherit the
   centred text of .alden-section / .quantmind-section, which breaks
   table alignment and makes long notes hard to read. */
#portal-content,
#portal-content h2,
#portal-content p,
#portal-content td,
#portal-content th,
.portal-panel,
.portal-gate p { text-align: start; }

.portal-gate { text-align: center; }
.portal-gate p { text-align: center; }

/* .link-arrow is normally an <a>; as a <button> it needs the UA button
   chrome stripped or it renders as a grey/white box on dark panels. */
button.link-arrow {
  background: none;
  border: none;
  border-bottom: 1px solid var(--surface-border);
  padding: 0 0 2px;
  font: inherit;
  font-size: 0.88rem;
  color: var(--text-dim);
  cursor: pointer;
}

button.link-arrow:hover { color: var(--accent); border-color: var(--accent); }

/* Invoice status (Paid / Unpaid) as a plain white value like the rest of
   the row, not a lit pill. */
.portal-table .portal-status.is-plain {
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font-size: inherit;
  font-weight: 400;
}
.portal-table .portal-status.is-plain::before { display: none; }

/* Plain row actions in the account tables (Confirm, Copy) match the white
   values and the white Invoice links beside them. */
.portal-table button.link-arrow { color: var(--text); }
/* Links and buttons in a row (Invoice, Pay, Revoke) at one size and weight. */
.portal-table .link-arrow { font-size: 0.88rem; font-weight: 400; }
/* Invoice is a link and Pay a button; both light up the same on hover. */
.portal-table a.link-arrow:hover { color: var(--accent); border-color: var(--accent); }

/* Destructive row actions in the account tables (Revoke a key, Sign out a
   device, Deny or Delete a trading account) take the same red as the other
   destructive actions (field errors, Close my account). */
button.link-arrow.is-danger { color: var(--danger-text); border-color: rgba(var(--danger-rgb), 0.4); }
button.link-arrow.is-danger:hover { color: var(--danger-text); border-color: var(--danger-text); }

/* Panel headers keep the action on the right rather than centred. */
.portal-panel-head { justify-content: space-between; }
.portal-panel-head .btn { flex: 0 0 auto; }


/* Moved out of quantmind.css: Alden's console needs these too, and it
   never loads the QuantMind stylesheet. */
/* ---------- Customer portal (dashboard, licenses) ---------- */

/* These pages are the signed-in application surface. Until a session
   exists they render the .portal-gate below instead of portal content,
   which is the same thing they'll do for any signed-out visitor once
   auth is live — not a placeholder state. */

.portal-gate {
  max-width: 460px;
  margin: 0 auto;
  padding: 40px 36px;
  text-align: center;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
}

.portal-gate h2 {
  font-size: 1.25rem;
  margin-bottom: 10px;
}

.portal-gate p {
  color: var(--text-dim);
  font-size: 0.94rem;
  margin-bottom: 24px;
}

.portal-gate .btn { margin: 0 6px; }

.portal-layout {
  display: grid;
  /* 200px so four tiles sit in one row (API Overview, Usage) instead of
     three and a lonely fourth. */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  margin-bottom: 32px;
}

.portal-stat {
  padding: 24px 26px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

.portal-stat .portal-stat-label {
  display: block;
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 10px;
}

.portal-stat .portal-stat-value {
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.portal-panel {
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 28px;
}

/* Keep a panel's title and its status pill on one line.

   flex-wrap: wrap breaks lines using each item's BASE size, before any
   shrinking — shrinking only applies within a line. So a title that does
   not fit wraps the pill onto a second row rather than giving way, and
   min-width:0 alone never gets a chance to help. "Paddle webhooks"
   needed 237px of a 225px head and dropped its pill; every other card on
   the dashboard fitted and did not.

   Scoped with :has to heads that actually carry a pill, so heads
   containing a button — which is flex: 0 0 auto and cannot shrink — keep
   their ability to wrap rather than overflowing. */
.portal-panel-head:has(> .portal-status),
.portal-panel-head:has(> .admin-count) {
  flex-wrap: nowrap;
}
.portal-panel-head > h2 { min-width: 0; }

.portal-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 20px 26px;
  border-bottom: 1px solid var(--surface-border);
}

.portal-panel-head h2 {
  font-size: 1.05rem;
  margin: 0;
}

.portal-table-wrap { overflow-x: auto; }

.portal-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  min-width: 560px;
}

.portal-table th {
  text-align: start;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  font-weight: 600;
  padding: 14px 22px;
  border-bottom: 1px solid var(--surface-border);
}

.portal-table td {
  padding: 16px 22px;
  border-bottom: 1px solid var(--surface-border);
  /* Values in white under the grey headers, the same in every account table
     on both products. */
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.portal-table tr:last-child td { border-bottom: none; }

.portal-empty {
  padding: 44px 26px;
  text-align: center;
  color: var(--text-faint);
  font-size: 0.92rem;
}

.portal-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 500;
  border: 1px solid var(--surface-border);
}

.portal-status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.portal-status.active {
  color: #4ade80;
  border-color: rgba(74, 222, 128, 0.35);
  background: rgba(74, 222, 128, 0.1);
}

.portal-status.inactive {
  color: var(--text-faint);
}

/* Red, for a state that has stopped rather than one that is merely idle.
   --danger-soft, not --danger: the deep red reads almost black on this
   ground, and a status dot has to be legible at 6px. */
.portal-status.stopped {
  color: var(--danger-soft);
  border-color: rgba(var(--danger-rgb), 0.4);
  background: rgba(var(--danger-rgb), 0.12);
}

/* Black, for a state that is off rather than failed: nothing is wrong, and
   nothing is running either. It still glows, so it reads as a deliberate
   state rather than as an unrendered dot. */
.portal-status.off {
  color: #12141c;
  border-color: rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.04);
}

.portal-status.off::before { background: #0a0b11; }

/* A black dot on a dark ground needs a light halo to be visible at all,
   so this one glows against itself rather than in its own colour. */
.portal-status.off::before { box-shadow: 0 0 6px rgba(255, 255, 255, 0.35); }

/* Text stays legible; only the dot is black. */
.portal-status.off { color: var(--text-dim); }

/* The dot glows. Small enough that a flat 6px circle reads as a rendering
   artefact rather than as a signal. currentColor, so each state carries
   its own. */
.portal-status.active::before,
.portal-status.pending::before,
.portal-status.stopped::before {
  box-shadow: 0 0 6px currentColor;
}

.portal-key {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.86rem;
  color: var(--text);
}

/* .btn is used on both <a> and <button>. A <button> keeps the browser's
   default `2px outset` bevel and native appearance unless they're reset,
   which showed up as a discoloured edge on the submit button next to an
   identically-classed link. */
button.btn,
input[type="submit"].btn {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  font: inherit;
  cursor: pointer;
}

button.btn:disabled { opacity: 0.55; cursor: default; }

/* ---------- Help centre: search + category cards ---------- */

/* One consistent width for the search field and the card grid beneath it,
   so the box doesn't float at a different size to the content it filters. */
.help-shell { max-width: 940px; margin: 0 auto; }

.help-shell .help-search {
  max-width: none;      /* fill the shell rather than a narrower column */
  margin: 0 0 40px;
}

.help-cats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}

@media (max-width: 900px) { .help-cats { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .help-cats { grid-template-columns: 1fr; } }

.help-cat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 26px 24px;
  min-height: 148px;
  border-radius: var(--radius);
  border: 1px solid var(--surface-border);
  background: var(--surface);
  text-align: start;
  font: inherit;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease);
}

.help-cat:hover {
  border-color: var(--accent);
  background: rgba(var(--accent-rgb), 0.06);
  transform: translateY(-2px);
}

.help-cat:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.help-cat h3 {
  margin: 0;
  font-size: 1.05rem;
  color: var(--text);
  line-height: 1.3;
}

.help-cat p {
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-dim);
  line-height: 1.5;
}

.help-cat .help-cat-count {
  margin-top: auto;
  font-size: 0.78rem;
  color: var(--text-faint);
  letter-spacing: 0.04em;
}

.help-back {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 22px;
  font-size: 0.9rem;
  color: var(--text-dim);
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  padding: 0;
}

.help-back:hover { color: var(--accent); }

.help-view-title {
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.35rem;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 20px;
  text-align: start;
}

@media (prefers-reduced-motion: reduce) {
  .help-cat:hover { transform: none; }
}

/* Breadcrumb inside the help centre, so it's always clear where you are
   and how to get back up a level. */
.help-crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 20px;
  font-size: 0.86rem;
  color: var(--text-faint);
  text-align: start;
}

.help-crumbs a,
.help-crumbs button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--text-dim);
  cursor: pointer;
}

.help-crumbs a:hover,
.help-crumbs button:hover { color: var(--accent); }

.help-crumbs .sep { color: var(--text-faint); opacity: 0.6; }
.help-crumbs .current { color: var(--text); }

/* ---------- Guide stepper ---------- */
/* Numbered walkthrough for setup guides. A connecting rail runs behind
   the badges so the sequence reads as one continuous path rather than
   five unrelated headings. */

.guide-steps {
  max-width: 780px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
  counter-reset: guide;
  text-align: start;
}

.guide-step {
  position: relative;
  padding: 0 0 40px 74px;
  counter-increment: guide;
}

/* The rail, drawn from each badge down to the next. */
.guide-step::before {
  content: "";
  position: absolute;
  left: 23px;
  top: 46px;
  bottom: 0;
  width: 2px;
  background: linear-gradient(var(--surface-border), transparent);
}

.guide-step:last-child { padding-bottom: 0; }
.guide-step:last-child::before { display: none; }

.guide-step > .guide-num {
  position: absolute;
  left: 0;
  top: 0;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--accent);
}

.guide-step h3 {
  font-size: 1.18rem;
  margin: 10px 0 10px;
  color: var(--text);
  line-height: 1.3;
}

.guide-step p {
  color: var(--text-dim);
  font-size: 0.96rem;
  line-height: 1.7;
  margin: 0 0 0.9em;
}

.guide-step p:last-child { margin-bottom: 0; }
.guide-step a { color: var(--accent); border-bottom: 1px solid rgba(var(--accent-rgb), 0.35); }

/* Inline callout for a recommendation or warning inside a step. */
.guide-tip {
  margin-top: 14px;
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  border-left: 2px solid var(--accent);
  background: rgba(var(--accent-rgb), 0.05);
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--text-dim);
}

.guide-tip strong { color: var(--text); }

/* Side-by-side comparison (e.g. Orb vs Workspace). */
.guide-compare {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  margin-top: 16px;
}

@media (max-width: 640px) { .guide-compare { grid-template-columns: 1fr; } }

.guide-compare > div {
  padding: 18px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--surface);
}

.guide-compare h4 {
  margin: 0 0 6px;
  font-size: 0.98rem;
  color: var(--text);
}

.guide-compare p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--text-faint);
}

@media (max-width: 620px) {
  .guide-step { padding-left: 60px; }
  .guide-step > .guide-num { width: 40px; height: 40px; font-size: 0.95rem; }
  .guide-step::before { left: 19px; top: 40px; }
}

.docs-subnav {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 44px;
}

.docs-subnav a {
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.85rem;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}

.docs-subnav a:hover,
.docs-subnav a.active {
  border-color: var(--accent);
  color: var(--accent);
}

.back-to-docs {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--text-faint);
  margin-bottom: 26px;
  transition: color 0.2s var(--ease);
}

.back-to-docs:hover { color: var(--accent); }

.faq-item {
  padding: 26px 0;
  border-bottom: 1px solid var(--surface-border);
  text-align: start;
}

.faq-item:last-child { border-bottom: none; }

.faq-item h3 { font-size: 1.05rem; margin-bottom: 10px; color: var(--text); }

.faq-item p { margin: 0; }

/* When the pill IS the page's <h1> (pages where a separate heading would
   just repeat the pill's word), it must not inherit heading type sizing —
   it should still read as the compact tag it is. */
h1.hero-eyebrow {
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  line-height: 1.2;
  /* Keeps the pill's own breathing room below it — a bare `margin: 0` here
     collapsed the gap between the tag and the text under it. */
  margin: 0 0 26px;
}

/* Links inside a page hero need to look clickable — the hero's own colour
   rules otherwise render them identically to the surrounding sentence. */
.page-hero p a {
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
  transition: border-color 0.2s var(--ease);
}

.page-hero p a:hover { border-bottom-color: var(--accent); }

/* The lone ticket call-to-action reads better centred than left-ragged,
   since there's no adjacent card to align against. */
.cap-grid.ticket-cta .cap-card,
.cap-grid.ticket-cta .cap-card > h3,
.cap-grid.ticket-cta .cap-card > p { text-align: center; }

.cap-grid.ticket-cta .cap-card > p { margin-left: auto; margin-right: auto; }

/* Available to screen readers, removed from the visual layout. Used for
   table headers whose column is obvious sighted (an action button) but
   which a screen reader still needs in order to announce the column. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* How a trading account is reached (MT5 / Python connector). Neutral on
   purpose: it states a fact about the account, so it must not read as a
   status next to the Active/Inactive pill in the adjacent column. */
.portal-conn {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.76rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* Links inside a risk-disclosure note point at the binding legal text, so
   they must not read as plain prose. Same treatment as hero links. */
/* Inline links only. Without the :not() this also caught buttons, and
   because it sets border-BOTTOM alone it repainted one edge of a four-sided
   border: an accent line underneath, --surface-border on the other three. */
.risk-note a:not(.btn) {
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
  transition: border-color 0.2s var(--ease);
}

.risk-note a:not(.btn):hover { border-bottom-color: var(--accent); }

/* A button in a risk note wears the accent on all four sides, so it reads
   as one border rather than an underline with three grey neighbours. */
.risk-note a.btn {
  color: var(--accent);
  border: 1px solid rgba(var(--accent-rgb), 0.45);
}

.risk-note a.btn:hover { border-color: var(--accent); }

/* ---------- Portal: account & subscription ---------- */

.portal-panel-body {
  padding: 24px 26px;
}

.portal-panel-body > p { margin: 0 0 16px; color: var(--text-dim); }
.portal-panel-body > p:last-child { margin-bottom: 0; }

.portal-value {
  font-size: 1.02rem;
  color: var(--text) !important;
}

.portal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 4px;
}

/* Opt-in, not the default: a panel's actions belong on the text edge so
   they line up with everything above them. A lone button closing a note
   card is the exception — with nothing to align to, centred reads as
   deliberate rather than stranded. */
.portal-actions.is-centered {
  justify-content: center;
  margin-top: 16px;
}

.portal-note {
  font-size: 0.86rem;
  color: var(--text-faint);
}

.portal-note a {
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
}

/* Scoped to the account panels. .portal-form is shared with the ticket
   form, which is a full-width page form — an unscoped max-width here
   squeezed it into a narrow left-aligned column. */
.portal-panel-body .portal-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  max-width: 440px;
  padding: 0;
}

.portal-panel-body .portal-form label {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.portal-panel-body .portal-form input {
  width: 100%;
  padding: 13px 16px;
  border-radius: 10px;
  border: 1px solid var(--surface-border);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

.portal-panel-body .portal-form input:focus { outline: none; border-color: var(--accent); }
.portal-panel-body .portal-form .portal-note { margin: -2px 0 4px; }

/* Success and failure must not look alike here — someone reading "changed"
   when nothing changed is how people lock themselves out. */
.form-status { font-size: 0.88rem; margin: 6px 0 0; }
.form-status.is-ok { color: var(--accent); }
.form-status.is-error { color: var(--danger-text); }

/* The stat card doubles as a way into the page that owns the detail. */
.portal-stat .link-arrow { margin-top: 10px; font-size: 0.84rem; }

.portal-panel-danger { border-color: rgba(var(--danger-rgb), 0.35); }
.portal-panel-danger .portal-panel-head h2 { color: var(--danger-text); }

/* ---------- Whole-card links ---------- */

/* A card that leads to exactly one place should be clickable across its
   whole surface, not just on the small arrow link at the bottom.
   Implemented as a stretched pseudo-element on the card's real <a>, so the
   link keeps its semantics: keyboard focus, middle-click, open in a new
   tab, and a visible URL on hover all still work, and none of it depends
   on JavaScript.

   Two kinds of card are deliberately excluded:
     :has(.btn)   — the card already offers a button to press, and that
                    button is the thing to click.
     :has(a + a)  — the card offers several destinations (Overview / Docs /
                    Download), so there is no single "the card leads here". */
.feature-card,
.flow-card,
.post-card,
.product-card {
  position: relative;
}

:is(.feature-card, .flow-card, .post-card, .product-card):not(:has(.btn)):not(:has(a + a)) {
  cursor: pointer;
}

/* Descendant, not child: some cards wrap their link in a .product-links
   row, so the anchor is a grandchild of the card. */
:is(.feature-card, .flow-card, .post-card, .product-card):not(:has(.btn)):not(:has(a + a)) a.link-arrow::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* Above .product-card's children, which are lifted to z-index 1. */
  z-index: 2;
}

/* .product-card lifts every direct child with `> * { position: relative }`
   so they clear the corner glow. That also made .product-links the
   overlay's containing block, so the stretch covered only the link row
   instead of the card. Opting the wrapper out hands the card back the job;
   the glow sits in the top-right corner well away from the links, so
   nothing visible moves. */
:is(.product-card):not(:has(.btn)):not(:has(a + a)) > .product-links {
  position: static;
}

/* The focus ring belongs on the card now that the card is the target. */
:is(.feature-card, .flow-card, .post-card, .product-card):has(> a:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Secondary line inside a stat card — the plan name is the headline, so
   the status sits under it rather than replacing it. */
.portal-stat-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Qualifier that sits with the hero's call to action rather than starting
   a new band below it. */
.hero-note {
  margin: 14px auto 0;
  max-width: 560px;
  font-size: 0.88rem;
  color: var(--text-faint);
}

/* ---------- Mobile nav: language above the menu button ---------- */

/* On a phone the language pill sat in the same row as the hamburger,
   squeezed between it and the brand — tight enough that the brand's
   back-label had to be suppressed to fit. Stacking the two controls in a
   right-hand column gives each one room without making the bar taller,
   since the two stacked controls are still shorter than the brand mark.
   .nav-links is absolutely positioned at top:100%, so it is unaffected by
   this and still drops below the whole bar. */
@media (max-width: 860px) {
  .nav {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    column-gap: 12px;
    row-gap: 6px;
    align-items: center;
  }

  .nav-brand-group {
    grid-column: 1;
    grid-row: 1 / span 2;
    min-width: 0;
  }

  .nav-lang {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    margin: 0;
  }

  .nav-toggle {
    grid-column: 2;
    grid-row: 2;
    justify-self: end;
  }

  /* Without an explicit cell the account button auto-places onto a third
     row, stretches to the full column and pushes the header to 180px with
     256px of horizontal overflow. Own column, both rows, still the
     right-hand edge. */
  .nav {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }

  .nav-account-menu,
  .nav-account {
    grid-column: 2;
    grid-row: 1 / span 2;
    justify-self: end;
    margin-inline-start: 12px;
  }
  .nav-account { gap: 3px; }

  /* Inside the wrapper the button is no longer a grid item. */
  .nav-account-menu .nav-account {
    grid-column: auto;
    grid-row: auto;
    justify-self: auto;
    margin-inline-start: 0;
  }

  /* It sits at the right edge of a narrow viewport, so the menu opens
     inboard rather than off-screen. */
  .nav-account-dropdown { inset-inline-end: 0; left: auto; }

  /* Portal pages carry one control more than marketing pages: the account
     menu, the hamburger and the language pill. Sharing a column made the
     hamburger sit on top of the account icon, so each gets its own. */
  .portal-nav,
  .nav.has-account { grid-template-columns: minmax(0, 1fr) auto auto auto; }

  .portal-nav .nav-account-menu,
  .nav.has-account .nav-account-menu {
    grid-column: 2;
    grid-row: 1 / span 2;
    /* The desktop wrapper is 176px so the button can sit centred over its
       menu. There is no room for that here, so it shrinks back to the
       button and the menu anchors to the right edge instead. */
    min-width: 0;
  }

  .nav-account-dropdown { left: auto; right: 0; min-width: 176px; }

  /* At the narrowest widths the label costs more than it earns: it pushed
     the PORTAL badge out of the brand column and under the icon. */
  .nav-account span { display: none; }

  /* The badge overflowed the brand column and ran under the account icon.
     It only has to read as a marker, not as a heading. */
  .portal-badge {
    margin-left: 8px;
    padding: 2px 6px;
    font-size: 0.6rem;
    letter-spacing: 0.08em;
  }

  .portal-nav .nav-toggle,
  .nav.has-account .nav-toggle {
    grid-column: 3;
    grid-row: 1 / span 2;
    align-self: center;
  }

  .portal-nav .nav-lang,
  .nav.has-account .nav-lang {
    position: static;
    grid-column: 4;
    grid-row: 1 / span 2;
    align-self: center;
    margin: 0;
  }

  .nav-account svg { width: 40px; height: 40px; padding: 9px; }
  .nav-account span { font-size: 0.82rem; }
}

/* Accounts awaiting the customer's confirmation — visually distinct from
   the connected list, since these are not yet permitted to trade. */
.portal-panel-pending { border-color: rgba(var(--accent-rgb), 0.35); }

.portal-panel-meta {
  font-size: 0.8rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.portal-row-actions {
  display: flex;
  gap: 16px;
  white-space: nowrap;
}

/* The first thing a new customer sees on this page, so it explains what to
   do rather than only reporting that a list is empty. */
.portal-empty-guide {
  text-align: start;
  padding: 28px 26px;
}

.portal-empty-guide p { margin: 0 0 12px; }
.portal-empty-guide .portal-actions { margin-top: 18px; }

/* "Everything in Pro" is not a feature, it's the baseline the rest of the
   list adds to. It leads the list, carries a + rather than a tick, and a
   rule separates it from the features that follow so the list reads as
   "that plan, plus these". */
.price-features .price-inherit {
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--surface-border);
  color: var(--text);
  font-weight: 500;
}

/* The + trails the label now, so it needs pushing to the end of the row
   rather than sitting in the icon slot the ticks use. */
.price-features .price-inherit svg {
  color: var(--accent);
  stroke-width: 3;
  margin-left: auto;
  order: 2;
}

.price-card.featured .price-features .price-inherit svg { color: var(--violet); }

/* Quiet until you look for it: sits under the password field, gains its
   underline on hover so it reads as a link without competing with the
   sign-in button beside it. */
.form-inline-link {
  display: inline-block;
  margin-top: 8px;
  font-size: 0.84rem;
  color: var(--text-faint);
  border-bottom: 1px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.form-inline-link:hover,
.form-inline-link:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ---------- Arabic: never letter-space a cursive script ---------- */

/* Arabic letters physically join, like handwriting. letter-spacing pulls
   those joins apart, so a word stops looking like a word — the effect is
   what "r e n d e r i n g   l i k e   t h i s" does to English, except a
   reader sees it as broken rather than styled. Negative tracking is just
   as wrong: it crushes the joins instead.

   Our uppercase-label and heading rules apply tracking site-wide, which
   is right for Latin and wrong here, so anything marked lang="ar" opts
   out. !important is deliberate: this is a correctness fix for the
   script, and it has to beat the more specific selectors those rules use
   (h1.hero-eyebrow, .section-head h2 and so on) rather than race them.

   Latin inside Arabic — brand names, "Paddle.com Market Ltd" — is marked
   lang="en" or .notranslate and keeps its own spacing where it matters. */
[lang="ar"],
[lang="ar"] * {
  letter-spacing: normal !important;
}

/* Text-transform is the same class of problem: Arabic has no letter case,
   so uppercase() is a no-op at best, but it signals the wrong intent and
   costs nothing to drop. */
[lang="ar"] * {
  text-transform: none !important;
}

/* ---------- Machine-readable strings must never be reordered ---------- */

/* In an RTL paragraph the bidirectional algorithm reorders neutral
   characters — punctuation, brackets, symbols — around the strong text
   they sit next to. "(QM-7F3A)" renders as ")QM-7F3A(" and "abc123!@#"
   as "#@!abc123". Someone reads a key off the screen, types what they
   saw, and it is wrong: a correctness bug, not a cosmetic one.

   Today's formats happen to be safe, because they start and end with
   letters or digits. That is luck rather than design — a base64 key
   ending in "=" would reorder, and key formats change. Isolating them
   costs nothing and removes the class of bug permanently.

   `isolate` stops the string affecting or being affected by the text
   around it; `direction: ltr` fixes its internal order regardless of the
   page. */
.portal-key,
.key-reveal code,
[data-field="license-key"],
[data-field*="key"],
[data-field="masked"],
.portal-table td code,
input[type="password"],
input[name="code"],
input[type="email"],
code {
  unicode-bidi: isolate;
  direction: ltr;
  text-align: start;
}

/* ---------- RTL: flip what points somewhere ---------- */

/* An arrow is a direction, not a decoration. "Read the post →" points at
   the next thing; in a right-to-left page the next thing is to the left,
   so an unflipped arrow points back at what you just read. Mirroring the
   glyph is the whole fix — the text beside it is already reordered by
   the bidi algorithm. */
[dir="rtl"] .link-arrow svg,
[dir="rtl"] .btn svg,
[dir="rtl"] .nav-back {
  transform: scaleX(-1);
}

/* The back-link's label would be mirrored along with its arrow, so it is
   flipped back. */
[dir="rtl"] .nav-back .nav-back-label {
  display: inline-block;
  transform: scaleX(-1);
}

/* The breadcrumb separator is a chevron in text form and points the same
   way regardless, so it is left alone; the bidi algorithm already places
   it correctly between reordered segments. */

/* ---------- Consent: scroll-wrap ---------- */

/* Conspicuousness is a legal factor, not a stylistic one. Courts weigh
   font size against surrounding text, contrast, proximity to the button
   that commits you, and whether other elements crowd it out. So this block
   is deliberately not quiet. */
.form-consent {
  margin: 6px 0 2px;
  padding: 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: rgba(255, 255, 255, 0.02);
}

.consent-intro {
  margin: 0 0 12px;
  font-size: 0.92rem;
  color: var(--text);
}

.consent-panel {
  margin: 14px 0 4px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg);
  overflow: hidden;
}

.consent-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--surface-border);
  font-size: 0.9rem;
}

.consent-close {
  background: none;
  border: 0;
  color: var(--text-faint);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}

.consent-close:hover { color: var(--text); }

/* A fixed height is the point: it forces the scroll that proves the text
   was passed through. Tall enough to read comfortably, short enough that
   reaching the end is a deliberate act. */
.consent-scroll {
  height: 420px;
  overflow-y: auto;
  padding: 16px 18px;
  font-size: 0.88rem;
  line-height: 1.65;
  color: var(--text-dim);
  scroll-behavior: smooth;
}

.consent-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.consent-scroll h2 {
  font-size: 0.95rem;
  margin: 22px 0 8px;
  color: var(--text);
}

.consent-scroll h3 { font-size: 0.88rem; margin: 16px 0 6px; color: var(--text); }
.consent-scroll p, .consent-scroll li { margin: 0 0 10px; }
.consent-scroll ul { padding-left: 20px; }
.consent-scroll .updated { display: block; margin-bottom: 14px; color: var(--text-faint); font-size: 0.8rem; }
.consent-loading { color: var(--text-faint); }
.consent-rule { border: 0; border-top: 1px solid var(--surface-border); margin: 26px 0; }

/* The signup panel holds two full legal documents, so it gets more room
   than the login/reset panels it shares a class with — a 460px column
   turns the terms into a slot of about forty characters, which is the
   width people stop reading at. Scoped to the pages that carry consent
   so the plain forms keep their compact shape. */

/* The tick lives at the bottom of the scrolling text, so it can only be
   reached by passing everything above it. Separated from the last
   paragraph by a rule and a slab of space so it reads as the end of the
   documents rather than another clause of them. */
.consent-end {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--surface-border);
}

.consent-end .consent-row { margin-top: 0; }

/* Required disclosure, kept on the page but sized as a footnote so it
   reads as a note under the pitch rather than a second competing claim. */
.voice-note {
  margin-top: 18px;
  font-size: 0.85rem;
  color: var(--text-faint);
}

/* Confirmation shown in place of the open button once the box is ticked,
   so the statement appears exactly once on the page at any time. */
.consent-done {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin: 14px 0 4px;
  font-size: 0.9rem;
  color: var(--text-dim);
}

.consent-done-tick { color: var(--accent); font-weight: 600; }

.consent-review {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
  cursor: pointer;
}

.consent-review:hover { border-bottom-color: var(--accent); }

/* A link inside a sentence is a link, not a call to action. These read
   as pill buttons otherwise, which makes the sentence look like a form
   control. */
.form-footnote a {
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
}

.form-footnote a:hover { border-bottom-color: var(--accent); }

.consent-foot {
  padding: 14px;
  border-top: 1px solid var(--surface-border);
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}

.consent-hint { margin: 0; font-size: 0.84rem; color: var(--text-faint); }

/* The label states what agreeing means. A button reading "Continue" is one
   of the documented reasons these agreements fail. */
.consent-foot .btn { text-align: start; line-height: 1.4; }
.consent-foot .btn:disabled { opacity: 0.45; cursor: not-allowed; }

.consent-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 14px;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text-dim);
  text-transform: none;
  letter-spacing: normal;
}

.consent-row input[type="checkbox"] {
  appearance: none;
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  border-radius: 5px;
  /* --surface-border on --bg is a 9% edge against the page's own colour,
     which on this box read as nothing at all. A control the customer has
     to find and click needs to be visible before they interact with it. */
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: var(--bg-elevated);
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.consent-row input[type="checkbox"]:hover { border-color: rgba(255, 255, 255, 0.5); }

.consent-row input[type="checkbox"]:checked { border-color: var(--accent); background: var(--accent); }

.consent-row input[type="checkbox"]:checked::after {
  content: "";
  display: block;
  width: 5px;
  height: 9px;
  margin: 1px auto 0;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Disabled until the terms have been read, so it reads as a result rather
   than a control to click past. */
.consent-row input[type="checkbox"]:disabled { opacity: 0.4; }
.consent-row a { color: var(--accent); border-bottom: 1px solid rgba(var(--accent-rgb), 0.45); }
.consent-row.is-missing input[type="checkbox"] { border-color: var(--danger); }

/* A drafting note that belongs to us, not to the reader's obligations —
   set apart so it isn't mistaken for a term of the agreement. */
.legal .clause-note {
  margin-top: 14px;
  padding: 10px 14px;
  border-inline-start: 2px solid var(--surface-border);
  color: var(--text-faint);
  font-size: 0.88rem;
}

/* ---------- Account page ---------- */

/* A smaller button for settings panels. The full-size .btn is a landing
   page control; inside a settings card it reads as the loudest thing on
   the page, which is wrong when the loud thing should be your data. */
/* .btn.btn-sm, not .btn-sm: a <button> picks up a font-size from an
   earlier, more specific rule than a bare class, so the same markup came
   out 16px on a button and 13.76px on a link — a 2px height difference
   between two controls sitting side by side. */
/* .btn.btn-sm, not .btn-sm: a <button> picks up a font-size from an
   earlier, more specific rule than a bare class, so the same markup came
   out 16px on a button and 13.76px on a link.

   min-height rather than relying on padding + line-height: <a> and
   <button> resolve their content boxes differently even with identical
   computed type, which left a 2px step between two controls sitting side
   by side. */
.btn.btn-sm {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 18px;
  font-size: 0.86rem;
  line-height: 1.2;
}

/* Identity block. Answers "am I in the right account" before any setting
   is on screen. */
.account-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 24px 26px;
  margin-bottom: 22px;
  border: 1px solid var(--surface-border);
  border-radius: var(--radius);
  background: var(--surface);
}

.account-identity {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
}

.account-avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  border: 1px solid var(--surface-border);
  background: var(--bg);
  color: var(--text-dim);
}

.account-avatar svg { width: 30px; height: 30px; }

.account-identity-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

/* An email address is a Latin run inside an otherwise Arabic page; left
   to the bidi algorithm its punctuation reorders and it reads wrong. */
.account-identity-text .portal-value {
  overflow-wrap: anywhere;
  unicode-bidi: isolate;
  direction: ltr;
  text-align: start;
}

.portal-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin: 0;
}

.portal-facts div { display: flex; flex-direction: column; gap: 4px; }

.portal-facts dt {
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.portal-facts dd { margin: 0; font-size: 0.96rem; color: var(--text); }

.portal-facts { margin-bottom: 18px; }

/* A settings row: the current value, and one quiet control to change it.
   The form underneath stays out of the way until asked for — a card with
   empty inputs sitting open reads as unfinished work. */
.portal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

.portal-row-main { display: flex; flex-direction: column; gap: 5px; min-width: 0; }

.portal-row-label {
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.portal-value-masked {
  letter-spacing: 0.18em;
  color: var(--text-dim) !important;
}

/* A value straight under a card's white header, with no label of its own:
   the header names it, so the value sits a step down in grey, the same as
   the hidden password. Site rule (Fahad, 2026-11-05): header + value -> grey
   value; header + label + value -> grey label, white value (tables). */
.portal-value-quiet:not(.is-empty) { color: var(--text-dim) !important; }
/* An empty balance is a stop: red, whatever else the value is styled as
   (.portal-value's !important had been hiding this). */
.portal-value.is-empty { color: var(--danger-text) !important; }

/* Not a .btn: this opens a form, it doesn't submit one. Giving it a
   button's weight put two primary-looking controls in one card. */
.portal-disclose {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 0.9rem;
  color: var(--accent);
  border-bottom: 1px solid rgba(var(--accent-rgb), 0.45);
  cursor: pointer;
  flex: none;
}

.portal-disclose:hover { border-bottom-color: var(--accent); }
.portal-disclose[aria-expanded="true"] { color: var(--text-dim); border-bottom-color: var(--surface-border); }

/* The disclosed form sits inside a panel body that already has padding. */
.portal-panel-body > .portal-form {
  padding: 22px 0 0;
  margin-top: 20px;
  border-top: 1px solid var(--surface-border);
}

.portal-form-actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* Reveal control, on the new password only. */
.portal-secret { position: relative; }

/* .portal-form sets align-items: flex-start, so a wrapper with no width
   of its own shrinks to fit while the plain input beside it fills the
   row. That made "New password" render narrower than "Current password"
   in the same card. The wrapper needs the width, not the input. */
.portal-secret { display: block; width: 100%; }
.portal-secret input { width: 100%; padding-inline-end: 46px !important; }

.portal-secret-toggle {
  position: absolute;
  inset-inline-end: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  appearance: none;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-faint);
  cursor: pointer;
}

.portal-secret-toggle:hover { color: var(--text); }
.portal-secret-toggle svg { width: 19px; height: 19px; }
.portal-secret-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Strength shown as a filling bar rather than a verdict alone: the words
   "weak" and "strong" mean nothing without a sense of the distance
   between them. */
/* The negative margin only makes sense inside .portal-form, which is a
   flex column with an 18px gap to absorb. In a plain .form-field there is
   no gap, so it pulled the meter up onto the input and the label collided
   with the checklist below it. */
.pw-strength { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.portal-form .pw-strength { margin-top: -6px; }
.portal-form .pw-rules { margin-top: -4px; }

.pw-strength-track {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--surface-border);
  overflow: hidden;
}

.pw-strength-track span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--text-faint);
  transition: width 0.25s var(--ease), background 0.25s var(--ease);
}

.pw-strength-label {
  font-size: 0.8rem;
  color: var(--text-faint);
  min-width: 72px;
  text-align: end;
}

.pw-strength[data-level="1"] .pw-strength-track span { width: 25%;  background: var(--danger); }
.pw-strength[data-level="2"] .pw-strength-track span { width: 50%;  background: #ffc46b; }
.pw-strength[data-level="3"] .pw-strength-track span { width: 78%;  background: #7ddba0; }
.pw-strength[data-level="4"] .pw-strength-track span { width: 100%; background: var(--accent); }

@media (max-width: 700px) {
  .account-summary { flex-direction: column; align-items: flex-start; }
  .portal-row { align-items: flex-start; }
}

/* Signed-in devices. A security table is scanned for the one row that
   looks wrong, so device, address and recency each need to be legible at
   a glance rather than uniform grey. */
/* These normally sit inside .portal-panel-body, which supplies the 26px
   inset. On the newer pages they are direct children of the panel, so they
   had none and rendered flush against the card border, 1px from it, while
   the heading beside them sat at 27px. Child combinator, so it cannot
   double up on the pages that do use a body wrapper. */
.portal-panel > .portal-row { padding: 20px 26px; }
.portal-panel > .portal-panel-intro { padding: 20px 26px 0; }
.portal-panel > .portal-form-actions { padding: 0 26px 22px; }
.portal-panel > [data-list] { padding: 0; }

.portal-panel-intro { padding-bottom: 0; }
.portal-panel-intro p { margin: 0; color: var(--text-dim); font-size: 0.9rem; }

.portal-table-sessions td { vertical-align: middle; }

.session-device { display: flex; align-items: center; gap: 12px; }

.session-icon {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--text-faint);
}

.session-current .session-icon { color: var(--accent); }

.session-device-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.session-device-text > span:first-child { color: var(--text); }

.session-meta { font-size: 0.78rem; color: var(--text-faint); }
.session-here { color: var(--accent); }

/* Monospace and tabular so addresses line up column-wise and a changed
   octet is visible without reading the whole string. */
.session-ip {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.84rem;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  unicode-bidi: isolate;
}

.session-current-note { font-size: 0.84rem; color: var(--text-faint); }

.session-current td { background: rgba(var(--accent-rgb), 0.04); }

@media (max-width: 700px) {
  .session-device { gap: 9px; }
  .session-icon { width: 18px; height: 18px; }
}

/* The plan you're already on. Marked rather than re-sold: a card still
   saying "Subscribe" under a plan you pay for reads as a page that
   doesn't know who you are. */
.price-card.is-current-plan {
  border-color: var(--accent);
}

/* The current plan's control. Sits where the buy button was, so the card
   has one marker rather than a badge at the top and a button at the
   bottom saying the same thing. Still a link — it goes to the page where
   the subscription is managed. */
.btn.is-current-plan-cta {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.btn.is-current-plan-cta:hover {
  border-color: var(--accent);
  color: var(--text);
}

/* Licence key: a credential, shown like one. */
.portal-key-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.portal-key-row .portal-key { margin: 0; }

.portal-key-row .portal-secret-toggle {
  position: static;
  transform: none;
  inset-inline-end: auto;
  width: 30px;
  height: 30px;
}

.portal-reveal-form {
  margin: 16px 0 4px;
  padding: 16px 18px;
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-sm);
  background: var(--bg);
}

.portal-reveal-form label {
  display: block;
  margin-bottom: 9px;
  font-size: 0.86rem;
  color: var(--text-dim);
}

.portal-reveal-controls { display: flex; gap: 10px; flex-wrap: wrap; }

.portal-reveal-controls input {
  flex: 1;
  min-width: 200px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: 0.94rem;
}

.portal-reveal-controls input:focus { outline: none; border-color: var(--accent); }

.portal-reveal-form .form-status { margin: 12px 0 0; }


/* Portal tables scroll inside their panel instead of widening the page.
   .portal-table-wrap already sets overflow-x, but a flex/grid child's
   default min-width is its content, so the panel grew to the table's
   width and took the document with it — every portal page scrolled
   sideways on a phone. */
.portal-panel { min-width: 0; }
.portal-table-wrap { max-width: 100%; min-width: 0; }
.portal-table { min-width: 640px; }

/* Two actions on an account row need separating, or they read as one
   long link. Left-aligned and set smaller than the row text: they were
   right-aligned at full size, which made this the widest column in the
   table and pushed it past its container. */
.portal-row-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-start;
  align-items: flex-end;
}

.portal-table td.portal-row-actions,
.portal-table .portal-row-actions { vertical-align: bottom; }

.portal-row-actions .link-arrow {
  font-size: 0.8rem;
  white-space: nowrap;
}

/* Small reference tables inside a note card. Not .portal-table: that one
   is for live API data with its own row states and hover. These are
   static and read like prose, so they stay quiet — no zebra striping, no
   borders beyond the header rule. */
.note-table-wrap {
  overflow-x: auto;
  margin: 14px 0 16px;
}

.note-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}

.note-table th {
  text-align: start;
  padding: 0 18px 8px 0;
  font-size: 0.72rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-faint);
  border-bottom: 1px solid var(--surface-border);
  white-space: nowrap;
}

.note-table td {
  padding: 9px 18px 9px 0;
  color: var(--text);
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  vertical-align: top;
}

.note-table tr:last-child td { border-bottom: 0; }
.note-table td:first-child { color: var(--text); }
.note-table td strong { color: var(--text); }

/* Arabic is RTL, so the trailing padding has to follow the text
   direction rather than sitting on the right in both. */
[lang="ar"] .note-table th,
[lang="ar"] .note-table td { padding-inline: 0 18px; }

/* Dates never wrap. "12 Aug 2026" breaking after "12" makes one row taller
   than its neighbours and the column stops scanning as a column. These
   tables already scroll horizontally inside .portal-table-wrap, so
   holding a date on one line costs nothing. */
.portal-table td.is-date,
.portal-table th.is-date,
.note-table td.is-date {
  white-space: nowrap;
}

/* ---------- Confirmation dialogs ---------- */

.ui-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(4, 5, 10, 0.72);
  backdrop-filter: blur(6px);
}

body.ui-modal-open { overflow: hidden; }

/* The same card as the notes under the tables: dark surface, violet rail,
   Space Grotesk title. A dialog that gates a destructive action should
   look like it belongs to the page that raised it. */
.ui-modal {
  width: min(520px, 100%);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  padding: 24px 28px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  /* Themed, not fixed violet: on Alden this reads blue, on QuantMind
     purple. A dialog should look like it belongs to the product that
     raised it. */
  border-left: 3px solid var(--accent);
  /* Frosted, matching the install gate: the panel's own tint stays
     translucent and the page behind it is blurred rather than hidden. The
     backdrop already dims the page, so tint alone would have read solid. */
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015)),
    rgba(5, 6, 10, 0.38);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  text-align: start;
}

/* Destructive actions get the warning rail rather than the brand one. */
.ui-modal.is-danger { border-left-color: var(--danger); }

.ui-modal-title {
  display: block;
  margin-bottom: 12px;
  color: var(--text);
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.05rem;
  line-height: 1.35;
}

.ui-modal-body p {
  margin: 0 0 12px;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text-dim);
}

.ui-modal-body p:last-child { margin-bottom: 0; }

.ui-modal-actions {
  display: flex;
  /* Centred, matching the install gate. This dialog was the one popup
     pushing its buttons to one edge. */
  justify-content: center;
  gap: 10px;
  margin-top: 22px;
  flex-wrap: wrap;
}

/* Confirming a destructive action shouldn't wear the brand gradient. */
.ui-modal.is-danger [data-modal-confirm] {
  background: rgba(var(--danger-rgb), 0.18);
  border: 1px solid rgba(var(--danger-rgb), 0.6);
  /* White, not --danger-soft. The pale salmon sat on a translucent red fill
     over a frosted panel and read as a disabled button rather than the one
     that carries out the action; it also only became legible on hover. The
     red border and fill still say what the button does. */
  color: #fff;
}

.ui-modal.is-danger [data-modal-confirm]:hover {
  background: rgba(var(--danger-rgb), 0.28);
  border-color: var(--danger);
  color: #fff;
  /* The button keeps .btn-primary, so without this it hovered with the
     brand accent's glow — cyan or violet light under a red button. The
     light matches the button, the way it does on every other danger
     control. */
  box-shadow: 0 10px 30px -8px rgba(var(--danger-rgb), 0.55);
}

@media (max-width: 520px) {
  .ui-modal { padding: 20px; }
  .ui-modal-actions { justify-content: stretch; }
  .ui-modal-actions .btn { flex: 1; }
}

/* Password requirements. A checklist that ticks as you type, rather than
   a sentence you have to hold in your head and check yourself against. */
.pw-rules {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
}

.pw-rules li {
  position: relative;
  padding-inline-start: 22px;
  font-size: 0.84rem;
  color: var(--text-faint);
  transition: color 0.2s var(--ease);
}

/* Empty circle until met, tick after. Drawn rather than an icon font so
   it can't arrive late or fail to load. */
.pw-rules li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid var(--surface-border);
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.pw-rules li.is-met { color: var(--text-dim); }

.pw-rules li.is-met::before {
  border-color: #7ddba0;
  background: #7ddba0;
}

.pw-rules li.is-met::after {
  content: "";
  position: absolute;
  inset-inline-start: 4px;
  top: 6px;
  width: 3px;
  height: 6px;
  border: solid var(--bg);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

/* An opt-in inside a form, distinct from the consent row's larger box. */
.portal-check {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.88rem;
  color: var(--text-dim);
  cursor: pointer;
}

.portal-check input[type="checkbox"] {
  appearance: none;
  flex: none;
  width: 16px;
  height: 16px;
  border-radius: 5px;
  border: 1px solid var(--surface-border);
  background: var(--bg);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.portal-check input[type="checkbox"]:checked {
  border-color: var(--accent);
  background: var(--accent);
}

.portal-check input[type="checkbox"]:checked::after {
  content: "";
  display: block;
  width: 4px;
  height: 8px;
  margin: 1px auto 0;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* The opt-in is a checkbox with a sentence beside it, not a field with a
   caption above it — so it has to opt out of the form's field styling.
   Unscoped, `.portal-panel-body .portal-form label` made the sentence
   uppercase and letter-spaced, and `.portal-form input` stretched the box
   to full width with field padding. */
.portal-panel-body .portal-form label.portal-check,
.portal-form label.portal-check {
  display: flex;
  margin-bottom: 0;
  font-size: 0.88rem;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-dim);
}

.portal-panel-body .portal-form .portal-check input[type="checkbox"],
.portal-form .portal-check input[type="checkbox"] {
  width: 16px;
  height: 16px;
  min-width: 0;
  padding: 0;
  border-radius: 5px;
}


/* A control that belongs to one product wears that product's colour, even
   on a company page. Both appear on the homepage and on /products.html,
   where the point is to market them side by side — so an Alden button
   next to a QuantMind button should read as two different products, not
   as one site. */
.product-card.alden,
[data-product="alden"] {
  --grad: var(--grad-alden);
  --btn-ink: #ffffff;
  --accent: #3b78ff;
  --accent-rgb: 59, 120, 255;
}

.product-card.quantmind,
[data-product="quantmind"] {
  --grad: var(--grad-quantmind);
  --btn-ink: #ffffff;
  --accent: #a76bff;
  --accent-rgb: 167, 107, 255;
}

/* A series broken out of a sentence onto its own lines. Once the items
   are stacked they read as a list, so they are punctuated as one: a
   marker each, no trailing commas, no closing "and". Running punctuation
   from the original sentence across separate lines looked like an error.

   Marker is drawn rather than a list-style bullet so it can take the
   product's accent and sit on the text's baseline. */
.prose-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
}

.prose-list li {
  position: relative;
  margin: 0 0 6px;
  padding-inline-start: 20px;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text-dim);
}

.prose-list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 4px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.75;
}

.prose-list li:last-child { margin-bottom: 0; }

/* A paragraph following the series needs the same break a paragraph after
   a paragraph gets — `.risk-note p + p` doesn't match across the <ul>. */
.prose-list + p { margin-top: 12px !important; }

/* A grid holding one card. The platform list is built for several, and
   with only macOS shipping the lone card sat hard left with a column of
   empty space beside it, which reads as a card that failed to load.
   Remove the modifier when a second platform ships. */
.feature-grid.is-single {
  display: flex;
  justify-content: center;
}

.feature-grid.is-single > .feature-card {
  max-width: 420px;
  width: 100%;
}

/* The text field inside a prompt dialog. */
.ui-modal-input {
  width: 100%;
  margin-top: 14px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 0.94rem;
}

.ui-modal-input:focus { outline: none; border-color: var(--accent); }
.ui-modal-choice {
  display: grid;
  gap: 8px;
  margin-top: 14px;
  font-size: 0.86rem;
  color: var(--text-dim);
}
.ui-modal-choice + .ui-modal-input { margin-top: 14px; }
/* Number fields: the browser's up/down arrows were drawn white on the dark
   page. color-scheme: dark draws them dark, in Chrome and Safari. */
input[type="number"] { color-scheme: dark; }
.ui-modal-input::placeholder { color: var(--text-faint); }

/* A validation message under the field it describes. */
.field-error {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 7px 0 0;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--danger-text);
  text-transform: none;
  letter-spacing: normal;
}

.field-error svg { flex: none; width: 14px; height: 14px; }

.form-field input.is-invalid,
.portal-form input.is-invalid {
  border-color: var(--danger) !important;
}

.form-field input.is-invalid:focus,
.portal-form input.is-invalid:focus {
  border-color: var(--danger) !important;
}

/* ---------- Two-factor setup ---------- */

.twofa-setup {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--surface-border);
}

.twofa-steps {
  margin: 0;
  padding-inline-start: 22px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.twofa-steps li { color: var(--text-dim); }
.twofa-steps > li > strong { display: block; color: var(--text); margin-bottom: 6px; }
.twofa-steps .portal-note { margin: 4px 0 0; }

/* The QR is an SVG the server rendered — the browser has no encoder and
   the CSP forbids fetching one. White plate because scanners need light
   modules on a light ground; a dark-on-dark code will not read. */
.twofa-qr {
  display: inline-block;
  margin: 12px 0 4px;
  padding: 12px;
  border-radius: var(--radius-sm);
  background: #fff;
  line-height: 0;
}

.twofa-qr svg, .twofa-qr img { display: block; width: 168px; height: 168px; }

.twofa-secret {
  margin: 8px 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  color: var(--text);
  unicode-bidi: isolate;
}

.twofa-confirm { margin-top: 12px; }
.twofa-confirm .form-field { max-width: 200px; margin-bottom: 12px; }

.twofa-confirm input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.15rem;
  letter-spacing: 0.28em;
  text-align: center;
}

/* Recovery codes. Warning-toned deliberately: this is the one screen in
   the flow that cannot be shown again. */
.twofa-recovery {
  margin-top: 20px;
  padding: 20px 22px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--danger-rgb), 0.35);
  background: rgba(var(--danger-rgb), 0.06);
}

.twofa-recovery > strong { display: block; color: var(--text); margin-bottom: 6px; }
.twofa-recovery p { margin: 0 0 14px; font-size: 0.9rem; color: var(--text-dim); }

.twofa-codes {
  list-style: none;
  margin: 0 0 16px;
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px 20px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9rem;
  color: var(--text);
  unicode-bidi: isolate;
}

.twofa-ack { margin: 16px 0 14px; }

@media (max-width: 520px) {
  .twofa-qr svg, .twofa-qr img { width: 140px; height: 140px; }
}

/* A failed sign-in. One line inside the form, not a card below it: the
   card read as a panel of its own and pushed the button off screen.

   Deliberately says "email or password" without saying which. Naming the
   wrong half turns the form into a way of testing whether an address has
   an account here. */
.form-alert {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 16px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--danger-rgb), 0.4);
  background: rgba(var(--danger-rgb), 0.08);
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--danger-text);
  text-align: start;
}

.form-alert svg { flex: none; width: 15px; height: 15px; margin-top: 2px; }
.form-alert a { color: inherit; border-bottom: 1px solid currentColor; }
.form-alert a:hover { color: #fff; }

/* The lede inside a capability card — used where the card carries one
   sentence and a button rather than a paragraph of explanation. */
/* This sentence stands where the card's heading used to, so it takes the
   heading's size and colour. Needs to outrank
   `.cap-grid.ticket-cta .cap-card > p`, which sets 0.98rem and the dimmer
   prose colour — that rule was written for a paragraph sitting *under* a
   heading, which is no longer the shape of this card. */
.cap-card .cap-lede,
.cap-grid.ticket-cta .cap-card > .cap-lede {
  font-size: 1.25rem;
  line-height: 1.45;
  margin: 0;
  color: var(--text);
}

/* Panels on the same measure as .form-panel (760px). Sign-up, sign-in and
   the ticket cards are the same kind of surface, so they are the same
   width; a narrower ticket card beside a wider sign-up card read as two
   different sites. */
.panel-measure > .container > .portal-panel,
.panel-measure > .container > div > .portal-panel {
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

/* ---------- API credit ---------- */

/* The balance is the number the page exists for, so it carries the weight
   the rest of the panel doesn't. */
.portal-value-lg {
  font-size: 2rem;
  font-family: "Space Grotesk", sans-serif;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.portal-value-lg.is-empty { color: var(--danger-text); }

.portal-note.is-warning { color: var(--danger-text); }

/* A currency field reads as money before it reads as a number, so the sign
   sits inside the box rather than in the label. */
.amount-input { position: relative; display: block; max-width: 220px; }
/* The spend alert sits under the limit field; same width. */
#spend-alert { max-width: 220px; }
.amount-input .amount-prefix {
  position: absolute;
  inset-inline-start: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  font-size: 0.94rem;
  pointer-events: none;
}
.amount-input input {
  width: 100%;
  padding-inline-start: 30px !important;
  font-variant-numeric: tabular-nums;
}

.auto-recharge-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 26px;
}
.auto-recharge-fields > div { flex: 1 1 200px; min-width: 0; }

/* Validation inside a dialog, in the same red as the field errors on the
   forms, so a rejected amount reads the same wherever it happens. */
.ui-modal-error {
  margin: 10px 0 0;
  font-size: 0.86rem;
  color: var(--danger-text);
}

/* ---------- Saved payment methods ---------- */

.method-row {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 18px 26px;
  border-top: 1px solid var(--surface-border);
}
.method-row:first-child { border-top: 0; }
.method-main { flex: 1; min-width: 0; }
.method-main .portal-note { margin: 6px 0 0; }
.method-actions { display: flex; gap: 8px; flex: none; }

/* The panel is ours, the fields inside it belong to the payment provider.
   It is visually a container and nothing else, so that whatever the
   provider renders does not end up fighting our own form styling. */
.add-method-panel {
  margin: 0 26px 26px;
  padding: 22px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--surface);
}
.add-method-panel h3 {
  margin: 0 0 14px;
  font-size: 1rem;
}
.provider-mount { min-height: 48px; }

.btn.btn-danger {
  color: var(--danger-text);
  border-color: rgba(var(--danger-rgb), 0.4);
}
.btn.btn-danger:hover {
  /* Lit, not filled. --danger-soft is a pale pink and using it as a
     background turned the button pink under the pointer, which read as a
     different button rather than the same one hovered. Destructive buttons
     now take the same light every other button takes, in their own red, so
     nothing loses its warning and nothing behaves differently. */
  border-color: var(--danger);
  box-shadow: 0 10px 30px -8px rgba(var(--danger-rgb), 0.55);
}

@media (max-width: 600px) {
  .method-row { flex-direction: column; align-items: flex-start; }
  .method-actions { width: 100%; }
}

/* The credit strip on the API page: a reminder, not a dashboard. It sits
   above the tabs so the balance is visible whichever one you are on. */
.credit-strip {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 18px;
  padding: 14px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border);
  background: var(--bg-elevated);
}
/* Laid out like the Usage tiles (label above value, same sizes), so the
   one number that leads the page reads like every other number on it. */
.credit-strip-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.credit-strip-label {
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.credit-strip-value {
  font-size: 1.5rem;
  font-weight: 600;
  font-family: "Space Grotesk", sans-serif;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--text);
}
.credit-strip-breakdown { font-size: 0.86rem; color: var(--text-faint); }

/* Low is a warning; empty is a stop, and the API is refusing requests. */
.credit-strip.is-low { border-color: rgba(255, 196, 107, 0.4); }
.credit-strip.is-low .credit-strip-value { color: #ffc46b; }
.credit-strip.is-empty { border-color: rgba(var(--danger-rgb), 0.45); }
.credit-strip.is-empty .credit-strip-value { color: var(--danger-text); }

@media (max-width: 600px) {
  .credit-strip { flex-direction: column; align-items: flex-start; }
}

/* ---------- Code expiry ---------- */

.otp-timer {
  margin: 8px 0 0;
  font-size: 0.86rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.otp-timer.is-expired { color: var(--danger-text); }

/* Once the code has lapsed, resending is the only way forward, so the
   control stops being a quiet link. */
.link-arrow.is-prominent {
  color: var(--accent);
  font-weight: 500;
}

.reset-sent-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}

/* Sign-in: "or" between the password and the passkey button. */
.form-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 14px;
  font-size: 0.82rem;
  color: var(--text-dim);
}
.form-divider::before,
.form-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--surface-border);
}

/* Where the CAPTCHA widget sits from the 5th wrong password. */
.captcha-box {
  padding: 18px;
  border: 1px dashed var(--surface-border);
  border-radius: 12px;
  font-size: 0.88rem;
  color: var(--text-dim);
  text-align: center;
}

/* Resend, as a sentence rather than a competing button. */
.resend-row {
  margin: 14px 0 0;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--text-dim);
}
.link-inline {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.link-inline:hover { color: var(--text); }
.link-inline:disabled {
  color: var(--text-faint);
  text-decoration: none;
  cursor: default;
}
.link-inline:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The auto-confirm row in the portal's pending panel: switch, then label,
   on one line, sitting above whatever the panel is currently saying. */
.portal-auto-confirm {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
  cursor: pointer;
}

.portal-auto-confirm span { font-size: 0.92rem; }

/* The disabled state's dot was --text-faint with no glow, so beside a green
   glowing "Enabled" it read as an unrendered element rather than as "off".
   It keeps the same treatment as every other state: a lit dot, in its own
   colour, so the difference is a colour change and not a presence change. */
.portal-status.inactive::before {
  box-shadow: 0 0 6px currentColor;
}

/* The switch sits in a table row beside the pill, so it must not stretch or
   pick up the row-action spacing meant for text buttons. */
.portal-row-actions .ui-switch {
  flex: 0 0 auto;
}

/* A switch that reports a state rather than a preference. The base .ui-switch
   knob is --accent, which is the brand colour and says nothing about whether
   the thing is on: on the accounts table it sat violet beside a green
   "Enabled" pill, so the row's two indicators disagreed. Here the knob
   carries the same green and red the pill does, so switch and pill say one
   thing. The pricing toggle keeps the accent, because monthly versus annual
   is a choice, not a state, and green/red would invent a right answer. */
/* Lit, the way the pill's dot is. .portal-status::before carries
   `box-shadow: 0 0 6px currentColor` — that glow is what makes a small
   coloured circle read as a live indicator rather than a painted dot, and
   the knob had the colour without it. Same 6px, same colour as the fill. */
.ui-switch-state[aria-pressed="true"]::after {
  background: #4ade80;
  box-shadow: 0 0 6px #4ade80;
}

.ui-switch-state[aria-pressed="false"]::after {
  background: var(--danger-text);
  box-shadow: 0 0 6px var(--danger-text);
}

/* A panel that sits outside .portal-form but should line up with the panels
   inside it. .portal-form carries 26px of padding and no visible box of its
   own, so every panel within it is inset by that much while a panel above it
   runs the full container width. The two then read as different sizes for no
   reason a visitor can see. */
.portal-panel.is-form-width {
  margin-left: 26px;
  margin-right: 26px;
}

@media (max-width: 600px) {
  .portal-panel.is-form-width { margin-left: 0; margin-right: 0; }
}

/* Buying credit without leaving the page. The dialog is a wrapper; the panel
   inside is the portal's own .portal-panel, so it is the surface the rest of
   the page is made of rather than a second visual language. Frosted like the
   install gate, for the same reason. */
.buy-credit {
  border: 0;
  background: transparent;
  color: var(--text);
  padding: 8px;
  max-width: 476px;
  width: calc(100% - 24px);
}

.buy-credit::backdrop {
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(2px);
}

.buy-credit .portal-panel { margin-bottom: 0; }

/* One saved card per row, the whole row clickable. */
.card-choice {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.card-choice + .card-choice { margin-top: 8px; }
.card-choice:has(input:checked) { border-color: var(--accent); }
.card-choice input { accent-color: var(--accent); }

/* .portal-form is a flex column with align-items: flex-start, so a child
   with no width of its own shrinks to fit. The actions row was already
   justify-content: center, but centring inside a box exactly as wide as the
   two buttons moves nothing — it sat at the form's left edge. Stretching it
   gives the centring something to work with. */
.buy-credit .install-gate-actions {
  align-self: stretch;
}

/* Solid, not translucent. .portal-panel and .btn-ghost both use --surface,
   which is rgba(255,255,255,0.04): on a page it sits over the opaque
   background and reads as a card, but in a dialog it sits over a dimmed view
   of the page and the page comes through it. A form asking for money should
   not be see-through, so this one is opaque rather than frosted like the
   install gate. */
.buy-credit .portal-panel {
  background: var(--bg-elevated);
}

.buy-credit .btn-ghost {
  background: var(--bg-elevated);
}

/* .portal-panel-head carries a divider so a panel can separate its title
   from a table. This panel is one short form, and the rule read as a stray
   line above the first field. */
.buy-credit .buy-credit-head {
  border-bottom: 0;
  padding-bottom: 0;
}

/* The one-time key reveal: copy, then dismiss. Two separate acts, because
   copying can fail silently and dismissing must be the customer's decision. */
.key-reveal-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  margin-top: 14px;
  flex-wrap: wrap;
}

/* A table sized by its contents moves its own columns. The keys table
   changed shape between projects — "Desktop app" and "Notebook" are
   different widths, so the headers landed in different places each time the
   picker changed, which reads as the layout being unstable rather than the
   data being different. Fixed layout plus a colgroup means the columns are
   the same wherever you are, and only the values change. */
.portal-table-fixed {
  table-layout: fixed;
}

/* A masked key and a long name must not force the table wider; they wrap or
   ellipsis instead. */
.portal-table-fixed td,
.portal-table-fixed th {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Two hero buttons that should read as a pair rather than a main one and an
   afterthought. Two things make them differ by default:

   Width — the labels are different lengths, so each button sizes to its own
   text. flex-basis 0 gives them the same share of the row.

   Height — a shared min-height, so nothing depends on the two variants
   resolving their content boxes the same way. .btn carries no border and
   .btn-ghost adds 1px, which is a 2px difference site-wide; fixing that on
   .btn itself put a transparent border over the primary's gradient and
   showed as a light edge, so it is a height here instead, which nothing can
   show through.

   Both buttons are .btn-primary: neither download is the lesser one, and a
   ghost beside a primary reads as a secondary action. */
.hero-actions.is-equal .btn {
  flex: 1 1 0;
  max-width: 300px;
  min-height: 52px;
}

@media (max-width: 560px) {
  .hero-actions.is-equal .btn { flex-basis: 100%; max-width: none; }
}

/* The effective-date line beside "Last updated", shown only while a new
   version is inside its notice period. Accented so it reads as a temporary
   notice rather than a second permanent timestamp. */
.updated-effective {
  margin-inline-start: 14px;
  color: var(--accent);
}

/* Sign-in and sign-up carry no navigation. Somebody on these pages is doing
   one thing, and Blog, Docs, Support and Pricing are four ways to abandon it.
   What stays is the way back to the product, and the language switcher,
   because a reader who needs Arabic needs it here most of all.

   The hamburger goes with the links: a menu button that opens nothing is
   worse than no button. */
.nav-bare .nav-links,
.nav-bare .nav-toggle {
  display: none;
}

/* ---------------------------------------------------------------------------
   Utilities that replace inline style attributes.

   Ninety-three style="" attributes were spread across forty-two served
   pages. They are here instead because an inline style attribute is what
   forces 'unsafe-inline' into the style-src directive of the CSP, and that
   directive is what stops the policy defending against injected style.

   Removing the directive is NOT possible yet, and this file should not
   pretend otherwise: the Google Translate widget applies its own inline
   styles from translate.googleapis.com, verified in a browser as five
   violations on load and fourteen after switching language. The same
   widget blocks script-src for the same reason. So this is the
   prerequisite done, not the fix — the day Translate goes, the directive
   is a one-line change.

   Every value here is transcribed EXACTLY from the attribute it replaced,
   including ones that look like they want rounding. 7px, 14px and 22px are
   not on any scale, and quietly tidying them would be a visual change
   wearing the clothes of a refactor.

   Every selector is doubled — .mt-7.mt-7 rather than .mt-7 — and that is
   load-bearing, not a typo. An inline style beats every selector, so the
   attributes these replace always won. A single class does not: the first
   attempt regressed ten pages, because rules like
   `.portal-form .form-note { margin: 0 }` outrank a lone class and the
   form notes silently lost their spacing. Doubling raises the utility to
   the same specificity as those rules, and this block being last in the
   file settles the tie.

   The check was a pixel diff of all forty-two pages before and after, at
   desktop and at mobile width. Anything that lost a specificity fight
   shows up there as a page that changed height, which is exactly how the
   ten regressions above were caught.
   ------------------------------------------------------------------------ */

/* Spacing, transcribed values, not a scale. */
.mt-7.mt-7 { margin-top: 7px; }
.mt-8.mt-8 { margin-top: 8px; }
.mt-10.mt-10 { margin-top: 10px; }
.mt-14.mt-14 { margin-top: 14px; }
.mt-16.mt-16 { margin-top: 16px; }
.mt-22.mt-22 { margin-top: 22px; }
.mt-32.mt-32 { margin-top: 32px; }
.mt-40.mt-40 { margin-top: 40px; }
.mt-48.mt-48 { margin-top: 48px; }
.mt-72.mt-72 { margin-top: 72px; }
.mt-80.mt-80 { margin-top: 80px; }
.pt-0.pt-0 { padding-top: 0; }
.pb-40.pb-40 { padding-bottom: 40px; }
.pb-80.pb-80 { padding-bottom: 80px; }
.my-44.my-44 { margin: 44px 0; }
.pad-26.pad-26 { padding: 26px; }
.pad-22-26.pad-22-26 { padding: 22px 26px; }

/* Text */
.ta-center.ta-center { text-align: center; }
.ta-left.ta-left { text-align: left; }
.fs-small.fs-small { font-size: 0.92rem; }
.mw-600.mw-600 { max-width: 600px; }
.mw-700.mw-700 { max-width: 700px; }

/* A centred lede under a heading. The margin-top it was written with
   varied (40px and 32px), so that half stays a separate .mt- class and
   composes: margin-left/right auto and margin-bottom 0 live here. */
.lede-center.lede-center {
  max-width: 680px;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 0;
  text-align: center;
}

/* A card that stacks, so a link inside it can be pushed to the bottom. */
.col-flex.col-flex { display: flex; flex-direction: column; }
.push-center.push-center { margin-top: auto; align-self: center; }
.flex-center-x.flex-center-x {
  display: flex;
  width: fit-content;
  margin-left: auto;
  margin-right: auto;
}

/* A form note stripped of the panel chrome it would otherwise inherit. */
.note-bare.note-bare { border: none; background: none; padding: 14px 0 0; margin: 0; }
.note-bare-flush.note-bare-flush { border: none; background: none; padding: 0; margin: 0; }

/* Table column widths, set on <col>. */
.col-w12.col-w12 { width: 12%; }
.col-w17.col-w17 { width: 17%; }
.col-w14.col-w14 { width: 14%; }
.col-w24.col-w24 { width: 24%; }
.col-w30.col-w30 { width: 30%; }

/* Delete, in the accounts row: smaller than the switch beside it, and
   nudged down and to the right.

   It is the destructive control in a row whose ordinary control is a
   toggle people use often. Matching the switch's visual weight invites
   the wrong click, so it reads a size smaller and sits well out to the
   right of it, on the same baseline. */
.portal-row-actions .row-delete {
  font-size: 0.74rem;
  margin-left: 40px;
}

/* A language listed but not published yet.

   It stays in the menu rather than being hidden, because hiding it tells
   a reader the site is English and always will be, while linking it would
   send them to a page that does not exist. Greyed and unclickable is the
   only one of the three that is true.

   A span, not a disabled link: an <a> with no href is still focusable and
   still reads as a control. cursor:default and no hover response so it
   does not invite the click it cannot answer. */
.lang-menu .lang-soon {
  display: block;
  padding: 8px 14px;
  color: var(--text-faint);
  cursor: default;
  opacity: 0.55;
}

.lang-menu .lang-soon::after {
  content: "soon";
  float: right;
  font-size: 0.72em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.8;
}

/* ---------- Capability cards ("What … actually does", both products) ---------- */
/* Shared by Alden and QuantMind: the accent comes from each product's
   own --accent and --accent-rgb, so the same cards are cyan on one and
   violet on the other. Moved here from alden.css for that reason. */
/* One card is the capability on top and its safeguards below, in a
   recessed panel. Cards share their rows through subgrid, so titles,
   bodies and safeguard panels line up across a row however long each
   one's text runs. The pointer glow reads --mx/--my, set by
   js/ability-glow.js. */

.ability-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

@media (max-width: 1020px) { .ability-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 680px)  { .ability-grid { grid-template-columns: minmax(0, 1fr); } }

.ability-card {
  --mx: 50%;
  --my: 0%;
  position: relative;
  display: grid;
  grid-row: span 4;
  grid-template-rows: subgrid;
  row-gap: 0;
  padding: 26px 26px 22px;
  border-radius: 20px;
  border: 1px solid var(--surface-border);
  background:
    radial-gradient(420px circle at var(--mx) var(--my), rgba(var(--accent-rgb), 0.07), transparent 60%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012));
  isolation: isolate;
  transition: border-color 0.4s var(--ease), transform 0.4s var(--ease);
  text-align: start;
}

/* Hairline of accent along the edge, brightest where the pointer is. */
.ability-card::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: radial-gradient(260px circle at var(--mx) var(--my), rgba(var(--accent-rgb), 0.55), transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
  z-index: -1;
}

.ability-card:hover { border-color: rgba(var(--accent-rgb), 0.22); transform: translateY(-2px); }
.ability-card:hover::before { opacity: 1; }

.ability-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 22px;
}

.ability-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: linear-gradient(145deg, rgba(var(--accent-rgb), 0.18), rgba(var(--accent-rgb), 0.04));
  border: 1px solid rgba(var(--accent-rgb), 0.28);
  box-shadow: 0 0 24px -6px rgba(var(--accent-rgb), 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.ability-icon svg,
.ability-footer-icon svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ability-num {
  font-family: "Space Grotesk", system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.ability-card h3 {
  font-size: 1.14rem;
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin: 0 0 10px;
  align-self: end;
}

.ability-body {
  color: var(--text-dim);
  font-size: 0.93rem;
  line-height: 1.62;
  margin: 0 0 22px;
}

/* align-self: start keeps a one-safeguard panel at its own height when a
   neighbour in the row has two; the tops still line up through subgrid. */
.ability-guard {
  align-self: start;
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
  overflow: hidden;
}

.ability-rule { padding: 16px 18px; }
.ability-rule + .ability-rule { border-top: 1px solid rgba(255, 255, 255, 0.06); }

.ability-rule h4 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text);
  margin: 0 0 6px;
}

.ability-shield {
  flex: none;
  width: 15px;
  height: 15px;
  fill: rgba(var(--accent-rgb), 0.14);
  stroke: var(--accent);
  stroke-width: 1.8;
  stroke-linejoin: round;
}

.ability-rule p {
  font-size: 0.85rem;
  line-height: 1.58;
  color: var(--text-faint);
  margin: 0;
  padding-inline-start: 23px;
}

/* The closing line: a banner that hands off to the Governance section. */
.ability-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  max-width: 760px;
  margin: 36px auto 0;
  padding: 16px 22px;
  border-radius: 999px;
  border: 1px solid rgba(var(--accent-rgb), 0.2);
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.02), rgba(var(--accent-rgb), 0.08), rgba(var(--accent-rgb), 0.02));
}

.ability-footer p {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
}

.ability-footer-icon { flex: none; display: grid; }

@media (max-width: 680px) {
  .ability-card { padding: 22px 20px 18px; }
  .ability-footer { border-radius: 18px; align-items: flex-start; text-align: start; }
}

@media (prefers-reduced-motion: reduce) {
  .ability-card, .ability-card::before { transition: none; }
  .ability-card:hover { transform: none; }
}

/* ---------- Status note: "Once it's running" on the install guides ----------
   A card with a violet-to-green edge: what you should see when it works
   (green) and what to check when it doesn't (amber). The live dot pulses
   gently, like a status light. */
.status-note {
  --sn-violet: 167, 107, 255;
  --sn-green: 62, 230, 168;
  max-width: 720px;
  margin: 44px auto 0;
  padding: 22px 24px;
  border-radius: 20px;
  border: 1px solid transparent;
  background:
    linear-gradient(160deg, rgba(var(--sn-violet), 0.10), rgba(var(--sn-green), 0.06)) padding-box,
    linear-gradient(rgba(11, 13, 22, 0.96), rgba(11, 13, 22, 0.96)) padding-box,
    linear-gradient(120deg, rgba(var(--sn-violet), 0.75), rgba(var(--sn-green), 0.75)) border-box;
  box-shadow: 0 18px 40px -24px rgba(var(--sn-green), 0.35), 0 18px 40px -28px rgba(var(--sn-violet), 0.4);
  text-align: start;
}
.status-note-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--sn-green));
}
.status-pulse {
  position: relative;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgb(var(--sn-green));
  box-shadow: 0 0 10px rgba(var(--sn-green), 0.8);
}
.status-pulse::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1px solid rgba(var(--sn-green), 0.6);
  /* two pulses (4.4 s) then still: WCAG 2.2.2 wants anything that moves on its own for more
     than 5 seconds to be stoppable, and a dot that stops by itself needs no button */
  animation: status-pulse 2.2s ease-out 2;
}
@keyframes status-pulse {
  from { transform: scale(0.6); opacity: 1; }
  to { transform: scale(1.8); opacity: 0; }
}
.status-note-row {
  display: flex;
  gap: 14px;
  padding: 14px 0;
}
.status-note-row + .status-note-row { border-top: 1px dashed rgba(255, 255, 255, 0.08); }
.status-note-row:last-child { padding-bottom: 0; }
.status-note-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
}
.status-note-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.status-note-row.is-ok .status-note-icon { color: rgb(var(--sn-green)); background: rgba(var(--sn-green), 0.1); border: 1px solid rgba(var(--sn-green), 0.3); }
.status-note-row.is-warn .status-note-icon { color: #ffc46b; background: rgba(255, 196, 107, 0.08); border: 1px solid rgba(255, 196, 107, 0.28); }
.status-note-row h4 { margin: 2px 0 4px; font-size: 0.95rem; color: var(--text); }
.status-note-row p { margin: 0; font-size: 0.9rem; line-height: 1.6; color: var(--text-dim); }
@media (prefers-reduced-motion: reduce) { .status-pulse::after { animation: none; } }

/* ---------- Phone menu (full-screen list behind the menu button) ----------
   Below 860px the header shows only the brand and the menu button. Every
   other header control, the page links, the account control and the
   language pill, lives in one full-screen list that slides in from the
   side while the button's three bars turn into an X. main.js moves the
   account control and the language pill into .nav-menu-extras while the
   list is open. This block comes last so it wins over the older stacked
   dropdown rules above. */
.nav-menu-extras,
.nav-menu-top { display: none; }
/* The name in the header wears the same gradient as the large title on
   the home page, on every page and every screen size. */
.nav .nav-brand > .notranslate {
  background: var(--grad-company);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
:root { --menu-ease: cubic-bezier(0.77, 0, 0.175, 1); }

@media (max-width: 860px) {
  nav.nav,
  nav.nav.has-account,
  nav.portal-nav { grid-template-columns: minmax(0, 1fr) auto; }

  nav.nav > .nav-account-menu,
  nav.nav > .nav-lang { display: none !important; }

  /* A taller bar with a bigger mark, and the name in the same gradient as
     the large title on the home page. */
  nav.nav { padding-top: 22px; padding-bottom: 22px; }
  .nav .nav-mark { height: 50px; }
  .nav .nav-brand { font-size: 1.3rem; gap: 12px; }
  .nav .nav-brand > .notranslate { font-weight: 700; }

  .nav-brand-group,
  .nav > .nav-brand { position: relative; z-index: 2; }

  nav.nav .nav-toggle,
  nav.portal-nav .nav-toggle,
  nav.nav.has-account .nav-toggle {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    justify-self: end;
    position: relative;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    margin-inline-end: -10px;
    padding: 0;
    font-size: 0;
    color: var(--text);
  }

  .nav-toggle-bars { position: relative; display: block; width: 28px; height: 18px; }
  .nav-toggle-bars span {
    position: absolute;
    left: 0;
    width: 100%;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: top 0.3s var(--menu-ease) 0.25s, transform 0.3s var(--menu-ease), opacity 0.25s var(--menu-ease);
  }
  .nav-toggle-bars span:nth-child(1) { top: 0; }
  .nav-toggle-bars span:nth-child(2) { top: 8px; }
  .nav-toggle-bars span:nth-child(3) { top: 16px; }
  /* Opening: the bars meet in the middle, then turn. Closing reverses it. */
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars span { transition: top 0.3s var(--menu-ease), transform 0.3s var(--menu-ease) 0.25s, opacity 0.25s var(--menu-ease); }
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1) { top: 8px; transform: rotate(45deg); }
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3) { top: 8px; transform: rotate(-45deg); }

  /* The list. It is fixed to the screen, so the bar's blur (which would
     trap a fixed child inside the bar) is dropped while it is open. */
  html.nav-open { overflow: hidden; }
  html.nav-open .nav {
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom-color: transparent;
  }

  nav .nav-links {
    display: flex;
    position: fixed;
    inset: 0;
    left: 0;
    right: 0;
    z-index: 1;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
    padding: 104px 28px 36px;
    overflow-y: auto;
    background: rgba(5, 6, 10, 0.97);
    backdrop-filter: none;
    border: 0;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.6s var(--menu-ease), visibility 0s linear 0.6s;
  }
  nav .nav-links.open {
    transform: none;
    visibility: visible;
    transition: transform 0.6s var(--menu-ease), visibility 0s;
  }
  [dir="rtl"] nav .nav-links { transform: translateX(100%); }
  [dir="rtl"] nav .nav-links.open { transform: none; }

  /* Every item is one full-width row with a dashed rule under it. */
  nav .nav-links > a,
  nav .nav-links > a:not(.nav-cta),
  nav .nav-links > a.nav-cta,
  nav .nav-links > .nav-dropdown > .nav-dropdown-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    align-self: stretch;
    margin: 0;
    padding: 18px 2px;
    border: 0;
    border-bottom: 1px dashed rgba(var(--accent-rgb), 0.35);
    border-radius: 0;
    background: none;
    box-shadow: none;
    font-family: "Space Grotesk", sans-serif;
    font-size: 1.45rem;
    font-weight: 500;
    color: var(--text) !important;
    text-align: start;
  }
  nav .nav-links > a[hidden] { display: none; }

  /* A dropdown in the list (the company Products menu) opens fully: its
     name becomes a small heading and every item is a row of its own. */
  nav .nav-links > .nav-dropdown > .nav-dropdown-toggle {
    padding: 22px 2px 6px;
    border-bottom: 0;
    font-family: "Inter", sans-serif;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-faint) !important;
    pointer-events: none;
  }
  nav .nav-links .nav-dropdown:not(.nav-account-menu) > .nav-dropdown-menu,
  nav .nav-links .nav-dropdown-menu:not(.nav-account-dropdown) {
    left: auto;
    transform: none;
    display: block !important;
    margin: 0;
    padding: 0;
    list-style: none;
  }
  nav .nav-links .nav-dropdown-menu:not(.nav-account-dropdown) li { padding: 0; }
  nav .nav-links .nav-dropdown-menu:not(.nav-account-dropdown) a {
    display: flex;
    width: 100%;
    padding: 18px 2px;
    border-bottom: 1px dashed rgba(var(--accent-rgb), 0.35);
    font-family: "Space Grotesk", sans-serif;
    font-size: 1.45rem;
    font-weight: 500;
    color: var(--text);
  }
  nav .nav-links > .nav-dropdown { width: 100%; margin: 0; }
  nav .nav-links a[aria-current="page"] { color: var(--accent) !important; background: none; box-shadow: none; }

  /* Account control and language at the foot of the list. */
  .nav-menu-extras {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: auto;
    padding-top: 28px;
  }
  .nav-menu-extras .nav-account-menu,
  .nav-menu-extras .nav-account { position: static; margin: 0; }
  .nav-menu-extras .nav-account { flex-direction: row; gap: 10px; }
  .nav-menu-extras .nav-account span { display: inline; font-size: 1rem; }
  /* Language: centred under everything else, horizontal and large, the
     way the pill looked before it was turned on its side for the bar. Its
     list opens upwards, since the pill sits at the foot of the screen. */
  .nav-menu-extras { flex-direction: column; align-items: stretch; gap: 24px; }
  /* Sign in / My account: the first row of the list. */
  .nav-menu-top:not(:empty) {
    display: block;
    padding: 0 0 18px;
    border-bottom: 1px dashed rgba(var(--accent-rgb), 0.35);
  }
  .nav-menu-top .nav-account-menu,
  .nav-menu-top .nav-account { position: static; margin: 0; }
  .nav-menu-top .nav-account { flex-direction: row; gap: 12px; }
  .nav-menu-top .nav-account span { display: inline; font-family: "Space Grotesk", sans-serif; font-size: 1.45rem; font-weight: 500; color: var(--text); }
  .nav-menu-top .nav-account-menu { display: flex; flex-direction: column; align-items: flex-start; justify-self: start; width: auto; min-width: 0; }
  .nav-menu-top .nav-account { justify-content: flex-start; width: auto; padding: 0; margin: 0 !important; }
  /* The signed-in menu opens in place, as rows, and takes no room while shut. */
  .nav-menu-top .nav-account-dropdown { position: static; min-width: 0; display: none; margin: 12px 0 0; padding: 0; border: 0; background: none; opacity: 1; visibility: visible; transform: none; }
  .nav-menu-top .nav-account-dropdown.open { display: block; }
  .nav-menu-top .nav-account-dropdown a { display: block; padding: 10px 2px 10px 52px; font-size: 1.05rem; color: var(--text-dim); }
  nav .nav-links .nav-menu-extras .nav-lang { position: relative; inset: auto; align-self: center; margin: 0; order: 2; }
  /* Same pill as before; only the globe and the letters inside it grow. */
  .nav-menu-extras .lang-toggle {
    flex-direction: row;
    justify-content: center;
    gap: 9px;
    width: 108px;
    height: 46px;
    padding: 0;
    border-radius: 999px;
    font-size: 1.35rem;
    line-height: 1;
  }
  .nav-menu-extras .lang-toggle svg { width: 34px; height: 34px; }
  nav .nav-links .nav-menu-extras .lang-menu { top: auto; bottom: calc(100% + 10px); right: auto; left: 50%; transform: translateX(-50%); width: 260px; max-height: calc(100vh - 240px); overflow-y: auto; }
  nav .nav-links .nav-menu-extras .lang-menu li { padding: 12px 16px; font-size: 1rem; }
  nav .nav-links .nav-menu-extras .lang-menu li:has(.lang-soon) { padding: 0; }
  nav .nav-links .nav-menu-extras .lang-menu .lang-soon { display: flex; justify-content: space-between; align-items: baseline; padding: 12px 16px; }
  nav .nav-links .nav-menu-extras .lang-menu .lang-soon::after { float: none; }
  nav .nav-links .nav-menu-extras .lang-menu .lang-soon[dir="rtl"] { flex-direction: row-reverse; }
  .nav-menu-extras .nav-account-dropdown { position: static; min-width: 0; }
}

@media (max-width: 860px) and (prefers-reduced-motion: reduce) {
  nav .nav-links,
  nav .nav-links.open,
  .nav-toggle-bars span,
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars span { transition: none; }
}
.pb-0.pb-0 { padding-bottom: 0; }

/* Company pages: the same header size as the product pages (logo 76px,
   name 28px). On phones the longer company name is set a step smaller so
   it still fits beside the menu button. */
.nav > .nav-brand { font-size: 1.75rem; gap: 14px; }
.nav > .nav-brand .nav-mark { height: 76px; }
@media (max-width: 860px) {
  .nav > .nav-brand { font-size: 1.3rem; }
  /* At this size the gradient letters read thin; a faint stroke in the
     gradient's middle colour thickens them without changing the look. */
  .nav > .nav-brand > .notranslate { -webkit-text-stroke: 1px rgba(140, 150, 255, 0.85); }
  .nav > .nav-brand .nav-mark { height: 64px; }
}

/* The old "Sign in" / "Sign up" pills stay in the HTML as the fallback
   link, but auth-nav.js replaces them with the account icon on every page.
   Hidden from the first paint, not after the session check answers, so they
   no longer flash in the header on load. The signed-in "Portal" link is the
   same element with data-auth-state="in", so it still shows. */
.nav [data-auth-link]:not([data-auth-state="in"]):not(.nav-talk),
.nav [data-auth-signup] { display: none; }
/* Except Alden's "Talk to Alden" (.nav-talk, 2026-11-06): the one button,
   shown signed in and signed out (auth-nav.js data-talk). */

/* Page tag pills (the eyebrow at the top of a page) show on phones only.
   On laptops they repeat what the header and the page title already say,
   so they go, on public and portal pages alike. Docs pages keep theirs on
   every screen (.keep-eyebrow): there the pill names the guide you are in.
   Where the pill is the page's only heading (an h1), it is hidden visually
   but kept for screen readers and search, so the page still has a title. */
@media (min-width: 861px) {
  /* The same open band under the header on every page, the one the
     pricing page has: 103px from the header to the first thing you see.
     A hero whose only child is the hidden pill already gets it from its
     own padding, so it keeps the base 160px. */
  header.page-hero { padding-top: 220px; }
  header.page-hero:has(> .hero-eyebrow:only-child:not(.keep-eyebrow)) { padding-top: 160px; }
  /* Docs guide pages keep their pill on laptops, so they keep the
     original spacing too. */
  header.page-hero:has(> .keep-eyebrow) { padding-top: 160px; }
  header.page-hero > span.hero-eyebrow:not(.keep-eyebrow) { display: none; }
  header.page-hero > h1.hero-eyebrow:not(.keep-eyebrow) {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* Download button labels. The button carries all four; js/os-early.js sets
   <html data-os> in <head>, before the first paint, and only the matching
   one shows, so the label never changes after the page appears. With
   scripts off there is no data-os and the plain label shows. */
.os-label { display: none; }
html:not([data-os]) .os-any,
html[data-os="mac"] .os-mac,
html[data-os="windows"] .os-windows,
html[data-os="unknown"] .os-unknown { display: inline; }

/* ---------- Header: darkens as you scroll, no line (2026-10-23) ----------
   The dark, blurred bar sits on a layer of its own whose strength follows
   the scroll (--nav-dark, set by js/main.js). Every page with the shared
   header: company, Alden and QuantMind. The 1px see-through border
   is gone: the bar's gradient repeated inside it and drew a dark line. */
.nav,
.nav.scrolled { border-bottom: 0; background-repeat: no-repeat; }
.nav.scrolled {
  background: linear-gradient(rgba(5, 6, 10, 0.65), rgba(5, 6, 10, 0));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.nav::before {
  content: "";
  position: absolute;
  inset: 0 0 -32px 0;
  z-index: -1;
  background: rgba(5, 6, 10, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 52px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 52px), transparent);
  opacity: var(--nav-dark, 0);
  pointer-events: none;
}
/* The signed-in portals (and admin) used a solid bar with a line of their
   own; they now share the same header: clear at the top, darkening with
   the scroll, no line. */
.nav.portal-nav,
.nav.portal-nav.scrolled {
  background: linear-gradient(rgba(5, 6, 10, 0.65), rgba(5, 6, 10, 0));
  background-repeat: no-repeat;
  border-bottom: 0;
}

/* Company header: the new mark (the orb with a three-candle tail) stands in
   for the Q of the name, so it reads [Q]uant Intelligence. Sizes match the
   approved preview: the ring meets the cap height and the candle tail drops
   below the baseline like a real Q's. The name's gradient runs blue to violet
   here, so blue sits next to the blue Q (--grad-company itself is unchanged). */
.nav-brand.qi-brand { gap: 0; min-height: 48px; } /* the menu button's height, so the row centres on both */
.qi-word {
  display: inline-block;
  white-space: nowrap;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.qi-word .qi-q {
  display: inline;
  height: 0.81em;
  width: auto;
  vertical-align: -0.08em;
  margin-right: 0.01em;
}
.qi-word .notranslate {
  background: linear-gradient(120deg, #3b78ff, #a76bff);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
@media (max-width: 860px) {
  .qi-word .notranslate { -webkit-text-stroke: 1px rgba(140, 150, 255, 0.85); }
}

/* The cookie question (js/visit-consent.js): a small card in the bottom-left
   corner, asked once. Decline and Accept look the same on purpose, so
   neither answer is pushed. */
.cookie-ask {
  position: fixed;
  left: calc(20px + env(safe-area-inset-left, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 1000;
  width: min(360px, calc(100vw - 40px));
  box-sizing: border-box;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: rgba(12, 14, 22, 0.94);
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-sm);
  box-shadow: 0 18px 50px -12px rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.cookie-ask p { margin: 0; }
.cookie-ask-title { color: var(--text); font-weight: 600; }
.cookie-ask-text { font-size: 0.88rem; line-height: 1.5; }
.cookie-ask-text a { color: var(--text); text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
.cookie-ask-buttons { display: flex; gap: 10px; margin-top: 6px; }
.cookie-ask-buttons .btn { flex: 1; }
.page-hero [data-cookie-change] { margin-top: 18px; }   /* the cookie policy's "Change your cookie choice" */

/* Two designs being compared on staging (?cookie-design=a / b). One is kept, the other removed. */
.cookie-ask.is-a, .cookie-ask.is-b {
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  background: #0c0e16;
}
.cookie-link {
  background: none;
  border: 0;
  padding: 8px 2px;
  font: inherit;
  font-size: 0.92rem;
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* A: a centred card, the cookie on top, the words centred, Decline as a link and Accept as a pill */
.cookie-ask.is-a {
  width: min(440px, calc(100vw - 32px));
  padding: 26px 28px 22px;
  border-radius: 22px;
  align-items: center;
  text-align: center;
  gap: 10px;
}
.is-a .cookie-ask-title { font-family: "Space Grotesk", "Inter", system-ui, sans-serif; font-size: 1.3rem; }
.is-a .cookie-ask-text { font-size: 0.92rem; max-width: 34ch; }
.is-a .cookie-ask-buttons { width: 100%; justify-content: space-between; align-items: center; margin-top: 10px; }
.is-a .cookie-ask-buttons .btn { flex: 0 0 auto; padding: 0 28px; }

/* B: a wide card with a soft outer ring, a big gradient title beside the cookie, two equal pills */
.cookie-ask.is-b {
  width: min(560px, calc(100vw - 32px));
  padding: 26px 28px 24px;
  border-radius: 24px;
  gap: 18px;
  box-shadow: 0 0 0 8px rgba(167, 107, 255, 0.08), 0 24px 60px -16px rgba(0, 0, 0, 0.75);
}
.cookie-b-top { display: flex; align-items: center; gap: 16px; }
.cookie-b-words { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.is-b .cookie-ask-title {
  font-family: "Space Grotesk", "Inter", system-ui, sans-serif;
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.1;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.is-b .cookie-art { flex: 0 0 auto; }
.is-b .cookie-ask-buttons { gap: 14px; margin-top: 0; }
.is-b .cookie-ask-buttons .btn { flex: 1; }
@media (max-width: 520px) {
  .is-b .cookie-art { width: 92px; height: 72px; }
  .is-b .cookie-ask-title { font-size: 1.35rem; }
}

/* The chosen card (default): bottom-right, the same width for both, each as tall as its words,
   the cookie drawing on top, centred words, buttons along the bottom */
.cookie-ask.is-card {
  left: auto;
  right: calc(20px + env(safe-area-inset-right, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  width: min(320px, calc(100vw - 40px));
  padding: 20px 20px 18px;
  border-radius: 20px;
  background: #0c0e16;
  align-items: center;
  text-align: center;
  gap: 8px;
}
.cookie-ask.is-card .cookie-art { width: 76px; height: 80px; flex: 0 0 auto; }
.cookie-ask.is-card .cookie-ask-title { font-family: "Space Grotesk", "Inter", system-ui, sans-serif; font-size: 1.15rem; }
.cookie-ask.is-card .cookie-ask-text { font-size: 0.86rem; }
/* the buttons along the bottom */
.cookie-ask.is-card .cookie-ask-buttons { width: 100%; margin-top: auto; padding-top: 6px; }
.cookie-ask.is-card .cookie-ask-buttons .btn { white-space: nowrap; padding-left: 12px; padding-right: 12px; }
/* Each site's card in its own colours (Fahad, 2026-11-08): the company card stays as above;
   Alden's and QuantMind's take their pages' gradients, set here so the card is right on any page. */
.cookie-ask.side-alden { background: #070c1c; border-color: rgba(47, 108, 245, 0.35); }
.cookie-ask.side-quantmind { background: #100a20; border-color: rgba(167, 107, 255, 0.35); }
.cookie-ask.side-alden .btn-primary { background: var(--grad-alden); color: #ffffff; }
.cookie-ask.side-quantmind .btn-primary { background: var(--grad-quantmind); color: #ffffff; }
@media (max-width: 520px) {
  .cookie-ask.is-card { left: 16px; right: 16px; width: auto; }
}
/* "Manage cookies" under the two equal buttons (group 1): a quiet text button */
.cookie-manage {
  margin-top: 2px;
  padding: 4px 8px;
  background: none;
  border: 0;
  color: var(--text-dim);
  font-size: 0.84rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.cookie-manage:hover { color: var(--text); }

/* Cookie settings (js/visit-consent.js manage()): every cookie, by category. A centred panel
   over a dimmed page; the list scrolls inside it, the buttons stay at the bottom. */
.cookie-settings-open, .cookie-settings-open body { overflow: hidden; }
.cookie-settings {
  position: fixed;
  inset: 0;
  z-index: 1001;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(16px + env(safe-area-inset-top, 0px)) max(16px, env(safe-area-inset-right, 0px))
           calc(16px + env(safe-area-inset-bottom, 0px)) max(16px, env(safe-area-inset-left, 0px));
  background: rgba(3, 4, 8, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.cookie-settings-panel {
  width: min(620px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: #0c0e16;
  border: 1px solid var(--surface-border);
  border-radius: 20px;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.8);
  overflow: hidden;
}
.cookie-settings-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 22px;
  border-bottom: 1px solid var(--surface-border);
}
.cookie-settings-title {
  margin: 0;
  font-family: "Space Grotesk", "Inter", system-ui, sans-serif;
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--text);
}
.cookie-settings-close {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  border: 1px solid var(--surface-border);
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
}
.cookie-settings-close:hover { color: var(--text); border-color: var(--text-dim); }
.cookie-settings-body {
  overflow-y: auto;
  padding: 18px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.cookie-settings-intro { margin: 0; font-size: 0.9rem; line-height: 1.6; color: var(--text-dim); }
.cookie-settings-intro a { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.cookie-cat {
  padding: 16px 18px;
  border: 1px solid var(--surface-border);
  border-radius: 14px;
  background: var(--surface);
}
.cookie-cat-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cookie-cat-title { margin: 0; font-weight: 600; color: var(--text); }
.cookie-cat-text { margin: 8px 0 0; font-size: 0.88rem; line-height: 1.55; color: var(--text-dim); }
.cookie-always {
  flex: 0 0 auto;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
/* on/off switch: a real checkbox, so keyboards and screen readers work */
.cookie-switch { position: relative; flex: 0 0 auto; display: inline-flex; cursor: pointer; }
.cookie-switch input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.cookie-switch-track {
  width: 46px;
  height: 26px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  transition: background 0.2s var(--ease);
  position: relative;
}
.cookie-switch-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.2s var(--ease);
}
.cookie-switch input:checked + .cookie-switch-track { background: var(--grad); }
.cookie-switch input:checked + .cookie-switch-track::after { transform: translateX(20px); }
.cookie-switch input:focus-visible + .cookie-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.cookie-cat-list { margin-top: 10px; }
.cookie-cat-list summary {
  cursor: pointer;
  font-size: 0.86rem;
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  width: fit-content;
}
.cookie-item {
  margin: 12px 0 0;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.25);
  font-size: 0.84rem;
}
.cookie-item-row { display: grid; grid-template-columns: 110px 1fr; gap: 10px; padding: 3px 0; }
.cookie-item-row dt { color: var(--text-faint); }
.cookie-item-row dd { margin: 0; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.cookie-item-row code { font-size: 0.82rem; }
.cookie-item-gap { border: 0; border-top: 1px solid var(--surface-border); margin: 10px 0; }
@media (max-width: 520px) {
  .cookie-item-row { grid-template-columns: 1fr; gap: 0; }
  .cookie-item-row dt { font-size: 0.76rem; }
}
.cookie-settings-foot {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  padding: 14px 22px;
  border-top: 1px solid var(--surface-border);
}
@media (max-width: 520px) {
  .cookie-settings-foot .btn { flex: 1; }
}

/* Two placements being compared on staging (?cookie-place=corner / strip), kept off the
   first screen's centre */
.cookie-ask.is-corner, .cookie-ask.is-strip { background: #0c0e16; }
.cookie-ask.is-corner .cookie-art, .cookie-ask.is-strip .cookie-art { width: 40px; height: 42px; flex: 0 0 auto; }
.cookie-place-words { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cookie-ask.is-corner .cookie-ask-title, .cookie-ask.is-strip .cookie-ask-title { font-size: 0.95rem; }
.cookie-ask.is-corner .cookie-ask-text, .cookie-ask.is-strip .cookie-ask-text { font-size: 0.84rem; }

/* corner: a compact box in the bottom-right corner */
.cookie-ask.is-corner {
  left: auto;
  right: calc(20px + env(safe-area-inset-right, 0px));
  width: min(330px, calc(100vw - 40px));
  padding: 14px 16px;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  column-gap: 12px;
  row-gap: 10px;
  align-items: start;
}
.cookie-ask.is-corner .cookie-ask-buttons { grid-column: 1 / -1; margin-top: 0; }

/* strip: a thin full-width bar along the bottom edge */
.cookie-ask.is-strip {
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  border-radius: 0;
  border-width: 1px 0 0;
  box-shadow: 0 -12px 40px -16px rgba(0, 0, 0, 0.7);
  flex-direction: row;
  align-items: center;
  gap: 16px;
  padding: 12px max(24px, env(safe-area-inset-right, 0px)) calc(12px + env(safe-area-inset-bottom, 0px)) max(24px, env(safe-area-inset-left, 0px));
}
.cookie-ask.is-strip .cookie-place-words { flex: 1; }
.cookie-ask.is-strip .cookie-ask-buttons { margin-top: 0; flex: 0 0 auto; }
.cookie-ask.is-strip .cookie-ask-buttons .btn { flex: 0 0 auto; min-width: 110px; }
@media (max-width: 640px) {
  .cookie-ask.is-strip { flex-wrap: wrap; }
  .cookie-ask.is-strip .cookie-place-words { flex: 1 1 calc(100% - 60px); }
  .cookie-ask.is-strip .cookie-ask-buttons { flex: 1 1 100%; }
  .cookie-ask.is-strip .cookie-ask-buttons .btn { flex: 1; }
}

/* ---------- Accessibility (WCAG 2.2 AA), 2026-11-05, localhost review ---------- */

/* Visible focus everywhere: anything reached with the keyboard shows a ring.
   :where() keeps this at zero specificity, so a component's own focus style
   still wins where one exists. Mouse clicks show nothing (focus-visible). */
/* No border-radius here: the ring follows each control's own shape (a
   pill button gets a pill ring), and setting one squared off the rounded
   buttons and the EN capsule the moment they took focus. */
:where(a, button, summary, [tabindex], [role="button"]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* "Skip to content": invisible until the first Tab press, then shown at the
   top left; Enter jumps past the header to the page's content
   (js/main.js adds it to every page). */
.skip-link {
  position: fixed;
  top: calc(10px + env(safe-area-inset-top, 0px));
  left: calc(10px + env(safe-area-inset-left, 0px));
  z-index: 2000;
  padding: 10px 16px;
  border-radius: 999px;
  background: var(--grad);
  color: var(--btn-ink);
  font-weight: 600;
  font-size: 0.9rem;
  transform: translateY(-160%);
  transition: transform 0.2s var(--ease);
}
.skip-link:focus { transform: none; outline: 2px solid #fff; outline-offset: 2px; }
/* the block the skip link lands on takes focus without drawing a ring round it */
[tabindex="-1"]:focus { outline: none; }

/* A jump to any anchor stops below the fixed header, so the focused item
   is never hidden behind it. */
html { scroll-padding-top: 96px; }

/* Click targets at least 24×24 px (2.5.8): same look, taller hit area. */
.footer-bottom-links a,
.footer-social a { display: inline-flex; align-items: center; min-height: 24px; }
.card-billing-opt { min-height: 24px; }

/* ================= Alden API console (2026-11-05) =================
   Components for js/api-console.js, modelled on the Claude Console
   (deploy/internal/claude-console-notes.md) in this site's colours:
   page head, filter bar, flat tables with two-line cells and "..." menus,
   side drawer, dialogs, stacked bar charts, sparklines. Colour pattern:
   grey labels, white values. */
.api-console .api-nav { margin-bottom: 26px; }
.c-blue { --c: #3b78ff; } .c-violet { --c: #a76bff; } .c-green { --c: #34d399; }
.c-amber { --c: #fbbf24; } .c-pink { --c: #f472b6; } .c-cyan { --c: #22d3ee; }

.ac-page { display: grid; gap: 22px; min-width: 0; }
.ac-loading, .ac-none { color: var(--text-dim); margin: 0; }
.ac-icon { display: inline-flex; width: 18px; height: 18px; }
.ac-icon svg, .ac-filter svg, .ac-search svg, .ac-menu-btn svg, .ac-icon-btn svg, .btn svg, .ac-seg svg, .ac-link svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.ac-menu-btn svg circle { fill: currentColor; stroke: none; }
.btn svg { margin-inline-end: 4px; vertical-align: -3px; }

/* Page head: title, count, one line, actions on the right with credit. */
.ac-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.ac-head-text { min-width: 0; flex: 1 1 380px; }
.ac-title { margin: 0; font-family: "Space Grotesk", sans-serif; font-size: 1.6rem; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 10px; }
.ac-count { font-size: 0.78rem; padding: 2px 9px; border-radius: 999px; background: var(--surface); border: 1px solid var(--surface-border); color: var(--text-dim); font-family: inherit; }
.ac-badge { font-size: 0.72rem; padding: 3px 9px; border-radius: 6px; background: var(--surface); border: 1px solid var(--surface-border); color: var(--text-dim); font-family: inherit; }
.ac-desc { margin: 8px 0 0; color: var(--text-dim); font-size: 0.92rem; line-height: 1.55; max-width: 72ch; }
.ac-head-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ac-credit { display: inline-flex; align-items: center; gap: 10px; padding: 7px 14px; border-radius: 999px; border: 1px solid var(--surface-border); background: var(--surface); font-size: 0.82rem; color: var(--text-faint); }
.ac-credit strong { color: var(--text); font-family: "Space Grotesk", sans-serif; font-variant-numeric: tabular-nums; }
.ac-credit:hover { border-color: var(--text-dim); }

/* Filter bar. */
.ac-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ac-search { display: inline-flex; align-items: center; gap: 8px; padding: 0 12px; height: 36px; min-width: 240px; flex: 0 1 300px;
  border-radius: var(--radius-sm); border: 1px solid var(--surface-border); background: var(--surface); color: var(--text-faint); }
.ac-search input { border: 0; background: transparent; color: var(--text); font: inherit; font-size: 0.88rem; outline: none; width: 100%; padding: 0; height: 100%; }
.ac-search:focus-within { border-color: var(--accent); }
.ac-filter { display: inline-flex; align-items: center; gap: 4px; height: 36px; padding: 0 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--surface-border); background: var(--surface); color: var(--text); font: inherit; font-size: 0.84rem; cursor: pointer; }
.ac-filter-label { color: var(--text-faint); }
.ac-filter:hover, .ac-filter[data-open="1"] { border-color: var(--text-dim); }
.ac-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 8px;
  border: 1px solid var(--surface-border); background: var(--surface); color: var(--text-dim); cursor: pointer; }
.ac-icon-btn:hover { color: var(--text); border-color: var(--text-dim); }
.ac-filters .ac-icon-btn { margin-inline-start: auto; }

/* Flat table: no card, rows divided by hairlines, two-line cells. */
.ac-table-wrap, .ac-chart-table { overflow-x: auto; min-width: 0; }
.ac-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; min-width: 640px; }
.ac-table th { text-align: start; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; color: var(--text-faint);
  padding: 10px 14px; border-bottom: 1px solid var(--surface-border); white-space: nowrap; }
.ac-table td { padding: 12px 14px; border-bottom: 1px solid var(--surface-border); color: var(--text); vertical-align: middle; }
.ac-table tr.is-clickable { cursor: pointer; }
.ac-table tbody tr:hover td { background: rgba(255, 255, 255, 0.025); }
.ac-table .num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ac-table .is-date { white-space: nowrap; }
.ac-table .ac-actions { text-align: end; width: 1%; white-space: nowrap; }
.ac-table .ac-wide { width: 40%; }
.ac-two, .ac-person > span { display: inline-grid; gap: 2px; }
.ac-l1 { color: var(--text); }
.ac-l2 { color: var(--text-faint); font-size: 0.8rem; }
.ac-mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.8rem; }
.ac-person { display: inline-flex; align-items: center; gap: 10px; }
.ac-avatar { width: 26px; height: 26px; flex: none; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--surface-border); color: var(--text-dim); font-size: 0.72rem; font-weight: 600; }
.ac-dot { display: inline-block; width: 9px; height: 9px; border-radius: 3px; background: var(--c, var(--accent)); margin-inline-end: 8px; vertical-align: 0; }
.ac-chip { display: inline-block; padding: 2px 9px; border-radius: 999px; border: 1px solid var(--surface-border); background: var(--surface); font-size: 0.78rem; color: var(--text); }
.ac-status { font-size: 0.82rem; }
.ac-status::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-inline-end: 7px; background: currentColor; vertical-align: 1px; }
.s-ok { color: #4ade80; } .s-off { color: var(--text-dim); } .s-bad { color: var(--danger-text); } .s-warn { color: #fbbf24; }
.ac-muted { color: var(--text-faint); }
.ac-link { color: var(--accent); background: none; border: 0; padding: 0; font: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.ac-link:hover { text-decoration: underline; }
.ac-menu-btn { width: 30px; height: 30px; border-radius: 8px; border: 1px solid transparent; background: transparent; color: var(--text-dim); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.ac-menu-btn:hover { background: var(--surface); border-color: var(--surface-border); color: var(--text); }

/* Tiles and meters. */
.ac-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.ac-tile { padding: 18px 20px; border-radius: var(--radius); border: 1px solid var(--surface-border); background: var(--surface); display: grid; gap: 8px; align-content: start; min-width: 0; }
.ac-tile-label { font-size: 0.74rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-faint); display: flex; align-items: center; gap: 6px; }
.ac-tile-value { font-family: "Space Grotesk", sans-serif; font-size: 1.5rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.ac-tile-sub { font-size: 0.82rem; color: var(--text-dim); }
.ac-tile .btn { justify-self: start; }
.ac-tip { display: inline-flex; color: var(--text-faint); cursor: help; }
.ac-tip svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.ac-meter { height: 6px; border-radius: 999px; background: rgba(255, 255, 255, 0.07); overflow: hidden; min-width: 80px; }
.ac-meter span { display: block; height: 100%; width: 0; border-radius: inherit; background: var(--accent); }
.ac-spark { width: 100%; height: 34px; }
.ac-spark polyline { fill: none; stroke: var(--accent); stroke-width: 1.6; vector-effect: non-scaling-stroke; }

/* Sections and panels. */
.ac-section { display: grid; gap: 12px; }
.ac-section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.ac-section-head h3, .ac-chart-head h3, .ac-panel-title { margin: 0; font-family: "Space Grotesk", sans-serif; font-size: 1.02rem; font-weight: 600; color: var(--text); }
.ac-panel { padding: 20px 22px; border-radius: var(--radius); border: 1px solid var(--surface-border); background: var(--surface); display: grid; gap: 14px; min-width: 0; }
.ac-panel-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.ac-panel-desc { margin: 4px 0 0; font-size: 0.86rem; color: var(--text-dim); line-height: 1.5; }
.ac-kv { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding-top: 12px; border-top: 1px solid var(--surface-border); flex-wrap: wrap; }
.ac-kv > div { display: grid; gap: 3px; }
.ac-k { font-size: 0.8rem; color: var(--text-faint); }
.ac-v { color: var(--text); }
.ac-balance { font-family: "Space Grotesk", sans-serif; font-size: 2rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.ac-limit { display: grid; gap: 8px; }
.ac-limit-top { display: flex; justify-content: space-between; color: var(--text-dim); font-size: 0.86rem; }
.ac-limit-top strong { color: var(--text); }
.ac-two-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 14px; }
.ac-rl-mini, .ac-mini-list { display: grid; gap: 12px; }
.ac-rl-row { display: grid; grid-template-columns: 1fr 120px 44px; gap: 12px; align-items: center; font-size: 0.86rem; color: var(--text-dim); }
.ac-rl-row .num { text-align: end; color: var(--text); }
.ac-mini-row { display: grid; grid-template-columns: 1fr auto auto; gap: 14px; align-items: center; font-size: 0.86rem; }
.ac-mini-row strong { color: var(--text); font-variant-numeric: tabular-nums; }
.ac-danger { border-color: rgba(var(--danger-rgb), 0.35); }
.ac-danger .ac-panel-title { color: var(--danger-text); }
.ac-alert { display: flex; gap: 10px; align-items: center; padding: 12px 16px; border-radius: var(--radius-sm); border: 1px solid rgba(251, 191, 36, 0.35); background: rgba(251, 191, 36, 0.08); color: #fbbf24; font-size: 0.88rem; }
.ac-alert svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.ac-empty { display: grid; justify-items: center; gap: 6px; padding: 44px 20px; border: 1px dashed var(--surface-border); border-radius: var(--radius); text-align: center; color: var(--text-faint); }
.ac-empty .ac-icon { width: 34px; height: 34px; }
.ac-empty .ac-icon svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.4; }
.ac-empty-title { margin: 6px 0 0; color: var(--text); font-weight: 600; }
.ac-empty-line { margin: 0; font-size: 0.86rem; }

/* Charts. */
.ac-chart { padding: 18px 20px 14px; border-radius: var(--radius); border: 1px solid var(--surface-border); background: var(--surface); display: grid; gap: 10px; min-width: 0; }
.ac-chart-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.ac-chart-body { position: relative; min-width: 0; }
.ac-svg { display: block; max-width: 100%; height: auto; }
.ac-grid { stroke: rgba(255, 255, 255, 0.06); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ac-axis { fill: var(--text-faint); font-size: 11px; font-family: Inter, system-ui, sans-serif; }
.ac-bar { fill: var(--c, var(--accent)); }
.ac-hit { fill: transparent; }
.ac-hit:hover { fill: rgba(255, 255, 255, 0.04); }
.ac-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 0.8rem; color: var(--text-dim); }
.ac-tooltip { position: absolute; z-index: 5; pointer-events: none; display: grid; gap: 4px; min-width: 170px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: #10121c; border: 1px solid var(--surface-border); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); font-size: 0.8rem; color: var(--text-dim); }
.ac-tooltip strong { color: var(--text-faint); font-weight: 500; font-size: 0.74rem; }
.ac-tooltip b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.ac-tip-total { margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--surface-border); color: var(--text); }
.ac-seg { display: inline-flex; padding: 3px; gap: 2px; border-radius: 8px; border: 1px solid var(--surface-border); background: var(--surface); }
.ac-seg button { border: 0; background: transparent; color: var(--text-faint); border-radius: 6px; padding: 4px 8px; cursor: pointer; font: inherit; font-size: 0.8rem; display: inline-flex; align-items: center; }
.ac-seg button[aria-pressed="true"] { background: rgba(255, 255, 255, 0.08); color: var(--text); }
.ac-seg-text { margin-top: 10px; }

/* Menus, drawer, dialogs, toast. */
.ac-menu { position: absolute; z-index: 2000; min-width: 200px; padding: 6px; border-radius: 12px; background: #10121c; border: 1px solid var(--surface-border);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55); display: grid; }
.ac-menu button { text-align: start; border: 0; background: transparent; color: var(--text); font: inherit; font-size: 0.86rem; padding: 8px 10px; border-radius: 8px; cursor: pointer; display: flex; justify-content: space-between; gap: 12px; }
.ac-menu button:hover, .ac-menu button:focus-visible { background: rgba(255, 255, 255, 0.06); outline: none; }
.ac-menu button.is-danger { color: var(--danger-text); }
.ac-menu-sep { border: 0; border-top: 1px solid var(--surface-border); margin: 4px 0; }
.ac-tick { color: var(--accent); }
.ac-scrim { position: fixed; inset: 0; z-index: 2001; background: rgba(3, 4, 8, 0.6); backdrop-filter: blur(2px); }
.ac-drawer { position: fixed; z-index: 2002; top: 0; bottom: 0; inset-inline-end: 0; width: min(440px, 100vw); overflow-y: auto; padding: 18px 24px 30px;
  background: #0d0f18; border-inline-start: 1px solid var(--surface-border); box-shadow: -20px 0 50px rgba(0, 0, 0, 0.5); }
.ac-drawer h3 { margin: 4px 0 18px; font-family: "Space Grotesk", sans-serif; font-size: 1.25rem; color: var(--text); }
.ac-drawer-bar { display: flex; justify-content: flex-end; }
.ac-drawer-kicker { margin: 0; font-size: 0.74rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-faint); }
.ac-drawer-sub { margin: 20px 0 6px; font-size: 0.9rem; color: var(--text); }
.ac-dl { display: grid; grid-template-columns: 140px 1fr; gap: 10px 14px; margin: 0; font-size: 0.88rem; }
.ac-dl dt { color: var(--text-faint); }
.ac-dl dd { margin: 0; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.ac-modal { position: fixed; z-index: 2002; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(520px, calc(100vw - 32px)); max-height: calc(100vh - 48px); overflow-y: auto;
  border-radius: var(--radius); background: #0d0f18; border: 1px solid var(--surface-border); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6); display: grid; }
.ac-modal-head { display: flex; justify-content: space-between; align-items: center; padding: 18px 22px 6px; }
.ac-modal-head h3 { margin: 0; font-family: "Space Grotesk", sans-serif; font-size: 1.15rem; color: var(--text); }
.ac-modal-body { padding: 8px 22px 6px; display: grid; gap: 14px; }
.ac-modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 22px 20px; }
.ac-field { display: grid; gap: 6px; font-size: 0.84rem; color: var(--text-dim); }
.ac-field input, .ac-field select, .ac-field textarea, .ac-form input, .ac-prompt textarea, .ac-threshold input {
  font: inherit; font-size: 0.9rem; color: var(--text); background: rgba(255, 255, 255, 0.03); border: 1px solid var(--surface-border); border-radius: var(--radius-sm); padding: 9px 12px; width: 100%; color-scheme: dark; }
.ac-field input:focus, .ac-field select:focus, .ac-field textarea:focus, .ac-form input:focus, .ac-prompt textarea:focus { outline: none; border-color: var(--accent); }
.ac-hint { color: var(--text-faint); font-size: 0.78rem; line-height: 1.45; }
.ac-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ac-roles, .ac-colors { border: 0; padding: 0; margin: 0; display: grid; gap: 8px; }
.ac-roles legend, .ac-colors legend { font-size: 0.84rem; color: var(--text-dim); margin-bottom: 6px; padding: 0; }
.ac-role { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--radius-sm); border: 1px solid var(--surface-border); cursor: pointer; }
.ac-role:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.ac-role span { display: grid; gap: 2px; }
.ac-role strong { color: var(--text); font-size: 0.88rem; }
.ac-role small { color: var(--text-dim); font-size: 0.8rem; }
.ac-colors { grid-auto-flow: column; justify-content: start; }
.ac-colors input { position: absolute; opacity: 0; }
.ac-swatch { display: inline-block; width: 26px; height: 26px; border-radius: 8px; background: var(--c); border: 2px solid transparent; cursor: pointer; }
.ac-colors input:checked + .ac-swatch { border-color: var(--text); }
.ac-colors input:focus-visible + .ac-swatch { outline: 2px solid var(--accent); outline-offset: 2px; }
.ac-amounts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.ac-amount { position: relative; }
.ac-amount input { position: absolute; opacity: 0; }
.ac-amount span { display: grid; gap: 2px; padding: 12px; border-radius: var(--radius-sm); border: 1px solid var(--surface-border); cursor: pointer; }
.ac-amount strong { color: var(--text); }
.ac-amount small { color: var(--text-faint); font-size: 0.74rem; }
.ac-amount input:checked + span { border-color: var(--accent); background: var(--accent-soft); }
.ac-amount input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.ac-summary { padding-top: 12px; border-top: 1px solid var(--surface-border); }
.ac-summary dd { text-align: end; }
.ac-thresholds { display: grid; gap: 8px; }
.ac-threshold { display: grid; grid-template-columns: 110px 1fr auto; gap: 10px; align-items: center; color: var(--text-dim); font-size: 0.86rem; }
.ac-check { display: flex; gap: 8px; align-items: center; color: var(--text); font-size: 0.9rem; }
.ac-keybox { display: flex; gap: 10px; align-items: center; padding: 10px 12px; border-radius: var(--radius-sm); border: 1px solid var(--surface-border); background: rgba(255, 255, 255, 0.03); }
.ac-keybox code { flex: 1; min-width: 0; overflow-wrap: anywhere; font-size: 0.84rem; color: var(--text); }
.ac-code { margin: 0; padding: 12px 14px; border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.03); border: 1px solid var(--surface-border); color: var(--text-dim);
  font-size: 0.78rem; line-height: 1.55; white-space: pre-wrap; max-height: 260px; overflow-y: auto; }
.ac-toast { position: fixed; z-index: 2003; left: 50%; bottom: 28px; transform: translateX(-50%); padding: 10px 16px; border-radius: 999px; background: #10121c; border: 1px solid var(--surface-border); color: var(--text); font-size: 0.86rem; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); }

/* Toggles, tabs, form, agents. */
.ac-toggle-row { display: flex; justify-content: space-between; gap: 18px; align-items: flex-start; padding: 14px 0; border-top: 1px solid var(--surface-border); }
.ac-toggle-row:first-child { border-top: 0; padding-top: 0; }
.ac-toggle-row strong { color: var(--text); font-size: 0.92rem; }
.ac-switch { flex: none; width: 40px; height: 22px; border-radius: 999px; border: 1px solid var(--surface-border); background: rgba(255, 255, 255, 0.08); position: relative; cursor: pointer; padding: 0; }
.ac-switch span { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--text-dim); transition: transform 0.2s var(--ease), background 0.2s var(--ease); }
.ac-switch[aria-checked="true"] { background: var(--accent); border-color: var(--accent); }
.ac-switch[aria-checked="true"] span { transform: translateX(18px); background: #fff; }
.ac-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--surface-border); }
.ac-tabbtn { border: 0; background: none; color: var(--text-dim); font: inherit; font-size: 0.9rem; padding: 10px 14px; border-bottom: 2px solid transparent; cursor: pointer; }
.ac-tabbtn[aria-pressed="true"] { color: var(--text); border-bottom-color: var(--accent); }
.ac-form label { display: grid; gap: 6px; font-size: 0.84rem; color: var(--text-dim); }
.ac-form-actions { display: flex; justify-content: flex-end; }
.ac-build { text-align: center; display: grid; justify-items: center; gap: 4px; padding-top: 10px; }
.ac-build-title { margin: 0; font-family: "Space Grotesk", sans-serif; font-size: 1.7rem; color: var(--text); }
.ac-prompt { width: min(640px, 100%); margin-top: 14px; display: grid; gap: 10px; justify-items: end; }
.ac-prompt textarea { resize: vertical; }
.ac-templates { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.ac-template { text-align: start; display: grid; gap: 8px; align-content: start; padding: 16px 18px; border-radius: var(--radius); border: 1px solid var(--surface-border);
  background: var(--surface); color: var(--text-dim); font: inherit; font-size: 0.86rem; line-height: 1.45; cursor: pointer; min-height: 140px; }
.ac-template strong { color: var(--text); font-size: 0.95rem; }
.ac-template:hover { border-color: var(--accent); }
.ac-template-foot { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: auto; }
.ac-tools { display: flex; flex-wrap: wrap; gap: 6px; }
.ac-tool { font-size: 0.72rem; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--surface-border); color: var(--text); }
.ac-tag { font-size: 0.74rem; color: var(--text-faint); }
.ac-center { text-align: center; color: var(--text-dim); margin: 0; }

@media (max-width: 640px) {
  .ac-search { min-width: 0; flex: 1 1 100%; }
  .ac-amounts { grid-template-columns: 1fr 1fr; }
  .ac-grid2 { grid-template-columns: 1fr; }
  .ac-dl { grid-template-columns: 1fr; }
  .ac-rl-row { grid-template-columns: 1fr 80px 40px; }
}

/* ---------- Language picker in the footer (Alden, 2026-11-06) ----------
   Like claude.ai: its own row under the footer links, at the bottom right,
   "globe  English  v" in a bordered box, opening upwards. js/lang-switch.js
   puts it here when its script tag says data-place="footer". */
.footer-lang {
  display: flex;
  justify-content: flex-end;
  margin-top: 22px;
  padding-top: 22px;
  border-top: 1px solid var(--surface-border);
}
.footer-lang .nav-lang { position: relative; inset: auto; top: auto; margin: 0; }
.footer-lang .lang-toggle {
  flex-direction: row;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 12px;
  background: transparent;
  font-size: 0.9rem;
  line-height: 1.2;
}
.footer-lang .lang-toggle svg { width: 16px; height: 16px; }
.footer-lang .lang-toggle .lang-chevron { width: 14px; height: 14px; transition: transform 0.2s var(--ease); }
.footer-lang .lang-toggle[aria-expanded="true"] .lang-chevron { transform: rotate(180deg); }
.footer-lang .lang-menu { top: auto; bottom: calc(100% + 10px); transform: translateY(6px); }
.footer-lang .lang-menu.open { transform: translateY(0); }
/* Phones: the footer above it is centred, so the picker is too. */
@media (max-width: 760px) {
  .footer-lang { justify-content: center; }
  .footer-lang .lang-menu { right: auto; left: 50%; transform: translate(-50%, 6px); }
  .footer-lang .lang-menu.open { transform: translate(-50%, 0); }
}

/* ---------- Account pages: passkey list and "Confirm it's you" (2026-11-06) */
.portal-list { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; }
.portal-list .portal-row-main { display: flex; flex-direction: column; gap: 2px; }
.reauth-form { margin-top: 16px; }
.reauth-form label { display: flex; flex-direction: column; gap: 6px; }

/* Sign-in page: "Download the desktop app" under the form (web sign-ins only) */
.login-desktop { display: flex; justify-content: center; margin-top: 18px; }
.login-desktop[hidden] { display: none; }
.login-desktop-btn { display: inline-flex; align-items: center; gap: 8px; }
.login-apple { width: 21px; height: 21px; flex: none; margin-top: -3px; }

/* Sign-in page: "Download for macOS" below the box, apart from it, like
   claude.ai's sign-in (owner, 2026-11-07). The Apple mark is the solid
   glyph (Simple Icons, CC0). */
.login-stage { display: block; }
.login-stage > .login-desktop { display: flex; justify-content: center; margin-top: 60px; }
.login-stage > .login-desktop[hidden] { display: none; }
.login-stage .login-desktop-btn { white-space: nowrap; gap: 10px; }
.login-stage .login-apple { width: 17px; height: 17px; margin-top: -3px; }
.login-stage .login-apple, .login-stage .login-apple path { fill: currentColor !important; stroke: none !important; }
