/* ==========================================================================
   IDV Products — catalogue, product and cart styles.
   Built on branding/tokens.css. Every value comes from a token; the brand
   linter (branding/lint-tokens.ps1) is the pass/fail here, not taste.

   The token block below is a VERBATIM copy of branding/tokens.css. It is
   copied rather than linked because this plugin ships independently of the
   landing-page build. Re-copy it whenever branding/tokens.css changes.
   ========================================================================== */

/* ==========================================================================
   IDV Pacific — Design Tokens   v1.0.0   (2026-06-11)
   THE single source of truth for visual design values on all landing pages.

   DO NOT edit values in your copy. Changes happen only here, go through
   branding/BRAND-GUIDE.md Appendix B (changelog) and must pass
   branding/contrast-gate.ps1 first.

   USAGE (every landing page):
   1. Add the class "idv-tokens" to your page root wrapper:
        <div class="xyz-landing idv-tokens"> ... </div>
   2. Paste this entire block UNCHANGED at the very top of your 3-css.txt.
   3. Reference tokens via var(--idv-...) — keep !important on the property
      as usual for Avada overrides:  color: var(--idv-c-text) !important;
   ========================================================================== */

.idv-tokens,
body:has(.idvp) {
  /* ---- 1. Color primitives -------------------------------------------- */
  --idv-c-primary:         #0B5CD8;  /* IDV Blue — THE brand color: CTAs, links, eyebrows */
  --idv-c-primary-strong:  #0A4DB5;  /* hover / active of primary */
  --idv-c-ink:             #0A1322;  /* near-black navy — dark sections, hero */
  --idv-c-ink-2:           #101D33;  /* headings on light; ink gradient stop */
  --idv-c-text:            #46556E;  /* body text on light */
  --idv-c-muted:           #5D6B84;  /* meta, captions, secondary labels */
  --idv-c-surface:         #FFFFFF;  /* default section background */
  --idv-c-tint:            #F4F7FC;  /* alternate (light blue-grey) section background */
  --idv-c-well:            #E8F0FC;  /* icon wells, chips, soft blue fills */
  --idv-c-well-green:      #E2F8EF;  /* "verified / live" badge background */
  --idv-c-border:          #E3E9F2;  /* 1px borders, dividers */
  --idv-c-verified:        #00C580;  /* green — fills/dots ONLY (behind dark text); never icons/text on light */
  --idv-c-verified-text:   #077A53;  /* green as text OR icons/ticks on light backgrounds */
  --idv-c-verified-ink:    #04301F;  /* text placed ON a green fill */
  --idv-c-error:           #B42318;  /* form errors only */
  --idv-c-gold:            #B8860B;  /* award / certification accent (e.g. Gold VAR partner medal). Icons/large only on surface (3.26:1); never body text */
  --idv-c-gold-ondark:     #FFA50D;  /* warm amber-gold for award icons ON dark/ink backgrounds (e.g. the Gold VAR medal on the indigo pill); 5.9:1 on scan-primary */

  /* on-dark set — use ONLY on ink backgrounds */
  --idv-c-ondark-text:     #B8C4D6;  /* body text on ink */
  --idv-c-ondark-accent:   #6EA8FF;  /* links / eyebrows on ink */
  --idv-c-ondark-verified: #2EE6A6;  /* green checks / accents on ink */
  --idv-c-ondark-border:   rgba(255, 255, 255, 0.14);
  --idv-c-ondark-fill:     rgba(255, 255, 255, 0.06);

  /* ---- 1a2. SCANNERS PAGE accents (added v1.5.0) ----------------------
     Scanners LP ONLY, page-scoped. accent.css re-points --idv-c-primary* to
     scan-primary within .idvs-landing, so the Scanners page rebrands off master
     IDV blue to the Thales indigo (buttons/links/icons/cards + the verb highlight).
     scan-primary = Thales indigo (dark -> WHITE text, 11.7:1). scan-accent = teal
     for the "how readers work" alt card/wedge fill (LIGHT -> DARK ink text, 7.8:1). */
  --idv-c-scan-primary:        #292D7F;
  --idv-c-scan-primary-strong: #20235F;
  --idv-c-scan-accent:         #65BDCA;

  /* ---- 1b. PRODUCT ACCENT — FoxID (added v1.2.0) -----------------------
     FoxID is an IDV product with its own brand colour (foxid.com.au orange).
     SCOPE: FoxID pages only. The accent NEVER replaces --idv-c-primary for
     buttons/links/primary actions (those stay IDV blue) — it themes product
     decoration: eyebrow icons, stat numerals, icon wells, card hover/top
     accents, the comparison "featured" column, the hero scan-line + pill.
     Source hexes verified live: pixPrimary #FF7213, secondary #FFBB3D,
     coral #FF6C5F, tint #FFF3EB. All pairings pass contrast-gate.ps1. */
  --idv-c-foxid:         #FF7213;  /* primary orange — decorative FILLS (dark ink on top) */
  --idv-c-foxid-strong:  #C2540A;  /* hover / border-on-white safe (>=3:1) */
  --idv-c-foxid-well:    #FFF3EB;  /* pale tint — icon wells, featured column, pill/callout bg */
  --idv-c-foxid-text:    #B5400D;  /* readable orange — icons / small text / stat numerals on light */
  --idv-c-foxid-ondark:  #FFBB3D;  /* amber accent on ink (hero highlight, on-ink icons) */
  /* text ON a foxid fill = --idv-c-ink (#0A1322); icons inherit, so they stay in-gamut */

  /* ---- 1c. PRODUCT ACCENT — enrol (added v1.3.0) ----------------------
     enrol = no-code identity onboarding / workflow builder. SCOPE: enrol pages only.
     Decoration only (same rules as FoxID): NEVER primary/links/buttons (those stay IDV
     blue), NEVER replaces verified-green. Teal chosen as a blue-green deliberately
     distinct from verified #00C580 and from master blue. All pairings pass contrast-gate. */
  --idv-c-enrol:         #12B0A0;  /* teal — decorative FILLS (dark ink on top, 6.9:1) */
  --idv-c-enrol-strong:  #0C8579;  /* hover / border-on-white safe (>=3:1) */
  --idv-c-enrol-well:    #E4F7F3;  /* pale tint — icon wells, featured column, pill/callout bg */
  --idv-c-enrol-text:    #0E7167;  /* readable teal — icons / small text / stat numerals on light */
  --idv-c-enrol-ondark:  #45D6C5;  /* teal accent on ink (hero highlight, on-ink icons) */
  /* text ON an enrol fill = --idv-c-ink (#0A1322); icons inherit, staying in-gamut */

  /* ---- 1d. PRODUCT ACCENT — Hub Manager (added v1.3.0) ----------------
     Hub Manager = centralised multi-site device management. SCOPE: hub pages only.
     Violet, aligned to the product's established purple identity, WCAG-tuned. Decoration
     only; primary/links/buttons stay IDV blue. All pairings pass contrast-gate. */
  --idv-c-hub:           #8B6DF0;  /* violet — decorative FILLS (dark ink on top, 4.9:1) */
  --idv-c-hub-strong:    #6D45D9;  /* hover / border-on-white safe (>=3:1) */
  --idv-c-hub-well:      #F1ECFE;  /* pale tint */
  --idv-c-hub-text:      #6033C7;  /* readable violet — icons / small text on light */
  --idv-c-hub-ondark:    #B9A6FF;  /* violet accent on ink */
  /* text ON a hub fill = --idv-c-ink (#0A1322) */

  /* ---- 1e. PRODUCT — mDL (added v1.3.0) -------------------------------
     mDL = ISO 18013-5 mobile driver licence: the flagship "house" product. It rides the
     MASTER IDV blue rather than a separate accent (deliberate — see §6.20). These aliases
     exist only so product enumeration stays symmetric; they add NO new colour. No --idv-g-mdl. */
  --idv-c-mdl:           var(--idv-c-primary);
  --idv-c-mdl-strong:    var(--idv-c-primary-strong);
  --idv-c-mdl-well:      var(--idv-c-well);
  --idv-c-mdl-text:      var(--idv-c-primary);
  --idv-c-mdl-ondark:    var(--idv-c-ondark-accent);

  /* ---- 2. The ONLY approved gradients ---------------------------------- */
  --idv-g-ink:  linear-gradient(165deg, #101D33 0%, #0A1322 100%);
  --idv-g-glow: radial-gradient(ellipse at 18% 10%, rgba(11, 92, 216, 0.20) 0%, transparent 52%),
                radial-gradient(ellipse at 85% 92%, rgba(0, 197, 128, 0.07) 0%, transparent 50%);
  /* FoxID product signature (added v1.2.0). SCOPE: FoxID pages, DECORATIVE only
     (scan-line, stat underline, featured-panel top bar, hero pill) — never on
     body text, never a button. Dark ink (--idv-c-ink) on it passes AA throughout. */
  --idv-g-foxid: linear-gradient(90deg, #FF6C5F 0%, #FF7213 50%, #FFBB3D 100%);
  /* enrol + hub product signatures (added v1.3.0). SCOPE: that product's pages, DECORATIVE
     only (scan-line, stat underline, featured-panel top bar, hero pill) — never body text,
     never a button. All stops chosen light enough that dark ink (--idv-c-ink) on them passes AA. */
  --idv-g-enrol: linear-gradient(90deg, #12B0A0 0%, #2BC9B5 50%, #5BE0CB 100%);
  --idv-g-hub:   linear-gradient(90deg, #8B6DF0 0%, #A48BF5 50%, #B9A6FF 100%);

  /* ---- 3. Typography (Karla only) --------------------------------------
     Weights: 400 body · 500 emphasis · 700 subheads · 800 display/H2/eyebrow */
  --idv-font: 'Karla', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --idv-fs-display: 3rem;      /* hero h1        800, lh 1.12, ls -0.02em  */
  --idv-fs-h2:      2.25rem;   /* section h2     800, lh 1.16, ls -0.015em */
  --idv-fs-h3:      1.375rem;  /* card heading   700, lh 1.3               */
  --idv-fs-h4:      1.125rem;  /* minor heading  700, lh 1.35              */
  --idv-fs-lead:    1.125rem;  /* hero / section intro  400, lh 1.7        */
  --idv-fs-body:    1rem;      /* default        400, lh 1.65              */
  --idv-fs-small:   0.875rem;  /* meta, fine print      lh 1.55            */
  --idv-fs-eyebrow: 0.8125rem; /* caps label     800, uppercase, ls 0.08em */
  /* display sizes — big numerals (stat bands, prices) & pullquotes ONLY, never body */
  --idv-fs-stat:    2.5rem;    /* stat / price numeral  800, lh 1.05        */
  --idv-fs-quote:   1.5rem;    /* pullquote / testimonial  500, lh 1.5      */

  /* ---- 4. Radii ---------------------------------------------------------- */
  --idv-r-control: 10px;   /* buttons, inputs */
  --idv-r-card:    16px;   /* cards, panels, FAQ items */
  --idv-r-feature: 24px;   /* hero mockups, featured panels */
  --idv-r-pill:    999px;  /* pills, badges, chips */

  /* ---- 5. Shadows (only these two) --------------------------------------- */
  --idv-sh-card: 0 1px 2px rgba(16, 29, 51, 0.04), 0 8px 24px rgba(16, 29, 51, 0.07);
  --idv-sh-pop:  0 24px 60px rgba(10, 19, 34, 0.35);   /* hero mockups on ink only */

  /* ---- 6. Layout ---------------------------------------------------------- */
  --idv-container: 1200px;
  --idv-pad-x: 24px;
  --idv-sec-y: 96px;   /* vertical section padding */
}

/* Responsive token overrides — do NOT hand-tune sizes per page */
@media (max-width: 768px) {
  .idv-tokens,
  body:has(.idvp) {
    --idv-fs-display: 2.1rem;
    --idv-fs-h2: 1.75rem;
    --idv-fs-h3: 1.25rem;
    --idv-fs-stat: 2rem;
    --idv-fs-quote: 1.25rem;
    --idv-sec-y: 64px;
  }
}
@media (max-width: 480px) {
  .idv-tokens,
  body:has(.idvp) {
    --idv-pad-x: 16px;
  }
}

/* ==========================================================================
   PAGE FRAME
   The single-product template calls get_header() and echoes directly, so it
   never receives Avada #main / .fusion-row wrappers. The shop and cart DO sit
   inside them. One frame serves both: a section supplies vertical rhythm, a
   container supplies width and side padding, exactly as every landing page in
   this repo does.
   ========================================================================== */
.idvp {
  font-family: var(--idv-font) !important;
  font-size: var(--idv-fs-body) !important;
  line-height: 1.65 !important;
  color: var(--idv-c-text) !important;
  -webkit-font-smoothing: antialiased !important;
  max-width: 100% !important;
  overflow-x: clip !important;
  /* Avada switches .fusion-row to display:flex at <=768px. As a flex item with no basis this
     wrapper shrank to min-content: measured 48px, which is exactly the two 24px paddings and no
     content at all. Claiming the full width keeps it correct whether the parent is block or flex. */
  width: 100% !important;
  flex: 1 1 100% !important;
  min-width: 0 !important;
}
.idvp *, .idvp *::before, .idvp *::after { box-sizing: border-box !important; }
.idvp h1, .idvp h2, .idvp h3, .idvp h4 { color: var(--idv-c-ink-2) !important; margin: 0 0 .5em !important; line-height: 1.25 !important; }

/* Top padding uses the sanctioned --tight ratio rather than the full --idv-sec-y.
   That token spaces stacked landing-page bands from each other; this is the first
   element under the site header, which already provides separation, and Avada adds
   its own row padding above it. At the full token the three pages opened with a
   130-200px void. Expressed as a ratio of the token, not a magic number, so the
   96/52/96 rhythm still governs it. */
.idvp-section { padding: calc(var(--idv-sec-y) * .55) 0 var(--idv-sec-y) !important; }
.idvp-section--tight { padding: calc(var(--idv-sec-y) * .55) 0 var(--idv-sec-y) !important; }

/* Do NOT put container-type on this element. It was tried and was wrong twice over: a container
   query measures the CONTENT box, so the 24px of side padding here made it read 48px narrow and the
   rail never appeared on a 1200px desktop; and container-type applies layout containment, which
   makes this element the containing block for position:fixed descendants and quietly broke the
   mobile drawer and its full-screen scrim. Layout mode is a plain media query - see the note in the
   catalogue layout section below. */
.idvp-container {
  max-width: var(--idv-container) !important;
  margin: 0 auto !important;
  padding: 0 var(--idv-pad-x) !important;
}

/* ---- Avada wrapper neutraliser ----------------------------------------
   Avada nests our output inside up to four wrappers, each taking a cut of the
   width: .fusion-row at max-width 80%, a .fusion-builder-row at 83.2% INSIDE
   that, negative gutter margins, and <main> padding. Compounded, /shop/ rendered
   at 872px inside a 1422px viewport and at 284px inside 504px on a phone - a
   quarter of a phone screen spent on empty gutters. /cart/ escaped it only
   because that page uses the 100%-width template.
   Every one of these is scoped with :has(.idvp), so ONLY the wrapper that
   actually contains this plugin is affected; nothing else on the site moves.
   After this, --idv-container is the single thing that decides the measure and
   --idv-pad-x is the only gutter.
   NOTE: this deliberately overrides the theme width for these pages. Embedding
   [idvp_catalogue] inside a narrow column will not stay narrow.
   ---------------------------------------------------------------------- */
.fusion-fullwidth:has(.idvp),
.fusion-builder-row:has(.idvp),
.fusion-layout-column:has(.idvp),
.fusion-column-wrapper:has(.idvp),
.fusion-text:has(.idvp),
.fusion-row:has(.idvp) {
  max-width: 100% !important; width: 100% !important;
  margin-left: 0 !important; margin-right: 0 !important;
  padding-left: 0 !important; padding-right: 0 !important;
  padding-top: 0 !important; padding-bottom: 0 !important;
  margin-top: 0 !important; margin-bottom: 0 !important;
}
/* The 100%-width template adds 50px above the content, which is what made the
   Request Order page open with a gap. Side padding goes too: our own
   --idv-pad-x is the gutter, and doubling them is what crushed the phone view. */
main:has(.idvp) { padding-top: 0 !important; padding-left: 0 !important; padding-right: 0 !important; }

/* Other Fusion Builder content on a catalogue page - on /shop/ that is the price
   volatility / ex-GST notice - keeps Avada's own nested row width. Measured after
   the neutraliser above landed: at a 504px viewport the notice started 50px in
   while the products started at 24px, so it read as a box that did not line up
   with the page. Capping it at the same container and giving it the same gutter
   puts the two on the same edge at every width (verified 24/24 at 504px and
   135/135 at 1422px).
   :not(:has(.idvp)) is load-bearing - without it this would double the padding on
   our own column. */
body:has(.idvp) main .fusion-builder-row:not(:has(.idvp)) {
  max-width: var(--idv-container) !important;
  margin-left: auto !important; margin-right: auto !important; }
body:has(.idvp) main .fusion-column-wrapper:not(:has(.idvp)) {
  margin-left: 0 !important; margin-right: 0 !important; }
body:has(.idvp) main .fusion-layout-column:not(:has(.idvp)) {
  padding-left: var(--idv-pad-x) !important; padding-right: var(--idv-pad-x) !important; }

/* Focus ring, house standard. Replaces an off-token amber outline. */
.idvp a:focus-visible, .idvp button:focus-visible,
.idvp input:focus-visible, .idvp select:focus-visible, .idvp textarea:focus-visible {
  outline: none !important;
  box-shadow: 0 0 0 3px rgba(11, 92, 216, 0.35) !important;
  border-radius: var(--idv-r-control) !important;
}
.idvp-sr { position: absolute !important; width: 1px !important; height: 1px !important;
  overflow: hidden !important; clip-path: inset(50%) !important; white-space: nowrap !important; }

/* ==========================================================================
   PAGE HEAD, breadcrumbs and title
   Breadcrumbs are a new component: none exists in the house style. Built only
   from existing tokens, and it mirrors the BreadcrumbList that
   class-idvp-schema.php already emits, so the visible trail and the structured
   data cannot disagree.
   ========================================================================== */
.idvp-head { margin-bottom: 36px !important; }
.idvp-crumbs { display: flex !important; flex-wrap: wrap !important; align-items: center !important;
  gap: 8px !important; margin: 0 0 14px !important; padding: 0 !important; list-style: none !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; }
.idvp-crumbs li { display: flex !important; align-items: center !important; gap: 8px !important; }
.idvp-crumbs a { color: var(--idv-c-muted) !important; text-decoration: none !important; }
.idvp-crumbs a:hover { color: var(--idv-c-primary) !important; text-decoration: underline !important; }
.idvp-crumbs li[aria-current] { color: var(--idv-c-ink-2) !important; font-weight: 700 !important; }
/* Separator belongs to the crumb BEFORE it, not the one after. As ::before on the
   following item, a trail that wrapped started the second line with a chevron
   hanging in front of nothing. */
.idvp-crumbs li:not(:last-child)::after { content: "" !important; width: 5px !important; height: 5px !important;
  border-top: 1.5px solid var(--idv-c-border) !important; border-right: 1.5px solid var(--idv-c-border) !important;
  transform: rotate(45deg) !important; flex: none !important; }
.idvp-head h1 { font-size: var(--idv-fs-h2) !important; font-weight: 800 !important;
  letter-spacing: -0.015em !important; margin: 0 0 8px !important; text-wrap: balance !important; }
.idvp-head p { margin: 0 !important; color: var(--idv-c-muted) !important; font-size: var(--idv-fs-small) !important; }

/* ==========================================================================
   TRUST BADGES, under the shop title and above the products
   Sizing copies the house logo strip on the about page: one shared height, each mark keeping its
   own aspect ratio under a max-width cap, so the wide Channel Partner banner takes more width
   rather than being cropped. flex-wrap plus centring is the whole responsive strategy above 480px;
   the modifiers exist because these five pieces of artwork have wildly different optical weight.
   ========================================================================== */
/* BELOW 1025: one layout column, so this is a compact centred row. order:-1 keeps it above the
   products - stranded under them it would never be seen on a phone. */
.idvp-trust { display: flex !important; flex-wrap: wrap !important;
  align-items: center !important; justify-content: center !important;
  gap: 14px 28px !important; margin: 0 0 26px !important; padding: 0 !important;
  order: -1 !important; }
.idvp-trust__item { display: flex !important; align-items: center !important; justify-content: center !important;
  padding: 0 !important; margin: 0 !important; }
.idvp-trust__item img { display: block !important; width: auto !important; height: auto !important;
  max-height: 58px !important; max-width: 116px !important; object-fit: contain !important; }
/* The partner mark is a 1015x445 banner; the other two are near-square. Capping by height alone
   would let it run three times as wide as its neighbours. */
.idvp-trust__item--partner img { max-height: 42px !important; max-width: 168px !important; }

/* 1025 AND UP: a third grid column on the right, one badge per row, sized to fill the column.
   The column is deliberately narrow - the products keep the space - but each mark is far larger
   than it was in the row, which is the point. align-self:start stops the stack centring itself
   against a tall product grid. */
@media (min-width: 1025px) {
  .idvp:not(.idvp-force-narrow) .idvp-trust {
    order: 0 !important; display: grid !important; grid-template-columns: minmax(0, 1fr) !important;
    gap: 22px !important; margin: 0 !important; align-self: start !important;
    justify-items: center !important;
    /* Sticky, the same way the filter rail is. --idvp-topgap is measured by filter.js and written
       to the root element, so the offset clears whatever the sticky site header actually is rather
       than a hard-coded guess. align-self:start above is load-bearing: a grid item stretches to the
       row height by default, and a stretched item has nothing to stick within.
       NO max-height AND NO overflow HERE, deliberately, unlike the rail just below.
       The first version copied the rail wholesale, including `overflow: auto` and
       `overscroll-behavior: contain`. That froze the page whenever the pointer sat over the badges:
       `overflow: auto` makes the element a scroll container even with nothing to scroll, and
       `contain` then stops the wheel chaining to the document. The rail wants that, because it
       really does scroll a long facet list. This stack is about 500px of images and never scrolls,
       so the whole mechanism was cost with no benefit.
       Removing it outright rather than just dropping `contain` is the point: with no scroll
       container there is nothing that can capture a wheel, so this cannot regress into the same bug
       under a future browser's chaining rules.
       The accepted trade is a viewport shorter than roughly 660px at desktop width, where the last
       badge is clipped once the column sticks. That is rare and mild; a frozen page is neither. */
    position: sticky !important;
    top: calc(var(--idvp-topgap, 0px) + 16px) !important; }
  .idvp:not(.idvp-force-narrow) .idvp-trust__item { width: 100% !important; }
  .idvp:not(.idvp-force-narrow) .idvp-trust__item img {
    max-height: none !important; max-width: 100% !important; }
  .idvp:not(.idvp-force-narrow) .idvp-trust__item--partner img { max-height: none !important; max-width: 100% !important; }
}



/* ==========================================================================
   WHAT HAPPENS NEXT, a compact strip under the cart title
   This was a full band in the cart landing-page frame, below the entire two-column grid, which put
   it at 64% of the page height - below the fold on every screen. It is deliberately denser than
   that band was: it has to earn its place above the form rather than push the form down.

   Namespaced .idvp-next*, NOT .idvp-step*. `.idvp-step` and `.idvp-step__b` further down this file
   are the quantity stepper - the plus/minus buttons on cart rows and the product page. Reusing that
   name gave this text span `display: inline-flex` from the stepper rule, which shrink-wrapped it to
   34px and made every step 189px tall, and pushed a muted font colour onto every stepper button.
   ========================================================================== */
/* No fill. The owner asked for the blue backgrounds gone, and --idv-c-tint is a light blue-grey.
   The border and the internal spacing do the separating instead, which is the same call already
   made on the cart panels and the product image well. */
.idvp-next { margin: 0 0 32px !important; padding: 20px 24px !important;
  background: none !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-card) !important; }
.idvp-next__title { margin: 0 0 14px !important; font-size: var(--idv-fs-eyebrow) !important;
  font-weight: 800 !important; text-transform: uppercase !important; letter-spacing: 0.06em !important;
  color: var(--idv-c-muted) !important; }
.idvp-next__list { list-style: none !important; margin: 0 !important; padding: 0 !important;
  display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  gap: 18px 28px !important; }
.idvp-next__list li { display: grid !important; grid-template-columns: 28px minmax(0, 1fr) !important;
  gap: 12px !important; align-items: start !important; margin: 0 !important; padding: 0 !important; }
/* Outlined, not a filled blue disc. Same reason as the panel: no blue fills. The number still reads
   as a step marker from the ring and the tabular weight, without a solid colour block. */
.idvp-next__n { display: inline-flex !important; align-items: center !important; justify-content: center !important;
  width: 28px !important; height: 28px !important; flex: none !important;
  border-radius: var(--idv-r-pill) !important;
  background: none !important; border: 1px solid var(--idv-c-border) !important;
  color: var(--idv-c-ink-2) !important;
  font-weight: 800 !important; font-size: var(--idv-fs-small) !important; line-height: 1 !important; }
/* display:block explicitly. A grid item blockifies an inline span on its own, but being explicit is
   what stops the next stray inline-flex rule collapsing this again. */
.idvp-next__t { display: block !important; min-width: 0 !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important;
  line-height: 1.5 !important; }
.idvp-next__t b { display: block !important; color: var(--idv-c-ink-2) !important;
  font-weight: 700 !important; margin-bottom: 2px !important; }
@media (max-width: 900px) {
  .idvp-next__list { grid-template-columns: minmax(0, 1fr) !important; gap: 14px !important; }
  .idvp-next { padding: 18px 20px !important; margin-bottom: 26px !important; }
}

/* ==========================================================================
   CATALOGUE LAYOUT, filter rail beside results, drawer when space is short
   ========================================================================== */
.idvp-layout { display: grid !important; grid-template-columns: minmax(0, 1fr) !important; gap: 28px !important; align-items: start !important; }
.idvp-results { min-width: 0 !important; }

/* LAYOUT MODE IS A MEDIA QUERY, and must stay one.
   It used to be a .is-wide class stamped by an inline script that measured the container
   (IDVP_Render::layout_probe()). That script read document.currentScript, and when LiteSpeed's JS
   combine folded it into the shared bundle currentScript stopped pointing at the inline tag, so the
   class was never applied at ANY width: no rail on desktop, and the mobile filter pill on every
   screen. The resize handler died with it, because it selected .idvp[data-idvp-wide] - an attribute
   only that script ever set. A container query was tried before that and was wrong twice over: it
   measures the CONTENT box, and container-type applies layout containment which breaks the
   position:fixed drawer and scrim below.
   A media query has neither failure mode and needs no JavaScript at all.
   1025px: tablets keep the button, laptops get the rail. Escape hatch for the one case a media
   query cannot see - the catalogue embedded in a genuinely narrow column - is .idvp-force-narrow
   on the .idvp wrapper, which keeps the drawer at any width. */

/* The host filter.js appends to <body> to lift the drawer and scrim out of Avada's stacking
   contexts. It carries `idvp idv-tokens` so every .idvp-scoped rule below still matches and so the
   design tokens still resolve - but it must contribute nothing of its own: no section padding, no
   overflow clip (which .idvp sets), and above all no positioning, or it would become a stacking
   context itself and reintroduce the exact bug it exists to solve. */
.idvp-portalhost { position: static !important; z-index: auto !important;
  display: block !important; margin: 0 !important; padding: 0 !important;
  width: auto !important; max-width: none !important; overflow: visible !important;
  transform: none !important; filter: none !important; contain: none !important;
  isolation: auto !important; will-change: auto !important; }

/* ---- default: the drawer. Products start at the top of the page instead of below 25 checkboxes.
   Genuinely fixed to the viewport, because no ancestor carries layout containment.
   Scoped .idvp so it outranks the plain .idvp-filters rule further down the file, which would
   otherwise win on source order and hand the drawer back its card border and radius. */
.idvp .idvp-filters {
  position: fixed !important; z-index: 100010 !important; inset: 0 auto 0 0 !important;
  width: min(360px, 88vw) !important; max-width: 88vw !important;
  transform: translateX(-102%) !important;
  transition: transform .22s ease, visibility .22s ease !important;
  border-radius: 0 !important; border-width: 0 1px 0 0 !important;
  overflow: auto !important; overscroll-behavior: contain !important;
  /* Closed, the panel is off-canvas but was still in the keyboard tab order, and its 60px-blur
     shadow bled a grey band down the left of every mobile page. visibility:hidden fixes both and
     still animates, because it is transitioned alongside the transform. */
  visibility: hidden !important; box-shadow: none !important; }
.idvp .idvp-filters.is-open { transform: translateX(0) !important;
  visibility: visible !important; box-shadow: var(--idv-sh-pop) !important; }
/* z-index 100010/100000 clears the site header, which is position:sticky at 99999 on both
   .idvh-landing and .idvh-header (idv-landing-pages/pages/header/styles-base.css:26,70). At the
   old 10000/9999 the drawer opened UNDERNEATH it. Nothing here is trapped in a stacking context -
   these numbers are compared in the root - so do not lower them without changing the header too.
   The popup notice clears the header the same way. */
.idvp .idvp-scrim { position: fixed !important; inset: 0 !important; z-index: 100000 !important;
  background: rgba(10, 19, 34, 0.45) !important; opacity: 0 !important; pointer-events: none !important;
  transition: opacity .22s ease !important; }
.idvp .idvp-scrim.is-open { opacity: 1 !important; pointer-events: auto !important; }
/* Confirm-and-close, pinned to the foot of the drawer. position:sticky inside the panel's own
   overflow:auto, so it stays reachable with a long facet list without covering anything when the
   list is short. The background is solid because rows scroll underneath it, and the padding carries
   the iOS home-indicator inset so the button is never under the gesture bar. */
.idvp .idvp-fdone { position: sticky !important; bottom: 0 !important; z-index: 1 !important;
  margin: 0 !important; padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px)) !important;
  background: var(--idv-c-surface) !important;
  border-top: 1px solid var(--idv-c-border) !important; }
.idvp .idvp-fdone__btn { display: block !important; width: 100% !important;
  padding: 14px 20px !important; border-radius: var(--idv-r-pill) !important;
  background: var(--idv-c-primary) !important; border: 1.5px solid var(--idv-c-primary) !important;
  color: var(--idv-c-surface) !important;
  font: 700 var(--idv-fs-body)/1.2 var(--idv-font) !important;
  text-align: center !important; cursor: pointer !important; }
.idvp .idvp-fdone__btn:hover { background: var(--idv-c-primary-strong) !important;
  border-color: var(--idv-c-primary-strong) !important; }

.idvp .idvp-filters .idvp-fclose {
  display: inline-flex !important; align-items: center !important; justify-content: center !important;
  width: 32px !important; height: 32px !important; border-radius: var(--idv-r-pill) !important;
  border: 1px solid var(--idv-c-border) !important; background: var(--idv-c-surface) !important;
  color: var(--idv-c-ink-2) !important; line-height: 1 !important; cursor: pointer !important; }

/* ---- 1025px and up: the drawer becomes a sticky rail beside the results.
   Every property the drawer set above has to be given back explicitly - position, z-index, inset,
   width, transform, transition, border, visibility, shadow - or a leftover from the off-canvas
   state leaks into the rail. */
@media (min-width: 1025px) {
  /* Three columns: filter rail, products, trust badges. The badge column is 176px - wide enough
     that a near-square mark reads at roughly twice the size it had in the old horizontal row, and
     narrow enough that the products still get the bulk of the width. */
  .idvp:not(.idvp-force-narrow) .idvp-layout { grid-template-columns: 250px minmax(0, 1fr) 176px !important; }
  .idvp:not(.idvp-force-narrow) .idvp-filterbar { display: none !important; }
  .idvp:not(.idvp-force-narrow) .idvp-filters {
    position: sticky !important; z-index: auto !important;
    inset: auto !important; top: calc(var(--idvp-topgap, 0px) + 16px) !important;
    width: auto !important; max-width: none !important;
    max-height: calc(100vh - var(--idvp-topgap, 0px) - 32px) !important;
    transform: none !important; transition: none !important;
    border-width: 1px !important; border-radius: var(--idv-r-card) !important;
    overflow: auto !important; overscroll-behavior: contain !important;
    visibility: visible !important; box-shadow: var(--idv-sh-card) !important; }
  /* The scrim carries [hidden] until the drawer opens, and the drawer cannot open at this width,
     but a full-viewport overlay is not something to leave one stray class away from appearing. */
  .idvp:not(.idvp-force-narrow) .idvp-scrim { display: none !important; }
  .idvp:not(.idvp-force-narrow) .idvp-filters .idvp-fclose { display: none !important; }
  /* Nothing to confirm on desktop: the rail and the results are on screen together, so the count
     above the grid is already the feedback and there is no drawer to dismiss. */
  .idvp:not(.idvp-force-narrow) .idvp-fdone { display: none !important; }
}

/* 1248 AND UP: a wider badge column, because the partner mark needs it.
   That banner is 1015x445, so at a 176px column it renders only 77px tall and its Thales logo and
   the accreditation line underneath come out about half the size of the two near-square marks
   beside it - which is exactly what the owner reported.
   This costs nothing. The container is capped at --idv-container (1200px), so from roughly 1248px
   of viewport upward it stops growing at 1152px and the arithmetic is fixed: 1152 less a 250px rail,
   a 250px badge column and two 28px gaps leaves 596px of grid, and two cards need 482px. The product
   grid was already at two per row at this width, so widening the column takes nothing from it.
   The two square marks are capped below the column width on purpose: left to fill 250px they would
   each stand ~250px tall and the stack would tower over the products it is meant to support. */
@media (min-width: 1248px) {
  .idvp:not(.idvp-force-narrow) .idvp-layout { grid-template-columns: 250px minmax(0, 1fr) 250px !important; }
  .idvp:not(.idvp-force-narrow) .idvp-trust { gap: 26px !important; }
  .idvp:not(.idvp-force-narrow) .idvp-trust__item--var img,
  .idvp:not(.idvp-force-narrow) .idvp-trust__item--warranty img { max-width: 196px !important; }
}
@media (prefers-reduced-motion: reduce) {
  .idvp-filters, .idvp-scrim { transition: none !important; }
}

/* ---- the filter trigger: a floating pill, not a bar -------------------
   It used to be a sticky bar across the top of the results, which ate a strip of
   every phone screen and pushed the products down. Floating it over the content
   gives the products the whole viewport and puts the control within thumb reach.
   Fixed to the viewport, which only works because no ancestor carries layout
   containment any more - see the note on .idvp-container.
   z-index sits below the drawer (10000) and its scrim (9999) so it can never
   cover them, and it hides outright while the drawer is open. */
.idvp-filterbar { position: fixed !important; top: auto !important;
  left: 50% !important; transform: translateX(-50%) !important;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px)) !important;
  z-index: 9000 !important; display: flex !important; align-items: center !important;
  padding: 0 !important; margin: 0 !important; background: none !important; }
