/* ==========================================================================
   3DOptix redesign — shared component layer.
   Depends on: dxr-tokens.css

   Contents
     §1  Scoped reset, prose defaults, a11y helpers
     §2  Keyframes (three, and only three) + prefers-reduced-motion
     §3  Container & section rhythm, fluid grids, measures
     §4  Type roles
     §5  Eyebrow (plain + spectrum bar)
     §6  Buttons, CTA link, CTA row
     §7  Beta badge, chips, dots, accent rule, step marker, meter
     §8  Blueprint grid overlay + hero glows
     §9  Hatch placeholder
     §10 Panel header strip, stat ledger, stat band
     §11 PRIMITIVE — hairline grid
     §12 PRIMITIVE — panel with cap rule
     §13 PRIMITIVE — ledger / spec row list
     §14 PRIMITIVE — scrollable data table
     §15 CTA band

   The legacy style.css is 98KB of unprefixed rules, so EVERYTHING here is
   prefixed dxr- and the reset is scoped to .dxr-shell--dxr / body.dxr-body-root.
   ========================================================================== */

/* ==========================================================================
   §1 Scoped reset

   SCOPE CONTRACT — read before editing.
   header.php wraps EVERY page (legacy Elementor pages, the blog, Academy,
   WooCommerce) in `.dxr-shell`. So `.dxr-shell` is NOT a safe reset hook: a
   list-style/margin/text-decoration reset hung on it rewrites legacy content,
   which the project brief classes as a defect. The redesign templates add the
   extra `--dxr` modifier, so every element-level reset below is scoped to
   `.dxr-shell--dxr` and reaches redesign markup only. Never widen these
   selectors back to bare `.dxr-shell`.

   SPECIFICITY CONTRACT — read before editing.
   `.dxr-shell--dxr p{margin:0}` would score (0,1,1) and therefore outrank
   every class-based margin in this file — (0,1,0) — leaving .dxr-hgrid__title,
   .dxr-panel__lede, .dxr-ctaband__title and the whole .dxr-mb-* set inert.
   Wrapping the element half in :where() drops it to (0,1,0) so the later,
   more specific-in-intent component rules win on source order instead.
   Any element-level reset added here MUST use the same :where() form.
   ========================================================================== */
.dxr-shell--dxr,
.dxr-shell--dxr *,
.dxr-shell--dxr *::before,
.dxr-shell--dxr *::after{box-sizing:border-box}

body.dxr-body-root{
  margin:0;
  background:var(--dxr-surface);
  color:var(--dxr-ink);
  font-family:var(--dxr-font-sans);
  -webkit-font-smoothing:antialiased;
  text-wrap:pretty;
}

/* Elementor's own base rule, .elementor-button{color:#fff}, sits at exactly the same
   (0,1,0) as this reset -- :where() zeroes its ARGUMENT, not the .dxr-shell--dxr that
   precedes it -- so the later stylesheet wins, and ours loads later. Every Elementor
   button that relies on that inherited white therefore came out link-blue on colour
   wherever Elementor markup enters a dxr surface: the content slot, the Academy
   library, the popups. Verified against production, where 'Start designing' is white
   on green and this reset does not exist.
   Buttons carrying their own Elementor colour are untouched by the exclusion: they
   already beat this reset today, which is why they match production while the
   white-text ones do not. Same exclusion the prose layer carries. */
.dxr-shell--dxr :where(a:not(.elementor-button):not(.elementor-button-link)){color:var(--dxr-primary);text-decoration:none}
.dxr-shell--dxr :where(a:not(.elementor-button):not(.elementor-button-link)):hover{color:var(--dxr-primary-hover)}
/* Embedded content must never widen the document. `height:auto` is deliberately
   NOT applied to iframe/svg: WordPress emits intrinsic width/height attributes
   on every wp_get_attachment_image(), so an <img> capped at max-width:100% keeps
   its ATTRIBUTE height and distorts (a 1440x900 asset drawn 280px wide would
   stay 900px tall). img/video carry an intrinsic ratio, so height:auto restores
   it; an iframe does not, and height:auto would collapse it to the UA's 150px. */
.dxr-shell--dxr :where(img,svg,video,iframe){max-width:100%}
.dxr-shell--dxr :where(img,video){height:auto}
.dxr-shell--dxr :where(p){margin:0}   /* every margin in this design is explicit */
.dxr-shell--dxr :where(h1,h2,h3,h4){margin:0}
.dxr-shell--dxr :where(ul,ol){margin:0;padding:0;list-style:none}

/* Long unbroken tokens (a URL, a part number, a mono spec value) are the only
   thing in this layer that can push a text block past the 280px content width a
   320px viewport leaves. `break-word` rather than `anywhere` is deliberate: its
   break opportunities are excluded from min-content sizing, so it cannot alter
   how any grid track or flex item resolves — it fixes overflow without moving
   a single desktop measurement. Flex children need min-width:0 as well; the one
   case that matters is .dxr-ledger__val (§13). */
.dxr-shell--dxr :where(h1,h2,h3,h4,p,li,dd,dt){overflow-wrap:break-word}

/* Accessible-hidden helper (skip links, drawer labels) */
.dxr-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;
}
/* Skip link — visible only on focus, sits above the sticky nav */
.dxr-skip-link{
  position:absolute;left:var(--dxr-gutter);top:-100px;z-index:calc(var(--dxr-nav-z) + 10);
  padding:12px 18px;border-radius:var(--dxr-radius-md);
  background:var(--dxr-ink-deep);color:var(--dxr-on-dark);
  font:600 14px/1 var(--dxr-font-sans);
}
.dxr-skip-link:focus{top:12px;color:var(--dxr-on-dark)}

/* Focus visibility — the prototype has none; required in production.
   Scoped to --dxr so legacy pages keep whatever focus ring they ship with. */
.dxr-shell--dxr :where(:focus-visible){
  outline:2px solid var(--dxr-primary);
  outline-offset:2px;
  border-radius:var(--dxr-radius-rule);
}
.dxr-on-dark :focus-visible,
.dxr-section--dark :focus-visible,
.dxr-hero--dark :focus-visible{outline-color:var(--dxr-cyan-light)}

/* ==========================================================================
   §2 The only three keyframes in the system
   ========================================================================== */
@keyframes dxr-pulseDot{0%,100%{opacity:.3}50%{opacity:1}}
@keyframes dxr-wavefront{
  0%{transform:translateX(0);opacity:0}
  12%{opacity:.5}
  88%{opacity:.5}
  100%{transform:translateX(152px);opacity:0}
}
@keyframes dxr-sweepX{0%{transform:translateX(-110%)}100%{transform:translateX(110%)}}

@media (prefers-reduced-motion:reduce){
  /* Scoped to --dxr for the same reason as §1: the blanket animation/transition
     kill must not reach legacy Elementor / WooCommerce markup. */
  .dxr-shell--dxr *,
  .dxr-shell--dxr *::before,
  .dxr-shell--dxr *::after{
    animation-duration:.001ms!important;
    animation-iteration-count:1!important;
    transition-duration:.001ms!important;
  }
  /* pulse dots must settle VISIBLE, not at opacity .3 */
  .dxr-dot--pulse,
  .dxr-dot--pulse-slow{animation:none!important;opacity:1!important}
  .dxr-wavefront{animation:none!important;opacity:.5!important}
  .dxr-sweep{animation:none!important;transform:none!important;opacity:0!important}
  .dxr-hero-video{display:none}          /* show the poster instead */
}

/* ==========================================================================
   §3 Container & section rhythm
   ========================================================================== */
.dxr-container{max-width:var(--dxr-container);margin:0 auto;padding:0 var(--dxr-gutter)}

.dxr-section{padding:var(--dxr-section-y) 0}
.dxr-section--tight  {padding:var(--dxr-section-y-tight) 0}
.dxr-section--loose  {padding:var(--dxr-section-y-loose) 0}
.dxr-section--compact{padding:var(--dxr-section-y-compact) 0}

