/* ==========================================================================
   3DOptix redesign — THE PROSE LAYER.
   Depends on: dxr-tokens.css (values only).
   Enqueued by: dxr_enqueue_prose_assets() in functions.php — Academy articles
                only, plus anything that opts in via `dxr_needs_prose_layer`.

   WHAT THIS FILE IS FOR
   ---------------------
   The Academy article is a redesign surface for its CHROME (shell, <main>,
   breadcrumb, eyebrow bar, buttons, sidebar panels) but its BODY is the
   client's existing library: 27 articles of editor-authored, Elementor-wrapped
   HTML that must not change. Those two facts collide, because
   dxr-components.css §1 ships a reset:

       .dxr-shell--dxr :where(a){color:var(--dxr-primary);text-decoration:none}
       .dxr-shell--dxr :where(a):hover{color:var(--dxr-primary-hover)}
       .dxr-shell--dxr :where(img){max-width:100%}
       .dxr-shell--dxr :where(p){margin:0}
       .dxr-shell--dxr :where(h1,h2,h3,h4){margin:0}
       .dxr-shell--dxr :where(ul,ol){margin:0;padding:0;list-style:none}

   Applied to the_content() that strips every bullet, every list indent, every
   paragraph break and every link underline in the library — 415 <ul>, 552 <p>,
   659 <li>, 115 <a>. This file is the positive re-declaration of all of it, in
   the new type scale. It is NOT a second reset and it is not a copy of
   dxr-components.css: it only ever ADDS formatting back.

   THE SPECIFICITY MECHANISM — do not break it
   -------------------------------------------
   Every selector in this file begins with the class `.dxr-prose` REPEATED
   THREE TIMES. Read `.dxr-prose.dxr-prose.dxr-prose` as PROSE³ throughout the
   comments below. Repeating a class is the standard way to buy specificity
   without an ID and without !important, and unlike source order it cannot be
   undone by an enqueue reshuffle, by Elementor switching its CSS Print Method
   from Internal Embedding to External File, or by a plugin re-ordering the
   queue.

   The three layers this file has to out-rank, measured against the real site:

     A. the redesign reset, dxr-components.css §1
        `.dxr-shell--dxr :where(el)`                                  (0,1,0)
        :where() is specificity-zero by definition, so the whole reset scores
        one class. Beaten by construction.

     B. the legacy `.single-academy …` block, style.css L645-766 + L3449-3554
        `.single-academy p`                                           (0,1,1)
        `.single-academy p strong` / `.single-academy figure img`     (0,1,2)
        `.single-academy .elementor-element-620ccb59 h2`              (0,2,1)
        `.single-academy .elementor-1405 h4`                          (0,2,1)
        (These appear to be inert already: the CPT slug is `academy-post`, so
        body_class() emits `single-academy-post`, and a class selector matches
        whole tokens — `.single-academy` never matches `.single-academy-post`.
        They are out-specified anyway rather than relied upon to stay dead, and
        style.css is NOT edited: it is a protected file.)

     C. Elementor's per-post CSS, printed inline in `elementor-frontend-inline-css`
        ~2,800 rules per article, BEFORE any theme stylesheet:
        `.elementor-{pid} .elementor-element.elementor-element-{eid}` (0,3,0)
          — font-family/size/weight/line-height/color/text-align, on the widget
            DIV. Descendant p/li/td receive these only by INHERITANCE, and a
            direct declaration beats an inherited value at any specificity, so
            the base tier below wins these outright.
        `… .elementor-element-{eid} .elementor-heading-title`         (0,4,0)
        `… .elementor-element-{eid} .widget-image-caption`            (0,4,0)
          — these land DIRECTLY on the element. This is the real fight, and
            why headings and captions carry an extra Elementor class.
        `… > .elementor-widget-container{padding}`                    (0,4,0)
          — DELIBERATELY NOT TOUCHED. That padding is the library's existing
            vertical rhythm (25px between text blocks, 40px before figures).
            Overriding it would re-space all 27 articles. Restyle type, keep
            Elementor's spacing.

   THE RESULTING LADDER — every number below is arithmetic, not a guess:

     tier          selector shape                                specificity
     ------------  --------------------------------------------  -----------
     BASE          PROSE³ el                                      (0,3,1)
                   beats A (0,1,0), all of B (max (0,2,1)),
                   and C's inherited (0,3,0)
     GUARDED       PROSE³ el:not(.cls)                            (0,4,1)
     ELEMENTOR     PROSE³ .elementor-widget-x el.elementor-y      (0,5,1)
                   beats C's direct (0,4,0) AND its (0,5,0) misc tier
     EDGE-RESET    PROSE³ .elementor-widget > .container > :first-child
                                                                  (0,6,0)
                   highest tier in the file; must out-rank every margin
                   rule above it

   Reminder on the columns: a pseudo-class (:not, :has, :first-child, :hover)
   counts in the CLASS column; :is()/:not()/:has() themselves contribute the
   specificity of their most specific argument; :where() contributes zero.

   NEVER write `.dxr-prose :where(el)` — that ties the reset at (0,1,0) and the
   outcome becomes an ordering accident. Never widen a rule here to a bare
   element selector: everything in this file is scoped under .dxr-prose so that
   nothing can leak onto the redesign templates, where the reset is correct.

   HANDS OFF — these already work and break if touched
   ---------------------------------------------------
     .katex, .katex-html, .katex-mathml, .katex .base
         395 equations across the library, rendered client-side by the katex
         plugin. It requires its own KaTeX_Main/KaTeX_Math faces and hides the
         MathML twin with position/clip. There is not one rule in this file
         that names any of them. The equation CARRIER is styled (see §5); the
         KaTeX output inside it is not.
     .elementor-wrapper, .elementor-video
         The video widget injects its iframe at runtime; the aspect ratio comes
         from Elementor's own frontend CSS. Setting height/aspect-ratio here
         collapses the player. See §11.
     .notice / img.alignleft
         77 callouts whose float comes from the parent theme and whose colour
         comes from per-widget Elementor Custom CSS at (0,8,0). No `display` or
         `float` declaration is made on img anywhere in this file.
     .elementor-accordion, .elementor-tab-title, .elementor-tab-content,
     .elementor-button
         Widget chrome, not prose.

   KNOWN RESIDUE THIS FILE CANNOT REACH (content-level, out of scope)
   ------------------------------------------------------------------
   Inline styles baked into post_content beat any stylesheet: 22 hard-coded
   `color:#3366ff`, 24 `color:#000000`, 19 `font-family:Tahoma/Calibri`,
   23 `<table style="width:700px">`, 2 `<iframe style="height:500px">`. Those
   need a content edit, which the client constraint forbids. Flagged, not
   fought.

   One more layer outranks everything here and is worth knowing about:
   `<style id="wp-custom-css">` prints on wp_head:101, i.e. after every
   enqueued stylesheet, and lives in the database. It currently contains no
   bare element selectors and nothing matching Academy prose, so it does not
   reach this file's territory — but it is the one thing load order cannot beat.

   Contents
     §1  Container, measure, defaults
     §2  Edge margins inside Elementor widgets
     §3  Headings — plain HTML
     §4  Headings — Elementor heading widgets
     §5  Equation carriers (bare headings) — NOT headings
     §6  Paragraphs and naked text
     §7  Links
     §8  Lists
     §9  Inline emphasis
     §10 Images, figures, captions
     §11 Embedded media
     §12 Tables
     §13 Blockquote, rules, code, definition lists
   ========================================================================== */


