:root {
}

* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
    width: 100%;
    min-height: 100%;
}

html {
    scroll-padding-top: 96px;
}

body.wipe-locked {
    overflow: hidden !important;
    height: 100vh !important;
    touch-action: none;
}

.skip-link {
    position: fixed;
    left: 16px;
    top: 16px;
    z-index: 10001;
    transform: translateY(-140%);
    transition: transform 0.2s ease;
}

.skip-link:focus {
    transform: translateY(0);
}

/* =========================
   PRELOADER / VIDEO SCRUB OVERLAY
========================== */
#preloader-container {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: #0d1b2e;
    opacity: 1;
    transition: opacity 0.6s ease;
}

#preloader-container.is-done {
    opacity: 0;
    pointer-events: none;
}

#scrub-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #0d1b2e;
}

/* Wiper overlay: tracks the same scroll progress as the video (JS sets
   `top` in lockstep with video.currentTime), sliding from the top of the
   screen to the bottom as the visitor scrolls through the intro. */
#scrub-wiper {
    position: absolute;
    left: 50%;
    top: 0%;
    width: 100vw;
    transform: translateX(-50%);
    z-index: 5;
    pointer-events: none;
    will-change: top;
}

#loading-text {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 6;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    padding: 0 16px 6vh;
    pointer-events: none;
    text-align: center;
    color: #fff;
    transition: opacity 0.25s ease;
}

#loading-text h2,
#loading-text p,
#loading-text svg {
    color: #ffffff;
    -webkit-text-fill-color: #ffffff;
    text-shadow:
        0 2px 4px rgba(0, 0, 0, 0.75),
        0 6px 18px rgba(0, 0, 0, 0.45);
}

#loading-text h2 {
    font-weight: 800;
    margin: 10px 0 0;
    line-height: 1.1;
}

#loading-text p {
    font-weight: 600;
    margin-top: 8px;
}

/* Progress rail */
#scrub-rail {
    position: absolute;
    left: 50%;
    bottom: 3vh;
    transform: translateX(-50%);
    width: min(240px, 50vw);
    height: 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.25);
    overflow: hidden;
    z-index: 6;
}

#scrub-rail span {
    display: block;
    height: 100%;
    width: 0%;
    background: #2bb5c6;
    border-radius: 999px;
}

/* Always reachable: keyboard-focusable and never covered by the video */
#skip-intro {
    position: absolute;
    right: 16px;
    top: 16px;
    z-index: 7;
}

@media (max-width: 767px) {
    #loading-text h2 {
        font-size: 1.75rem;
    }

    #loading-text p {
        font-size: 0.95rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    #preloader-container {
        transition: none;
    }
}

/* =========================
   SITE STYLES
========================== */
.glass-nav {
    /* Opaque rather than blurred: a full-width backdrop-filter on a fixed
       element repaints on every scroll frame, and at 0.94 alpha the blur
       was not visible anyway. */
    background: rgba(255, 255, 255, 0.97);
    border-bottom: 1px solid rgba(34, 63, 111, 0.12);
}

.hero-home {
    min-height: min(820px, 92vh);
    background-image:
        linear-gradient(90deg, rgba(20, 39, 68, 0.88) 0%, rgba(20, 39, 68, 0.72) 42%, rgba(20, 39, 68, 0.30) 100%),
        url("../images/cleaning-hero.jpeg");
    background-size: cover;
    background-position: center;
}

/* Taller stage for a background <video> with object-cover: too short a
   section crops the footage down to a narrow center strip. */
.hero-video {
    min-height: min(680px, 82vh);
}

/* Light photo backdrop for page-opener sections that keep dark text (unlike
   .hero-home, which goes dark-overlay + white text). The wash is opaque
   enough that body copy stays legible while the glass texture still reads. */
.hero-misty {
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.94)),
        url("../images/misty-glass.jpg");
    background-size: cover;
    background-position: center;
}