.idvp:has(.idvp-filters.is-open) .idvp-filterbar,
.idvp-filterbar.is-away { opacity: 0 !important; pointer-events: none !important;
  transform: translateX(-50%) translateY(12px) !important; }
.idvp-filterbar { transition: opacity .18s ease, transform .18s ease !important; }
@media (prefers-reduced-motion: reduce) { .idvp-filterbar { transition: none !important; } }
.idvp-filterbtn { display: inline-flex !important; align-items: center !important; gap: 10px !important;
  padding: 13px 24px !important; border-radius: var(--idv-r-pill) !important;
  background: var(--idv-c-primary) !important; border: 1.5px solid var(--idv-c-primary) !important;
  color: var(--idv-c-surface) !important; font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important;
  box-shadow: var(--idv-sh-pop) !important; cursor: pointer !important; }
.idvp-filterbtn:hover { background: var(--idv-c-primary-strong) !important; border-color: var(--idv-c-primary-strong) !important; }
.idvp-fcount { display: none; min-width: 20px !important; padding: 0 6px !important;
  border-radius: var(--idv-r-pill) !important; background: var(--idv-c-surface) !important;
  color: var(--idv-c-primary) !important; font-size: 0.72rem !important; line-height: 20px !important;
  text-align: center !important; }
.idvp-filterbtn.has-active .idvp-fcount { display: inline-block !important; }
.idvp-fclose { display: none; }

