/* ==========================================================================
   3DOptix redesign — Home page layer.

   Depends on: dxr-tokens.css -> dxr-components.css -> dxr-shell.css
   Enqueued CONDITIONALLY from page-templates/dxr-home.php (handle
   'dxr-page-home', dependency 'dxr-shell'), so it ships on this page only and
   can never reorder the shared layer.

   RULES FOR THIS FILE:
   - EVERY selector is scoped by .dxr-page--home (the body class set through the
     dxr_page_accent filter), so nothing here can reach another page.
   - No new colour or size literals beyond geometry transcribed verbatim from
     the approved prototype (the 340px media crop, the 66px play disc).

   Contents
     §1  Hero spacers
     §2  Hero media block
     §3  Stat band
     §4  Products
     §5  Dark technology band
     §6  Testimonials
   ========================================================================== */

/* ==========================================================================
   §1 Hero — explicit spacers the shared layer has no token class for
   ========================================================================== */
.dxr-page--home .dxr-home-hero__title{margin:0 0 24px}
.dxr-page--home .dxr-home-hero__actions{margin-top:34px}
.dxr-page--home .dxr-home-hero__note{margin:18px 0 0}

/* ==========================================================================
   §2 Hero media block — a cross-fading slideshow of real captures

   THE CONTAINER OWNS THE HEIGHT, not the image. Every frame is stacked and
   absolutely positioned so a cross-fade cannot reflow the panel, and an
   absolute box has no intrinsic contribution to make — something has to state
   the height, and the container is the only element that is always present
   whichever branch renders (slideshow, or a supplied video loop).

   The crop height is FLUID, not the flat 340px the prototype states, because
   340px is only the right crop at the width the prototype was drawn at. The
   widest frame is 2.19:1. object-fit:cover scales to fill the box HEIGHT, so a
   fixed 340px always renders 340 x 2.19 = 745px of image regardless of how
   narrow the box gets. At a 320px viewport the box is 278px wide, so 278/745 =
   37% of the screenshot was visible: a tall sliver of the left third of a wide
   UI shot. 50vw holds the desktop framing all the way down; the 340px ceiling
   reproduces the prototype exactly from a 680px viewport up, where 50vw reaches
   it. The 190px floor is the height the caption strip needs to sit clear of the
   frame's own content.
   ========================================================================== */
.dxr-page--home .dxr-home-media{
  --dxr-slide-fade:420ms;   /* == FADE_MS in dxr-home-hero.js — change both */
  position:relative;
  height:clamp(190px,50vw,340px);
  background:var(--dxr-ink-deep);
}
/* Frames stack. Only .is-on is opaque; the rest stay painted but transparent so
   the browser has already decoded them by the time they are needed. */
.dxr-page--home .dxr-home-media__frame{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:left top;
  opacity:0;
}
.dxr-page--home .dxr-home-media__frame.is-on{opacity:1}
/* Transition only once the script is driving, so the first paint is not a
   fade-in from nothing. */
.dxr-page--home .dxr-home-media.is-live .dxr-home-media__frame{
  transition:opacity var(--dxr-slide-fade) ease;
}
@media (prefers-reduced-motion:reduce){
  .dxr-page--home .dxr-home-media.is-live .dxr-home-media__frame{transition:none}
}
.dxr-page--home .dxr-home-media__video{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:left top;
}
.dxr-page--home .dxr-home-media__scrim{position:absolute;inset:0;background:var(--dxr-scrim-image)}
/* `right` closes the box. With only `left` set, the strip is shrink-to-fit
   against the containing block, and its min-content is the caption's longest
   unbreakable run plus the three 18px dashes (flex-shrink:0) — 291px inside a
   278px media block at a 320px viewport, so the dashes ran 17px past the right
   edge. .dxr-panel and .dxr-hero both clip it, so it was never a page overflow,
   just a cut-off strip. Bounding it keeps the dashes inside the media block. */
