/* =========================================================================
   lpen-course-v2.css — the course description page, built to
   LPEN-Design-System.md sections 3, 4, 5 and 9.

   EVERY SELECTOR CONTAINS ONE OF THIS PAGE'S SECTION IDS, and
   build_course_v2.py fails the build if one does not. lpen-assets/ is
   enqueued site wide (CLAUDE.md 18j) and this file reaches into WooCommerce's
   own add-to-cart markup, so a single unanchored rule would repaint the cart,
   the checkout and every product page. An id anchor is a fact you can grep;
   prefix scoping is a transformation you have to trust (18k).

   Almost everything here is a control Elementor cannot express: sticky
   positioning, pills, the numbered curriculum rail, a line clamp, a fixed
   bar, and the cart glyph that WooCommerce injects where no build guard can
   see it. Colour, type and spacing are set on the widgets themselves so the
   team can change them in the editor.

     1  tokens
     2  hero, on the photograph
     3  audience pills                       section 9.2
     4  the sticky enrolment rail            section 9.8
     5  CPD credit block                     section 9.1
     6  numbered curriculum
     7  instructor cards                     section 9.7
     8  related course cards                 section 9.5
     9  the mobile enrol bar
    10  the WooCommerce cart glyph
    11  focus, motion
   ========================================================================= */

/* ------------------------------------------------------------ 1 tokens */
/* Declared on the two ids that the html widget and the rail own, then
   inherited by their subtrees. There is no :root block, because an
   unanchored :root in a site wide stylesheet is exactly the leak the build
   guard exists to prevent. */
#lpen-cv-bar,
#lpen-cv-body {
  --cv-navy-900: #012032;
  --cv-navy-800: #022e48;
  --cv-navy-700: #0b4465;
  --cv-navy-200: #9abfd6;
  --cv-navy-100: #c9dde8;
  --cv-navy-50: #e5eef3;
  --cv-navy-25: #f3f7f9;
  --cv-action: #0f5c96;
  --cv-gold-700: #8a5a08;
  --cv-gold-500: #c8901f;
  --cv-gold-300: #e8b64a;
  --cv-gold-100: #f7e4bd;
  --cv-ink: #111820;
  --cv-muted: #4a5666;
  --cv-subtle: #6b7684;
  --cv-border: #cfd6dd;
  --cv-border-strong: #78848f;
  --cv-surface-1: #f6f8fa;
  --cv-white: #fff;
  --cv-buy: #ffd814;
  --cv-buy-hover: #f0b800;
  --cv-buy-ink: #1d3149;
  --cv-focus: #6a3fd4;
  --cv-fam: "Source Sans 3", "Source Sans Pro", -apple-system,
    BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  /* Section 4, elevation. Navy tinted, not black: a black shadow against a
     navy palette reads muddy. */
  --cv-elev-1: 0 1px 3px rgba(1, 32, 50, 0.08), 0 1px 2px rgba(1, 32, 50, 0.06);
  --cv-elev-2: 0 4px 16px rgba(1, 32, 50, 0.12), 0 2px 6px rgba(1, 32, 50, 0.08);
}

/* -------------------------------------------------------------- 2 hero */
#lpen-cv-hero a {
  color: #fff;
}

#lpen-cv-hero .lpen-cv-crumb a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 255, 255, 0.5);
}

#lpen-cv-hero .lpen-cv-crumb a:hover {
  text-decoration-color: #fff;
}

/* ------------------------------------------------------ 3 audience pills */
/* Section 9.2: radius-pill, body-sm, navy-50 fill with navy-800 text. On the
   hero photograph the fill is a translucent white instead, because navy-50
   on a dark image reads as a hole rather than a tag. */
#lpen-cv-hero .lpen-cv-pills .elementor-icon-list-items {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

#lpen-cv-hero .lpen-cv-pills .elementor-icon-list-item {
  margin: 0;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.34);
}

#lpen-cv-hero .lpen-cv-pills .elementor-icon-list-item::after {
  display: none;
}

/* Regulator chips inside the CPD block. Section 9.1: gold-300 fill, navy
   label. Section 9.2 warns these must never render for a regulator whose
   approval is only pending, which the builder enforces by having no sentence
   to print in that state. */
#lpen-cv-cpd .lpen-cv-chip .elementor-heading-title {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 999px;
  background: var(--cv-gold-300, #e8b64a);
  color: #012032;
}

/* ------------------------------------------- 4 the sticky enrolment rail */
/* Section 9.8: sticky on desktop at lg+, inline above the fold at sm,
   elevation-2 when sticky. --lpen-sticky-top is measured at runtime by the
   site header (18c), so the rail clears it whether or not the redesign
   notice and the utility bar are showing. */