/* surfaces */
.dxr-section--sunken{background:var(--dxr-surface-sunken)}
.dxr-section--dark  {background:var(--dxr-ink-deep);color:var(--dxr-on-dark)}
/* A BUTTON IS NOT A LINK HERE. This rule is (0,1,1) and .dxr-btn--cyan is
   (0,1,0), so without the exclusion a dark section repainted every button's text
   cyan-light and beat the variant that owns that colour: "Request a pilot" came
   out cyan-light ON the cyan fill (~1.5:1), and "Get the plugin" came out
   cyan-light on the blue primary. The hero variants escaped only because a hero
   is .dxr-hero--dark, not .dxr-section--dark, which is why the same button looked
   right higher up the same page. Buttons carry their own ink; leave them alone. */
.dxr-section--dark a:not(.dxr-btn){color:var(--dxr-cyan-light)}
.dxr-section--dark a:not(.dxr-btn):hover{color:var(--dxr-on-dark)}

/* Section boundaries — a section carries EITHER a top or a bottom hairline,
   never both, so rules never double up between adjacent sections. */
.dxr-section--rule-b{border-bottom:1px solid var(--dxr-border)}
.dxr-section--rule-t{border-top:1px solid var(--dxr-border)}

/* Sections that host absolute overlays (blueprint / glow) */
.dxr-section--overlay{position:relative;overflow:hidden}
.dxr-section__inner{position:relative}

/* Heroes need their own padding because the overlays are absolute */
.dxr-hero{
  position:relative;overflow:hidden;
  background:var(--dxr-surface);
  border-bottom:1px solid var(--dxr-border);
}
.dxr-hero--dark{background:var(--dxr-ink-deep);border-bottom:0;color:var(--dxr-on-dark)}
.dxr-hero__inner{
  position:relative;
  max-width:var(--dxr-container);margin:0 auto;
  padding:var(--dxr-section-y) var(--dxr-gutter) var(--dxr-section-y-tight);
}

/* Fluid grids. The min() guard prevents overflow below N. Never change N. */
.dxr-grid{display:grid;gap:var(--dxr-gap-panels)}
.dxr-grid--2up   {grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-2up)),1fr))}
.dxr-grid--3up   {grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-3up)),1fr))}
.dxr-grid--4up   {grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-4up)),1fr))}
.dxr-grid--panels{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-panels)),1fr));align-items:stretch}
.dxr-grid--hero{
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-2up)),1fr));
  gap:var(--dxr-gap-col-hero);align-items:center;
}
.dxr-grid--band{
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-band)),1fr));
  gap:var(--dxr-gap-col-xwide);align-items:center;
}

/* Heading pair: H2 left, supporting paragraph right, baselines aligned bottom */
.dxr-headpair{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-headpair)),1fr));
  gap:var(--dxr-gap-col-hero);align-items:end;
}

/* Section intro copy widths, verbatim from the prototype */
.dxr-measure-560{max-width:min(100%,560px)}
.dxr-measure-620{max-width:min(100%,620px)}
.dxr-measure-640{max-width:min(100%,640px)}
.dxr-measure-660{max-width:min(100%,660px)}
.dxr-measure-820{max-width:min(100%,820px)}

/* Explicit vertical spacers — no margin collapsing games */
.dxr-mb-10{margin-bottom:10px}
.dxr-mb-12{margin-bottom:12px}
.dxr-mb-18{margin-bottom:18px}
.dxr-mb-22{margin-bottom:22px}
.dxr-mb-26{margin-bottom:26px}
.dxr-mb-32{margin-bottom:32px}
.dxr-mb-40{margin-bottom:40px}

/* ==========================================================================
   §4 Type roles
   The `font:` shorthand is deliberate — it matches the prototype and resets
   stray metrics inherited from the legacy stylesheet.
   ========================================================================== */
.dxr-h1            {font:800 clamp(36px,5.6vw,62px)/1.04 var(--dxr-font-sans);letter-spacing:-.042em;color:var(--dxr-ink)}
.dxr-h1--page      {font:800 clamp(31px,4.9vw,54px)/1.05 var(--dxr-font-sans);letter-spacing:-.042em;color:var(--dxr-ink)}
.dxr-h1--compact   {font:800 clamp(30px,4.7vw,52px)/1.06 var(--dxr-font-sans);letter-spacing:-.042em;color:var(--dxr-ink)}
.dxr-h2            {font:800 clamp(24px,3.8vw,42px)/1.1  var(--dxr-font-sans);letter-spacing:-.036em;color:var(--dxr-ink)}
.dxr-h2--home      {font:800 clamp(26px,4.0vw,44px)/1.08 var(--dxr-font-sans);letter-spacing:-.036em;color:var(--dxr-ink)}
.dxr-h2--alt       {font:800 clamp(23px,3.6vw,40px)/1.1  var(--dxr-font-sans);letter-spacing:-.036em;color:var(--dxr-ink)}
.dxr-h2--mid       {font:800 clamp(26px,3.3vw,36px)/1.14 var(--dxr-font-sans);letter-spacing:-.034em;color:var(--dxr-ink)}
.dxr-h2--band      {font:800 clamp(24px,3.1vw,34px)/1.16 var(--dxr-font-sans);letter-spacing:-.032em;color:var(--dxr-ink)}
.dxr-h2--article   {font:800 clamp(22px,2.5vw,28px)/1.25 var(--dxr-font-sans);letter-spacing:-.028em;color:var(--dxr-ink)}
.dxr-h3-product    {font:800 clamp(19px,2.2vw,24px)/1.16 var(--dxr-font-sans);letter-spacing:-.03em; color:var(--dxr-ink)}
.dxr-h3-card       {font:700 17px/1.35 var(--dxr-font-sans);letter-spacing:-.015em;color:var(--dxr-ink)}
.dxr-h3-card--step {font:700 17px/1.32 var(--dxr-font-sans);letter-spacing:-.015em;color:var(--dxr-ink)}
.dxr-h3-faq        {font:700 16px/1.35 var(--dxr-font-sans);color:var(--dxr-ink)}
.dxr-panel-title   {font:700 18px/1.3 var(--dxr-font-sans);letter-spacing:-.02em;color:var(--dxr-ink)}
.dxr-row-title     {font:700 14px/1.35 var(--dxr-font-sans);color:var(--dxr-ink)}
.dxr-row-title--tight{font:700 14px/1.3 var(--dxr-font-sans);color:var(--dxr-ink)}

/* On a dark surface every heading role flips to white */
.dxr-section--dark .dxr-h1,.dxr-section--dark .dxr-h1--page,.dxr-section--dark .dxr-h1--compact,
.dxr-section--dark .dxr-h2,.dxr-section--dark .dxr-h2--home,.dxr-section--dark .dxr-h2--alt,
.dxr-section--dark .dxr-h2--mid,.dxr-section--dark .dxr-h2--band,
.dxr-hero--dark .dxr-h1,.dxr-hero--dark .dxr-h1--page,.dxr-hero--dark .dxr-h1--compact,
.dxr-hero--dark .dxr-h2,.dxr-hero--dark .dxr-h2--band,
.dxr-on-dark .dxr-h2,.dxr-on-dark .dxr-h2--band{color:var(--dxr-on-dark)}
/* the dark band H2 runs a slightly looser line */
.dxr-section--dark .dxr-h2--band,
.dxr-hero--dark .dxr-h2--band{line-height:1.18}

