/* ============================================================================
   VANTIQ COMMUNITY PORTAL - PORTABLE STYLESHEET
   ----------------------------------------------------------------------------
   Plain CSS. No build step, no Tailwind, no preprocessor, no dependencies.
   Drop this into a WordPress theme and enqueue it, then write markup against
   the classes below.

   WHY THIS FILE EXISTS
   The mockup is a Next.js app styled with Tailwind utility classes written
   inline in the markup: about 1,700 class attributes across 149 files. That is
   workable in React and unworkable as a handover artifact, because there is no
   stylesheet to take and no name to point at when you want "a card".

   This file is that stylesheet. Every value here is copied from the mockup's
   own token set, so the two agree by construction rather than by eye.

   NAMING
   Everything is prefixed `vq-` so nothing collides with a WordPress theme,
   Gutenberg block styles, or a plugin.

   HOW TO READ IT
   1. Tokens        - every colour, radius and shadow the design uses
   2. Base          - resets and typography defaults
   3. Layout        - app shell, sidebar, page container, hero
   4. Type roles    - the recurring text styles, by role rather than by size
   5. Components    - card, button, badge, input, table, nav, empty state
   6. Utilities     - the small handful genuinely worth having

   DARK MODE
   Add class="dark" to <html> (or any ancestor). Every token re-resolves; no
   component rule needs to change.
   ========================================================================= */


/* ============================================================================
   1. TOKENS
   The single source of truth. Change a colour here and it changes everywhere.
   Names match the mockup exactly, so you can cross-reference app/globals.css.
   ========================================================================= */

:root {
  /* --- Surfaces ------------------------------------------------------- */
  --vq-background: #f3f8f7;        /* page field */
  --vq-foreground: #0d3d3d;        /* default ink, "Deep Teal" */
  --vq-card: #ffffff;              /* raised surface */
  --vq-card-foreground: #0d3d3d;
  --vq-popover: #ffffff;
  --vq-popover-foreground: #0d3d3d;
  --vq-surface-elevated: #ffffff;
  --vq-surface-sunken: #edf4f3;    /* wells, image placeholders */

  /* --- Brand ---------------------------------------------------------- */
  --vq-primary: #0f5a5b;           /* the CTA colour, all interactive teal */
  --vq-primary-foreground: #ffffff;
  --vq-secondary: #d6e6e5;         /* "Azure Mist", hero gradients, chips */
  --vq-secondary-foreground: #0d3d3d;
  --vq-muted: #e7efee;             /* hover fills, quiet backgrounds */
  --vq-muted-foreground: #5b6a85;  /* secondary text */
  --vq-accent: #e2e6f0;
  --vq-accent-foreground: #2e3a54;

  /* --- Status --------------------------------------------------------- */
  --vq-success: #2f7548;
  --vq-success-foreground: #ffffff;
  --vq-warning: #c98a3e;
  --vq-warning-foreground: #3d2408;
  --vq-info: #4c6fa6;
  --vq-info-foreground: #ffffff;
  --vq-destructive: #b23a34;
  --vq-destructive-foreground: #ffffff;
  --vq-critical: #6b3566;
  --vq-critical-foreground: #ffffff;

  /* Emphasis is NOT a status. It is "Midnight Violet", used where something
     should stand out as a highlight rather than report a state, e.g. a deal
     stage badge. It never replaces primary for buttons or links. */
  --vq-emphasis: #351431;
  --vq-emphasis-foreground: #ffffff;

  /* --- Lines and controls --------------------------------------------- */
  --vq-border: #d8e3e2;
  --vq-input: #e9f1f0;
  --vq-ring: #0f5a5b;              /* focus ring */

  /* --- Sidebar (its own scale so it can diverge from the page) -------- */
  --vq-sidebar: #ffffff;
  --vq-sidebar-foreground: #0d3d3d;
  --vq-sidebar-accent: #e7efee;
  --vq-sidebar-border: #dee8e7;

  /* --- Charts --------------------------------------------------------- */
  --vq-chart-1: #0f5a5b;
  --vq-chart-2: #4c6fa6;
  --vq-chart-3: #8797b2;
  --vq-chart-4: #c98a3e;
  --vq-chart-5: #6b3566;

  /* --- Geometry -------------------------------------------------------- */
  --vq-radius: 0.5rem;                            /* 8px, the base */
  --vq-radius-sm: calc(var(--vq-radius) - 4px);   /* 4px */
  --vq-radius-md: calc(var(--vq-radius) - 2px);   /* 6px */
  --vq-radius-lg: var(--vq-radius);               /* 8px */
  --vq-radius-xl: calc(var(--vq-radius) + 4px);   /* 12px */

  /* --- Type ------------------------------------------------------------ */
  /* Placeholder stack pending a branding decision on Gilroy vs an open
     fallback. Swap these two lines and nothing else. */
  --vq-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --vq-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* --- Elevation ------------------------------------------------------- */
  /* Tinted to the portal's own ink rather than black, which is why cards
     read as teal-grey rather than dirty grey. */
  --vq-shadow-color: 13 61 61;
  --vq-shadow-card:
    rgb(var(--vq-shadow-color) / 0.04) 0 0 0 1px,
    rgb(var(--vq-shadow-color) / 0.04) 0 1px 1px -0.5px,
    rgb(var(--vq-shadow-color) / 0.04) 0 3px 3px -1.5px,
    rgb(var(--vq-shadow-color) / 0.04) 0 6px 6px -3px,
    rgb(var(--vq-shadow-color) / 0.04) 0 12px 12px -6px,
    rgb(var(--vq-shadow-color) / 0.04) 0 24px 24px -12px;

  /* --- Layout ---------------------------------------------------------- */
  --vq-container: 1320px;   /* max content width */
  --vq-sidebar-w: 260px;
  --vq-header-h: 64px;
}

.dark {
  --vq-background: #0d3d3d;
  --vq-foreground: #e3efee;
  --vq-card: #123f41;
  --vq-card-foreground: #e3efee;
  --vq-popover: #123f41;
  --vq-popover-foreground: #e3efee;
  --vq-surface-elevated: #123f41;
  --vq-surface-sunken: #0a3233;

  --vq-primary: #1f7a7b;
  --vq-primary-foreground: #ffffff;
  --vq-secondary: #164f50;
  --vq-secondary-foreground: #e3efee;
  --vq-muted: #143f41;
  --vq-muted-foreground: #8797b2;
  --vq-accent: #1b5657;
  --vq-accent-foreground: #e3efee;

  --vq-success: #4caf6c;
  --vq-success-foreground: #0c2415;
  --vq-warning: #e0a559;
  --vq-warning-foreground: #3d2408;
  --vq-info: #6e8fc4;
  --vq-info-foreground: #12233d;
  --vq-destructive: #d9645d;
  --vq-destructive-foreground: #2a0e0c;
  --vq-critical: #8c4a86;
  --vq-critical-foreground: #ffffff;
  --vq-emphasis: #6e2f63;
  --vq-emphasis-foreground: #ffffff;

  --vq-border: #1d4f50;
  --vq-input: #163f41;
  --vq-ring: #1f7a7b;

  --vq-sidebar: #0a3233;
  --vq-sidebar-foreground: #e3efee;
  --vq-sidebar-accent: #1b5657;
  --vq-sidebar-border: #1a4b4c;

  --vq-chart-1: #2e8c8d;
  --vq-chart-2: #6e8fc4;
  --vq-chart-3: #8797b2;
  --vq-chart-4: #e0a559;
  --vq-chart-5: #8c4a86;

  --vq-shadow-color: 0 0 0;
}


/* ============================================================================
   2. BASE
   Scoped under .vq-root so this file can never fight a WordPress theme's own
   resets. Put class="vq-root" on your wrapper (or on <body>).
   ========================================================================= */

/* --- THEME FIREWALL --------------------------------------------------
 *
 * Read this before debugging any "why does it look wrong in WordPress"
 * problem.
 *
 * Inside a WordPress theme this stylesheet is not the only thing styling
 * these elements. Astra emits a large block of dynamic CSS into the document
 * head and puts a long list of classes on <body>, so headings, paragraphs,
 * links, lists and form controls all arrive with the theme's opinions already
 * applied. Those rules are frequently more specific than a single class, e.g.
 * `.ast-page-builder-template .entry-content h2`.
 *
 * So the selectors below are deliberately written as `.vq-root.vq-root`. That
 * is the same element matched twice, which is legal CSS and costs nothing at
 * runtime, but it raises specificity from (0,1,0) to (0,2,0). That is enough
 * to beat typical theme selectors without a single !important, which keeps
 * the file overridable by whoever comes next.
 *
 * If something still loses, do NOT reach for !important. Add one more class
 * (`.vq-root.vq-root.vq-root`) to that specific rule and leave a comment
 * saying which theme selector forced it.
 *
 * Everything here is scoped. Nothing leaks out to the theme's own header,
 * footer or admin bar.
 * ------------------------------------------------------------------- */