.dxr-page--home .dxr-home-media__foot{
  position:absolute;left:18px;right:18px;bottom:16px;
  display:flex;align-items:center;gap:14px;
}
/* ONE LINE AT EVERY WIDTH. Bounding the strip above stopped the dashes
   overflowing but let the caption wrap instead, and this caption is a long
   unbroken asset path: at a 365px viewport it took three lines and grew a 14px
   strip into a 42px block that climbed toward the play disc, leaving 4px
   between them. A single-line ribbon over an image is the design's own
   treatment, so the caption truncates rather than wraps.
   min-width:0 is the load-bearing one — as a flex item its automatic minimum
   size is its min-content width, so without it the item refuses to shrink and
   text-overflow never engages. The dashes keep flex-shrink:0 below and stay
   pinned right. */
.dxr-page--home .dxr-home-media__caption{
  font:400 10px/1.4 var(--dxr-font-mono);letter-spacing:.08em;text-transform:uppercase;
  color:var(--dxr-on-dark-image);
  min-width:0;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.dxr-page--home .dxr-home-media__dots{display:flex;gap:6px;flex-shrink:0}
/* These were decorative spans; they are <button>s now, so the UA's border,
   padding and background have to be cleared before the 3px bar reads right.
   The 44px hit area comes from the pseudo-element rather than from padding,
   which would otherwise push the dashes apart. */
.dxr-page--home .dxr-home-media__dash{
  position:relative;
  width:18px;height:3px;padding:0;border:0;
  border-radius:var(--dxr-radius-rule);background:rgba(255,255,255,0.35);
  cursor:pointer;transition:background .15s ease;
}
.dxr-page--home .dxr-home-media__dash::after{content:"";position:absolute;inset:-14px -3px}
.dxr-page--home .dxr-home-media__dash:hover{background:rgba(255,255,255,0.6)}
.dxr-page--home .dxr-home-media__dash--on{background:var(--dxr-on-dark)}

/* ==========================================================================
   §3 Stat band
   The section carries no vertical rhythm of its own — the 34px cell padding IS
   the band height.
   ========================================================================== */
.dxr-page--home .dxr-home-statband{padding:0}

/* ==========================================================================
   §4 Products
   ========================================================================== */
.dxr-page--home .dxr-home-products__grid{margin-top:44px}

/* Panel 01 preview crop, verbatim from the prototype. */
.dxr-page--home .dxr-home-product--ds .dxr-panel__media img{object-position:34% 34%}
/* The OptiTwin and TrueScene panels carry the accent class as a SUBTREE, so
   cap/hover/CTA resolve from the accent channel. One value the channel cannot
   express: the TrueScene CTA is --dxr-cyan-deeper. (A second exception lived here
   for the OptiTwin panel ordinal, which no longer exists -- the panels are
   identified by their 3px cap rule alone.) */
.dxr-page--home .dxr-home-product--truescene .dxr-cta-link{color:var(--dxr-cyan-deeper)}
.dxr-page--home .dxr-home-product--truescene .dxr-cta-link:hover{color:var(--dxr-cyan-deep)}

/* ==========================================================================
   §5 Dark technology band
   ========================================================================== */
.dxr-page--home .dxr-home-tech__title{margin:0 0 20px}

/* ==========================================================================
   §6 Testimonials — one lead, two beside it, rotating

   TREATMENT COMES FROM THE SLOT, NOT THE QUOTE. Every testimonial ships the
   same <figure>; the lead slot styles its child large and rule-marked, the side
   slot styles its children small and border-marked. That is what lets
   dxr-home-quotes.js rotate by moving nodes between the two slots — a quote is
   restyled by arriving, and there is only ever one markup shape to maintain.

   400px minimum with the 64px wide gutter — between .dxr-grid--band (72px) and
   .dxr-grid--2up (420px), so it needs its own rule.

   align-items:start, not center. The side column holds two stacked quotes and is
   taller than the lead, so centring left a dead band above "TRUSTED BY
   PROFESSIONALS" and pushed the lead into the middle of the section.
   ========================================================================== */
.dxr-page--home .dxr-home-quotes{
  --dxr-quote-swap:320ms;   /* == SWAP_MS in dxr-home-quotes.js — change both */
  grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--dxr-min-band)),1fr));
  gap:var(--dxr-gap-col-wide);align-items:start;
}
/* Header row spans both tracks: eyebrow left, position control right. The
   control lived in the lead column until it collided with the attribution rule
   (see dxr-home.php); as section chrome it is unambiguous and the two columns
   below start on a shared line. */
