/* ==========================================================================
   3DOptix redesign — single VIDEO page-unique CSS.

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

   Sibling of assets/css/dxr-page-article.css, which is the Academy article's
   page CSS and the transcription of the approved handoff (prototype '3DOptix
   Website.dc.html' lines 744-819 + README §6). This screen is not in the
   prototype; it reuses that layout deliberately, so the two singulars read as
   one system. Where a value here is a prototype value it is inherited through
   that file's geometry, restated in §0 with the same provenance comment rather
   than cross-referenced, so this file stays independently readable.

   THIS FILE IS THE PAGE'S CHROME, NOT THE PLAYER.
   The body — an Elementor document whose only widget is a video widget — is
   styled by Elementor's own widget-video.min.css (which supplies
   `.elementor-widget-video .elementor-wrapper{aspect-ratio:var(--video-aspect-ratio)}`
   and `… .elementor-wrapper iframe{width:100%;height:100%}`) and by
   assets/css/dxr-prose.css (which supplies `iframe{max-width:100%}` and,
   deliberately, nothing else). THERE IS NOT ONE SELECTOR IN THIS FILE THAT
   REACHES INSIDE .dxr-prose, and there must never be. Two reasons, both fatal:

     1. dxr-prose.css is an arithmetic-checked specificity ladder (see its
        header). A stray rule from a page stylesheet landing in the middle of it
        is how legacy content gets silently unstyled.
     2. dxr-prose.css §11 states the specific trap on this screen: an
        `aspect-ratio` or a `height` declared on the video widget's iframe
        COLLAPSES THE PLAYER TO ZERO, because Elementor injects that iframe at
        runtime, absolutely sized inside a wrapper that already owns the ratio.
        The player is already responsive. Do not "fix" it here.

   If something about the body needs changing, change dxr-prose.css.

   RULES FOR THIS FILE:
   - SCOPE. Every selector is scoped to .dxr-page-video, the wrapper the template
     emits inside the shell's <main>. No bare element selectors, ever.
   - No new colour. Every value resolves from the token layer; the only raw
     numbers are geometry, and each is named in §0 rather than repeated below.
   - The spectrum gradient appears ZERO times in this file. The header eyebrow
     uses --dxr-spectrum-eyebrow through the shared .dxr-eyebrow-bar component;
     dxr-tokens.css §1.4 documents that five-stop gradient as a different asset
     from the seven-stop --dxr-spectrum and explicitly outside the two-placement
     rule. Site-wide budget unchanged: nav 2px + footer 3px.
   - ONE media query, at --dxr-nav-break (900px), the site's only named
     breakpoint. See §2.

   Contents
     §0  Page-local custom properties
     §1  Header band
     §2  Two-column body — the grid and its collapse
     §3  Player column: the player slot, the missing-embed placeholder,
         pagination, end-of-page bar, legacy tag list
     §4  Sidebar shell
     §5  Sidebar (a) — more videos
     §6  Sidebar (b) — dark CTA
   ========================================================================== */


/* ==========================================================================
   §0 Page-local custom properties

   Declared once, used by name below, so a design change is a one-line edit and
   every literal in this file is traceable.
   ========================================================================== */
.dxr-page-video{
  --dxr-vid-head-top:34px;                    /* proto 747, via dxr-page-article */
  --dxr-vid-head-bot:clamp(27px,3.3vw,44px);  /* proto 747 */
  --dxr-vid-body-top:clamp(35px,4.1vw,56px);  /* proto 765 */
  --dxr-vid-aside-w:348px;                    /* README §6 — see §2 */
  --dxr-vid-sticky-top:104px;                 /* proto 795 == --dxr-nav-h (76) + 28 */
  --dxr-vid-card-pad:22px;                    /* proto 804 / 813 */
  --dxr-vid-radius-chip:6px;                  /* legacy tag pills, §3 */
  --dxr-vid-player-ratio:16/9;                /* the missing-embed box, §3 */
}


/* ==========================================================================
   §1 Header band

   .dxr-section supplies the hairline (--rule-b) and the container; the rhythm
   is the article's own, much tighter than --dxr-section-y, so it is overridden
   here. The two-class selector out-ranks .dxr-section (0,1,0) without
   !important.

   A video header is a label for what follows, not a hero: no blueprint, no
   glow, no lede. Deliberately NOT .dxr-hero.
   ========================================================================== */
