/**
 * Queantic brand layer — fonts + CSS vars shared by dashboard, settings,
 * shared dashboards, and public layout. Complements compiled styles.css
 * (Tailwind indigo remapped to lander purple).
 */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap');

:root {
  --q-primary: #5817ea;
  --q-primary-hover: #240b63;
  --q-primary-mid: #4611c0;
  --q-lavender: #ece7fd;
  --q-lavender-mid: #d6cbfb;
  --q-lavender-deep: #a893ea;
  --q-surface: #f5f5f7;
  --q-surface-raised: #ffffff;   /* the "white block" */
  --q-deep: #240b63;             /* the deep-purple block */
  --q-on-primary: #ffffff;
  --q-text: #1c1c22;
  --q-muted: #5f5f6d;
  --q-border: #e8e8ee;
  --q-font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
  --q-font-display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --q-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Tertiary text. Was typed as the literal #8a8a99 in four places here and in
     a dozen templates; tokenised so the palette is complete and greppable. */
  --q-text-3: #8a8a99;

  /* Aliases, not renames.
     The palette already carried these roles under lander-era names, and
     renaming would rewrite 700 lines of CSS plus every template for no visual
     change. The aliases let new work read in role terms — tint, tint-strong,
     violet-soft — while the originals keep every existing rule working. */
  --q-tint:        var(--q-lavender);       /* pale card / pill background   */
  --q-tint-strong: var(--q-lavender-mid);   /* tinted banners, hover tint    */
  --q-violet:      var(--q-primary);
  --q-violet-deep: var(--q-deep);
  --q-violet-soft: var(--q-lavender-deep);  /* line-art + text on deep       */
  --q-line:        var(--q-border);
  --q-ink:         var(--q-text);
  --q-bg-2:        #f0f0f4;                 /* neutral half of a compare pair */

  /* The page frame. Broadridge insets every panel from the page ground by a
     fixed gutter; this is that gutter, as one number so the header, the hero
     and every section cannot drift apart. */
  --q-inset: 64px;
  --q-panel: 1312px;   /* 1440 - 2x inset */
}

/* The inset is one number so the header, banner, body and footer cannot drift
   apart. It has to shrink on narrow screens, and it has to shrink HERE so they
   all keep agreeing — a per-component mobile override is how they drift. */
@media (max-width: 1000px) { :root { --q-inset: 40px; } }
@media (max-width: 640px)  { :root { --q-inset: 20px; } }

html {
  font-family: var(--q-font-sans);
  color: var(--q-text);
}

body {
  font-family: var(--q-font-sans);
  background-color: var(--q-surface);
  color: var(--q-text);
}

h1, h2, h3, .font-display {
  font-family: var(--q-font-display);
}

code, kbd, pre, .font-mono {
  font-family: var(--q-font-mono);
}

/* ---- Public chrome (lander-identical header + full-bleed footer) ---- */
/* One frame for the whole site.
 *
 * The shell was 1216px/32px while the lander's sections were 1280px and the
 * body container is 1312px/64px — three different frames, which is why the
 * logo never quite lined up with the page title beneath it. Now every element
 * on a public page resolves to the same left edge. */
.q-chrome-shell {
  max-width: var(--q-panel);
  margin: 0 auto;
  padding: 0 var(--q-inset);
  box-sizing: border-box;
}
@media (max-width: 900px) {
  .q-chrome-shell { padding: 0 20px; }
}
.q-chrome-top {
  background: #240b63;
}
.q-chrome-utility {
  padding: 10px 0;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 22px;
  font-size: 13px;
  line-height: 1;
}
.q-chrome-utility a {
  color: #ffffff;
  text-decoration: none;
  opacity: 0.92;
}
.q-chrome-utility a:hover {
  opacity: 1;
}
.q-chrome-sep {
  color: rgba(255, 255, 255, 0.35);
}
.q-chrome-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: #ffffff;
  border-bottom: 1px solid var(--q-border);
}
.q-chrome-bar {
  background: #ffffff;
}
.q-chrome-bar-inner {
  min-height: 72px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.q-chrome-logo {
  display: flex;
  align-items: center;
  padding: 18px 0;
}
.q-chrome-logo img {
  display: block;
  height: 28px;
  width: auto;
  image-rendering: -webkit-optimize-contrast;
}
.q-chrome-nav {
  display: flex;
  align-items: center;
  gap: 34px;
  flex-wrap: wrap;
  font-size: 16px;
}
.q-chrome-nav a {
  color: var(--q-text);
  text-decoration: none;
}
.q-chrome-cta-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.q-chrome-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 11px 22px;
  background: var(--q-primary);
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  border-radius: 4px;
  white-space: nowrap;
}
.q-chrome-ham {
  display: none;
  align-items: center;
  justify-content: center;
  width: 56px;
  border: none;
  background: transparent;
  color: var(--q-text);
  cursor: pointer;
}
.q-chrome-ham svg {
  width: 24px;
  height: 24px;
}
.q-chrome-mobile {
  display: none;
  flex-direction: column;
  background: #ffffff;
  border-top: 1px solid var(--q-border);
}
.q-chrome-mobile.q-open {
  display: flex;
}
.q-chrome-mobile a {
  padding: 16px 32px;
  color: var(--q-text);
  font-size: 16px;
  border-bottom: 1px solid var(--q-border);
  text-decoration: none;
}
.q-chrome-mobile a:hover {
  color: var(--q-primary);
}
@media (max-width: 800px) {
  .q-chrome-nav { display: none !important; }
  .q-chrome-ham { display: flex; }
  .q-chrome-cta { display: none; }
}

/* ---- Footer ------------------------------------------------------------
 *
 * Was a thin white list with grey links — visually identical to the page it
 * sat on, so the site simply stopped rather than ended. A full-bleed deep
 * block gives the page a floor, and it is the one place the site can afford
 * to carry the whole index without competing with the content above it.
 * ------------------------------------------------------------------------- */
.q-chrome-footer {
  background: var(--q-deep);
  color: #fff;
  margin-top: 0;
  padding: 64px 0 28px;
  width: 100%;
  border-top: 0;
}
.q-chrome-footer-inner {
  background: transparent;
  padding: 0;
}
.q-chrome-footer-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
}
.q-chrome-footer-brand {
  flex: 1 1 100%;
  min-width: 0;
}
.q-chrome-footer-brand img {
  display: block;
  height: 24px;
  width: auto;
  margin-bottom: 16px;
  /* The wordmark is a dark PNG and the footer is now dark, so it was rendering
     purple-on-purple and all but invisible. brightness(0) flattens it to solid
     black, invert(1) takes it to solid white — one filter, no new asset, and
     it stays correct if the artwork is ever replaced. */
  filter: brightness(0) invert(1);
}
.q-chrome-footer-brand p {
  font-size: 15px;
  line-height: 1.6;
  color: var(--q-lavender);
  max-width: 34ch;
  margin: 0;
}
.q-chrome-footer-grid > div:not(.q-chrome-footer-brand) {
  flex: 1 1 140px;
  min-width: 0;
}
/* Group headings read as headings, not as micro-labels: Broadridge's are the
   same size as the links under them, which is what stops a four-column link
   list looking like a spreadsheet. */