/* The panel itself. Padding sits on the sections rather than the panel so the group
   dividers reach its full width, the way a real shop filter does. */
.idvp-filters { background: var(--idv-c-surface) !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-card) !important; box-shadow: var(--idv-sh-card) !important; padding: 0 !important; }
.idvp-filters-head { display: flex !important; justify-content: space-between !important; align-items: center !important;
  gap: 12px !important; margin: 0 !important; padding: 16px 20px !important;
  border-bottom: 1px solid var(--idv-c-border) !important; font-size: var(--idv-fs-h4) !important;
  font-weight: 800 !important; color: var(--idv-c-ink-2) !important; }
.idvp-filters-head__act { display: flex !important; align-items: center !important; gap: 6px !important; }

/* One collapsible group per attribute. <details> means a group folds with no script
   at all, so the panel stays usable even if filter.js never loads. */
.idvp-fgroup { border-bottom: 1px solid var(--idv-c-border) !important; }
.idvp-fgroup:last-of-type { border-bottom: 0 !important; }
.idvp-fgroup > summary { display: flex !important; align-items: center !important;
  justify-content: space-between !important; gap: 10px !important; padding: 14px 20px !important;
  cursor: pointer !important; list-style: none !important; font-size: var(--idv-fs-eyebrow) !important;
  font-weight: 800 !important; text-transform: uppercase !important; letter-spacing: 0.06em !important;
  color: var(--idv-c-muted) !important; }
