/* Reimplementation of the handful of Wix components this site actually uses.
 *
 * The Wix client runtime is removed at build time (scripts/strip-runtime),
 * because it cannot work without Wix and a half-booted runtime destroys the
 * server-rendered content. This file replaces the small number of components
 * that genuinely needed JS. See DECISIONS.md.
 *
 * Everything else on the site is plain markup and Wix's own CSS, which is
 * kept as-is.
 */

/* ------------------------------------------------------------------
 * <fluid-columns-repeater>  -- 26 pages, incl. the programs grid
 *
 * A custom element. Wix's JS defines it, and on upgrade it absolutely
 * positions its children into columns and clears the inline
 * visibility:hidden it ships with. Undefined, it never upgrades, so the
 * whole subtree stays hidden -- 452 of 842 elements on /programs.
 *
 * Observed on the live site: children are a fixed 649px wide (from Wix's
 * own CSS, which we still have), two per row, 40px gaps, centred. Flex
 * wrap reproduces that without needing to compute positions.
 * ------------------------------------------------------------------ */

fluid-columns-repeater {
  /* Beats the inline style="visibility:hidden" the element ships with. */
  visibility: visible !important;

  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 40px;
  position: relative;
  box-sizing: border-box;

  /* `safe` degrades to flex-start instead of centring when a row overflows.
   * Plain `center` splits the overflow both ways, and the left half is
   * unreachable -- it cannot be scrolled to and gets clipped.
   */
  justify-content: safe center;

  /* Wix ships this element with a negative margin on all four sides: gutter
   * compensation for the 20px inset it gives each child, so that the outer
   * children still sit flush with the container edge. The real component
   * positions children absolutely at `top: 20px`; we lay them out with flex
   * and never apply that inset, so every negative margin is left uncancelled.
   *
   * Horizontally that dragged the row 20px left and clipped the leftmost card
   * at every column count. Vertically it pulled the cards 20px up into the
   * text above them -- measured against the live site, which leaves 23px
   * between the paragraph and the first card where we had 3px.
   *
   * Both axes are the same bug, so both are zeroed.
   */
  margin: 0 !important;
  width: auto !important;
}

/* The real component takes children out of flow and places them. Without it
 * they must flow normally or they stack on top of each other.
 *
 * `flex: 0 1 auto` with Wix's fixed 649px width gives the behaviour we want
 * for free: cards keep their width and wrap when a row is full, and shrink
 * only once a single card no longer fits. No breakpoints needed.
 */
fluid-columns-repeater > * {
  position: relative !important;
  inset: auto !important;
  flex: 0 1 auto;
  max-width: 100%;
  min-width: 0;
}

/* ------------------------------------------------------------------
 * <wix-video> background video
 *
 * Wix's runtime set the video's width, height and object-fit as inline
 * styles; no stylesheet carries them. Without the runtime the <video> falls
 * back to its intrinsic size -- an 854x480 box letterboxed inside a 993x622
 * container -- which reads as "the video is not playing" even while it is.
 *
 * data-video-info gives fittingType "fill" and alignType "center" for both
 * of these, which is cover/centre.
 * ------------------------------------------------------------------ */

wix-video {
  overflow: hidden;
}

wix-video > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;

  /* Wix ships the video at opacity 0 and fades it in from the runtime once
   * playback starts. Without the runtime it plays correctly and stays
   * invisible -- which looks exactly like "the video is not autoplaying".
   */
  opacity: 1 !important;
}

/* The poster sits AFTER the video in DOM order, so it paints on top of it.
 * Wix's runtime fades the poster out once playback begins; without that the
 * video advances perfectly underneath a static image -- which is
 * indistinguishable from a video that never started.
 *
 * The poster is kept until the shim reports playback, so a still frame is
 * shown rather than a blank box while the video loads, and it stays put
 * entirely if the video never plays (autoplay refused, JS disabled, decode
 * failure).
 */
wix-video [class*="bgVideoposter"] {
  transition: opacity 400ms ease;
}

wix-video.as-video-playing [class*="bgVideoposter"] {
  opacity: 0;
  pointer-events: none;
}

/* Anchor buttons scroll rather than jump, as they do on the live site, where
 * the runtime animated it. Declared as a preference query so that a visitor
 * who has asked for reduced motion keeps the instant jump.
 */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ------------------------------------------------------------------
 * In-page anchors  -- scripts/fix-anchors
 *
 * Wix's anchor buttons are JS-driven: the href is a no-JS fallback pointing
 * at the current page, and the runtime reads data-anchor and scrolls. With
 * the runtime gone, clicking them just reloaded the page.
 *
 * They are rewritten to real fragment links at build time. Wix only renders
 * an anchor element for anchors that were named in the editor, so the rest
 * are generated with this class -- zero height, no layout impact, matching
 * how Wix's own anchor divs behave.
 * ------------------------------------------------------------------ */

/* The sticky header, faded -- see initHeaderFade() in the shim. Same values
 * the live site's own class carries: it stays in the layout and keeps its
 * place at the top, it simply stops being visible or clickable.
 */
.as-header-faded {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease-in;
}

/* Where the scroll lands.
 *
 * An earlier version of this offset the target by the height of the site
 * header, on the reasoning that `#SITE_HEADER` is `position: sticky` and
 * would otherwise cover the top of the section. It does say sticky -- and it
 * never sticks: it is a grid item of #masterPage with `grid-area: 1/1/2/2`,
 * and a sticky box can only travel inside its containing block, which for a
 * grid item is its grid area. That row is exactly as tall as the header, so
 * there is nowhere to travel and it scrolls away like any other element.
 * The live site behaves the same way, being the same CSS: click an anchor
 * there and the header is gone, with the section's divider at the very top.
 * The 104px this used to add therefore landed every section a header's
 * height lower than Wix landed it.
 *
 * Wix's own anchor div, where the editor had one, sits exactly where its
 * runtime scrolled to, so that lands at the top with no adjustment at all.
 */
