/* ================================================================
   GALLERY PAGE — /gallery/
   Hero pins in place (position:fixed, behind everything) and STAYS —
   unlike Home/Brands/About, nothing ever curtains fully over it.
   .gallery-page__content (the grid) has a transparent background and
   scrolls over the hero for the whole page instead. As the user
   scrolls, gallery-page.js fades the hero's text out fast and dims
   the scrim further; .gallery-page__edge-fade (two fixed gradient
   bands, below) makes the grid's tiles fade in/out at the top/bottom
   viewport edges instead of hard-clipping.
   ================================================================ */

/* --gallery-zone: the spacer's own height (below) — the point where
   the grid starts peeking in from underneath. Defined here on the
   shared ancestor (not on .gallery-hero itself) so both it and
   .gallery-hero__spacer — siblings, not descendants of each other —
   can see the same value; custom properties only inherit down the
   tree, not sideways. .gallery-hero__content's centering math
   (further down) reads it too, so all three always agree on exactly
   where the grid starts peeking in without three numbers to keep in
   sync by hand. */
.gallery-page {
    --gallery-zone: 65dvh;
}

.gallery-hero {
    --header-h: calc(var(--utility-bar-h) + var(--mobile-brand-bar-h));
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    /* height (not inset:0's implicit bottom:0) deliberately, pinned to
       the LARGE viewport — mobile browser chrome (address bar) shows
       and hides as you scroll, and inset:0 would let this fixed box's
       actual height track that in real time. Since .gallery-hero__bg
       is background-size:cover, a box that resizes mid-scroll rescales
       the image with it — that's the "zooms in/out weirdly" users
       reported, nothing to do with the deliberate JS parallax below.
       100lvh never changes as the chrome shows/hides, so the box (and
       the image covering it) now stays visually fixed-size while
       scrolling; 100vh is a pre-dvh-support fallback only. */
    height: 100vh;
    height: 100lvh;
    z-index: 0;
    text-align: center;
    background: var(--color-navy-dark);
    overflow: hidden;
}

@media (min-width: 768px) {
    .gallery-hero {
        --header-h: calc(var(--utility-bar-h) + var(--nav-h) + var(--sp-4) * 2);
    }
}

/* Shorter than a full 100vh — the grid's first row should already be
   peeking in from the bottom edge at rest, not hidden a full screen
   below, since it's arriving as a visible fade-in rather than behind
   a curtain. */
.gallery-hero__spacer {
    height: var(--gallery-zone);
}

.gallery-hero__content,
.gallery-hero__scroll-cue {
    transition: opacity 200ms linear;
}

/* Scaled up for overscan so the scroll-linked parallax shift
   (gallery-page.js) never exposes an edge, same technique as the
   Brands/About page heroes. gallery-hero-centered.webp is a pre-
   cropped version of the original wide ribbon-cutting photo: trimmed
   from the left (2400x1600 → 1800x1600) so Aamir and Houd's combined
   midpoint lands exactly on the new image's own horizontal center, and
   trimmed from the bottom (→ 1800x1400, floor only — faces and the
   ribbon-cutting moment are well above the cut) so their heads sit
   further down as a fraction of the frame. On phones this crop is
   height-driven (background-size:cover shows the image's full height,
   no vertical slack background-position could use), so without that
   bottom trim their heads landed right under the fixed header/utility
   bar — "sponsor's head getting cut off" — no CSS position value fixes
   that on its own when there's zero vertical slack to shift within.
   Keep this trim light — an earlier, heavier crop (1800x1150) cleared
   the header too but, combined with the scale() below, read as "too
   zoomed in" on phones; 1400 is the lighter version that still clears
   the header with margin. */
.gallery-hero__bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center 35%;
    /* 1.15, not 1.2 — matches the About page hero's overscan value and
       is still comfortably more than the JS parallax's 60px MAX_SHIFT
       needs, without adding extra zoom beyond what the overscan
       requires (see gallery-page.js, which sets this same value
       inline on scroll and so is the one that actually wins at rest). */
    transform: scale(1.15);
    will-change: transform;
}

.gallery-hero__scrim {
    position: absolute;
    inset: 0;
    background: rgba(7, 25, 41, 0.8);
    /* Shared by both the load entrance (bright → dim, gallery-page.js)
       and the scroll-driven further dim — one duration/easing that
       reads as a deliberate settle either way. */
    transition: opacity 900ms var(--ease-out);
}

/* Entrance — starts hidden/lowered, gallery-page.js adds .is-in
   (double-rAF, same pattern as the Home/About hero entrances) once
   the bright-first scrim state has had a frame to actually paint.
   Scroll-driven opacity (gallery-page.js) takes over the opacity side
   from here on; the transform stays exactly this settled state since
   scroll never touches it. */