/* ==========================================================================
   §1 Container, measure, defaults

   The container carries the type scale so that naked text nodes — 242 widgets
   in the library have no block wrapper at all — inherit something correct even
   before §6 reaches them.

   MEASURE. --dxr-prose-measure caps the reading line. In the article's
   two-column grid (minmax(0,1fr) body + 348px sidebar + 72px gap) the body
   track is ~880px at container width, which is ~95 characters at 17px — too
   wide to track comfortably. 720px lands at roughly 72-78 characters, inside
   the 60-80 band. It is applied to the TEXT blocks only (§3, §4, §6, §8, §13),
   never to the container itself, so figures, tables and embeds still use the
   full column. A template can opt out with `--dxr-prose-measure:none`.

   overflow-wrap on the container is load-bearing, not cosmetic: the body
   column is minmax(0,1fr) and a long unbroken URL in legacy copy would
   otherwise push the grid track past the sidebar.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose{
  --dxr-prose-measure:min(100%,720px);
  --dxr-prose-flow:22px;          /* the standard block-to-block gap */
  --dxr-prose-flow-lg:34px;       /* around figures, embeds, equations */

  font:400 17px/1.8 var(--dxr-font-sans);   /* == the .dxr-body-article role */
  color:var(--dxr-body);                    /* #334155 */
  overflow-wrap:break-word;
}