.idvp-fgroup > summary::-webkit-details-marker { display: none !important; }
.idvp-fgroup > summary::marker { content: "" !important; }
.idvp-fgroup > summary:hover { color: var(--idv-c-ink-2) !important; background: var(--idv-c-tint) !important; }
.idvp-chev { flex: none !important; transition: transform .18s ease !important; }
.idvp-fgroup[open] > summary .idvp-chev { transform: rotate(180deg) !important; }
.idvp-fgroup fieldset { border: 0 !important; margin: 0 !important; padding: 2px 20px 14px !important; }

/* option row: box, label, live count */
.idvp-fopt { display: grid !important; grid-template-columns: 16px minmax(0, 1fr) auto !important;
  gap: 10px !important; align-items: center !important; margin: 0 !important; padding: 7px 0 !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-text) !important; cursor: pointer !important; }
.idvp-fopt:hover .idvp-fopt__t { color: var(--idv-c-ink-2) !important; }
.idvp-fopt input[type="checkbox"] { margin: 0 !important; accent-color: var(--idv-c-primary) !important;
  width: 16px !important; height: 16px !important; flex: none !important; }
.idvp-fopt__n { font-size: 0.72rem !important; font-weight: 700 !important; color: var(--idv-c-muted) !important;
  background: var(--idv-c-tint) !important; border-radius: var(--idv-r-pill) !important;
  min-width: 22px !important; padding: 1px 7px !important; text-align: center !important; }