.q-chrome-footer h3 {
  font-family: var(--q-font-sans);
  font-size: 16px;
  letter-spacing: 0;
  text-transform: none;
  color: #fff;
  margin: 0 0 16px;
  font-weight: 500;
}
.q-chrome-footer-links {
  display: grid;
  gap: 11px;
  font-size: 15px;
}
.q-chrome-footer-links a {
  color: var(--q-lavender-mid);
  text-decoration: none;
}
.q-chrome-footer-links a:hover { color: #fff; }

/* Legal row — the links that have to exist but should not compete. */
.q-chrome-footer-legal {
  margin-top: 44px;
  padding-top: 22px;
  border-top: 1px solid rgba(168, 147, 234, .25);
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  font-size: 13px;
}
.q-chrome-footer-legal a { color: var(--q-lavender-mid); text-decoration: none; }
.q-chrome-footer-legal a:hover { color: #fff; }

.q-chrome-footer-bottom {
  margin-top: 22px;
  padding-top: 0;
  border-top: 0;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--q-lavender-deep);
}
.q-chrome-footer-meta {
  font-family: var(--q-font-mono);
}

@media (min-width: 900px) {
  .q-chrome-footer-brand {
    flex: 1 1 220px;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
 * Dashboard brand layer  (body.q-dash — logged-in dashboard + public shares)
 *
 * Applied as a layer rather than by rewriting the markup, because ~28 lines of
 * JS in main.js and side.js locate elements by their Tailwind classes
 * (e.g. 'div.bg-white\/80.rounded-xl.p-5.shadow-sm'). Removing those classes
 * would silently break modal detection, card scraping and the browser/OS
 * breakdown readers. Restyling them leaves every selector intact.
 * ══════════════════════════════════════════════════════════════════════════ */

.q-dash {
  background: var(--q-surface);
  color: var(--q-text);
}

/* ---- Hero ------------------------------------------------------------- */
.q-dash-hero {
  position: relative;
  overflow: hidden;
  background: #240b63;
  color: #fff;
  padding: clamp(28px, 4vw, 52px);
  margin-bottom: 24px;
}
.q-dash-hero::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 38%;
  background: rgba(88, 23, 234, 0.32);
  clip-path: polygon(40% 0, 100% 0, 100% 100%, 0 100%);
}
.q-dash-hero > * { position: relative; z-index: 1; }
/* One pill. Four pages had each re-typed this same mono-uppercase chip with a
   different margin; the margins are the only thing that actually differed.
   (.q-st-badge is NOT one of these — its colour carries status meaning.) */
.q-pill,
.q-dash-eyebrow,
.q-tpl-pill,
.q-demo-pill,
.q-ps-tag {
  display: inline-block;
  background: #5817ea;
  color: #fff;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 9px;
  margin-bottom: 18px;
}
.q-tpl-pill  { margin-bottom: 12px; }
.q-demo-pill { margin-bottom: 22px; }
/* The partner signup uses the tinted treatment on light panels. */
.q-ps-tag    { background: var(--q-lavender); color: var(--q-primary); margin: 0 0 18px; }
.q-dash-hero h1 {
  font-family: var(--q-font-display);
  font-size: clamp(34px, 6vw, 68px);
  line-height: 0.92;
  font-weight: 700;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  margin: 0;
  color: #fff;
}
.q-dash-hero p {
  font-size: clamp(15px, 1.5vw, 18px);
  line-height: 1.5;
  color: #d6cbfb;
  margin: 14px 0 0;
  max-width: 62ch;
}

/* ---- Stat tiles ------------------------------------------------------- */
/* dt/dd are the tile label and value in both dashboards. */
.q-dash dt {
  font-family: var(--q-font-mono) !important;
  font-size: 11px !important;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500 !important;
  color: #8a8a99 !important;
}
.q-dash dd {
  font-family: var(--q-font-display) !important;
  font-size: clamp(28px, 3vw, 40px) !important;
  line-height: 1 !important;
  font-weight: 700 !important;
  /* -0.04em (the display-heading value) pulled every comma hard into the next digit at
     40px+, which read as though the thousands separators had no space after them.
     Figures need far less tracking than headlines. */
  letter-spacing: -0.01em;
  /* Tabular figures so columns of numbers align, and so a comma always occupies the
     same advance width. */
  font-variant-numeric: tabular-nums;
  color: var(--q-text) !important;
  /* Grid and flex children default to min-width:auto, so a long value like
     1,519,779,000 forced its track wider than the tile and bled over the edge.
     min-width:0 lets it shrink; the rest degrades gracefully instead of overflowing. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Same escape hatch one level up — without this the tile itself refuses to shrink. */
.stats-grid > div,
.stats-grid > div dd,
.q-dash .stats-grid > div,
.q-dash [data-q-tile] { min-width: 0; }

/* The tile surface: flat white on a hairline, no translucency, no rounding.
   Targeted by attribute so the class list itself is left untouched. */
/* Scoped to legacy tiles only: without :not(.q-block) this forces a white background
   onto every coloured block the redesign introduces. */
.q-dash [class*="bg-white/80"]:not(.q-block) {
  background: var(--q-surface-raised, #ffffff) !important;
  border: 1px solid var(--q-border);
}
.q-dash .stats-grid > div { padding: 26px 24px; }

/* ---- Card headings ---------------------------------------------------- */
.q-dash h3.text-lg {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  color: #8a8a99;
}

/* ---- Buttons ---------------------------------------------------------- */
.q-dash .bg-indigo-600,
.q-dash button.bg-indigo-600 {
  border-radius: 0;
}
.q-dash .bg-indigo-600:hover { background-color: #240b63 !important; }

/* ---- Section headings in the page body -------------------------------- */
.q-dash h2 {
  font-family: var(--q-font-display);
  letter-spacing: -0.025em;
}

/* Hero actions: the measured site is the primary action, Queantic is a byline. */
.q-dash-hero-actions {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 26px;
}
.q-dash-hero-cta,
.q-dash-hero-secondary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  font-size: 15px;
  padding: 13px 24px;
  text-decoration: none;
  border: none;
  cursor: pointer;
}
.q-dash-hero-cta {
  background: #ffffff;
  color: #240b63;
}
.q-dash-hero-cta:hover { background: #5817ea; color: #ffffff; }
.q-dash-hero-secondary {
  background: rgba(255,255,255,0.16);
  color: #ffffff;
  border: 1px solid rgba(255,255,255,0.22);
}
.q-dash-hero-secondary:hover { background: rgba(255,255,255,0.24); }
.q-dash-hero-by {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #a893ea;
  text-decoration: none;
}
.q-dash-hero-by:hover { color: #ffffff; }

/* ══════════════════════════════════════════════════════════════════════════
 * Block primitives — the lander's composition vocabulary, made reusable.
 *
 * The system's rule: the gutter IS the border. Blocks carry no edges because
 * each is a saturated field separated by 20px of surface. Lifted out of
 * landing-shell.php so the dashboard composes from the same pieces rather
 * than approximating them.
 * ══════════════════════════════════════════════════════════════════════════ */

.q-grid { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 640px)  { .q-grid[data-cols="4"], .q-grid[data-cols="3"] { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) {
  .q-grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
  .q-grid[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
  /* The asymmetric stat row: one wide block leading two narrow ones. */
  .q-grid[data-cols="2-1-1"] { grid-template-columns: 2fr 1fr 1fr; }
}

.q-block {
  position: relative;
  overflow: hidden;
  padding: 32px 28px;
  min-width: 0;                 /* never let a long figure widen the track */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.q-block--deep     { background: var(--q-deep, #240b63); color: #ffffff; }
.q-block--primary  { background: var(--q-primary); color: #ffffff; }
.q-block--lavender { background: var(--q-lavender); color: var(--q-text); }
.q-block--white    { background: var(--q-surface-raised, #ffffff); color: var(--q-text); }

/* Diagonal accent, as on the lander's hero and +87% block. */
.q-diag::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 40%;
  background: rgba(88, 23, 234, 0.32);
  clip-path: polygon(44% 0, 100% 0, 100% 100%, 0 100%);
  pointer-events: none;
}
.q-block > * { position: relative; z-index: 1; }
/* .q-diag is also used on its own, not only via .q-block. Its ::after shard is
   pointer-events:none but still PAINTS over content, so children need lifting
   there too or the text sits under the diagonal. */
.q-diag > * { position: relative; z-index: 1; }

/* The 34px notched mark that opens each lander card. */
.q-mark {
  width: 34px; height: 34px;
  background: var(--q-primary);
  clip-path: polygon(0 0, 100% 0, 100% 58%, 58% 100%, 0 100%);
  margin-bottom: 26px;
}
.q-block--deep .q-mark, .q-block--primary .q-mark { background: #ffffff; }

/* Figures. --xl is the lander's statement numeral; the others step down. */
.q-num {
  font-family: var(--q-font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow-wrap: anywhere;
}
.q-num--xl { font-size: clamp(56px, 10vw, 148px); letter-spacing: -0.04em; }
.q-num--lg { font-size: clamp(40px, 5vw, 68px); }
.q-num--md { font-size: clamp(30px, 3.4vw, 44px); }

/* Mono micro-label. On a dark field use --q-lavender-mid: #a893ea is only ~3.6:1
   at 11px, which fails contrast for text this small. */
.q-label {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-muted);
  display: block;
}
.q-block--deep .q-label,
.q-block--primary .q-label { color: var(--q-lavender-mid, #d6cbfb); }

/* Sub-line under a figure — the exact value beneath a compact one. */
.q-sub {
  font-family: var(--q-font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--q-muted);
  margin-top: 12px;
}
.q-block--deep .q-sub,
.q-block--primary .q-sub { color: var(--q-lavender-mid, #d6cbfb); }

/* Hero: statement numeral beside the copy, the lander's +87% composition. */
.q-dash-hero-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
}
.q-dash-hero-figure { min-width: 0; }
.q-dash-hero-copy   { min-width: 0; }
.q-dash-hero-copy h1 { font-size: clamp(28px, 3.4vw, 46px); }
.q-dash-hero-facts {
  display: flex;
  gap: 36px;
  margin: 24px 0 0;
  padding-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.25);
}
.q-dash-hero-facts dt { margin-bottom: 6px; }
.q-dash-hero-facts dd {
  margin: 0;
  font-family: var(--q-font-display);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: #ffffff;
}

/* Mobile/desktop split bar — flat, no radius, sits under the percentage. */
.q-split {
  height: 4px;
  background: var(--q-lavender-mid);
  margin-top: 14px;
  overflow: hidden;
}
.q-split > span { display: block; height: 100%; background: var(--q-primary); }
.q-block--primary .q-split { background: rgba(255,255,255,0.28); }
.q-block--primary .q-split > span { background: #ffffff; }

/* Inside a block the legacy dt/dd colour overrides must yield to the block's own
   foreground, or a white label lands on a white field. */
.q-block--deep dt, .q-block--primary dt,
.q-block--deep dd, .q-block--primary dd { color: #ffffff !important; }
.q-block--deep .q-sub, .q-block--primary .q-sub { color: var(--q-lavender-mid) !important; }
.q-block--lavender dd { color: var(--q-text) !important; }

/* Charts: geographic wide, device narrow. Blocks, not cards. */
.q-charts-grid { grid-template-columns: 1fr; }
@media (min-width: 1000px) { .q-charts-grid { grid-template-columns: 1fr 2fr; } }
.q-chart-block { display: block; padding: 28px; }
.q-chart-block canvas { max-width: 100%; }

/* Breakdown row: name left, figure right, distribution bar beneath.
   The percentage used to be a grey parenthetical only — the bar makes the shape of
   the distribution readable without reading any numbers. */
.q-dash .q-stat-row { position: relative; flex-wrap: wrap; padding-bottom: 12px !important; }
.q-stat-bar {
  flex: 0 0 100%;
  height: 3px;
  background: var(--q-lavender-mid);
  margin-top: 8px;
  display: block;
}
.q-stat-bar > span { display: block; height: 100%; background: var(--q-primary); }

.q-ts-head { margin-bottom: 18px; }
.q-ts-head h3 { margin: 0; }
.q-ts-head .q-sub { margin-top: 6px; }

/* Control strip: fills rather than strokes. An outlined input on a flat field reads as
   a leftover card; a recessed fill belongs to the system. */
.q-dash .q-input {
  border: none !important;
  background: var(--q-surface) !important;
  border-radius: 0 !important;
  height: 44px;
  font-family: var(--q-font-sans);
  color: var(--q-text);
}
.q-dash .q-input:focus { outline: 2px solid var(--q-primary); outline-offset: -2px; box-shadow: none !important; }
.q-dash .q-input::placeholder { font-family: var(--q-font-mono); font-size: 13px; color: var(--q-muted); }

.q-dash .q-btn-ghost {
  border: none !important;
  background: transparent !important;
  border-bottom: 2px solid var(--q-deep) !important;
  border-radius: 0 !important;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-text) !important;
}
.q-dash .q-btn-ghost:hover { border-bottom-color: var(--q-primary) !important; color: var(--q-primary) !important; }

.q-dash .q-controls { align-items: center; }

/* ══════════════════════════════════════════════════════════════════════════
 * PORTAL CHROME — the utility strip above every logged-in page.
 *
 * This block lived in an inline <style> inside template-parts/portal.php,
 * where it styled that one page. template-parts/agency.php uses the same five
 * class names and got none of it: the "Your sites" header rendered as bare
 * text on a white background, and its way back to the portal was an unstyled,
 * effectively invisible link. Every page that adopts the chrome markup now
 * gets the chrome styling, which is what having class names is for.
 *
 * Written against the brand tokens rather than the literals it used to carry,
 * so the strip tracks the palette instead of drifting from it.
 * ══════════════════════════════════════════════════════════════════════════ */

.q-portal-utility { background: var(--q-deep); }
.q-portal-shell { max-width: 1280px; margin: 0 auto; padding: 0 32px; }
.q-portal-utility .q-portal-shell {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; min-height: 52px; flex-wrap: wrap;
}
/* Unused as of now — partials/portal-strip.php renders no wordmark in the
   top-left corner. Kept so putting one back is a markup change only. */
.q-portal-logo {
  display: inline-flex; align-items: center;
  font-family: var(--q-font-display);
  font-size: 1rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: #ffffff; text-decoration: none;
}
.q-portal-logo span { display: block; line-height: 1; }
/* The left-hand slot: whatever the page passes as $strip_extra (Mail's
   view switcher, the portal's site switcher). Empty on pages that pass
   nothing, in which case the links below are the shell's only child. */
.q-portal-extra { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* margin-left:auto, not the shell's space-between: with the wordmark gone the
   links can be the shell's only child, and space-between puts a lone child at
   the start. Without this the whole nav jumps to the left edge. */
.q-portal-links { display: flex; align-items: center; gap: 22px; margin-left: auto; }
.q-portal-utility a { color: #d6cbfb; text-decoration: none; font-size: 14px; }
.q-portal-utility a:hover { color: #ffffff; }
.q-portal-utility .q-portal-signout { color: #a893ea; }
/* The page you are already on: shown, not linked. A link back to the current
   page reads as a way out and isn't one. */
.q-portal-here { color: #ffffff; font-size: 14px; font-weight: 600; }

/* Site switcher — chrome, not page body, so it reads as applying to
   everything below it. Solo accounts only: an agency picks a site from the
   client list, where the choice has context. */
.q-portal-switch { display: inline-flex; align-items: center; gap: 6px; margin: 0 4px 0 0; }
.q-portal-switch-label {
  color: rgba(255,255,255,.62); font-size: 12px;
  letter-spacing: .04em; text-transform: uppercase;
}
.q-portal-switch select {
  background: rgba(255,255,255,.10); color: #fff; border: 1px solid rgba(255,255,255,.22);
  border-radius: 6px; padding: 4px 26px 4px 8px; font-size: 13px; line-height: 1.4;
  max-width: 220px; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23ffffff' fill-opacity='.7' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 9px;
}
.q-portal-switch select:hover { background-color: rgba(255,255,255,.16); }
.q-portal-switch select option { color: var(--q-text); background: #fff; }

/* ── Portal cards ──────────────────────────────────────────────────────── */

.q-portal-title {
  font-size: clamp(30px, 4vw, 52px); font-weight: 700;
  letter-spacing: -0.04em; text-transform: uppercase; margin: 0 0 8px;
}
.q-portal-lede { font-size: 16px; line-height: 1.6; color: var(--q-muted); margin: 0 0 28px; max-width: 60ch; }
.portal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.q-portal-card {
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 32px 28px; min-height: 240px; text-decoration: none;
}
/* The lander's data-hover script is scoped to .q-chrome-*, so page-body cards
   need a real :hover. Each card passes its own colour in --q-hover-bg. */
.q-portal-card:hover { background: var(--q-hover-bg) !important; }
.q-portal-card .q-portal-mark {
  width: 34px; height: 34px; margin-bottom: 28px;
  clip-path: polygon(0 0, 100% 0, 100% 58%, 58% 100%, 0 100%);
}
.q-portal-card h2 { font-size: 22px; font-weight: 500; letter-spacing: -0.02em; margin: 0 0 12px; }
.q-portal-card p { font-size: 15px; line-height: 1.6; margin: 0; }
.q-portal-card .q-portal-cta { margin-top: 22px; font-size: 15px; font-weight: 600; }
.q-portal-label {
  font-family: var(--q-font-mono); font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; display: block; margin-bottom: 14px; opacity: 0.75;
}

@media (max-width: 1024px) { .portal-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 768px) {
  .portal-grid { grid-template-columns: 1fr; gap: 16px; }
  .portal-wrap { padding: 32px 16px !important; }
  .q-portal-shell { padding: 0 18px; }
}

/* ══════════════════════════════════════════════════════════════════════════
 * PUBLIC MARKETING SURFACE
 *
 * Everything below is scoped to .q-public-page, the body class
 * template-parts/layout-public.php has always appended and which, until now,
 * matched nothing at all. It is a free hook: no existing rule targets it, so
 * nothing here can collide with the dashboard, the portal or the lander.
 * ══════════════════════════════════════════════════════════════════════════ */

/* ---- Typography ---------------------------------------------------------
 *
 * THIS IS THE FIX FOR THIRTY PAGES.
 *
 * Every inner page opens with the identical string
 *     class="container mx-auto px-4 py-12 max-w-5xl prose prose-lg"
 * and `prose` has never existed: @tailwindcss/typography is not a dependency
 * and tailwind.config.js has `plugins: []`. So `.prose` compiles to nothing,
 * and every heading, paragraph, list and blockquote on /about, /terms, every
 * /features/*, every /compare/* and the rest has been rendering at browser
 * default — 16px Times-ish rhythm with no measure and no scale.
 *
 * They did not merely look like unstyled documents. They were unstyled
 * documents. This gives the class a meaning rather than asking thirty files
 * to change.
 *
 * `.container` gets fixed here too. Tailwind emitted it with no `center` or
 * `padding` config and no lg/xl tiers, so it tops out at 768px — narrower
 * than the 1216px header above it. That mismatch is why the inner pages read
 * as a column of text adrift on grey.
 * ------------------------------------------------------------------------- */

/* Align the body column with the banner above it.
 *
 * Tailwind emitted `.container` with no `center`/`padding` config and no
 * lg/xl tiers, so it tops out at 768px while the header above it is 1216px —
 * body copy was literally narrower than its own chrome. Matching the banner's
 * inner width and inset means the page title and the first paragraph share a
 * left edge, which is most of what makes a page look composed.
 *
 * The `max-w-*` utility on the same element would otherwise win, so it is
 * overridden here rather than removed from thirty templates. */
.q-public-page .container {
  max-width: var(--q-panel) !important;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--q-inset);
  padding-right: var(--q-inset);
  box-sizing: border-box;
}

/* Prose is read, not scanned, so it keeps a measure inside that wider frame
   rather than running the full 1312px. Two columns of furniture can sit beside
   it later; a 1300px line of body text can never be made readable. */
.q-public-page .container.prose > * { max-width: 72ch; }
/* Text holds a 72ch measure; structural blocks are allowed to breathe
   wider so the page does not read as a column adrift on the frame. */
.q-public-page .container.prose > .q-note-pair,
.q-public-page .container.prose > .q-sample,
.q-public-page .container.prose > table,
.q-public-page .container.prose > .q-note,
.q-public-page .container.prose > div:has(> .q-note, > .q-note-pair, > .q-sample, > table) { max-width: 940px; }
/* Text keeps its measure wherever it sits inside those wrappers. */
.q-public-page .container.prose .q-note > :is(p, ul, ol, h3, h4) { max-width: 72ch; }

@media (max-width: 900px) {
  .q-public-page .container { padding-left: 20px; padding-right: 20px; }
}

.q-public-page .prose {
  color: var(--q-muted);
  font-size: 17px;
  line-height: 1.65;
}

.q-public-page .prose h1,
.q-public-page .prose h2,
.q-public-page .prose h3,
.q-public-page .prose h4 {
  font-family: var(--q-font-display);
  color: var(--q-text);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* One H2 size site-wide. The old pages ranged from 27px to 68px because each
   picked its own Tailwind text-* class; a single scale is most of what makes
   a set of pages read as one site. */
.q-public-page .prose h2 {
  font-size: clamp(26px, 2.6vw, 34px);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin: 56px 0 18px;
}
.q-public-page .prose h2:first-child { margin-top: 0; }

.q-public-page .prose h3 {
  font-size: 21px;
  font-weight: 500;
  line-height: 1.3;
  margin: 36px 0 12px;
}

.q-public-page .prose h4 {
  font-size: 17px;
  font-weight: 600;
  margin: 28px 0 10px;
}

.q-public-page .prose p { margin: 0 0 18px; }
.q-public-page .prose > :last-child { margin-bottom: 0; }

.q-public-page .prose a {
  color: var(--q-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.q-public-page .prose a:hover { color: var(--q-primary-hover); }

/* A button inside prose is a button, not a link. The rule above is (0,2,1) and
   .q-btn is (0,1,0), so without this the label renders purple-on-purple with
   an underline — invisible. */
.q-public-page .prose a.q-btn { color: #fff; text-decoration: none; }
.q-public-page .prose a.q-btn:hover { color: #fff; }
.q-public-page .prose a.q-btn--ghost { color: var(--q-primary); }
.q-public-page .prose a.q-btn--ghost:hover { color: var(--q-violet-deep); }

.q-public-page .prose strong { color: var(--q-text); font-weight: 600; }

.q-public-page .prose ul,
.q-public-page .prose ol { margin: 0 0 20px; padding-left: 0; list-style: none; }
.q-public-page .prose li { position: relative; padding-left: 22px; margin: 0 0 9px; }

/* A drawn marker rather than list-style, so the bullet is on the palette and
   lines up with the text rather than hanging wherever the UA decides. */
.q-public-page .prose ul > li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  background: var(--q-lavender-deep);
}
.q-public-page .prose ol { counter-reset: q-ol; }
.q-public-page .prose ol > li { counter-increment: q-ol; }
.q-public-page .prose ol > li::before {
  content: counter(q-ol);
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--q-font-mono);
  font-size: 12px;
  color: var(--q-primary);
  line-height: 1.75;
}
.q-public-page .prose li > ul,
.q-public-page .prose li > ol { margin: 9px 0 0; }

.q-public-page .prose blockquote {
  margin: 28px 0;
  padding: 2px 0 2px 20px;
  border-left: 3px solid var(--q-lavender-mid);
  color: var(--q-text);
  font-size: 18px;
  line-height: 1.55;
}
.q-public-page .prose blockquote p:last-child { margin-bottom: 0; }

.q-public-page .prose code {
  font-family: var(--q-font-mono);
  font-size: 0.88em;
  background: var(--q-lavender);
  color: var(--q-primary-hover);
  padding: 2px 5px;
}
.q-public-page .prose pre {
  background: var(--q-deep);
  color: #fff;
  padding: 20px 22px;
  overflow-x: auto;
  margin: 0 0 22px;
  font-size: 13px;
  line-height: 1.6;
}
.q-public-page .prose pre code { background: none; color: inherit; padding: 0; font-size: inherit; }

.q-public-page .prose hr { border: 0; border-top: 1px solid var(--q-border); margin: 44px 0; }

.q-public-page .prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 24px;
  font-size: 15px;
}
.q-public-page .prose th,
.q-public-page .prose td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--q-border);
  vertical-align: top;
}
.q-public-page .prose th {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--q-muted);
  font-weight: 500;
}

@media (max-width: 720px) {
  .q-public-page .prose { font-size: 16px; }
  .q-public-page .prose h2 { margin-top: 40px; }
}

/* ---- The page frame -----------------------------------------------------
 *
 * Broadridge's whole page reads as panels floating on a ground, inset by a
 * fixed gutter, rather than full-bleed bands butted against each other. That
 * one device is most of the difference between "a site" and "a document with
 * a header on it".
 * ------------------------------------------------------------------------- */

.q-section {
  max-width: var(--q-panel);
  margin: 0 auto 20px;
  padding: 0 var(--q-inset);
}
.q-section:last-child { margin-bottom: 0; }

/* ---- Page banner --------------------------------------------------------
 *
 * Title left in a LIGHT weight, one-paragraph lede right. Light weight is the
 * part people get wrong: the instinct is to make a page title bold, and the
 * reason Broadridge's inner pages feel composed rather than shouted is that
 * the biggest text on the page is also the thinnest.
 *
 * Three variants, rotated by section so consecutive pages in a journey do not
 * all open identically. Emitted by layout-public.php from three optional
 * locals, so a page opts in with three lines and no markup.
 * ------------------------------------------------------------------------- */

/* The banner is full-bleed (as Broadridge's are) but its CONTENT box is
   identical to the body container below it — same max-width, same inset, same
   box-sizing — so the page title and the first paragraph share a left edge.
   Putting the inset on the outer element instead would misalign them by
   exactly the inset, which is the kind of 64px drift that reads as sloppiness
   without being obviously wrong. */
.q-page-banner {
  position: relative;
  overflow: hidden;
  padding: 58px 0 62px;
  margin: 0 0 20px;
}
.q-page-banner-inner {
  max-width: var(--q-panel);
  margin: 0 auto;
  padding: 0 var(--q-inset);
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 40px;
  align-items: start;
}
.q-page-banner h1 {
  font-family: var(--q-font-display);
  font-size: clamp(34px, 4.4vw, 56px);
  font-weight: 300;
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0;
  text-wrap: balance;
}
.q-page-banner p {
  margin: 0;
  font-size: 17px;
  line-height: 1.6;
  max-width: 52ch;
  align-self: end;
}
.q-page-banner > * { position: relative; z-index: 1; }

/* Deep — product, pricing, contact, compare. */
.q-page-banner--deep { background: var(--q-deep); color: #fff; }
.q-page-banner--deep p { color: var(--q-lavender); }
.q-page-banner--deep::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 42%;
  z-index: 0;
  background: rgba(88, 23, 234, .32);
  clip-path: polygon(44% 0, 100% 0, 100% 100%, 0 100%);
}

/* Tinted — company, legal, resources. */
.q-page-banner--tint { background: var(--q-lavender-mid); color: var(--q-text); }
.q-page-banner--tint p { color: var(--q-primary-hover); }
.q-page-banner--tint::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 40%;
  z-index: 0;
  background: var(--q-lavender);
  clip-path: polygon(52% 0, 100% 0, 100% 100%, 0 100%);
}

/* Solid — utility pages. No facet: a sign-in screen does not need decoration. */
.q-page-banner--solid { background: var(--q-deep); color: #fff; }
.q-page-banner--solid p { color: var(--q-lavender); }

/* An eyebrow above the title, for pages that belong to a family. */
.q-page-banner .q-page-eyebrow {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  margin: 0 0 12px;
  opacity: .8;
}

@media (max-width: 900px) {
  .q-page-banner { padding: 40px 0 44px; }
  .q-page-banner-inner { grid-template-columns: 1fr; gap: 16px; padding: 0 20px; }
  .q-page-banner p { align-self: start; }
  .q-section { padding: 0 20px; }
  .q-page-banner::after { display: none; }
}

/* ---- Flashes ------------------------------------------------------------
 * Printed by qntc_render_flashes() inside <main> on every public page.
 * Severity carried by a rule and the text colour rather than a saturated
 * fill, so one style works on the white panels and on the grey ground.
 * ------------------------------------------------------------------------- */
.q-flash {
  max-width: var(--q-panel);
  margin: 0 auto 16px;
  padding: 13px 16px;
  font-size: 15px;
  line-height: 1.5;
  background: var(--q-surface-raised);
  border: 1px solid var(--q-border);
  border-left: 3px solid var(--q-lavender-mid);
  color: var(--q-text);
}
.q-public-page .q-flash { margin-left: var(--q-inset); margin-right: var(--q-inset); }
.q-flash--error   { border-left-color: var(--q-deep); }
.q-flash--success { border-left-color: var(--q-primary); }

/* dashboard-chrome-open.php prints this as the first thing in <body>, ahead
 * of portal-strip.php / app-banner.php — those are included by each app
 * template individually, so the chrome (the one seam shared by all of them)
 * can't render after them. Flush against the WP admin bar on one side and
 * whatever bar the template adds next with zero margin either way, the
 * panel read as a stray rectangle rather than a notification. Matches the
 * card's own bottom margin so it's framed, not wedged. */
.q-flash--chrome { margin-top: 16px; }
@media (max-width: 900px) {
  .q-public-page .q-flash { margin-left: 20px; margin-right: 20px; }
}

/* ---- Header: inset panel + mega-menus -----------------------------------
 *
 * The header floats as a panel on the page ground rather than spanning it as
 * a full-bleed bar. That one change is what makes the whole page read as
 * composed panels instead of stacked bands.
 * ------------------------------------------------------------------------- */

.q-chrome-dot { color: rgba(255, 255, 255, .45); }

/* The utility bar carries four items and a separator; at phone width that
   wraps to two lines and doubles the height of a strip nobody came for.
   Tighten it rather than dropping items — they are the only route to Docs
   and Client access before the menu is opened. */
@media (max-width: 560px) {
  .q-chrome-utility { gap: 14px; font-size: 12px; justify-content: center; }
  .q-chrome-utility .q-chrome-dot { display: none; }
}

.q-chrome-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: transparent;   /* the panel is .q-chrome-bar, not the header */
  border-bottom: 0;
  padding-top: 12px;
}
/* Panel, then content inside it.
 *
 * The white panel fills the 1312px frame; its contents are inset another 64px
 * within. So the panel edge sits on the page frame and the logo sits on the
 * same left edge as the page title and the body copy below — which is the
 * whole point of having one frame. */
.q-chrome-header .q-chrome-shell { padding-left: 0; padding-right: 0; }
.q-chrome-top .q-chrome-shell { padding-left: var(--q-inset); padding-right: var(--q-inset); }
.q-chrome-bar {
  background: var(--q-surface-raised);
  padding: 0 var(--q-inset);
  transition: box-shadow .14s ease;
}
.q-chrome-bar-inner { min-height: 80px; }
/* Lifted once the page has moved. A border at rest would draw a line across
   the page ground on either side of the panel, which is exactly the
   full-bleed look the inset is removing. */
.q-chrome-header.q-scrolled .q-chrome-bar { box-shadow: 0 1px 0 var(--q-border); }
.q-chrome-header.q-nav-open .q-chrome-bar { box-shadow: none; }

.q-chrome-nav { gap: 30px; font-size: 15px; }

.q-nav-item { position: static; }
.q-nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  padding: 8px 0;
  font-family: var(--q-font-sans);
  font-size: 15px;
  color: var(--q-text);
  cursor: pointer;
}
.q-nav-trigger:hover,
.q-nav-plain:hover { color: var(--q-primary); }
.q-nav-trigger[aria-expanded="true"] { color: var(--q-primary); }
.q-nav-chev { width: 10px; height: 7px; transition: transform .14s ease; }
.q-nav-trigger[aria-expanded="true"] .q-nav-chev { transform: rotate(180deg); }
.q-nav-plain { color: var(--q-text); text-decoration: none; font-size: 15px; }

/* Focus that is actually visible. The default ring is invisible on white at
   small sizes, and a nav nobody can see focus in is a nav nobody can use. */
.q-nav-trigger:focus-visible,
.q-nav-plain:focus-visible,
.q-nav-panel a:focus-visible,
.q-chrome-cta:focus-visible,
.q-chrome-utility a:focus-visible,
.q-mob-trigger:focus-visible,
.q-chrome-footer a:focus-visible {
  outline: 2px solid var(--q-lavender-deep);
  outline-offset: 2px;
}

/* The panel spans the header's own panel width, matching its left and right
   edges — so it reads as the header opening, not as a layer over the page. */
/* The panel is the same 1312px frame as the bar above it, so the menu reads
   as the header opening downwards rather than as a sheet laid over the page. */
.q-nav-panel {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: var(--q-panel);
  background: var(--q-surface-raised);
  border-top: 1px solid var(--q-border);
  box-shadow: 0 18px 40px rgba(28, 28, 34, .10);
}
.q-nav-panel[hidden] { display: none; }
.q-nav-panel-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 48px;
  padding: 30px var(--q-inset) 32px;
}
.q-nav-links {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 40px;
  align-content: start;
}
.q-nav-links a {
  color: var(--q-text);
  text-decoration: none;
  font-size: 15px;
  padding: 9px 0;
}
.q-nav-links a:hover { color: var(--q-primary); }

.q-nav-promo {
  display: flex;
  flex-direction: column;
  background: var(--q-lavender);
  padding: 22px 24px;
  text-decoration: none;
  color: var(--q-text);
}
.q-nav-promo:hover { background: var(--q-lavender-mid); }
.q-nav-promo-eyebrow {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--q-primary);
  margin-bottom: 12px;
}
.q-nav-promo-title {
  font-family: var(--q-font-display);
  font-size: 19px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
}
.q-nav-promo-arrow { margin-top: auto; padding-top: 18px; color: var(--q-primary); font-size: 19px; }

/* ---- Mobile accordion --------------------------------------------------- */
.q-chrome-mobile { border-top: 1px solid var(--q-border); }
.q-mob-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  border: 0;
  background: none;
  padding: 15px 20px;
  font-family: var(--q-font-sans);
  font-size: 16px;
  color: var(--q-text);
  border-bottom: 1px solid var(--q-border);
  cursor: pointer;
  text-align: left;
}
.q-mob-trigger[aria-expanded="true"] { color: var(--q-primary); }
.q-mob-trigger[aria-expanded="true"] .q-nav-chev { transform: rotate(180deg); }
.q-mob-links { display: grid; background: var(--q-surface); }
.q-mob-links[hidden] { display: none; }
.q-mob-links a {
  padding: 13px 20px 13px 34px;
  font-size: 15px;
  color: var(--q-muted);
  text-decoration: none;
  border-bottom: 1px solid var(--q-border);
}
.q-mob-flat {
  padding: 15px 20px;
  font-size: 16px;
  color: var(--q-text);
  text-decoration: none;
  border-bottom: 1px solid var(--q-border);
}
/* Scoped to the panel, because `.q-chrome-mobile a` (0,1,1) out-specifies a
   bare `.q-mob-cta` (0,1,0) and was repainting the label dark-on-purple. */
.q-chrome-mobile .q-mob-cta {
  margin: 16px 20px 20px;
  padding: 15px 20px;
  background: var(--q-primary);
  color: #fff;
  justify-content: center;
  font-weight: 600;
  text-decoration: none;
}
/* Every tap target clears 44px. */
.q-chrome-mobile a, .q-mob-trigger { min-height: 44px; display: flex; align-items: center; }

@media (max-width: 1000px) {
  .q-nav-panel-inner { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .q-nav-promo { display: none; }
}
@media (max-width: 900px) {
  .q-chrome-header { padding-top: 0; }
  .q-chrome-bar-inner { min-height: 64px; }
  .q-nav-panel { display: none !important; }
}

/* ───────────────────────────────────────────────────────────────────────────
   Callouts
   Six pages had each invented their own tinted box — the same heading-plus-
   list component in red, yellow, blue, purple, green and orange. They are one
   component with one tint; --strong is for the rule-down-the-side variant.
   ─────────────────────────────────────────────────────────────────────────── */
.q-note {
  background: var(--q-tint);
  border-radius: 14px;
  padding: 22px 26px;
  margin: 28px 0;
}
.q-note > :first-child { margin-top: 0; }
.q-note > :last-child  { margin-bottom: 0; }

.q-note h3, .q-note h4, .q-note .q-note-title {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--q-violet-deep);
  margin: 0 0 10px;
}
.q-note ul, .q-note ol { margin: 0; }
.q-note li { color: var(--q-muted); }

/* Pulled out of the column with a rule, for the one summary block per page. */
.q-note--strong {
  background: #fff;
  border-left: 3px solid var(--q-primary);
  border-radius: 0 14px 14px 0;
  padding-left: 26px;
}

/* Terminal-ish sample. vs-plausible and vs-simple both asked for `bg-black-50`,
   which is not a Tailwind colour, so these were green text on white. */
.q-sample {
  display: block;
  background: var(--q-ink);
  color: #d6cbfb;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13.5px;
  line-height: 1.7;
  padding: 18px 22px;
  border-radius: 12px;
  overflow-x: auto;
  margin: 24px 0;
  -webkit-user-select: all;
  user-select: all;
}

/* ───────────────────────────────────────────────────────────────────────────
   Forms
   application-form.php alone carried 39 ring-blue-500 and 36 border-blue-500.
   One set of rules replaces all of them.
   ─────────────────────────────────────────────────────────────────────────── */
:is(.q-public-page, .q-app) label { display: block; font-size: 14px; font-weight: 500; color: var(--q-text); margin-bottom: 6px; }

:is(.q-public-page, .q-app) input[type="text"],
:is(.q-public-page, .q-app) input[type="email"],
:is(.q-public-page, .q-app) input[type="tel"],
:is(.q-public-page, .q-app) input[type="url"],
:is(.q-public-page, .q-app) input[type="date"],
:is(.q-public-page, .q-app) input[type="number"],
:is(.q-public-page, .q-app) input[type="password"],
:is(.q-public-page, .q-app) select,
:is(.q-public-page, .q-app) textarea {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 15px;
  color: var(--q-text);
  background: #fff;
  border: 1px solid var(--q-border);
  border-radius: 10px;
  padding: 11px 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
:is(.q-public-page, .q-app) textarea { min-height: 140px; resize: vertical; }
:is(.q-public-page, .q-app) select   { appearance: none; padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%238a8a99' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; }

:is(.q-public-page, .q-app) input:focus,
:is(.q-public-page, .q-app) select:focus,
:is(.q-public-page, .q-app) textarea:focus {
  outline: none;
  border-color: var(--q-primary);
  box-shadow: 0 0 0 3px var(--q-tint-strong);
}
:is(.q-public-page, .q-app) input::placeholder,
:is(.q-public-page, .q-app) textarea::placeholder { color: var(--q-text-3); }

:is(.q-public-page, .q-app) input[type="checkbox"],
:is(.q-public-page, .q-app) input[type="radio"] { accent-color: var(--q-primary); width: 16px; height: 16px; }

/* Buttons. Scoped tightly so it cannot reach the chrome or the banner CTAs. */
.q-btn,
.q-public-page button[type="submit"],
.q-public-page input[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 24px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: var(--q-primary);
  border: 1px solid var(--q-primary);
  border-radius: 10px;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s ease, border-color .15s ease;
}
.q-btn:hover,
.q-public-page button[type="submit"]:hover,
.q-public-page input[type="submit"]:hover { background: var(--q-violet-deep); border-color: var(--q-violet-deep); }

.q-btn:focus-visible,
.q-public-page button[type="submit"]:focus-visible,
.q-public-page input[type="submit"]:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: 2px; }

.q-btn--ghost { background: #fff; color: var(--q-primary); border-color: var(--q-border); }
.q-btn--ghost:hover { background: var(--q-tint); color: var(--q-violet-deep); border-color: var(--q-violet-soft); }

/* The other half of a them-vs-us pair: same box, no brand tint, so the
   tinted one reads as ours. Equal height so the two line up. */
.q-note--muted { background: var(--q-bg-2); }
.q-note-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  align-items: stretch;
  margin: 28px 0;
}
.q-note-pair > .q-note { margin: 0; }
@media (max-width: 760px) { .q-note-pair { grid-template-columns: minmax(0, 1fr); } }

/* ───────────────────────────────────────────────────────────────────────────
   Touch targets
   Measured at 390px: the hamburger was 24px tall and the footer and utility
   links 12–20px. Inline links inside prose are exempt (WCAG 2.5.8), the rest
   are not. Padding rather than height, so nothing reflows on desktop.
   ─────────────────────────────────────────────────────────────────────────── */
.q-chrome-ham { min-width: 44px; min-height: 44px; }

@media (max-width: 900px) {
  .q-chrome-footer-links a,
  .q-chrome-footer-legal a,
  .q-chrome-footer-brand a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  /* One short line, like a utility strip. 24px is the AA target; 44px made
     the bar as tall as the logo row. nowrap because a second row is the
     banner we are removing, and the four labels fit at 320px. */
  .q-chrome-utility {
    flex-wrap: nowrap;
    padding: 0;
    font-size: 12px;
    line-height: 1;
  }
  .q-chrome-utility a {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    padding-block: 6px;
  }
}

/* ───────────────────────────────────────────────────────────────────────────
   Narrow-screen escapes
   Found by measuring at 390px, not by eye. Each of these was pushing the
   document wider than the viewport or under the 44px touch minimum.
   ─────────────────────────────────────────────────────────────────────────── */
@media (max-width: 760px) {
  /* A wide comparison table scrolls inside itself rather than the page. */
  .q-public-page table {
    display: block;
    overflow-x: auto;
    max-width: 100%;
    -webkit-overflow-scrolling: touch;
  }
}

@media (max-width: 900px) {
  /* /docs: the section nav was one non-wrapping 892px row. */
  .q-docs-nav { flex-wrap: wrap; }
  .q-docs-nav-link { min-height: 44px; display: inline-flex; align-items: center; }

  /* /templates: the category pills sat at 34px. */
  .q-tpl-cat { min-height: 44px; display: inline-flex; align-items: center; }

  /* /login and the other auth screens: the wordmark is the only way back. */
  .q-au-logo { min-height: 44px; display: inline-flex; align-items: center; }
}

/* Card action links are the card's tap target, not inline prose. */
@media (max-width: 900px) {
  .q-card-cta { min-height: 44px; align-items: center; }
}


/* ───────────────────────────────────────────────────────────────────────────
   Lander sections
   landing-shell.php carried its frame as an inline style on all 11 sections,
   which no stylesheet could narrow without !important — inline always wins.
   These are those frames, so the mobile claw-back in the page's <style> block
   can go and the lander shares the site's inset token.
   ─────────────────────────────────────────────────────────────────────────── */
.q-sec {
  max-width: var(--q-panel);
  margin: 0 auto;
  padding: 40px var(--q-inset) 0;
}
/* Same outer width as .q-chrome-bar. The colored panels fill the frame;
   their own padding keeps the word in from the edge. */
.q-sec--hero  { padding: 16px 0 20px; }
.q-sec--tight { padding: 0 var(--q-inset) 20px; }
.q-sec--end   { padding: 40px var(--q-inset); }

/* The lander's card action link — the "… →" that closes a card. Nine of these
   were hand-typed inline; on a phone they measured 19-20px tall. */
.q-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 16px;
  font-weight: 600;
  color: var(--q-ink);
  text-decoration: none;
}
.q-cta--end  { margin-top: auto; }          /* pins to the bottom of a flex card */
.q-cta--on-deep { color: #ffffff; }
.q-cta--gap34 { margin-top: 34px; }

/* On a phone the arrow link IS the card's tap target. */
@media (max-width: 900px) { .q-cta { min-height: 44px; } }
.q-cta--sm     { font-size: 15px; gap: 8px; }
/* Sits in a running line of text, so it must not become a 44px block; the
   surrounding row gives it the height instead. */
/* Sits in a byline row, so it keeps the row's type rather than the CTA's.
   It is a flex item, so `display: inline` would blockify anyway. */
.q-cta--inline { font-weight: inherit; font-size: inherit; gap: 6px; }

/* ────────────────────────────────────────────────────────────────────────
   Lander vocabulary
   Generated from landing-shell.php's inline styles verbatim, so the
   computed style is unchanged — only where it is written moves.
   ──────────────────────────────────────────────────────────────────────── */
/* The lander's lavender feature card. */
.q-tile { display: flex; flex-direction: column; justify-content: space-between; background: #ece7fd; padding: 32px 28px; min-height: 0; color: #1c1c22; }
/* The notched mark that opens each card. */
.q-tile-mark { width: 34px; height: 34px; background: #5817ea; clip-path: polygon(0 0, 100% 0, 100% 58%, 58% 100%, 0 100%); margin-bottom: 28px; }
/* Card heading. */
.q-tile-h { font-size: 24px; font-weight: 500; letter-spacing: -0.02em; margin-bottom: 14px; }
/* Card body. */
.q-tile-p { font-size: 16px; line-height: 1.6; color: #5f5f6d; }
/* Card body, looser leading. */
.q-tile-p2 { font-size: 16px; line-height: 1.65; color: #5f5f6d; }
/* The arrow that closes a card. */
.q-tile-arrow { font-size: 22px; color: #5817ea; margin-top: 22px; }
.q-mono.q-mono--primary { font-family: 'IBM Plex Mono', monospace; color: #5817ea; }
.q-mono.q-mono--on-deep { font-family: 'IBM Plex Mono', monospace; color: #ffffff; }
.q-mono.q-mono--soft { font-family: 'IBM Plex Mono', monospace; color: #a893ea; }
.q-mono.q-mono--mute { font-family: 'IBM Plex Mono', monospace; color: #b8b8c6; }
/* Mono marker, used as list bullets and eyebrows. */
.q-mono { font-family: 'IBM Plex Mono', monospace; }
.q-rule.q-rule--on-deep { display: flex; gap: 14px; border-top: 1px solid rgba(255,255,255,0.25); padding-top: 16px; }
/* A ruled row: marker plus text. */
.q-rule { display: flex; gap: 14px; border-top: 1px solid #e8e8ee; padding-top: 16px; }
/* A ruled list row. */
.q-rule-row { border-top: 1px solid #e8e8ee; padding: 22px 0; }
.q-row10 { display: flex; gap: 10px; }
.q-row12 { display: flex; gap: 12px; }
.q-lede19 { font-size: 19px; font-weight: 500; margin-bottom: 8px; }
.q-lede18 { font-size: 18px; font-weight: 500; margin-bottom: 12px; }
/* Section heading. */
.q-h2 { font-size: clamp(28px, 3.2vw, 40px); font-weight: 400; letter-spacing: -0.025em; line-height: 1.1; margin-bottom: 14px; }
.q-link { color: #5817ea; }

/* ───────────────────────────────────────────────────────────────────────────
   Hover
   These were a data-hover="..." attribute plus a script that wrote el.style on
   mouseenter — which beat every CSS :hover rule — and on mouseleave restored
   the whole style attribute, wiping anything set since. Real CSS instead, and
   :focus-visible so a keyboard gets the same affordance the mouse always had.
   ─────────────────────────────────────────────────────────────────────────── */
.q-hv-link:hover,          .q-hv-link:focus-visible          { color: #5817ea; }
.q-hv-white:hover,         .q-hv-white:focus-visible         { color: #ffffff; }
.q-hv-tint:hover,          .q-hv-tint:focus-visible          { color: #ece7fd; }
.q-hv-soft:hover,          .q-hv-soft:focus-visible          { color: #d6cbfb; }
.q-hv-tile:hover,          .q-hv-tile:focus-visible          { background: #ddd3fb; color: #1c1c22; }
.q-hv-deep:hover,          .q-hv-deep:focus-visible          { background: #240b63; color: #ffffff; }
.q-hv-deep-lift:hover,     .q-hv-deep-lift:focus-visible     { background: #2e1178; color: #ffffff; }
.q-hv-primary:hover,       .q-hv-primary:focus-visible       { background: #5817ea; color: #ffffff; }
.q-hv-primary-deep:hover,  .q-hv-primary-deep:focus-visible  { background: #4611c0; color: #ffffff; }
.q-hv-invert:hover,        .q-hv-invert:focus-visible        { background: #ffffff; color: #240b63; }
.q-hv-ghost:hover,         .q-hv-ghost:focus-visible         { border-color: #ffffff; background: rgba(255,255,255,0.12); color: #ffffff; }

/* ───────────────────────────────────────────────────────────────────────────
   Tabs (the lander's "who it's for" strip)
   Active state was written straight onto btn.style by the click handler, so no
   stylesheet could reach it and assistive tech was told nothing. It is a class
   plus aria-selected now.
   ─────────────────────────────────────────────────────────────────────────── */
.q-tabs {
  display: flex;
  gap: 0;
  flex-wrap: wrap;
  border-bottom: 1px solid #dcdce6;
  margin-bottom: 32px;
}
.q-tab-btn {
  background: transparent;
  border: none;
  border-bottom: 3px solid transparent;
  color: var(--q-muted, #5f5f6d);
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 19px;
  padding: 14px 30px 16px 0;
  margin-right: 34px;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.q-tab-btn:last-child { margin-right: 0; }
.q-tab-btn:hover { color: var(--q-primary); }
.q-tab-btn.is-active { border-bottom-color: var(--q-primary); color: var(--q-primary); }
.q-tab-btn:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: -2px; }

@media (max-width: 640px) {
  .q-tab-btn { font-size: 17px; margin-right: 22px; padding-right: 0; }
}

/* Pricing cards, lifted from landing-shell.php verbatim. */
/* The $N / mo line on a pricing card. */
.q-price-row { display: flex; align-items: baseline; gap: 6px; margin-bottom: 16px; }
/* The figure itself. */
.q-price { font-family: 'Space Grotesk', sans-serif; font-size: 36px; font-weight: 700; letter-spacing: -0.03em; }
.q-price-per { font-size: 15px; color: #8a8a99; }
.q-price-note { font-size: 16px; line-height: 1.6; color: #5f5f6d; margin-bottom: 26px; }

/* Comparison pages: the opening framing line and the closing provenance note. */
.q-cmp-pitch { font-size: 19px; line-height: 1.6; }
.q-cmp-cta   { display: flex; flex-wrap: wrap; gap: 12px; margin: 22px 0 0; }
.q-cmp-note  { font-size: 14px; color: var(--q-text-3); }

/* In-page anchors (#build, #who, #hosting, #pricing…) landed underneath the
   sticky header, which hid the first ~90px of the section. */
.q-sec[id], .q-public-page [id] { scroll-margin-top: 96px; }
@media (max-width: 900px) { .q-sec[id], .q-public-page [id] { scroll-margin-top: 76px; } }

/* A link in a table cell is a real tap target, not an inline link in a
   sentence — the row is the thing a thumb aims at. */
@media (max-width: 900px) {
  .q-public-page td > a:only-child { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ───────────────────────────────────────────────────────────────────────────
   App shell
   The signed-in app shares brand.css with the marketing site; .q-app is its
   counterpart to .q-public-page. Account context sits where the wordmark used
   to: it answers "whose account is this" without stamping the network's brand
   on a screen a customer reads as their own.
   ─────────────────────────────────────────────────────────────────────────── */
.q-portal-account {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-lavender-mid, #d6cbfb);
  min-width: 0;
}
.q-portal-account-n {
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.q-portal-account-sep { opacity: 0.55; }
.q-portal-account-p   { white-space: nowrap; }

@media (max-width: 640px) {
  /* The plan is the first thing to go; the account name identifies the screen. */
  .q-portal-account-sep, .q-portal-account-p { display: none; }
}

/* The strip's links are the app's only navigation on a phone, so they take the
   44px minimum. The skip link is exempt — it is visually hidden until focused. */
@media (max-width: 900px) {
  .q-portal-links a,
  .q-portal-links .q-portal-here {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   APP COMPONENTS
   One vocabulary for the signed-in screens, built on the same tokens as the
   marketing site. Each of these replaces a pattern that six screens had each
   re-implemented privately (q-ag-*, q-portal-*, qm-*, inline <style>).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---- Panel: the container everything else sits in ---------------------- */
.q-panel {
  background: #fff;
  border: 1px solid var(--q-line);
  border-radius: 4px;
  padding: 24px;
}
.q-panel + .q-panel { margin-top: 20px; }
.q-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.q-panel-title {
  font-family: var(--q-font-display);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--q-ink);
  margin: 0;
}
.q-panel-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.q-panel--deep { background: var(--q-deep); border-color: transparent; color: #fff; }
.q-panel--deep .q-panel-title { color: #fff; }
.q-panel--tint { background: var(--q-tint); border-color: transparent; }
@media (max-width: 640px) { .q-panel { padding: 16px; } }

/* ---- KPI tile ----------------------------------------------------------
 * Label on top in mono caps, figure in the display face with tabular
 * numerals so a column of them cannot jitter. One tile per screen may be
 * filled --deep; Client management already had that idea.
 * ---------------------------------------------------------------------- */
.q-kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: 16px;
}
.q-kpi {
  background: #fff;
  border: 1px solid var(--q-line);
  border-radius: 4px;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.q-kpi-l {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-muted);
  margin: 0;
}
.q-kpi-n {
  font-family: var(--q-font-display);
  font-size: clamp(26px, 2.6vw, 34px);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--q-ink);
  font-variant-numeric: tabular-nums;
  margin: 0;
  overflow-wrap: anywhere;
}
.q-kpi-sub { font-size: 13px; color: var(--q-muted); margin: 0; }
.q-kpi--deep { background: var(--q-deep); border-color: transparent; }
.q-kpi--deep .q-kpi-l   { color: var(--q-lavender-mid, #d6cbfb); }
.q-kpi--deep .q-kpi-n,
.q-kpi--deep .q-kpi-sub { color: #fff; }

/* Delta chip. Direction is carried by the glyph and the word, never by hue —
   the palette has no green or red and meaning must not depend on colour. */
.q-delta {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  align-self: flex-start;
  background: var(--q-tint);
  color: var(--q-violet-deep);
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 2px;
  font-variant-numeric: tabular-nums;
}
.q-kpi--deep .q-delta { background: rgba(255,255,255,0.16); color: #fff; }

/* ---- Data table --------------------------------------------------------
 * 56px rows, mono caps header, numerics right-aligned and tabular. Reflows
 * to stacked cards below 760px using the header text from data-label.
 * ---------------------------------------------------------------------- */
.q-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.q-table { width: 100%; border-collapse: collapse; font-size: 15px; }
.q-table th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--q-tint);
  font-family: var(--q-font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--q-muted);
  text-align: left;
  padding: 12px 14px;
  white-space: nowrap;
}
.q-table td {
  padding: 14px;
  border-top: 1px solid var(--q-line);
  color: var(--q-ink);
  vertical-align: middle;
}
.q-table tbody tr { min-height: 56px; }
.q-table tbody tr:hover { background: var(--q-tint); }
.q-table .num, .q-table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Names must stay one line — four-line wrapping was the trackers table's
   worst problem. The full value stays reachable via title=. */
.q-table .ellip {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.q-table-sub {
  display: block;
  font-family: var(--q-font-mono);
  font-size: 12px;
  color: var(--q-text-3);
}

@media (max-width: 760px) {
  .q-table thead { display: none; }
  .q-table, .q-table tbody, .q-table tr, .q-table td { display: block; width: 100%; }
  .q-table tr {
    border: 1px solid var(--q-line);
    border-radius: 4px;
    margin-bottom: 12px;
    padding: 4px 0;
    background: #fff;
  }
  .q-table td {
    border-top: 0;
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 9px 14px;
  }
  .q-table td::before {
    content: attr(data-label);
    font-family: var(--q-font-mono);
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--q-muted);
    flex: 0 0 auto;
  }
  .q-table .num { text-align: right; }
  .q-table .ellip { max-width: none; }
}

/* ---- Overflow menu: one primary action visible, the rest behind ⋯ ------ */
.q-menu { position: relative; display: inline-block; }
.q-menu-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  min-height: 36px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--q-muted);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
}
.q-menu-trigger:hover { background: var(--q-tint); color: var(--q-violet-deep); }
.q-menu-trigger:focus-visible { outline: 2px solid var(--q-violet-soft); outline-offset: 1px; }
.q-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 30;
  min-width: 200px;
  background: #fff;
  border: 1px solid var(--q-line);
  border-radius: 4px;
  box-shadow: 0 8px 32px rgba(36, 11, 99, 0.12);
  padding: 6px;
  display: none;
}
.q-menu-panel[data-open="true"] { display: block; }
.q-menu-panel a, .q-menu-panel button {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 10px;
  padding: 10px 12px;
  min-height: 44px;
  background: none;
  border: 0;
  border-radius: 2px;
  font: inherit;
  font-size: 14px;
  color: var(--q-ink);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.q-menu-panel a:hover, .q-menu-panel button:hover { background: var(--q-tint); }

/* ---- Modal ------------------------------------------------------------- */
.q-modal-back {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(36, 11, 99, 0.6);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 20px;
  overflow-y: auto;
}
.q-modal {
  width: 100%;
  max-width: 560px;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 8px 32px rgba(36, 11, 99, 0.12);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 80px);
}
.q-modal-head { padding: 22px 24px 16px; border-bottom: 1px solid var(--q-line); }
.q-modal-head h2 {
  font-family: var(--q-font-display);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--q-ink);
}
.q-modal-context {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--q-muted);
  margin: 0 0 6px;
}
.q-modal-body { padding: 22px 24px; overflow-y: auto; }
.q-modal-foot {
  padding: 16px 24px;
  border-top: 1px solid var(--q-line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  background: #fff;
  position: sticky;
  bottom: 0;
}
@media (max-width: 640px) {
  .q-modal-back { padding: 0; align-items: stretch; }
  .q-modal { max-width: none; max-height: 100vh; border-radius: 0; }
}

/* ---- Destructive: outlined, never red; confirm is a typed step --------- */
.q-btn--danger {
  background: #fff;
  color: var(--q-ink);
  border-color: var(--q-ink);
}
.q-btn--danger:hover { background: var(--q-ink); color: #fff; border-color: var(--q-ink); }

/* ---- Empty state ------------------------------------------------------- */
.q-empty { text-align: center; padding: 48px 24px; }
.q-empty svg { display: block; margin: 0 auto 18px; color: var(--q-violet-soft); }
.q-empty h3 {
  font-family: var(--q-font-display);
  font-size: 18px;
  font-weight: 500;
  color: var(--q-ink);
  margin: 0 0 8px;
}
.q-empty p { font-size: 15px; color: var(--q-muted); margin: 0 auto 18px; max-width: 42ch; }

/* ---- Toast ------------------------------------------------------------- */
.q-toast {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 200;
  background: var(--q-deep);
  color: #fff;
  padding: 14px 18px;
  border-radius: 4px;
  font-size: 14px;
  box-shadow: 0 8px 32px rgba(36, 11, 99, 0.24);
  max-width: min(420px, calc(100vw - 40px));
}

/* ---- Ranked bar: a value list with its own magnitude behind it --------- */
.q-rank { display: grid; gap: 2px; }
.q-rank-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 12px;
  font-size: 14px;
  isolation: isolate;
}
.q-rank-row::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--q-rank-pct, 0%);
  background: var(--q-tint);
  border-radius: 2px;
  z-index: -1;
}
.q-rank-v { font-variant-numeric: tabular-nums; color: var(--q-muted); white-space: nowrap; }
.q-rank-k { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Skeleton ---------------------------------------------------------- */
.q-skel {
  background: linear-gradient(90deg, var(--q-tint) 25%, #f6f4fe 37%, var(--q-tint) 63%);
  background-size: 400% 100%;
  animation: q-skel 1.4s ease infinite;
  border-radius: 2px;
  min-height: 14px;
}
@keyframes q-skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .q-skel { animation: none; } }

/* ───────────────────────────────────────────────────────────────────────────
   App page heading
   One definition. This accumulated three copies during editing and the last
   one won, which is why headings stayed small after the rule "changed".
   Weight and scale match .q-portal-title: the portal set the app's heading
   style and the other screens were the odd ones out, not it.
   ─────────────────────────────────────────────────────────────────────────── */
.q-app-head {
  max-width: 1280px;
  margin: 0 auto;
  padding: 30px 32px 0;
}
/* Operator screens: the same heading, sized for a screen you open repeatedly
   rather than one you land on once. The display-type default took most of the
   first viewport on /admin/* and pushed the controls under the fold. */
.q-app-head--compact {
  padding: 18px 32px 0;
}
.q-app-head--compact h1 {
  font-size: 22px;
  letter-spacing: -0.02em;
  text-transform: none;
  margin: 0 0 4px;
}
.q-app-head--compact p {
  font-size: 14px;
  margin: 0;
}
/* ──────────────────────────────────────────────────────────────────────────
   Operator index (/admin/dashboard) — .q-ns-*

   Lives here rather than in a <style> block because brand.css is the vehicle:
   the operator panel is the one surface that had drifted into its own visual
   language, and the point of rebuilding it was to stop that.
   ────────────────────────────────────────────────────────────────────────── */
.q-ns { max-width: 1280px; margin: 0 auto; padding: 18px 32px 56px; }
.q-ns section { margin-bottom: 26px; }

/* Needs attention */
.q-ns-att {
  border: 1px solid var(--q-line);
  border-left: 3px solid #d9a84f;
  border-radius: 10px;
  background: #fffdf7;
  padding: 16px 18px;
}
.q-ns-att--clear { border-left-color: #35a06b; background: #f7fdf9; }
.q-ns-att h2 { font-size: 15px; font-weight: 700; margin: 0 0 10px; color: var(--q-ink); }
.q-ns-att h2 span {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  background: #d9a84f; color: #fff; font-size: 12px; font-weight: 700;
}
.q-ns-att--clear p { margin: 0; font-size: 13px; color: var(--q-muted); }
.q-ns-att ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.q-ns-att li {
  display: grid; grid-template-columns: minmax(170px, 1fr) minmax(170px, 1fr) 2fr;
  gap: 10px; align-items: baseline;
  padding: 9px 11px; border-radius: 7px; background: #fff; border: 1px solid var(--q-line);
}
.q-ns-att li.is-error { border-left: 3px solid #c0453c; }
.q-ns-att li.is-warning { border-left: 3px solid #d9a84f; }
.q-ns-att__what { font-weight: 650; font-size: 13px; color: var(--q-ink); }
.q-ns-att__who { font-size: 13px; }
.q-ns-att__who span { color: var(--q-text-3); margin-left: 6px; font-size: 12px; }
.q-ns-att__why { font-size: 12.5px; color: var(--q-muted); }

/* Counts */
.q-ns-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.q-ns-tile { border: 1px solid var(--q-line); border-radius: 10px; padding: 14px 15px; background: #fff; }
.q-ns-tile__label { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--q-text-3); }
.q-ns-tile__value { font-size: 26px; font-weight: 700; letter-spacing: -.02em; color: var(--q-ink); margin: 3px 0 1px; }
.q-ns-tile__sub { font-size: 12px; color: var(--q-muted); }

/* Every site */
.q-ns-panel { border: 1px solid var(--q-line); border-radius: 10px; background: #fff; overflow: hidden; }
.q-ns-panel > header {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 13px 16px; border-bottom: 1px solid var(--q-line); background: #fbfaff;
}
.q-ns-panel > header h2 { font-size: 14px; font-weight: 700; margin: 0; color: var(--q-ink); }
.q-ns-panel > header h2 span { color: var(--q-text-3); font-weight: 600; margin-left: 5px; }
.q-ns-panel > header input {
  margin-left: auto; min-width: 280px; flex: 0 1 340px;
  border: 1px solid var(--q-line); border-radius: 7px; padding: 7px 10px; font-size: 13px;
}
/* Section tone: the heading carries the meaning, so the panel edge agrees
   with it rather than every row shouting individually. */
.q-ns-panel--bad  { border-left: 3px solid #c0453c; }
.q-ns-panel--warn { border-left: 3px solid #d9a84f; }
.q-ns-panel--ok   { border-left: 3px solid var(--q-line); }
.q-ns-panel__lede { margin: 0; font-size: 12.5px; color: var(--q-muted); flex: 1 1 260px; }
.q-ns-filter {
  margin-left: auto; min-width: 180px; flex: 0 1 240px;
  border: 1px solid var(--q-line); border-radius: 7px; padding: 6px 9px; font-size: 13px;
}

/* Inline actions. Small, quiet, and all the same size: these are the things a
   partner can already do to their own sites, so they should not look like
   anything more dangerous here. */
.q-ns-acts { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; align-items: center; }
.q-ns-acts form { display: inline; margin: 0; }
.q-ns-act {
  display: inline-block; padding: 4px 9px; border-radius: 6px;
  border: 1px solid var(--q-line); background: #fff;
  font-size: 12px; font-weight: 600; line-height: 1.4;
  color: var(--q-ink); text-decoration: none; cursor: pointer; white-space: nowrap;
  font-family: inherit;
}
.q-ns-act:hover:not(:disabled) { background: #f4f2fc; border-color: var(--q-primary); color: var(--q-primary); }
.q-ns-act:disabled { opacity: .55; cursor: default; }
.q-ns-act--primary { background: var(--q-primary); border-color: var(--q-primary); color: #fff; }
.q-ns-act--primary:hover:not(:disabled) { background: var(--q-primary-hover); border-color: var(--q-primary-hover); color: #fff; }
.q-ns-act--fix { border-color: #c0453c; color: #c0453c; }
.q-ns-act--fix:hover:not(:disabled) { background: #c0453c; border-color: #c0453c; color: #fff; }

.q-ns-tablewrap { overflow-x: auto; }
.q-ns-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.q-ns-table th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--q-text-3); font-weight: 700; padding: 9px 12px; border-bottom: 1px solid var(--q-line);
  white-space: nowrap;
}
.q-ns-table td { padding: 10px 12px; border-bottom: 1px solid #f2f0f7; vertical-align: top; }
.q-ns-table tbody tr:hover { background: #fbfaff; }
.q-ns-table .num { text-align: right; white-space: nowrap; }
.q-ns-site { font-weight: 650; color: var(--q-ink); }
.q-ns-sub { font-size: 11.5px; color: var(--q-text-3); margin-top: 1px; }
.q-ns-none { color: #c6c2d2; }
.q-ns-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--q-ink); word-break: break-all; }

/* People, grouped by what each account is. Each group is a sub-block inside the
   one panel rather than its own panel, so the five groups read as one answer to
   one question instead of five competing sections. */
.q-ns-people { margin-top: 18px; }
.q-ns-people:first-of-type { margin-top: 4px; }
.q-ns-people__h {
  font-size: 13px; font-weight: 700; color: var(--q-ink); margin: 0;
  display: flex; align-items: center; gap: 7px;
}
.q-ns-people__h span {
  font-size: 11px; font-weight: 700; padding: 1px 7px; border-radius: 999px;
  background: #ece7fd; color: var(--q-primary);
}
.q-ns-people__h--bad  span { background: #fdeceb; color: #c0453c; }
.q-ns-people__h--warn span { background: #fdf4e3; color: #9a6b12; }
.q-ns-people__h--ok   span { background: #e9f6ee; color: #246b43; }
.q-ns-people .q-ns-sub { margin: 2px 0 8px; }
.q-ns-people .q-ns-table { font-size: 12.5px; }
.q-ns-sub--warn { color: #c0453c; font-weight: 600; }

/* A stored key, revealed in place. Masked by default because this table lists
   every site on the network at once -- an operator screenshotting their own
   dashboard should not be handing out every passphrase with it. */
.q-ns-key { margin-top: 3px; }
.q-ns-keytoggle {
  font: inherit; font-size: 11.5px; padding: 1px 6px; cursor: pointer;
  border: 1px dashed var(--q-line); border-radius: 5px;
  background: transparent; color: var(--q-text-3);
}
.q-ns-keytoggle:hover { border-color: var(--q-primary); color: var(--q-primary); }
.q-ns-keytoggle--open {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  border-style: solid; color: var(--q-ink); background: #f7f5ff;
  user-select: all;
}

/* Credential panel. Pinned, and dismissed only deliberately: a generated
   mailbox password exists in readable form exactly once. */
.q-ns-creds {
  position: fixed; z-index: 9999; top: 16px; left: 50%; transform: translateX(-50%);
  width: min(460px, calc(100vw - 32px));
  background: #fff; color: var(--q-ink);
  border: 1px solid var(--q-primary); border-radius: 10px;
  box-shadow: 0 18px 48px rgba(32, 18, 72, .26);
  padding: 16px 18px;
}
.q-ns-creds__h { font-weight: 700; font-size: 14px; margin-bottom: 10px; }
.q-ns-creds__row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.q-ns-creds__k { font-size: 11.5px; color: var(--q-text-3); min-width: 108px; flex: none; }
.q-ns-creds__v {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  background: #f7f5ff; border: 1px solid var(--q-line); border-radius: 6px;
  padding: 3px 7px; flex: 1 1 auto; word-break: break-all; user-select: all;
}
.q-ns-creds__copy {
  font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; flex: none;
  border: 1px solid var(--q-line); border-radius: 5px; background: #fff;
  padding: 3px 7px; color: var(--q-text-3);
}
.q-ns-creds__copy:hover { border-color: var(--q-primary); color: var(--q-primary); }
.q-ns-creds__note { font-size: 11.5px; color: #c0453c; margin: 9px 0 12px; }
.q-ns-creds__done {
  font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; width: 100%;
  background: var(--q-primary); border: 1px solid var(--q-primary); color: #fff;
  border-radius: 7px; padding: 7px 10px;
}
.q-ns-creds__done:hover { background: var(--q-primary-hover); border-color: var(--q-primary-hover); }
.q-ns-grant { font-size: 12px; color: var(--q-muted); }
.q-ns-owed { font-weight: 700; color: #c0453c; }
.q-ns-pill {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; white-space: nowrap;
}
.q-ns-pill--open    { background: #e8f6ee; color: #1f7a4d; }
.q-ns-pill--private { background: #eeecf8; color: #574b8e; }
.q-ns-pill--bad     { background: #fbe9e7; color: #a7352d; }

/* Network mail */
.q-ns-mail { padding: 14px 16px; }
.q-ns-maillist { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 6px; }
.q-ns-maillist li {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 11px; border: 1px solid var(--q-line); border-radius: 7px; background: #fff;
}
.q-ns-mailaddr { font-weight: 650; font-size: 13.5px; color: var(--q-ink); }
.q-ns-maillist li .q-ns-sub { margin: 0; }
.q-ns-maillist li .q-ns-act { margin-left: auto; }
.q-ns-mailnew label {
  display: block; font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--q-text-3); font-weight: 700; margin-bottom: 6px;
}
.q-ns-mailrow { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.q-ns-mailrow input {
  border: 1px solid var(--q-line); border-radius: 7px; padding: 7px 10px; font-size: 13px;
}
#q-ns-mail-local { width: 160px; }
#q-ns-mail-domain { width: 190px; }
.q-ns-mailat { color: var(--q-text-3); font-weight: 700; }
.q-ns-mailnew .q-ns-sub { margin-top: 7px; max-width: 46rem; }

/* Operator tools */
.q-ns-tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }
.q-ns-toolgroup { border: 1px solid var(--q-line); border-radius: 10px; background: #fff; padding: 13px 14px; }
.q-ns-toolgroup h3 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--q-text-3); font-weight: 700; margin: 0 0 8px;
}
.q-ns-toolgroup a { display: block; padding: 7px 8px; border-radius: 7px; text-decoration: none; }
.q-ns-toolgroup a:hover { background: #f4f2fc; }
.q-ns-tool__label { display: block; font-size: 13.5px; font-weight: 650; color: var(--q-primary); }
.q-ns-tool__blurb { display: block; font-size: 12px; color: var(--q-muted); margin-top: 1px; }

@media (max-width: 860px) {
  .q-ns { padding: 14px 18px 44px; }
  .q-ns-att li { grid-template-columns: 1fr; gap: 3px; }
}

/* A page's own "up", beside its title. The utility strip's back link is
   always-present chrome; this is the one that belongs to this screen. */
.q-app-head__back {
  display: inline-block;
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--q-primary);
  text-decoration: none;
}
.q-app-head__back:hover { text-decoration: underline; }
.q-app-head__back span { margin-right: 2px; }
.q-app-head h1 {
  font-family: var(--q-font-display);
  font-size: clamp(30px, 4vw, 52px);
  font-weight: 700;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  line-height: 1.02;
  color: var(--q-ink);
  margin: 0 0 8px;
}
.q-app-head p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--q-muted);
  margin: 0;
  max-width: 60ch;
}
@media (max-width: 900px) {
  .q-app-head { padding: 22px 20px 0; }
}

/* A note under a worked example: the figures above are computed from live
   policy, and saying so is the difference between a table a partner trusts and
   one they re-check against the composer every time. */
.q-public-page .prose .q-legal-note {
  margin: 4px 0 26px;
  font-size: 13px;
  color: var(--q-muted);
}
/* Tucked up only when it is captioning the table directly above it. The
   negative margin was written for that case and then collided with a
   paragraph when the note moved further down the section. */
.q-public-page .prose table + .q-legal-note { margin-top: -14px; }

/* The second line in a worked-example cell — "once, on payment", "ours".
   A money figure without its period is ambiguous in exactly the way this
   table exists to fix. */
.q-public-page .prose .q-legal-sub {
  font-family: var(--q-font-mono);
  font-size: 11px;
  letter-spacing: .03em;
  color: var(--q-text-3);
}
.q-public-page .prose table td { line-height: 1.45; }
