/* ════════════════════════════════════════════════════════════════════════════
   QwikGlam — APP SHELL  (WEB-1)
   File: assets/css/qg-app-shell.css
   Loads LAST. One authoritative answer to "what is the chrome on this device?"
   ════════════════════════════════════════════════════════════════════════════

   WHAT THIS FIXES, MEASURED ON THE LIVE SITE AT 375px
   ---------------------------------------------------
   Seven public pages, three different mobile behaviours, because every template
   decided the shell for itself and none of them agreed:

     page            header      footer        bottom nav   body pad-bottom
     /               none  ok    2121px  BAD   block  ok    —
     /services/      none  ok    1852px  BAD   block  ok    0px      BAD
     /our-artists/   none  ok    none    ok    none   BAD   0px
     /about/         flex  BAD   1946px  BAD   block  ok    68px     ok
     /contact/       flex  BAD   1946px  BAD   block  ok    68px     ok
     /download/      none  ok    none    ok    none   BAD   0px
     /terms/         flex  BAD   1946px  BAD   block  ok    68px     ok

   Read the columns, not the rows — every one of them disagrees with itself
   somewhere. Concretely, on a phone today:

     • /about/, /contact/ and /terms/ render the DESKTOP HEADER, which is the
       one piece of chrome a phone is not supposed to have;
     • five of seven render a ~1,900–2,100px desktop footer, which on a phone is
       three screens of link columns nobody scrolls to;
     • /our-artists/ and /download/ render NO bottom nav, so a phone that lands
       there has no navigation at all — the page is a dead end;
     • /services/ reserves NO space for the nav it does show, so the last 68px
       of the page sits underneath it.

   None of that is a per-page bug. It is the absence of a shell. Fixing it in 34
   templates would mean 34 chances to disagree again, so it is fixed once, here.

   THE RULE
   --------
     ≤ 767px   PHONE   → no header, no footer, bottom nav always, safe areas.
                         The brief: "in mobile header and footer not required,
                         design should be like native mobile app experience."
     768–1023  TABLET  → keeps header and footer. A 768px iPad has the room for
                         them, and stripping a tablet down to phone chrome wastes
                         half the screen. It keeps the bottom nav too, which the
                         theme already shows below 1100px.
     ≥ 1024px  DESKTOP → untouched. Nothing here applies.

   If you want iPad on the phone shell instead, raise --qg-shell-phone-max to
   1023px in the media queries below — it is the only number that decides it.

   SPECIFICITY: `html body` prefixes and !important, matching the idiom
   footer-qwikglam.php already uses. This file has to beat per-page rules that
   are themselves !important (page-our-artists.php line 193, for one), and it
   loads last precisely so it can. If LiteSpeed's CSS-combine reorders it, add
   it to Page Optimization → CSS → "CSS Combine Exclude" — do NOT re-add a
   render-blocking <link> bypass, which is the trade functions.php already
   considered and rejected.
   ════════════════════════════════════════════════════════════════════════════ */

:root {
  /* Height of the fixed bottom nav. Everything that reserves space reads this,
     so the nav's height is stated once rather than hard-coded as 68px in a
     dozen templates that then drift apart. */
  --qg-appbar-h: 68px;
  --qg-safe-b: env(safe-area-inset-bottom, 0px);
  --qg-safe-t: env(safe-area-inset-top, 0px);
}

/* ────────────────────────────────────────────────────────────────────────────
   1 · UNIVERSAL — every device
   ──────────────────────────────────────────────────────────────────────────── */

/* A horizontal scrollbar on a phone is never intentional. This makes the page
   itself unable to scroll sideways; section 4 gives the genuinely wide things
   (carousels, tickers, price rows) their own scroller so they still work. */
/* clip, not hidden — hidden on html+body turns <body> into a nested scroll
   container and the page stops scrolling properly (see qg-global.css). */
html, body { max-width: 100%; overflow-x: clip; }
@supports not (overflow: clip) {
  html { overflow-x: visible; }
  body { overflow-x: hidden; }
}

/* iOS inflates font sizes in landscape unless told not to, which silently
   breaks any layout tuned in portrait. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* Media must never be the thing that widens a page. */
img, video, canvas, svg, iframe { max-width: 100%; }
img, video { height: auto; }

/* Tables and <pre> are the two elements that ignore their container by default. */
table { max-width: 100%; }
pre, code { white-space: pre-wrap; word-break: break-word; }