.idvp-fopt__n:empty { display: none !important; }
/* An option that would return nothing is dimmed in place, never removed: options
   that jump around as you click them are what makes a filter feel broken. */
.idvp-fopt.is-empty { opacity: .45 !important; cursor: not-allowed !important; }

.idvp-clear { background: transparent !important; border: 0 !important; padding: 4px 6px !important;
  color: var(--idv-c-primary) !important; font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important;
  cursor: pointer !important; text-decoration: underline !important; }
.idvp-clear[hidden] { display: none !important; }
.idvp-i { flex: none !important; }

/* ==========================================================================
   RESULTS TOOLBAR: count and sort. The old WooCommerce shop had this bar above
   its grid and it is the control people actually used. Rebuilt client-side: its
   ?product_orderby= links minted a fresh uncacheable URL on every archive.
   ========================================================================== */
.idvp-toolbar { display: flex !important; align-items: center !important; justify-content: space-between !important;
  gap: 16px !important; flex-wrap: wrap !important; margin: 0 0 18px !important; padding-bottom: 14px !important;
  border-bottom: 1px solid var(--idv-c-border) !important; }
.idvp-sort { display: inline-flex !important; align-items: center !important; gap: 10px !important;
  margin: 0 !important; font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; }
.idvp-sort select { padding: 9px 12px !important; min-height: 40px !important; max-width: 100% !important;
  border: 1px solid var(--idv-c-border) !important; border-radius: var(--idv-r-control) !important;
  background: var(--idv-c-surface) !important; color: var(--idv-c-ink-2) !important;
  font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important; cursor: pointer !important; }

/* Search. Same control height and border as the sort select beside it, so the toolbar reads as one
   row of controls rather than three unrelated things. flex:1 with a min-width lets it take the slack
   on a wide toolbar and wrap to its own full-width line on a narrow one. */
.idvp-search { display: inline-flex !important; align-items: center !important; gap: 8px !important;
  flex: 1 1 200px !important; min-width: 0 !important; max-width: 340px !important;
  padding: 0 10px 0 12px !important; min-height: 40px !important;
  border: 1px solid var(--idv-c-border) !important; border-radius: var(--idv-r-control) !important;
  background: var(--idv-c-surface) !important; color: var(--idv-c-muted) !important; }
/* The ring goes on the wrapper, not the bare input, or it draws inside the border and looks like a
   rendering fault. The input's own outline is suppressed for the same reason. */
.idvp-search:focus-within { outline: none !important;
  box-shadow: 0 0 0 3px rgba(11, 92, 216, 0.35) !important;
  border-color: var(--idv-c-primary) !important; }
.idvp-search .idvp-i { flex: none !important; color: var(--idv-c-muted) !important; }
.idvp-search__input { flex: 1 1 auto !important; min-width: 0 !important;
  padding: 0 !important; margin: 0 !important; border: 0 !important; outline: none !important;
  background: transparent !important; box-shadow: none !important;
  font: 400 var(--idv-fs-small)/1.2 var(--idv-font) !important; color: var(--idv-c-ink-2) !important;
  -webkit-appearance: none !important; appearance: none !important; }
.idvp-search__input::placeholder { color: var(--idv-c-muted) !important; opacity: 1 !important; }
/* Kill the house focus ring ON THIS INPUT ONLY. `.idvp input:focus-visible` up at the top of this
   file gives every input a 3px ring plus a control radius, which is right everywhere else and wrong
   here: the ring belongs on the wrapper, which already draws one via :focus-within. With both, the
   field rendered as a blue rounded box inside a blue rounded box.
   The selector has to carry .idvp to out-specify it - (0,3,1) against (0,2,1). A bare
   `.idvp-search__input` is (0,1,0) and loses no matter where it sits in the file, which is why the
   `outline: none` already on the base rule did nothing. */
.idvp .idvp-search__input:focus,
.idvp .idvp-search__input:focus-visible {
  outline: none !important; box-shadow: none !important; border-radius: 0 !important; }
/* WebKit draws its own cancel and magnifier inside type=search. There is a real clear button here,
   and two of them side by side is the sort of thing that reads as a broken control. */
.idvp-search__input::-webkit-search-decoration,
.idvp-search__input::-webkit-search-cancel-button,
.idvp-search__input::-webkit-search-results-button { -webkit-appearance: none !important; display: none !important; }
.idvp-search__clear { flex: none !important; display: inline-flex !important;
  align-items: center !important; justify-content: center !important;
  width: 24px !important; height: 24px !important; padding: 0 !important; margin: 0 !important;
  border: 0 !important; border-radius: var(--idv-r-pill) !important;
  background: none !important; color: var(--idv-c-muted) !important; cursor: pointer !important; }
.idvp-search__clear:hover { background: var(--idv-c-tint) !important; color: var(--idv-c-ink-2) !important; }
.idvp-search__clear[hidden] { display: none !important; }
@media (max-width: 768px) {
  .idvp-search { order: 3 !important; flex-basis: 100% !important; max-width: none !important; }
}