/* First and last child of the whole body never add outer space — the section
   rhythm above and the end-of-article footer below own that. */
.dxr-prose.dxr-prose.dxr-prose > :first-child{margin-top:0}
.dxr-prose.dxr-prose.dxr-prose > :last-child{margin-bottom:0}


/* ==========================================================================
   §2 Edge margins inside Elementor widgets                          (0,6,0)

   Elementor gives every widget container its own padding — 615 rules, and that
   padding IS the library's existing rhythm. The margins this file adds would
   stack on top of it, inflating every gap in all 27 articles. Collapsing the
   first/last child's outer margin inside each widget keeps this file's
   inter-block rhythm (which only ever applies WITHIN a multi-block widget)
   while leaving Elementor's between-widget rhythm exactly as authored.

   ARITHMETIC. 3 (.dxr-prose ×3) + 1 (.elementor-widget)
             + 1 (.elementor-widget-container) + 1 (:first-child) = (0,6,0),
   which must out-rank the (0,5,1) heading tier in §4 — and does, because the
   class column is compared before the element column: 6 > 5 decides it.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose .elementor-widget > .elementor-widget-container > :first-child{margin-top:0}
.dxr-prose.dxr-prose.dxr-prose .elementor-widget > .elementor-widget-container > :last-child{margin-bottom:0}


/* ==========================================================================
   §3 Headings — plain HTML                                          (0,3,1)

   The path for content that is NOT Elementor-wrapped: a future plain-HTML
   article, or an imported post. Beats the reset's h1-h4 margin:0 (0,1,0) and
   the legacy `.single-academy h3/h4` (0,1,1) and
   `.single-academy .elementor-1405 h4` (0,2,1).

   H2 is the value the handoff names and the value `.dxr-h2--article` already
   ships in dxr-components.css:211 — 800 clamp(22px,2.5vw,28px)/1.25 at
   -.028em. Reproduced rather than referenced because these elements come out
   of the editor and cannot carry a class.

   H1 sits at the .dxr-h2--mid step, not at an .dxr-h1--* step: 8 articles
   carry their own <h1> inside post_content, and the page's real <h1> is the
   template's title. An in-content H1 at 52px would compete with it.

   NOTE FOR THE ARTICLE-TEMPLATE / CONTENT OWNER, not a defect here: the
   library uses <h3> as its main section heading far more than <h2> (104 vs
   46). This file follows the DESIGN's semantic scale — h2 > h3 > h4 — rather
   than re-mapping h3 onto the h2 step, because several articles use both and
   flattening them would destroy a real hierarchy. If the client wants the
   104 h3 section heads at the 28px step, that is a content decision (promote
   them to h2) or an explicit override, not a silent scale change.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose h1{
  max-width:var(--dxr-prose-measure);
  margin:44px 0 20px;
  font:800 clamp(26px,3.3vw,36px)/1.14 var(--dxr-font-sans);
  letter-spacing:-.034em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose h2{
  max-width:var(--dxr-prose-measure);
  margin:40px 0 18px;
  font:800 clamp(22px,2.5vw,28px)/1.25 var(--dxr-font-sans);
  letter-spacing:-.028em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose h3{
  max-width:var(--dxr-prose-measure);
  margin:32px 0 12px;
  font:700 20px/1.4 var(--dxr-font-sans);
  letter-spacing:-.02em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose h4{
  max-width:var(--dxr-prose-measure);
  margin:26px 0 10px;
  font:700 17px/1.45 var(--dxr-font-sans);
  letter-spacing:-.015em;
  color:var(--dxr-ink);
}
/* h5/h6 are not in the reset's margin:0 list, so they would otherwise keep
   their UA margins on top of everything else. Declared explicitly. */
.dxr-prose.dxr-prose.dxr-prose h5,
.dxr-prose.dxr-prose.dxr-prose h6{
  max-width:var(--dxr-prose-measure);
  margin:24px 0 10px;
  font:700 15px/1.5 var(--dxr-font-sans);
  letter-spacing:-.01em;
  color:var(--dxr-ink);
}