/* Prose */
.dxr-lede            {font:400 17px/1.72 var(--dxr-font-sans);color:var(--dxr-body-muted)}
.dxr-lede--home      {font:400 18px/1.68 var(--dxr-font-sans);color:var(--dxr-body-muted)}
.dxr-body            {font:400 16px/1.72 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-body--panel     {font:400 15px/1.68 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-body-sm         {font:400 14px/1.65 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-body-sm--tight  {font:400 13px/1.6  var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-body-band       {font:400 15px/1.7  var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-body-article    {font:400 17px/1.8  var(--dxr-font-sans);color:var(--dxr-body)}
.dxr-quote           {font:600 clamp(21px,2.5vw,27px)/1.42 var(--dxr-font-sans);letter-spacing:-.02em;color:var(--dxr-ink)}

.dxr-section--dark .dxr-lede,.dxr-hero--dark .dxr-lede,
.dxr-section--dark .dxr-lede--home,.dxr-hero--dark .dxr-lede--home,
.dxr-section--dark .dxr-body,.dxr-section--dark .dxr-body-band,
.dxr-hero--dark .dxr-body,.dxr-hero--dark .dxr-body-band{color:var(--dxr-on-dark-body)}
.dxr-section--dark .dxr-body-sm,.dxr-hero--dark .dxr-body-sm{color:var(--dxr-on-dark-secondary)}

/* Mono roles. Mono is permitted ONLY in these roles — never in a paragraph,
   a heading, or a button label. */
.dxr-label        {font:500 10px/1 var(--dxr-font-mono);letter-spacing:.14em;text-transform:uppercase;color:var(--dxr-text-subtle)}
.dxr-label--strip {font:500 10px/1 var(--dxr-font-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--dxr-text-subtle)}
.dxr-th           {font:500 10px/1 var(--dxr-font-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--dxr-text-subtle)}
.dxr-stat         {font:500 clamp(24px,3.1vw,34px)/1 var(--dxr-font-mono);letter-spacing:-.03em;color:var(--dxr-ink)}
.dxr-price        {font:500 clamp(26px,3.3vw,36px)/1 var(--dxr-font-mono);letter-spacing:-.03em;color:var(--dxr-ink)}
.dxr-price--custom{font:500 clamp(20px,2.4vw,26px)/1.3 var(--dxr-font-mono);letter-spacing:-.02em;color:var(--dxr-on-dark)}
.dxr-spec         {font:500 13px/1 var(--dxr-font-mono);color:var(--dxr-ink)}
.dxr-spec-label   {font:400 11px/1.3 var(--dxr-font-sans);color:var(--dxr-text-subtle)}
.dxr-mono-note    {font:400 12px/1.5 var(--dxr-font-mono);color:var(--dxr-text-subtle)}
.dxr-mono-note--wrap{font:400 12px/1.6 var(--dxr-font-mono);color:var(--dxr-text-subtle)}
.dxr-mono-11      {font:400 11px/1.5 var(--dxr-font-mono)}
.dxr-mono-index   {font:500 11px/1 var(--dxr-font-mono);color:var(--dxr-accent-text)}
.dxr-mono-caption {font:400 10px/1 var(--dxr-font-mono);color:var(--dxr-hatch-ink)}

.dxr-section--dark .dxr-stat,.dxr-hero--dark .dxr-stat,
.dxr-section--dark .dxr-spec,.dxr-hero--dark .dxr-spec{color:var(--dxr-on-dark)}
.dxr-section--dark .dxr-label,.dxr-hero--dark .dxr-label,
.dxr-section--dark .dxr-th,.dxr-hero--dark .dxr-th{color:var(--dxr-on-dark-caption)}

/* ==========================================================================
   §5 Eyebrow — both forms
   ========================================================================== */
/* (a) plain mono eyebrow */
.dxr-eyebrow{
  font:500 11px/1 var(--dxr-font-mono);letter-spacing:.13em;text-transform:uppercase;
  color:var(--dxr-text-subtle);margin:0 0 20px;
}
.dxr-eyebrow--accent {color:var(--dxr-primary)}
.dxr-eyebrow--on-dark{color:var(--dxr-cyan-light)}
.dxr-eyebrow--flush  {margin-bottom:0}

/* (b) spectrum eyebrow: 44x3 five-stop bar + label. Heroes + page headers. */
.dxr-eyebrow-bar{display:flex;align-items:center;gap:14px;margin:0 0 26px}
.dxr-eyebrow-bar--tight{margin-bottom:22px}
.dxr-eyebrow-bar__rule{
  width:44px;height:3px;border-radius:var(--dxr-radius-rule);
  background:var(--dxr-spectrum-eyebrow);flex-shrink:0;
}
.dxr-eyebrow-bar__label{
  font:500 11px/1 var(--dxr-font-mono);letter-spacing:.13em;text-transform:uppercase;
  color:var(--dxr-primary);
}
.dxr-hero--dark .dxr-eyebrow-bar__label,
.dxr-section--dark .dxr-eyebrow-bar__label{color:var(--dxr-cyan-light)}

/* ==========================================================================
   §6 Buttons
   ========================================================================== */
.dxr-btn{
  display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;
  border-radius:var(--dxr-radius-md);border:1px solid transparent;
  font-family:var(--dxr-font-sans);text-decoration:none;cursor:pointer;
  transition:background-color .15s ease,border-color .15s ease,color .15s ease;
}
/* sizes: height / horizontal padding / font */
.dxr-btn--xl   {height:52px;padding:0 26px;font:700 15px/1 var(--dxr-font-sans)}  /* Home hero */
.dxr-btn--lg   {height:50px;padding:0 26px;font:700 15px/1 var(--dxr-font-sans)}  /* page hero, CTA bands */
.dxr-btn--md   {height:48px;padding:0 24px;font:700 14px/1 var(--dxr-font-sans)}  /* in-panel bands */
.dxr-btn--nav  {height:44px;padding:0 22px;font:700 14px/1 var(--dxr-font-sans)}  /* nav CTA */
.dxr-btn--block{height:46px;padding:0 18px;width:100%;font:700 14px/1 var(--dxr-font-sans)} /* pinned panel CTA */
/* secondary/ghost forms run one weight lighter at every size */
.dxr-btn--secondary.dxr-btn--xl,
.dxr-btn--secondary.dxr-btn--lg,
.dxr-btn--secondary.dxr-btn--md,
.dxr-btn--secondary.dxr-btn--nav,
.dxr-btn--secondary.dxr-btn--block,
.dxr-btn--secondary-violet.dxr-btn--xl,
.dxr-btn--secondary-violet.dxr-btn--lg,
.dxr-btn--secondary-violet.dxr-btn--md,
.dxr-btn--secondary-violet.dxr-btn--nav,
.dxr-btn--secondary-violet.dxr-btn--block{font-weight:600}

/* variants */
.dxr-btn--primary{background:var(--dxr-primary);color:var(--dxr-on-dark)}
.dxr-btn--primary:hover{background:var(--dxr-primary-hover);color:var(--dxr-on-dark)}

.dxr-btn--secondary{background:var(--dxr-surface);border-color:var(--dxr-border-strong);color:var(--dxr-ink)}
.dxr-btn--secondary:hover{border-color:var(--dxr-primary);color:var(--dxr-primary)}

/* per-accent: violet (OptiTwin) */
.dxr-btn--violet{background:var(--dxr-violet-deep);color:var(--dxr-on-dark)}
.dxr-btn--violet:hover{background:var(--dxr-violet-hover);color:var(--dxr-on-dark)}
.dxr-btn--secondary-violet{background:var(--dxr-surface);border-color:var(--dxr-border-strong);color:var(--dxr-ink)}
.dxr-btn--secondary-violet:hover{border-color:var(--dxr-violet-deep);color:var(--dxr-violet-deep)}

/* per-accent: cyan (TrueScene). Fill is cyan, TEXT IS DARK — never white. */
.dxr-btn--cyan{background:var(--dxr-cyan);color:var(--dxr-cyan-ink)}
.dxr-btn--cyan:hover{background:var(--dxr-cyan-hover);color:var(--dxr-cyan-ink)}

/* dark-section variants */
.dxr-btn--ghost-dark{background:transparent;border-color:var(--dxr-btn-border-dark);color:var(--dxr-on-dark);font-weight:600}
.dxr-btn--ghost-dark:hover{border-color:var(--dxr-btn-border-dark-hover);color:var(--dxr-on-dark)}
.dxr-btn--inverse{background:var(--dxr-surface);color:var(--dxr-ink)}   /* Enterprise panel */
.dxr-btn--inverse:hover{background:var(--dxr-surface-pressed);color:var(--dxr-ink)}

/* accent-driven variant — resolves from the page accent channel */
.dxr-btn--accent{background:var(--dxr-accent);color:var(--dxr-accent-ink)}
.dxr-btn--accent:hover{background:var(--dxr-accent-hover);color:var(--dxr-accent-ink)}

/* Text-link CTA — the panel-bottom "Explore X →" form.
   `font:600 14px/1` with no padding makes the flex line box exactly 14px tall,
   which is the whole tap target — this is the primary next-step link on every
   product panel. Padding would move it and break the margin-top:auto pinning
   contract (§12) that aligns CTAs across panels of unequal height, so the hit
   area is grown with a transparent overlay instead — visual position untouched.
   16px rather than 15 so the 13px variants the Academy article and aside apply
   still clear 44px (13 + 32 = 45); the canonical 14px form lands at 46. Vertical
   only, so it can never reach a neighbouring column, and 16px sits inside the
   22px .dxr-panel__lede / 24px ledger margin above every pinned CTA. */
.dxr-cta-link{
  position:relative;
  display:inline-flex;align-items:center;gap:8px;
  font:600 14px/1 var(--dxr-font-sans);color:var(--dxr-accent-text);
}
.dxr-cta-link::after{content:"";position:absolute;inset:-16px 0}
.dxr-cta-link:hover{color:var(--dxr-accent-hover)}
.dxr-cta-link__arrow{font:500 13px/1 var(--dxr-font-mono)}  /* typographic, not an icon */
/* A grid cell's read-more, spaced off the body it follows.
   NOT bottom-aligned across a row: that would need .dxr-hgrid__cell to become a
   column flexbox, and that cell is shared by every capability, principle and
   step grid in the redesign -- changing its display model for one optional link
   is a worse trade than links that sit under copy of differing lengths. */
.dxr-hgrid__link{padding-top:14px}
.dxr-section--dark .dxr-cta-link,.dxr-on-dark .dxr-cta-link{color:var(--dxr-cyan-light)}
.dxr-section--dark .dxr-cta-link:hover,.dxr-on-dark .dxr-cta-link:hover{color:var(--dxr-on-dark)}

/* CTA cluster */
.dxr-cta-row{display:flex;gap:var(--dxr-gap-cta);flex-wrap:wrap}

/* ==========================================================================
   §7 Beta badge, chips, dots, accent rule, step marker, meter
   ========================================================================== */
.dxr-badge-beta{
  display:inline-block;font:500 10px/1 var(--dxr-font-mono);letter-spacing:.08em;
  text-transform:uppercase;color:var(--dxr-amber-deep);background:var(--dxr-amber-tint);
  border-radius:var(--dxr-radius-badge);padding:5px 8px;
}
.dxr-chip{
  display:inline-block;font:500 9px/1 var(--dxr-font-mono);letter-spacing:.1em;
  text-transform:uppercase;border-radius:var(--dxr-radius-badge);padding:5px 7px;
}
.dxr-chip--popular{color:var(--dxr-primary-hover);background:var(--dxr-primary-tint)}
.dxr-chip--soon   {color:var(--dxr-amber-deep);background:var(--dxr-amber-tint)}

.dxr-dot{width:5px;height:5px;border-radius:var(--dxr-radius-round);flex-shrink:0;background:var(--dxr-border-strong)}
.dxr-dot--6{width:6px;height:6px}
.dxr-dot--live{background:var(--dxr-green)}
.dxr-dot--cyan{background:var(--dxr-cyan)}
.dxr-dot--violet{background:var(--dxr-violet)}
.dxr-dot--primary{background:var(--dxr-primary)}
.dxr-dot--pulse{animation:dxr-pulseDot 2s ease-in-out infinite}
.dxr-dot--pulse-slow{animation:dxr-pulseDot 2.4s ease-in-out infinite}  /* topbar */

/* 26x3 accent rule — the hairline-grid cell marker. NO icons anywhere. */
.dxr-accent-rule{
  width:26px;height:3px;border-radius:var(--dxr-radius-rule);
  background:var(--dxr-accent-rule,var(--dxr-primary));margin-bottom:20px;
}
.dxr-accent-rule--blue  {background:var(--dxr-primary)}
.dxr-accent-rule--cyan  {background:var(--dxr-cyan)}
.dxr-accent-rule--violet{background:var(--dxr-violet)}
.dxr-accent-rule--green {background:var(--dxr-green)}

/* Step / Stage marker: mono label then a hairline filling the rest */
.dxr-step{display:flex;align-items:center;gap:10px;margin-bottom:18px}
.dxr-step__label{font:500 11px/1 var(--dxr-font-mono);color:var(--dxr-accent-text)}
.dxr-step__rule{flex:1;height:1px;background:var(--dxr-border-inner)}

/* Meter bar (deployment table "Scale" column) */
/* display:block on BOTH is load-bearing. dxr_data_table() emits the meter as
   nested <span>s, and an inline box ignores width and height — so every fill
   computed a correct width:N% and background, then rendered at zero size. The
   deployment-layers table looked like four identical empty grey bars. */
.dxr-meter{display:block;width:100%;height:5px;background:var(--dxr-border-inner);border-radius:var(--dxr-radius-meter);max-width:130px}
.dxr-meter__fill{display:block;height:100%;border-radius:var(--dxr-radius-meter);background:var(--dxr-accent)}
.dxr-meter__fill--spectrum{background:var(--dxr-gradient-meter)}

/* Animation carriers (markup is authored by the page templates) */
/* Wavefront timing is verbatim from the prototype ('3DOptix Website.dc.html'
   lines 420-422): `wavefront 3.6s linear infinite`. THREE lines share the
   class and are staggered by per-instance animation-delay ONLY — line 1 no
   delay, line 2 `animation-delay:1.2s`, line 3 `animation-delay:2.4s` (one
   third of the period each). The delays belong on the instances in the page
   template, not here; do not fold them into this shared rule. Easing is
   `linear`, not ease-in-out — the sweep must read as constant-velocity. */
.dxr-wavefront{animation:dxr-wavefront 3.6s linear infinite}
.dxr-sweep{animation:dxr-sweepX 2.8s ease-in-out infinite}

/* ==========================================================================
   §8 Blueprint grid overlay + hero glow
   ========================================================================== */
.dxr-blueprint{
  position:absolute;inset:0;pointer-events:none;
  background-image:
    linear-gradient(var(--dxr-blueprint,var(--dxr-blueprint-line)) 1px,transparent 1px),
    linear-gradient(90deg,var(--dxr-blueprint,var(--dxr-blueprint-line)) 1px,transparent 1px);
  background-size:var(--dxr-blueprint-size) var(--dxr-blueprint-size);
}
.dxr-blueprint--violet{--dxr-blueprint:var(--dxr-blueprint-line-violet)}
.dxr-blueprint--dark  {--dxr-blueprint:var(--dxr-blueprint-line-dark)}    /* dark bands */
.dxr-blueprint--dark-2{--dxr-blueprint:var(--dxr-blueprint-line-dark-2)}  /* dark heroes */
.dxr-blueprint--accent{--dxr-blueprint:var(--dxr-accent-blueprint)}
.dxr-blueprint--64    {--dxr-blueprint-size:64px}                         /* Academy index only */

/* Hero glow — one per hero, bleeding off the top-right. */
.dxr-glow{position:absolute;border-radius:var(--dxr-radius-round);pointer-events:none}
.dxr-glow--home{top:-220px;right:-140px;width:680px;height:680px;
  background:radial-gradient(circle,rgba(6,182,212,0.16),rgba(37,99,235,0.06) 45%,transparent 70%)}
.dxr-glow--ds{top:-240px;right:-160px;width:640px;height:640px;
  background:radial-gradient(circle,rgba(37,99,235,0.14),rgba(6,182,212,0.05) 48%,transparent 72%)}
.dxr-glow--optitwin{top:-240px;right:-160px;width:640px;height:640px;
  background:radial-gradient(circle,rgba(124,92,240,0.16),rgba(37,99,235,0.05) 48%,transparent 72%)}
.dxr-glow--truescene{top:-260px;right:-140px;width:680px;height:680px;
  background:radial-gradient(circle,rgba(6,182,212,0.22),rgba(37,99,235,0.08) 46%,transparent 72%)}
.dxr-glow--about{top:-240px;right:-120px;width:620px;height:620px;
  background:radial-gradient(circle,rgba(37,99,235,0.24),rgba(6,182,212,0.08) 46%,transparent 72%)}

/* ==========================================================================
   §9 Hatch placeholder
   Every hatch block carries a mono caption naming the missing asset. They are
   MEANT to look unresolved.
   ========================================================================== */
/* The gap matters once a --split hatch carries two captions. justify-content
   only distributes LEFTOVER space, and at a 365px viewport there is none: the
   pair fills the row, so the label and the asset name ended up touching at
   exactly the same x and read as one run ("game-engine renderasset: ts-…").
   A gap is reserved before free space is distributed, so the two stay apart at
   every width. Inert on the single-caption hatches. */
.dxr-hatch{
  display:flex;align-items:flex-end;gap:14px;padding:14px 16px;
  background:repeating-linear-gradient(135deg,
    var(--dxr-hatch-a) 0 8px,var(--dxr-hatch-b) 8px 16px);
}
.dxr-hatch--pad-lg{padding:16px 18px}
.dxr-hatch--split {justify-content:space-between}
.dxr-hatch--violet{--dxr-hatch-a:var(--dxr-hatch-violet-a);--dxr-hatch-b:var(--dxr-hatch-violet-b)}
.dxr-hatch--cyan  {--dxr-hatch-a:var(--dxr-hatch-cyan-a);  --dxr-hatch-b:var(--dxr-hatch-cyan-b)}
.dxr-hatch--dark  {--dxr-hatch-a:var(--dxr-hatch-dark-a);  --dxr-hatch-b:var(--dxr-hatch-dark-b)}
.dxr-hatch--dark-cyan{--dxr-hatch-a:var(--dxr-hatch-dcyan-a);--dxr-hatch-b:var(--dxr-hatch-dcyan-b)}
.dxr-hatch__caption{font:400 10px/1 var(--dxr-font-mono);color:var(--dxr-hatch-ink)}
.dxr-hatch--violet .dxr-hatch__caption{color:var(--dxr-hatch-violet-ink)}
.dxr-hatch--cyan   .dxr-hatch__caption{color:var(--dxr-hatch-cyan-ink)}
.dxr-hatch--dark   .dxr-hatch__caption,
.dxr-hatch--dark-cyan .dxr-hatch__caption{color:var(--dxr-on-dark-image)}
/* heights, verbatim: 172 tutorial cards · 180 product preview & portraits
   200 article aside · 220 TrueScene comparison · 300 large hero panels */
.dxr-hatch--h172{height:172px}
.dxr-hatch--h180{height:180px}
.dxr-hatch--h200{height:200px}
.dxr-hatch--h220{height:220px}
.dxr-hatch--h300{height:300px}

/* ==========================================================================
   §10 Panel header strip, stat ledger, stat band
   ========================================================================== */
/* header strip on a screenshot/diagram panel */
/* flex-wrap: the strip is one 14px caption bar with room to spare from ~420px
   of content width up. Below that, label + status exceed the line and both flex
   items shrink to their min-content floor, turning a one-line bar into four
   ragged lines. Wrapping drops the status to a second line instead — two clean
   lines, and nothing changes at any width where the pair already fits. */
.dxr-strip{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;
  padding:var(--dxr-pad-strip);
  border-bottom:1px solid var(--dxr-border-inner);
  background:var(--dxr-surface-raised);
}
.dxr-strip__label{font:500 10px/1 var(--dxr-font-mono);letter-spacing:.12em;
  text-transform:uppercase;color:var(--dxr-text-subtle)}
.dxr-strip__status{display:flex;align-items:center;gap:7px;
  font:500 10px/1 var(--dxr-font-mono);color:var(--dxr-green-status)}
.dxr-on-dark .dxr-strip,
.dxr-section--dark .dxr-strip{
  background:transparent;border-bottom-color:var(--dxr-border-on-dark-soft);padding:15px 20px;
}
.dxr-on-dark .dxr-strip__label,.dxr-section--dark .dxr-strip__label{color:var(--dxr-on-dark-caption)}
.dxr-on-dark .dxr-strip__status,.dxr-section--dark .dxr-strip__status{color:var(--dxr-cyan-light)}

/* 3-cell stat ledger under a hero panel.
   RULE MECHANISM — §11's hardened cell ring, not per-cell border-right.
   border-right is direction-locked: the moment the grid collapses to one
   column the three readouts would stack with NO separator between them and two
   orphan vertical ticks left hanging at the right edge. gap:1px + a 1px ring on
   each cell is direction-agnostic — it draws whichever gaps actually exist.
   overflow:hidden clips the outer ring so the perimeter still reads exactly as
   before: one border-top on the container and nothing else.

   COLUMNS ARE EXPLICIT, and --dxr-min-3up must NOT come back here. That token
   is 320px, tuned for a 3-up card grid spanning the full 1300px container.
   This ledger's container is a HERO PANEL, which measures 423-580px at every
   desktop width — three 320px columns need 3x320 + 2 = 962px, so auto-fit
   resolved to a single column and the readout stacked vertically on every
   screen, not just the phones the note above assumed. Worse, where the hero
   stacks (745-885px) the panel widens to 682-811px and auto-fit found room for
   exactly TWO columns, leaving a 2+1 orphan.
   No single auto-fit minimum fixes both ends: 3-up inside a 423px panel needs
   m <= 140, while collapsing to 1-up inside a 323px phone panel needs m > 161.
   So the two steps are stated outright. 480px is the viewport where the panel
   first reaches 423px, which is where three cells stop being cramped.
   repeat(3) is the contract, not a coincidence: all three call sites pass
   exactly three cells. dxr_home_hero_readout is filterable, so a fourth cell
   would wrap to a row of its own — deliberately visible rather than silently
   resized, because a four-up readout needs its own width decision. */
.dxr-statledger{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:1px;
  border-top:1px solid var(--dxr-border-inner);
  overflow:hidden;
}
@media (min-width:480px){
  .dxr-statledger{grid-template-columns:repeat(3,minmax(0,1fr))}
}
.dxr-statledger__cell{padding:16px 18px;box-shadow:0 0 0 1px var(--dxr-border-inner)}
.dxr-statledger__value{font:500 13px/1 var(--dxr-font-mono);color:var(--dxr-ink);margin-bottom:6px}
.dxr-statledger__label{font:400 11px/1.3 var(--dxr-font-sans);color:var(--dxr-text-subtle)}
.dxr-on-dark .dxr-statledger,.dxr-section--dark .dxr-statledger{border-top-color:var(--dxr-border-on-dark-soft)}
.dxr-on-dark .dxr-statledger__cell,.dxr-section--dark .dxr-statledger__cell{box-shadow:0 0 0 1px var(--dxr-border-on-dark-soft)}
.dxr-on-dark .dxr-statledger__value,.dxr-section--dark .dxr-statledger__value{color:var(--dxr-on-dark)}
.dxr-on-dark .dxr-statledger__label,.dxr-section--dark .dxr-statledger__label{color:var(--dxr-on-dark-caption)}

/* 4-up stat band on a sunken surface.
   Same ring mechanism and same reason as .dxr-statledger above: border-right
   only draws rules in the one direction the band is NOT stacked in.

   The asymmetric first/last padding pulls the rules flush to the container
   edges and is kept — but it is only correct while the band is genuinely 4-up.
   Once the grid collapses, padding-left:0 on the first cell alone leaves that
   one figure 28px to the left of the other three. Four cells resolve at
   4x240 + 3x1 gap = 963px of container; --dxr-gutter has reached its 40px
   maximum by then, so the band is 4-up from a 1044px viewport and the flush-edge
   pair is gated there. Below it the cells are symmetric. */
.dxr-statband{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-4up)),1fr));
  gap:1px;
  overflow:hidden;
}
.dxr-statband__cell{padding:34px 28px;box-shadow:0 0 0 1px var(--dxr-border)}
@media (min-width:1044px){
  .dxr-statband__cell:first-child{padding-left:0}
  .dxr-statband__cell:last-child{padding-right:0}
}
.dxr-statband__figure{
  font:500 clamp(24px,3.1vw,34px)/1 var(--dxr-font-mono);letter-spacing:-.03em;
  color:var(--dxr-ink);margin-bottom:10px;
}
.dxr-statband__label{font:400 13px/1.5 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-section--dark .dxr-statband__cell{box-shadow:0 0 0 1px var(--dxr-border-on-dark-soft)}
.dxr-section--dark .dxr-statband__figure{color:var(--dxr-on-dark)}
.dxr-section--dark .dxr-statband__label{color:var(--dxr-on-dark-body)}

/* ==========================================================================
   §11 PRIMITIVE — HAIRLINE GRID (the most reused pattern on the site)

   Mechanism: gap:1px over a --dxr-border container background, so the GAPS
   become the rules. No per-cell borders, ever.

   HARDENING: with auto-fit and an item count that does not divide by the
   resolved column count (9 cells at 2 columns), the empty trailing track shows
   the container tint as a phantom grey cell. So the rule lives on a 1px cell
   ring instead of the container background: pixel-identical for filled rows,
   and empty tracks stay white.
   ========================================================================== */
.dxr-hgrid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-hgrid-min,var(--dxr-min-3up))),1fr));
  gap:1px;
  background:var(--dxr-surface);          /* hardened — was --dxr-border */
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-panel);
  overflow:hidden;                        /* clips cell corners + the perimeter ring */
}
.dxr-hgrid--4up{--dxr-hgrid-min:var(--dxr-min-4up)}   /* workflow / pipeline steps */
.dxr-hgrid--2up{--dxr-hgrid-min:var(--dxr-min-2up)}