.dxr-page-video .dxr-vid__head{
  background:var(--dxr-surface);
  padding:var(--dxr-vid-head-top) 0 var(--dxr-vid-head-bot);
}

/* --- breadcrumb ----------------------------------------------------------
   .dxr-crumbs is still NOT in dxr-components.css — nothing in the shared layer
   is a breadcrumb, and the mono eyebrow roles are all uppercase/tracked, which
   a path is not. dxr-page-article.css carries its own scoped copy of these five
   rules and says so explicitly: "scoped to this page until then, so two
   parallel page stylesheets can each carry their own copy without colliding."
   This is that second copy, identical value for value. THE PATTERN IS NOW
   CONFIRMED ON TWO SCREENS — promoting .dxr-crumbs into dxr-components.css and
   deleting both copies is a safe, mechanical follow-up, and is flagged for the
   owner of the shared layer rather than done from a page stylesheet.

   flex-wrap is load-bearing, not defensive: the third crumb is the post title,
   and video titles run long ("3DOptix Live - Creating optomechanical and
   optical objects using CAD files" is 71 characters). It wraps rather than
   truncates — a truncated breadcrumb tells the reader less than a two-line one.
   ------------------------------------------------------------------------ */
.dxr-page-video .dxr-crumbs{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:10px;
  margin:0 0 28px;                              /* proto 748 */
  font:400 12px/1.4 var(--dxr-font-mono);       /* mono: a path is measured content */
  color:var(--dxr-text-subtle);
}
.dxr-page-video .dxr-crumbs a{
  color:var(--dxr-text-subtle);
  transition:color .15s ease;
}
.dxr-page-video .dxr-crumbs a:hover{color:var(--dxr-primary)}
.dxr-page-video .dxr-crumbs__sep{color:var(--dxr-text-disabled)}
.dxr-page-video .dxr-crumbs__current{color:var(--dxr-ink)}

/* --- title row -----------------------------------------------------------
   align-items:flex-end pins the CTA's baseline to the title's (proto 753).
   flex-wrap drops the button under the title before it can squeeze the
   heading — the button is white-space:nowrap, so without the wrap a long title
   and a long label fight for the same row. Video titles make that likely, not
   theoretical.
   ------------------------------------------------------------------------ */
.dxr-page-video .dxr-vid__head-row{
  display:flex;
  align-items:flex-end;
  justify-content:space-between;
  gap:40px;
  flex-wrap:wrap;
}
.dxr-page-video .dxr-vid__head-copy{
  min-width:0;   /* lets a long unbroken title shrink instead of overflowing */
}

/* Local deltas from the shared .dxr-eyebrow-bar (26px margin, 44px rule,
   .13em). Proto 751-753 gives 16px, 40px, .12em. Everything else inherited. */
.dxr-page-video .dxr-vid__eyebrow{margin-bottom:16px}
.dxr-page-video .dxr-vid__eyebrow .dxr-eyebrow-bar__rule{width:40px}
.dxr-page-video .dxr-vid__eyebrow .dxr-eyebrow-bar__label{letter-spacing:.12em}

/* The type role is .dxr-h1--compact (shared). Only the box is local — and the
   element may be an <h1> or a <p> depending on whether the stored content
   already carries an <h1>, which is why margin is declared here rather than
   relied on from the reset.

   overflow-wrap is not decorative here either: every tutorial title opens with
   an unbroken ordinal token ("01:", "02:") and the webinar titles carry long
   hyphenated runs. */
.dxr-page-video .dxr-vid__title{
  margin:0;
  overflow-wrap:break-word;
}

/* The CTA never shrinks below its label — it wraps to its own row instead. */
.dxr-page-video .dxr-vid__cta{flex-shrink:0}