/* Dark, dramatic backdrop (stage lights/smoke) - same dark-gradient-over-photo
   pattern as .hero-home, so text needs to switch to white/light, not the
   navy/slate used against .hero-misty's light wash. */
.hero-atmosphere {
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.55), rgba(20, 39, 68, 0.75)),
        url("../images/services-atmosphere-bg.jpg");
    background-size: cover;
    background-position: center;
}

/* Donate page hero: same dark-gradient-over-photo pattern as .hero-home. */
.hero-donate {
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.55), rgba(20, 39, 68, 0.65)),
        url("../images/way-to-health-service-member.jpg");
    background-size: cover;
    background-position: center;
}

.section-kicker {
    color: #c91f2e;
    font-weight: 800;
    text-transform: uppercase;
    font-size: 0.78rem;
}

.text-balance {
    text-wrap: balance;
}

.service-card {
    border-radius: 8px;
    border: 1px solid rgba(34, 63, 111, 0.12);
    background: #ffffff;
    box-shadow: 0 18px 48px -36px rgba(20, 39, 68, 0.55);
    transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.35s ease, border-color 0.35s ease;
}

/* Tag-qualified (rather than plain .service-card:hover) so this rule's
   specificity (element+class+pseudo) beats .reveal.is-visible (two classes,
   later in the cascade) - otherwise a hovered, already-revealed card would
   have its lift silently overridden back to transform:none. Every element
   tag actually carrying this class needs to be listed here. */
a.service-card:hover,
li.service-card:hover,
article.service-card:hover,
div.service-card:hover,
figure.service-card:hover {
    transform: translateY(-6px);
    border-color: rgba(201, 31, 46, 0.35);
    box-shadow: 0 28px 56px -28px rgba(20, 39, 68, 0.45);
}

.card-icon {
    transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.service-card:hover .card-icon {
    transform: scale(1.1) rotate(-4deg);
}

/* Franchise page hero and investment-range CTA: same dark-gradient-over-photo
   pattern as the other page heroes, replacing the flat bg-brand-navy fill. */
.hero-franchise {
    /* Shaped to the film, not the other way round. A 16:9 clip in a letterbox
       hero has to be cropped to cover it; matching the ratio means the whole
       frame is on screen, at every width.

       Deliberately no max-height. A cap looks harmless but it is just cropping
       under another name - at 88vh it took ~12% off the frame on an ordinary
       16:9 monitor. The cost of leaving it off is that on an ultrawide the hero
       is taller than the viewport and you scroll through it, which is the
       better trade when the whole point is to show the film uncropped. */
    aspect-ratio: 16 / 9;

    /* The site header is position:fixed and opaque, so anything starting at
       y=0 has its top 80px covered. Every other hero absorbs that with a big
       pt-32 and lets its background photo run underneath - fine for a photo,
       wrong for a film, where those 80px are frame you never see. Push the
       whole band clear of the header instead: 5rem for the header's h-20, plus
       the 1px bottom border that .glass-nav adds under it. */
    margin-top: calc(5rem + 1px);

    /* Fallback poster under the iframe: shown when the embed is gated off
       (phones, tablets, reduced-motion) or refused. Kept much lighter than the
       other page heroes because no text sits on it any more - it is a photo
       band, not a backdrop for copy. */
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.32), rgba(20, 39, 68, 0.46)),
        url("../images/franchise-hero-bg.jpg");
    background-size: cover;
    background-position: center;
}

/* Deliberately no small-screen override. The film plays at every width now,
   and anything that takes the 16:9 shape away puts the cropping straight back
   - the player would be cover-fitted into a band that is no longer its own
   ratio. A 16:9 band is short on a phone, but it is the whole frame. */


/* With the copy moved out to its own section, this is no longer doing contrast
   work - it is a light vignette that ties the film to the brand palette and
   keeps the fixed white header from sitting on a blown-out frame. Heavier at
   the foot so the band hands off cleanly to the navy section below. */