.dxr-hgrid__cell{
  background:var(--dxr-surface);
  padding:var(--dxr-pad-cell);            /* 30px */
  box-shadow:0 0 0 1px var(--dxr-border); /* the hardened rule */
  transition:background-color .15s ease;
}
.dxr-hgrid__cell--step{padding:28px 26px 30px}
/* hover lightens the cell; only on interactive/capability grids, NOT on the
   static Principles grid */
.dxr-hgrid--hover .dxr-hgrid__cell:hover{background:var(--dxr-surface-raised)}

/* Optional cell thumbnail. A fixed height with object-fit:cover so a row reads
   as one band whatever the source aspect ratios are — the workflow captures
   this exists for are 337x309 and 270x247. Bled flush to the cell edges by
   negative margins that cancel the cell's own padding, the same way
   .dxr-panel__media sits flush inside a panel.
   The offsets are stated per variant rather than from one token, because
   --step is asymmetric (28px top / 26px sides) while the base cell is a uniform
   30px — a single value would leave a visible sliver on one edge. */
.dxr-hgrid__media{
  margin:-30px -30px 20px;
  height:132px;overflow:hidden;background:var(--dxr-ink-deep);
}
.dxr-hgrid__cell--step .dxr-hgrid__media{margin:-28px -26px 20px}
.dxr-hgrid__media img{display:block;width:100%;height:100%;object-fit:cover}