/* selected facets, removable */
.idvp-chips { display: flex !important; flex-wrap: wrap !important; align-items: center !important;
  gap: 8px !important; margin: 0 0 18px !important; }
.idvp-chips[hidden] { display: none !important; }
.idvp-chip { display: inline-flex !important; align-items: center !important; gap: 8px !important;
  padding: 6px 12px !important; min-height: 0 !important; border-radius: var(--idv-r-pill) !important;
  border: 1px solid var(--idv-c-primary) !important; background: var(--idv-c-well) !important;
  color: var(--idv-c-primary) !important; font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important;
  cursor: pointer !important; }
.idvp-chip:hover { background: var(--idv-c-primary) !important; color: var(--idv-c-surface) !important; }
.idvp-chips__clear { background: transparent !important; border: 0 !important; padding: 6px 8px !important;
  color: var(--idv-c-muted) !important; font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important;
  cursor: pointer !important; text-decoration: underline !important; }
.idvp-chips__clear:hover { color: var(--idv-c-primary) !important; }

/* ==========================================================================
   PRODUCT GRID AND CARD (anatomy taken from pages/scanners .idvs-model)
   auto-fill with minmax is intentionally container-relative: it needs no query,
   it simply fits as many columns as the real width allows.
   ========================================================================== */
.idvp-count { margin: 0 !important; color: var(--idv-c-muted) !important; font-size: var(--idv-fs-small) !important; }
.idvp-grid { display: grid !important; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)) !important; gap: 22px !important; }

.idvp-card { display: flex !important; flex-direction: column !important;
  background: var(--idv-c-surface) !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-card) !important; box-shadow: var(--idv-sh-card) !important;
  padding: 18px 18px 22px !important; transition: transform .2s ease, border-color .2s ease !important; }
.idvp-card:hover { transform: translateY(-3px) !important; border-color: var(--idv-c-primary) !important; }
.idvp-card[hidden] { display: none !important; }
.idvp-card-link { text-decoration: none !important; color: inherit !important; display: block !important; }
.idvp-card__media { display: flex !important; align-items: center !important; justify-content: center !important;
  height: 150px !important; margin-bottom: 18px !important; padding: 0 !important;
  background: none !important; border: 0 !important; overflow: hidden !important; }
.idvp-card__media img { width: auto !important; max-width: 100% !important; max-height: 100% !important;
  height: auto !important; object-fit: contain !important; display: block !important; }
.idvp-card h3 { font-size: var(--idv-fs-h4) !important; font-weight: 800 !important;
  color: var(--idv-c-ink-2) !important; margin: 0 0 6px !important; }
.idvp-card-price { margin: 0 0 14px !important; font-weight: 800 !important;
  font-size: var(--idv-fs-h4) !important; color: var(--idv-c-ink-2) !important; }
.idvp-card .idvp-add { margin-top: auto !important; width: 100% !important; }

/* ==========================================================================
   BUTTONS (house set: no green, no gradients)
   ========================================================================== */
.idvp-add, .idvp-enquire, .idvp-btn {
  display: inline-flex !important; align-items: center !important; justify-content: center !important;
  gap: 8px !important; padding: 12px 22px !important; min-height: 46px !important;
  border-radius: var(--idv-r-control) !important; border: 1.5px solid var(--idv-c-primary) !important;
  background: var(--idv-c-primary) !important; color: var(--idv-c-surface) !important;
  font: 700 var(--idv-fs-body)/1.2 var(--idv-font) !important; text-decoration: none !important;
  cursor: pointer !important; transition: background-color .2s ease, transform .2s ease !important; }
.idvp-add:hover, .idvp-btn:hover { background: var(--idv-c-primary-strong) !important; border-color: var(--idv-c-primary-strong) !important; transform: translateY(-2px) !important; }
.idvp-enquire { background: var(--idv-c-surface) !important; color: var(--idv-c-primary) !important; }
.idvp-enquire:hover { background: var(--idv-c-well) !important; transform: translateY(-2px) !important; }
/* Added state. Deliberately NOT green: a green button is the most explicit
   prohibition in the brand guide. Strong blue plus a tick reads as done. */
.idvp-add.is-added { background: var(--idv-c-primary-strong) !important; border-color: var(--idv-c-primary-strong) !important; }
.idvp-remove { background: transparent !important; border: 0 !important; padding: 4px 6px !important;
  color: var(--idv-c-error) !important; font: 700 var(--idv-fs-small)/1.2 var(--idv-font) !important;
  cursor: pointer !important; text-decoration: underline !important; min-height: 0 !important; }

/* ==========================================================================
   SINGLE PRODUCT
   ========================================================================== */
.idvp-single { display: grid !important; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) !important;
  gap: 32px 44px !important; align-items: start !important; }
/* Hero. The frame is FIXED, and that is the point: swapping to an image with a different aspect
   ratio must not move the page. The imported images run from about 0.96 to 1.55 with a mean near
   1.18, so a 6/5 box fills more of itself on average than 4/3 or 3/2 would, and object-fit
   letterboxes the remainder against the surface the border already encloses. This also removes a
   shift that predates the gallery: the markup declared 600x600 while every real image is landscape,
   so the reserved box collapsed the instant the image loaded. */
.idvp-gallery img.idvp-hero { width: 100% !important; height: auto !important;
  aspect-ratio: 6 / 5 !important; object-fit: contain !important;
  border: 1px solid var(--idv-c-border) !important; border-radius: var(--idv-r-card) !important;
  background: var(--idv-c-surface) !important; }
/* Wrapping, not scroll-snapping. Five 84px thumbs plus gaps is 460px in a ~505px column, and 360px
   inside the 448px a 480px phone gives after gutters, so a scroller would have nothing to scroll on
   any real viewport. A clipped thumbnail also does not read as clipped the way a clipped tab label
   does - it just reads as a narrow picture - so overflow here would hide images with no signal. */
.idvp-thumbs { display: flex !important; gap: 10px !important; margin-top: 12px !important; flex-wrap: wrap !important; }
/* Buttons now, not bare images. The frame moved from the <img> to the control, or the two would
   nest and paint a border inside a border. The pointer cursor and hover are gated behind
   .idvp-gallery--ready for the same reason the tab strip is gated: without gallery.js these do
   nothing, and a pointer cursor on a dead control is a lie the stylesheet should not tell. */
.idvp-thumb { appearance: none !important; -webkit-appearance: none !important;
  display: block !important; flex: none !important; width: 84px !important; height: 84px !important;
  padding: 6px !important; margin: 0 !important; cursor: default !important;
  background: var(--idv-c-surface) !important;
  border: 1px solid var(--idv-c-border) !important; border-radius: var(--idv-r-control) !important;
  transition: border-color .2s ease, box-shadow .2s ease !important; }
.idvp-thumb img { display: block !important; width: 100% !important; height: 100% !important;
  object-fit: contain !important; }
.idvp-gallery--ready .idvp-thumb { cursor: pointer !important; }
.idvp-gallery--ready .idvp-thumb:hover { border-color: var(--idv-c-primary) !important; }
/* Active state, the same shape as .idvp-tab[aria-selected="true"] below, but on the honest
   attribute: these are pictures being chosen, not tabs. */
.idvp-thumb[aria-current="true"] { border-color: var(--idv-c-primary) !important;
  box-shadow: inset 0 0 0 1px var(--idv-c-primary) !important; }
/* The house focus ring is a box-shadow and its specificity beats the rule above, so the current
   thumb would lose its ring at the exact moment the keyboard landed on it. Both, explicitly. */
.idvp-thumb[aria-current="true"]:focus-visible {
  box-shadow: 0 0 0 3px rgba(11, 92, 216, 0.35), inset 0 0 0 1px var(--idv-c-primary) !important; }
@media (prefers-reduced-motion: reduce) { .idvp-thumb { transition: none !important; } }
.idvp-title { font-size: var(--idv-fs-h2) !important; font-weight: 800 !important; letter-spacing: -0.015em !important; }
.idvp-sku { color: var(--idv-c-muted) !important; font-size: var(--idv-fs-small) !important; margin: 0 0 10px !important; }
.idvp-sku span { display: inline-block !important; padding: 3px 8px !important;
  background: var(--idv-c-tint) !important; border-radius: var(--idv-r-control) !important;
  font-weight: 700 !important; font-size: 0.78rem !important; color: var(--idv-c-muted) !important; }
/* Price uses the token the guide reserves for prices and big numerals. */
.idvp-price { font-size: var(--idv-fs-stat) !important; font-weight: 800 !important; line-height: 1.05 !important;
  color: var(--idv-c-ink-2) !important; margin: 4px 0 14px !important; }
.idvp-rating { display: flex !important; align-items: center !important; gap: 10px !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; margin: 0 0 16px !important; }
.idvp-stars { display: inline-flex !important; align-items: center !important; gap: 2px !important; }
.idvp-star { fill: var(--idv-c-border) !important; flex: none !important; }
.idvp-star.is-on { fill: var(--idv-c-gold) !important; }
.idvp-short { line-height: 1.65 !important; margin-bottom: 22px !important; }