.dxr-page--home .dxr-home-quotes__head{
  grid-column:1/-1;
  display:flex;align-items:baseline;justify-content:space-between;
  gap:24px;flex-wrap:wrap;
  margin-bottom:6px;
}
.dxr-page--home .dxr-home-quotes__eyebrow{margin-bottom:0}

/* The reserve is markup, not layout: hidden already gives display:none, but the
   grid must not reserve a track for it either. */
.dxr-page--home .dxr-home-quotes__reserve{display:none}

.dxr-page--home .dxr-home-quotes__side{display:flex;flex-direction:column;gap:34px}

/* ---- the shared figure ---------------------------------------------------- */
.dxr-page--home .dxr-home-quote{margin:0}
.dxr-page--home .dxr-home-quote__attrib{display:flex;align-items:center;gap:14px}
.dxr-page--home .dxr-home-quote__who{display:flex;flex-direction:column}
.dxr-page--home .dxr-home-quote__rule{
  width:40px;height:3px;border-radius:var(--dxr-radius-rule);
  background:var(--dxr-gradient-quote);flex-shrink:0;
}

/* ---- lead slot ------------------------------------------------------------ */
/* Reproduces the .dxr-quote role (dxr-components.css:256) and the attribution
   pair the lead column shipped before the rotation existed, so the resting frame
   is unchanged — only which quote occupies it moves. */
/* The .dxr-quote role (dxr-components.css:256) is the SHORT step. See the length
   note in dxr-home.php: the bodies run 157-565 characters, so the lead steps
   down as they get longer instead of letting a five-sentence quote run thirteen
   lines. Only the lead steps — at the side slot's 14px the spread costs a few
   lines, not a section. */
.dxr-page--home .dxr-home-quotes__slot--lead .dxr-home-quote__text{
  margin:0 0 26px;
  font:600 clamp(21px,2.5vw,27px)/1.42 var(--dxr-font-sans);
  letter-spacing:-.02em;color:var(--dxr-ink);
}
.dxr-page--home .dxr-home-quotes__slot--lead .dxr-home-quote--mid .dxr-home-quote__text{
  font-size:clamp(18px,2vw,22px);line-height:1.48;
}
.dxr-page--home .dxr-home-quotes__slot--lead .dxr-home-quote--long .dxr-home-quote__text{
  font-size:clamp(16px,1.7vw,19px);line-height:1.55;letter-spacing:-.01em;
}
.dxr-page--home .dxr-home-quotes__slot--lead .dxr-home-quote__name{
  font:700 14px/1.3 var(--dxr-font-sans);color:var(--dxr-ink);
}
.dxr-page--home .dxr-home-quotes__slot--lead .dxr-home-quote__role{
  font:400 12px/1.4 var(--dxr-font-mono);color:var(--dxr-text-subtle);
}

/* ---- side slot ------------------------------------------------------------ */
.dxr-page--home .dxr-home-quotes__side .dxr-home-quote{
  border-left:2px solid var(--dxr-border);padding:4px 0 4px 20px;
}
/* THREE LINES, THEN AN ELLIPSIS. The bodies are real testimonials running to 565
   characters; unclamped, a side quote could be eight lines and out-weigh the
   lead it is meant to support. Clamping is safe here in a way it usually is not,
   because nothing is withheld: every quote takes its turn in the lead slot,
   where it always renders in full. The full text is in the DOM either way, so
   assistive tech and search engines read the whole thing.
   -webkit-box is load-bearing, not legacy vendor noise — line-clamp only applies
   to that box type, and it is what every current engine implements. */