.dxr-hgrid__title{margin:0 0 10px;font:700 17px/1.35 var(--dxr-font-sans);
  letter-spacing:-.015em;color:var(--dxr-ink)}
.dxr-hgrid__body{margin:0;font:400 14px/1.65 var(--dxr-font-sans);color:var(--dxr-text-muted)}

/* dark variant */
.dxr-hgrid--on-dark{background:var(--dxr-ink-deep);border-color:var(--dxr-border-on-dark)}
.dxr-hgrid--on-dark .dxr-hgrid__cell{
  background:var(--dxr-ink-deep);box-shadow:0 0 0 1px var(--dxr-border-on-dark);
}
.dxr-hgrid--on-dark .dxr-hgrid__title{color:var(--dxr-on-dark)}
.dxr-hgrid--on-dark .dxr-hgrid__body{color:var(--dxr-on-dark-body)}

/* ==========================================================================
   §12 PRIMITIVE — PANEL WITH CAP RULE
   3px coloured top rule, 12px radius, 1px border, flex column, CTA pinned to
   the bottom with margin-top:auto so CTAs align across unequal panels.
   ========================================================================== */
.dxr-panel{
  display:flex;flex-direction:column;
  background:var(--dxr-surface);
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-panel);
  overflow:hidden;                        /* lets the cap rule inherit the radius */
  transition:border-color .15s ease,box-shadow .15s ease;
}
.dxr-panel__cap{height:3px;flex-shrink:0;background:var(--dxr-accent-rule,var(--dxr-primary))}
.dxr-panel__cap--neutral {background:var(--dxr-border-strong)}   /* Free plan, "Without OptiTwin" */
.dxr-panel__cap--blue    {background:var(--dxr-primary)}
.dxr-panel__cap--violet  {background:var(--dxr-violet)}
.dxr-panel__cap--cyan    {background:var(--dxr-cyan)}
.dxr-panel__cap--green   {background:var(--dxr-green)}
.dxr-panel__cap--spectrum{background:var(--dxr-spectrum-cap)}    /* Enterprise plan ONLY */

