/* =====================================================================
   West London Orthodontist — shared site styles (site.css)
   Foundation used by every page: reset, scroll-reveal, nav underline,
   image hover-zoom, the mobile nav drawer, and the responsive/mobile layer.
   New treatment pages link this file and inherit all of the above.
   Page-specific styles live inline in each page's <head> <style> block.
   ===================================================================== */

/* WLO-431: every page redeclares this same theme-token set as an inline
   custom-property block on a wrapper div rather than at :root, so any
   section that ends up outside that div's subtree (a stray/extra closing
   tag anywhere above it is enough) silently loses every var(--ink),
   var(--bright) etc. below that point - which is exactly what "text
   styles not loading" looks like (transparent backgrounds, fonts still
   fine since body{color} happens to match --ink). Root-level fallbacks
   here guarantee every page resolves these regardless of its own nesting;
   the per-page inline declarations still take precedence where in scope. */
:root{--ink:#1c1c1c;--bone:#f5f0ea;--bright:#ffffff;--page:#f7f4ef;--mauve:#825f62;--mauve-deep:#4a3537;--mauve-900:#2e2122;--stone:#5b554f;--line:rgba(28,28,28,0.08);--metal:#b08a52}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
/* WLO-431 follow-up: the same per-page wrapper div also sets font-family
   inline, so any section outside its subtree fell back to the browser's
   default serif instead of Raleway. Fallback here for the same reason as
   the custom properties above. */
body{margin:0;background:#f7f4ef;color:#1c1c1c;font-family:'Raleway','Helvetica Neue',Arial,sans-serif}
img{display:block;max-width:100%}
a{color:inherit}
summary{list-style:none;cursor:pointer}
summary::-webkit-details-marker{display:none}
.reveal{opacity:0;transform:translateY(38px);transition:opacity 1s cubic-bezier(0.22,1,0.36,1),transform 1s cubic-bezier(0.22,1,0.36,1)}
.reveal.active{opacity:1;transform:translateY(0)}
.nav-link{position:relative;text-decoration:none}
.nav-link::after{content:'';position:absolute;width:0;height:1px;bottom:-5px;left:0;background:currentColor;transition:width .3s ease}
.nav-link:hover::after{width:100%}
.faq-plus{transition:transform .35s cubic-bezier(0.22,1,0.36,1)}
details[open] .faq-plus{transform:rotate(45deg)}
.rev-img{overflow:hidden}
.rev-img img{transition:transform 1.2s cubic-bezier(0.22,1,0.36,1)}
.rev-img:hover img{transform:scale(1.05)}

/* ── VIDEO SWAP: muted background loop → native player on click ───────
   Reusable component (JS in js/site.js, initVideoSwap). A short, silent,
   downscaled clip autoplays/loops to signal "there's a video here"; a
   click swaps in the real self-hosted <video controls> with sound,
   started inside the click handler so it counts as a user gesture and
   audio is allowed to play. No YouTube/lightbox iframe involved — every
   asset is served from /assets/videos/. Drop a `.vidswap` block anywhere
   a `.rev-img` media box would go; markup contract:
   <div class="vidswap" data-vidswap data-src="/assets/videos/....mp4">
     <video class="vidswap-bg" src="....-preview.mp4" autoplay muted loop playsinline preload="metadata"></video>
     <button class="vidswap-play" aria-label="Play video: …">…svg…</button>
   </div> */
.vidswap{position:relative;overflow:hidden;background:#1c1c1c}
.vidswap video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.vidswap-play{position:absolute;inset:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;border:none;padding:0;margin:0;background:rgba(16,12,11,0.16);cursor:pointer}
.vidswap-play span{width:64px;height:64px;border-radius:50%;background:rgba(245,240,234,0.92);display:flex;align-items:center;justify-content:center;transition:transform .3s ease,background .3s ease}
.vidswap-play:hover span,.vidswap-play:focus-visible span{transform:scale(1.08);background:#fff}
.vidswap-play:focus-visible{outline:2px solid #f5f0ea;outline-offset:-6px}
.vidswap.is-playing .vidswap-bg,.vidswap.is-playing .vidswap-play{display:none}
.vidswap-full{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000;display:none}
.vidswap.is-playing .vidswap-full{display:block}
/* Optional caption overlay for vidswap cards used in testimonial grids/carousels
   (e.g. patient label + treatment eyebrow). Sits above the preview loop and
   play button, hidden once the real video is swapped in. */
.vidswap-caption{position:absolute;left:0;right:0;bottom:0;padding:50px 20px 18px;background:linear-gradient(transparent,rgba(16,12,11,0.88));pointer-events:none;z-index:1}
.vidswap.is-playing .vidswap-caption{display:none}

/* ── VIDEO SWIPER — real Swiper.js instance for any horizontal strip of
   .vidswap cards (all forced to a 9:16 frame — see forceAspect in the
   renderVideoTestimonials calls). Built by renderVideoTestimonials with
   {swiper:true} (site.js) and initialised by window.initVideoSwiper().
   Needs the Swiper CDN bundle (swiper-bundle.min.css/js) loaded on the page.

   Controls sit in their OWN row below the slides at every breakpoint —
   never overlapping the video like the default Swiper skin (and unlike the
   homepage's still-image "Art of Transformation" gallerySwiper, which can
   get away with overlaying arrows on a static photo). Overlaid arrows here
   caused two real problems: the pagination dots sat on top of the bottom of
   the video card (invisible against the caption gradient), and a click
   meant for the arrow landed on the .vidswap-play button underneath it
   instead, triggering the video. A dedicated row below removes both.
   `.swiper.videoSwiper` (not just `.videoSwiper`): the Swiper CDN stylesheet
   loads after site.css on every page, so its unconditional `.swiper{
   display:block}` has equal specificity and later source order and would
   otherwise beat a single-class override regardless of viewport — doubling
   the selector wins on load order alone, no media query needed for that. */
.swiper.videoSwiper{--swiper-navigation-color:var(--mauve-deep);--swiper-navigation-size:20px;--swiper-pagination-color:var(--mauve-deep);--swiper-pagination-bullet-inactive-color:var(--ink);--swiper-pagination-bullet-inactive-opacity:0.22;display:grid;grid-template-columns:auto 1fr auto;align-items:center;column-gap:10px;row-gap:18px;padding:4px clamp(24px,5vw,60px) 24px}
.videoSwiper .swiper-wrapper{grid-column:1/4;grid-row:1}
.videoSwiper .swiper-slide{flex-shrink:0}
.videoSwiper .swiper-pagination{position:relative;grid-column:2;grid-row:2}
.videoSwiper .swiper-button-prev,.videoSwiper .swiper-button-next{position:relative;grid-row:2;top:auto;left:auto;right:auto;margin:0;width:44px;height:44px;background:#fff;border:1px solid var(--line);border-radius:0;box-shadow:0 14px 32px -16px rgba(28,28,28,0.3)}
.videoSwiper .swiper-button-prev{grid-column:1}
.videoSwiper .swiper-button-next{grid-column:3}
.videoSwiper .swiper-button-next:after,.videoSwiper .swiper-button-prev:after{font-size:15px}
.videoSwiper .swiper-button-disabled{opacity:0.35}
.videoSwiper .swiper-pagination-bullet{width:8px;height:8px;border-radius:50%;opacity:1;transition:width .3s ease,border-radius .3s ease}
.videoSwiper .swiper-pagination-bullet-active{width:22px;border-radius:4px}
@media (max-width:768px){
  .videoSwiper .swiper-button-prev,.videoSwiper .swiper-button-next{width:38px;height:38px}
}
.tlink{text-decoration:none;border-bottom:1px solid currentColor;padding-bottom:3px;transition:color .3s ease,border-color .3s ease}
.tlink:hover{color:#825f62;border-color:#825f62}
.gal-scroll{scrollbar-width:thin;scrollbar-color:rgba(28,28,28,0.25) transparent}
.gal-scroll::-webkit-scrollbar{height:7px}
.gal-scroll::-webkit-scrollbar-thumb{background:rgba(28,28,28,0.22)}

/* ── H1 ITALIC ACCENT (WLOU-31) ───────────────────────────────────────
   Every page H1 is sentence case, NOT uppercase. The closing part of the
   headline - the location, the promise, the qualifier - is set in the
   Playfair italic at regular weight, the treatment first used on the
   gapped-teeth page and now the site-wide standard.
   Markup contract: wrap that closing part in <em> inside the <h1>; do not
   split the heading into separate elements and do not repeat these
   declarations inline.
     <h1 style="...">Professional composite bonding dentists <em>in Hammersmith, West London</em></h1>
   text-transform:none is kept as a safety net so the accent stays
   sentence case even if a page's own H1 style is ever set to uppercase. */
h1 em{font-style:italic;font-weight:400;text-transform:none}
/* ── REVISED-COPY MARKERS ─────────────────────────────────────────────
   Add class "copy-draft" to any element — block OR inline — whose text was
   authored or reworked to fit a section, i.e. it does NOT reflect the
   verbatim content of the client's original/source page. It gets a subtle
   50%-opacity orange highlight so every revised asset is unmistakable.
   Mark the SPECIFIC element (heading, paragraph, span, step, list item…),
   never a wrapper, so it is always clear exactly what changed.
   TO DISABLE EVERYWHERE (every page, one edit): set --draft-bg to transparent.
   CURRENTLY DISABLED. The .copy-draft classes are deliberately left in the
   markup as the audit trail of which copy is AI-authored/reworked vs the
   client's verbatim source — do NOT strip them. To switch the highlight back
   on, restore --draft-bg to rgba(255,145,40,0.5) below. */
:root{--draft-bg:transparent;--hdr-h:60px}
.copy-draft{background-color:var(--draft-bg);box-decoration-break:clone;-webkit-box-decoration-break:clone;border-radius:2px}
@media (max-width:1080px){.wlo-nav2{display:none!important}.wlo-find{display:none!important}}

/* ── SITE NAV: DROPDOWNS / MEGA MENUS ─────────────────────────────────
   Structure and class-naming follow the signed-off nav component spec
   (nav_dropdown-trigger / nav_dropdown-wrapper / nav_dropdown-frame /
   nav_column-* / nav_menu-link). The SKIN is WLO's own and must not drift:
   the bar keeps its existing inline typography (13px / 600 / 0.2em / upper)
   and panels use the site's real card idiom — #fff on a var(--line) hairline
   with NO border-radius (the built pages use square corners throughout).

   Desktop (>1080px): hover to expand, parent stays directly clickable.
   Tablet and below (<=1080px): the whole bar is hidden and the drawer takes
   over, where the same groups become tap-to-expand <details> accordions.
   ------------------------------------------------------------------- */

/* Trigger — a real link (so the parent navigates) plus a caret. Inherits the
   bar's font, so it is pixel-identical to the plain .nav-link items. */
.nav_dropdown-trigger{position:relative;display:flex;align-items:center}
.nav_core-dropdown-trigger{display:flex;align-items:center;gap:9px;text-decoration:none;color:inherit;font:inherit;letter-spacing:inherit;text-transform:inherit}
.nav_caret{display:flex;align-items:center;justify-content:center;width:10px;height:7px;flex:none;transition:transform .3s ease}
.nav_caret svg{width:100%;height:100%;display:block}
.nav_dropdown-trigger.is-open .nav_caret{transform:rotate(180deg)}

/* Panel. Lives as a child of #siteHeader (not of the trigger) so a 4-column
   mega menu can never overflow the viewport at narrow desktop widths — it is
   inset to the header's own horizontal padding instead of centred on an item. */
/* visibility is stepped, not eased: it flips to visible the instant the panel
   opens (so a keyboard user tabbing straight in can reach the links), and is
   held back until the fade-out finishes when closing. Easing it in both
   directions would make focusability depend on transition progress. */
.nav_dropdown-wrapper{position:absolute;top:100%;z-index:5;opacity:0;visibility:hidden;transform:translateY(-8px);pointer-events:none;transition:opacity .28s ease,transform .28s cubic-bezier(0.22,1,0.36,1),visibility 0s linear .28s}
.nav_dropdown-wrapper.is-open{opacity:1;visibility:visible;transform:translateY(0);pointer-events:auto;transition:opacity .28s ease,transform .28s cubic-bezier(0.22,1,0.36,1),visibility 0s}
/* The wide Treatments panel is inset to the header's own padding — at 920px it
   is close enough to full-bleed that aligning it to its trigger would just push
   it off the right edge. */
.nav_dropdown-wrapper[data-nav-panel="treatments"]{left:clamp(24px,5vw,60px);width:min(920px,calc(100vw - 2*clamp(24px,5vw,60px)))}
/* Narrow panels sit directly below their trigger instead. Because the panel is
   a child of #siteHeader (see above), CSS cannot know the trigger's x position —
   flex layout decides it — so js/site.js sets `left` for anything marked
   data-nav-align="trigger", clamped to stay on screen. The value below is only
   the pre-JS fallback. */
.nav_dropdown-wrapper[data-nav-panel="about"]{left:clamp(24px,5vw,60px);width:264px}
.nav_dropdown-frame{background:#fff;border:1px solid rgba(28,28,28,0.08);box-shadow:0 26px 64px -22px rgba(28,28,28,0.32);padding:32px 34px 34px;color:#1c1c1c;font-family:'Raleway','Helvetica Neue',Arial,sans-serif}
.nav_dropdown-frame._4-columns{display:grid;grid-template-columns:repeat(4,1fr);gap:0 30px;align-items:start}
.nav_dropdown-frame.is-solo-column{padding:22px 24px}

/* Column head = clickable category. Small uppercase eyebrow per the brand
   rules (uppercase only for eyebrows/buttons/badges). */
.nav_column-header{display:block;margin-bottom:14px;padding-bottom:11px;border-bottom:1px solid rgba(28,28,28,0.08)}
.nav_column-header a,.nav_column-header span{display:block;text-decoration:none;color:#825f62;font-size:11px;font-weight:700;letter-spacing:0.2em;text-transform:uppercase;line-height:1.4;transition:color .3s ease}
.nav_column-header a:hover{color:#4a3537}
.nav_column-list{display:flex;flex-direction:column}

/* Items — sentence case body type, arrow slides in on hover. */
.nav_menu-link{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 0;text-decoration:none;color:#1c1c1c;font-size:14.5px;font-weight:500;letter-spacing:0.01em;text-transform:none;line-height:1.35;transition:color .3s ease}
.nav_menu-link:hover{color:#825f62}
.nav_menu-link .icon_wrapper{display:flex;align-items:center;justify-content:center;width:16px;height:13px;flex:none;opacity:0;transform:translateX(-6px);transition:opacity .3s ease,transform .3s ease}
.nav_menu-link .icon_wrapper svg{width:100%;height:100%;display:block}
.nav_menu-link:hover .icon_wrapper{opacity:1;transform:translateX(0)}

/* ── BOOK BUTTON ──────────────────────────────────────────────────────
   Two variants ship in the markup and only ONE is ever present on a page:
   .nav_book-wrap (dual-option hover menu) on every normal page, or the plain
   single "Book a Consultation" anchor on the consultation/contact pages.
   The dropdown div is REMOVED on single-option pages, not merely hidden. */
.nav_book-wrap{position:relative;display:flex;align-items:center;flex:none}
.nav_book-toggle{display:inline-flex;align-items:center;gap:10px;cursor:pointer}
.nav_book-toggle .nav_caret{width:9px;height:6px}
.nav_book-wrap.is-open .nav_caret{transform:rotate(180deg)}
.nav_book-menu{position:absolute;top:calc(100% + 12px);right:0;min-width:246px;background:#fff;border:1px solid rgba(28,28,28,0.08);box-shadow:0 26px 64px -22px rgba(28,28,28,0.32);padding:8px;z-index:6;opacity:0;visibility:hidden;transform:translateY(-8px);pointer-events:none;transition:opacity .28s ease,transform .28s cubic-bezier(0.22,1,0.36,1),visibility 0s linear .28s}
.nav_book-wrap.is-open .nav_book-menu{opacity:1;visibility:visible;transform:translateY(0);pointer-events:auto;transition:opacity .28s ease,transform .28s cubic-bezier(0.22,1,0.36,1),visibility 0s}
.nav_book-menu a{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 14px;text-decoration:none;color:#1c1c1c;font-family:'Raleway','Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:600;letter-spacing:0.14em;text-transform:uppercase;line-height:1.3;transition:background .3s ease,color .3s ease}
.nav_book-menu a:hover{background:#f5f0ea;color:#825f62}
.nav_book-menu .icon_wrapper{display:flex;align-items:center;justify-content:center;width:15px;height:12px;flex:none;opacity:0;transform:translateX(-6px);transition:opacity .3s ease,transform .3s ease}
.nav_book-menu .icon_wrapper svg{width:100%;height:100%;display:block}
.nav_book-menu a:hover .icon_wrapper{opacity:1;transform:translateX(0)}

/* ── EXTERNAL-LINK ICON ───────────────────────────────────────────────
   Reusable marker for any CTA that opens the external online-booking
   widget (target="_blank"). Sits to the right of the button text.
   Markup contract: <span class="icon-ext" aria-hidden="true"><svg>…</svg></span> */
.icon-ext{display:inline-flex;align-items:center;justify-content:center;width:12px;height:12px;margin-left:8px;flex:none;vertical-align:middle}
.icon-ext svg{width:100%;height:100%;display:block}

/* The single-option variant ("Book a Consultation") is ~3x the width of "Book",
   so it needs to give a little back at narrow desktop widths or it pushes the
   bar over. Same colours, same weight — only the breathing room tightens. */
.nav_book-single{display:inline-flex;align-items:center;gap:10px}
@media (min-width:1081px) and (max-width:1280px){
  .nav_book-single{padding:13px 18px!important;letter-spacing:0.12em!important}
  /* Even trimmed, the long label leaves the bar ~28px short at the tightest
     desktop band (1081px, just above the drawer breakpoint). Scoped with
     :has() to the pages that actually carry it, so the 20 pages using the
     compact "Book" button keep their signed-off 42px spacing untouched.
     Where :has() is unsupported the rule is simply ignored — the bar is a
     little tight, nothing breaks. */
  #siteHeader nav:has(.nav_book-single) .wlo-nav2{gap:30px!important}
}

/* Panels belong to the desktop bar only — the drawer handles <=1080px. */
@media (max-width:1080px){.nav_dropdown-wrapper{display:none!important}}

/* ===== MOBILE LAYER (does not affect desktop) ===== */
.wlo-burger{display:none;align-items:center;justify-content:center;width:44px;height:44px;background:none;border:none;cursor:pointer;color:currentColor;padding:0;flex:none}
.m-phone{display:none;align-items:center;justify-content:center;width:44px;height:44px;text-decoration:none;color:currentColor;flex:none}
.viddot{position:relative}
.viddot::after{content:'';position:absolute;inset:-18px}
.m-drawer{position:fixed;inset:0;z-index:120;visibility:hidden;pointer-events:none}
.m-drawer.open{visibility:visible;pointer-events:auto}
.m-drawer-scrim{position:absolute;inset:0;background:rgba(16,12,11,0.55);opacity:0;transition:opacity .3s ease}
.m-drawer.open .m-drawer-scrim{opacity:1}
.m-drawer-panel{position:absolute;top:0;right:0;height:100%;width:min(86vw,360px);background:#1c1c1c;color:#f5f0ea;transform:translateX(100%);transition:transform .35s cubic-bezier(0.22,1,0.36,1);display:flex;flex-direction:column;padding:24px;overflow-y:auto;box-shadow:-30px 0 80px -30px rgba(0,0,0,0.6)}
.m-drawer.open .m-drawer-panel{transform:translateX(0)}
.m-drawer-panel a{display:flex;align-items:center;min-height:50px;text-decoration:none;color:#f5f0ea;font-size:14px;font-weight:600;letter-spacing:0.16em;text-transform:uppercase;border-bottom:1px solid rgba(245,240,234,0.12)}

/* ── DRAWER: TAP-TO-EXPAND GROUPS (tablet and below) ──────────────────
   The desktop hover mega menus become tap toggles here, per the nav spec.
   Built on <details>/<summary> — the same idiom the FAQ sections already
   use — so this needs no JavaScript at all. Sub-links are indented and
   de-emphasised (sentence case) so the group's own label stays the
   dominant hit target. */
.m-drawer-group{border-bottom:1px solid rgba(245,240,234,0.12)}
.m-drawer-group>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:50px;color:#f5f0ea;font-size:14px;font-weight:600;letter-spacing:0.16em;text-transform:uppercase}
.m-drawer-group>summary .nav_caret{width:11px;height:8px;color:#f5f0ea;opacity:.75}
.m-drawer-group[open]>summary .nav_caret{transform:rotate(180deg)}
.m-drawer-sub{padding:2px 0 14px}
.m-drawer-sub .m-drawer-cat{display:block;padding:12px 0 4px;color:#b08a52;font-size:10.5px;font-weight:700;letter-spacing:0.2em;text-transform:uppercase;text-decoration:none;min-height:0;border-bottom:none}
/* "All treatments" is the group's overview link, not a category — without a
   divider it reads as a category that happens to have no items under it. */
.m-drawer-sub .m-drawer-cat.is-overview{padding:6px 0 12px;margin-bottom:6px;border-bottom:1px solid rgba(245,240,234,0.12)}
.m-drawer-sub a:not(.m-drawer-cat){min-height:44px;padding-left:14px;font-size:13.5px;font-weight:500;letter-spacing:0.02em;text-transform:none;color:rgba(245,240,234,0.86);border-bottom:none}
.m-drawer-sub a:not(.m-drawer-cat):hover{color:#fff}

@media (max-width:1080px){ .wlo-burger{display:flex} }
@media (max-width:680px){ .m-phone{display:flex}.wlo-phone2{display:none!important} }

/* ── BOOK BUTTON: ICON-ONLY ON NARROW MOBILE ───────────────────────────
   Below 680px there's no room for "Book"/"Book a Consultation" plus a
   caret next to the phone and burger icons. The label and caret hide;
   a calendar glyph becomes the button's whole content, at the same
   44px square footprint as the icons beside it. The anchor's aria-label
   (not the now-hidden visible text) is what keeps this accessible at
   every width. */
.nav_book-icon{display:none;align-items:center;justify-content:center;width:18px;height:18px;flex:none}
.nav_book-icon svg{width:100%;height:100%;display:block}
@media (max-width:680px){
  .nav_book-icon{display:flex}
  .nav_book-label{display:none}
  .nav_book-toggle .nav_caret,.nav_book-single .nav_caret{display:none}
  .nav_book-toggle,.nav_book-single{width:44px!important;height:44px!important;padding:0!important;justify-content:center;gap:0}
}

/* ── TREATMENT PAGE HERO (bento) — MOBILE ─────────────────────────────
   Below 860px the two-column hero grid stacks into one column. The text
   +CTA column is left untouched (it already reads top-to-bottom in the
   right order). Only .ihero-right — which normally positions the photo
   and its two overlay link-cards on top of each other — is set to
   display:contents, freeing the photo and the link-cards to become
   independent, orderable items of the single-column grid instead of one
   stacked box. Only the photo gets an explicit order, pulling it above
   the text+CTA column as a banner; the link-cards keep their natural
   document order, which — since .ihero-right comes after the text+CTA
   column in the markup — lands them after the CTA panel ("Book a
   Consultation" / "Call us"), not overlaid on the photo. The photo's own
   inline absolute-positioning (built for sitting inside the old fixed-
   height box) and the link-cards' inline absolute-positioning (built
   for overlaying the photo) are both switched back to normal flow, and
   the link-cards' 2-column grid is forced to 1 column so it can't force
   the row wider than the viewport. The breadcrumb is trimmed to its last
   two crumbs (category + current page) to avoid wrapping across
   multiple lines on narrow screens. */
@media (max-width:860px){
  .ihero-grid{grid-template-columns:1fr!important}
  .ihero-grid > div{min-width:0!important}
  .ihero-right{display:contents!important}
  .ihero-right > img{order:-1;position:static!important;width:100%!important;height:clamp(176px,27.2vh,256px)!important}
  .ihero-right > .ihero-media{order:-1;position:relative!important;width:100%!important;height:clamp(176px,27.2vh,256px)!important;overflow:hidden}
  .ihero-right > div{position:static!important;grid-template-columns:1fr!important}
  .ihero-grid nav[aria-label="Breadcrumb"] > *:not(:nth-last-child(-n+3)){display:none}
}

/* Children's Dentistry uses its own one-off hero classes (.ch-grid /
   .ch-img) instead of the shared .ihero-grid / .ihero-right bento
   pattern above, but the same mobile treatment applies: photo to the
   top, its overlay link-cards back to normal flow (landing after the
   CTA row instead of on top of the photo), breadcrumb trimmed. */
@media (max-width:860px){
  .ch-grid{grid-template-columns:1fr!important}
  .ch-grid > div{min-width:0!important}
  .ch-img{display:contents!important}
  .ch-img > img{order:-1;position:static!important;width:100%!important;height:clamp(220px,34vh,320px)!important}
  .ch-img > div{position:static!important;grid-template-columns:1fr!important}
  .ch-grid nav[aria-label="Breadcrumb"] > *:not(:nth-last-child(-n+3)){display:none}
}
@media (max-width:768px){
  #dc-root p{font-size:16px !important;line-height:1.7 !important}
  #siteHeader nav{padding:13px clamp(14px,5vw,22px) !important}
  #siteHeader a[aria-label="West London Orthodontist"] img{height:34px !important}
  .baprev,.banext{min-width:44px;min-height:44px}
  #explained .itab-row{display:none !important}
  #explained .ipanel{display:grid !important;grid-template-columns:1fr !important;margin-bottom:34px;scroll-margin-top:calc(var(--hdr-h) + 64px)}
  #explained .ipanel h3{margin-top:1rem !important}
}

/* ── TAB BAR OVERFLOW ──────────────────────────────────────────────────
   Above 768px the real `.itab-row` is visible and its tabs are
   `white-space:nowrap`. The Invisalign page carries five of them
   (WLOU-19) which is wider than the column between 769px and roughly
   1000px, and that pushed the whole page into horizontal scroll. Let the
   bar scroll inside itself instead. Harmless on the 2- and 3-tab pages:
   an `auto` scroller shows nothing until its content actually overflows.
   Scrollbar hidden to match `.itab-mnav-inner` below; the clipped last
   tab is the affordance that there is more to the right. */
.itab-row{overflow-x:auto;scrollbar-width:none}
.itab-row::-webkit-scrollbar{display:none}

/* ── MOBILE TABS QUICK-NAV ─────────────────────────────────────────────
   Built by js/site.js (initTabsMnav) for every `.itab-row` tab bar. At
   <=768px the real tab bar is hidden (above) and every panel is stacked
   instead, so this sticky row of anchor links — one per tab — lets a
   mobile visitor jump straight to a panel instead of scrolling past the
   ones before it. Sticks just under the fixed header ("in the nav")
   while its section is on screen, then scrolls away with the page once
   the section is passed, because it's an in-flow child of that section. */
.itab-mnav{display:none}
@media (max-width:768px){
  .itab-mnav{display:block;position:sticky;top:var(--hdr-h);z-index:70;background:var(--page,#f7f4ef)}
  .itab-mnav-inner{display:flex;gap:22px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;padding:14px 0;border-bottom:1px solid rgba(28,28,28,0.08)}
  .itab-mnav-inner::-webkit-scrollbar{display:none}
  .itab-mlink{flex:none;white-space:nowrap;text-decoration:none;font-size:12px;font-weight:600;letter-spacing:0.14em;text-transform:uppercase;color:var(--stone,#5b554f);padding-bottom:4px;border-bottom:2px solid transparent;transition:color .3s ease,border-color .3s ease}
  .itab-mlink.on{color:var(--ink,#1c1c1c);border-color:var(--mauve,#825f62)}
}
@media (max-width:560px){
  #dc-root h1{font-size:clamp(38px,12.5vw,60px) !important}
}
/* ── IN-PAGE LINKS TO A FORM ───────────────────────────────────────────
   The header is fixed, so a link like href="#bookform" put the form's
   top edge at y=0 - behind the header - which hid the first field or two.
   Offset the scroll target by the real header height (--hdr-h, kept in
   sync by js/site.js, 60px fallback on :root) plus breathing room.

   Deliberately not done globally with `html{scroll-padding-top}`:
   `#explained .ipanel` already carries its own scroll-margin-top and the
   two would stack, overshooting those panels on mobile. */
#bookform,
#contactform,
#referralform,
#call-log-form{scroll-margin-top:calc(var(--hdr-h) + 28px)}

/* ── ACTIVECAMPAIGN FIELD TEXT ON THE DARK FORM PANELS ─────────────────
   The embed ships its own rule:

     #_form_<runtime id>_ ._form_element :is(textarea, select,
       input[type="text"], input[type="date"], input[type="phone"],
       input[type="email"]) { color:#142341 !important }

   #142341 is near-black navy, effectively unreadable on the dark panels
   these two forms sit on. The page-local overrides only covered text,
   tel, date and textarea, so `select` and `email` fields were still
   dark. That runtime id changes per form instance so it cannot be
   targeted directly - the doubled wrapper id below carries two IDs
   against the embed's one, which wins the cascade outright regardless of
   class count.

   #referralform joined this list once its placeholder markup was replaced
   with a real AC embed. Still excluded: the phone-calls form, which sits on
   a white panel where the embed's dark text is correct. */
#bookform#bookform ._form_element :is(textarea, select, input[type="text"], input[type="email"], input[type="phone"], input[type="tel"], input[type="date"], input[type="number"]),
#contactform#contactform ._form_element :is(textarea, select, input[type="text"], input[type="email"], input[type="phone"], input[type="tel"], input[type="date"], input[type="number"]),
#referralform#referralform ._form_element :is(textarea, select, input[type="text"], input[type="email"], input[type="phone"], input[type="tel"], input[type="date"], input[type="number"]){color:#f5f0ea !important}

#bookform#bookform ._form_element :is(textarea, input)::placeholder,
#contactform#contactform ._form_element :is(textarea, input)::placeholder,
#referralform#referralform ._form_element :is(textarea, input)::placeholder{color:rgba(245,240,234,0.45) !important;opacity:1 !important}

/* A white-on-dark <select> would otherwise render its dropdown list as
   white text on the OS default light menu. Keep the options legible. */
#bookform#bookform ._form_element select option,
#contactform#contactform ._form_element select option,
#referralform#referralform ._form_element select option{color:#1c1c1c !important;background:#f5f0ea !important}

/* WLO-436: team band CTAs right-align on desktop, stay full-width stacked on mobile */
#dc-root .wlo-fullband-cta{align-items:flex-end !important}
@media (max-width:768px){
  #dc-root [style*="repeat(4,1fr)"]{grid-template-columns:1fr 1fr !important}
  #dc-root [style*="repeat(3,1fr)"]{grid-template-columns:1fr !important}
  #dc-root [style*="repeat(8,1fr)"]{grid-template-columns:repeat(4,1fr) !important}
  #dc-root [style*="repeat(12,1fr)"]{grid-template-columns:1fr !important}
  #dc-root [style*="repeat(12,1fr)"] > *{grid-column:auto !important;grid-row:auto !important}
  #dc-root [style*="grid-template-columns:2fr 1fr"]{grid-template-columns:1fr !important}
  #dc-root [style*="grid-template-columns:1fr 1fr"]{grid-template-columns:1fr !important}
  #dc-root [style*="1.7fr 1fr 1fr 1.25fr"]{grid-template-columns:1fr !important}
  #dc-root .wc-grid > .rev-img{order:-1}
  #dc-root .ig-handle{font-size:clamp(20px,7vw,62px) !important}
  #dc-root #gallery h2{font-size:clamp(30px,9.5vw,112px) !important}
  #dc-root .wlo-fullband{height:clamp(260px,42vh,420px) !important}
  #dc-root .wlo-fullband-cta{align-items:stretch !important}
}

/* ── SPECIALIST JOURNEY — mobile fallback ─────────────────────────────
   .jstep/.jdot/#journeyTrack markup is duplicated verbatim across many
   pages (index.html, our-team, several treatment pages, book/consultation).
   The desktop zig-zag uses absolute left/right % positions with no mobile
   fallback, so steps overlap the snake path on narrow screens. Fixing it
   once here applies everywhere the component is reused: drop the SVG
   snake + standalone dots, and draw the line/dot as pseudo-elements on
   each .jstep instead, stacked in normal flow. No JS changes needed —
   the scroll handler already toggles .on on both .jstep and .jdot. */
@media (max-width:768px){
  #journeyTrack{height:auto !important;padding-left:18px}
  #journeyTrack > svg,#journeyTrack > .jdot{display:none !important}
  .jstep{position:relative !important;top:auto !important;left:0 !important;right:auto !important;
    text-align:left !important;padding:0 0 44px 26px !important;transform:none !important}
  .jstep::before{content:'';position:absolute;left:-1px;top:0;bottom:-44px;width:1px;background:rgba(245,240,234,0.13)}
  .jstep:last-child::before{bottom:6px}
  .jstep::after{content:'';position:absolute;left:-7px;top:6px;width:13px;height:13px;border-radius:50%;
    background:rgba(245,240,234,0.25);transition:background .5s ease,box-shadow .5s ease}
  .jstep.on::after{background:#b08a52;box-shadow:0 0 0 6px rgba(176,138,82,0.16)}
}

/* ── STICKY BOTTOM NAV (mobile phones only — portrait & landscape) ────
   Appears once the page has scrolled ~400px (see initStickyNav in
   site.js). Deliberately excluded on tablet at any orientation: a
   width-only breakpoint can't tell a phone-landscape viewport from a
   tablet-portrait one (both land around 700-800px wide), so this uses
   width for portrait and a short-viewport landscape check for phones
   (tablets in landscape are much taller than a phone in landscape). */
.wlo-stickynav{position:fixed;left:0;right:0;bottom:0;z-index:70;display:none;
  transform:translateY(100%);transition:transform .32s ease}
@media (max-width:767px),(max-height:500px) and (orientation:landscape){
  .wlo-stickynav{display:block}
}
.wlo-stickynav.is-visible{transform:translateY(0)}
/* The top keyline and the lifted shadow are deliberate (UAT #WLOU-3
   follow-up): on pages that end on a dark footer the bar's #2e2122 sat
   against a near-identical background and stopped reading as a floating
   overlay - it looked like part of the page. The 1px --metal hairline
   gives it a defined top edge on light and dark alike, and the deeper
   shadow separates it from whatever is behind. Do not drop these back
   to a plain background. */
.wlo-stickynav-bar{display:flex;align-items:stretch;background:#2e2122;
  border-top:1px solid rgba(176,138,82,0.7);
  box-shadow:0 -6px 18px rgba(0,0,0,0.26)}
.wlo-stickynav-call{flex:none;width:64px;display:flex;align-items:center;justify-content:center;
  color:#f5f0ea;border-right:1px solid rgba(245,240,234,0.14)}
/* padding is a deliberate 22px 18px, not the 16px 12px it shipped with: the shorter
   button was a fiddly tap target on a phone (UAT #WLOU-3). Signed off 2026-09-09. */
.wlo-stickynav-book{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;
  background:none;border:none;color:#f5f0ea;font-family:inherit;font-size:12px;font-weight:600;
  letter-spacing:0.18em;text-transform:uppercase;padding:22px 18px;cursor:pointer}
.wlo-stickynav-caret{display:inline-flex;transform:rotate(180deg);transition:transform .24s ease}
.wlo-stickynav.is-open .wlo-stickynav-caret{transform:rotate(0deg)}
.wlo-stickynav-menu{position:absolute;left:0;right:0;bottom:100%;background:#4a3537;
  box-shadow:0 -6px 24px rgba(0,0,0,0.18);max-height:0;overflow:hidden;
  transition:max-height .28s ease}
.wlo-stickynav.is-open .wlo-stickynav-menu{max-height:160px}
.wlo-stickynav-menuitem{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:16px 20px;color:#f5f0ea;text-decoration:none;font-size:13px;font-weight:600;
  letter-spacing:0.05em;border-top:1px solid rgba(245,240,234,0.1)}
.wlo-stickynav-menuitem:first-child{border-top:none}
.wlo-stickynav-menuitem svg{flex:none;opacity:1}

/* ── HOMEPAGE HERO ON SHORT DESKTOP VIEWPORTS (UAT #WLOU-38) ──────────
   The homepage hero is height:100vh with its content vertically centred,
   but at full desktop scale that content is ~710px tall. On a laptop at
   1536x703 (the reported case) it is taller than the viewport, so
   centring pushed the logo up behind the 88px fixed header - "too close
   to the main nav" - and pushed the "Book a consultation" CTA row below
   the fold at the same time (measured: the logo starts colliding at
   ~819px of viewport height).
   Two things fix it. The 90px padding-top clears the header: the content
   stays vertically CENTRED - top-aligning it instead left a big dead
   band at the bottom on mid-height screens - but it is now centred in
   the space below the header rather than in the whole viewport, so the
   top and bottom gaps come out equal at every height. Because the
   section is height:100vh under the global border-box, that padding
   does not make it taller than the viewport. Then the logo and headline
   scale against viewport HEIGHT as well as width, so the whole hero -
   logo through CTAs - still fits once the header is accounted for.
   Measured at 1536x703: logo 36px clear of the header (was 46px behind
   it), CTA row 33px clear of the bottom (was 51px past it).
   The 900px ceiling is deliberately above the ~819px where the logo
   actually collides: between those two the logo cleared the header by
   only a few pixels, which is the same "too close to the main nav"
   complaint. Desktop-only (min-width:1081px): below that the mobile
   layer owns the hero, and a phone in landscape is a separate case.
   The .hero-sect / .hero-shell hooks exist only on index.html. */
@media (min-width:1081px) and (max-height:900px){
  .hero-sect{padding-top:90px!important}
  .hero-shell{margin-top:0!important}
  .hero-up-1{height:clamp(54px,9.5vh,98px)!important;margin:0 auto 12px!important}
  .hero-up-2{font-size:clamp(48px,min(12vw,19.5vh),178px)!important;margin-bottom:18px!important}
  .hero-up-3{margin-bottom:26px!important}
}

/* ── HOMEPAGE HERO: CAMPAIGN TAKEOVER (Smile Discovery Day, Thu 1 Oct 2026) ──
   Scoped to .hero-takeover on the hero <section>. These rules go dead the
   moment that class/block is removed - see HERO-TAKEOVER.md for the revert.
   Reason they exist: the type scale above is tuned for the standard hero's
   two-word "Specialist care" headline, at up to 178px. The takeover headline
   is a full sentence, so it needs its own much smaller scale - and its own
   short-viewport override, because the WLOU-38 rule sets font-size with
   !important and would otherwise blow this headline up to ~137px on a
   laptop. Specificity (0,2,0) + !important is what beats it. */
.hero-takeover .hero-up-2{font-size:clamp(30px,4.6vw,62px);line-height:1.07}
@media (min-width:1081px) and (max-height:900px){
  .hero-takeover .hero-up-2{font-size:clamp(28px,min(4.6vw,7.6vh),62px)!important}
}
/* Under 560px a pre-existing global rule (#dc-root h1) forces
   clamp(38px,12.5vw,60px)!important on EVERY h1. That is sized for the
   standard hero's two-word headline; it drove this sentence-length one to
   47px and four lines, filling the phone screen. Beaten here only by
   out-specifying it - #dc-root (1,0,1) + !important needs (1,2,1) + !important
   in reply, hence the id in this selector. */
@media (max-width:560px){
  #dc-root .hero-takeover .hero-up-2{font-size:clamp(26px,8.4vw,38px)!important}
}

/* extra bottom breathing room on tablet/mobile so the fixed .wlo-stickynav bar never crowds the footer credit line */
@media (max-width:1024px){.site-footer{padding-bottom:calc(40px + 1rem) !important}}

/* hero "explained"/"journey" jump-link pills: solid (not translucent)
   background on mobile, and no gap between the two of them. Breakpoint must
   match the 860px one above that stacks the hero — the translucent, gapped
   treatment only reads correctly while the pills still overlay the photo. */
@media (max-width:860px){
  .hv4-pill{background:rgba(20,16,16,1) !important}
  .hv4-wrap{gap:0 !important}
}
/* pill's circular arrow icon: same solid-mauve hover fill used by the .hv2 slider-arrow buttons elsewhere on the site */
.hv4-pill:hover .hv4-icon,.hv4-pill:focus-visible .hv4-icon,.hv4-pill:active .hv4-icon{background:#816062 !important;border-color:#816062 !important;color:#f5f0ea !important}

/* ── PROMO POPUPS (.wlo-popup) ─────────────────────────────────────────
   Behaviour, markup contract and the cookie logic live in js/site.js and
   CONVENTIONS.md. A page opts in by shipping the block; nothing here
   shows anything on its own - JS adds .is-open.
   The panel is capped at 92vh with its own scroll so a long promo can
   never trap content off-screen on a short laptop or a phone in
   landscape, which is the usual way popups break. */
.wlo-popup{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;
  padding:clamp(16px,4vw,40px);opacity:0;pointer-events:none;
  transition:opacity .26s cubic-bezier(0.22,1,0.36,1)}
.wlo-popup.is-open{opacity:1;pointer-events:auto}
.wlo-popup[hidden]{display:none!important}
.wlo-popup-backdrop{position:absolute;inset:0;background:rgba(20,16,16,0.62);backdrop-filter:blur(4px);cursor:pointer}
.wlo-popup-panel{position:relative;z-index:1;width:100%;max-width:560px;max-height:92vh;overflow-y:auto;
  background:var(--bright);border-top:3px solid var(--metal);
  box-shadow:0 30px 80px rgba(20,16,16,0.34);
  transform:translateY(14px) scale(0.985);
  transition:transform .32s cubic-bezier(0.22,1,0.36,1)}
.wlo-popup.is-open .wlo-popup-panel{transform:translateY(0) scale(1)}
/* z-index + a translucent scrim: stacked, this button sits ON the photo,
   where a bare dark glyph can vanish against the image. */
.wlo-popup-close{position:absolute;top:12px;right:12px;z-index:3;width:40px;height:40px;display:flex;
  align-items:center;justify-content:center;border:none;padding:0;cursor:pointer;
  background:rgba(247,244,239,0.86);backdrop-filter:blur(3px);
  color:var(--ink);border-radius:50%;transition:background .25s ease,color .25s ease}
.wlo-popup-close:hover{background:var(--bright);color:var(--mauve-deep)}
.wlo-popup-close:focus-visible,.wlo-popup-cta:focus-visible,.wlo-popup-dismiss:focus-visible{
  outline:2px solid var(--mauve);outline-offset:3px}
.wlo-popup-eyebrow{display:block;font-size:10px;font-weight:600;letter-spacing:0.28em;
  text-transform:uppercase;color:var(--mauve);margin-bottom:18px}
.wlo-popup-title{font-family:'Playfair Display',Georgia,serif;font-weight:700;
  font-size:clamp(27px,3.2vw,40px);line-height:1.06;letter-spacing:-0.02em;color:var(--ink);margin:0 0 16px}
.wlo-popup-text{font-weight:300;font-size:16px;line-height:1.7;color:var(--stone);margin:0 0 26px}
/* The CTA is sticky INSIDE the scrolling panel so the only action is never
   below the popup's own fold. It matters on small phones: at 320x568 the
   content is ~589px against a 497px panel, and without this the button
   needed a scroll inside the popup to reach. The upward shadow in the page
   colour masks text sliding underneath. On roomier screens nothing scrolls,
   so sticky is a no-op and the button sits in normal flow. */
.wlo-popup-cta{position:sticky;bottom:0;z-index:2;
  display:block;text-align:center;text-decoration:none;background:var(--mauve-deep);
  color:#f5f0ea;padding:19px 32px;font-size:11px;font-weight:600;letter-spacing:0.2em;
  text-transform:uppercase;transition:background .3s ease;
  box-shadow:0 -16px 18px var(--bright)}
.wlo-popup-cta:hover{background:var(--mauve-900)}
.wlo-popup-dismiss{display:block;width:100%;margin:16px 0 0;background:none;border:none;padding:6px;
  font-family:inherit;font-size:11px;font-weight:600;letter-spacing:0.14em;text-transform:uppercase;
  color:var(--stone);cursor:pointer;transition:color .25s ease}
.wlo-popup-dismiss:hover{color:var(--ink)}

/* ── PHOTO BESIDE THE OFFER ─────────────────────────────────────────
   Two shapes, chosen on WIDTH not device: a narrow screen stacks the photo
   as a banner above the copy; anything from 620px up puts it in a column
   beside the copy. Width is the right axis because a phone in LANDSCAPE is
   wide and short - side-by-side costs it no vertical space at all, which
   is exactly what it has least of. */
.wlo-popup-media{overflow:hidden;background:var(--bone);height:clamp(158px,26vh,225px)}
/* The two layouts crop the SAME photo on different axes, which is easy to
   get wrong. As a wide banner (375x211 from a 640x742 portrait) the overflow
   is vertical, so the Y value chooses the slice: ~45% lands the eyes-to-chin
   band in frame. A lower number rides up and cuts the chin, a higher one
   drops onto the neck. In the tall column the box is narrower than the
   scaled photo, so the overflow is HORIZONTAL and Y does nothing at all -
   only X matters there, and centred is right. */
.wlo-popup-media img{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 45%}
.wlo-popup-body{padding:clamp(24px,3.4vw,40px)}
.wlo-popup-person-name{display:block;font-family:'Playfair Display',Georgia,serif;font-weight:600;
  font-size:clamp(15px,1.6vw,17px);line-height:1.2;color:var(--ink)}
.wlo-popup-person-role{display:block;font-size:10px;font-weight:600;letter-spacing:0.16em;
  text-transform:uppercase;color:var(--mauve);margin:4px 0 clamp(14px,1.8vw,20px)}

@media (min-width:620px){
  /* Panel owns no padding here - the body column does, so the photo can run
     full-bleed to the panel edges. The panel stops scrolling and the BODY
     scrolls instead, so a long promo never drags the photo out of frame. */
  .wlo-popup-panel{max-width:780px;padding:0;display:grid;
    grid-template-columns:minmax(0,38%) minmax(0,1fr);overflow:hidden}
  .wlo-popup-media{height:auto;min-height:100%}
  .wlo-popup-body{min-height:0;overflow-y:auto;padding:clamp(30px,3.2vw,44px)}
}

/* Narrow AND short (a small phone in portrait): the banner is the first
   thing worth losing, so the copy and the CTA still fit without scrolling. */
@media (max-width:619px) and (max-height:620px){
  .wlo-popup-media{display:none}
}


/* countdown: JS writes the numbers, and an already-passed target stops the
   popup showing at all rather than counting into negatives */
.wlo-popup-countdown{display:flex;gap:10px;margin:0 0 28px}
.wlo-popup-cd-cell{flex:1;background:var(--page);border:1px solid var(--line);padding:12px 6px;text-align:center}
.wlo-popup-cd-num{display:block;font-family:'Playfair Display',Georgia,serif;font-weight:700;
  font-size:26px;line-height:1;color:var(--mauve-deep);font-variant-numeric:tabular-nums}
.wlo-popup-cd-label{display:block;font-size:9px;font-weight:600;letter-spacing:0.16em;
  text-transform:uppercase;color:var(--stone);margin-top:7px}

@media (max-width:560px){
  .wlo-popup{align-items:flex-end;padding:0}
  .wlo-popup-panel{max-width:none;max-height:88vh;border-top-width:3px}
  .wlo-popup.is-open .wlo-popup-panel{transform:translateY(0)}
  .wlo-popup-panel{transform:translateY(28px)}
  .wlo-popup-countdown{gap:6px}
  .wlo-popup-cd-num{font-size:22px}
}

/* Short viewports (small phones, and any phone in landscape): claw back
   vertical space so the whole promo fits without an internal scroll. Nothing
   is removed - only the spacing tightens. */
@media (max-height:680px){
  .wlo-popup-body{padding-top:22px;padding-bottom:22px}
  .wlo-popup-person-role{margin-bottom:12px}
  .wlo-popup-title{margin-bottom:10px}
  .wlo-popup-text{margin-bottom:16px;font-size:15px;line-height:1.6}
  .wlo-popup-countdown{margin-bottom:16px}
  .wlo-popup-cd-cell{padding:8px 4px}
  .wlo-popup-cd-num{font-size:20px}
}

/* Below ~600px of viewport height (an iPhone SE, or most phones in
   landscape) even the tightened layout overflows, which left the countdown
   half-tucked behind the sticky CTA. The countdown is decoration and already
   aria-hidden, and the copy carries the deadline in words anyway - so drop
   it here and the whole promo fits with no internal scroll at all. */
@media (max-height:600px){
  .wlo-popup-countdown{display:none}
}

/* The fade/slide is decoration; the popup must still appear and dismiss
   for anyone who has asked the OS to stop animations. */
@media (prefers-reduced-motion:reduce){
  .wlo-popup,.wlo-popup-panel{transition:none}
  .wlo-popup-panel{transform:none}
}