.dxr-page--home .dxr-home-quotes__side .dxr-home-quote__text{
  margin:0 0 14px;font:400 14px/1.68 var(--dxr-font-sans);color:var(--dxr-body-muted);
  display:-webkit-box;
  -webkit-box-orient:vertical;
  -webkit-line-clamp:3;
  line-clamp:3;
  overflow:hidden;
}
.dxr-page--home .dxr-home-quotes__side .dxr-home-quote__name{
  font:700 13px/1.3 var(--dxr-font-sans);color:var(--dxr-ink);
}
.dxr-page--home .dxr-home-quotes__side .dxr-home-quote__role{
  font:400 11px/1.4 var(--dxr-font-mono);color:var(--dxr-text-subtle);
}
/* The gradient rule is the lead's mark. Beside it the 2px border already does
   that job, and repeating it three times would flatten the hierarchy. */
.dxr-page--home .dxr-home-quotes__side .dxr-home-quote__rule{display:none}

/* ---- the swap ------------------------------------------------------------- */
/* Only the two live slots animate. height is transitioned on the grid itself
   because the bodies differ in length by a factor of five, and an un-eased
   height change is the jolt this whole treatment exists to avoid; the script
   pins a from-height, measures the to-height, then releases back to auto. */
.dxr-page--home .dxr-home-quotes.is-live{
  transition:height var(--dxr-quote-swap) ease;
}
.dxr-page--home .dxr-home-quotes.is-live .dxr-home-quotes__slot--lead,
.dxr-page--home .dxr-home-quotes.is-live .dxr-home-quotes__side{
  transition:opacity var(--dxr-quote-swap) ease,transform var(--dxr-quote-swap) ease;
}
/* Leaving: settle upward. Entering starts just below and rises into place, so
   the pair reads as one continuous drift rather than two unrelated fades. */
.dxr-page--home .dxr-home-quotes.is-swapping .dxr-home-quotes__slot--lead,
.dxr-page--home .dxr-home-quotes.is-swapping .dxr-home-quotes__side{
  opacity:0;transform:translateY(-8px);
}
.dxr-page--home .dxr-home-quotes.is-entering .dxr-home-quotes__slot--lead,
.dxr-page--home .dxr-home-quotes.is-entering .dxr-home-quotes__side{
  opacity:0;transform:translateY(8px);transition:none;
}

/* ---- position numerals ---------------------------------------------------- */
/* Type, not geometry, so it cannot be read as a rule -- which matters here
   because 3px painted rules now identify the products. These numerals survive the
   ordinal cull because they are not decoration: they report WHICH quote of the set
   is showing, so the number is the state. Hit area comes from the pseudo-element:
   the numeral stays 11px without becoming a target only a mouse can hit. */
.dxr-page--home .dxr-home-quotes__nav{display:flex;gap:16px;align-items:baseline}
.dxr-page--home .dxr-home-quotes__num{
  position:relative;padding:0;border:0;background:none;cursor:pointer;
  font:500 11px/1 var(--dxr-font-mono);letter-spacing:.08em;
  color:var(--dxr-text-disabled);
  transition:color .15s ease;
}
.dxr-page--home .dxr-home-quotes__num::after{content:"";position:absolute;inset:-15px -6px}
.dxr-page--home .dxr-home-quotes__num:hover{color:var(--dxr-text-subtle)}
/* Weight AND colour: state never rests on colour alone (WCAG 1.4.1). */
.dxr-page--home .dxr-home-quotes__num.is-on{color:var(--dxr-ink);font-weight:700}

@media (prefers-reduced-motion:reduce){
  .dxr-page--home .dxr-home-quotes.is-live,
  .dxr-page--home .dxr-home-quotes.is-live .dxr-home-quotes__slot--lead,
  .dxr-page--home .dxr-home-quotes.is-live .dxr-home-quotes__side{transition:none}
}

/* Per-slide hero readout. One ledger per frame is rendered stacked; only the
   active one is displayed. display:none rather than a fade, because this strip
   is measured content — a value cross-fading into a different value reads as a
   glitch, whereas the frame above it is imagery and wants the fade. */
.dxr-page--home .dxr-home-readout{display:none}
.dxr-page--home .dxr-home-readout.is-on{display:block}