.as-anchor-target {
  scroll-margin-top: 0;
}

/* Where there was no anchor div, the id went on the section heading instead
 * -- which is below the point Wix scrolled to, by whatever gap the editor
 * left there. That gap was measured per page by driving the live site (see
 * "_offsets" in inventory/anchors.json) and each target carries its own
 * figure in a style attribute; this is only the fallback for a target with
 * no measurement, and the measured ones run from 30px to 72px.
 */
.as-anchor-heading {
  scroll-margin-top: 40px;
}

/* ------------------------------------------------------------------
 * horizontal-menu dropdowns  -- 69 pages (Services, Company)
 *
 * The submenu is fully present in the DOM with real links; Wix hides it with
 * `display: none` on the positionBox and `opacity: 0` on the animationBox,
 * and its JS toggles both on hover/focus.
 *
 * The positionBox is `position: fixed` with no offsets, which sounds like it
 * would need JS to place. It does not: a fixed element with auto offsets
 * resolves to its static position, which is already directly beneath its menu
 * item. Measured -- revealing it with no other change puts the flyout at
 * top:60 left:400 against an item whose bottom is 60 and left is 400.
 *
 * Class names are Wix-generated (itemDepth02233374943__positionBox), so these
 * match on the stable part of the name rather than the whole thing.
 * ------------------------------------------------------------------ */

.wixui-horizontal-menu__item:hover [class*="positionBox"],
.wixui-horizontal-menu__item:focus-within [class*="positionBox"] {
  display: block !important;
}

.wixui-horizontal-menu__item:hover [class*="animationBox"],
.wixui-horizontal-menu__item:focus-within [class*="animationBox"] {
  opacity: 1 !important;
}

/* The flyout is a descendant of the item and sits flush against its bottom
 * edge, so moving the pointer into it keeps :hover alive and there is no
 * gap to fall through.
 *
 * Known limitation: aria-expanded stays "false" because CSS cannot change an
 * attribute. Screen readers will announce the trigger as collapsed even when
 * it is open. Fixing that needs a few lines of JS; noted in BUGS.md rather
 * than shipping script for it now.
 */

/* ------------------------------------------------------------------
 * Captcha  -- /contact and /professional-development
 *
 * Wix renders a placeholder that its runtime swaps for the real widget:
 *
 *   <div class="... wixui-captcha">
 *     <div class="Captcha...__captchaLoader"></div>
 *     <div class="Captcha...__captcha"></div>
 *
 * Without the runtime the loader spins forever next to the submit button.
 *
 * The container is reused rather than hidden: scripts/add-turnstile swaps its
 * contents for a Cloudflare Turnstile widget, so the real captcha lands
 * exactly where Wix's was, already positioned and spaced by Wix's own CSS.
 *
 * Matched on wixui-captcha, not the hashed Captcha3940957316 class, which is
 * regenerated by Wix builds.
 * ------------------------------------------------------------------ */

/* Anything left of Wix's placeholder if a page was not rewritten. */
[class*="captchaLoader"],
[class*="Captcha"][class*="__checkbox"] {
  display: none !important;
}

/* Wix's captcha slot is 234x60 and its position is load-bearing: on
 * /professional-development the Submit button sits in the SAME row, starting
 * 10px to the right of the slot. Widening the slot to centre the widget -- an
 * earlier attempt here -- made it overlap the button on that page.
 *
 * So the slot keeps exactly the geometry Wix gave it, matching the live site,
 * where the captcha is left-aligned with the Submit button's left edge rather
 * than centred on the form.
 *
 * Turnstile's widget is ~300px, wider than the 234px slot. Two things follow:
 *
 *  - `flex: none` stops flexbox shrinking the widget to 234px, which squashed
 *    it and was what actually looked wrong.
 *  - it is scaled down to fit instead, by a factor the shim measures per slot.
 *    Scaling rather than overflowing, because overflow would run straight into
 *    the Submit button on /professional-development.
 */
.as-turnstile {
  display: flex;
  justify-content: flex-start;   /* left-aligned, as on the live site */
  align-items: center;
  min-height: 60px;              /* the slot's own height, so nothing jumps */
}

.as-turnstile > * {
  flex: none;
  transform: scale(var(--as-ts-scale, 1));
  transform-origin: left center;
}

/* Honeypot: out of sight and out of the accessibility tree, without
 * display:none or visibility:hidden -- some bots specifically skip fields
 * hidden that way, which would defeat the point.
 */
.as-honeypot {
  position: absolute !important;
  left: -9999px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ------------------------------------------------------------------
 * Members / login
 *
 * Dropped entirely: 31 accounts, exactly one of which ever logged in
 * twice, and that is the owner. Booking never required an account. The
 * control is inert without the Wix runtime, so hide it rather than leave
 * a dead button in the header.
 * ------------------------------------------------------------------ */

.wixui-login-social-bar,
[class*="wixui-login-social-bar"] {
  display: none !important;
}

/* ------------------------------------------------------------------
 * Cart
 *
 * Wix Stores is installed but has never taken a payment, and with Bookings
 * and Members both dropped there is nothing to buy and no one to buy it.
 * The icon links to /cart-page, which is not even in the mirror.
 *
 * Identifying it takes a component id because the semantic markers
 * (aria-label="Cart with 0 items", data-hook="cart-icon-button") are added
 * by the Wix runtime and so are absent from the stripped build -- verified
 * by inspecting the same component on the live site. The data-hook rules
 * are kept as a defensive second selector in case a future mirror captures
 * a page where the runtime had already run.
 * ------------------------------------------------------------------ */

#comp-m6fobjwt,
[data-hook="cart-icon-button"],
[data-hook="cart-icon"] {
  /* Hidden, not removed. `display: none` took the icon out of layout, and the
   * icon is the tallest thing in the header's grid row -- 15px above it,
   * 40.625px of icon, 18px below, which is exactly the 73.625px header Wix
   * renders. Without it the logo set the height instead, at 73px, and every
   * page in the site sat 1px high.
   *
   * That 0.625px is not invisible in practice. Line boxes land on fractional
   * positions (body text is 25.2px line-height), so shifting a block by
   * 0.625px makes SOME lines round to a different pixel row and leaves others
   * where they were -- text that visibly moves relative to the text beside it.
   * The business found it by flipping between the two sites.
   *
   * So the box stays and only the paint is suppressed: no dead cart on a site
   * that sells nothing, and no 1px offset either. */
  visibility: hidden !important;
}

/* The runtime never draws the icon, so the element would collapse to zero even
 * while occupying layout. This is the height the icon had on the live site,
 * measured there rather than derived: 15 + 40.625 + 18 = 73.625. */
#comp-m6fobjwt {
  height: 40.625px !important;
}