#lpen-cv-rail {
  position: sticky;
  top: calc(var(--lpen-sticky-top, 90px) + 20px);
  align-self: start;
}

@media (max-width: 1024px) {
  #lpen-cv-rail {
    position: static;
  }
}

#lpen-cv-buy {
  box-shadow: var(--cv-elev-2, 0 4px 16px rgba(1, 32, 50, 0.12));
}

/* ------------------------------------------------- 5 CPD credit block */
/* The regulator rows are separated by a gold hairline rather than by cards,
   so the block reads as one statement about one course. Section 9.1 puts it
   above the fold because it is the buying reason. */
/* An id on the wrapper, not a class on each row: Elementor containers render
   _element_id and DROP _css_classes (18b.2), so a container level class
   selector matches nothing. */
#lpen-cv-regs > .e-con + .e-con {
  border-top: 1px solid rgba(200, 144, 31, 0.55);
  padding-top: 18px;
}

/* ----------------------------------------------------- 6 curriculum */
/* A numbered rail down the left. The counter cannot live on the containers:
   Elementor containers render _element_id and DROP _css_classes (18b.2), so
   the count is driven off the section id and the direct children instead. */
#lpen-cv-mods {
  counter-reset: cv-mod;
}

#lpen-cv-mods > .e-con {
  counter-increment: cv-mod;
  position: relative;
  padding-left: 62px;
}

#lpen-cv-mods > .e-con::before {
  content: counter(cv-mod, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: #e5eef3;
  color: #022e48;
  font: 700 14px/1 "Source Sans 3", sans-serif;
}

/* The connecting rule, drawn between one badge and the next. */
#lpen-cv-mods > .e-con::after {
  content: "";
  position: absolute;
  left: 18px;
  top: 44px;
  bottom: -22px;
  width: 2px;
  background: #e5eef3;
}

#lpen-cv-mods > .e-con:last-child::after {
  display: none;
}

@media (max-width: 480px) {
  #lpen-cv-mods > .e-con {
    padding-left: 48px;
  }

  #lpen-cv-mods > .e-con::before {
    width: 32px;
    height: 32px;
    font-size: 13px;
  }

  #lpen-cv-mods > .e-con::after {
    left: 15px;
    top: 38px;
  }
}

/* -------------------------------------------------- 7 instructor cards */
/* Section 9.7: photo optional, 1:1. Elementor's image widget has no aspect
   ratio or focal point control and these headshots are different shapes, so
   a centred square crop takes one of them at the chin (18j). */
#lpen-cv-fac .lpen-cv-face img {
  width: 88px;
  height: 88px;
  object-fit: cover;
  border-radius: 8px;
}

#lpen-cv-fac .lpen-cv-social .elementor-icon-list-items {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

#lpen-cv-fac .lpen-cv-social .elementor-icon-list-item::after {
  display: none;
}

/* Elementor 4.2 does not emit
   `accordion_item_title_position_horizontal: space-between`, so the plus sign
   sits jammed against the question instead of at the right edge (18n). Every
   other page in this repo with an accordion has the same defect. */
#lpen-cv-faqs .e-n-accordion-item-title,
#lpen-cv-fac .e-n-accordion-item-title {
  justify-content: space-between;
  gap: 16px;
}

/* ----------------------------------------------- 8 related course cards */
/* Section 9.5: title h4 with a 2 line clamp, one link, price as text.

   ⚠️ THE COVER ART HAS THE COURSE TITLE BURNED INTO IT. Every 2026 course
   uses one 1920x1080 template: a photograph in the left third and a coloured
   panel carrying the title in caps across the rest. 18j records this for the
   HR courses; it is true of the immigration catalogue too. Dropping the whole
   banner onto a card prints the title twice and imports a colour from
   outside the palette, so the card shows the PHOTOGRAPH only: the image is
   scaled so its left third fills the card, then clipped to a band. The alt
   is empty, because what survives the crop is decorative and the title is
   the heading right below it. */
#lpen-cv-more .lpen-cv-thumb {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 7;
  border-radius: 8px 8px 0 0;
  line-height: 0;
}

#lpen-cv-more .lpen-cv-thumb img {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translateY(-50%);
  width: 312%;
  max-width: none;
  height: auto;
  border-radius: 0;
  display: block;
}

#lpen-cv-more .lpen-cv-clamp .elementor-heading-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

#lpen-cv-more .elementor-heading-title a {
  text-decoration: none;
}

#lpen-cv-more .elementor-heading-title a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --------------------------------------------------- 9 mobile enrol bar */
/* Desktop already has a persistent CTA in the rail, so a second one would be
   two buy buttons on screen at once. Below the rail's breakpoint there is no
   persistent CTA at all, which is where a bar earns its place. */