/* CTA row. One shared control height is what makes the quantity field and both
   buttons sit on a single baseline; they were 0.55rem and 0.6rem before. */
.idvp-cta { display: flex !important; align-items: stretch !important; flex-wrap: wrap !important; gap: 12px !important; margin-top: 4px !important; }
.idvp-qty { display: inline-flex !important; align-items: center !important; gap: 10px !important;
  padding: 0 14px !important; min-height: 46px !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-control) !important; background: var(--idv-c-tint) !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; }
.idvp-qty input { width: 3.5rem !important; padding: 0 !important; border: 0 !important; background: transparent !important;
  font: 700 var(--idv-fs-body)/1 var(--idv-font) !important; color: var(--idv-c-ink-2) !important; text-align: center !important; }
.idvp-cta-note { flex-basis: 100% !important; margin: 6px 0 0 !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; }
.idvp-badge { display: inline-block !important; min-width: 22px !important; padding: 0 7px !important;
  margin-left: 8px !important; background: var(--idv-c-primary) !important; color: var(--idv-c-surface) !important;
  border-radius: var(--idv-r-pill) !important; font-size: 0.75rem !important; line-height: 22px !important;
  text-align: center !important; font-weight: 800 !important; }
.idvp-badge[hidden] { display: none !important; }

/* ==========================================================================
   TABS (underline variant, matching pages/apis .idva-code__tab)
   Panels ship OPEN; tabs.js closes the secondary ones. A reader without
   JavaScript therefore gets the whole document, not four invisible sections.
   ========================================================================== */
.idvp-tabs { grid-column: 1 / -1 !important; margin-top: 44px !important; }
/* One row that scrolls sideways rather than wrapping onto a second line. Four tabs wrapped on a
   phone and the strip stopped reading as tabs at all.
   The scrollbar itself is hidden on purpose: internal scrollbars have been rejected repeatedly on
   this project, and the fade on the right edge is what says there is more to the right. */
.idvp-tablist { display: none; gap: 4px !important; flex-wrap: nowrap !important;
  overflow-x: auto !important; overflow-y: hidden !important;
  scrollbar-width: none !important; -ms-overflow-style: none !important;
  -webkit-overflow-scrolling: touch !important; overscroll-behavior-x: contain !important;
  border-bottom: 1px solid var(--idv-c-border) !important; }
.idvp-tablist::-webkit-scrollbar { display: none !important; height: 0 !important; }
.idvp-tab { flex: none !important; white-space: nowrap !important; }
.idvp-tabs--ready .idvp-tablist { display: flex !important; }
.idvp-tab { appearance: none !important; background: transparent !important; border: 0 !important;
  border-bottom: 2px solid transparent !important; margin-bottom: -1px !important;
  padding: 12px 18px !important; cursor: pointer !important;
  font: 700 var(--idv-fs-body)/1.2 var(--idv-font) !important; color: var(--idv-c-muted) !important; }
.idvp-tab:hover { color: var(--idv-c-ink-2) !important; }
.idvp-tab[aria-selected="true"] { color: var(--idv-c-primary) !important; border-bottom-color: var(--idv-c-primary) !important; }
.idvp-panel { padding-top: 28px !important; line-height: 1.7 !important; }
.idvp-panel[hidden] { display: none !important; }
.idvp-panel img { max-width: 100% !important; height: auto !important; }
.idvp-panel-heading { font-size: var(--idv-fs-h3) !important; font-weight: 800 !important; margin: 0 0 14px !important; }
.idvp-tabs--ready .idvp-panel-heading { position: absolute !important; width: 1px !important; height: 1px !important;
  overflow: hidden !important; clip-path: inset(50%) !important; white-space: nowrap !important; }
.idvp-tabs--single { padding-top: 28px !important; line-height: 1.7 !important; grid-column: 1 / -1 !important; }

/* ==========================================================================
   SPEC TABLE (house data-table pattern)
   ========================================================================== */
.idvp-spec-group { font-size: var(--idv-fs-h4) !important; font-weight: 800 !important; margin: 26px 0 10px !important; }
.idvp-spec-group:first-child { margin-top: 0 !important; }
.idvp-spec-table { width: 100% !important; border-collapse: collapse !important; }
.idvp-spec-table th, .idvp-spec-table td { text-align: left !important; vertical-align: top !important;
  padding: 11px 16px !important; border-bottom: 1px solid var(--idv-c-border) !important;
  font-size: var(--idv-fs-small) !important; }
.idvp-spec-table th { width: 34% !important; font-weight: 700 !important;
  color: var(--idv-c-ink-2) !important; background: var(--idv-c-tint) !important; }
.idvp-spec-table td { white-space: pre-line !important; color: var(--idv-c-text) !important; }

/* video and datasheets */
.idvp-video { position: relative !important; width: 100% !important; max-width: 720px !important;
  aspect-ratio: 16 / 9 !important; background: var(--idv-c-ink) !important;
  border-radius: var(--idv-r-card) !important; overflow: hidden !important; }
.idvp-video iframe { position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important; border: 0 !important; }
.idvp-docs { list-style: none !important; margin: 0 !important; padding: 0 !important; }
.idvp-docs li { border-bottom: 1px solid var(--idv-c-border) !important; }
.idvp-docs a { display: block !important; padding: 14px 2px !important; color: var(--idv-c-primary) !important;
  font-weight: 700 !important; text-decoration: none !important; }
.idvp-docs a:hover { text-decoration: underline !important; }

/* reviews */
.idvp-review { border: 1px solid var(--idv-c-border) !important; border-radius: var(--idv-r-card) !important;
  padding: 18px 20px !important; margin-bottom: 14px !important; background: var(--idv-c-surface) !important; }
.idvp-review-head { margin: 0 0 8px !important; display: flex !important; gap: 12px !important;
  align-items: baseline !important; flex-wrap: wrap !important; font-size: var(--idv-fs-small) !important;
  color: var(--idv-c-ink-2) !important; }
.idvp-review-head time { color: var(--idv-c-muted) !important; }
.idvp-review-body { margin: 0 !important; line-height: 1.65 !important; }

/* empty state */
.idvp-empty { text-align: center !important; padding: 52px 24px !important;
  color: var(--idv-c-muted) !important; font-size: var(--idv-fs-small) !important; }

/* ==========================================================================
   CART, titled Request Order. Form LEFT, items RIGHT, per the owner.
   ========================================================================== */
.idvp-cart { display: grid !important; grid-template-columns: minmax(0, 1fr) !important; gap: 32px !important; align-items: start !important; }
/* Same 1025px threshold as the filter rail, and for the same reason it is a media query rather
   than a class - see the note at the top of the catalogue layout section. A separate, lower
   threshold for the cart was tried and removed: it fired first, which made the names lie, and a
   900px tablet is better served by one full-width form column than by two cramped ones.
   Stacked, the cart goes FIRST. On a phone the form is long enough that the cart would otherwise
   sit below the fold, and the products are what the visitor came to check. */
.idvp .idvp-cart { gap: 40px !important; }
.idvp .idvp-cart__items { order: -1 !important; }
@media (min-width: 1025px) {
  /* Wider gap than the panels needed: with no borders, whitespace is the only thing separating
     the two columns. */
  .idvp:not(.idvp-force-narrow) .idvp-cart {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr) !important; gap: 64px !important; }
  .idvp:not(.idvp-force-narrow) .idvp-cart__items { order: 0 !important; }
}
/* No panel boxes. Two bordered cards around a form and a list made the page look like a
   dashboard, and the shorter of the two left a large empty framed area beside a very tall form.
   Same call as the product cards, where the bordered image well came off: separation comes from
   the headings, the row dividers and the column gap. */
.idvp-cart__panel { background: none !important; border: 0 !important; border-radius: 0 !important;
  box-shadow: none !important; padding: 0 !important; }
.idvp-cart__panel h2 { font-size: var(--idv-fs-h3) !important; font-weight: 800 !important; margin: 0 0 6px !important; }
.idvp-cart__hint { margin: 0 0 20px !important; color: var(--idv-c-muted) !important; font-size: var(--idv-fs-small) !important; }

/* items list */
.idvp-enquiry-items { list-style: none !important; margin: 0 !important; padding: 0 !important; }
.idvp-enquiry-items li { display: grid !important; grid-template-columns: 64px minmax(0, 1fr) !important;
  gap: 14px !important; align-items: start !important; padding: 16px 0 !important;
  border-bottom: 1px solid var(--idv-c-border) !important; }
.idvp-enquiry-items li:first-child { padding-top: 0 !important; }
.idvp-enquiry-items li:last-child { border-bottom: 0 !important; padding-bottom: 0 !important; }
.idvp-item__media { width: 64px !important; height: 64px !important; display: flex !important;
  align-items: center !important; justify-content: center !important; padding: 6px !important;
  background: var(--idv-c-tint) !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-control) !important; overflow: hidden !important; }