.vq-root.vq-root {
  font-family: var(--vq-font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: normal;
  color: var(--vq-foreground);
  background: var(--vq-background);
  -webkit-font-smoothing: antialiased;
  text-transform: none;
}

.vq-root.vq-root *,
.vq-root.vq-root *::before,
.vq-root.vq-root *::after { box-sizing: border-box; }

/* INHERITANCE GUARD.
 *
 * The firewall below deliberately skips anything carrying a vq- class, so the
 * component rule is the only thing styling it. That leaves a gap: a theme rule
 * targeting an element DIRECTLY, like `h2 { font-family: Georgia }`, beats an
 * inherited value no matter how specific the ancestor is, and the component
 * classes set size, weight and colour but not family or alignment.
 *
 * Found by putting a .vq-title inside a hostile theme and watching it render
 * in Georgia. This re-asserts inheritance for the two properties that leak.
 * At (0,3,0) it clears any element selector and does not fight the components,
 * which never set these two. */
.vq-root.vq-theme-firewall [class*="vq-"] {
  font-family: inherit;
  text-align: inherit;
}

/* `inherit` above is only as good as what it inherits from. Astra sets
   text-align on <body>, so without this the whole scope inherits centred text
   and the guard faithfully re-inherits it. Breaking the chain here is what
   makes the guard mean "left". */
.vq-root.vq-theme-firewall { text-align: left; }

/* IMPORTANT, and the reason for every `:not([class*="vq-"])` below.
 *
 * Raising the reset to (0,2,x) to beat the theme also raises it above the
 * component classes, which are a single class at (0,1,0). Left alone, the
 * reset wins against its own design system: `.vq-root.vq-root a` beats
 * `.vq-nav__item--active`, and the active nav pill silently loses its fill.
 * That is a real bug, found by looking at the rendered page rather than the
 * stylesheet.
 *
 * The fix is to reset only what this design system has NOT claimed. Any
 * element carrying a `vq-` class is skipped, so the component rule is the
 * only thing styling it. Unclassed elements, which is what a WordPress editor
 * or a plugin emits, still get the firewall.
 *
 * Consequence worth knowing: elements styled through a DESCENDANT selector
 * (`.vq-table th`, `.vq-card__media img`) carry no class of their own, so
 * they would still lose to a reset. That is why `th`, `td` and image sizing
 * are deliberately NOT reset below. Do not add them back. */

/* Headings: the theme sizes, colours and spaces these. */
.vq-root.vq-theme-firewall h1:not([class*="vq-"]),
.vq-root.vq-theme-firewall h2:not([class*="vq-"]),
.vq-root.vq-theme-firewall h3:not([class*="vq-"]),
.vq-root.vq-theme-firewall h4:not([class*="vq-"]),
.vq-root.vq-theme-firewall h5:not([class*="vq-"]),
.vq-root.vq-theme-firewall h6:not([class*="vq-"]) {
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  letter-spacing: inherit;
  color: inherit;
  text-transform: none;
}

/* Flow content: themes add bottom margins that break card padding. */
.vq-root.vq-theme-firewall p:not([class*="vq-"]),
.vq-root.vq-theme-firewall figure:not([class*="vq-"]),
.vq-root.vq-theme-firewall blockquote:not([class*="vq-"]),
.vq-root.vq-theme-firewall dl:not([class*="vq-"]),
.vq-root.vq-theme-firewall dd:not([class*="vq-"]),
.vq-root.vq-theme-firewall pre:not([class*="vq-"]) { margin: 0; }

.vq-root.vq-theme-firewall ul:not([class*="vq-"]),
.vq-root.vq-theme-firewall ol:not([class*="vq-"]) { margin: 0; padding: 0; list-style: none; }

/* Links: the theme colours and underlines these. Components opt back in. */
.vq-root.vq-theme-firewall a:not([class*="vq-"]) { color: inherit; text-decoration: none; background: none; }

/* Form controls do not inherit font by default, and themes restyle them
   heavily. Reset to inherit and let the component classes dress them. */
.vq-root.vq-theme-firewall button:not([class*="vq-"]),
.vq-root.vq-theme-firewall input:not([class*="vq-"]),
.vq-root.vq-theme-firewall select:not([class*="vq-"]),
.vq-root.vq-theme-firewall textarea:not([class*="vq-"]) {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  margin: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  text-transform: none;
}
.vq-root.vq-theme-firewall button:not([class*="vq-"]) { padding: 0; }

/* Only max-width. Sizing and display are left alone so `.vq-card__media img`
   and any other descendant-styled image keep working. */
.vq-root.vq-theme-firewall img:not([class*="vq-"]),
.vq-root.vq-theme-firewall svg:not([class*="vq-"]) { max-width: 100%; }

/* The other half of the `:not([class*="vq-"])` decision.
 *
 * Skipping vq-classed elements above means they no longer get the reset at
 * all, so the BROWSER defaults come back: every `<a>` underlines and every
 * `<button>` grows a grey border and background. Which is exactly what
 * happened the first time, on the nav.
 *
 * So they are normalised here instead, wrapped in :where() to force
 * specificity to (0,0,1). That is high enough to beat the user agent
 * stylesheet, and low enough that any component class, at (0,1,0), still
 * wins. The order matters more than the weight: this is a floor, not a rule.
 *
 * SCOPED TO THE FIREWALL, and that part is not cosmetic.
 *
 * These match ANY vq- class, not just the control classes: `button[class*="vq-"]`
 * catches a button carrying nothing but a layout helper like `vq-inline`. In
 * WordPress that is what you want, since there is no other reset and the
 * browser default would otherwise show through.
 *
 * Inside the mockup it is actively harmful. Tailwind's preflight already
 * normalises controls, so this adds nothing, and `background: none; border: 0`
 * being unlayered beats every Tailwind utility at any specificity. Putting one
 * layout class on a styled <button> silently strips its border and fill.
 * Measured: a bordered, tinted button lost both the moment `vq-inline-lg` was
 * added.
 *
 * So it applies only where the firewall is switched on, which is exactly where
 * it is needed. */
:where(.vq-root.vq-theme-firewall) :where(a[class*="vq-"]) {
  color: inherit;
  text-decoration: none;
}
:where(.vq-root.vq-theme-firewall) :where(button[class*="vq-"], input[class*="vq-"],
                                          select[class*="vq-"], textarea[class*="vq-"]) {
  font: inherit;
  color: inherit;
  margin: 0;
  background: none;
  border: 0;
}

.vq-root.vq-theme-firewall code:not([class*="vq-"]),
.vq-root.vq-theme-firewall pre:not([class*="vq-"]) { font-family: var(--vq-font-mono); }

.vq-root.vq-root :focus-visible {
  outline: 2px solid var(--vq-ring);
  outline-offset: 2px;
}

/* Browsers default <button> to cursor:default, unlike <a>. Restores the
   expected pointer on every control in one rule. */
.vq-root.vq-root button:not(:disabled),
.vq-root.vq-root select:not(:disabled),
.vq-root.vq-root [role="button"]:not([aria-disabled="true"]) { cursor: pointer; }
.vq-root.vq-root button:disabled,
.vq-root.vq-root select:disabled { cursor: not-allowed; }

/* Hidden utility, needed by the interface script for drawers and menus. */
/* Three scope classes, not two, and the reason is load-bearing.
 *
 * At (0,2,1) this loses to any component class, which sit at (0,3,0): a
 * `.vq-card` carrying `hidden` kept its `display: block` and stayed on
 * screen. That is what made search and filter look broken on resources.html
 * while sort still worked, since sort reorders and never hides. Reported by
 * Mike, 20 Aug.
 *
 * It only showed with vantiq-portal.css alone. Loaded alongside
 * vantiq-mockup.css the bug is masked, because the generated file carries
 * the Tailwind preflight [hidden] rule with !important.
 *
 * At (0,3,1) this clears every component class. Per the note at the top of
 * this file: add a class, do not reach for !important. */
.vq-root.vq-root.vq-root [hidden] { display: none; }


/* ============================================================================
   3. LAYOUT
   ========================================================================= */

/* The app shell: fixed header, fixed-width sidebar, scrolling content. */
.vq-root.vq-root .vq-shell { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.vq-root.vq-root .vq-shell__body { display: flex; flex: 1; overflow: hidden; }

.vq-root.vq-root .vq-header {
  display: flex;
  align-items: center;
  height: var(--vq-header-h);
  flex-shrink: 0;
  border-bottom: 1px solid var(--vq-border);
  background: var(--vq-card);
}

/* The header's left block is exactly the sidebar's width and carries the SAME
   right border, so the sidebar rule reads as one unbroken line from the very
   top of the page rather than starting below the header. */
.vq-root.vq-root .vq-header__brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: var(--vq-sidebar-w);
  flex-shrink: 0;
  min-width: 0;
  height: 100%;
  padding-inline: 1.25rem;
  border-right: 1px solid var(--vq-border);
}
@media (max-width: 1023px) {
  /* No sidebar below this width, so there is no line to continue. */
  .vq-root.vq-root .vq-header__brand { width: auto; border-right: 0; }
}

/* Column, so a footer block can be pinned to the bottom with margin-top:auto. */
.vq-root.vq-root .vq-sidebar {
  width: var(--vq-sidebar-w);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--vq-sidebar-border);
  background: var(--vq-sidebar);
  color: var(--vq-sidebar-foreground);
  overflow-y: auto;
}
@media (max-width: 1023px) { .vq-root.vq-root .vq-sidebar { display: none; } }