/* ==========================================================================
   §4 Headings — Elementor heading widgets                           (0,5,1)

   In this library a REAL heading is always `.elementor-heading-title` inside
   an `.elementor-widget-heading`. That class is also exactly where Elementor's
   217 per-post typography rules land, at (0,4,0), directly on the element —
   inheritance tricks do not help here, this has to be out-specified.

   ARITHMETIC. 3 (.dxr-prose ×3) + 1 (.elementor-widget-heading)
             + 1 (.elementor-heading-title) = 5 classes, + 1 element = (0,5,1).
   (0,5,1) > (0,4,0) and also > the (0,5,0) misc tier. The only Elementor rules
   left above this are the 18 at (0,8,0), which are per-widget Custom CSS for
   .notice and .elementor-button — deliberately left alone, see HANDS OFF.

   Values are identical to §3; only the selector changes. Kept as separate
   blocks rather than merged so that the two tiers stay independently
   editable — and because merging them would give the plain-HTML selectors the
   Elementor tier's specificity for no reason.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h1.elementor-heading-title{
  max-width:var(--dxr-prose-measure);
  margin:44px 0 20px;
  font:800 clamp(26px,3.3vw,36px)/1.14 var(--dxr-font-sans);
  letter-spacing:-.034em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h2.elementor-heading-title{
  max-width:var(--dxr-prose-measure);
  margin:40px 0 18px;
  font:800 clamp(22px,2.5vw,28px)/1.25 var(--dxr-font-sans);
  letter-spacing:-.028em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h3.elementor-heading-title{
  max-width:var(--dxr-prose-measure);
  margin:32px 0 12px;
  font:700 20px/1.4 var(--dxr-font-sans);
  letter-spacing:-.02em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h4.elementor-heading-title{
  max-width:var(--dxr-prose-measure);
  margin:26px 0 10px;
  font:700 17px/1.45 var(--dxr-font-sans);
  letter-spacing:-.015em;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h5.elementor-heading-title,
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-heading h6.elementor-heading-title{
  max-width:var(--dxr-prose-measure);
  margin:24px 0 10px;
  font:700 15px/1.5 var(--dxr-font-sans);
  letter-spacing:-.01em;
  color:var(--dxr-ink);
}


/* ==========================================================================
   §5 Equation carriers — bare headings are NOT headings             (0,5,1)

   THE SINGLE MOST DESTRUCTIVE THING A NAIVE PROSE LAYER DOES HERE.
   76 headings in the library have no `.elementor-heading-title`. 75 of them
   contain `span.katex-eq` and one is empty. NONE is a real heading. The
   editor used <h4>/<h3> purely as a centred, roomy carrier for a formula:

       <h4><span class="katex-eq" data-katex-display="false">\frac1u+\frac1v=\frac1f</span></h4>

   Style those as 22-28px bold headings and 75 equations across the library
   render bold and oversized. The presence of `.elementor-heading-title` is the
   exact discriminator, verified across all 27 articles — hence :not().

   All 395 equations are displayMode:false, so KaTeX emits an INLINE
   `<span class="katex">` and sizes it at 1.21em of its carrier. The carrier
   must therefore sit at BODY size (17px), which puts the rendered maths at
   ~20.6px — the intended display look — instead of 1.21 × 28px.

   text-align is deliberately NOT declared: the centring comes from Elementor's
   (0,3,0) rule on the widget and is inherited. Do not add it and do not
   override it.

   overflow-x lives HERE, on the block carrier, and not on .katex-eq or on
   anything KaTeX generates. `.katex .base` is inline-block/nowrap, so a long
   equation would otherwise widen the minmax(0,1fr) body track. Putting the
   scroll on the carrier leaves every KaTeX-generated node untouched.

   ARITHMETIC. 3 (.dxr-prose ×3) + 1 (.elementor-widget-text-editor)
             + 1 (:not(.elementor-heading-title), which contributes its
                  argument's (0,1,0))
             = 5 classes, + 1 element from :is(h1…h6) = (0,5,1).
   That out-ranks §3's (0,3,1) — so a bare <h4> in a text widget lands here,
   not there — and Elementor's inherited (0,3,0) font-size on the widget.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose .elementor-widget-text-editor :is(h1,h2,h3,h4,h5,h6):not(.elementor-heading-title){
  margin:0 0 var(--dxr-prose-flow);
  font:400 17px/1.8 var(--dxr-font-sans);
  letter-spacing:normal;
  color:var(--dxr-body);
  max-width:100%;
  overflow-x:auto;
  overflow-y:hidden;
}


/* ==========================================================================
   §6 Paragraphs and naked text

   Two shapes, both common:
     <p>…</p>                                            552 in the library
     bare text directly in .elementor-widget-container    242 widgets

   The naked case has no element to select, so the container carries the type.
   That rule is (0,5,0): 3 + .elementor-widget-text-editor
   + .elementor-widget-container. It only declares font/colour/max-width — it
   does NOT touch padding, which is Elementor's rhythm (see §2).

   The 515 `<span style="font-weight:400">` wrappers Google Docs left behind
   are harmless: 400 is already this design's body weight.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose p{
  max-width:var(--dxr-prose-measure);
  margin:0 0 var(--dxr-prose-flow);
  font:400 17px/1.8 var(--dxr-font-sans);
  color:var(--dxr-body);
}

.dxr-prose.dxr-prose.dxr-prose .elementor-widget-text-editor > .elementor-widget-container{
  max-width:var(--dxr-prose-measure);
  font:400 17px/1.8 var(--dxr-font-sans);
  color:var(--dxr-body);
}

/* Elementor icon lists carry their label in a span, not a <li> text node. */
.dxr-prose.dxr-prose.dxr-prose .elementor-icon-list-text{
  font:400 17px/1.8 var(--dxr-font-sans);
  color:var(--dxr-body);
}