/* Long unbroken strings — a pasted URL, a booking ref, an email — otherwise
   push the whole layout wide on a 375px screen. */
h1, h2, h3, h4, p, li, a, span, td { overflow-wrap: anywhere; }

/* ────────────────────────────────────────────────────────────────────────────
   2 · PHONE APP SHELL  (≤767px)
   ──────────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px) {

  /* ── 2a. No header. ──────────────────────────────────────────────────────
     Three pages render it today and four do not. A native app does not have a
     desktop site header, and the brief says so outright. */
  html body header.qg-header,
  html body .qg-header,
  html body .qg-header-wrap,
  html body #qgHeader,
  html body .qg-announcement,
  html body .qg-ann,
  html body .qg-topbar { display: none !important; }

  /* ── 2b. No footer — but the <footer> ELEMENT has to stay. ───────────────
     ~2,000px of link columns on an 812px-tall screen. Everything in it is
     reachable from the bottom nav, Profile, or the sitemap; on a phone it is
     three screens of scrolling that exists to be skipped.

     THE TRAP, found by testing this rule on the live page before shipping it:
     `footer.qg-footer { display: none }` also removes the BOTTOM NAV, because
     the nav is a CHILD of the footer element (footer-qwikglam.php line 1347),
     as is the concierge button. The nav reported display:block with height 0 —
     visible by its own computed style, dead because an ancestor was not. That
     turns every phone page into the dead end this file exists to fix.

     So the element stays and its CONTENT goes. The allow-list is the functional
     children — nav, concierge, its modal, and the inline style/script the
     footer ships with. A decorative child added later is hidden by default,
     which is the right direction for a mobile shell; a functional one has to be
     named here, which is a deliberate act rather than an accident. */
     And it has to be forced VISIBLE. /our-artists/ and /download/ hide the whole
     footer, which is why those two pages had no navigation at all on a phone.
     Simply not hiding it is not enough — the shell has to put it back. */
  html body footer.qg-footer,
  html body .qg-footer,
  html body #qgFooter {
    display: block !important;
    background: none !important;
    border: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    min-height: 0 !important;
    position: static !important;
  }
  html body footer.qg-footer > *:not(.qg-bottom-nav):not(#qgBottomNav):not(.qg-concierge):not(#qgConciergeBtn):not(.qg2-modal):not(style):not(script),
  html body .qg-footer__membership-banner { display: none !important; }

  /* ── 2c. The bottom nav, wherever a page has not deliberately replaced it. ──
     Once 2a and 2b run, this is the phone's navigation, so it is forced on for
     every page that has no opinion — which is most of them.

     IT IS NOT FORCED OVER A PAGE THAT HAS OPTED OUT ON PURPOSE, and the
     specificity here is chosen so that it cannot be. /download/ carries
     `body.qg-dlpage` and /our-artists/ `body.oaw-fullscreen`, each with

         html body.qg-dlpage #qgBottomNav { display: none !important }

     at (1,1,2) against this rule's (1,0,2). Theirs wins, by one class, by
     design. An earlier draft of this file treated those two as broken — they
     look identical to a bug from a stylesheet audit: no header, no footer, no
     nav. They are not. Both are immersive, modal-style screens with their own
     bottom CTA ("Join as Artist", "Download") and a back chevron top-left, and
     stacking a tab bar underneath a sticky CTA would give them two bottom bars.
     A native app does exactly this for a full-screen takeover.

     So: force the nav where nothing objects, yield where a page has said no.
     If you DO want the tab bar on those two, the fix is to delete their opt-out
     rule in the page template — not to escalate specificity here, which would
     make every future opt-out impossible.

     The nav element is already in the markup on every page — it was only ever
     hidden — so none of this needs a template change. */
  html body .qg-bottom-nav,
  html body #qgBottomNav,
  html body.oaw-fullscreen .qg-bottom-nav,
  html body.oaw-fullscreen #qgBottomNav {
    display: block !important;
    position: fixed !important;
    bottom: 0; left: 0; right: 0;
    z-index: 8900;
    padding-bottom: max(4px, var(--qg-safe-b)) !important;
  }

  /* ── 2d. Reserve the space it occupies. ──────────────────────────────────
     /services/ shows the nav over its own last 68px of content. Stated once,
     from the same variable the nav is sized by, so they cannot drift.

     A page that genuinely owns the full viewport — the reels feed — opts out
     with `qg-shell-nopad` on <body> rather than by deleting this rule.

     `body[class]`, not `body`. Measured, not styled to taste: on /services/ a
     plain `html body { padding-bottom: … !important }` computes to 0px, because
     something with a class-level selector and !important beats it — it lives in
     a stylesheet this page cannot read across origins, so it cannot be named
     here, only out-specified. `html body[class]` tests at the required 68px.
     Every WordPress <body> carries classes, so the attribute always matches. */
  html body[class] { padding-bottom: var(--qg-appbar-h) !important; }
  @supports (padding: env(safe-area-inset-bottom)) {
    html body[class] { padding-bottom: calc(var(--qg-appbar-h) + env(safe-area-inset-bottom)) !important; }
  }
  html body.qg-shell-nopad,
  html body.qg-reels,
  html body.single-reel { padding-bottom: 0 !important; }

  /* Anything the page itself pins above the nav — a sticky "Book now" bar, a
     cart total — has to clear it too, or it lands on top of the nav. */
  html body .qg-sticky-cta,
  html body .qg-sticky-bar,
  html body .qg-floating-cta {
    bottom: calc(var(--qg-appbar-h) + var(--qg-safe-b)) !important;
  }

  /* The concierge bubble sits in the same corner as the nav's right-hand item. */
  html body .qg-concierge,
  html body #qgConciergeBtn {
    bottom: calc(var(--qg-appbar-h) + var(--qg-safe-b) + 12px) !important;
  }

  /* ── 2e. Native-app feel. ────────────────────────────────────────────────
     The small things that separate "a website on a phone" from "an app". */

  /* The grey flash on every tap is the single clearest tell of a web page. */
  html body a, html body button, html body [role="button"], html body .qg-bnav-item {
    -webkit-tap-highlight-color: transparent;
  }

  /* Rubber-banding the whole document past its ends reveals the browser
     chrome underneath and breaks the illusion. */
  /* html only. On body it stopped a scroll that reached body's edge from
     passing on to the page — the "page won't slide" feel. */
  html { overscroll-behavior-y: contain; }

  /* Horizontal rails: see "7 · RAILS NEVER SCROLL VERTICALLY" at the end of
     this file (kept outside this 767px block so a 768px device is covered). */

  /* Momentum scrolling inside horizontal rails. */
  html body [class*="-rail"],
  html body [class*="-scroller"],
  html body [class*="-carousel"] { -webkit-overflow-scrolling: touch; }

  /* Text selection on chrome elements reads as a web page, not an app. Left ON
     for actual content — a customer copying a booking reference needs it. */
  html body .qg-bottom-nav, html body .qg-bnav-item {
    -webkit-user-select: none; user-select: none;
  }

  /* Top safe area. With the header gone, the first element is against the
     status bar / notch unless something accounts for it. */
  html body > main,
  html body .qg-main,
  html body .site-main { padding-top: var(--qg-safe-t); }

  /* ── 2f. Touch targets. ──────────────────────────────────────────────────
     44px is the floor below which taps start missing. Applied to the nav and
     to small controls rather than to every link, because forcing 44px onto
     inline text links inside a paragraph would wreck the line spacing. */
  html body .qg-bnav-item { min-height: 48px; }
  html body button,
  html body input[type="submit"],
  html body .qg-btn,
  html body .btn { min-height: 44px; }

  /* iOS zooms the page in when a focused input is under 16px. That zoom is not
     reversible by the user, so a 14px field traps them at 1.3× for the rest of
     the session. */
  html body input, html body select, html body textarea { font-size: 16px !important; }
}