/* Pinned to the bottom of the sidebar regardless of how short the nav is. */
.vq-root.vq-root .vq-sidebar__footer {
  margin-top: auto;
  flex-shrink: 0;
  padding: 1rem 0.75rem;
  border-top: 1px solid var(--vq-sidebar-border);
}

.vq-root.vq-root .vq-main { flex: 1; overflow-y: auto; overflow-x: hidden; background: var(--vq-background); }

/* How much sits to the LEFT of the centred content column, which is what a
   full-bleed band has to cancel to reach the window edge. See .vq-hero.
   Only above 1023px, because that is where .vq-sidebar stops being display:none;
   below it there is no sidebar and the offset is correctly 0.
 *
 * `--app-sidebar-w` is the same number under the name the MOCKUP uses. Page
 * markup pasted straight out of pages/ carries its own inline bleed layer that
 * reads that variable, and with nothing defining it the variable falls back to
 * 0: the band then over-extends to the left, where overflow-x hides it, and
 * stops 130px short on the right, where it shows. Defining both names here
 * means pasted markup and hand-written markup bleed identically. */
@media (min-width: 1024px) {
  .vq-root.vq-root .vq-shell__body > .vq-main {
    --vq-bleed-offset: var(--vq-sidebar-w);
    --app-sidebar-w: var(--vq-sidebar-w);
  }
}

/* Centred content column.
 *
 * Deliberately NO padding and NO background. Pages supply their own content
 * and their own gutters, so anything imposed here has to be undone by every
 * template that wants a full-bleed band. This is a max width and a centre,
 * nothing else. Add `.vq-container--pad` when you do want the standard
 * gutters, or set padding on the section itself. */
.vq-root.vq-root .vq-container {
  max-width: var(--vq-container);
  margin-inline: auto;
}
.vq-root.vq-root .vq-container--pad {
  padding-inline: 1.5rem;
  padding-block: 2rem;
}
@media (min-width: 768px) {
  .vq-root.vq-root .vq-container--pad { padding-inline: 2.5rem; padding-block: 2.5rem; }
}

/* Vertical rhythm between page sections. Two weights, deliberately:
   landing-weight pages breathe, dense interior pages sit tighter. */
.vq-root.vq-root .vq-sections > * + * { margin-top: 3rem; }          /* 48px, dense interior */
.vq-root.vq-root .vq-sections--landing > * + * { margin-top: 4rem; } /* 64px, landing */

/* Page hero. The gradient runs from Azure Mist into the page field, so it
   reads as hero space rather than a header bar and blends into the content
   below it with no visible seam.
 *
 * FULL BLEED, WHICH IS THE WHOLE DIFFICULTY.
 *
 * The band has to reach the browser edge, but the element carrying it normally
 * sits inside .vq-container, which stops at --vq-container (1320px). On a wide
 * window that leaves plain page field either side of the gradient. Reported by
 * Mike, 17 Aug, and he was right: the gradient was simply a background on an
 * element that is not full width.
 *
 * There are two fixes here because they suit different templates.
 *
 * 1. `.vq-hero-band` is the gradient on its own. Put it on something that
 *    ALREADY spans the full width of <main>, either <main> itself or a wrapper
 *    outside .vq-container, and nest the container inside it. No arithmetic,
 *    so it stays correct at any width and whatever the surrounding template
 *    does. Prefer this where you control the markup.
 *
 *      <main class="vq-main">
 *        <div class="vq-hero-band">
 *          <div class="vq-container"><div class="vq-hero"> ... </div></div>
 *        </div>
 *        <div class="vq-container vq-container--pad"> ... </div>
 *      </main>
 *
 * 2. `.vq-hero` bleeds on its own, for markup where the hero is already inside
 *    the centred column and restructuring is not worth it. A pseudo-element is
 *    stretched outward by exactly the gutter between the column and the window,
 *    so it lands on the window edge at any width and collapses to no extension
 *    once the window is narrower than the column.
 *
 * The gutter is (window - whatever sits left of the column - column) / 2.
 * `--vq-bleed-offset` is that middle term, i.e. the sidebar. It is set on
 * .vq-main below and stays 0 anywhere without one, which is why this is also
 * correct on the signed-out pages that drop the sidebar entirely.
 *
 * One dependency worth stating: `100vw` counts the vertical scrollbar and the
 * layout box does not, so on a scrolling page the band over-extends by exactly
 * the scrollbar width, about 15px, on the side the column is measured from.
 * `.vq-main` sets `overflow-x: hidden`, which absorbs it; the right edge lands
 * exact and no horizontal scrollbar appears. If you put this hero inside
 * something other than `.vq-main`, give that element `overflow-x: hidden` too.
 * Option 1 above has none of this arithmetic and none of this caveat. */
.vq-root.vq-root .vq-hero {
  position: relative;
  padding: 4rem 1.5rem 2.5rem;
}
.vq-root.vq-root .vq-hero::before {
  content: "";
  position: absolute;
  inset-block: 0;
  left:  calc(0px - max(0px, (100vw - var(--vq-bleed-offset, 0px) - var(--vq-container)) / 2));
  right: calc(0px - max(0px, (100vw - var(--vq-bleed-offset, 0px) - var(--vq-container)) / 2));
  background: linear-gradient(to bottom, var(--vq-secondary) 0%, var(--vq-card) 100%);
  pointer-events: none;
}
/* The gradient above is a positioned element, so anything that must sit on top
   of it needs a position too. `.vq-hero__inner` is the documented content
   wrapper and gets one here; give any other direct child of .vq-hero
   `position: relative` as well, or it will paint behind the band. */
.vq-root.vq-root .vq-hero__inner { position: relative; }

/* The same gradient with no bleed arithmetic, for an element already spanning
   the full width. See note 1 above. */
.vq-root.vq-root .vq-hero-band {
  background: linear-gradient(to bottom, var(--vq-secondary) 0%, var(--vq-card) 100%);
}
/* Nested inside a band, .vq-hero must not paint a second gradient over it. */
.vq-root.vq-root .vq-hero-band .vq-hero::before { content: none; }

@media (min-width: 768px) { .vq-root.vq-root .vq-hero { padding: 5rem 2.5rem 2.5rem; } }
.vq-root.vq-root .vq-hero__inner { max-width: 48rem; display: flex; flex-direction: column; gap: 0.75rem; }
/* 30px/36px below 640, 36px/40px above. Both the leading and the tracking
   were measured off the mockup: it was inheriting 1.5 (54px) and using
   -0.02em where the mockup renders -0.025em. */
.vq-root.vq-root .vq-hero__title {
  font-size: 1.875rem; line-height: 2.25rem;
  font-weight: 600; letter-spacing: -0.025em; margin: 0;
}
@media (min-width: 640px) {
  .vq-root.vq-root .vq-hero__title { font-size: 2.25rem; line-height: 2.5rem; }
}
/* Pin a control to the hero's top right (e.g. a bookmark toggle). */
.vq-root.vq-root .vq-hero__corner { position: absolute; top: 1rem; right: 1rem; }

/* Responsive card grids. These three cover every grid in the mockup. */
.vq-root.vq-root .vq-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
.vq-root.vq-root .vq-grid--gap-lg { gap: 1.5rem; }
@media (min-width: 640px) { .vq-root.vq-root .vq-grid--2, .vq-root.vq-root .vq-grid--3, .vq-root.vq-root .vq-grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .vq-root.vq-root .vq-grid--3 { grid-template-columns: repeat(3, 1fr); }
                             .vq-root.vq-root .vq-grid--4 { grid-template-columns: repeat(4, 1fr); } }


/* ============================================================================
   4. TYPE ROLES
   Named for the job, not the size, so the same role stays consistent
   everywhere. These five cover the great majority of text in the mockup:
   `text-sm font-medium text-foreground` alone appears 33 times inline.
   ========================================================================= */

/* LINE HEIGHT IS DECLARED, NOT INHERITED.
 *
 * This was wrong until it was measured. Every role here omitted line-height
 * and inherited 1.5 from the root, but the mockup pairs a line height with
 * each size the way Tailwind does, so every one of these was too loose:
 *
 *     12px text   was 18px   should be 16px
 *     14px text   was 21px   should be 20px
 *     36px title  was 54px   should be 40px
 *
 * On a 36px page title that is 14px of extra leading per line. Reconciled
 * against the running mockup, not chosen. */