/* ==========================================================================
   §7 Links                                                          (0,5,1)

   The reset removes the underline. In chrome that is right — a nav item reads
   as a control from its position. In a long unstructured body it is not:
   colour alone is not a sufficient distinguisher (WCAG 2.1 SC 1.4.1 Use of
   Colour), and the library's 115 in-prose links sit mid-sentence with nothing
   else to mark them.

   Elementor renders its BUTTON widget as an <a class="elementor-button">.
   Underlining and re-colouring that would wreck it, so both button classes are
   excluded here rather than patched back afterwards — which also means
   Elementor's own button colour rules keep working untouched.

   ARITHMETIC. 3 + 2 (:not ×2, each contributing its class argument) = 5
   classes, + 1 element = (0,5,1). The :has() variant adds one more element
   for (0,5,2), so it wins over the rule directly above it.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose a:not(.elementor-button):not(.elementor-button-link){
  color:var(--dxr-primary);
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:2px;
}
.dxr-prose.dxr-prose.dxr-prose a:not(.elementor-button):not(.elementor-button-link):hover{
  color:var(--dxr-primary-hover);
}
/* An anchor wrapping only an image is a figure link, not a text link. */
.dxr-prose.dxr-prose.dxr-prose a:not(.elementor-button):not(.elementor-button-link):has(> img){
  text-decoration:none;
}


/* ==========================================================================
   §8 Lists — THE RULE THE RESET WOULD HAVE DESTROYED

   `.dxr-shell--dxr :where(ul,ol){margin:0;padding:0;list-style:none}` removes
   the marker, the indent and the spacing in one line. 334 of the 959
   text-editor widgets in the library OPEN with a bare <ul>; there are 415 <ul>,
   22 <ol> and 659 <li> in total. All three properties are re-declared.

   `padding-inline-start` rather than `padding-left` so RTL keeps the indent on
   the correct side. `list-style-position` stays outside (the default) — inside
   would reflow every wrapped line under the bullet.

   ORDERED LISTS. Several articles continue a numbered list across widget
   boundaries with `<ol start="2">`. Nothing here declares `counter-reset` or
   `content`, so the `start` attribute keeps working; using list-style rather
   than generated counters is what preserves it.

   EXCLUSIONS.
     .elementor-icon-list-items — Elementor's own icon list, already marker-less
       and correctly indented by the plugin. Adding discs would double the
       bullets.
     li[style*="list-style-type: none"] — 6 deliberate authoring choices in the
       library. The inline style out-ranks any stylesheet, so they survive
       without help; noted so nobody "fixes" them.

   ARITHMETIC. 3 + 1 (:not argument) = 4 classes, + 1 element = (0,4,1),
   above the base tier and far above the reset.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose ul:not(.elementor-icon-list-items),
.dxr-prose.dxr-prose.dxr-prose ol:not(.elementor-icon-list-items){
  max-width:var(--dxr-prose-measure);
  margin:0 0 var(--dxr-prose-flow);
  padding-inline-start:22px;
}
.dxr-prose.dxr-prose.dxr-prose ul:not(.elementor-icon-list-items){list-style:disc outside}
.dxr-prose.dxr-prose.dxr-prose ol:not(.elementor-icon-list-items){list-style:decimal outside}

/* Nested levels. Second level is a hollow ring, third a filled square — the
   standard three-step ladder, so depth stays readable in long procedures. */