#lpen-cv-bar {
  position: fixed;
  z-index: 60;
  left: 0;
  right: 0;
  bottom: 0;
  transform: translateY(105%);
  visibility: hidden;
  transition: transform 200ms cubic-bezier(0.2, 0, 0.2, 1),
    visibility 0s linear 200ms;
  background: var(--cv-navy-800, #022e48);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: var(--cv-elev-2, 0 4px 16px rgba(1, 32, 50, 0.12));
  font-family: var(--cv-fam);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

@media (min-width: 1025px) {
  #lpen-cv-bar {
    display: none;
  }
}

/* No JS, no bar. The page carries three enrol buttons in the flow, so
   nothing is lost. */
#lpen-cv-bar:not(.is-ready) {
  display: none;
}

/* visibility, not opacity: a focusable control behind a slid-off panel is
   the kind of defect nobody finds until they use a keyboard. */
#lpen-cv-bar.is-on {
  transform: translateY(0);
  visibility: visible;
  transition: transform 200ms cubic-bezier(0.2, 0, 0.2, 1),
    visibility 0s linear 0s;
}

#lpen-cv-bar .lpen-cvb {
  max-width: 1120px;
  margin: 0 auto;
  padding: 9px 16px;
  display: flex;
  align-items: center;
  gap: 14px;
}

#lpen-cv-bar .lpen-cvb__what {
  min-width: 0;
  flex: 1 1 auto;
}

#lpen-cv-bar .lpen-cvb__title {
  margin: 0;
  color: #fff;
  font: 600 15px/21px var(--cv-fam);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#lpen-cv-bar .lpen-cvb__meta {
  margin: 1px 0 0;
  color: var(--cv-navy-200, #9abfd6);
  font: 400 13px/18px var(--cv-fam);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#lpen-cv-bar .lpen-cvb__buy {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 10px 20px;
  border-radius: 4px;
  background: var(--cv-buy, #ffd814);
  color: var(--cv-buy-ink, #1d3149);
  text-decoration: none;
  white-space: nowrap;
  font: 700 15px/20px var(--cv-fam);
}

#lpen-cv-bar .lpen-cvb__buy:hover {
  background: var(--cv-buy-hover, #f0b800);
  color: var(--cv-buy-ink, #1d3149);
}

/* Icons are CSS masks, never inline SVG: the html widget sanitiser strips
   <svg> silently and returns ok true (18b.1). */
#lpen-cv-bar .lpen-i {
  display: inline-block;
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

@media (max-width: 380px) {
  #lpen-cv-bar .lpen-cvb__meta {
    display: none;
  }
}

/* ------------------------------------------ 10 the WooCommerce cart glyph */
/* `fas fa-shopping-cart` comes from WooCommerce, not from _elementor_data, so
   no build guard in this repo can see it (18j). Zero it and redraw as a
   Tabler mask.
   ⚠️ Scoped to the wc-add-to-cart WIDGET, not to the section: matching every
   button icon in a section also catches the Elementor buttons beside it and
   redraws their chevron as a shopping cart.
   ⚠️ Sized in PIXELS: `font-size: 0` with `width: 1em` resolves to zero and
   draws nothing, which is the bug 18n found on /academy/ and /passes/. */
#lpen-cv-hero .elementor-widget-wc-add-to-cart .elementor-button-icon i,
#lpen-cv-body .elementor-widget-wc-add-to-cart .elementor-button-icon i,
#lpen-cv-cta .elementor-widget-wc-add-to-cart .elementor-button-icon i {
  font-size: 0;
  line-height: 0;
}

#lpen-cv-hero .elementor-widget-wc-add-to-cart .elementor-button-icon i::before,
#lpen-cv-body .elementor-widget-wc-add-to-cart .elementor-button-icon i::before,
#lpen-cv-cta .elementor-widget-wc-add-to-cart .elementor-button-icon i::before {
  content: "";
  display: inline-block;
  width: 18px;
  height: 18px;
  vertical-align: -3px;
  background-color: currentColor;
  -webkit-mask-image: var(--lpen-cart-mask);
  mask-image: var(--lpen-cart-mask);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* WooCommerce's add-to-cart is not an Elementor button, so 18i's underline
   fix never reaches it. ⚠️ Off the product page WooCommerce emits an ajax
   `a.add_to_cart_button`, NOT `.single_add_to_cart_button`, so the obvious
   selector matches nothing and all four buttons render underlined. */
#lpen-cv-hero .elementor-widget-wc-add-to-cart a,
#lpen-cv-body .elementor-widget-wc-add-to-cart a,
#lpen-cv-cta .elementor-widget-wc-add-to-cart a,
#lpen-cv-hero .elementor-widget-wc-add-to-cart button,
#lpen-cv-body .elementor-widget-wc-add-to-cart button,
#lpen-cv-cta .elementor-widget-wc-add-to-cart button {
  text-decoration: none;
}