.vq-root.vq-root .vq-eyebrow {                 /* small caps label above a title */
  font-size: 0.75rem; line-height: 1rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--vq-muted-foreground); margin: 0;
}
.vq-root.vq-root .vq-title    { font-size: 1.125rem; line-height: 1.75rem; font-weight: 600; color: var(--vq-foreground); margin: 0; }
.vq-root.vq-root .vq-subtitle { font-size: 1rem;     line-height: 1.5rem;  font-weight: 600; color: var(--vq-foreground); margin: 0; }
.vq-root.vq-root .vq-label    { font-size: 0.875rem; line-height: 1.25rem; font-weight: 500; color: var(--vq-foreground); margin: 0; }
.vq-root.vq-root .vq-body     { font-size: 0.875rem; line-height: 1.25rem; color: var(--vq-foreground); margin: 0; }
.vq-root.vq-root .vq-meta     { font-size: 0.75rem;  line-height: 1rem;    color: var(--vq-muted-foreground); margin: 0; }
.vq-root.vq-root .vq-muted    { color: var(--vq-muted-foreground); }

/* Two sizes below the documented ramp, used 20 times between them for card
   category labels and dense meta rows. Kept because the refactor's contract
   is that nothing changes how it renders, not because 10px and 11px are
   values the design system should have. Worth a decision from Alsu on whether
   they collapse into .vq-meta at 12px; that is a design change, not a port. */
/* line-height stays at the inherited 1.5 rather than the 1rem .vq-meta uses,
   because that is what these render at today. */
.vq-root.vq-root .vq-meta--sm { font-size: 0.6875rem; line-height: 1.5; }  /* 11px */
.vq-root.vq-root .vq-meta--xs { font-size: 0.625rem;  line-height: 1.5; }  /* 10px */

/* A single figure with its caption, as used across the dashboards. The values
   are the same as .vq-meta and .vq-title on purpose: this is not a new type
   step, it is a name for a pair that always appears together. `vq-title` on a
   number would read as a heading to anyone copying the markup, which is the
   one thing this vocabulary is meant to avoid. Wrap them in whatever grid or
   stack the page needs; there is no wrapper class, because the arrangement
   genuinely varies. */
.vq-root.vq-root .vq-stat__label { font-size: 0.75rem;  line-height: 1rem;    color: var(--vq-muted-foreground); margin: 0; }
.vq-root.vq-root .vq-stat__value { font-size: 1.125rem; line-height: 1.75rem; font-weight: 600; color: var(--vq-foreground); margin: 0; }

/* Section heading: a label with an optional action on the right. */
/* Reconciled against the running mockup: alignment was `baseline` where the
   real one is `flex-start`, and the bottom margin was 12px against its 16px.
   Baseline alignment visibly drops the trailing control when the heading
   wraps to two lines, which is why the mockup does not use it. */
.vq-root.vq-root .vq-section-heading {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; margin-bottom: 1rem;
}
/* The heading itself. Emphasis rather than foreground, and a row so it can
   carry a leading icon. Note this is the one heading in the system that is
   NOT foreground-coloured. */
.vq-root.vq-root .vq-section-heading__title {
  display: flex; align-items: center; gap: 0.5rem;
  font-size: 0.875rem; line-height: 1.25rem; font-weight: 500;
  color: var(--vq-emphasis);
  margin: 0;
}
/* Optional sub-line. Carries its own 2px offset, since the type roles set
   `margin: 0` and would beat a utility at the call site. */
.vq-root.vq-root .vq-section-heading__desc {
  font-size: 0.75rem; line-height: 1rem;
  color: var(--vq-muted-foreground);
  margin: 0.125rem 0 0;
}
/* The "View all" affordance beside a section heading. */
.vq-root.vq-root .vq-section-heading__link {
  font-size: 0.75rem; line-height: 1rem;
  color: var(--vq-emphasis);
  text-decoration: none;
}
.vq-root.vq-root .vq-section-heading__link:hover { text-decoration: underline; }

/* Truncation that actually works. NOTE: `overflow:hidden` is inert on an
   inline box, so this sets display:block. Putting a truncate class on a bare
   <a> without this is the single most common way to break a row layout. */
.vq-root.vq-root .vq-truncate { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.vq-root.vq-root .vq-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }


/* ============================================================================
   5. COMPONENTS
   ========================================================================= */

/* --- Card ------------------------------------------------------------- */
/* `display: block` is declared, not assumed. These classes get applied to
   whatever element the template emits, and a whole-tile link is usually an
   <a>, which is inline by default. Without this the card collapses and the
   media band renders about 20px tall. */
.vq-root.vq-root .vq-card {
  display: block;
  background: var(--vq-card);
  color: var(--vq-card-foreground);
  /* The mockup's cards carry a hairline AND the shadow. This file had only
     the shadow, so every card sat 2px smaller and read slightly softer. */
  border: 1px solid var(--vq-border);
  border-radius: var(--vq-radius-xl);
  box-shadow: var(--vq-shadow-card);
}
.vq-root.vq-root .vq-card--flat    { box-shadow: none; border: 1px solid var(--vq-border); }
.vq-root.vq-root .vq-card--pad     { padding: 1.5rem; }
.vq-root.vq-root .vq-card--pad-sm  { padding: 1rem; }
/* Vertical padding only, for a card whose rows supply their own gutters. */
.vq-root.vq-root .vq-card--pad-y   { padding-block: 1.5rem; }

/* A card whose children stack with a gap, rather than a plain box the markup
   spaces itself. This is what the mockup's Card is by default, and getting it
   wrong is expensive: the resource cards space their children with this 24px
   gap AND their own margins, so dropping to a plain block silently removes
   24px from every card on the page.
   Deliberately carries no padding, so a call site can still set its own. */
.vq-root.vq-root .vq-card--stack {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
/* The same column WITHOUT the gap, for a card that sets its own spacing.
   Needed because `--stack` sets gap at a specificity no utility can reach. */
.vq-root.vq-root .vq-card--col  { display: flex; flex-direction: column; }
/* A row. Gap deliberately unset, so the call site keeps control of it. */
.vq-root.vq-root .vq-card--row  { display: flex; flex-direction: row; }
/* A grid. Columns deliberately unset, for the same reason. */
.vq-root.vq-root .vq-card--grid { display: grid; }
/* Whole-tile link card. .vq-card now supplies the border, so this only
   animates its colour; declaring a transparent border here would wipe the
   hairline the card is supposed to have. */
.vq-root.vq-root .vq-card--link {
  transition: border-color .15s;
}
.vq-root.vq-root .vq-card--link:hover { border-color: var(--vq-primary); }

/* The title and description inside a card tile. These carry their own top
   margin on purpose: the type roles all set `margin: 0` to fend off theme
   defaults, which means a `mt-3` at the call site loses to them. Absorbing
   the spacing into the class is the fix, and it is also the pattern the
   vocabulary asks for: a card title is a thing, not a stack of utilities. */
.vq-root.vq-root .vq-card__title {
  font-size: 0.875rem; line-height: 1.25rem; font-weight: 500;
  color: var(--vq-foreground);
  margin: 0.75rem 0 0;
}
.vq-root.vq-root .vq-card__desc {
  font-size: 0.75rem; line-height: 1rem;
  color: var(--vq-muted-foreground);
  margin: 0.25rem 0 0;
}
.vq-root.vq-root .vq-card--link:hover { border-color: var(--vq-primary); }
/* Media band at the top of a card. Clip on the media, not the card, or you
   crop the focus ring off every card in a grid. */
.vq-root.vq-root .vq-card__media {
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--vq-radius-lg);
  background: var(--vq-surface-sunken);
}
.vq-root.vq-root .vq-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Same reasoning: these are frequently applied to <a> or <span> inside a
   whole-tile link, where inline layout would break them. */
.vq-root.vq-root .vq-row { display: flex; }
.vq-root.vq-root .vq-badge { display: inline-flex; }
.vq-root.vq-root .vq-btn { display: inline-flex; }

/* --- Button ----------------------------------------------------------- */
.vq-root.vq-root .vq-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  white-space: nowrap; border-radius: var(--vq-radius-md);
  font-family: inherit; font-size: 0.875rem; font-weight: 500;
  height: 2.25rem; padding-inline: 1rem;
  border: 1px solid transparent; cursor: pointer;
  transition: background-color .15s, color .15s, opacity .15s, box-shadow .15s;
}
.vq-root.vq-root .vq-btn:disabled { pointer-events: none; opacity: 0.5; }
.vq-root.vq-root .vq-btn:focus-visible { outline: 2px solid var(--vq-ring); outline-offset: 2px; }

/* Default. This is a soft gradient chip rather than a solid teal fill; it is
   what the mockup currently ships. For a solid brand button use .vq-btn--cta. */
.vq-root.vq-root .vq-btn--default {
  border-color: color-mix(in oklab, var(--vq-primary) 25%, transparent);
  background-image: linear-gradient(to bottom right,
    color-mix(in oklab, var(--vq-emphasis) 20%, transparent), var(--vq-accent), var(--vq-secondary));
  color: var(--vq-foreground);
}
.vq-root.vq-root .vq-btn--default:hover { opacity: 0.9; }

.vq-root.vq-root .vq-btn--cta        { background: var(--vq-primary); color: var(--vq-primary-foreground); }
.vq-root.vq-root .vq-btn--cta:hover  { background: color-mix(in srgb, var(--vq-primary) 90%, black); }