.dxr-prose.dxr-prose.dxr-prose ul:not(.elementor-icon-list-items) ul{list-style:circle outside}
.dxr-prose.dxr-prose.dxr-prose ul:not(.elementor-icon-list-items) ul ul{list-style:square outside}
.dxr-prose.dxr-prose.dxr-prose ol:not(.elementor-icon-list-items) ol{list-style:lower-alpha outside}
.dxr-prose.dxr-prose.dxr-prose ol:not(.elementor-icon-list-items) ol ol{list-style:lower-roman outside}

.dxr-prose.dxr-prose.dxr-prose li:not(.elementor-icon-list-item){
  margin:0 0 8px;
  font:400 17px/1.8 var(--dxr-font-sans);
  color:var(--dxr-body);
}
.dxr-prose.dxr-prose.dxr-prose li:not(.elementor-icon-list-item):last-child{margin-bottom:0}

/* A nested list belongs to its parent item — it must not inherit the 22px
   bottom gap that separates top-level lists from the next block.
   The :not() guard is required, not decorative: without it this scores (0,3,2)
   while the base ul rule above scores (0,4,1), and the class column is compared
   first — 4 beats 3, so the base wins and this rule never applies. Matching the
   base's guard lifts it to (0,4,2), where the element column decides. */
.dxr-prose.dxr-prose.dxr-prose li > ul:not(.elementor-icon-list-items),
.dxr-prose.dxr-prose.dxr-prose li > ol:not(.elementor-icon-list-items){margin:8px 0 0}

/* --dxr-border-strong is the token's documented "bullet dots" value. */
.dxr-prose.dxr-prose.dxr-prose li::marker{color:var(--dxr-border-strong)}


/* ==========================================================================
   §9 Inline emphasis

   336 <strong> + 77 <b>, 70 <i> + 1 <em>, 1 <sup>, 1 <u>. The legacy
   `.single-academy p strong` (0,1,2) swapped in an OpenSans-Bold face that no
   longer loads; the base tier's (0,3,2) here replaces it cleanly.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose strong,
.dxr-prose.dxr-prose.dxr-prose b{font-weight:700;color:var(--dxr-ink)}
.dxr-prose.dxr-prose.dxr-prose em,
.dxr-prose.dxr-prose.dxr-prose i{font-style:italic}
.dxr-prose.dxr-prose.dxr-prose sub,
.dxr-prose.dxr-prose.dxr-prose sup{font-size:.75em;line-height:0}
.dxr-prose.dxr-prose.dxr-prose mark{background:var(--dxr-amber-tint);color:var(--dxr-ink);padding:0 3px}


/* ==========================================================================
   §10 Images, figures, captions

   481 images, all S3-hosted, all carrying explicit width/height attributes up
   to 1648×807 and none carrying srcset. `max-width:100%` + `height:auto` is
   exactly the pair that makes them fit the column while KEEPING the intrinsic
   ratio the attributes declare — the attributes stay useful for CLS, they just
   stop being a hard floor.

   NO `display` AND NO `float` IS DECLARED ON img ANYWHERE IN THIS FILE.
   77 `img.alignleft` notice arrows are floated by the parent theme
   (hello-elementor/theme.min.css `.alignleft{float:left;margin-right:1rem}`),
   which loads before this file. A `display:block` here would unfloat all of
   them and break the callouts. `display:block` appears only inside figures,
   where there is no alignment class in play.

   FIGURES. 136 <figure>, 96 with `figcaption.widget-image-caption`. The framed
   treatment (hairline border, 12px radius, caption on a ruled shelf) is the
   handoff's figure block. Figures are NOT capped to the reading measure — they
   use the full body column, which is the whole reason the measure is applied
   per-element in §1 rather than to the container.

   CAPTIONS ARE SANS, NOT MONO. The two-font split is strict: JetBrains Mono is
   for measured content — counts, page numbers, spec values, table headers — and
   an editorial figure caption is prose about a figure. The design's mono
   captions are the placeholder/hatch labels, which do not exist here.

   ARITHMETIC on the caption. Elementor's `.widget-image-caption` rules are
   (0,4,0) — 96 of them set margin-block-start and 23 set the full type stack.
   `PROSE³ figcaption.widget-image-caption` = 3 + 1 = 4 classes, + 1 element =
   (0,4,1) > (0,4,0). The bare `figcaption` selector alongside it is (0,3,1),
   which is all a non-Elementor caption needs; the browser applies whichever
   selector matches the element.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose img{
  max-width:100%;
  height:auto;
}

/* NOT EVERY <figure> IS A FIGURE. Elementor's Image Box widget wraps its icon
   in `figure.elementor-image-box-img` — widget chrome, not an editorial figure.
   In the library those hold 24x75 step icons, and the framed treatment drew a
   hairline box with a grey ground and a 34px gap around each one. The two
   shapes present across the 27 articles are exactly `figure.wp-caption` (the
   real ones, full column width) and `figure.elementor-image-box-img`, so the
   guard names the widget the same way §8 excludes .elementor-icon-list-items.
   ARITHMETIC. The :not() argument contributes (0,1,0), lifting the base
   (0,3,1) to (0,4,1) — still below §2's (0,6,0) edge-reset, which must keep
   winning on the first and last child inside a widget. */