.gallery-hero__content,
.gallery-hero__scroll-cue {
    opacity: 0;
    transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
}

/* Centered within the header-to-grid zone specifically (--header-h to
   --gallery-zone), not the full screen — a plain 50%-of-viewport
   centering would sit noticeably lower than this text block actually
   reads right at, since the grid already starts peeking in at
   --gallery-zone rather than the bottom of the viewport. */
.gallery-hero__content {
    position: absolute;
    z-index: 1;
    left: 50%;
    top: calc(var(--header-h) + (var(--gallery-zone) - var(--header-h)) / 2);
    transform: translate(-50%, -50%) translateY(24px);
    padding: var(--sp-6);
}

.gallery-hero__scroll-cue {
    transform: translateX(-50%) translateY(16px);
}

.gallery-hero.is-in .gallery-hero__content {
    opacity: 1;
    transform: translate(-50%, -50%) translateY(0);
}

.gallery-hero.is-in .gallery-hero__scroll-cue {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .gallery-hero__content,
    .gallery-hero__scroll-cue {
        transition: opacity 200ms linear;
    }

    .gallery-hero__content {
        transform: translate(-50%, -50%);
    }

    .gallery-hero__scroll-cue {
        transform: translateX(-50%);
    }
}

/* No eyebrow label any more — one big statement plus one short line
   reads cleaner/more minimal than a three-tier label+title+sentence
   stack did, and lets the title go genuinely large without the block
   as a whole feeling crowded. Both this and .gallery-hero__sub are
   forced to one line (white-space:nowrap) with their vw multiplier
   measured against the actual rendered text ("5 Years of 3T" /
   "Five years. One journey." in Montserrat, via canvas measureText)
   at the narrowest real phone width this site supports (320px) and
   checked back up through desktop, so neither wraps or overflows the
   .container padding at any width — not just "big text that happens
   to usually fit". Title pushed bigger / sub pulled smaller on purpose
   — more contrast between the headline and the one-line caption under
   it — while the sub's desktop/tablet size (its clamp max, from
   ~600px up) is untouched; only the small-viewport end shrank. */
.gallery-hero__title {
    font-size: clamp(2rem, 12.5vw, 6.5rem);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-display);
    line-height: var(--lh-tight);
    color: var(--color-white);
    white-space: nowrap;
}

.gallery-hero__sub {
    margin-top: var(--sp-2);
    font-size: clamp(0.8125rem, 4vw, 1.5rem);
    font-weight: var(--fw-medium);
    letter-spacing: 0.02em;
    color: rgba(255, 255, 255, 0.78);
    white-space: nowrap;
}

