/* ============================================================
   PRIME ACADEMY GROUP — pag-v6-elementor.css
   THE v6 BRIDGE. Loaded last, after Elementor's own frontend CSS.

   Same job and same rule as pag-elementor.css, for the other design system:
   pag-v6-system.css and pag-v6-home.css are v6.1.2's stylesheets, byte for
   byte, written against hand-authored HTML. Elementor emits its own wrappers,
   so a handful of those selectors no longer reach what they were written for.

   This file is the ONLY place that knows about Elementor. Every rule exists
   because a v6 selector missed, and each one says which. Nothing here invents
   a look — it re-points an existing one.

   IT GROWS BY MEASUREMENT, NOT BY GUESSING. Rules are added when a measured
   difference against v6.1.2 shows one is needed, never in anticipation. That
   is how the v4.7 bridge was built and why it stayed honest.

   RULE FOR ANYONE EDITING: to change how something LOOKS, change
   pag-v6-system / pag-v6-home, or the Elementor panel. Come here only when
   Elementor's markup is the reason a rule misses.
   ============================================================ */

/* ------------------------------------------------------------
   1. WIDGET WRAPPERS
   Elementor puts .elementor-widget and .elementor-widget-container around
   every widget. v6 styles direct children — `.opt-body > h3`, `.tile > p`
   and so on — so those wrappers sit between a rule and its target.

   Rather than restate each selector, the wrappers are made transparent to
   layout wherever they appear inside a v6 block. They keep existing for
   Elementor's own panel controls; they simply stop being boxes.
   ------------------------------------------------------------ */
.elementor .pag-v6 .elementor-widget-container{ margin:0; padding:0; }

/* Elementor gives a widget inside a flex container width:100%. Correct almost
   everywhere; wrong on a row meant to sit inline, which in v6 is the hero
   buttons and the two closing cards' buttons. */
.elementor .hero-cta > .elementor-element{ width:auto; }

/* ------------------------------------------------------------
   2. HEADINGS AND TEXT
   Elementor's heading widget emits its own h1-h6 with no margin, which is
   what v6 wants anyway. The text editor's trailing margin was handled here
   too, twice over — the rule now lives once, in §14, and at a specificity
   that does not take v6's own paragraph margins with it.
   ------------------------------------------------------------ */

/* ------------------------------------------------------------
   3. IMAGE WIDGETS
   Nothing needed. Elementor's `.elementor-widget-image img{width:100%}` and
   v6's `.nav-brand img{width:150px}` have the same specificity, so this was
   a pure ordering problem — solved by enqueuing v6 after Elementor's widget
   stylesheets rather than by restating v6's widths here. See inc/pag-v6.php.

   A blanket `width:auto` was tried first and was wrong: it beat v6 as surely
   as Elementor had, and the mark rendered at its attribute size instead.
   ------------------------------------------------------------ */

/* ------------------------------------------------------------
   4. ICON LISTS AS PLAIN LISTS
   v6 writes its menus as `ul > li > a` and styles those three directly. An
   Icon List wraps each item in flex and pads it, and puts the label in a
   second span.

   The wrappers are made transparent rather than restyled: the ul and li keep
   existing, so the client still edits one repeater, but v6's own `.nav-links a`
   and `.foot-col li` rules land on exactly what they were written for.
   ------------------------------------------------------------ */
body.pag-v6 .elementor-icon-list-items{ list-style:none; padding:0; margin:0; }
body.pag-v6 .elementor-icon-list-item{ margin:0; padding:0; }
body.pag-v6 .elementor-icon-list-item{ display:block; font-size:inherit; }
/* The anchor is inline-block, not block. Elementor makes it a full-width flex
   row; v6 leaves it inline, and the footer's three link columns are sized by
   their widest link. Made block they filled their grid track instead — 242px
   against v6's 113 — which pushed the brand column narrow and reflowed its
   paragraph a line taller. */
body.pag-v6 .elementor-icon-list-item > a{ display:inline-block; font-size:inherit; }
body.pag-v6 .elementor-icon-list-text{ padding:0; }

/* The nav is one flex row of anchors. `display:contents` takes the ul and the
   li out of the box tree entirely, so the anchors become the flex children of
   .nav-links — which is what v6's `gap` is spacing and what its
   `justify-self:center` is centring. Restated for the li so the row survives
   the rule above. */
body.pag-v6 .nav-links .elementor-icon-list-items,
body.pag-v6 .nav-links .elementor-icon-list-item{ display:contents; }

/* The footer columns are the opposite case: there the li IS the row, and v6
   spaces them with `.foot-col li{margin-bottom:7px}`. Only the icon list's own
   padding has to go, which the block above already did. */

/* The social row is a flex list of three marks. Same treatment as the nav, and
   the label inside each one is v6's visually-hidden span by another name. */