/* Buying two seats in a webinar does not enrol two people, it charges twice
   for one account. */
#lpen-cv-hero .elementor-widget-wc-add-to-cart .quantity,
#lpen-cv-body .elementor-widget-wc-add-to-cart .quantity,
#lpen-cv-cta .elementor-widget-wc-add-to-cart .quantity {
  display: none;
}

/* ----------------------------------------------------- 11 focus, motion */
/* Section 3.2: the focus ring is purple because every other hue in the
   system already means something, so focus can never be mistaken for hover
   or selection. Two layers, so it stays visible on the navy bar. */
#lpen-cv-bar a:focus-visible,
#lpen-cv-hero a:focus-visible,
#lpen-cv-body a:focus-visible,
#lpen-cv-cta a:focus-visible {
  outline: 2px solid var(--cv-focus, #6a3fd4);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px #f3f7f9;
}

/* Section 4: a WCAG 2.1 obligation, not a nicety. */
@media (prefers-reduced-motion: reduce) {
  #lpen-cv-bar {
    transition: none;
  }
}

/* ============ GENERATED: DO NOT EDIT ============ */
/* Tabler 3.46.0, 2 glyphs, generated by build_course_v2.py */
#lpen-cv-hero,
#lpen-cv-body,
#lpen-cv-cta{--lpen-cart-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'><path fill-rule='evenodd' d='m3.7 2-.6.5v1q.2.4.6.4l.7.1H5v12.1l-.3.2Q3.5 17 3 18.2q-.1.8 0 1.6a3 3 0 0 0 2 2q1 .3 1.7 0a3 3 0 0 0 2-2c.2-.4.2-1.3 0-1.7l2.7-.1H14v1.8a3 3 0 0 0 2 2q.3.3.9.2l.8-.1a3 3 0 0 0 2-2q.3-1 0-1.7a3 3 0 0 0-2-2l-.3-.2H7v-2h12.3l.6-.5.6-3.8L21 6a1 1 0 0 0-.8-.9L7.1 4 7 3.5l-.1-.9-.6-.4zm9.2 4.4 6 .4-.8 5v.1H7V6zM6.2 18.1q.7.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2m11 0q.6.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2'/></svg>")}
#lpen-cv-bar .lpen-i--shopping-cart{-webkit-mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'><path fill-rule='evenodd' d='m3.7 2-.6.5v1q.2.4.6.4l.7.1H5v12.1l-.3.2Q3.5 17 3 18.2q-.1.8 0 1.6a3 3 0 0 0 2 2q1 .3 1.7 0a3 3 0 0 0 2-2c.2-.4.2-1.3 0-1.7l2.7-.1H14v1.8a3 3 0 0 0 2 2q.3.3.9.2l.8-.1a3 3 0 0 0 2-2q.3-1 0-1.7a3 3 0 0 0-2-2l-.3-.2H7v-2h12.3l.6-.5.6-3.8L21 6a1 1 0 0 0-.8-.9L7.1 4 7 3.5l-.1-.9-.6-.4zm9.2 4.4 6 .4-.8 5v.1H7V6zM6.2 18.1q.7.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2m11 0q.6.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2'/></svg>");mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'><path fill-rule='evenodd' d='m3.7 2-.6.5v1q.2.4.6.4l.7.1H5v12.1l-.3.2Q3.5 17 3 18.2q-.1.8 0 1.6a3 3 0 0 0 2 2q1 .3 1.7 0a3 3 0 0 0 2-2c.2-.4.2-1.3 0-1.7l2.7-.1H14v1.8a3 3 0 0 0 2 2q.3.3.9.2l.8-.1a3 3 0 0 0 2-2q.3-1 0-1.7a3 3 0 0 0-2-2l-.3-.2H7v-2h12.3l.6-.5.6-3.8L21 6a1 1 0 0 0-.8-.9L7.1 4 7 3.5l-.1-.9-.6-.4zm9.2 4.4 6 .4-.8 5v.1H7V6zM6.2 18.1q.7.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2m11 0q.6.1.7.9 0 .5-.5.9h-1a1 1 0 0 1-.2-1.6 1 1 0 0 1 1-.2'/></svg>")}

/* Headshot focal points, generated from COURSE['faculty'] */
#lpen-cv-fac .lpen-cv-face--andrew-carvajal img{object-position:50% 18%}
#lpen-cv-fac .lpen-cv-face--mark-holthe img{object-position:50% 22%}