.vq-root.vq-root .vq-btn--secondary       { background: var(--vq-secondary); color: var(--vq-secondary-foreground); }
.vq-root.vq-root .vq-btn--secondary:hover { background: color-mix(in oklab, var(--vq-secondary) 80%, transparent); }

.vq-root.vq-root .vq-btn--outline       { background: var(--vq-background); border-color: var(--vq-border); box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); }
.vq-root.vq-root .vq-btn--outline:hover { background: var(--vq-accent); color: var(--vq-accent-foreground); }

/* A destructive action that is not the primary button on the screen: the
   Reset control in Settings. Must come after --outline, which sets
   border-color at the same (0,3,0) and would otherwise win on source order. */
.vq-root.vq-root .vq-btn--outline-danger {
  border-color: color-mix(in oklab, var(--vq-destructive) 40%, transparent);
  color: var(--vq-destructive);
}
.vq-root.vq-root .vq-btn--outline-danger:hover {
  background: color-mix(in oklab, var(--vq-destructive) 5%, transparent);
  color: var(--vq-destructive);
}

.vq-root.vq-root .vq-btn--ghost:hover { background: var(--vq-accent); color: var(--vq-accent-foreground); }

.vq-root.vq-root .vq-btn--destructive       { background: var(--vq-destructive); color: #fff; }
.vq-root.vq-root .vq-btn--destructive:hover { background: color-mix(in srgb, var(--vq-destructive) 90%, black); }

.vq-root.vq-root .vq-btn--link {
  height: auto; padding: 0; background: none; border: none;
  color: var(--vq-primary); text-underline-offset: 4px;
}
.vq-root.vq-root .vq-btn--link:hover { text-decoration: underline; }
/* The quiet version: a secondary link-button, smaller and muted until hover.
   Used for "Lost your password?" under the sign-in form. Must come after
   --link, since both land at (0,3,0) and only source order separates them. */
.vq-root.vq-root .vq-btn--link-quiet {
  font-size: 0.75rem; font-weight: 400; line-height: 1;
  color: var(--vq-muted-foreground);
}
.vq-root.vq-root .vq-btn--link-quiet:hover { color: var(--vq-primary); }


.vq-root.vq-root .vq-btn--sm   { height: 2rem;   padding-inline: 0.75rem; gap: 0.375rem; }
.vq-root.vq-root .vq-btn--lg   { height: 2.5rem; padding-inline: 1.5rem; }
.vq-root.vq-root .vq-btn--icon    { width: 2.25rem; padding: 0; }
.vq-root.vq-root .vq-btn--icon-sm { width: 2rem;   height: 2rem;   padding: 0; }
.vq-root.vq-root .vq-btn--icon-lg { width: 2.5rem; height: 2.5rem; padding: 0; }

/* Icons inside a button are 16px unless the markup sizes them itself, never
   shrink, and never swallow the click. */
.vq-root.vq-root .vq-btn svg { flex-shrink: 0; pointer-events: none; }
.vq-root.vq-root .vq-btn svg:not([class*="size-"]):not([width]) { width: 1rem; height: 1rem; }

/* A button holding an icon gets tighter side padding, because the icon reads
   as part of the padding. The mockup expresses this as `has-[>svg]:px-3`. */
.vq-root.vq-root .vq-btn:has(> svg)              { padding-inline: 0.75rem; }
.vq-root.vq-root .vq-btn--sm:has(> svg)          { padding-inline: 0.625rem; }
.vq-root.vq-root .vq-btn--lg:has(> svg)          { padding-inline: 1rem; }
.vq-root.vq-root .vq-btn--icon:has(> svg),
.vq-root.vq-root .vq-btn--icon-sm:has(> svg),
.vq-root.vq-root .vq-btn--icon-lg:has(> svg)     { padding-inline: 0; }

/* A button whose label may wrap onto two lines, so it grows with its content
   instead of keeping a fixed control height. Used where the label is a
   sentence rather than a verb, e.g. "Get Access to Full Training".
   MUST come after the size modifiers: --sm and --lg set an explicit height at
   the same (0,3,0), and only source order separates them. */
.vq-root.vq-root .vq-btn--wrap {
  height: auto;
  white-space: normal;
  text-align: left;
}
/* Muted label. A class rather than a utility because the element reset near
   the top of this file sets `color: inherit` on vq- classed buttons, and
   being unlayered it beats any Tailwind colour utility whatever its
   specificity. */
.vq-root.vq-root .vq-btn--muted { color: var(--vq-muted-foreground); }

/* --- Badge ------------------------------------------------------------ */
.vq-root.vq-root .vq-badge {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.25rem;
  width: fit-content; white-space: nowrap;
  border: 1px solid transparent; border-radius: var(--vq-radius-md);
  flex-shrink: 0; overflow: hidden;
  padding: 0.125rem 0.5rem;
  font-size: 0.75rem; line-height: 1rem; font-weight: 500;
  background: var(--vq-primary); color: var(--vq-primary-foreground);
  transition: color .15s, box-shadow .15s;
}
/* Icons inside a badge are 12px, and must not swallow the click when the
   badge is a link or a filter button. */
.vq-root.vq-root .vq-badge > svg { width: 0.75rem; height: 0.75rem; pointer-events: none; }
.vq-root.vq-root .vq-badge--destructive { background: var(--vq-destructive); color: #fff; border-color: transparent; }
/* 10px badge, for dense tag rows on course tiles. Line height stays at the
   badge's 1rem, which is what these rendered at before. */
.vq-root.vq-root .vq-badge--xs { font-size: 0.625rem; }
.vq-root.vq-root .vq-badge--secondary   { background: var(--vq-secondary); color: var(--vq-secondary-foreground); }
.vq-root.vq-root .vq-badge--outline     { background: transparent; border-color: var(--vq-border); color: var(--vq-foreground); }

/* Tinted status badges. This 10%-fill-plus-solid-text pattern is the house
   style for anything reporting a state. Note the odd one out: plain
   `.vq-badge` is SOLID primary, so the tinted teal that belongs to this
   family needs its own modifier rather than being the default. */
.vq-root.vq-root .vq-badge--primary  { background: color-mix(in oklab, var(--vq-primary) 10%, transparent);  color: var(--vq-primary); }
.vq-root.vq-root .vq-badge--success  { background: color-mix(in oklab, var(--vq-success) 10%, transparent);  color: var(--vq-success); }
.vq-root.vq-root .vq-badge--warning  { background: color-mix(in oklab, var(--vq-warning) 10%, transparent);  color: var(--vq-warning); }
.vq-root.vq-root .vq-badge--info     { background: color-mix(in oklab, var(--vq-info) 10%, transparent);     color: var(--vq-info); }
.vq-root.vq-root .vq-badge--critical { background: color-mix(in oklab, var(--vq-critical) 10%, transparent); color: var(--vq-critical); }
.vq-root.vq-root .vq-badge--emphasis { background: color-mix(in oklab, var(--vq-emphasis) 10%, transparent); color: var(--vq-emphasis); }

/* Neutral fills, for a badge that is a count or a quiet marker rather than a
   status. These MUST sit after --secondary: a call site often passes
   variant="secondary" and a tone together, both land at (0,3,0), and source
   order is the only thing separating them. Declared above --secondary they
   lose, and the badge silently renders Azure Mist instead. */
.vq-root.vq-root .vq-badge--muted { background: var(--vq-muted); color: var(--vq-muted-foreground); }
.vq-root.vq-root .vq-badge--card  { background: var(--vq-card); color: var(--vq-foreground); border-color: transparent; }

/* Toggleable filter chip, used above list pages.
 *
 * Reconciled against the running mockup, where this is not a pill at all: it
 * is a Badge that happens to be a button. It was previously built as a 32px
 * pill with a 9999px radius and 6px/12px padding, against a real 22px chip
 * with a 6px radius and 2px/8px padding. Colours were right, geometry was not.
 *
 * `display` is declared rather than assumed. On a <button> an undeclared
 * display falls back to inline-block, which is the same trap that once made
 * .vq-card__media collapse to 21px.
 *
 * Geometry is deliberately identical to .vq-badge; only the interactive
 * states are added here. */
.vq-root.vq-root .vq-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.25rem;
  width: fit-content; white-space: nowrap;
  border: 1px solid transparent; border-radius: var(--vq-radius-md);
  padding: 0.125rem 0.5rem;
  font-size: 0.75rem; line-height: 1rem; font-weight: 500;
  background: var(--vq-secondary); color: var(--vq-secondary-foreground);
  cursor: pointer;
  transition: background-color .15s, color .15s, border-color .15s;
}
.vq-root.vq-root .vq-chip:hover           { border-color: var(--vq-primary); }
.vq-root.vq-root .vq-chip[aria-pressed="true"],
.vq-root.vq-root .vq-chip--active         { background: var(--vq-primary); border-color: var(--vq-primary); color: var(--vq-primary-foreground); }

/* --- Horizontal tab bar ------------------------------------------------ */
/* Sits directly under the header on pages that have no sidebar, which in the
   mockup means the signed-out visitor view of /resources and
   /resources/knowledge-base. Values measured off the running mockup: 44px
   tall, card background, one border underneath, 4px between items. */
.vq-root.vq-root .vq-tabs {
  display: flex; align-items: center; gap: 0.25rem;
  height: 2.75rem; flex-shrink: 0;
  padding-inline: 1.5rem;
  background: var(--vq-card);
  border-bottom: 1px solid var(--vq-border);
  overflow-x: auto;
}
.vq-root.vq-root .vq-tabs__item {
  display: inline-flex; align-items: center; white-space: nowrap;
  height: 2rem; padding: 0.375rem 0.75rem;
  border: 0; background: transparent;
  border-radius: var(--vq-radius-md);
  font-size: 0.875rem; font-weight: 500;
  color: var(--vq-muted-foreground);
  cursor: pointer;
  transition: background-color .15s, color .15s;
}
.vq-root.vq-root .vq-tabs__item:hover {
  background: var(--vq-muted); color: var(--vq-foreground);
}
.vq-root.vq-root .vq-tabs__item--active,
.vq-root.vq-root .vq-tabs__item[aria-current="page"] {
  background: var(--vq-secondary); color: var(--vq-secondary-foreground);
}

/* --- Form controls ---------------------------------------------------- */
/* Reconciled against the running mockup rather than written by eye. Two
   values were wrong here and both are visible: the radius was 8px where the
   mockup renders 6px, and the border took --vq-border (#d8e3e2) where the
   mockup uses the softer --vq-input (#e9f1f0). That is what the --vq-input
   token was named for. */
.vq-root.vq-root .vq-input, .vq-root.vq-root .vq-select, .vq-root.vq-root .vq-textarea {
  width: 100%; min-width: 0;
  height: 2.25rem; padding: 0.25rem 0.75rem;
  font-family: inherit;
  /* 16px on small screens, 14px from 768px up. The 16px is not a style
     choice: iOS Safari zooms the viewport when a focused field is under 16px,
     and dropping to 14px here reintroduces that jump on every iPhone. The
     mockup expresses this as `text-base md:text-sm`. */
  font-size: 1rem; line-height: 1.5rem;
  color: var(--vq-foreground);
  background: transparent; border: 1px solid var(--vq-input);
  border-radius: var(--vq-radius-md);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);   /* the mockup's shadow-xs */
  /* Matches the mockup's `transition-[color,box-shadow]`. Transitioning
     border-color instead would arguably look better, since the border is what
     changes on focus, but that is a design change rather than a port. */
  transition: color .15s, box-shadow .15s;
}
@media (min-width: 768px) {
  .vq-root.vq-root .vq-input,
  .vq-root.vq-root .vq-select,
  .vq-root.vq-root .vq-textarea { font-size: 0.875rem; line-height: 1.25rem; }
}
/* Reconciled against the running mockup: min-height was 5rem against its
   4rem, and `display` was left to the element default where the mockup sets
   flex. `resize: vertical` stays, because the mockup does compute that.
   `field-sizing: content` is what lets the field grow with its text; browsers
   without it just keep the min-height, which is the old behaviour rather than
   a broken one. */