.hero-franchise-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to bottom,
        rgba(20, 39, 68, 0.38) 0%,
        rgba(20, 39, 68, 0.14) 22%,
        rgba(20, 39, 68, 0.16) 70%,
        rgba(20, 39, 68, 0.52) 100%
    );
}

/* A YouTube iframe cannot object-fit the way a <video> can, so the 16:9 player
   is oversized on whichever axis is short and re-centred - the CSS equivalent
   of object-fit: cover. Container units measure the hero itself; the vh/vw pair
   above them is the fallback for engines without container queries, which
   measure the viewport instead and so only approximate the crop. */
.hero-youtube {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    container-type: size;
}

/* main.js toggles [hidden] as the viewport crosses 1024px. Spelled out rather
   than left to the UA sheet, so a later display rule on .hero-youtube cannot
   silently outrank it and leave a cropped player on screen. */
.hero-youtube[hidden] {
    display: none;
}

.hero-youtube iframe {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border: 0;
    width: 177.7778vh;
    min-width: 100%;
    height: 56.25vw;
    min-height: 100%;
}

@supports (width: 1cqw) {
    .hero-youtube iframe {
        width: max(100cqw, 177.7778cqh);
        height: max(100cqh, 56.25cqw);
        min-width: 0;
        min-height: 0;
    }
}

/* Who actually gets the film is decided in main.js, not here: the iframe ships
   with no src, so hiding it in CSS would still leave the embed downloading on
   the devices we are trying to spare. */

.section-franchise-cta {
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.7), rgba(20, 39, 68, 0.82)),
        url("../images/franchise-cta-bg.jpg");
    background-size: cover;
    background-position: center;
}

/* "Benefits" section: same dark-gradient-over-photo pattern as the page
   heroes, replacing the flat bg-brand-navy fill. */
/* About page commitment band. The copy is centred and spans the full width,
   so this needs an even wash rather than a directional one - the photo is
   only there to give the block some depth behind the promise. */
/* Light counterpart to the dark photo bands: a bright office behind a white
   wash, so the navy headings, slate copy and white contact cards on top keep
   their contrast. Anything darker than this and the cards stop reading as
   cards. */
.section-soft-photo {
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.28), rgba(20, 39, 68, 0.38)),
        url("../images/benefits-office.jpg");
    background-size: cover;
    background-position: center;
}

.section-commitment {
    /* Radial rather than flat: the copy is a centred max-w-4xl column, so the
       wash stays heavy behind it and opens up towards the edges where there
       is no text and the lobby can actually be seen. */
    background-image:
        radial-gradient(
            ellipse 72% 78% at 50% 50%,
            rgba(20, 39, 68, 0.90) 0%,
            rgba(20, 39, 68, 0.86) 42%,
            rgba(20, 39, 68, 0.60) 78%,
            rgba(20, 39, 68, 0.48) 100%
        ),
        url("../images/clean-team.jpg");
    background-size: cover;
    background-position: center;
}

/* Kept dark across the top third, where the kicker/heading/intro copy sits,
   then lightened through the card band so the photo actually reads. A flat
   light wash pushed the intro text to ~2:1 contrast over the bright windows. */
.section-benefits {
    background-image:
        linear-gradient(
            rgba(20, 39, 68, 0.84) 0%,
            rgba(20, 39, 68, 0.80) 22%,
            rgba(20, 39, 68, 0.52) 42%,
            rgba(20, 39, 68, 0.58) 100%
        ),
        url("../images/benefits-office.jpg");
    background-size: cover;
    background-position: center;
}

/* "Get Started" section: same dark-gradient-over-photo pattern as the page
   heroes, replacing the flat bg-brand-navy fill. */
.section-get-started {
    background-image:
        linear-gradient(rgba(20, 39, 68, 0.55), rgba(20, 39, 68, 0.68)),
        url("../images/get-started-bg.jpg");
    background-size: cover;
    background-position: center;
}