.dxr-prose.dxr-prose.dxr-prose figure:not(.elementor-image-box-img){
  margin:0 0 var(--dxr-prose-flow-lg);
  border:1px solid var(--dxr-border);
  border-radius:var(--dxr-radius-panel);
  overflow:hidden;
  background:var(--dxr-surface);
}
.dxr-prose.dxr-prose.dxr-prose figure:not(.elementor-image-box-img) img{
  display:block;
  /* width:auto, not 100%: legacy figure images carry intrinsic width/height
     attributes, and forcing 100% UPSCALES anything narrower than the column —
     a 300px diagram stretched to 700px and visibly soft. max-width from §10
     still stops anything overflowing. */
  width:auto;
  max-width:100%;
  height:auto;
  margin:0;
  border-radius:0;   /* the frame owns the corners; kills .single-academy's 4px */
}
.dxr-prose.dxr-prose.dxr-prose figcaption,
.dxr-prose.dxr-prose.dxr-prose figcaption.widget-image-caption{
  margin:0;
  padding:14px 18px;
  border-top:1px solid var(--dxr-border);
  font:400 13px/1.6 var(--dxr-font-sans);
  color:var(--dxr-text-muted);
  text-align:left;   /* overrides the legacy centred caption */
}


/* ==========================================================================
   §11 Embedded media

   DO NOT ADD height OR aspect-ratio TO A BARE `iframe` SELECTOR HERE.
   The Elementor video widget ships an EMPTY `.elementor-video` div and injects
   the YouTube iframe with JS at runtime; the ratio comes from Elementor's own
   `.elementor-wrapper` padding / --video-aspect-ratio, and the injected iframe
   is absolutely positioned at 100%/100% inside it. `height:auto` or an
   `aspect-ratio` on that element collapses the player to zero height. The
   global rule below therefore declares max-width and nothing else — which is
   inert for the positioned case and useful everywhere else.

   The responsive-ratio treatment is applied POSITIVELY, only to embeds that
   nothing else is managing: a direct child of the prose body, of a paragraph,
   of a figure, or of a core embed wrapper.

   The library's 2 hard-coded 3DOptix widget iframes live in an Elementor HTML
   widget with inline `width:100%; height:500px`. Inline styles win, so they are
   already fluid horizontally and keep their fixed height. Nothing to do, and
   nothing here disturbs them.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose iframe,
.dxr-prose.dxr-prose.dxr-prose video,
.dxr-prose.dxr-prose.dxr-prose embed,
.dxr-prose.dxr-prose.dxr-prose object{
  max-width:100%;
}

.dxr-prose.dxr-prose.dxr-prose > iframe,
.dxr-prose.dxr-prose.dxr-prose > video,
.dxr-prose.dxr-prose.dxr-prose p > iframe,
.dxr-prose.dxr-prose.dxr-prose figure > iframe,
.dxr-prose.dxr-prose.dxr-prose .wp-block-embed__wrapper > iframe{
  display:block;
  width:100%;
  height:auto;
  aspect-ratio:16/9;
  border:0;
  border-radius:var(--dxr-radius-md);
  background:var(--dxr-ink-deep);
  margin:0 0 var(--dxr-prose-flow-lg);
}

.dxr-prose.dxr-prose.dxr-prose .wp-block-embed{margin:0 0 var(--dxr-prose-flow-lg)}
.dxr-prose.dxr-prose.dxr-prose .wp-block-embed__wrapper{margin:0}


/* ==========================================================================
   §12 Tables

   26 tables, raw and classless, with hard pixel widths inline
   (`<table style="width:700px">`, four at 781px) and per-cell inline widths.
   Those inline values cannot be overridden, so the job is to stop them
   overflowing the grid rather than to re-lay them out.

   `display:block` + `overflow-x:auto` turns the table into its own scroll
   container. The tbody/tr/td keep their internal display values and the engine
   regenerates the anonymous table boxes, so the layout is unchanged — it just
   scrolls instead of pushing the minmax(0,1fr) body track wider than the
   column. This is the same treatment the design system's own
   dxr_data_table() primitive uses (.dxr-table, inc/dxr-components.php), so wide data reads consistently across
   the site.

   The library has no <th> at all — every table leads with a bold <td>. The <th>
   rule is here for correctness if one is ever added, and it is the ONE place in
   this file that uses JetBrains Mono: a column header is a label for measured
   content, which is exactly the split the two-font rule draws.
   ========================================================================== */