.vq-root.vq-root .vq-textarea {
  display: flex;
  height: auto;
  min-height: 4rem;
  padding-block: 0.5rem;
  resize: vertical;
  field-sizing: content;
}
.vq-root.vq-root .vq-input:focus-visible, .vq-root.vq-root .vq-select:focus-visible, .vq-root.vq-root .vq-textarea:focus-visible {
  border-color: var(--vq-ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--vq-ring) 50%, transparent);
  outline: none;
}
.vq-root.vq-root .vq-input::placeholder, .vq-root.vq-root .vq-textarea::placeholder { color: var(--vq-muted-foreground); }

/* Disabled and invalid. These are real design states rather than theme
   defence, so they belong here: without them, converting the mockup's Input
   component to .vq-input would silently drop both. */
.vq-root.vq-root .vq-input:disabled,
.vq-root.vq-root .vq-select:disabled,
.vq-root.vq-root .vq-textarea:disabled {
  pointer-events: none;
  cursor: not-allowed;
  opacity: 0.5;
}
.vq-root.vq-root .vq-input[aria-invalid="true"],
.vq-root.vq-root .vq-select[aria-invalid="true"],
.vq-root.vq-root .vq-textarea[aria-invalid="true"] {
  border-color: var(--vq-destructive);
}
.vq-root.vq-root .vq-input[aria-invalid="true"]:focus-visible,
.vq-root.vq-root .vq-select[aria-invalid="true"]:focus-visible,
.vq-root.vq-root .vq-textarea[aria-invalid="true"]:focus-visible {
  border-color: var(--vq-destructive);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--vq-destructive) 20%, transparent);
}
/* A field that is filled rather than transparent: read-only values, or an
   input that should read as disabled without being disabled. */
.vq-root.vq-root .vq-input--muted {
  background: var(--vq-muted);
  color: var(--vq-muted-foreground);
}

/* Selection colour inside fields, matching the mockup's `selection:bg-primary`. */
.vq-root.vq-root .vq-input::selection,
.vq-root.vq-root .vq-textarea::selection {
  background: var(--vq-primary);
  color: var(--vq-primary-foreground);
}

/* Header search. Not transparent like a form input: it sits on the white
   header and needs to read as a filled control, so it takes the page field
   colour with the softer input border. Measured off the mockup:
   background #F3F8F7, border #E9F1F0, radius 6px. */
.vq-root.vq-root .vq-search {
  background: var(--vq-background);
  border-color: var(--vq-input);
  border-radius: var(--vq-radius-md);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-align: left;
  color: var(--vq-muted-foreground);
  cursor: pointer;
}
/* Input with a leading icon. Wrap the input and an inline <svg>; the icon is
   positioned and the input gets room made for it, so no padding guessing at
   the call site. pointer-events:none keeps the icon from eating the click
   that should focus the field. */
.vq-root.vq-root .vq-input-icon { position: relative; display: block; }
.vq-root.vq-root .vq-input-icon > svg {
  position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%);
  /* Sized here rather than left to the icon library's default of 24px. The
     mockup renders this slot at 16px, and an unsized SVG overlaps the text. */
  width: 1rem; height: 1rem;
  color: var(--vq-muted-foreground); pointer-events: none;
}
.vq-root.vq-root .vq-input-icon > .vq-input { padding-left: 2.25rem; }

.vq-root.vq-root .vq-field { display: flex; flex-direction: column; gap: 0.375rem; }

/* The form label. A row rather than a block, because a label often carries a
   required marker or a hint icon beside its text. `line-height: 1` is
   deliberate and measured: a label sits directly above its control and the
   usual 1.25rem leading adds a visible gap. Not to be confused with
   `.vq-label`, which is the TYPE ROLE used for card titles and list rows. */
.vq-root.vq-root .vq-field__label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1;
  font-weight: 500;
  user-select: none;
}

/* --- Checkbox and switch ----------------------------------------------- */
/* Both are driven by Radix, which writes `data-state` on the element. The
   visual base lives here; the state selectors key off that attribute rather
   than a class, so the same CSS works whether the markup comes from React or
   from a PHP template that sets the attribute itself. */
.vq-root.vq-root .vq-checkbox {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1rem; height: 1rem; flex-shrink: 0;
  border: 1px solid var(--vq-input);
  border-radius: var(--vq-radius-sm);          /* 4px */
  background: transparent;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition: box-shadow .15s;
  cursor: pointer;
}
.vq-root.vq-root .vq-checkbox[data-state="checked"],
.vq-root.vq-root .vq-checkbox[aria-checked="true"] {
  background: var(--vq-primary);
  border-color: var(--vq-primary);
  color: var(--vq-primary-foreground);
}
.vq-root.vq-root .vq-checkbox:disabled { cursor: not-allowed; opacity: 0.5; }
.vq-root.vq-root .vq-checkbox[aria-invalid="true"] { border-color: var(--vq-destructive); }
.vq-root.vq-root .vq-checkbox__indicator {
  display: flex; align-items: center; justify-content: center; color: currentColor;
}
.vq-root.vq-root .vq-checkbox__indicator svg { width: 0.875rem; height: 0.875rem; }

/* The track. 1.15rem is not a typo and not on the radius scale: it is the
   height the mockup ships, and it is what makes the 1rem thumb sit with a
   hairline of track visible above and below. */
.vq-root.vq-root .vq-switch {
  display: inline-flex; align-items: center;
  width: 2rem; height: 1.15rem; flex-shrink: 0;
  border: 1px solid transparent;
  border-radius: 9999px;
  background: var(--vq-input);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition: background-color .15s, box-shadow .15s;
  cursor: pointer;
}
.vq-root.vq-root .vq-switch[data-state="checked"],
.vq-root.vq-root .vq-switch[aria-checked="true"] { background: var(--vq-primary); }
.vq-root.vq-root .vq-switch:disabled { cursor: not-allowed; opacity: 0.5; }
/* Moved with the `translate` property rather than `transform`, matching the
   mockup. Both look identical; they are not interchangeable to anything that
   reads computed style, and `translate` leaves `transform` free for a
   separate effect. */