/* ==========================================================================
   §2 Two-column body

   grid-template-columns: minmax(0,1fr) body + a 348px sidebar at a 72px gap,
   which is README §6's statement of the article layout. (Proto 767's
   repeat(auto-fit,minmax(min(100%,340px),1fr)) resolves to two EQUAL columns —
   a ~574px sidebar at container width — and is a prototyping shortcut that
   collapses without a media query. README wins, exactly as it does in
   dxr-page-article.css §2.)

   minmax(0,1fr) ON THE BODY COLUMN IS LOAD-BEARING. A grid track's default
   min-width is auto, i.e. its content's min-content size. The player is an
   Elementor widget at 100% width with a declared aspect ratio, and a video post
   is editable content that could gain an image or a table tomorrow. Under `1fr`
   alone any of those would widen the track and push the sidebar off the
   container. This is the outer guarantee that makes dxr-prose §10/§12 work.

   At container width the body track is ~880px, so a 16:9 player renders at
   880x495 — which is the height this grid and the sticky sidebar in §4 are
   sized around.

   align-items:start is what makes the sidebar's position:sticky function at
   all — the default `stretch` gives the aside the full row height, leaving it
   nothing to slide within.
   ========================================================================== */
.dxr-page-video .dxr-vid__body{
  background:var(--dxr-surface);
  padding:var(--dxr-vid-body-top) 0 var(--dxr-section-y);   /* proto 765 */
}
.dxr-page-video .dxr-vid__grid{
  display:grid;
  grid-template-columns:minmax(0,1fr) var(--dxr-vid-aside-w);
  gap:var(--dxr-gap-col-xwide);                             /* 72px */
  align-items:start;
}
.dxr-page-video .dxr-vid__main{min-width:0}

/* --- the collapse --------------------------------------------------------
   ONE breakpoint, at the site's only named one.

   Why 900px is the right number and not a guess: at a 901px viewport the gutter
   is 4vw ~= 36px, leaving 829px of container. Minus the 72px gap and the 348px
   sidebar the body column still holds 409px — a 409x230 player, still
   comfortably watchable. Below 900px the sidebar stacks under the player,
   un-sticks, and reads as an appendix; the dark CTA card (§6) becomes the
   end-of-page CTA. Do not introduce a second breakpoint.
   ------------------------------------------------------------------------ */
@media (max-width:900px){                                   /* == --dxr-nav-break */
  .dxr-page-video .dxr-vid__grid{
    grid-template-columns:minmax(0,1fr);
    gap:40px;
  }
  .dxr-page-video .dxr-vid__aside{
    position:static;
    top:auto;
  }
}


/* ==========================================================================
   §3 Player column

   THE PLAYER SLOT IS A BOX, NOT A SKIN.

   .dxr-vid__player is the SAME ELEMENT as .dxr-prose — the template puts both
   classes on one div — so the two rules below are the only things this file is
   allowed to say about it, and they say nothing about its contents:

     min-width:0  the flex/grid escape hatch. Belt and braces alongside
                  .dxr-vid__main above; a block child cannot widen a
                  minmax(0,1fr) track, but a future author who turns this into a
                  flex context would find out the hard way.
     margin:0     the wrapper contributes no rhythm of its own. Elementor's
                  per-widget container padding IS the body's vertical rhythm and
                  is deliberately not overridden anywhere in the redesign
                  (dxr-prose.css §2); adding a margin here would stack on top of
                  it.

   No border, no radius, no background, no padding. The player draws its own
   black box (Elementor's `.elementor-wrapper iframe{background-color:#000}`),
   and framing it from out here would either clip the YouTube chrome or, worse,
   tempt the next edit into a descendant selector. See the file header.
   ========================================================================== */
.dxr-page-video .dxr-vid__player{
  min-width:0;
  margin:0;
}

/* --- the missing-embed placeholder ---------------------------------------
   Rendered INSTEAD of .dxr-prose when the body carries neither a playable
   element nor a word of text (see the guard in single-video.php). It takes the
   player's own 16:9 box so the column does not change shape between the two
   cases, and so the sidebar's sticky travel is the same either way.

   .dxr-hatch is a flex container with align-items:flex-end, so the caption
   sits on the bottom edge of the box exactly as it does in every other hatch
   on the site. NOTHING sets a background here: dxr_hatch() puts .dxr-hatch on
   this same div, and a flat background would paint over the diagonal gradient
   and make the placeholder invisible — the same trap dxr-page-videos.css §4
   documents for its card media slot.
   ------------------------------------------------------------------------ */
.dxr-page-video .dxr-vid__missing{
  aspect-ratio:var(--dxr-vid-player-ratio);
  border-radius:var(--dxr-radius-md);
  overflow:hidden;
}