/* display:block turns the table into its own scroll container, but it also
   stops the element generating a table box — the rows then form an anonymous
   one that shrink-wraps, so the 26 legacy tables carrying an inline
   style="width:700px" lose their column widths. Restoring display:table on
   tbody puts a real table box back INSIDE the scroller, so the widths hold and
   the overflow still scrolls in its own box rather than the page. */
.dxr-prose.dxr-prose.dxr-prose tbody,
.dxr-prose.dxr-prose.dxr-prose thead{display:table;width:100%}
.dxr-prose.dxr-prose.dxr-prose table{
  display:block;
  max-width:100%;
  overflow-x:auto;
  border-collapse:collapse;
  margin:0 0 28px;
  font:400 14px/1.6 var(--dxr-font-sans);
  color:var(--dxr-body);
}
.dxr-prose.dxr-prose.dxr-prose th{
  padding:14px 18px;
  text-align:left;
  vertical-align:bottom;
  border-bottom:1px solid var(--dxr-border);
  background:var(--dxr-surface-raised);
  font:500 10px/1 var(--dxr-font-mono);
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--dxr-text-subtle);
  white-space:nowrap;
}
.dxr-prose.dxr-prose.dxr-prose td{
  padding:14px 18px;
  vertical-align:top;
  border-bottom:1px solid var(--dxr-border-inner);
  font:400 14px/1.6 var(--dxr-font-sans);
  color:var(--dxr-body);
}
.dxr-prose.dxr-prose.dxr-prose tr:last-child td{border-bottom:0}


/* ==========================================================================
   §13 Blockquote, rules, code, definition lists

   None of these appear in the 27 articles as they stand today. They are
   declared anyway because the Academy is an editable library and the next
   article can contain any of them — an unstyled <pre> inside a
   minmax(0,1fr) track is a horizontal-scroll bug waiting to happen, and an
   unstyled <blockquote> inherits nothing useful from the reset. Cheap
   insurance, clearly labelled as such.
   ========================================================================== */
.dxr-prose.dxr-prose.dxr-prose blockquote{
  max-width:var(--dxr-prose-measure);
  margin:0 0 28px;
  padding:4px 0 4px 22px;
  border-inline-start:3px solid var(--dxr-primary);
  font:400 17px/1.7 var(--dxr-font-sans);
  color:var(--dxr-body-muted);
}
.dxr-prose.dxr-prose.dxr-prose blockquote p:last-child{margin-bottom:0}

.dxr-prose.dxr-prose.dxr-prose hr{
  border:0;
  border-top:1px solid var(--dxr-border);
  margin:var(--dxr-prose-flow-lg) 0;
}

.dxr-prose.dxr-prose.dxr-prose code{
  font:400 15px/1.6 var(--dxr-font-mono);
  background:var(--dxr-surface-sunken);
  border-radius:var(--dxr-radius-badge);
  padding:2px 6px;
  color:var(--dxr-ink);
}
.dxr-prose.dxr-prose.dxr-prose pre{
  max-width:100%;
  margin:0 0 28px;
  padding:20px 22px;
  overflow-x:auto;
  background:var(--dxr-ink-deep);
  border-radius:var(--dxr-radius-md);
  font:400 13px/1.7 var(--dxr-font-mono);
  color:var(--dxr-on-dark-strong);
}
.dxr-prose.dxr-prose.dxr-prose pre code{
  background:none;
  padding:0;
  color:inherit;
  font-size:inherit;
}

.dxr-prose.dxr-prose.dxr-prose dl{max-width:var(--dxr-prose-measure);margin:0 0 var(--dxr-prose-flow)}
.dxr-prose.dxr-prose.dxr-prose dt{font-weight:700;color:var(--dxr-ink);margin-top:14px}
.dxr-prose.dxr-prose.dxr-prose dd{margin:4px 0 0;padding-inline-start:22px;color:var(--dxr-body)}