.vq-root.vq-root .vq-switch__thumb {
  display: block; width: 1rem; height: 1rem;
  border-radius: 9999px;
  background: var(--vq-background);
  pointer-events: none;
  transition: translate .15s;
}
.vq-root.vq-root .vq-switch__thumb[data-state="checked"] { translate: calc(100% - 2px); }
.vq-root.vq-root .vq-switch__thumb[data-state="unchecked"] { translate: 0; }

/* Small tinted square holding an icon, the marker on a callout row. */
.vq-root.vq-root .vq-icon-tile {
  display: grid; place-items: center; flex-shrink: 0;
  width: 2rem; height: 2rem;
  border-radius: var(--vq-radius-md);
  background: color-mix(in oklab, var(--vq-emphasis) 10%, transparent);
  color: var(--vq-emphasis);
}

/* --- Row: icon, text block, trailing control -------------------------- */
/* The workhorse list item. min-width:0 on the text block is what stops long
   titles pushing the trailing control off the end of the row. */
.vq-root.vq-root .vq-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; }
.vq-root.vq-root .vq-row--bordered { border: 1px solid var(--vq-border); border-radius: var(--vq-radius-md); }
.vq-root.vq-root .vq-row__body { min-width: 0; flex: 1; }
.vq-root.vq-root .vq-row__end  { flex-shrink: 0; margin-left: auto; }
/* The second line under a row's title. Carries its own 2px offset, since the
   type roles set `margin: 0` and beat a utility at the call site. */
.vq-root.vq-root .vq-row__sub {
  font-size: 0.75rem; line-height: 1rem;
  color: var(--vq-muted-foreground);
  margin: 0.125rem 0 0;
}
/* A row that is itself a target or a link: the border picks up the brand
   colour on hover, and on :target so an anchored jump is visible. */
.vq-root.vq-root .vq-row--link { transition: border-color .15s, background-color .15s; }
.vq-root.vq-root .vq-row--link:hover  { border-color: var(--vq-primary); }
.vq-root.vq-root .vq-row--link:target {
  border-color: var(--vq-primary);
  background: color-mix(in oklab, var(--vq-primary) 5%, transparent);
}
.vq-root.vq-root .vq-list > * + * { border-top: 1px solid var(--vq-border); }

/* --- Table ------------------------------------------------------------ */
.vq-root.vq-root .vq-table-wrap { width: 100%; overflow-x: auto; }
.vq-root.vq-root .vq-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.vq-root.vq-root .vq-table th {
  text-align: left; font-weight: 500; color: var(--vq-muted-foreground);
  padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--vq-border);
  background: var(--vq-card);
}
.vq-root.vq-root .vq-table td { padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--vq-border); }
.vq-root.vq-root .vq-table tbody tr:last-child td { border-bottom: 0; }
.vq-root.vq-root .vq-table tbody tr:hover { background: var(--vq-muted); }
/* A sticky header or footer MUST have an opaque background or scrolled rows
   show through it. */
.vq-root.vq-root .vq-table--sticky thead th { position: sticky; top: 0; z-index: 2; }
.vq-root.vq-root .vq-table--sticky tfoot td { position: sticky; bottom: 0; z-index: 2; background: var(--vq-muted); font-weight: 500; }
.vq-root.vq-root .vq-num { font-variant-numeric: tabular-nums; }

/* --- Sidebar navigation ----------------------------------------------- */
.vq-root.vq-root .vq-nav { padding: 0.75rem; }
.vq-root.vq-root .vq-nav__group-label {
  font-size: 0.6875rem; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--vq-muted-foreground);
  padding: 0.5rem 0.75rem;
}
/* A leaf nav entry: one control that navigates. */
.vq-root.vq-root .vq-nav__item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.625rem 0.75rem; border-radius: var(--vq-radius-md);
  font-size: 0.875rem; color: var(--vq-sidebar-foreground);
  transition: background-color .15s;
}
.vq-root.vq-root .vq-nav__item:hover { background: var(--vq-sidebar-accent); }
/* Active leaf: teal text at weight 500 on a transparent ground, NOT a solid
   pill. Measured off the mockup: background rgba(0,0,0,0), colour #0F5A5B,
   font-weight 500. The solid fill is reserved for the group row below, and
   only when you are on that section's own landing page. */
.vq-root.vq-root .vq-nav__item--active {
  background: transparent;
  color: var(--vq-primary);
  font-weight: 500;
}
.vq-root.vq-root .vq-nav__item--active:hover { background: var(--vq-sidebar-accent); }

/* --- Nav GROUP header ------------------------------------------------
 * A section header is deliberately TWO controls sharing one row, not one:
 *
 *   <div class="vq-nav__group">
 *     <a class="vq-nav__link" href="/sales-hub/"> icon + Sales Hub </a>
 *     <button class="vq-nav__toggle" data-vq-accordion-trigger
 *             aria-expanded="false" aria-controls="nav-sales"> chevron </button>
 *   </div>
 *   <div class="vq-nav__sub" id="nav-sales" data-vq-accordion-panel hidden> ... </div>
 *
 * The link navigates to the section landing page, the button expands the
 * group, and they are independent. That is why the anchor is flex: 1. Merging
 * them into a single button removes the ability to reach the landing page at
 * all, which is a real loss of function rather than a simplification.
 *
 * The hover and active fill belong on the row wrapper so both halves light up
 * together.
 * ------------------------------------------------------------------- */
.vq-root.vq-root .vq-nav__group {
  display: flex; align-items: center;
  border-radius: var(--vq-radius-md);
  color: var(--vq-sidebar-foreground);
  transition: background-color .15s;
}
.vq-root.vq-root .vq-nav__group:hover { background: var(--vq-sidebar-accent); }
.vq-root.vq-root .vq-nav__group--active { background: var(--vq-sidebar-accent); }

.vq-root.vq-root .vq-nav__link {
  display: flex; flex: 1; min-width: 0; align-items: center; gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  font-size: 0.875rem; color: inherit;
}
.vq-root.vq-root .vq-nav__group--active .vq-nav__link { font-weight: 500; }

.vq-root.vq-root .vq-nav__toggle {
  display: flex; align-items: center; justify-content: center;
  padding: 0.625rem; border-radius: var(--vq-radius-md);
  color: inherit; flex-shrink: 0;
}
.vq-root.vq-root .vq-nav__toggle svg { width: 1rem; height: 1rem; transition: transform .2s ease; }
/* The chevron points down when closed and up when open. Driven by the same
   aria-expanded the script maintains, so state lives in one place. */
.vq-root.vq-root .vq-nav__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Icons inside nav entries. 18px matches the mockup. */
.vq-root.vq-root .vq-nav__item svg,
.vq-root.vq-root .vq-nav__link svg { width: 1.125rem; height: 1.125rem; flex-shrink: 0; }

/* Children of a group, indented with a rule so the nesting is visible. */
.vq-root.vq-root .vq-nav__sub {
  margin-left: 1.875rem;
  padding-left: 0.75rem;
  border-left: 1px solid var(--vq-sidebar-border);
  display: flex; flex-direction: column; gap: 0.125rem;
}
/* Collapsing panels animate their height, so the overflow must be clipped
   while moving. jQuery slideDown/slideUp sets this itself; this covers the
   no-jQuery path. */
.vq-root.vq-root .vq-nav__sub[data-vq-accordion-panel] { overflow: hidden; }

/* --- Empty state ------------------------------------------------------ */
.vq-root.vq-root .vq-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 0.5rem; padding: 3rem 1.5rem;
  border: 1px solid var(--vq-border); border-radius: var(--vq-radius-xl);
  background: var(--vq-card);
}
.vq-root.vq-root .vq-empty__title { font-size: 0.875rem; font-weight: 600; }
.vq-root.vq-root .vq-empty__desc  { font-size: 0.875rem; color: var(--vq-muted-foreground); max-width: 32rem; }

/* --- Progress --------------------------------------------------------- */
/* Reconciled: the track was --vq-muted, a pale grey-green, where the mockup
   uses the primary at 20%, which is a pale teal. Height stays 6px, which is
   what the mockup actually renders. The bar takes no radius of its own: the
   track clips it, and the mockup leaves it square. */
.vq-root.vq-root .vq-progress {
  position: relative;
  width: 100%;
  height: 0.375rem;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--vq-primary) 20%, transparent);
  overflow: hidden;
}
.vq-root.vq-root .vq-progress__bar {
  height: 100%;
  width: 100%;
  flex: 1;
  background: var(--vq-primary);
  transition: transform .15s;
}

/* --- Skip link (keyboard users land here first) ----------------------- */
.vq-root.vq-root .vq-skip {
  position: absolute; left: -9999px;
  background: var(--vq-primary); color: var(--vq-primary-foreground);
  padding: 0.5rem 1rem; border-radius: var(--vq-radius-md); z-index: 50;
}
.vq-root.vq-root .vq-skip:focus { left: 0.75rem; top: 0.75rem; }