/* wp_link_pages() for split content. Absent from the design and inert today
   (nothing on this CPT paginates), but the parent template called it and
   dropping the styling would leave bare, reset-stripped number links if an
   editor ever splits a post. Mono, because page numbers are measured content. */
.dxr-page-video .dxr-vid__pages{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:8px;
  margin-top:34px;
  font:500 13px/1 var(--dxr-font-mono);
  color:var(--dxr-body);
}
.dxr-page-video .dxr-vid__pages a,
.dxr-page-video .dxr-vid__pages > span{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  /* 44px is the touch floor, matching the pagers on the four list pages.
     .dxr-vid__pages wraps, so the taller cells cannot widen the row. */
  min-width:44px;
  height:44px;
  padding:0 10px;
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-md);
  background:var(--dxr-surface);
  color:inherit;
  transition:border-color .15s ease,color .15s ease;
}
.dxr-page-video .dxr-vid__pages a:hover{
  border-color:var(--dxr-primary);
  color:var(--dxr-primary);
}
.dxr-page-video .dxr-vid__pages > span{
  background:var(--dxr-ink);
  border-color:var(--dxr-ink);
  color:var(--dxr-on-dark);
}

/* --- end-of-page bar ----------------------------------------------------
   Sits INSIDE <article>, after the player and outside .dxr-prose. It is the
   whole of the page's close: there is no full-width band on this screen. The
   header CTA and the dark sidebar card carry the conversion load.
   ------------------------------------------------------------------------ */
.dxr-page-video .dxr-vid__foot{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:24px;
  flex-wrap:wrap;
  margin-top:34px;
  padding-top:26px;
  border-top:1px solid var(--dxr-border);
}
.dxr-page-video .dxr-vid__back{
  display:inline-flex;
  align-items:center;
  gap:8px;
  font:600 14px/1 var(--dxr-font-sans);
  color:var(--dxr-primary);
  transition:color .15s ease;
}
.dxr-page-video .dxr-vid__back:hover{color:var(--dxr-primary-hover)}
/* The arrow is typographic — there is no icon set on this site. */
.dxr-page-video .dxr-vid__meta{
  font:400 12px/1.4 var(--dxr-font-mono);       /* a date is measured content */
  color:var(--dxr-text-subtle);
}

/* Legacy tag list. Inert today — the CPT does not register post_tag — but the
   parent template emitted it, so it keeps a styled home rather than falling
   back to reset-stripped links if tags are ever switched on. */
.dxr-page-video .dxr-vid__tags{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:8px;
  margin-top:26px;
  font:400 12px/1 var(--dxr-font-mono);
  color:var(--dxr-text-subtle);
}
.dxr-page-video .dxr-vid__tags a{
  padding:7px 12px;
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-vid-radius-chip);
  font:600 12px/1 var(--dxr-font-sans);
  color:var(--dxr-body);
  transition:border-color .15s ease,color .15s ease;
}
.dxr-page-video .dxr-vid__tags a:hover{
  border-color:var(--dxr-primary);
  color:var(--dxr-primary);
}


/* ==========================================================================
   §4 Sidebar shell

   position:sticky, not fixed: the aside must stop scrolling when the player
   column runs out, which fixed cannot do. It needs three things and all three
   are here or in §2 — a sticky element, an `align-items:start` grid parent
   (§2), and no overflow:hidden anywhere on the ancestor chain (which is why
   that section is a plain .dxr-section and NOT .dxr-section--overlay).

   A sidebar taller than the viewport simply scrolls with the page; there is no
   inner scroller, because a nested scroll region inside a sticky panel is a
   trap on a trackpad. Two cards total ~380px, well under any laptop viewport.

   THE STICKY IS NEARLY INERT ON THIS SCREEN AND THAT IS FINE. A single video
   page is short — a 495px player plus a footer bar — so on a desktop viewport
   there is often nothing to scroll and the sidebar never travels. It is kept
   because the column becomes tall the moment a video gains a transcript or a
   description, and because behaving identically to the Academy article is the
   point of reusing the layout. It costs nothing when it does not fire.
   ========================================================================== */
.dxr-page-video .dxr-vid__aside{
  position:sticky;
  top:var(--dxr-vid-sticky-top);
  display:flex;
  flex-direction:column;
  gap:var(--dxr-gap-panels);                    /* 20px, proto 795 */
  min-width:0;
}