/* ────────────────────────────────────────────────────────────────────────────
   3 · TABLET / iPad  (768–1023px)
   Keeps the header and footer; a 768px screen has the room, and the theme
   already swaps the desktop nav for the bottom nav below 1100px.
   ──────────────────────────────────────────────────────────────────────────── */
@media (min-width: 768px) and (max-width: 1023px) {

  html body .qg-bottom-nav,
  html body #qgBottomNav {
    padding-bottom: max(4px, var(--qg-safe-b)) !important;
  }
  html body { padding-bottom: calc(var(--qg-appbar-h) + var(--qg-safe-b)); }

  /* Fixed pixel widths tuned for a 1280px desktop overflow a 768px iPad. Only
     containers are relaxed — not every element, which would flatten layouts
     that are deliberately sized. */
  html body .qg-container,
  html body .qg-wrap,
  html body .qg-section__inner,
  html body .qg-header__inner { max-width: 100% !important; }

  /* Three- and four-column desktop grids are unreadable at 768px. */
  html body .qg-grid-3,
  html body .qg-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}

/* ────────────────────────────────────────────────────────────────────────────
   4 · ELEMENTS WIDER THAN THE SCREEN — AND WHY MOST OF THEM ARE FINE

   The audit flagged three elements exceeding their viewport:
     .qg-urgency-badges  533px at 375   (/services/)
     .qg-bp-row         1882px at 768   (/services/)
     .qg-ann-track      3663px at 375   (/about/)

   An earlier draft of this file turned all three into snap-scroll rails. That
   was wrong, and checking rather than assuming is what caught it: every one of
   them is a TRACK inside a parent that already handles the overflow —

     .qg-bp-track       overflow-x: hidden   (a marquee; the width IS the animation)
     .qg-urgency-strip  overflow-x: auto     (already a swipeable scroller)

   Forcing `overflow-x: auto` onto the track itself would have stopped the
   marquee animating and double-nested a scroller inside a scroller. The page
   reports 0px of horizontal overflow today, which is the number that actually
   matters. There was no bug here to fix.

   What remains is a safety net for anything future that is wide WITHOUT a
   clipping parent: section 1 already stops it widening the document. This block
   only guarantees the known wrappers keep clipping.
   ──────────────────────────────────────────────────────────────────────────── */