body.pag-v6 .foot-social .elementor-icon-list-items{ display:flex; gap:var(--s4); }
body.pag-v6 .foot-social .elementor-icon-list-item{ display:block; }
body.pag-v6 .foot-social .elementor-icon-list-text{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
body.pag-v6 .foot-social .elementor-icon-list-icon svg{ width:19px; height:19px; display:block; }

/* ------------------------------------------------------------
   5. WHOLE-CARD LINKS
   v6 makes each option card and each closing card a single <a>, and the card is
   a container with its own inner link stretched over it. One link per card,
   which is also what a screen reader should hear.

   THE REASON GIVEN HERE WAS WRONG, and it stood for five pages. It said a
   container cannot be an `<a>`. It can: `Utils::ALLOWED_HTML_WRAPPER_TAGS`
   includes `a` (utils.php:29), and the container registers a `link` control
   conditioned on `html_tag === 'a'` (container.php:611) which `before_render()`
   passes to `add_link_attributes( '_wrapper', $link )` at :290.

   THE ATTRIBUTE HALF IS NARROWER THAN IT FIRST LOOKED, and the first version of
   this comment over-generalised it. Free Elementor has NO element-level
   attributes control: `add_custom_attributes_controls()` registers a section
   called `custom_attributes_pro` whose only control is a RAW_HTML Pro teaser
   (controls.php:1200-1218), and nothing anywhere reads an element-level
   `_attributes` at render. What survives is only this: a container that is an
   `<a>` AND has a link set can carry arbitrary attributes, because
   `add_link_attributes()` merges `parse_custom_attributes()` off the LINK's own
   field (element-base.php:453). Any other container cannot carry any attribute
   at all — which is why the aria table and Subscription Plans' `data-billing`
   both write theirs at `elementor/frontend/before_render`. Platform's `.pick` cards are built that way and render as
   real anchors.

   The overlay is kept on the cards that already ship it, because they are
   verified and the pattern works — not because it is the only way. A NEW card
   row should be an `<a>` container: no overlay, no `position:relative` on the
   card, no `position:static` on an inner wrapper, and nothing for
   `cardhit.mjs` to catch. Two things follow that the overlay does not have:
   the whole card is genuinely one link, and the parent theme's
   `a:hover{color:#336}` now reaches it — see §29 and Platform's §5.
   ------------------------------------------------------------ */
body.pag-v6 .opt{ position:relative; }
body.pag-v6 .close-card{ position:relative; }
body.pag-v6 .opt .link a::after,
body.pag-v6 .close-card .btn a::after{
  content:''; position:absolute; inset:0; z-index:1;
}

/* ------------------------------------------------------------
   6. BUTTON WIDGETS
   The v6 class is forwarded onto the anchor by pag_v6_forward_classes(), so
   the anchor IS the button and v6's `.btn` rules land where they were written
   for. The widget's own wrapper would otherwise be a second box carrying the
   same padding — 209x71 against v6's 184x47 — so it is taken out of the box
   tree entirely and the anchor becomes the flex child its parent expects.
   ------------------------------------------------------------ */
body.pag-v6 .elementor-widget-button{ display:contents; }

/* ------------------------------------------------------------
   7. LINKED SVG MARKS
   Elementor ships:

       .elementor-widget-image a img[src$=".svg"]{ width:48px }

   — a fallback for SVGs with no intrinsic size, at specificity 0,3,1. v6 sizes
   its two brand marks with `.nav-brand img` and `.foot-brand img`, both 0,1,1,
   so Elementor won and both marks rendered 48px wide instead of 150 and 142.

   Nothing lower-specificity can undo it and `width:auto` here would beat v6 as
   surely as Elementor did, so the two values are restated — the only place in
   this file that repeats a v6 number. If either changes in pag-v6-home.css it
   has to change here too, which is why it is called out rather than buried.
   ------------------------------------------------------------ */
body.pag-v6 .nav-brand .elementor-widget-image a img[src$=".svg"],
body.pag-v6 .nav-brand a img[src$=".svg"]{ width:150px; }
body.pag-v6 .foot-brand .elementor-widget-image a img[src$=".svg"],
body.pag-v6 .foot-brand a img[src$=".svg"]{ width:142px; }

/* ------------------------------------------------------------
   8. WIDGETS IN A FLEX ROW
   Elementor gives a widget inside a flex container `width:100%`, which is
   right almost everywhere and wrong on a row meant to read as a row: the
   footer's legal line stacked into three, taking it from 69px to 142.
   ------------------------------------------------------------ */
body.pag-v6 .hero-cta > .elementor-element,
body.pag-v6 .foot-end > .elementor-element,
body.pag-v6 .foot-legal > .elementor-element{ width:auto; flex-basis:auto; }

/* `width:auto` on its own was not enough: Elementor also gives the child a
   flex-basis, and a basis of 100% wraps a row that would otherwise fit. The
   footer's legal line measured 584 + 218 + 89 inside 1210 and still stacked. */

/* The legal pair is two links v6 spaces with `.foot-legal{display:flex;gap:32px}`.
   A Text Editor puts them both inside one <p>, so the gap had nothing to space.
   The paragraph steps out of the box tree and the anchors become the flex
   children the rule was written for. */
body.pag-v6 .foot-legal .elementor-widget-container,
body.pag-v6 .foot-legal p{ display:contents; }

/* ------------------------------------------------------------
   9. THE IMAGE ANCHOR
   Elementor's linked image renders <a><img></a> and leaves the anchor inline,
   so the img sat on a text baseline and the widget measured 7.5px taller than
   the image inside it — 56.98 against v6's 49.48 on the nav mark.
   ------------------------------------------------------------ */
body.pag-v6 .elementor-widget-image a{ display:block; line-height:0; }

/* ------------------------------------------------------------
   10. FLEX ROWS THAT ELEMENTOR MAKES COLUMNS
   An Elementor container is a flex COLUMN by default, and
   `.e-con.e-flex{flex-direction:var(--flex-direction)}` is specificity 0,2,0 —
   it beats pag-flow's 0,1,0. Where v6 relies on the browser default of `row`
   and does not say so itself, the column won: the footer's legal line stacked
   into three centred rows, 142px instead of 69.

   Named one at a time, never blanket. A blanket rule strong enough to beat
   Elementor here would also beat v6's own `.opt{flex-direction:column}` and
   lay the option cards on their side. These two are the only containers v6
   leaves at the default and Elementor changes.
   ------------------------------------------------------------ */
/* Bounded by the same breakpoint v6 uses. `@media (max-width:900px)` turns
   `.foot-end` into a column, and an unbounded rule here — at 0,2,1 against
   v6's 0,1,0 — beat that at every width, so the footer stayed a row on a
   phone where v6 stacks it. A bridge rule that corrects Elementor must not
   also outrank the original's own responsive behaviour. */
@media (min-width:901px){
  body.pag-v6 .foot-end{ flex-direction:row; }
}
body.pag-v6 .hero-cta{ flex-direction:row; }

/* ------------------------------------------------------------
   11. TEXT INSIDE A FLEX CARD
   v6 grows the option card's paragraph with `.opt-body p{flex:1}` so all three
   cards end level whatever their copy. A Text Editor puts that paragraph two
   wrappers down, so the wrapper was the flex child and the paragraph sat at
   its natural height — two cards at 47px against v6's 70.5, and the card grid
   19px taller overall.

   The wrappers step out of the box tree so the paragraph is the flex child the
   rule was written for.
   ------------------------------------------------------------ */
body.pag-v6 .opt-body > .elementor-widget-text-editor,
body.pag-v6 .opt-body > .elementor-widget-text-editor > .elementor-widget-container{ display:contents; }

/* ------------------------------------------------------------
   12. FOOTER LINK COLUMNS
   `.foot-top` is a grid whose link columns size to their widest link. An Icon
   List's <ul> is a full-width flex column, so each track took the space it was
   offered rather than the space it needed — 242px against v6's 113.
   ------------------------------------------------------------ */
body.pag-v6 .foot-col .elementor-icon-list-items{ display:block; width:auto; }
body.pag-v6 .foot-col .elementor-widget-icon-list,
body.pag-v6 .foot-col .elementor-widget-icon-list > .elementor-widget-container{ width:auto; }

/* AND THE COLUMN ITSELF, which is what made the page scroll SIDEWAYS.
   An Elementor container carries `width:var(--width)` with `--width:100%`, and
   in a grid `width:100%` resolves against the whole track — then `.foot-col +
   .foot-col{margin-left:var(--foot-gap)}` is added OUTSIDE that. So each of the
   last two columns was its track wide plus 121px of margin, and the last one
   finished 6px past the viewport.

   `width:auto` is what a grid item is supposed to have: `justify-items` is
   `stretch` by default, and stretch fills the track MINUS the margins. v6's own
   columns measure 113px in a 234px track for exactly that reason.

   Invisible in every check that mattered. The tracks are identical on both
   pages, so `probe.mjs` on `.foot-top` agreed; the columns are left-aligned, so
   nothing MOVED and the pixel diff was clean; and the overflow is 6px of empty
   navy off the right edge, which photographs as nothing. `widths.mjs` prints
   `hOverflow` for this and nothing else does. */
body.pag-v6 .foot-col{ width:auto; }

/* ------------------------------------------------------------
   13. HEADING LINE HEIGHT
   v6 sets its heading rhythm on the ELEMENT — `h3{line-height:1.3}`,
   specificity 0,0,1. Elementor's Heading widget resets
   `.elementor-heading-title{line-height:1}` at 0,1,0 and wins, so every
   heading on the page sat tighter than v6 draws it: the option card titles
   measured 21.59 against 28.06, and the difference compounds down the page.

   Elementor's class cannot simply be dropped — its panel typography controls
   target it, and the client would lose them. So the three v6 headings that
   appear on this page have their line height restated, and this is the second
   and last place in the file that repeats a v6 value.

       h2  1.12   ·  h3  1.3   ·  h4  1.3     (pag-v6-system.css)

   h1 is not restated: v6 leaves it to .hero h1, which is a class and already
   beats Elementor.
   ------------------------------------------------------------ */
body.pag-v6 h2.elementor-heading-title{ line-height:1.12; }
body.pag-v6 h3.elementor-heading-title{ line-height:1.3; }
body.pag-v6 h4.elementor-heading-title{ line-height:1.3; }

/* ------------------------------------------------------------
   14. TEXT EDITOR'S TRAILING MARGIN
   Elementor gives the last paragraph in a Text Editor a bottom margin. v6's
   reset is `*{margin:0}` and it spaces prose with the flex gap of the block
   around it, so the extra 9.975px was pure addition.

   WRITTEN AT (0,3,2) FIRST, AND THAT WAS THE BUG.
   The margin it undoes comes from a rule at (0,0,1). Beating a (0,0,1) rule
   from (0,3,2) also beats every v6 rule that gives a paragraph a margin BY
   CLASS, and v6 has a dozen of them:

       .route p:not(.tag){ margin-bottom:var(--s5) }    (0,2,1)
       .path p{ margin-bottom:var(--s5) }               (0,1,1)
       .rel-card p{ margin-bottom:var(--s5) }           (0,1,1)

   Any Text Editor holding ONE paragraph is `p:last-child`, so on those blocks
   v6's margin vanished. Nowhere to put it back either: Elementor's own
   `.e-con .elementor-widget.elementor-widget{margin-block-end:0}` is (0,3,0)
   and flattens the wrapper, so both boxes lose their spacing and the section
   closes up.

   It never showed on the homepage because every class-scoped paragraph rule
   there sets `margin:0` anyway — `.tile p`, `.diff-row p`. The homepage
   measured 0.00% with this rule already wrong. Found on About, by why.mjs.

   `:where()` contributes NOTHING to specificity, so the same selector now
   weighs (0,0,1): a tie with the rule it was written to undo, which the bridge
   wins because it loads last — and a loss to every v6 class rule, which is the
   entire point.

   AND IT IS EVERY PARAGRAPH, NOT JUST THE LAST ONE. It was `p:where(:last-child)`
   first, which is the case a single-paragraph widget hits — and that hid the
   other one for four pages. A widget holding TWO paragraphs keeps the theme's
   `.7em` between them, and whether it shows depends on the parent:

     · in a BLOCK parent the two margins collapse, so v6's own
       `p + p{margin-top:…}` swallows it and nothing is visible. About's
       three-paragraph block is like this, and measured exactly.
     · in a FLEX column they do NOT collapse, so the gap is v6's margin PLUS
       9.275px. Online Training's certificate block is like this.

   v6's reset is `*{margin:0}`: no paragraph anywhere in v6 carries a bottom
   margin it did not ask for by class. Restating that for every paragraph is
   restating v6, and it is what makes the rule independent of how many
   paragraphs a widget happens to hold.
   ------------------------------------------------------------ */
:where(body.pag-v6 .elementor-widget-text-editor) p{ margin-bottom:0; }

/* ------------------------------------------------------------
   15. THE LAST THREE
   Each one is Elementor's own value winning a property v6 also sets, at a
   specificity v6's selector cannot reach. Written with v6's tokens rather than
   numbers, so none of these can drift from pag-v6-system.css.
   ------------------------------------------------------------ */

/* The eyebrow. v6 spaces it with `.tag{margin-bottom:var(--s3)}` and the margin
   was arriving as 0, because Elementor's own `margin-block-end` beat it — 12px
   missing from each side of "the difference it makes".

   `:where()`, AND THAT MATTERS. Written as `body.pag-v6 .tag` this weighs
   (0,2,1), which beats not only Elementor but v6's OWN overrides: Subscription
   Plans sets `.plan .tag{margin-bottom:var(--s5)}` at (0,2,0) to give the
   eyebrow more room inside a plan card, and this rule quietly won — every plan
   card came out 12px short and the section 12px with it.

   That is §22's failure exactly: a bridge rule written to neutralise Elementor
   becomes the thing overriding the original. `:where()` drops the scope to
   nothing, so it lands at (0,0,1) — enough to beat Elementor's element-level
   rule on order, and a loss to every v6 class rule including `.tag` itself.
   `margin-block-end` is restated because that is the property Elementor sets. */
:where(body.pag-v6) .tag{ margin-bottom:var(--s3); margin-block-end:var(--s3); }

/* `margin-block-end`, not only `margin-bottom`. Elementor writes the LOGICAL
   property for a widget's spacing, and a logical property set later beats the
   physical one whatever its specificity — so `margin-bottom` landed as 0 while
   the cascade showed v6's rule winning. That contradiction is the tell. */

/* v6's card link is plain text with an arrow. Elementor's Button widget brings
   `.elementor-size-sm` padding with it, which made the link 38px tall against
   v6's 23.5 and pushed every card 15px taller. Undoing Elementor's padding,
   not restating v6's — v6 sets none. */
body.pag-v6 a.link{ padding:0; line-height:inherit; }

/* v6 grows the card body with `.opt-body{flex:1}` so the three cards end level
   and the paragraph inside has room to fill. Elementor's
   `.elementor-element{flex-grow:var(--flex-grow)}` is the same specificity and
   the body stopped at its content height, leaving the paragraph at two lines
   where v6 draws three. */
body.pag-v6 .opt-body{ flex-grow:1; }

/* The footer's three link columns are grid tracks that size to their widest
   link. An Icon List's list still asked for the whole track, which squeezed
   the brand column from 423px to 396 and reflowed its paragraph. */
body.pag-v6 .foot-col .elementor-widget-icon-list,
body.pag-v6 .foot-col .elementor-icon-list-items{ width:max-content; }

/* The eyebrow's 12px, restated against the token so it cannot drift. The plain
   `.tag` selector above did not take even though the cascade showed it
   winning — the child selector does, which is the difference between the two
   and the reason this line exists rather than the shorter one. */
body.pag-v6 .diff-head > .tag{ margin-bottom:var(--s3); }

/* ------------------------------------------------------------
   16. THE SOCIAL ROW
   v6 draws three 19px marks: `.foot-social svg{width:19px;height:19px;display:block}`.
   An Icon List leaves its anchor inline and pads the icon span, so each mark
   sat on a text baseline inside a padded box — the row measured 99.5x26.5
   against v6's 89x19, and those 7.5px were the last of the footer's difference.

   The anchor and the icon span are made blocks with no line box of their own,
   so the svg is the only thing setting the size, which is what v6 intends.
   ------------------------------------------------------------ */
body.pag-v6 .foot-social .elementor-icon-list-item > a{ display:block; line-height:0; padding:0; }
body.pag-v6 .foot-social .elementor-icon-list-icon{ display:block; line-height:0; padding:0; width:auto; }

/* ------------------------------------------------------------
   17. THE THEME'S SKIP LINK
   Hello Elementor prints its own "Skip to content" link at the top of <body>.
   v6 has the same link and hides it with `.visually-hidden`; the theme's uses
   its own class, which v6's stylesheet knows nothing about, so it sat in flow
   and pushed the entire page down by one line — 23.5px, on every section,
   while each section measured exactly right on its own.

   Hidden the way v6 hides its own, and still reachable: it stays focusable and
   a keyboard user still gets it.
   ------------------------------------------------------------ */
body.pag-v6 .skip-link:not(:focus){
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}

/* ------------------------------------------------------------
   18. BOTTOM MARGINS ON WIDGETS
   The `margin-block-end` problem from §15, generalised. Wherever v6 puts a
   bottom margin on something that becomes an Elementor WIDGET, Elementor's
   logical margin lands on the same computed property and wins, and the space
   simply vanishes. It is invisible at some widths and obvious at others —
   the hero's 48px was intact at 1440 and gone at 390.

   Restated as the logical property, against v6's own tokens, so nothing here
   can drift from pag-v6-system.css. Containers are unaffected: `.diff-head`
   keeps its margin because it is a container, not a widget.
   ------------------------------------------------------------ */
/* Written as a child selector, like the eyebrow in §15. The plain
   `body.pag-v6 .hero-lead` form does not take even though it is more specific
   than anything Elementor sets — the same behaviour, in the same place in the
   cascade, and the child form is what works in both cases. */
body.pag-v6 .hero-in > .hero-lead{ margin-block-end:var(--s7); }
body.pag-v6 .foot-col h3{ margin-block-end:var(--s4); }
body.pag-v6 .opts-head h2{ margin-block-end:var(--s4); }

/* ------------------------------------------------------------
   19. THE TIMELINE STEP TEXT
   v6 styles the step name and its line as `.tlpop b` and `.tlpop > span` — the
   second is a CHILD selector, and a Text Editor puts its content two wrappers
   and a <p> down, so the span was never a child and never picked up its size.
   Two pixels a step, six steps, on every width.

   The wrappers and the paragraph step out of the box tree so both are the
   direct children the rules were written for.
   ------------------------------------------------------------ */
body.pag-v6 .tlpop > .elementor-widget-text-editor,
body.pag-v6 .tlpop > .elementor-widget-text-editor > .elementor-widget-container,
body.pag-v6 .tlpop p{ display:contents; }

/* ------------------------------------------------------------
   20. FOOTER LINK SPACING
   `.foot-col li{margin-bottom:7px}` — the logical-margin problem again, on the
   list items this time. 7px a link, three links a column.
   ------------------------------------------------------------ */
body.pag-v6 .foot-col .elementor-icon-list-item{ margin-block-end:7px; }

/* ------------------------------------------------------------
   21. THE STEP LINE, AND WHY §19 WAS NOT ENOUGH
   §19 gave the Text Editor wrappers `display:contents` so the step's <b> and
   <span> would become `.tlpop`'s children. That fixes LAYOUT and not
   SELECTORS: `display:contents` removes a box from the box tree, it does not
   change the DOM, so v6's `.tlpop > span` still had a <p> and two divs between
   it and the span and never matched. The <b> looked right only because v6
   styles it as `.tlpop b`, a descendant selector.

   So the span is re-pointed here. `display:block` is the whole of it — the
   size and colour in v6's rule are inherited from `.tlpop` and were never the
   problem; without the block the span shrank to its text, 209px wide instead
   of 222, and set one line differently.
   ------------------------------------------------------------ */
body.pag-v6 .tlpop span{ display:block; font-size:var(--t-small); line-height:var(--lh-small); }

/* ------------------------------------------------------------
   22. FOOTER LINK LINE BOX
   `inline-block` (§12) sizes the column correctly but gives each anchor a
   line box of its own, 0.7px taller than the inline one v6 draws. Three links
   a column, three columns, on every width.
   ------------------------------------------------------------ */
/* `inline-block`, not block. v6 leaves the anchor inline, so each list item is
   as tall as the LIST's line height (23.09) and not the link's own (22.09) —
   made a block the anchor became the item's whole height and every link lost a
   pixel. Inline-block keeps the item's line box while still letting the column
   size to its widest link. */
body.pag-v6 .foot-col .elementor-icon-list-item > a{ display:inline-block; }

/* The label inside each footer link is an inline span with its own line box,
   which sat 0.7px proud of the anchor's. Made a block it takes the anchor's
   line height and the column lands exactly on v6's. */
body.pag-v6 .foot-col .elementor-icon-list-text{ display:block; line-height:inherit; }

/* The size, restated against v6's tokens. Two rules were forcing `font-size:
   inherit` on this anchor — Elementor's `.elementor-widget .elementor-icon-list-item a`
   at 0,2,1, and my own §4 rule — and both beat v6's `.foot-col a` at 0,1,1. The
   links set at 14.25px instead of 13, which is where the last 0.7px a line came
   from. Mine was half the problem, which is worth saying: a bridge rule written
   to neutralise Elementor can quietly become the thing overriding the original. */
body.pag-v6 .foot-col .elementor-icon-list-item > a{
  font-size:var(--t-small); line-height:var(--lh-small);
}

/* The card's "Explore Free Courses" link is plain text with an arrow in v6.
   Elementor's Button widget brings a filled background as well as padding
   (§15), which drew a solid blue bar across every card. */
body.pag-v6 a.link{ background:none; border:0; }

/* ------------------------------------------------------------
   23. THE CARD PHOTOGRAPHS
   v6 fills the figure with `.opt-fig img{position:absolute;inset:0}`, against
   `.opt-fig{position:relative}`. Elementor puts the image inside two wrappers,
   and the inner one became the positioned ancestor — a box whose only child is
   absolute, so it has no height, so `inset:0` resolved to a 0px-tall image.

   The photographs were not misrendered. They were not rendered at all: every
   card showed its empty `--wash` background, and because the layout is driven
   by `aspect-ratio` on the figure rather than by the image, every height still
   measured exactly right. Nothing but the pixel diff could have caught this.

   The wrappers step out of the box tree so `.opt-fig` is the positioned
   ancestor again, which is what v6's rule was written against.
   ------------------------------------------------------------ */
body.pag-v6 .opt-fig > .elementor-widget-image,
body.pag-v6 .opt-fig > .elementor-widget-image > .elementor-widget-container,
/* The hero's film is the same defect and was just as invisible: `.hero-bg` is
   also `position:absolute;inset:0`, so the video collapsed to nothing and the
   hero rendered on the pale page ground with white text on it — legible in the
   measurements, unreadable on screen. */
body.pag-v6 .hero > .elementor-widget-html,
body.pag-v6 .hero > .elementor-widget-html > .elementor-widget-container{ display:contents; }

/* The social row sat 10.5px wide of v6's 89: three 19px marks and two 16px
   gaps. The Icon List's items were carrying width of their own. */
body.pag-v6 .foot-social .elementor-icon-list-item{ width:19px; }
body.pag-v6 .foot-social .elementor-icon-list-icon{ width:19px; }

/* ------------------------------------------------------------
   24. FOUR THINGS THE MEASUREMENTS COULD NOT SEE
   Every one of these is a colour or an alignment. Heights matched, widths
   matched, the ladder was Δ0 at four widths, and the page was still wrong in
   four places. Screenshots found them; numbers never would have.
   ------------------------------------------------------------ */

/* 1 · The hero text sat 64px too high. v6 bottom-aligns it with
   `.hero{display:flex;align-items:flex-end}`, and Elementor's
   `.e-con.e-flex{align-items:var(--align-items)}` at 0,2,0 beat that 0,1,0 —
   the same shape as the footer's flex-direction, and the same fix. */
body.pag-v6 .hero{ align-items:flex-end; flex-direction:row; }

/* `flex-direction` as well as `align-items`, and that is the whole reason this
   took two passes. An Elementor container is a flex COLUMN, and in a column
   `align-items` governs the horizontal axis — so the rule applied, the computed
   value read `flex-end` on both sides, and the text still sat at the top.
   A correct-looking computed value on the wrong axis. */

/* 2 · The card link's hover. v6 writes `.link:hover, a:hover .link` because on
   the option cards the whole card is one <a> and the pointer is rarely on the
   words. Our card is a container with a stretched inner link, so `a:hover`
   never fired and the link stayed Deep Blue while the rest of the card
   responded. */
body.pag-v6 .opt:hover a.link{ color:var(--azure); }
body.pag-v6 .opt:hover a.link svg{ transform:translateX(3px); }

/* 3 · The step line was rendering white. §21 re-pointed `.tlpop > span` for
   display and size and dropped the colour with it — v6 sets it at 64% white
   over navy, and full white is a different reading weight against that panel. */
body.pag-v6 .tlpop span{ color:color-mix(in srgb, #fff 64%, var(--navy)); }

/* 4 · The social marks are now v6's own SVGs, injected by the class filter.
   Their label is the link text, hidden the way v6 hides it. */
body.pag-v6 .foot-social .elementor-button-content-wrapper{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
body.pag-v6 .foot-social{ flex-direction:row; }
body.pag-v6 .foot-social a{ display:block; line-height:0; padding:0; background:none; }
body.pag-v6 .foot-social svg{ width:19px; height:19px; display:block; }

/* ------------------------------------------------------------
   25. A STICKY HEADER
   A DELIBERATE CHANGE FROM v6.1.2, asked for on 26 Aug so the nav behaves like
   the one on the current site. v6.1.2's nav scrolls away; this pins it.

   `sticky`, not `fixed`. The live v4.7 nav is `position:fixed`, which takes it
   out of flow — every following block moves up by its height and has to be
   padded back down. Sticky keeps the nav in flow, so the page geometry is
   exactly what was measured against v6.1.2 and only the behaviour changes.

   The header template's own wrapper has to go first. Elementor renders the
   template inside `<div class="elementor elementor-148">`, and a sticky element
   sticks within its nearest scrolling ancestor — that div, which is only as
   tall as the nav itself, so it would pin against nothing and do nothing at
   all. `display:contents` makes the nav a child of <body> and the viewport the
   thing it sticks to.

   The ground colour is not decoration: without it the panels below scroll
   through the nav, and v6's nav has no background of its own because in the
   original nothing ever passes behind it.
   ------------------------------------------------------------ */
body.pag-v6 > .elementor[data-elementor-type="section"]:has(> .nav){ display:contents; }

body.pag-v6 .nav{
  position:sticky;
  top:0;
  z-index:60;
  background:var(--ground);
}

/* ------------------------------------------------------------
   26. DELIBERATE CHANGES TO v6.1.2, 26 Aug 2026
   Everything above this line re-points a v6 rule that Elementor broke. This
   block does not: it CHANGES the design, on instruction. Kept here rather than
   edited into pag-v6-home.css so that file stays byte-identical to v6.1.2 and
   the two can still be diffed.
   ------------------------------------------------------------ */

/* 26.1 · The sticky bar runs the full width of the window.
   `.nav` is inset by the page gutter, so its background stopped 57px short of
   each edge and the panels' drop shadows showed past it as they scrolled под.
   The bar is drawn by a pseudo-element pinned to the viewport width instead of
   the nav's own box, so the nav keeps its measured geometry — the gutter still
   positions the logo and the links exactly where v6 puts them. */
body.pag-v6 .nav{ background:none; }
body.pag-v6 .nav::before{
  content:'';
  position:absolute;
  top:0; bottom:0;
  left:50%;
  width:100vw;
  transform:translateX(-50%);
  background:var(--ground);
  z-index:-1;
}

/* Nothing in v6 goes above z-index 3, so 60 clears the panels and their
   shadows with room to spare. */
body.pag-v6 .nav{ z-index:60; }

/* 26.2 · The sticky bar's own height, measured. Constant at every width —
   12px of padding, a 49.48px mark, 24px of padding. Declared once here so the
   pinned section below can offset itself by it rather than repeating a number. */
body.pag-v6{ --nav-h:85.5px; }

/* 26.3 · Tighter spacing in the platform panel. */
body.pag-v6 .tlstage .head{ margin-bottom:calc(var(--s6) + 2px); }
body.pag-v6 .tl{ margin:0 0 var(--s5); }

/* 26.4 · The pinned section clears the sticky bar.
   v6 pins it at `top:var(--s4)` — 16px from the top of the window, which was
   the top of the page. With a bar sitting there the panel slid underneath it.
   The offset is now the bar plus that same 16px, so the daylight above the
   panel is what v6 intended and the bar is simply where the window edge was. */
body.pag-v6 .tlstage{ top:var(--tl-top, calc(var(--nav-h) + var(--s4))); }

/* 26.5 · Anything jumped to by a #hash — the skip link's `#main` is the one on
   this page — lands below the bar rather than behind it. */
body.pag-v6 [id]{ scroll-margin-top:calc(var(--nav-h) + var(--s4)); }

/* 26.6 · Looser rhythm between the sections, and a tighter gap between the two
   closing cards. All three currently sit on --s5 (24px); the vertical spacing
   goes to --s6 (32px) and the closing pair's gap comes down to --s5.

       .panel       margin-bottom  24 -> 32
       .hero        margin-bottom  24 -> 32
       .close-grid  margin-bottom  24 -> 32   ·   gap  32 -> 24                */
body.pag-v6 .panel{ margin:0 auto var(--s6); }
body.pag-v6 .hero{ margin:0 auto var(--s6); }
body.pag-v6 .close-grid{ gap:var(--s5); margin:0 auto var(--s6); }

/* 26.7 · `.open` joins them, confirmed 26 Aug. It is the inner pages' hero and
   shared.css leaves it on --s5, so without this the band under the nav would
   sit 8px tighter on About and Contact than the hero does on the homepage —
   the one place the difference would actually be seen, since the two are the
   same component at two sizes. Nothing renders it yet; it lands when the first
   inner page is built. */
body.pag-v6 .open{ margin:0 auto var(--s6); }

/* 26.8 · The three items in each "difference it makes" card sit 32px apart,
   not 48px. Asked for on 27 Aug.

   Each row carries --s5 (24px) of padding top and bottom, so two neighbouring
   rows put 48px between them. --s4 (16px) makes that 32px.

   The last row's `padding-bottom:0` is RESTATED below. It comes from
   `.diff-row:last-of-type`, which weighs (0,2,0), and the rule above it here
   weighs (0,2,1) — so without the restatement 16px would go back under the
   last row and every card would grow by exactly that much.

   The gap under the card's own title lands at 40px, asked for on 27 Aug: the
   24px `.diff-head` margin in pag-v6-home.css, left alone, plus the first
   row's 16px. It is NOT overridden here — the number falls out of the design
   file's own value, so there is one fewer rule to keep in step.

   `.diff-row:first-of-type{padding-top:0}` never applied on this build and
   still does not — `.diff-head` is the first div in the card, so the first
   ROW is not the first element of its type. Nothing here depends on it.    */
body.pag-v6 .diff-row{ padding-top:var(--s4); padding-bottom:var(--s4); }
body.pag-v6 .diff-row:last-of-type{ padding-bottom:0; }

/* ------------------------------------------------------------
   27. THE THEME'S LINK-HOVER COLOUR
   Hello Elementor's reset.css ships `a:active,a:hover{color:#336}` at
   specificity 0,1,1. v6.1.2 has no such rule.

   It only surfaces on ONE button. Every other v6 variant sets its own hover
   colour — `.btn-secondary:hover{color:var(--deep)}`, `.link:hover{color:
   var(--azure)}`, `.btn-ghost:hover` — and those beat the theme's. But
   `.btn-primary:hover` deliberately changes ONLY the background, leaving the
   colour to `.btn-primary{color:var(--white)}` at 0,1,0 — which loses. So
   "Contact Us" in the closing box, and the hero's primary button, went dark
   navy on hover instead of staying white.

   Scoped to `.btn-primary` alone, and written on the BASE state rather than
   the hover state: at 0,2,1 it outranks the theme in every state, and because
   it does not mention `:hover` it cannot override any variant that legitimately
   changes colour on hover. Nothing else on the page shares it.
   ------------------------------------------------------------ */
body.pag-v6 a.btn-primary{ color:var(--white); }

/* ------------------------------------------------------------
   28. THE INNER PAGES' OPENING BAND
   `.open` in shared.css is the hero's smaller sibling and it is built the same
   way — `display:flex; align-items:flex-end`, with `.open-bg` and `.open-scrim`
   absolutely positioned inside it. So it needs the two fixes the hero needed,
   for the same two reasons.

   Written here rather than in a page bridge because About, Contact and every
   other inner page share it. Written BEFORE either page was built, from the
   hero's proven pattern rather than from measurement — so if a page turns up a
   third thing, add it here and say so, don't work around it in a page bridge.
   ------------------------------------------------------------ */

/* An Elementor container is a flex COLUMN, and in a column `align-items`
   governs the horizontal axis — so `flex-end` applied, computed correctly, and
   the text still sat at the top. See §26.4. */
body.pag-v6 .open{ align-items:flex-end; flex-direction:row; }

/* `.open-bg` and `.open-scrim` are `position:absolute; inset:0` against
   `.open`. Elementor's wrapper would be the positioned ancestor instead — a box
   whose only children are absolute, so it has no height, so `inset:0` resolves
   to nothing and the photograph renders 0px tall. It does not fail visibly in
   any measurement: `.open` has its own `min-height`, so every number is right
   with no picture in it. See §23. */
body.pag-v6 .open > .elementor-widget-html,
body.pag-v6 .open > .elementor-widget-html > .elementor-widget-container{ display:contents; }

/* The band's buttons sit in a flex row, same as the hero's. */
body.pag-v6 .open-cta{ flex-direction:row; }
body.pag-v6 .open-cta > .elementor-element{ width:auto; flex-basis:auto; }

/* v6 turns the band's link white on hover through `.open a:hover .link`,
   because there the whole band can be the link. Our bands are containers with
   stretched inner links, so `a:hover` never fires — the same gap the option
   cards had. See §26. */
body.pag-v6 .open:hover a.link{ color:#fff; }


/* ------------------------------------------------------------
   29. THE INLINE LINK KEEPS ITS UNDERLINE
   `.elementor a{text-decoration:none}` is (0,1,1); v6's `.ilink` is (0,1,0), so
   Elementor won and the underline was gone from the details card, the platform
   row and the consent line's Privacy Policy.

   That underline is a decision, not decoration. Section 24 of the design system
   records that the site has no style for a link inside a sentence — `.link` is
   a standalone call to action with an arrow, a different object — and in
   running text colour alone is not enough. On the details card in particular
   the "link" colour is white on blue, which is the colour of everything else on
   it.

   `text-decoration` is a SHORTHAND, so `none` also reset the colour to
   `currentColor` — v6 draws that underline at 38% of Deep Blue, and a full
   strength one under running text is a different, heavier thing. All three come
   back; the offset and the transition were never touched.

   THE SPECIFICITY IS THE WHOLE PROBLEM AND `:where()` IS THE WHOLE ANSWER.
   `body.pag-v6 .ilink` would be (0,2,0) — which beats `.elementor a`, but also
   TIES `.detail .ilink` (0,2,0) and `.ilink:hover` (0,2,0), and the bridge
   loads last, so it would win both ties and take the card's white underline and
   the hover with it. `:where(body.pag-v6) a.ilink` weighs (0,1,1): it ties
   `.elementor a` and wins on order, and it loses to every v6 rule that means to
   say something else. One value restated, and the two rules entitled to
   override it still do.

   Nearly invisible either way: five rows of the pixel diff, 0.04% of one
   section on Contact, one row per link on the legal pages, and not a pixel of
   it anywhere a measurement or `textdiff.mjs` looks.

   SHARED, not Contact's, though that is where it was found. `.ilink` is v6's
   inline link and three pages use it — the consent line and the details card on
   Contact, and inside a clause and the aside card on Privacy and Terms. The
   legal pages showed it again after Contact was already finished, which is what
   moved it here.
   ------------------------------------------------------------ */
:where(body.pag-v6) a.ilink{
  text-decoration-line:underline;
  text-decoration-thickness:1px;
  text-decoration-color:color-mix(in srgb, var(--deep) 38%, transparent);
}


/* ------------------------------------------------------------
   30. CF7'S CONSENT CHECKBOX
   `.consent` is a two-column grid — `auto minmax(0,1fr)` — and v6 puts the bare
   `<input type="checkbox">` in the first cell. CF7's `[acceptance]` puts three
   elements between the cell and the input, and its own stylesheet gives the
   innermost one `display:inline-block; margin:0 0 0 1em`.

   Both halves of that hurt. The 1em left margin makes the first column 32px
   instead of 18, so the label starts 14px right of where v6 draws it — and
   `inline-block` adds the baseline's descender space under the box, so the row
   is 27.09px tall where v6's is 22.09. Two lines of label hide it at 1440,
   because the label is then taller than either; at 900 the label is one line
   and the checkbox decides the row.

   Made block, and the margin dropped. The input keeps the 2px top nudge v6
   gives it, which is what lines it up with the first line of the label rather
   than with the top of the text box.

   SHARED, not Contact's, though that is where it was found. `.consent` moved up
   into shared.css on 26 August so Register could use the form vocabulary rather
   than growing a second copy of it — and what bridges CF7's markup to that
   vocabulary belongs in the same place, by the same argument. Register measured
   `.consent` at 28px against v6.1.2's 24 without these, and 23 with them.

   THE REMAINING 1px ON REGISTER IS v6.1.2's OWN INCONSISTENCY, not this rule.
   `shared.css:405` styles `.consent label`, and Contact's markup is
   `.consent > [acceptance] + <label>` so it hits. register.html has no label at
   all — `.consent` IS the label there, and its text sits in a plain `<span>` at
   the inherited body size. CF7 emits a `<label for>` either way and the
   checkbox needs the association, so Register's row reads 23 where the static
   reads 24. The shared vocabulary applying consistently is the better outcome
   than restating a font size to match one page's markup accident.
   ------------------------------------------------------------ */
body.pag-v6 .consent .wpcf7-form-control-wrap,
body.pag-v6 .consent .wpcf7-acceptance,
body.pag-v6 .consent .wpcf7-list-item,
body.pag-v6 .consent .wpcf7-list-item > label{ display:block; margin:0; }

/* AND THE INPUT ITSELF, which is the half the margin did not explain. Dropping
   the 1em fixed the column and left the row still 27.09px, because a checkbox
   is `inline-block` and CF7 wraps it in a label — so it sits on a LINE BOX, and
   the line box is the label's line-height plus the box's own 2px nudge plus the
   descender space under it.

   In v6 there is no wrapping label: the input is a direct child of a grid, and
   a grid item is blockified. Saying so here is not adding a rule, it is
   restoring the one v6's markup used to imply. */
body.pag-v6 .consent input[type="checkbox"]{ display:block; }


/* ═══════════════════════════════════════════════════════════════════════════
   §31 · THE HEADER CART CONTROL

   v6.1.2 has no cart, because a static site has none to have. The shop does,
   and a visitor who has just added a plan needs to see that it happened and
   needs a way back to it — so the header carries the same control v4.7's did,
   drawn in v6's own terms.

   THE MARKUP IS SHARED. `pag_cart_link_shortcode()` in inc/pag-woo.php writes
   it for both design systems: a link, a masked icon, a count. It is
   design-agnostic on purpose — the two stylesheets colour it, and there is one
   place the accessible name is written rather than two that can drift.

   IT RENDERS NOTHING WHILE THE CART IS EMPTY. That is the whole reason the
   layout below is shaped the way it is: empty is the state v6.1.2 is measured
   in, so in that state every one of the nine pages must be pixel-identical to
   the static, and the control must cost exactly zero boxes.
   ═══════════════════════════════════════════════════════════════════════════ */

/* THE ROW. `.nav` is a three-column grid and the static's `.nav-cta` names
   column 3 — so a second child asking for the same column would sit ON TOP of
   the button rather than beside it. The two share a flex row instead, and the
   column placement moves onto the row.

   `.nav-cta` keeps `grid-column:3;justify-self:end` from the shared stylesheet.
   Inside a flex parent both are simply ignored, which is why the shared file
   needs no edit — and it must not have one: it is a byte-for-byte copy. */
/* `body.pag-v6`, NOT `:where(body.pag-v6)`, and the difference is the whole
   rule working or not. Elementor sets its container's direction through a
   custom property — `.e-con.e-flex{--flex-direction:column}` and
   `.e-con-full.e-flex{flex-direction:var(--flex-direction)}`, both (0,2,0). A
   `:where()`-scoped rule is (0,1,0) and loses to that however late it loads,
   which is exactly what happened: the declaration was there, the cascade
   ignored it, and the cart sat stacked on top of the button.

   `:where()` is the right default in this file because most rules here should
   lose to v6's own. This one has no v6 rule to lose to — `.nav-actions` is a
   WordPress-only wrapper that the static has no equivalent of — so it can and
   must carry real weight. */
body.pag-v6 .nav-actions{
  grid-column:3; justify-self:end;
  display:flex; align-items:center; gap:var(--s4);
  /* TWO ELEMENTOR DEFAULTS, and this file has been caught by both before.

     `flex-direction:row` — an Elementor container is a flex COLUMN. Saying
     `display:flex` and stopping left the cart stacked ON TOP of the button and
     the nav 36px taller on every page, which reads as a spacing choice rather
     than as a bug.

     `width:auto` — an Elementor container is `width:100%`. In a grid cell that
     resolves against the whole track, so `justify-self:end` had nothing to do:
     the row was already the full 298px of column 3 and the button sat wherever
     the flex rules put it inside that. Same trap as §12's `.foot-col`, which
     ran the footer 6px past the right edge for the same reason. */
  flex-direction:row; width:auto;
}

/* ZERO BOXES WHEN EMPTY, and this is the rule that makes the claim above true.

   `display:contents` removes the widget wrapper from the box tree entirely, so
   when the shortcode returns an empty string there is no flex item at all — and
   `gap` only applies BETWEEN items, so a single-child row has none. Without it
   the empty wrapper would still be an item and the 16px gap would push the
   header 16px across on all nine pages, every one of which is measured against
   a static that has no cart.

   This is v4.7's fix, for v4.7's reason, and it is worth keeping the note: the
   failure is silent, uniform, and looks like a design decision. */
body.pag-v6 .nav-cart,
body.pag-v6 .nav-cart > .elementor-shortcode{ display:contents; }

/* THE CONTROL. Sized and coloured off `.nav-links a`, which is what it sits
   beside, so it answers the pointer the way the rest of the bar does. */
:where(body.pag-v6) .nav-cart__link{
  position:relative; display:inline-flex; align-items:center;
  color:var(--muted); text-decoration:none;
  transition:color var(--fast) var(--ease);
}
:where(body.pag-v6) .nav-cart__link:hover{ color:var(--deep); }
:where(body.pag-v6) .nav-cart__link:focus-visible{
  outline:2px solid var(--deep); outline-offset:4px; border-radius:var(--r-chip);
}

/* The mark, drawn as a mask and tinted with currentColor — the technique this
   site already uses for its button arrows, its nav toggle and its social
   marks, so the cart is not the one icon that works a different way. */
:where(body.pag-v6) .nav-cart__icon{
  display:block; width:22px; height:22px; flex:none;
  background-color:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 8.5h11l-1.1 8.2a2 2 0 0 1-2 1.8H9.6a2 2 0 0 1-2-1.8Z'/%3E%3Cpath d='M9.4 8.5V6.9a2.6 2.6 0 0 1 5.2 0v1.6'/%3E%3C/svg%3E") center / 22px 22px no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 8.5h11l-1.1 8.2a2 2 0 0 1-2 1.8H9.6a2 2 0 0 1-2-1.8Z'/%3E%3Cpath d='M9.4 8.5V6.9a2.6 2.6 0 0 1 5.2 0v1.6'/%3E%3C/svg%3E") center / 22px 22px no-repeat;
}

/* THE COUNT, riding the mark's top-right corner rather than sitting beside it —
   which is what a badge is, and keeps the whole control 22px wide however many
   plans are in the basket.

   SIGNAL RED, and this is what the palette reserves it for: "marks only, rare".
   There is no other red on a v6 page, so the one mark that means "something has
   changed since you last looked" is unmistakable and is not competing with the
   Deep Blue button 16px to its right. The ring is the page ground, so the badge
   reads as sitting ON the mark rather than punched into it. */
:where(body.pag-v6) .nav-cart__n{
  position:absolute; top:-6px; right:-9px;
  display:inline-flex; align-items:center; justify-content:center;
  min-width:17px; height:17px; padding:0 5px; border-radius:var(--r-pill);
  font:600 10.5px/1 var(--font-display); letter-spacing:.01em;
  color:#fff; background:var(--red);
  box-shadow:0 0 0 2px var(--ground);
}

/* ── NARROW WINDOWS ──
   Below 1140 the shared stylesheet collapses the bar to `1fr auto`, hides the
   links and the CTA, and shows the toggle. The cart must survive that: a
   basket you cannot see on a phone is a basket you forget you filled.

   `display:contents` on the row hands the cart link and the CTA back to `.nav`
   as direct grid children, so every rule the shared file already writes about
   `.nav-cta` — hidden when closed, `grid-column:1/-1` when the menu is open —
   keeps working untouched. */
@media (max-width:1140px){
  body.pag-v6 .nav-actions{ display:contents; }

  /* `:has()` IS THE POINT OF THIS RULE, not a flourish.

     A third column costs a `column-gap` whether or not anything occupies it, so
     writing it unconditionally would move the mobile header on all nine pages —
     in the empty state, which is the state they are measured in. The selector
     only matches once the shortcode has actually rendered a link, so a page
     with an empty cart is untouched to the pixel and a page with a full one
     gains exactly the room it needs. */
  body.pag-v6 .nav:has(.nav-cart__link){ grid-template-columns:1fr auto auto; }
  /* `grid-row:1` ON BOTH, and it is not decoration.

     Auto-placement walks the DOM in order and never goes backwards: brand
     lands in row 1 column 1, the toggle names column 3, and by the time the
     cart — which comes after the toggle in the markup — asks for column 2, the
     cursor is already past it. The grid opens a SECOND ROW rather than
     reaching back, and the bar came out 38px taller with the cart alone on a
     line underneath the logo.

     Naming the row removes the question. The DOM order itself is deliberate
     and is not the thing to change: the toggle comes before the menu it opens
     because that is the order a keyboard should meet them in. */
  body.pag-v6 .nav:has(.nav-cart__link) .nav-toggle{ grid-column:3; grid-row:1; }
  body.pag-v6 .nav:has(.nav-cart__link) .nav-cart__link{
    grid-column:2; grid-row:1; justify-self:end; align-self:center;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   §32 · THE PARENT THEME PAINTS EVERY <button> ON HOVER AND ON FOCUS

   Hello Elementor's reset.css:

       [type=button]:focus,[type=button]:hover,[type=submit]:focus,
       [type=submit]:hover,button:focus,button:hover{
         background-color:#c36; color:#fff; text-decoration:none }

   `#c36` is #CC3366, and it is unopposed on any `<button>` this site does not
   itself recolour on hover — which is nearly all of them, because v6's controls
   answer a pointer with transform, shadow and border-colour rather than a fill.

   It was found on Register, where `.pick:hover` turned a course card magenta
   with white text, and fixed there in that page's own bridge. That was the
   right size of fix for what was known then and the wrong size now: the rule
   lives in the PARENT theme, so it reaches every `<button>` on all nine pages
   and on every WooCommerce screen — the coupon toggle, the quantity steppers,
   Place Order, the payment accordion, the billing switch, the five platform
   steps, the enquiry form's submit. Each was one hover from magenta.

   ── WHY THIS IS NOT ONE RULE ──

   The obvious fix — reset every button at a weight the theme cannot reach —
   does not work, and the reason is worth writing down because it is not
   obvious until you try it.

   The theme's selector for `<button type="button">` is `[type=button]:hover`,
   which is (0,2,0). To beat it you need (0,2,1). But (0,2,1) also beats every
   rule v6 writes about a button, because v6 writes them as single classes:
   `.switch-btn:hover` is (0,2,0), `.back:hover` is (0,2,0), `.btn-primary` is
   (0,1,0). So a blanket reset does not undo the theme — it undoes the theme AND
   the design, and the damage is worse than the bug because it is spread across
   controls nobody thought to re-check.

   There is no weight above (0,2,0) and below (0,1,0). Cascade layers do not
   help either: the theme's rule is unlayered, and an unlayered rule beats a
   layered one whatever its specificity.

   So the undo is scoped to `:hover`/`:focus` — the only states the theme
   touches — and every fill v6 legitimately owns is RESTATED immediately after
   it, in this file, where a specificity tie goes to the later rule.

   ── THE LIST IS COMPLETE, AND IT WAS MEASURED ──

   Every real `<button>` and `[type=submit]` on all nine v6 pages and the four
   shop screens was enumerated before this was written, rather than recalled:

       button.btn.btn-primary            contact — the enquiry submit
       button.switch-btn                 subscription-plans — the billing switch
       button.pick                       register — the three course cards
       button.back                       register — "Change course"
       button (no class) in .fitem       platform — the five pinned steps
       button.nav-toggle                 every page, below 1140
       button.woocommerce-button, and    the shop screens
         the wc-block buttons

   The shop's are not restated here: `pag-v6-woo.css` sets them on the base
   state at (0,2,1) and loads after this file, so it already wins in every
   state. `.pick` and `.back` are restated because Register's bridge writes them
   at (0,2,0) and would lose to the undo below.

   A NEW `<button>` NEEDS A LINE HERE. That is the cost of this shape, and it is
   the smaller cost — `verify/hover.mjs` is what finds one that is missing, and
   nothing that measures a page at rest ever will.
   ═══════════════════════════════════════════════════════════════════════════ */

/* THE UNDO. (0,3,1) — comfortably past the theme, deliberately past everything,
   because what follows puts back the things it should not have taken. */
body.pag-v6 :is(button,[type="button"],[type="submit"],[type="reset"]):hover,
body.pag-v6 :is(button,[type="button"],[type="submit"],[type="reset"]):focus{
  background-color:transparent;
  color:inherit;
}

/* ── AND NOW EVERY FILL v6 ACTUALLY OWNS ──

   IN TWO GROUPS, BECAUSE FOCUS IS NOT HOVER.

   The first version of this collapsed the two into one declaration per control,
   and that was wrong in a way worth recording. The theme fires on both states,
   so the undo has to cover both — but the RESTATEMENT does not, because v6
   never changes a background or a colour on focus. Grepping the four v6
   stylesheets for `:focus` on any of these controls returns `.pick:focus-visible`
   and `.back:focus-visible`, and both set an OUTLINE and nothing else.

   That distinction is deliberate design, not an omission. A control the
   keyboard has merely reached has not been chosen; drawing it as though the
   pointer were on it tells a keyboard user something untrue about where they
   are.

   Caught by primeacg2 on `.back` — measured three ways with the state forced
   over CDP: design `focus MUTED`, live with their page-level correction in
   place `focus MUTED`, live with it removed `focus DEEP BLUE`. Their fix was a
   correction to THIS file living in a page bridge, so it is folded in here and
   the same error is repaired in the four other controls that had it:
   `.btn-primary`, `.btn-secondary`, `.btn-ghost` and `.switch-btn` were all
   drawing their hover state on focus alone.

   `:focus:not(:hover)` is (0,4,2) and the hover rules are (0,3,2), so when both
   are true — pointer on a focused control — hover wins, which is right. ── */

/* ── group one · THE BASE STATE, restored when the keyboard has merely
      arrived. Every value here is the control's own resting value, copied from
      the stylesheet that owns it. ── */
body.pag-v6 :is(button,[type="submit"]).btn-primary:focus:not(:hover){
  background-color:var(--deep); color:var(--white);
}
body.pag-v6 :is(button,[type="submit"]).btn-secondary:focus:not(:hover){
  background-color:var(--panel); color:var(--ink);
}
body.pag-v6 :is(button,[type="submit"]).btn-ghost:focus:not(:hover){
  background-color:color-mix(in srgb, #fff 12%, transparent); color:#fff;
}
body.pag-v6 button.nav-toggle:focus:not(:hover){ background-color:var(--panel); color:var(--ink); }
body.pag-v6 button.switch-btn:focus:not(:hover){ background-color:transparent; color:var(--muted); }
body.pag-v6 button.switch-btn[aria-pressed="true"]:focus:not(:hover){
  background-color:var(--deep); color:#fff;
}
body.pag-v6 button.pick:focus:not(:hover){ background-color:var(--panel); color:inherit; }
body.pag-v6 button.back:focus:not(:hover){ background-color:transparent; color:var(--muted); }

/* ── group two · THE HOVER STATE, which is the only one v6 actually draws ── */

body.pag-v6 :is(button,[type="submit"]).btn-primary:hover{
  background-color:var(--deep-press); color:var(--white);
}
body.pag-v6 :is(button,[type="submit"]).btn-secondary:hover{
  background-color:var(--panel); color:var(--deep);
}
body.pag-v6 :is(button,[type="submit"]).btn-ghost:hover{ background-color:#fff; color:var(--deep); }

/* THE DARK-PANEL VARIANT, because `.btn-secondary` means something different
   there: `.deep .btn-secondary:hover{background:#fff;color:var(--navy)}` and
   `.close-card--deep .btn-secondary:hover{background:color-mix(#fff 86%,azure)}`.
   Neither is a `<button>` today — every one of them is an Elementor Button,
   which renders an `<a>` — so these two lines change nothing now. They are here
   because the rules above are (0,3,2) and would silently outrank both the day
   somebody makes one a real button, and a list that is complete for the
   variants the design HAS is worth more than one that is complete for the
   markup as it stands this afternoon. */
body.pag-v6 .deep :is(button,[type="submit"]).btn-secondary:hover{
  background-color:#fff; color:var(--navy);
}
body.pag-v6 .close-card--deep :is(button,[type="submit"]).btn-secondary:hover{
  background-color:color-mix(in srgb, #fff 86%, var(--azure)); color:var(--deep);
}

/* The nav toggle, `.pick` and the pressed half of the switch change nothing on
   hover — their hover value IS their base value — so one rule covers both
   states for each of them. */
body.pag-v6 button.nav-toggle:hover{ background-color:var(--panel); color:var(--ink); }
body.pag-v6 button.pick:hover{ background-color:var(--panel); color:inherit; }
body.pag-v6 button.switch-btn[aria-pressed="true"]:hover{ background-color:var(--deep); color:#fff; }

/* The two that go Deep Blue in TEXT on hover and keep no fill either way. */
body.pag-v6 button.switch-btn:hover{ background-color:transparent; color:var(--deep); }
body.pag-v6 button.back:hover{ background-color:transparent; color:var(--deep); }

/* The platform's five steps are `<button type="button">` with no class, styled
   through `.fitem button{background:none;color:inherit}` — the undo's own
   values, in both states. Named here only so the next reader does not go
   looking for the line that is missing. */


/* ═══════════════════════════════════════════════════════════════════════════
   §33 · WOOCOMMERCE'S NOTICES ON A v6 CONTENT PAGE

   `/subscription-plans/` performs an add-to-cart now, so it can be told things:
   that the plan went in, or that it is already there and cannot go in twice.
   Until today no v6 page could raise a WooCommerce notice and none had anywhere
   to print one — the notices queued in the session and surfaced later, on
   whichever page happened to render them, which was `/my-account/`.

   `pag_v6_notices()` prints them between the nav and the first panel, and emits
   NOTHING when the queue is empty. These rules therefore never apply on a quiet
   page, which is what keeps the nine measured pages pixel-identical.

   The look is `.form-error`'s from shared.css — a tinted ground, a matching
   border at 26%, `--r-chip`, and the ink mixed toward navy so the page stays
   one temperature. The same three tempers `pag-v6-woo.css` gives the shop, said
   once more here because that file loads on shop screens only and this is the
   other side of that boundary.
   ═══════════════════════════════════════════════════════════════════════════ */

body.pag-v6 .pag-v6-notices{
  max-width:var(--frame); margin:0 auto var(--s5);
  display:flex; flex-direction:column; gap:var(--s3);
}
body.pag-v6 .pag-v6-notices .woocommerce-message,
body.pag-v6 .pag-v6-notices .woocommerce-info,
body.pag-v6 .pag-v6-notices .woocommerce-error{
  margin:0; padding:var(--s4) var(--pad); list-style:none;
  border:1px solid var(--hair); border-radius:var(--r-panel);
  background:var(--panel);
  font-size:var(--t-text); line-height:var(--lh-text); color:var(--muted);
  display:flex; align-items:center; justify-content:space-between;
  flex-wrap:wrap; gap:var(--s3) var(--s5);
}
/* SUCCESS and INFO in the site's own blue — the palette has one accent for
   "this is fine" and it is Deep Blue. */
body.pag-v6 .pag-v6-notices .woocommerce-message,
body.pag-v6 .pag-v6-notices .woocommerce-info{
  background:color-mix(in srgb, var(--deep) 6%, var(--white));
  border-color:color-mix(in srgb, var(--deep) 26%, transparent);
  color:color-mix(in srgb, var(--deep) 72%, var(--navy));
}
/* ERROR takes `.form-error`'s exact values. #E53935 on white is 4.0:1 and fails
   on body text; mixed toward navy it is 6.5:1. */
body.pag-v6 .pag-v6-notices .woocommerce-error{
  background:color-mix(in srgb, var(--red) 6%, var(--white));
  border-color:color-mix(in srgb, var(--red) 26%, transparent);
  color:color-mix(in srgb, var(--red) 72%, var(--navy));
}
/* WooCommerce puts an icon at the notice's top-left which lands on the first
   letter under this padding, and renders `.woocommerce-error` as a <ul>. */
body.pag-v6 .pag-v6-notices li{ list-style:none; margin:0; }
body.pag-v6 .pag-v6-notices .woocommerce-message::before,
body.pag-v6 .pag-v6-notices .woocommerce-info::before,
body.pag-v6 .pag-v6-notices .woocommerce-error::before{ display:none; }

/* The action a notice carries — "View cart", "Continue shopping" — is `.button`,
   which this design system has no rule for outside the shop. `.btn-secondary`'s
   values, since a notice's action is never the page's primary one. */
body.pag-v6 .pag-v6-notices .button{
  flex:none; order:2;
  display:inline-flex; align-items:center; gap:var(--s2);
  font:600 .9375rem/1 var(--font-display); letter-spacing:.01em;
  padding:11px 20px; border-radius:var(--r-pill);
  border:1px solid var(--hair); background:var(--panel); color:var(--ink);
  text-decoration:none; white-space:nowrap;
  transition:border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
body.pag-v6 .pag-v6-notices .button:hover{ border-color:var(--deep); color:var(--deep); }


/* ═══════════════════════════════════════════════════════════════════════════
   §34 · THE TINTED CARDS' GRADIENT PAINTS OVER THEIR OWN CONTENTS

   Client items 2 and 6, and they are one bug in two skins: the closing CTA card
   on every page, and the middle plan card on Subscription Plans.

   Both are a deep-blue card with a decorative radial gradient in a `::before`,
   and both keep that gradient BEHIND their contents the same way:

       .close-card--deep::before{ position:absolute; inset:0; … }
       .close-card--deep > *    { position:relative }

   An absolutely-positioned `::before` paints above in-flow content, so the
   second line is what saves it: making each child positioned lifts it back over
   the gradient. It works on the static site because the children ARE the
   heading, the lead and the button.

   IN ELEMENTOR THEY ARE NOT. Each child is a widget wrapper, and this bridge
   sets several widget types to `display:contents` — so those wrappers have no
   box, `position:relative` on them does nothing at all, and the real elements
   inside are one level too deep for `> *` to reach. Measured: on the closing
   card the button's wrapper is `display:contents` and the `<a>` inside it is
   `position:static`; on the plan card most of the contents are.

   The visible result is the one reported — the gradient washing across the
   button and dulling it.

   FIXED AT THE GRADIENT, NOT THE CHILDREN. Restating `position:relative` for
   every element the wrappers hid would mean naming them, and the list changes
   whenever a widget type is added to the `display:contents` set — a fix that
   rots quietly. A negative z-index on the `::before` puts it behind ALL of the
   card's content and depends on nothing about the markup: negative-index
   children paint after the parent's background and border, and before its
   in-flow content, which is exactly where a decorative wash belongs.

   `isolation:isolate` is not optional beside it. The card is `position:relative`
   with `z-index:auto`, so it does NOT create a stacking context of its own —
   without isolating it, a `z-index:-1` child is free to paint behind the CARD's
   own background and disappear entirely. It creates the context the negative
   index needs to stay inside.
   ═══════════════════════════════════════════════════════════════════════════ */
body.pag-v6 :is(.close-card--deep, .plan--featured){ isolation:isolate; }
body.pag-v6 :is(.close-card--deep, .plan--featured)::before{ z-index:-1; }


/* ═══════════════════════════════════════════════════════════════════════════
   §35 · THE HOMEPAGE TIMELINE'S TRACK RAN PAST ITS LAST STOP

   Client item 1: the connecting line carries on past the sixth step,
   Certificate, and trails off into empty space.

   The design draws the track as `.tl::before` with NO width — it is sized by
   two insets, `left:8.3333%` and `right:8.3333%`, which is one twelfth at each
   end of a six-column grid. That lands it exactly on the centre of the first
   badge and the centre of the last.

   `.tl` is an Elementor container here, so `.e-con::before` reaches it — and
   Elementor's container overlay sets `width:100%`. A box with `left`, `right`
   AND `width` ignores one of them: the width wins and the right inset stops
   meaning anything. The track then spans the full 1210px of the grid instead
   of 1008, and the extra 100px hangs past the last badge.

   Measured, and the numbers say it exactly:

       design  left 100.797  right 100.797  width 1008.03
       live    left 100.797  right 100.797  width 1209.62

   Identical insets, different width, which is only possible if the width is
   set. The progress FILL was always right, because `.tl::after` states its own
   width and so was never open to the same override — which is why the line
   looked correct until it was full.

   `width:auto` gives the insets back their meaning. Nothing else changes.

   SCOPED ABOVE 880, AND THAT IS LOAD-BEARING. Below it the design turns the
   timeline vertical and states `.tl::before{inset:28px auto 28px 27px;
   width:2px}` inside `@media (max-width:880px)`. An unscoped bridge rule at
   (0,2,1) would beat that (0,1,0) media rule and force the vertical rail to
   `auto`, which is the trap this file has hit before: a rule with no media
   query outranks a design rule that has one. */
@media (min-width:881px){
  body.pag-v6 .tl::before{ width:auto; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   §36 · A SECOND, LARGER BUTTON AROUND THE MENU'S BUTTON

   Client item 8: open the menu on a tablet or phone and the Start Training
   button inside it is far too large for the panel.

   It is not one button drawn wrong. It is TWO buttons, one inside the other.

       the <a>      184x47   correct, identical to the design
       its wrapper  238x79   blue, padded, and painted like a button

   `pag_v6_forward_classes()` COPIES a widget's classes onto the element inside
   rather than moving them, so `btn btn-primary nav-cta` is on both the wrapper
   and the anchor. That is deliberate and everything else depends on it — but it
   means the wrapper will paint as a button the moment it has a box.

   Everywhere else it has none: `body.pag-v6 .elementor-widget-button
   {display:contents}` removes it from the box tree. In the OPEN MOBILE MENU it
   comes back, and the cascade is doing exactly what it should:

       .nav[data-open] .nav-cta          (0,3,0)   display:inline-flex
       body.pag-v6 .elementor-widget-button (0,2,1)   display:contents

   An attribute selector counts as a class, so `[data-open]` makes the design's
   rule (0,3,0) and it legitimately outranks the bridge. The wrapper gets a box,
   inherits the button's own look from the copied classes, and — being a grid
   item at `grid-column:1/-1` — stretches to 238 and wraps its text to 79.

   So the fix is not to fight the design's rule but to be more specific than it
   in the one place it collides. `display:contents` again, at (0,5,1), scoped to
   the open menu. The anchor then becomes the grid item and picks up
   `grid-column:1/-1` and `justify-self:start` itself, which is what it was
   always supposed to do.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width:1140px){
  body.pag-v6 .nav[data-open] .elementor-widget-button.nav-cta{ display:contents; }
}


/* ------------------------------------------------------------
   37. THE CARD PHOTOGRAPHS ARE ALL ONE SHAPE, 27 Aug 2026

   RENUMBERED FROM 31 ON MIRRORING. It was written as §31 against a copy of this
   file that ended at §30; six more sections (§31–§36) had been added here in the
   meantime, so it arrived carrying a number already in use and sitting after
   §36. Nothing else about it is changed.

   A deliberate change, the same kind as §26 rather than a re-pointing of
   something Elementor broke. Asked for on 27 Aug: the homepage's "Where would
   you like to start?" cards are the shape the client wants, and the card grids
   on the other three pages were each a different one.

       platform         .pick-fig   16/10  ->  2/1    3 cards
       online training  .pk-fig     16/9   ->  2/1    6 cards
       about            .cap-fig    16/10  ->  2/1    6 cards

   THE HOMEPAGE IS NOT TOUCHED. `.opt-fig` already carries the target shape —
   written `4/2` rather than `2/1`, by hand, straight into pag-v6-home.css on
   the server. That edit is in NEITHER this repo nor any note; see
   PENDING-MIRROR.md, which now records it. This block is deliberately written
   `2/1` rather than copying the odd form, so nobody reads `4/2` here and takes
   it for a considered choice.

   All three grids are the same column width as the homepage's, so this is not
   only the same shape but the same pixel height: 192.6px at 1440.

   NO @media COMPANION IS NEEDED, and that is worth stating because `.opt-fig`
   would need one. The homepage's figure changes shape twice on the way down —
   21/9 for the last card between 881 and 1080, 4/3 below 880 — because its row
   reflows from three cards to two-plus-one to one. These three grids hold a
   single ratio at every width, so one declaration each covers every screen.

   The images are `object-fit:cover`, so a shorter box crops further into the
   photograph rather than squashing it. Nothing here changes a widget, so every
   heading, paragraph and image on those pages stays editable in Elementor.

   Kept in the shared bridge rather than edited into the three page
   stylesheets, so those stay byte-identical to v6.1.2 and can still be diffed
   against it — the thing `.opt-fig`'s hand edit cost the homepage.        */
body.pag-v6 .pick-fig{ aspect-ratio:2/1; }
body.pag-v6 .pk-fig{ aspect-ratio:2/1; }
body.pag-v6 .cap-fig{ aspect-ratio:2/1; }


/* ------------------------------------------------------------
   38. THE SIX TILES PUT THEIR ICON BESIDE THE WORDS, 27 Aug 2026
   A deliberate change, asked for on 27 Aug: on the homepage's "One platform
   behind all three" panel the six cards stacked icon, then title, then line —
   the icon owning a row of its own. It now sits to the LEFT of both, spanning
   them.

   The card becomes a two-column grid and the icon is placed explicitly at
   column 1 spanning both rows; the heading and the text editor are left to
   auto-place, which puts them in column 2, rows 1 and 2. Nothing is reordered
   in the markup and no widget is touched, so all six stay editable exactly as
   they were.

   SCOPED `.tiles > .tile`, AND THAT MATTERS. `.tile` is shared.css's card and it
   is NOT only used here: Platform's five step cards are `.fstep tile`, and they
   are a title over a paragraph with no icon at all. An unscoped `.tile` rule
   would have made them two-column grids with an empty first track. `.tiles` is
   the six-card grid and nothing else on the site uses it — checked on all six
   pages, one container, here.

   `margin-bottom:0` on the icon undoes shared.css's `--s4`, which was the gap to
   the title BELOW it and is now a gap to nothing. `line-height:1` keeps the
   glyph's box the size of the glyph so `align-items:start` lands it on the
   title's first line rather than a text baseline's worth lower.

   The cards lose the icon's row, so they are shorter and the navy panel comes
   down with them. That is the change, not a side effect of it.
   ------------------------------------------------------------ */
body.pag-v6 .tiles > .tile{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  column-gap:var(--s4);
  align-items:start;
}
body.pag-v6 .tiles > .tile > .tile-ico{
  grid-column:1;
  grid-row:1 / span 2;
  margin-bottom:0;
  line-height:1;
}


/* ------------------------------------------------------------
   39. THE CLOSING CARDS' HEADING SITS ON THE PADDING, 27 Aug 2026
   A deliberate change, asked for on 27 Aug: "the cards look like the top and
   bottom paddings are not same as left and right".

   THE PADDING WAS ALREADY EQUAL — 57.6px on all four sides, measured. What is
   not equal is what the eye reads, because three of those edges are boxes and
   one is type:

       left / right   57.6px to the text          57.6 seen
       bottom         57.6px to the button, a filled box with a real edge
       top            57.6px to the heading's LINE BOX,
                      70.2px to the first capital letter

   The 12.6px is two gaps that live inside the line box, above the letters:
   half-leading, `(49.28 - 44) / 2` = 2.6px, and the space every font leaves
   between the top of its em box and the top of a capital, `44 - 34` = 10px.
   Measured off the font itself with `TextMetrics.actualBoundingBoxAscent`, not
   guessed.

   `-0.287em` IS THAT NUMBER AS A RATIO, AND THE em IS THE POINT. The heading is
   44px at 1440 and 30.4px on a phone, so a fixed `-12.6px` would over-correct
   the small one by 3px. Measured across five widths the offset holds between
   0.279em and 0.303em — the spread is the browser rounding cap height to whole
   pixels, not the ratio moving — so one value tracks the type at every size.

   IT DOES NOT OPEN UP THE GAP UNDER THE HEADING, which was the first question
   asked about it and a fair one. The negative margin lifts the heading AND
   everything below it by the same amount, so the heading-to-body distance is
   untouched; only the card's own height comes down. Trialled in the browser
   before it was written:

       heading letters from the top   70.2 -> 57.6
       heading to body, letter to letter   28.8 -> 28.8
       button to the bottom edge      57.6 -> 57.6
       card height                   358.7 -> 346.1

   `.close-card h2` and nothing wider. Every heading on the site sits in a line
   box with the same dead space above it, and correcting all of them is a
   site-wide typographic decision rather than this one; the client asked for the
   closing band. The band is on all six pages, so this reaches all six.
   ------------------------------------------------------------ */
body.pag-v6 .close-card h2{ margin-top:-0.287em; }