/* ------------------------------------------------------------------
 * Contact form status
 *
 * The form posts with fetch and reports in place, so there is no thank-you
 * page and the API returns JSON rather than HTML. role="status" announces
 * the result to screen readers without moving focus mid-task.
 * ------------------------------------------------------------------ */

.as-form-status {
  margin: 1em 0 0;
  min-height: 1.5em;      /* reserve the line so nothing jumps on first message */

  /* Absolute, not relative. This element is appended to the <form>, which
   * inherits a very small font-size from Wix's own form styles -- 0.95em of
   * that rendered at roughly 8px and was genuinely unreadable. The one message
   * telling a visitor their enquiry failed is the last thing that should be
   * hard to read.
   */
  font-size: 16px;
  line-height: 1.5;
  font-weight: 500;

  /* Every other control in the form is centred; the status was left-aligned
   * against the form's left edge and read as unrelated to it.
   */
  text-align: center;
}

/* The address has to stay legible against whatever the form sits on, so it
 * inherits the status colour rather than picking up a link colour that may be
 * invisible on this background.
 */
.as-form-status a {
  color: inherit;
  text-decoration: underline;
}

.as-form-status[data-kind="success"] { color: #7ee787; }
.as-form-status[data-kind="error"]   { color: #ff9492; }
.as-form-status:empty                { min-height: 0; margin: 0; }

/* The related-posts carousel at the foot of a blog post, on mobile.
 *
 * It is a slick carousel, and slick sizes itself from JavaScript: the runtime
 * measures the list and writes pixel widths onto the track and each slide.
 * The server-rendered markup carries `width:100%` as a placeholder, so without
 * the runtime the card fills the full 320px instead of the 260px Wix gives it
 * -- 60px too wide, which is what the render baseline caught once the page
 * stopped being skipped.
 *
 * 60px is slick's centerMode peek: 30px each side, so the next card shows at
 * the edges. Measured off the live site, where the list is 320 and the slide
 * is 260.
 *
 * Only the track needs setting. The slide's own `width:100%` then resolves
 * against the track rather than the list, and comes out right on its own.
 *
 * This assumes a single slide, which is what the site has -- one carousel, on
 * one page, with one related post. slick's real rule is
 * `trackWidth = slideWidth * slideCount`, so a post that ever gets a second
 * related card needs that multiplier rather than this.
 */
.slick-track {
  width: calc(100% - 60px) !important;
}

/* The mobile blog's category picker -- see scripts/fix-links.
 *
 * Wix's runtime built the dropdown's menu, so without it the control could
 * not open and the categories were unreachable on a phone. It is a <details>
 * now: the <summary> keeps the button's every attribute and its whole inside,
 * so closed it is pixel-for-pixel the control Wix rendered -- same width,
 * same label, same chevron -- and opening it reveals the categories.
 *
 * No JavaScript: disclosure is a thing HTML does, and this way the keyboard
 * and screen readers get the real control rather than an imitation of one.
 */
.as-menu > summary {
  /* The one thing the element swap costs: a <button> is border-box by the UA
   * stylesheet and a <summary> is content-box, so the control's own
   * `width: 320px` plus its 18px side padding came out 356px wide -- wider
   * than the phone. Put back what the button had.
   */
  box-sizing: border-box;

  /* The disclosure triangle, in both spellings: WebKit still wants the
   * pseudo-element, everyone else takes list-style. Either left in place
   * would be a mark Wix never drew.
   */
  list-style: none;
  cursor: pointer;
}

.as-menu > summary::-webkit-details-marker { display: none; }
.as-menu > summary::marker { content: ""; }

/* The chevron flips to point back the way it came when the menu is open.
 * Wix's markup already rotates the glyph 90 degrees in the SVG itself, so it
 * points down closed; half a turn from there points it up, which is what a
 * dropdown does. A quarter turn -- the obvious-looking value -- pointed it
 * sideways, because it was turning a chevron that was already turned.
 */
.as-menu > summary svg {
  transition: transform 0.15s ease;
}

.as-menu[open] > summary svg {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .as-menu > summary svg { transition: none; }
}

/* The choices, dressed as the control itself: same padding, same type, same
 * white on the same navy. Measured off the control rather than guessed --
 * `color: inherit` was the first attempt and rendered navy text on the navy
 * header, which is to say nothing at all, because what it inherited was the
 * page's body colour and not the control's.
 *
 * Hardcoded rather than taken from a Wix variable: these two values are the
 * blog header's, they are already fixed in the capture, and a variable that
 * the runtime used to set is not a thing this build can rely on.
 */
.as-menu-item {
  display: block;
  padding: 10px 18px;
  font-size: 16px;
  line-height: 22px;
  text-decoration: none;
  color: #fff;
  background: #2e3f66;
}

.as-menu-item[aria-current="page"] { font-weight: 700; }

.as-menu-item:hover,
.as-menu-item:focus-visible { text-decoration: underline; }

/* An <a> that replaced an inert Wix <button> -- see scripts/fix-links.
 *
 * A button centres its label through the UA stylesheet; an anchor inherits
 * text-align instead, and Wix's own rules never set it because with a real
 * button they never needed to. Left alone the label aligns left, which is
 * invisible while it is one line inside a centred flex box and wrong the
 * moment it wraps. The render baseline caught it; a by-hand comparison of
 * background, colour, font, padding, border and cursor did not, because
 * text-align was not on the list.
 */
[data-as-was-button] { text-align: center; }

/* ------------------------------------------------------------------
 * Contact form dialog
 *
 * The status line above is still written and still announced, but on /contact
 * it sits below the fold on a laptop: pressing Send emptied the form and
 * appeared to do nothing else. This dialog is what the visitor actually sees
 * -- a spinner while the request is in flight, then the outcome -- and the
 * form keeps what was typed until it is dismissed.
 *
 * Same z-index as the lightbox, for the same reason: #SITE_HEADER is sticky
 * with a z-index of its own and Wix's sections create stacking contexts, so
 * anything appended to <body> has to be above all of it. The two are never
 * open at once (one is on /contact, the other on the gallery pages).
 * ------------------------------------------------------------------ */

.as-modal {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  background: rgba(20, 28, 46, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
}

/* The same trap the lightbox hits below, and for the same reason: `display:
 * flex` above is an author rule, so it beats the browser's own
 * [hidden] { display: none }. Without this, `root.hidden = true` sets the
 * attribute and changes nothing on screen -- the dialog stays up while every
 * dismissal path fires correctly, so Close, the backdrop and Escape all look
 * broken at once. It shipped that way; the local check asserted the property
 * rather than that the thing had actually gone.
 */
.as-modal[hidden] { display: none !important; }

/* Same again for the Close button, which is hidden while the request is in
 * flight. Its own rules below do not set `display`, so the browser's [hidden]
 * would normally be enough -- but only until one of Wix's stylesheets sets a
 * display on buttons, and then the dialog would offer a Close button during
 * the spinner that deliberately does nothing.
 */
.as-modal-close[hidden] { display: none !important; }

.as-modal-box {
  background: #fff;
  color: #2e3f66;
  border-radius: 8px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
  padding: 32px 28px 28px;
  width: 100%;
  max-width: 420px;
  box-sizing: border-box;
  text-align: center;

  /* Absolute, like the status line and for the same reason: the dialog is a
   * child of <body>, but Wix's font-size cascade is unpredictable enough that
   * a relative size here has already produced 8px text once.
   */
  font-size: 16px;
  line-height: 1.5;
  font-weight: 500;
}

/* It is focused on open so the focus trap has somewhere to hold, which is a
 * programmatic focus the visitor did not ask for -- and a ring around the
 * whole dialog reads as an error state.
 */
.as-modal-box:focus { outline: none; }

.as-modal-spinner {
  width: 40px;
  height: 40px;
  margin: 0 auto 18px;
  border: 3px solid rgba(46, 63, 102, 0.18);
  border-top-color: #2e3f66;
  border-radius: 50%;
  animation: as-modal-spin 0.8s linear infinite;
}

/* Only while the request is in flight. Once there is an outcome the spinner
 * would be saying the opposite of the message beside it.
 */
.as-modal:not([data-kind="pending"]) .as-modal-spinner { display: none; }

@keyframes as-modal-spin { to { transform: rotate(360deg); } }

/* A spinner is exactly the kind of thing this preference is about. The dot
 * still marks the place, and the message says what is happening.
 */
@media (prefers-reduced-motion: reduce) {
  .as-modal-spinner {
    animation: none;
    border-color: rgba(46, 63, 102, 0.18);
    border-top-color: #2e3f66;
  }
}

.as-modal-message {
  margin: 0;
}

.as-modal-message a {
  color: inherit;
  text-decoration: underline;
}

/* Darker than the status line's colours, which were chosen for white text on
 * the form's dark panel; #7ee787 on white is barely there.
 */
.as-modal[data-kind="success"] .as-modal-message { color: #1a7f37; }
.as-modal[data-kind="error"]   .as-modal-message { color: #b42318; }

.as-modal-close {
  margin: 22px 0 0;
  padding: 10px 28px;
  border: 0;
  border-radius: 4px;
  background: #2e3f66;
  color: #fff;
  font: inherit;
  cursor: pointer;
}

.as-modal-close:hover  { background: #26355a; }
.as-modal-close:focus-visible { outline: 2px solid #2e3f66; outline-offset: 2px; }

/* The page behind must not scroll: it is a form the visitor should not be
 * editing until they have read the outcome. The compensating padding for the
 * vanished scrollbar is set in JS, where its width can be measured.
 */
body.as-modal-open { overflow: hidden; }

/* ------------------------------------------------------------------
 * Pro Gallery lightbox  -- 16 pages, 202 images
 *
 * Clicking a gallery image opens Wix's fullscreen viewer on the live site.
 * That viewer is built entirely by the runtime, so in the mirror the images
 * were inert. scripts/fix-gallery tags each expandable item with the URL of
 * an uncropped rendition and the shim builds the viewer.
 *
 * Colours are taken from the live viewer rather than guessed: the backdrop
 * computes to rgb(46,63,102), which is Wix's --wix-color-1. Hardcoded because
 * that variable is defined per-component, not on :root, so it is not reliably
 * in scope for an element appended to <body>.
 * ------------------------------------------------------------------ */

.as-lightbox {
  position: fixed;
  inset: 0;
  /* Above the sticky header and Wix's own stacking contexts. */
  z-index: 2147483000;
  background: #2e3f66;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Room for the edge controls so the image never sits under them.
   *
   * 90px, not a round 88: the image is width-constrained here, so the
   * horizontal padding sets its size directly. At 88 the picture came out
   * 1104x638 against the live viewer's 1100x635 -- the whole difference was
   * this number, not the rendition. Measured at 1280x800. */
  padding: 72px 90px;
  box-sizing: border-box;
}

/* Wix's mobile pages declare a fixed 320px viewport, so the desktop padding
 * above eats 176 of 320 and leaves a 144px image -- smaller than the thumbnail
 * that opened it. Measured, not guessed: the viewer rendered at exactly 144px
 * wide on a Pixel 8 before this rule existed.
 *
 * The arrows tighten to the edge and overlap the image slightly, which is the
 * right trade on a phone: swiping is the primary gesture there, and the
 * alternative is a postage stamp.
 */
/* The reset in the page head already carries [hidden]{display:none!important},
 * but this element is toggled by a stylesheet we do not control the order of,
 * and `display:flex` above would otherwise win against a plain [hidden].
 */
.as-lightbox[hidden] { display: none !important; }

/* The viewer is a horizontal scroll-snap track rather than one <img> whose src
 * is swapped. Swapping made navigation a hard cut; the live viewer slides, and
 * it builds the same thing -- inspecting it mid-swipe shows every image in the
 * gallery present side by side.
 *
 * Native scrolling also means a finger drags the image with it, which no
 * amount of transform animation reproduces faithfully.
 */
.as-lb-track {
  display: flex;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Keeps a drag from turning into browser back-navigation on the edges. */
  overscroll-behavior-x: contain;
}

.as-lb-track::-webkit-scrollbar { display: none; }

.as-lb-slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}

.as-lightbox img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

.as-lightbox button,
.as-lightbox .as-lb-download {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  /* Full strength, the way the live viewer draws them: it dims its controls
   * only once the browser itself is in full screen, and never on hover. */
  color: #fff;
  line-height: 1;
  font-family: inherit;
}

/* A visible focus ring matters more here than usual: the viewer traps focus,
 * so a keyboard user who cannot see where focus is has no way out but Esc.
 * This is the live viewer's own ring -- a white hairline inside blue, tucked
 * within the button rather than drawn around it.
 */
.as-lightbox button:focus-visible,
.as-lightbox .as-lb-download:focus-visible {
  outline: none;
  border-radius: 7px;
  box-shadow: inset 0 0 0 1px #fff, inset 0 0 1px 4px #116dff;
}

/* box-shadow is not painted in forced-colours mode, which would drop the ring
 * for exactly the people least able to do without it.
 */
@media (forced-colors: active) {
  .as-lightbox button:focus-visible,
  .as-lightbox .as-lb-download:focus-visible { outline: 2px solid CanvasText; }
}

/* Geometry from the stylesheet the icons came out of (Wix's pro-fullscreen
 * renderer): the corner controls are a 38x44 button 30px down from the top
 * holding a 26px glyph; the arrows sit in a 100px square hit area centred
 * vertically against the viewport edge, with a 15x27 chevron in the middle.
 * The hit area is far larger than the mark it shows, which is the point.
 */
.as-lb-close  { top: 30px; right: 30px; width: 38px; height: 44px; }
.as-lb-expand { top: 26px; left: 35px;  width: 38px; height: 44px; }  /* 26, not 30: Wix's expand control really does sit 4px above the
     download and close buttons. Measured, not tidied. */
/* Wix leaves a gap between the two: expand occupies 35-73 and download starts
 * at 128, so they are 55px apart rather than touching. An earlier reading of
 * this put expand at x=90 and concluded they were flush; re-measured inside
 * the fullscreen container, they are not. */
.as-lb-download { top: 30px; left: 128px; width: 38px; height: 44px; }
.as-lb-close .as-lb-icon,
.as-lb-expand .as-lb-icon { width: 26px; height: 26px; }
.as-lb-download .as-dl-icon { width: 26px; height: 26px; }

/* The close control carries both glyphs and CSS picks one: an X on desktop, a
 * back arrow on mobile, which is what Wix does -- it swaps the icon, not just
 * the position. Shipping both avoids asking JavaScript what size the screen is
 * and then being wrong after a rotate. */
/* Scoped to .as-lb-close so it outranks `.as-lb-icon { display: block }`
 * further down. Written unscoped first, at equal specificity and ABOVE that
 * rule, so the later one won and both the X and the back arrow painted in the
 * corner together. */
.as-lb-close .as-lb-ico-back { display: none; }
.as-lb-close .as-lb-ico-back { width: 25px; height: 20px; }

/* 50x50, inset 20px from the edge, vertically centred -- the live viewer's
 * arrows, measured. The 100px squares here were a guess at a comfortable
 * touch target and put the arrows hard against the viewport edge, which is
 * neither what Wix does nor what the rest of this file matches. */
.as-lb-prev, .as-lb-next { top: calc(50% - 25px); width: 50px; height: 50px; }
.as-lb-prev { left: 20px; }
.as-lb-next { right: 20px; }
.as-lb-prev .as-lb-icon,
.as-lb-next .as-lb-icon { width: 15px; height: 27px; }

/* Wix dims its arrows slightly at rest and brings them to full strength under
 * the pointer -- measured on the live viewer: the arrow container computes to
 * opacity 0.9 away and 1 over, with no transition, so the change is instant.
 * Ours sat flat at 1 and did nothing on hover, which reads as a dead control.
 * The 100x100 hit areas hid it: at that size the pointer was almost always
 * over an arrow, so there was nothing to notice. */
.as-lb-prev,
.as-lb-next { opacity: 0.9; }

.as-lb-prev:hover,
.as-lb-next:hover { opacity: 1; }

.as-lb-icon { display: block; }

/* Hidden at the ends of the gallery -- see sync() in the shim. The rule above
 * gives these buttons `display: flex`, and an author declaration beats the
 * browser's own [hidden] rule, so without this they would stay visible.
 */
.as-lightbox button[hidden],
.as-lightbox .as-lb-download[hidden] { display: none !important; }

/* Hidden rather than removed when there is only one image, so the layout and
 * the focus order do not change between galleries of different sizes.
 */
.as-lightbox[data-single="1"] .as-lb-prev,
.as-lightbox[data-single="1"] .as-lb-next { display: none; }

/* ------------------------------------------------------------------
 * The viewer on a phone
 *
 * Wix's mobile viewer is a different arrangement, not a smaller one: a back
 * arrow at the top left, download at the top right, both 60x60 flush to the
 * edges, the image full-bleed directly beneath them -- and no full-screen
 * control and no arrows at all, because you swipe. Measured on the live site
 * at the 320px viewport its mobile pages declare.
 *
 * This block deliberately sits AFTER the desktop rules it overrides. The
 * version before it did not, and since a media query adds no specificity,
 * every declaration in it lost to the later desktop rule and had no effect --
 * for as long as it had existed. The phone was quietly getting the desktop
 * layout, which is how it drifted this far without anyone seeing it.
 * ------------------------------------------------------------------ */

@media (max-width: 600px) {
  .as-lightbox {
    /* Room for the control row, and nothing at the sides: the image is
     * full-bleed here, unlike desktop. */
    padding: 60px 0 0;
    align-items: flex-start;
  }

  /* It is already full screen, and Wix offers no arrows -- swiping the track
   * is the whole navigation. */
  .as-lb-expand,
  .as-lb-prev,
  .as-lb-next { display: none !important; }

  .as-lb-close {
    top: 0; left: 0; right: auto;
    width: 60px; height: 60px;
  }

  .as-lb-download {
    top: 0; right: 5px; left: auto;
    width: 60px; height: 60px;
  }

  .as-lb-close .as-lb-ico-x { display: none; }
  .as-lb-close .as-lb-ico-back { display: block; }

  /* Top-aligned under the controls, not centred in what is left. */
  .as-lb-slide { align-items: flex-start; }
}

/* Announced to screen readers as the image changes; the live viewer shows no
 * visible counter and adding one would be a visual difference.
 */
.as-lb-count {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Gallery thumbnails that open the viewer. Wix gives its items
 * tabindex="-1" and drives focus from JS; the shim makes them real buttons
 * instead, so they need to look focusable.
 */
[data-as-full] { cursor: pointer; }

.gallery-item-container:focus-visible,
[data-hook="item-link-wrapper"]:focus-visible {
  outline: 3px solid #2e3f66;
  outline-offset: 2px;
}

/* Wix locks the page behind its viewer; without this the page scrolls under
 * the backdrop when the wheel is used.
 */
body.as-lightbox-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .as-lightbox * { transition: none !important; }
}


/* ------------------------------------------------------------------
 * Mobile navigation menu
 *
 * Wix hides the panel with `[data-undisplayed=true]{display:none}` and then
 * layers opacity/visibility on a generated class (.I_VSKP at time of writing).
 * The attribute is stable and semantic; the class name is a build artifact and
 * will change the next time the site is mirrored, so the shim toggles the
 * attribute and supplies its own visible state keyed on Wix's public
 * `wixui-` class instead of the hash.
 * ------------------------------------------------------------------ */

.wixui-mobile-menu.as-menu-open {
  opacity: 1 !important;
  visibility: visible !important;
}

/* The toggle CANNOT double as the close button here, however much z-index it
 * is given. #SITE_HEADER is `position: sticky; z-index: 50`, which makes it a
 * stacking context, so the toggle inside it is confined to that context and
 * can never paint above the panel at z-index 99999. Measured:
 * document.elementFromPoint over the toggle returns the panel, and the click
 * never reaches the button.
 *
 * So the toggle is hidden while the panel is open and a real close button is
 * rendered inside the panel, where being on top is not in question.
 */
.wixui-mobile-menu__icon.as-menu-open-toggle { visibility: hidden; }

.as-menu-close {
  position: absolute;
  top: 12px;
  right: 16px;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  z-index: 10;
}

.as-menu-close::before,
.as-menu-close::after {
  content: "";
  position: absolute;
  left: 10px;
  top: 21px;
  width: 24px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

.as-menu-close::before { transform: rotate(45deg); }
.as-menu-close::after  { transform: rotate(-45deg); }

.as-menu-close:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

/* The scrolling element is <html>, not <body> -- locking only the body left
 * the page scrolling underneath the open panel. Both are locked because which
 * one scrolls varies by browser and by document.
 */
html.as-menu-locked,
body.as-menu-locked {
  overflow: hidden;
}

/* A menu taller than the viewport still has to scroll within itself. */
.wixui-mobile-menu.as-menu-open {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}



/* Expandable Services/Company sections inside the panel. Same reasoning: Wix
 * adds a generated class to the <li>; this keys on the public one.
 */
.wixui-vertical-menu__item.as-submenu-open > * > .wixui-vertical-menu__submenu,
.wixui-vertical-menu__item.as-submenu-open > .wixui-vertical-menu__submenu {
  display: block !important;
  opacity: 1 !important;
}

/* The submenu panel takes the site's own navy instead of Wix's green.
 *
 * Wix filled it with rgb(88, 218, 93) -- a fluorescent green -- and put the
 * menu's gold rgb(255, 225, 139) labels on top of it. That pairing measures
 * 1.41:1, where WCAG AA wants 4.5:1 for text this size: not a matter of
 * taste, the labels are barely legible on it.
 *
 * #2E3F66 is the site's own primary (Wix's color_11, the header's background)
 * and puts the submenu at 8.12:1 -- the identical gold-on-navy pairing the
 * rest of the menu already uses, so the panel reads as part of the menu
 * rather than something pasted over it.
 *
 * Scoped to the submenu on purpose. That green is a theme colour used
 * site-wide -- it is the /about hero heading, among other things -- and as
 * a colour for large type on a dark ground it works. It is only as a solid
 * background behind pale text that it fails.
 */
.wixui-vertical-menu__submenu .wixui-vertical-menu__item {
  background-color: #2e3f66 !important;
}

/* The submenu is wide enough for its longest label.
 *
 * A deliberate divergence, and the only one here that is not a repair: the
 * live Wix site does this too, so it is a Wix bug reproduced faithfully
 * rather than one we introduced. The vertical menu is 188px wide, and
 * "Professional Development" needs 210px at 18px questrial. Wix gives the
 * label `white-space: nowrap` with `text-overflow: ellipsis` AND
 * `direction: rtl`, so the ellipsis lands at the START of the string: it
 * renders as "...ssional Development", which reads as a broken page.
 *
 * Widening the submenu panel rather than wrapping the text, because every
 * element from the label up to the <li> carries an explicit 56px height with
 * `overflow: hidden` on the label -- a second line is simply cut off, which
 * would be worse than the ellipsis. Widening keeps Wix's one-line design and
 * its type size, and changes nothing above the submenu.
 *
 * 224px fits the longest label with room to spare, and the negative margin
 * keeps the panel centred in the 320px mobile viewport that Wix declares.
 * Mobile only: the desktop build has a horizontal menu and none of these
 * elements.
 */
.wixui-vertical-menu__submenu {
  width: 224px !important;
  margin-left: -18px;
}

/* If a label ever does outgrow the panel, it truncates at the end like any
 * other text. The rtl above is Wix's, and its only purpose was to move the
 * ellipsis to the front.
 */
.wixui-vertical-menu__item-label {
  direction: ltr;
}

/* The chevron points up while its section is open, as on the live site. */
.wixui-vertical-menu__arrow {
  transition: transform 200ms ease;
}

.wixui-vertical-menu__item.as-submenu-open > * > * > .wixui-vertical-menu__arrow,
.wixui-vertical-menu__item.as-submenu-open .wixui-vertical-menu__arrow {
  transform: rotate(180deg);
}

/* ------------------------------------------------------------------
 * Back to top
 *
 * #BACK_TO_TOP_BUTTON is already in the markup, at opacity 0 / visibility
 * hidden, waiting for a runtime that no longer exists. It only needs showing
 * while the visitor is scrolling and for a moment afterwards.
 * ------------------------------------------------------------------ */

.BACK_TO_TOP_BUTTON {
  transition: opacity 250ms ease, visibility 250ms ease;
}

.BACK_TO_TOP_BUTTON.as-btt-visible {
  opacity: 1 !important;
  visibility: visible !important;
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .as-lb-track { scroll-behavior: auto; }
}


/* ------------------------------------------------------------------
 * Blog post "more actions" menu
 *
 * The button is in the markup with aria-haspopup, but Wix built the menu
 * itself from JS, so there is nothing to style -- these rules describe the
 * menu the shim creates. Colours come from the post card rather than being
 * invented, so it sits on the page rather than on top of it.
 * ------------------------------------------------------------------ */

.as-post-menu {
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 20;
  min-width: 160px;
  padding: 4px;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
}

.as-post-menu[hidden] { display: none !important; }

.as-post-menu-item {
  display: block;
  width: 100%;
  padding: 10px 14px;
  border: 0;
  border-radius: 4px;
  background: none;
  font: inherit;
  font-size: 15px;
  color: #2e3f66;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}

.as-post-menu-item:hover,
.as-post-menu-item:focus-visible { background: rgba(46, 63, 102, 0.08); }

/* Restored Wix VideoPlayer contents -- see scripts/fix-video-players.
 *
 * Wix sized the surrounding div and its runtime sized the video to match. With
 * the runtime gone the <video> falls back to the intrinsic size of whatever it
 * has to measure -- the 1920x1080 poster -- and renders 1920px wide inside a
 * 724px box, overflowing the page. `display: inline` also puts it on a text
 * baseline, which added 42px of descender space below it.
 *
 * So: fill the box Wix already sized, and never exceed it. `contain` letterboxes
 * rather than crops, because these are trailers with titles in frame and a
 * cover-crop would cut them off. */
.as-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}

/* Jobs page copy -- see scripts/fix-jobs.
 *
 * Wix's rich-text CSS sets margin:0 on every element inside a rich-text block,
 * and the captured page bought its spacing with <br> tags and paragraphs
 * containing a single zero-width space. Generated markup inherits the zeroes
 * and none of the fakery, so without this every heading, paragraph and list on
 * the page sits flush against the next one: measured gaps were 0-1px
 * throughout, headings included.
 *
 * Spacing is asymmetric on purpose. A heading belongs to what follows it, so
 * the space above it is roughly three times the space below -- that grouping
 * is what makes a long page scannable, and it is the thing that was missing.
 *
 * Scoped to .as-job-* so none of it reaches the rest of the site.
 *
 * Each class is doubled. Wix zeroes these margins with `.N8MGzv h3, .N8MGzv p,
 * ...{margin:0}`, which is a class plus an element and so outranks a single
 * class no matter what it says. Repeating the class buys a second class's
 * worth of specificity without inventing a wrapper or reaching for
 * !important, and without depending on Wix's generated class name. */
.as-job-section.as-job-section {
  /* ~43px above, which is about two lines of body text. 2.4em was three and
   * read as a gap rather than a break. */
  margin: 1.6em 0 0.55em;
  line-height: 1.25;
}

/* The first section in a posting is the first child of its block, so its top
 * margin was zeroed and the only space above it came from the 20px gap Wix
 * puts between the job-title block and the body block -- 20px against 65px
 * everywhere else, which is the difference that showed. This makes up the
 * difference so every section heading sits the same distance below whatever
 * precedes it, whether that is a paragraph or the job title. */
.as-job-section.as-job-section:first-child { margin-top: 23px; }

.as-job-label.as-job-label {
  margin: 1.6em 0 0.45em;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
  /* The colour the page already used for state names, moved off an inline
   * span so the level is styled in one place. */
  color: rgb(255, 205, 24);
}

.as-job-text.as-job-text { margin: 0 0 0.85em; }

.as-job-list.as-job-list {
  margin: 0 0 1.2em;
  padding-left: 1.4em;
}

/* Bullets carry their own paragraph; it must not add a second gap. */
.as-job-list.as-job-list .as-job-text.as-job-text { margin: 0; }
.as-job-list.as-job-list li + li { margin-top: 0.35em; }
.as-job-list.as-job-list ul { margin: 0.35em 0 0; }

/* A label never opens a block with a gap above it. */
.as-job-label.as-job-label:first-child { margin-top: 0; }

/* A date under a town in the hiring schedule: its own line, indented, and
 * deliberately without a bullet -- these are lines of one entry, not entries.
 * Doubled for the same specificity reason as the rules above. */
.as-job-date.as-job-date {
  display: block;
  padding-left: 1.1em;
}

/* ------------------------------------------------------------------
 * "Fall Adventures" label, Minecraft Marketing Materials  -- issue #2
 *
 * The label belongs above the trailer it names. It shipped below it, so it
 * read as the heading of an empty section -- which is what the original
 * report ("the heading floats and moves down the page") was describing. The
 * root cause of that report was a missing video, since fixed; this is the
 * half that remained, and the business asked for it to sit under "Program
 * Trailers" and above the video.
 *
 * Done in CSS rather than by moving the markup because this section is a Wix
 * mesh: the container is `display:grid` and each child is placed by an
 * explicit `grid-area`, so document order has no effect on what you see.
 * Moving the DOM would change the reading order for assistive technology
 * while leaving the page looking identical -- the worst of both.
 *
 * Specificity: the generated rule is
 *   [data-mesh-id=...] > [id="comp-..."]      (0,2,0)
 * so a bare id selector (1,0,0) wins regardless of source order. That matters
 * because one <style> block follows the shim link, so ties would go to the
 * page.
 *
 * The bottom margins trade places along with the rows, so the section's total
 * height is unchanged and nothing below it moves. The two builds use different
 * numbers, and the first attempt at this applied the desktop pair to both --
 * which grew the mobile section by 37px and shifted 31 elements down the page.
 * `.device-mobile-optimized` is Wix's own body class on the mobile build, so
 * this keys on which build is being served rather than on viewport width;
 * width would be the wrong axis, since the two builds are chosen by user agent
 * and a narrowed desktop window still gets the desktop one.
 *
 * No font-size change: the label is already `h4` under an `h3` and smaller in
 * both builds (28 vs 38 desktop, 20 vs 23 mobile).
 * ------------------------------------------------------------------ */

#comp-mtch9h8p {                 /* "Fall Adventures" -- was row 3, now row 2 */
  grid-area: 2 / 1 / 3 / 2;
  margin-bottom: 4px;            /* the video's old margin */
}

#comp-mtch7hh8 {                 /* the trailer video -- was row 2, now row 3 */
  grid-area: 3 / 1 / 4 / 2;
  margin-bottom: 63px;           /* the label's old margin */
}

.device-mobile-optimized #comp-mtch9h8p { margin-bottom: 10px; }
.device-mobile-optimized #comp-mtch7hh8 { margin-bottom: 20px; }


/* ---------------------------------------------------------------------------
 * Gallery image downloads
 *
 * Wix puts a download button at the right of the bar it draws across a hovered
 * gallery item, on the galleries the business enabled it for. The bar and its
 * grey come from the captured CSS and already work; only this button was drawn
 * by the runtime we removed, so what shipped was an empty bar (#101).
 *
 * Geometry measured on the live site rather than guessed: a 44x48 hit area
 * holding a 24x28 icon, 20px in from the bar's right edge and 15px up from its
 * bottom, white, at full opacity.
 * ------------------------------------------------------------------------- */

.as-gal-dl {
  position: absolute;
  right: 20px;
  bottom: 15px;
  width: 24px;
  height: 28px;
  padding: 10px;
  box-sizing: content-box;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The bar is what reacts to hover; this must not add a second background. */
  background: none;
  border: 0;
  text-decoration: none;
}

.as-gal-dl .as-dl-icon { width: 24px; height: 24px; display: block; }

/* Wix emits no hover overlay at all in the mobile markup -- the same reason
 * `fullscreen-enabled` is absent there -- so the phone build has no bar to put
 * this in and correctly shows no thumbnail button. Downloading on a phone goes
 * through the viewer, which opens on tap and carries its own button.
 *
 * This guard is therefore about a touch device served the DESKTOP tree, where
 * the bar does exist: with no hover to reveal it, fading the button in would
 * mean it never appeared, so it stays visible instead. */
@media (hover: hover) {
  .as-gal-dl { opacity: 0; transition: opacity .2s; }
  .gallery-item-container:hover .as-gal-dl,
  .gallery-item-container:focus-within .as-gal-dl { opacity: 1; }
}

/* Only what the shared control rule above does not already give an <a>. */
.as-lb-download { text-decoration: none; }

/* ------------------------------------------------------------------ footer
 *
 * The footer's three link lists are real <ul>s of <li>s with no list-style of
 * their own, so they inherited the browser's disc bullets -- which Wix shipped
 * and the business did not want (#29).
 *
 * list-style rather than ::marker or display:block on the <li>: the marker is
 * drawn OUTSIDE the content box here (the <ul> carries no padding-left, only a
 * ~9px margin), so removing it moves no text. Keeping the items as list-items
 * also keeps them a list for anything reading the page aloud, which changing
 * display would quietly undo. */
footer#SITE_FOOTER ul { list-style: none; }