/* Media slot. dxr_panel_cap() composes `.dxr-panel__media .dxr-hatch` on the
   same element while an asset is missing, and .dxr-hatch (§9) is a single-class
   selector declared EARLIER, so a solid `background` here would silently win
   and swallow the diagonal-hatch placeholder. The solid fill is therefore only
   applied when the hatch is absent — box metrics stay unconditional. */
.dxr-panel__media{height:180px;overflow:hidden;flex-shrink:0}
.dxr-panel__media:not(.dxr-hatch){background:var(--dxr-ink-deep)}
.dxr-panel__media img{display:block;width:100%;height:100%;object-fit:cover}

.dxr-panel__body{display:flex;flex-direction:column;flex:1;padding:var(--dxr-pad-panel-body)}
.dxr-panel__body--plan{padding:var(--dxr-pad-plan-body)}
.dxr-panel__body--lg  {padding:var(--dxr-pad-panel)}

.dxr-panel__head{display:flex;align-items:center;gap:12px;margin-bottom:12px;flex-wrap:wrap}
.dxr-panel__title{margin:0;font:800 clamp(19px,2.2vw,24px)/1.16 var(--dxr-font-sans);
  letter-spacing:-.03em;color:var(--dxr-ink)}
.dxr-panel__title--plan{font:700 18px/1.3 var(--dxr-font-sans);letter-spacing:-.02em}
.dxr-panel__lede{margin:0 0 22px;font:400 15px/1.68 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-panel__lede--plan{margin:0 0 22px;font:400 13px/1.6 var(--dxr-font-sans);color:var(--dxr-text-muted)}

/* THE PINNING CONTRACT — every panel CTA uses this, nothing else. */
.dxr-panel__cta{margin-top:auto}

/* hover: border shifts to the panel accent */
.dxr-panel--interactive:hover{border-color:var(--dxr-accent-rule,var(--dxr-primary))}

/* emphasis (Professional plan) — the ONLY panel that carries a shadow */
.dxr-panel--emphasis{border-color:var(--dxr-primary);box-shadow:var(--dxr-shadow-emphasis)}

/* inverted (Enterprise plan) */
.dxr-panel--inverted{background:var(--dxr-ink-deep);border-color:var(--dxr-ink-deep);color:var(--dxr-on-dark)}
.dxr-panel--inverted .dxr-panel__title{color:var(--dxr-on-dark)}
.dxr-panel--inverted .dxr-panel__lede,
.dxr-panel--inverted .dxr-panel__lede--plan{color:var(--dxr-on-dark-muted)}

/* panel on a dark section ("Who it is for") */
.dxr-panel--on-dark{background:var(--dxr-panel-on-dark);border-color:var(--dxr-border-on-dark);color:var(--dxr-on-dark)}
.dxr-panel--on-dark .dxr-panel__title{color:var(--dxr-on-dark)}
.dxr-panel--on-dark .dxr-panel__lede,
.dxr-panel--on-dark .dxr-panel__lede--plan{color:var(--dxr-on-dark-body)}

/* borderless panel used only for FAQ cards (no cap rule) */
.dxr-panel--flat{padding:var(--dxr-pad-panel-sm);display:block}

/* ==========================================================================
   §13 PRIMITIVE — LEDGER / SPEC ROW LIST
   One primitive, four row shapes. The separator is always
   1px solid --dxr-border-inner: border-top on every row plus border-bottom on
   the last, so the list reads as fully ruled inside the panel.
   ========================================================================== */
.dxr-ledger{display:flex;flex-direction:column}
.dxr-ledger--gap-24{margin-bottom:24px}     /* inside product / plan panels */

.dxr-ledger__row{
  display:flex;align-items:center;gap:12px;
  padding:11px 0;
  border-top:1px solid var(--dxr-border-inner);
  font:400 13px/1.5 var(--dxr-font-sans);color:var(--dxr-body);
}
.dxr-ledger__row:last-child{border-bottom:1px solid var(--dxr-border-inner)}

/* (i) bulleted — dot + label */
.dxr-ledger--bulleted .dxr-ledger__row > .dxr-dot{margin:0}

/* (ii) marked — typographic checkmark / cross */
.dxr-ledger--marked .dxr-ledger__row{gap:10px;align-items:flex-start}
.dxr-ledger__mark{flex-shrink:0;line-height:1.5}
.dxr-ledger__mark--pass{color:var(--dxr-green-deep)}
.dxr-ledger__mark--fail{color:var(--dxr-red)}
.dxr-ledger__mark--warn{color:var(--dxr-amber-deep)}

/* (iii) label/value — About "In brief", OptiTwin findings */
.dxr-ledger--kv .dxr-ledger__row{
  align-items:baseline;justify-content:space-between;gap:16px;
  padding:var(--dxr-pad-row);                    /* 18px 20px */
  border-top:0;border-bottom:1px solid var(--dxr-border-inner);
}
.dxr-ledger--kv .dxr-ledger__row:last-child{border-bottom:1px solid var(--dxr-border-inner)}
.dxr-ledger__key{font:400 13px/1.4 var(--dxr-font-sans);color:var(--dxr-text-muted)}
/* The row is a flex container, so both children floor at min-content and a mono
   value with NO break opportunity cannot shrink. At 320px the pair shares
   280 - 2 (panel border) - 40 (--dxr-pad-row) - 16 (gap) = 222px; an email in
   the contact ledger ("support@3doptix.com", 19 chars x 7.8px = 148.2) plus the
   key floor (71.5) = 235.7, which .dxr-panel{overflow:hidden} then CLIPS rather
   than scrolls. min-width:0 releases the floor; `anywhere` rather than
   `break-word` because only `anywhere` counts toward min-content sizing, which
   is exactly what a flex item needs. Keys stay word-breaking.

   line-height 1 -> 1.4 so a value that DOES now wrap does not collide with its
   own second line; it matches the key's 13px/1.4 and the OptiTwin findings
   override, and it moves nothing: with align-items:baseline the key already
   contributes 12.35px above and 5.85px below the shared baseline, and 13px/1.4
   gives the value exactly the same pair, so the row's resolved height is
   unchanged at every width where the value still fits on one line. */
.dxr-ledger__val{
  font:500 13px/1.4 var(--dxr-font-mono);color:var(--dxr-ink);text-align:right;
  min-width:0;overflow-wrap:anywhere;
}
.dxr-ledger__val--fail{color:var(--dxr-red)}
.dxr-ledger__val--pass{color:var(--dxr-green-deep)}
.dxr-ledger__val--warn{color:var(--dxr-amber-deep)}

/* (iv) indexed — leading mark + title + description (dark "one engine" panel).
   The mark is either a product identity band or a mono ordinal; see dxr_ledger().
   Note the row border below reads --dxr-border-on-dark-soft unconditionally, so
   this variant is dark-ground BY CONSTRUCTION -- which is why the bands here use
   the lightened gradients and no light-ground variants exist. */
.dxr-ledger--indexed .dxr-ledger__row{
  display:flex;align-items:flex-start;gap:16px;padding:20px;
  border-top:0;border-bottom:1px solid var(--dxr-border-on-dark-soft);
}
.dxr-ledger__idx{font:500 11px/1.5 var(--dxr-font-mono);flex-shrink:0}
.dxr-ledger__idx--01{color:var(--dxr-primary-light)}
.dxr-ledger__idx--02{color:var(--dxr-violet-light)}
.dxr-ledger__idx--03{color:var(--dxr-cyan-light)}
/* Identity band. align-self:stretch overrides the row's align-items:flex-start,
   so the bar spans the title+description stack without being given a height. */
.dxr-ledger__band{width:3px;align-self:stretch;border-radius:2px;flex-shrink:0}
.dxr-ledger__band--design{background:var(--dxr-product-band-design-on-dark)}
.dxr-ledger__band--optitwin{background:var(--dxr-product-band-optitwin-on-dark)}
.dxr-ledger__band--truescene{background:var(--dxr-product-band-truescene-on-dark)}
/* display:block is required, not cosmetic. dxr_ledger() emits both as bare
   <span>s, and an inline box drops vertical margin and cannot start a new line —
   so the title and the description concatenated: "Design & SimulationFast Rays
   for live layout…". The margin-bottom below was inert for the same reason. */
.dxr-ledger__rowtitle{display:block;font:700 14px/1.3 var(--dxr-font-sans);color:var(--dxr-on-dark);margin-bottom:4px}
.dxr-ledger__rowdesc {display:block;font:400 13px/1.5 var(--dxr-font-sans);color:var(--dxr-on-dark-soft)}

/* on-dark row separators (Enterprise plan features) */
.dxr-ledger--on-dark .dxr-ledger__row{
  border-top-color:var(--dxr-border-on-dark-inner);color:var(--dxr-on-dark-strong);
}
.dxr-ledger--on-dark .dxr-ledger__row:last-child{border-bottom-color:var(--dxr-border-on-dark-inner)}
.dxr-ledger--on-dark.dxr-ledger--kv .dxr-ledger__row{border-bottom-color:var(--dxr-border-on-dark-inner)}
.dxr-ledger--on-dark .dxr-ledger__key{color:var(--dxr-on-dark-caption)}
.dxr-ledger--on-dark .dxr-ledger__val{color:var(--dxr-on-dark)}
.dxr-ledger--on-dark .dxr-ledger__mark--pass{color:var(--dxr-green-light)}

/* closing note strip on a ledger panel (patent note) */
.dxr-ledger__note{padding:16px 20px;font:400 11px/1.5 var(--dxr-font-mono);
  color:var(--dxr-amber-deep);background:var(--dxr-amber-tint-soft)}
.dxr-ledger--on-dark .dxr-ledger__note{background:transparent;color:var(--dxr-amber-light)}

/* ==========================================================================
   §14 PRIMITIVE — SCROLLABLE DATA TABLE
   Column widths are fixed; the table scrolls horizontally.
   NEVER stack a spec table into cards.
   ========================================================================== */
.dxr-table{
  background:var(--dxr-surface);
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-panel);
  overflow:auto;                          /* the scroll wrapper */
  -webkit-overflow-scrolling:touch;
}
.dxr-table__row{
  display:grid;
  grid-template-columns:var(--dxr-table-cols);
  min-width:var(--dxr-table-min);          /* forces the scroll instead of crushing */
  align-items:center;
}
.dxr-table__row--head{
  padding:var(--dxr-pad-th);               /* 14px 26px */
  background:var(--dxr-surface-raised);
  /* head rule is --dxr-border, body rules are --dxr-border-inner */
  border-bottom:1px solid var(--dxr-border);
}
.dxr-table__row--body{
  padding:var(--dxr-pad-td);               /* 24px 26px */
  border-bottom:1px solid var(--dxr-border-inner);
}
.dxr-table__row--body:last-child{border-bottom:0}
.dxr-table__row--compact{padding:var(--dxr-pad-td-sm)}         /* 17px 26px */
.dxr-table__row--tinted {background:var(--dxr-surface-raised)} /* last / summary row */