/* ==========================================================================
   §5 Sidebar (a) — more videos

   The label reuses the shared .dxr-label--strip role (500 10px/1 mono, .12em,
   uppercase, --dxr-text-subtle); only its margin is local. It prints the
   category name when there is one — "VIDEO TUTORIALS", "3DOPTIX LIVE" — which
   is real taxonomy content and more informative than a generic heading, and
   falls back to a generic one when the post is uncategorised.

   The rows are real posts, ordered title ASC so a tutorial series reads in
   watch order (see the query in single-video.php), and the card is suppressed
   entirely when there are none — so this never renders as an empty frame or
   with invented titles.
   ========================================================================== */
.dxr-page-video .dxr-vid-related{
  padding:var(--dxr-vid-card-pad);
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-panel);
  background:var(--dxr-surface);
}
.dxr-page-video .dxr-vid-related__label{margin-bottom:16px}
.dxr-page-video .dxr-vid-related__list{
  display:flex;
  flex-direction:column;
  gap:14px;
}
.dxr-page-video .dxr-vid-related__link{
  padding-bottom:14px;
  border-bottom:1px solid var(--dxr-border-inner);
  font:600 14px/1.45 var(--dxr-font-sans);
  color:var(--dxr-ink);
  /* Video titles are the longest link text in the sidebar; without this a
     71-character title with no space after a hyphen can push the 348px column. */
  overflow-wrap:break-word;
  transition:color .15s ease;
}
.dxr-page-video .dxr-vid-related__link:hover{color:var(--dxr-primary)}

/* Last row loses the rule and the padding — the "All videos" link below closes
   the card, and a hairline immediately above it would read as a second frame.
   The selector is anchored on .dxr-vid-related__list, not on the card: the CTA
   link is a sibling of the list, not a member of it, and anchoring on the card
   would make "last row" mean the CTA. */
.dxr-page-video .dxr-vid-related__list .dxr-vid-related__link:last-child{
  padding-bottom:0;
  border-bottom:0;
}

/* The shared .dxr-cta-link is 14px; the sidebar scale is 13px, matching the
   Academy sidebar cards. Size and box only — colour, hover and the typographic
   arrow all stay with the component. */
.dxr-page-video .dxr-vid-related__all{
  margin-top:18px;
  font-size:13px;
}


/* ==========================================================================
   §6 Sidebar (b) — dark CTA card

   .dxr-on-dark is on the element in the template, which is what gives the
   shared .dxr-cta-link its --dxr-cyan-light colour and white hover — the
   component already does it, so nothing needs restating here.

   THE BORDER IS THE LIGHT HAIRLINE ON A DARK FILL, transcribed from proto 813
   and carried over from dxr-page-article.css §7 unchanged, so the two singulars
   cannot drift. dxr-page-article.css flags it for sign-off: the reading that
   makes it deliberate is that all sidebar cards then share one frame and the
   stack reads as a set, one of which happens to be filled dark; the reading
   that makes it a copy-paste from the light cards above it is equally
   plausible. IF THAT SIGN-OFF LANDS, IT IS A TWO-FILE EDIT — this rule and
   dxr-page-article.css §7 — to:
       border-color:var(--dxr-border-on-dark);
   Flagged in both places rather than silently changed in one, which would be
   the worst outcome: two cards that are supposed to be identical, and are not.
   ========================================================================== */
.dxr-page-video .dxr-vid-aside-cta{
  position:relative;
  overflow:hidden;
  padding:var(--dxr-vid-card-pad);
  border:1px solid var(--dxr-border);            /* see the note above */
  border-radius:var(--dxr-radius-panel);
  background:var(--dxr-ink-deep);
}
.dxr-page-video .dxr-vid-aside-cta__title{
  margin-bottom:8px;
  font:700 15px/1.4 var(--dxr-font-sans);
  color:var(--dxr-on-dark);
}
.dxr-page-video .dxr-vid-aside-cta__text{
  margin:0 0 16px;
  font:400 13px/1.6 var(--dxr-font-sans);
  color:var(--dxr-on-dark-muted);                /* rgba(255,255,255,0.60), proto 815 */
}
/* Proto 816 is 13px; the shared .dxr-cta-link is 14px. Size only. */
.dxr-page-video .dxr-vid-aside-cta .dxr-cta-link{font-size:13px}