/* ============================================================================
   6. UTILITIES
   Kept deliberately short. If you find yourself wanting a big utility set,
   you want Tailwind, and the mockup already has it.
   ========================================================================= */

.vq-root.vq-root .vq-stack    { display: flex; flex-direction: column; }
.vq-root.vq-root .vq-stack-sm > * + * { margin-top: 0.375rem; }
.vq-root.vq-root .vq-stack-md > * + * { margin-top: 0.75rem; }
.vq-root.vq-root .vq-stack-lg > * + * { margin-top: 1.5rem; }
.vq-root.vq-root .vq-inline   { display: flex; align-items: center; gap: 0.5rem; }
.vq-root.vq-root .vq-inline-lg{ display: flex; align-items: center; gap: 0.75rem; }
/* Top-aligned variant, for a row whose items are different heights: an icon
   beside two lines of text, a badge beside a wrapping title. Must come after
   the two above, since it beats them on source order rather than specificity. */
.vq-root.vq-root .vq-inline--top { align-items: flex-start; }
.vq-root.vq-root .vq-between  { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.vq-root.vq-root .vq-wrap     { flex-wrap: wrap; }
.vq-root.vq-root .vq-grow     { flex: 1; min-width: 0; }
.vq-root.vq-root .vq-shrink-0 { flex-shrink: 0; }
.vq-root.vq-root .vq-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Responsive switches for the shell: the drawer button is mobile-only, the
   brand wordmark's second half and the account name are desktop-only. 1024px
   is the same breakpoint the sidebar uses, so they flip together.
   These live here rather than in the reference HTML on purpose. They are used
   by header markup that gets copied into header.php, and a copied element
   whose visibility class is defined only in the file it was copied FROM fails
   silently: the drawer button simply shows up at desktop width. */
.vq-root.vq-root .vq-only-mobile { display: none; }
@media (max-width: 1023px) {
  .vq-root.vq-root .vq-only-mobile  { display: inline-flex; }
  .vq-root.vq-root .vq-only-desktop { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .vq-root *, .vq-root *::before, .vq-root *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}


/* ============================================================================
   7. WORDPRESS LOGIN FORM
   The one place we style markup we do not control. wp_login_form() emits a
   fixed structure (<p class="login-username"> wrapping a label and an input,
   and so on), so this targets that structure directly rather than asking for
   vq- classes that cannot be added.

   Everything is scoped to #community-loginform. An ID is (1,0,0), which beats
   Astra, the theme's form styles and any plugin, so this needs no repeated
   class trick and cannot leak to another form on the page. It also does not
   depend on .vq-root, so the form works wherever it is placed.

   Matches the homepage sign-in card in the mockup. Put it inside a
   .vq-card.vq-card--pad and add the heading above the form:

     <div class="vq-card vq-card--pad" style="max-width:24rem;">
       <h2 class="vq-title" style="margin-bottom:1rem;">Sign in</h2>
       <?php wp_login_form(array('form_id' => 'community-loginform')); ?>
     </div>
   ========================================================================= */

#community-loginform {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  font-family: var(--vq-font-sans);
  font-size: 0.875rem;
  color: var(--vq-foreground);
  text-align: left;
}

/* wp_login_form wraps every row in a <p>. Astra gives those a bottom margin
   and a 19px font size; the flex gap above is doing the spacing instead. */
#community-loginform p {
  margin: 0;
  padding: 0;
  font-size: inherit;
  line-height: inherit;
}

#community-loginform label {
  display: block;
  margin-bottom: 0.375rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--vq-foreground);
}

/* `size="20"` is in the emitted markup and would otherwise cap the width. */
/* Kept in step with .vq-input deliberately: same 6px radius, same softer
   --vq-input border, and the same 16px-on-mobile rule that stops iOS Safari
   zooming the viewport on focus. */
#community-loginform input[type="text"],
#community-loginform input[type="email"],
#community-loginform input[type="password"] {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  height: 2.25rem;
  padding: 0.25rem 0.75rem;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5rem;
  color: var(--vq-foreground);
  background: var(--vq-card);
  border: 1px solid var(--vq-input);
  border-radius: var(--vq-radius-md);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition: border-color .15s, box-shadow .15s;
}
@media (min-width: 768px) {
  #community-loginform input[type="text"],
  #community-loginform input[type="email"],
  #community-loginform input[type="password"] { font-size: 0.875rem; line-height: 1.25rem; }
}
#community-loginform input::placeholder { color: var(--vq-muted-foreground); }
#community-loginform input:focus-visible {
  border-color: var(--vq-ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--vq-ring) 50%, transparent);
  outline: none;
}

/* Remember Me: the label wraps the checkbox, so it is a row, not a block. */
#community-loginform .login-remember label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0;
  font-weight: 400;
  cursor: pointer;
}
#community-loginform input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  margin: 0;
  flex-shrink: 0;
  border-radius: var(--vq-radius-sm);
  accent-color: var(--vq-primary);   /* the checked fill, no custom markup */
  cursor: pointer;
}

/* Submit. This is the mockup's default button: a soft gradient chip rather
   than solid teal, which is what the homepage card shows. For a solid brand
   button swap the background for var(--vq-primary) and the colour for #fff. */
#community-loginform input[type="submit"],
#community-loginform .button-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 2.25rem;
  padding-inline: 1rem;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: none;
  text-shadow: none;
  color: var(--vq-foreground);
  border: 1px solid color-mix(in oklab, var(--vq-primary) 25%, transparent);
  border-radius: var(--vq-radius-md);
  background-image: linear-gradient(to bottom right,
    color-mix(in oklab, var(--vq-emphasis) 20%, transparent),
    var(--vq-accent), var(--vq-secondary));
  box-shadow: none;
  cursor: pointer;
  transition: opacity .15s;
}
#community-loginform input[type="submit"]:hover,
#community-loginform .button-primary:hover { opacity: 0.9; }
#community-loginform input[type="submit"]:focus-visible,
#community-loginform .button-primary:focus-visible {
  outline: 2px solid var(--vq-ring);
  outline-offset: 2px;
}

/* Optional extras. wp_login_form() does not emit these, so add them yourself
   after the form if you want the homepage card's full set of links. */
#community-loginform + .vq-login-links {
  margin-top: 0.75rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.vq-login-links a { text-decoration: none; }
.vq-login-links .vq-login-lost {
  font-size: 0.75rem;
  color: var(--vq-muted-foreground);
}
.vq-login-links .vq-login-register {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--vq-primary);
}
.vq-login-links a:hover { text-decoration: underline; }


/* ============================================================================
   8. OVERLAY PANELS THAT SHIP IN THE MARKUP
   ----------------------------------------------------------------------------
   A dialog, popover or menu built in React normally renders its panel into a
   portal, and only while it is open. Exported to a static file that leaves a
   button with nothing behind it: 36 of the generated pages had a dialog trigger
   and no dialog. Reported by Mike, 20 Aug, and he was right that the control
   "looked" done and did nothing.

   The components now render their panels inline and keep them mounted, so the
   panel is in the file. Which means the closed state has to be hidden here
   rather than by not existing.

   `data-state` is written by the component in the mockup and by
   vantiq-interface.js in an exported page, so one rule serves both.

   Scoped x4 rather than x2 on purpose. These panels carry Tailwind layout
   utilities such as `grid`, which vantiq-mockup.css scopes to .vq-root x4,
   i.e. (0,5,0). Two classes plus two attributes is (0,4,0) and loses to it,
   which showed up as every dialog rendering open and inline in the export.
   ========================================================================= */

.vq-root.vq-root.vq-root.vq-root [data-slot="dialog-content"][data-state="closed"],
.vq-root.vq-root.vq-root.vq-root [data-slot="dialog-overlay"][data-state="closed"],
.vq-root.vq-root.vq-root.vq-root [data-slot="alert-dialog-content"][data-state="closed"],
.vq-root.vq-root.vq-root.vq-root [data-slot="alert-dialog-overlay"][data-state="closed"],
.vq-root.vq-root.vq-root.vq-root [data-slot="popover-content"][data-state="closed"] {
  display: none;
}

/* Open panels sit above the page. The overlay is the scrim behind them. */
.vq-root.vq-root.vq-root.vq-root [data-slot="dialog-overlay"][data-state="open"],
.vq-root.vq-root.vq-root.vq-root [data-slot="alert-dialog-overlay"][data-state="open"] {
  position: fixed;
  inset: 0;
  z-index: 50;
  /* The mockup compiles `bg-black/50` to #00000080, which is this exactly.
     Pure black at 50% is the one case where Tailwind oklab mix and a plain
     alpha agree, so no color-mix is needed here. Not a palette colour: a
     scrim is deliberately neutral so it darkens whatever it covers. */
  background: rgb(0 0 0 / 0.5);
}

/* Tab panels are kept mounted for the same reason the dialogs are: an exported
   page would otherwise carry only whichever panel happened to be open, and the
   other tabs would click onto nothing. `data-state` is set by the component in
   the mockup and by vantiq-interface.js in an exported page. */
.vq-root.vq-root.vq-root.vq-root [data-slot="tabs-content"][data-state="inactive"] {
  display: none;
}