@media (max-width: 1023px) {
  html body .qg-ann,
  html body .qg-announcement,
  html body .qg-ann-wrap,
  html body .qg-bp-track,
  html body .qg-urgency-strip { max-width: 100%; }
}

/* ────────────────────────────────────────────────────────────────────────────
   4b · CONDENSED SECTIONS (WEB-4) — phones only.

   The home page measures 15,229px at 375×812: 18.8 screens. Section padding
   across all 21 sections totals 970px — six per cent — so there is no
   whitespace to reclaim. The length is content, and content is not deleted
   here; it is clamped to about half a screen behind a "Show more" that puts it
   back in one tap. Everything stays in the DOM: indexed, findable with the
   browser's own find-in-page, and reachable without JavaScript (the clamp is
   applied BY the script, so if it never runs the section is simply full-length).

   `.qg-app` is the exception and is hidden outright — see the duplicate-section
   note in assets/js/qg-home-condense.js.
   ──────────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px) {

  /* The older of two app-download sections. 668px, and its two primary CTAs
     are "App Store Soon" / "Google Play Soon" — placeholders that cannot be
     tapped anywhere. .qgapp below it does the same job with a working APK
     download and a web-app link. */
  html body section.qg-app { display: none !important; }

  html body .qg-condensed {
    position: relative;
    max-height: var(--qg-clamp, 460px);
    overflow: hidden;
    /* The fade tells the reader the section continues. Without it a hard cut
       looks like the page broke rather than like there is more to see. */
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 90px), transparent 100%);
            mask-image: linear-gradient(to bottom, #000 calc(100% - 90px), transparent 100%);
    /* Room for the button, which is positioned over the faded tail. */
    padding-bottom: 56px !important;
  }

  html body .qg-condensed--open {
    max-height: none;
    -webkit-mask-image: none;
            mask-image: none;
  }

  html body .qg-condense-btn {
    position: absolute;
    left: 50%;
    bottom: 12px;
    transform: translateX(-50%);
    z-index: 2;
    max-width: calc(100% - 32px);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-height: 40px;
    padding: 10px 20px;
    border: 1px solid rgba(59, 31, 58, .18);
    border-radius: 999px;
    background: #fff;
    color: #3B1F3A;
    font: 600 12.5px/1 inherit;
    letter-spacing: .04em;
    box-shadow: 0 2px 10px rgba(59, 31, 58, .08);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  /* Once open, the button sits in normal flow at the end rather than floating
     over the last line of content. */
  html body .qg-condensed--open .qg-condense-btn {
    position: static;
    transform: none;
    display: block;
    margin: 16px auto 0;
  }

  html body .qg-condense-btn:focus-visible {
    outline: 2px solid #C2547B;
    outline-offset: 2px;
  }
}