.dxr-table__th{font:500 10px/1 var(--dxr-font-mono);letter-spacing:.12em;
  text-transform:uppercase;color:var(--dxr-text-subtle)}
.dxr-table__cell {margin:0;font:400 14px/1.65 var(--dxr-font-sans);color:var(--dxr-text-muted)}
.dxr-table__label{font:400 14px/1.5 var(--dxr-font-sans);color:var(--dxr-body)}
.dxr-table__key  {font:500 13px/1 var(--dxr-font-mono)}                              /* colour per row */
.dxr-table__mark {font:500 13px/1 var(--dxr-font-mono);color:var(--dxr-green-deep)}   /* checkmark */
.dxr-table__dash {font:400 13px/1 var(--dxr-font-mono);color:var(--dxr-text-disabled)}/* em dash */
.dxr-table__beta {font:400 11px/1.4 var(--dxr-font-mono);color:var(--dxr-amber-deep)} /* "beta" */

/* the three shipped configurations, verbatim */
.dxr-table--deploy    {--dxr-table-cols:230px 180px 1fr;      --dxr-table-min:680px}
.dxr-table--analysis  {--dxr-table-cols:240px 210px 1fr;      --dxr-table-min:700px}
.dxr-table--comparison{--dxr-table-cols:1.6fr repeat(4,1fr);  --dxr-table-min:720px}