/* "How It Works" step cards: each gets a distinct real photo with a dark
   overlay (same dark-gradient-over-photo pattern as the page heroes), so
   text switches to white instead of .service-card's navy/slate. Declared
   after .service-card so its background-image wins over the plain white
   background at equal (single-class) specificity. */
.step-card {
    position: relative;
    background-size: cover;
    background-position: center;
}

.step-card-assess {
    background-image: linear-gradient(rgba(20, 39, 68, 0.72), rgba(20, 39, 68, 0.85)), url("../images/step-assess.jpg");
}

.step-card-propose {
    background-image: linear-gradient(rgba(20, 39, 68, 0.72), rgba(20, 39, 68, 0.85)), url("../images/step-propose.jpg");
}

.step-card-approve {
    background-image: linear-gradient(rgba(20, 39, 68, 0.72), rgba(20, 39, 68, 0.85)), url("../images/step-approve.jpg");
}

.step-card-inspect {
    background-image: linear-gradient(rgba(20, 39, 68, 0.6), rgba(20, 39, 68, 0.78)), url("../images/step-inspect.jpg");
}

.check-list li {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    transition: transform 0.25s ease;
}

.check-list li:hover {
    transform: translateX(4px);
}

.check-list svg {
    flex: 0 0 auto;
    margin-top: 0.2rem;
    transition: transform 0.25s ease;
}

.check-list li:hover svg {
    transform: scale(1.15);
}

/* Underline-on-hover for nav and footer links: a scaleX transform rather
   than animating width/border, so it runs on the compositor. */
.nav-link {
    position: relative;
}

.nav-link::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -4px;
    height: 2px;
    background: #c91f2e;
    border-radius: 999px;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s ease;
}

.nav-link:hover::after,
.nav-link:focus-visible::after,
.nav-link[aria-current="page"]::after {
    transform: scaleX(1);
}

.nav-link[aria-current="page"] {
    color: #c91f2e;
}

.footer-link {
    position: relative;
    display: inline-block;
}

.footer-link::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    /* .footer-link has py-3 (12px) for a 44px tap target; without this offset
       the underline would sit under the padding box, floating well below the
       actual text instead of hugging it. */
    bottom: 10px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s ease;
}

.footer-link:hover::after,
.footer-link:focus-visible::after {
    transform: scaleX(1);
}

.footer-link:hover,
.nav-link:hover {
    color: #c91f2e;
}

/* =========================
   SCROLL-REVEAL
   Elements marked .reveal fade and rise into place the first time they
   cross into view. transform/opacity only, so it stays on the compositor
   and never triggers layout during scroll.
========================== */
.reveal {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.7s cubic-bezier(0.2, 0.65, 0.3, 1), transform 0.7s cubic-bezier(0.2, 0.65, 0.3, 1);
    transition-delay: var(--reveal-delay, 0s);
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .reveal {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

@media (max-width: 767px) {
    .hero-home {
        min-height: 88vh;
        background-image:
            linear-gradient(180deg, rgba(20, 39, 68, 0.92) 0%, rgba(20, 39, 68, 0.82) 55%, rgba(20, 39, 68, 0.68) 100%),
            url("../images/cleaning-hero.jpeg");
        background-position: center;
    }
}

/* =========================
   PHOTO PLACEHOLDER
   Stand-in for real photos not yet supplied (used on donate.html, pending
   the Way to Health charity photo files). Drop a same-purpose image into
   assets/ and remove the .photo-placeholder wrapper + its <span> label.
========================== */
.photo-placeholder {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: repeating-linear-gradient(135deg, #eef9fb, #eef9fb 10px, #e3f2f5 10px, #e3f2f5 20px);
    border: 2px dashed rgba(34, 63, 111, 0.35);
    color: #223f6f;
    font-weight: 700;
    font-size: 0.8rem;
    padding: 1.5rem;
}