/* ────────────────────────────────────────────────────────────────────────────
   5 · MOTION
   The nav and any shell transition respect the OS setting. A native app does.
   ──────────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html body .qg-bottom-nav,
  html body .qg-bnav-item,
  html body .qg-bnav-reels-inner {
    transition: none !important;
    animation: none !important;
  }
}

/* ────────────────────────────────────────────────────────────────────────────
   6 · PRINT — legal pages get printed; app chrome should not be.
   ──────────────────────────────────────────────────────────────────────────── */
@media print {
  .qg-bottom-nav, .qg-header, .qg-footer, .qg-concierge { display: none !important; }
  html body { padding-bottom: 0 !important; }
}

/* ────────────────────────────────────────────────────────────────────────────
   7 · RAILS NEVER SCROLL VERTICALLY — phones and small tablets
   overflow-x:auto forces overflow-y to compute to auto, and the scroll-reveal
   on rail cards starts each card 22px low (--m-rise in qg-home-motion.css), so
   every rail below became a vertical scroll container 16-22px deep —
   .qg-leaders__rail is 827px tall, taller than a phone screen. A vertical swipe
   that started on one scrolled the rail's few pixels instead of the page:
   "can't scroll properly on mobile, fine on desktop" (a mouse wheel rarely
   latches onto them). Measured on the live homepage at 375px.
   hidden keeps sideways scrolling and changes nothing visually (auto already
   clipped vertically). Selectors out-rank qg-mobile.css's
   `html body.home #qg-home …` !important rules. 900px covers qg-mobile.css's
   768px breakpoint and the tablet range where the rails still apply.
   ──────────────────────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  html body.home #qg-home .qg-services .qg-services-grid,
  html body.home #qg-home .qg-services .qg-services-filter,
  html body.home #qg-home .qg-packages #qgPackagesTrack,
  html body.home #qg-home .qg-how .qg-how__steps,
  html body.home #qg-home .qg-artists .qg-artists-grid,
  html body.home #qg-home .qg-membership .qg-membership__cards,
  html body.home #qg-home .qg-seasonal .qg-seasonal__grid,
  html body.home #qg-home .qg-before-after .qg-ba-grid,
  html body.home #qg-home .qg-leaders .qg-leaders__rail,
  html body.home .qg-leaders .qg-leaders__rail,
  html body.page-template-page-services .qg-collections .qg-collections-scroll,
  html body.page-template-page-services .qg-payment-trust {
    overflow-y: hidden !important;
  }
}

/* ────────────────────────────────────────────────────────────────────────────
   8 · TOUCH DEVICES — no backdrop blur, no blend-mode particles
   Android Chrome re-rasterises every on-screen backdrop-filter on every scroll
   frame. Measured on the live pages at phone width: 208 backdrop blurs on the
   services page (a glass heart + duration pill on each of 99 cards, the filter
   bar, the bottom nav, the closed exit overlay) and 28 on the home page (bottom
   nav, hero pills and buttons over the video). iOS absorbs that; mid-range
   Android drops frames and the page "won't scroll properly". Desktop keeps the
   glass — this only applies where there is no hover pointer.

   Why `:is(#qg-t#qg-o#qg-u, *)`: :is() takes the specificity of its most
   specific argument, so this matches every element but weighs three IDs —
   enough to beat the 40+ `!important` blur rules already in the theme, the
   strongest of which (`body:has(#qgHero) #qgHero …`) weighs two.
   ──────────────────────────────────────────────────────────────────────────── */
@media (hover: none) and (pointer: coarse) {
  html body :is(#qg-t#qg-o#qg-u, *),
  html body :is(#qg-t#qg-o#qg-u, *)::before,
  html body :is(#qg-t#qg-o#qg-u, *)::after {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }

  /* Glass that relied on the blur for contrast gets a denser ground instead. */
  html body #qgServicesGrid .qgc .qgc-chip,
  html body #qgServicesGrid .qgc .qgc-dur { background: rgba(24,8,17,.62) !important; }
  html body #qgServicesGrid .qgc .qg-card-wish-btn.qgc-wish { background: rgba(255,255,255,.96) !important; }
  html body #qgFilterHub.qgf { background: #FFFFFF !important; }
  html body .qg-bottom-nav { background: rgba(14,5,28,.98) !important; }

  /* Two drifting particle layers with mix-blend-mode: screen over the hero
     video, animating forever: a full repaint of the hero on every frame. */
  /* Same :is() weighting — qg-services-mobile-master.css forces these to
     display:block !important from a one-ID selector. */
  html body :is(#qg-t#qg-o#qg-u, .qg-hero__particles) { display: none !important; }
}