.gallery-hero__scroll-cue {
    position: absolute;
    z-index: 1;
    bottom: var(--sp-8);
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    color: rgba(255, 255, 255, 0.7);
    font-size: var(--text-xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.gallery-hero__scroll-cue svg {
    width: 16px;
    height: 16px;
    animation: gallery-scroll-bounce 1.8s ease-in-out infinite;
}

@keyframes gallery-scroll-bounce {
    0%, 100% { transform: translateY(0); opacity: 0.55; }
    50% { transform: translateY(7px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .gallery-hero__scroll-cue svg {
        animation: none;
    }
}

/* Transparent on purpose — the still-fixed, dimmed hero shows through
   behind the grid as it scrolls (see the file-level comment above).
   The footer (after this wrapper, in normal flow) has its own opaque
   background already, so it still fully covers the hero once reached. */
.gallery-page__content {
    position: relative;
    z-index: 1;
}

/* Fixed vignette bands so grid tiles fade rather than hard-clip as
   they cross the viewport edges. Sit above the transparent grid
   (z-index:1) but below the header (--z-fixed and up). */
.gallery-page__edge-fade {
    position: fixed;
    left: 0;
    right: 0;
    z-index: 2;
    height: 18vh;
    pointer-events: none;
}

.gallery-page__edge-fade--top {
    top: 0;
    background: linear-gradient(to bottom, rgba(7, 25, 41, 0.85), transparent);
}

.gallery-page__edge-fade--bottom {
    bottom: 0;
    background: linear-gradient(to top, rgba(7, 25, 41, 0.85), transparent);
}

/* Same reason as about.css's .site-footer override — the footer is a
   sibling of <main>, not a child of .gallery-page__content, but it
   still needs pulling above the fixed hero's stacking level, and its
   sitewide margin-top (main.css) zeroed too, or that gap shows the
   hero through it. */
body.toman-gallery-page .site-footer {
    position: relative;
    z-index: 2;
    margin-top: 0;
}

/* ── Grid ─────────────────────────────────────────────────────── */

/* Plain uniform rectangles — every tile the same 3:2 size, 2 per row
   on mobile, 3 on tablet and desktop alike. Portrait source photos
   crop into that same shape too (.gallery-grid__item--portrait below
   just biases object-position toward the top, not a different tile
   size), same as every other tile in the row. */
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-3);
    padding-block: clamp(var(--sp-8), 6vw, var(--sp-12));
}

@media (min-width: 640px) {
    .gallery-grid {
        grid-template-columns: repeat(3, 1fr);
        gap: var(--sp-4);
    }
}

/* Own repeatable fade — NOT the sitewide [data-animate] utility (that
   only ever fires once per element, which read as "only the first
   couple of rows animate" since everything below was already
   revealed by the time you scrolled to it). gallery-page.js toggles
   .is-visible on/off every time a tile crosses in/out of the
   viewport, so it fades in from the bottom on the way in and fades
   back out the same way on the way past, every time, not just once. */
.gallery-grid__item {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    border-radius: var(--r-lg);
    overflow: hidden;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    opacity: 0;
    transform: translateY(36px);
    transition:
        box-shadow var(--t-fast),
        opacity 0.6s var(--ease-out),
        transform 0.6s var(--ease-out);
}

.gallery-grid__item.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.gallery-grid__item:hover,
.gallery-grid__item:focus-visible {
    box-shadow: var(--shadow-card-hover);
}

@media (prefers-reduced-motion: reduce) {
    .gallery-grid__item {
        transition: box-shadow var(--t-fast);
    }
}

.gallery-grid__item img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    transition: transform var(--t-slow);
}

/* Same 3:2 tile as everything else — anchored above centre rather
   than pinned all the way to the top. Pure "top" put too much bare
   wall/logo in frame and pushed people's heads down near the bottom
   edge in photos where they stand lower in the shot; this splits the
   difference across the 4 portrait photos so heads never get cut and
   hands/gifts/bouquets held at chest height stay in frame too. */
.gallery-grid__item--portrait img {
    object-position: center 36%;
}

.gallery-grid__item:hover img {
    transform: scale(1.04);
}

/* ── Lightbox — same fixed/backdrop/fade-scale pattern as the search
   modal (main.css .toman-search-modal), so it feels consistent with
   the rest of the site's overlays rather than a one-off. ───────── */

.gallery-lightbox {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    align-items: center;
    justify-content: center;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--t-base), visibility var(--t-base);
}

.gallery-lightbox.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

.gallery-lightbox__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(7, 25, 41, 0.92);
}

.gallery-lightbox__box {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: clamp(var(--sp-6), 8vw, var(--sp-16));
    transform: scale(0.94);
    transition: transform var(--t-base);
}

.gallery-lightbox.is-open .gallery-lightbox__box {
    transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
    .gallery-lightbox__box { transition: opacity var(--t-fast); transform: none; }
}

#galleryLightboxImg {
    max-width: 100%;
    max-height: 100%;
    border-radius: var(--r-md);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
    object-fit: contain;
}

.gallery-lightbox__close {
    position: absolute;
    top: var(--sp-4);
    right: var(--sp-4);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--r-full);
    background: rgba(255, 255, 255, 0.1);
    color: var(--color-white);
    transition: background var(--t-fast);
}

.gallery-lightbox__close:hover {
    background: rgba(255, 255, 255, 0.2);
}

.gallery-lightbox__close svg {
    width: 18px;
    height: 18px;
}

.gallery-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: var(--r-full);
    background: rgba(255, 255, 255, 0.1);
    color: var(--color-white);
    transition: background var(--t-fast);
    z-index: 1;
}

.gallery-lightbox__nav:hover {
    background: rgba(255, 255, 255, 0.2);
}

.gallery-lightbox__nav svg {
    width: 22px;
    height: 22px;
}

.gallery-lightbox__nav--prev {
    left: var(--sp-3);
}

.gallery-lightbox__nav--next {
    right: var(--sp-3);
}

@media (min-width: 768px) {
    .gallery-lightbox__nav--prev {
        left: var(--sp-8);
    }

    .gallery-lightbox__nav--next {
        right: var(--sp-8);
    }
}

.gallery-lightbox__count {
    position: absolute;
    /* Clears the floating mobile .tab-bar (main.css) the same way the
       Brands page showcase does for its own bottom-anchored UI. */
    bottom: calc(3.75rem + var(--sp-4) + env(safe-area-inset-bottom, 0px));
    left: 50%;
    transform: translateX(-50%);
    font-size: var(--text-xs);
    font-weight: var(--fw-medium);
    letter-spacing: 0.06em;
    color: rgba(255, 255, 255, 0.7);
}

@media (min-width: 768px) {
    .gallery-lightbox__count {
        bottom: var(--sp-4);
    }
}