/* ==========================================================================
   §15 CTA band — copy left, button cluster right, wraps on narrow
   ========================================================================== */
.dxr-ctaband__inner{
  max-width:var(--dxr-container);margin:0 auto;padding:0 var(--dxr-gutter);
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--dxr-gap-col);flex-wrap:wrap;
}
.dxr-ctaband__copy{max-width:min(100%,620px)}
.dxr-ctaband__title{margin:0 0 12px}
.dxr-ctaband__text {margin:0}
.dxr-section--dark .dxr-ctaband__text{color:var(--dxr-on-dark-secondary)}

/* ==========================================================================
   ELEMENTOR CONTENT SLOT
   Companion to dxr_content_slot() in inc/dxr-components.php. The prose layer
   caps text blocks at a 720px reading measure, which is right for an article
   column and wrong for a designed section — it would narrow every paragraph
   inside an Elementor column. Released here. Elementor sections carry their own
   container and boxing, so the slot adds none of its own.
   ========================================================================== */
/* Released for the rare 'prose' => true case. Three classes, not one: .dxr-prose
   declares the 720px measure at (0,1,0) on this SAME element, so a single class
   ties it and loses on load order -- which is why the cap was still applying. */
.dxr-content-slot.dxr-content-slot.dxr-content-slot{--dxr-prose-measure:none}

/* ------------------------------------------------------------------------
   RESET RESTORE. §1 is :where()-wrapped at (0,1,0), so Elementor's kit
   (0,1,1) and per-widget rules (0,3,0)+ beat it unaided -- that is what lets
   injected content keep its own design. But three §1 rules have no Elementor
   counterpart and would simply take effect, and body.dxr-body-root is not
   scoped to .dxr-shell--dxr at all, so it re-bases inherited type document-wide.
   Both are handed back here, at (0,1,0) in the same file and later in source
   order: it ties the reset and wins, and still loses to the kit.

   EVERY value below is production's, measured on the live page, not chosen.
   ------------------------------------------------------------------------ */
/* hello-elementor style.css:42-48 -- verbatim, because 'revert' would jump past
   that author sheet to the UA default (Times). Elementor's kit styles headings
   and most text, but NOT list items: production renders those in this stack at
   #333, and without this they came out Plus Jakarta Sans on the redesign ink. */
.dxr-content-slot{
  font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  color:#333333;
  text-wrap:wrap;
}
/* :where(p){margin:0}. hello-elementor style.css:91-93; production measures
   0 / 14.4px, which is this. */
.dxr-content-slot :where(p){margin-block:0 .9rem}
/* :where(ul,ol){padding:0;list-style:none}. 'revert' IS right here and only
   here: production's disc/decimal + 40px indent are the UA's own values, so
   reverting past every author sheet lands exactly on them. Margins are left
   alone deliberately -- production's differ per element (ul .9rem, ol none),
   which means Elementor supplies them.
   Icon lists are Elementor's own furniture and must stay unmarked. */
.dxr-content-slot :where(ul,ol):not(.elementor-icon-list-items){
  padding-inline-start:revert;
  list-style:revert;
}
/* :where(a) colour. The child theme's global (style.css:3850) restated at the
   reset's tier, so an unstyled in-content link reads as production's link and
   not as redesign blue. Buttons are excluded for the reason given in §1.
   NOT restored: h1-h4 margins. The reset zeroes them and production measures
   0/0 too, because Elementor's widget CSS sets margin:0 on its headings --
   handing back hello-elementor's .5rem/1rem would have INVENTED space that
   production does not have. */
.dxr-content-slot :where(a:not(.elementor-button):not(.elementor-button-link)){color:#23A7DE}
/* NO VERTICAL PADDING FROM US, at the slot or either seam.
   Elementor content arrives with its own section rhythm -- measured on the live
   page, its first inner container is padded 125px 0 160px -- so every pixel this
   wrapper or its neighbours contribute lands ON TOP of that. It stacked to 297px
   above the first heading (84 from the preceding section + 88 here + 125 from
   Elementor) and 298px below (134 + 88 + 76), which read as a hole in the page.
   Zeroing all three of our contributions leaves Elementor's own 125/134, which
   is what production shows. The seam rule stays: it now sits directly above that
   125px, which is how a divider should behave. */
.dxr-content-slot-section{padding-block:0}
.dxr-section:has(+ .dxr-content-slot-section){padding-bottom:0}
.dxr-content-slot-section + .dxr-section{padding-top:0}

/* Elementor's own section padding is the rhythm inside the slot; a section
   surface passed to the helper would otherwise double it at the seams. */
.dxr-content-slot > .elementor:first-child > .elementor-section:first-child{margin-top:0}
.dxr-content-slot > .elementor:last-child > .elementor-section:last-child{margin-bottom:0}