.idvp-item__media img { max-width: 100% !important; max-height: 100% !important; width: auto !important;
  height: auto !important; object-fit: contain !important; display: block !important; }
.idvp-item__body { min-width: 0 !important; }
a.idvp-item-name { text-decoration: none !important; }
a.idvp-item-name:hover { color: var(--idv-c-primary) !important; text-decoration: underline !important; }
.idvp-item__media a { display: flex !important; align-items: center !important; justify-content: center !important;
  width: 100% !important; height: 100% !important; }
.idvp-item-name { display: block !important; font-weight: 700 !important; color: var(--idv-c-ink-2) !important;
  font-size: var(--idv-fs-small) !important; line-height: 1.4 !important; margin-bottom: 4px !important; }
.idvp-enquiry-items code { display: inline-block !important; font-family: var(--idv-font) !important;
  font-size: 0.72rem !important; font-weight: 700 !important; color: var(--idv-c-muted) !important;
  background: var(--idv-c-tint) !important; border-radius: var(--idv-r-control) !important; padding: 2px 8px !important; }
.idvp-item__row { display: flex !important; align-items: center !important; gap: 12px !important;
  flex-wrap: wrap !important; margin-top: 10px !important; }
/* Minus / value / plus. The same control on the cart rows and the product page, so the number
   means the same thing in both places. Buttons carry the padding, not the shell. */
.idvp-step { padding: 0 !important; overflow: hidden !important; }
.idvp-step input { width: 2.75rem !important; text-align: center !important;
  color: var(--idv-c-ink-2) !important; font-weight: 800 !important; }
/* The spinner arrows duplicate the buttons and are unusable at this size. */
.idvp-step input::-webkit-outer-spin-button,
.idvp-step input::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0 !important; }
.idvp-step input[type="number"] { -moz-appearance: textfield !important; appearance: textfield !important; }
.idvp-step__b { display: inline-flex !important; align-items: center !important; justify-content: center !important;
  width: 34px !important; align-self: stretch !important; padding: 0 !important;
  background: transparent !important; border: 0 !important; cursor: pointer !important;
  color: var(--idv-c-primary) !important; }
.idvp-step__b:hover { background: var(--idv-c-well) !important; }
.idvp-step__b svg { pointer-events: none !important; }
.idvp-enquiry-items .idvp-step { min-height: 38px !important; }
.idvp-enquiry-items .idvp-step__b { width: 30px !important; }

/* The products field. It is the point of this page, so it is styled as a cart
   summary you can edit rather than as one more text box in a contact form. */
.idvp-cart__form .idvp-hint { display: block !important; font-weight: 400 !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important;
  margin: 0 0 8px !important; }
.idvp-cart__form textarea#idvp-products {
  background: var(--idv-c-tint) !important; border-color: var(--idv-c-primary) !important;
  font-weight: 700 !important; color: var(--idv-c-ink-2) !important; line-height: 1.7 !important; }
.idvp-cart__form textarea#idvp-products::placeholder { font-weight: 400 !important; color: var(--idv-c-muted) !important; }

/* Shown only while the cart is empty; it is the explanation for the disabled button. */
.idvp-emptynote { margin: 0 0 20px !important; padding: 14px 16px !important;
  border: 1px solid var(--idv-c-border) !important; border-left: 3px solid var(--idv-c-primary) !important;
  border-radius: var(--idv-r-control) !important; background: var(--idv-c-tint) !important;
  font-size: var(--idv-fs-small) !important; color: var(--idv-c-muted) !important; }
.idvp-emptynote[hidden] { display: none !important; }
.idvp-cart__form input[type="submit"]:disabled,
.idvp-cart__form button[type="submit"]:disabled {
  opacity: .5 !important; cursor: not-allowed !important; }

/* the form panel */
.idvp-cart__form .wpcf7 { margin: 0 !important; }
.idvp-cart__form label { display: block !important; font-size: var(--idv-fs-small) !important;
  font-weight: 700 !important; color: var(--idv-c-ink-2) !important; margin-bottom: 6px !important;
  /* Avada draws a 1px bottom border on every <label>. Harmless until these became real
     <label for=...> elements, at which point it painted a rule across the form under each
     field name. */
  border: 0 !important; padding-bottom: 0 !important; }
.idvp-cart__form input[type="text"], .idvp-cart__form input[type="email"], .idvp-cart__form input[type="tel"],
.idvp-cart__form input[type="url"], .idvp-cart__form input[type="number"], .idvp-cart__form textarea, .idvp-cart__form select {
  width: 100% !important; padding: 13px 16px !important; border: 1px solid var(--idv-c-border) !important;
  border-radius: var(--idv-r-control) !important; font: 400 var(--idv-fs-body)/1.4 var(--idv-font) !important;
  background: var(--idv-c-tint) !important; color: var(--idv-c-ink-2) !important; margin-bottom: 16px !important; }
.idvp-cart__form input:focus, .idvp-cart__form textarea:focus, .idvp-cart__form select:focus {
  outline: none !important; border-color: var(--idv-c-primary) !important; background: var(--idv-c-surface) !important;
  box-shadow: 0 0 0 3px rgba(11, 92, 216, 0.15) !important; }
.idvp-cart__form textarea { min-height: 120px !important; resize: vertical !important; }
.idvp-cart__form .wpcf7-submit {
  width: 100% !important; padding: 14px 26px !important; min-height: 46px !important;
  background: var(--idv-c-primary) !important; color: var(--idv-c-surface) !important;
  border: 1.5px solid var(--idv-c-primary) !important; border-radius: var(--idv-r-control) !important;
  font: 700 var(--idv-fs-body)/1.2 var(--idv-font) !important; cursor: pointer !important; }
.idvp-cart__form .wpcf7-submit:hover { background: var(--idv-c-primary-strong) !important; border-color: var(--idv-c-primary-strong) !important; }
.idvp-cart__form .wpcf7-not-valid-tip { color: var(--idv-c-error) !important; font-size: var(--idv-fs-small) !important; }
.idvp-cart__form .wpcf7-response-output { border-radius: var(--idv-r-control) !important;
  border-width: 1px !important; font-size: var(--idv-fs-small) !important; padding: 12px 16px !important; }

/* ==========================================================================
   RESPONSIVE. House breakpoints: 1024 / 768 / 480. Every grid collapses on the
   same one, because mixed measures at a single width is what reads as ragged.
   ========================================================================== */
@media (max-width: 1024px) {
  .idvp-single { grid-template-columns: minmax(0, 1fr) !important; gap: 28px !important; }
  /* Centred, not flush left. The single-column collapse above removes the 5fr/6fr grid, so this
     cap made the gallery a full-width item pinned left with the whole remainder dumped on the
     right: measured 224px of void at a 768px viewport and 480px at 1024. */
  .idvp-single .idvp-gallery { max-width: 520px !important; margin-inline: auto !important; }
}
@media (max-width: 768px) {
  .idvp-head h1 { font-size: var(--idv-fs-h2) !important; }
  .idvp-tab { padding: 10px 14px !important; font-size: var(--idv-fs-small) !important; }
  .idvp-spec-table th { width: 42% !important; }
  /* Two across on a phone, asked for directly. auto-fill would give one at this
     width, so the count is fixed rather than derived - and the card shrinks to
     suit rather than the grid stretching to fit an unchanged card. */
  .idvp-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 14px !important; }
  .idvp-card { padding: 12px 12px 16px !important; }
  .idvp-card__media { height: 118px !important; margin-bottom: 12px !important; }
  .idvp-card h3 { font-size: var(--idv-fs-small) !important; }
  .idvp-card-price { font-size: var(--idv-fs-body) !important; margin-bottom: 10px !important; }
  .idvp-card .idvp-add { padding: 10px 10px !important; min-height: 42px !important;
    font-size: var(--idv-fs-small) !important; }
  /* Clear the floating filter pill, which sits over the last row otherwise. */
  .idvp-results { padding-bottom: 76px !important; }

  /* Measured 72px above the tabs at a 504px viewport: 28px of grid row-gap plus this 44px margin.
     The grid gap alone is the right amount, so the margin goes. Desktop keeps its 44px. */
  .idvp-tabs { margin-top: 0 !important; }
  /* No edge fade: the brand gate allows only the two governed gradients, and a hand-rolled one
     here would also have been a white-to-transparent hack that breaks on a tinted background.
     The affordance is the next tab being clipped mid-word, which is what the padding below
     guarantees - a row that ends flush looks finished, a row that ends mid-tab does not. */
  .idvp-tablist { padding-right: 24px !important; }
  .idvp-cart__hint { margin-bottom: 16px !important; }
}
@media (max-width: 480px) {
  .idvp-cta { flex-direction: column !important; align-items: stretch !important; }
  .idvp-cta .idvp-add, .idvp-cta .idvp-enquire, .idvp-qty { width: 100% !important; }
  .idvp-qty { justify-content: space-between !important; }
  .idvp-thumb { width: 64px !important; height: 64px !important; }
}
