/*
 * Loop Property — g2lux-exp surface · structural CSS
 *
 * Layout, grid, spacing, and responsive behaviour for the Lux properties
 * listing page. STRUCTURAL ONLY — no fonts, no brand colours, no
 * decorative properties. The theme (giro-2lux-parent) layers its SCSS
 * on top with brand palette and typography.
 *
 * Scoped to .loop-property-g2lux-exp to avoid leaking into non-Lux
 * pages. Class names mirror the Karl Ormerod Lux microsite reference
 * so theme-side SCSS can target them directly.
 *
 * Enqueued by loop-property.php when loop_property_active_surface()
 * returns 'g2lux-exp'.
 */

/* === Container + scope ================================================ */

.loop-property-g2lux-exp,
.loop-property-g2lux-exp * {
    box-sizing: border-box;
}

.loop-property-g2lux-exp .container {
    width: 100%;
    max-width: 1280px;
    margin-left: auto;
    margin-right: auto;
    padding-left: 24px;
    padding-right: 24px;
}

/* Eyebrow base — every Lux eyebrow is tracked uppercase. Individual
   contexts (listings-intro, price-row, single hero, etc.) layer their
   own size / spacing / opacity on top and win at their higher
   specificity; this base just guarantees the uppercase + tracking even
   where a context rule only tweaks opacity or margin. Without it, the
   sold track-record hero eyebrow (which sits in .lux-section--agent —
   opacity only) rendered in sentence case. */
.loop-property-g2lux-exp .eyebrow {
    text-transform: uppercase;
    letter-spacing: 0.22em;
}

/* === Search band (hero) =============================================== */

/* Hero band sits under the site header and runs full-bleed. Generous
   top padding clears any fixed/sticky agency header that overlays the
   page. Dark background + light text are defensive defaults — the
   active theme typically paints these with brand palette via SCSS,
   but without them the band would be invisible against the body and
   the user could lose the search entirely (Kane's flag). */
.loop-property-g2lux-exp .search-band {
    width: 100%;
    /* Top padding clears the fixed transparent header (~90px) AND
       leaves comfortable breathing room below it before the eyebrow /
       H1 — 120px was too tight (header ate most of it). */
    padding: 180px 0 56px;
    background: #1a1a1a;
    color: #ffffff;
}

.loop-property-g2lux-exp .search-band h1 {
    margin: 8px 0 32px;
    line-height: 1.05;
    font-weight: 300;
    /* Scales hero copy between mobile and the full 1280px container.
       Without this, mobile devices render the inherited theme h1
       size which is usually configured for desktop and overflows. */
    font-size: clamp(32px, 5.5vw, 72px);
    letter-spacing: -0.01em;
    /* max-width forces the headline to wrap onto multiple lines on
       desktop. Without it, "Where would you like to live?" sits on a
       single 64px+ line and dominates the hero band horizontally
       instead of forming the magazine-style stack from the reference.
       On mobile the container constrains the width below this anyway,
       so the cap is silently overridden. */
    max-width: 760px;
}

.loop-property-g2lux-exp .search-band .ai-note {
    display: inline-block;
    margin-bottom: 4px;
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Search bar — chunky white panel housing both the input and the
   black SEARCH button. .bigsearch is the white container; its 12px
   padding creates the visible breathing room around the button on
   top / bottom / right. Without the container padding the button
   sits flush against the bar's right edge with no white margin —
   Kane's flag.
   max-width keeps the bar from stretching edge-to-edge of the
   container — visible dark breathing room on the right matches the
   reference's compositional rhythm and lets the popular-search row
   below wrap naturally onto two lines instead of running off in one. */
.loop-property-g2lux-exp .bigsearch {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin-bottom: 24px;
    max-width: 920px;
    padding: 12px;
    /* White panel — the input (transparent bg, dark text) and the dark
       SEARCH button both sit ON this white box, with the button inset
       by the 12px padding so there's a white frame around it. Without
       this the search bar is invisible on the dark hero (dark text +
       dark button on a dark band). */
    background: #ffffff;
}

.loop-property-g2lux-exp .bigsearch input[type="text"] {
    flex: 1 1 auto;
    min-width: 0;
    padding: 16px 22px;
    font-size: 16px;
    border: 0;
    /* transparent so the .bigsearch white shows through — input and
       button sit on the same continuous white panel with the button
       inset by the parent's 12px padding. */
    background: transparent;
    color: #1a1a1a;
    /* Placeholder reads as a soft hint inside the chunky white panel,
       not the same weight as a real value. */
}

.loop-property-g2lux-exp .bigsearch input[type="text"]::placeholder {
    color: #6e6e6e;
}

.loop-property-g2lux-exp .bigsearch button {
    flex: 0 0 auto;
    padding: 16px 32px;
    font-size: 13px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    cursor: pointer;
    border: 0;
    background: #1a1a1a;
    color: #ffffff;
}

/* Popular searches block — "POPULAR SEARCHES" label on a row of its
   own, then the bordered buttons below. flex-wrap lets the buttons
   spill onto a second row at the same column width as the search bar
   (max-width 920px) so the hero composition reads as a single
   coherent stack rather than three different-width strips. */
.loop-property-g2lux-exp .suggestions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    margin-bottom: 24px;
    max-width: 920px;
}

.loop-property-g2lux-exp .suggestions .small {
    flex: 1 0 100%;
    margin-right: 0;
    margin-bottom: 4px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.55;
}

/* Suggestion buttons — rectangular bordered chips (sharp corners,
   not pills). Uppercase tracked copy reads as a tagged stamp; the
   border inherits currentColor so it stays visible on either a dark
   hero or a light theme panel. */
.loop-property-g2lux-exp .suggestions .sug {
    padding: 14px 22px;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    cursor: pointer;
    border-radius: 0;
}

/* Narrative strip. max-width matches the search bar + popular-search
   row above for vertical alignment within the hero. Border-left is
   structural (width + style) — the colour is delegated entirely to
   the theme's .accent-border rule so a brand override doesn't have
   to fight a plugin default. */
.loop-property-g2lux-exp .conv {
    padding: 16px 20px;
    margin: 16px 0 0;
    max-width: 920px;
    border-left-width: 3px;
    border-left-style: solid;
    font-size: 14px;
    line-height: 1.6;
}

/* === Filter row (price slider + beds/baths) =========================== */

.loop-property-g2lux-exp .filter-row.primary {
    display: flex;
    gap: 32px;
    align-items: stretch;
    margin: 24px 0;
    flex-wrap: wrap;
}

.loop-property-g2lux-exp .price-filter {
    flex: 1 1 480px;
    min-width: 280px;
}

/* Price readout — large range above, average underneath. Stacked
   vertically to give the figures room to breathe; the histogram + dual
   slider sit immediately below. */
.loop-property-g2lux-exp .price-readout {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 18px;
}

.loop-property-g2lux-exp .price-readout .pr-range {
    font-size: 22px;
    font-weight: 300;
    line-height: 1.1;
    letter-spacing: -0.01em;
}

/* Average price subtitle — regular case to match the Karl reference
   ("Average price is £2.4m"); the tracked-caps treatment that suits
   labels and stamps reads too formal for this conversational line. */
.loop-property-g2lux-exp .price-readout .pr-avg {
    font-size: 12px;
    letter-spacing: 0;
    text-transform: none;
    opacity: 0.55;
}

/* Histogram — distribution of property prices across 24 equal-width
   buckets. Bars use currentColor so the theme paints them with brand
   accent; opacity set inline by JS per-bar (drops on empty buckets so
   the chart reads as a continuous distribution). */
.loop-property-g2lux-exp .price-histogram {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 64px;
    margin-bottom: 4px;
}

/* Individual histogram bar. Bar height + opacity are set inline by
   price-stats.js. Background left undeclared — .accent-bg (added by
   price-stats.js alongside the size) is the sole colour source, so
   the theme's brand paint applies without a specificity contest. */
.loop-property-g2lux-exp .price-histogram .hist-bar {
    flex: 1 1 0;
    min-width: 4px;
    border-radius: 2px 2px 0 0;
    transition: opacity 0.15s ease;
}

.loop-property-g2lux-exp .dual-slider {
    position: relative;
    height: 28px;
}

.loop-property-g2lux-exp .dual-track {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    transform: translateY(-50%);
    background: currentColor;
    opacity: 0.18;
}

/* Selected-range fill — sits on the track line. left + width are set
   by price-stats.js as the slider handles move. Background is left
   for the theme's .accent-bg (present in the partial markup) so the
   brand accent paints without competing against a plugin default. */
.loop-property-g2lux-exp .dual-fill {
    position: absolute;
    top: 50%;
    height: 2px;
    transform: translateY(-50%);
    /* left, width set inline by JS; background painted by .accent-bg */
}

.loop-property-g2lux-exp .dual-slider input[type="range"] {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    appearance: none;
    background: transparent;
    pointer-events: none;
}

/* Unified range-input treatment for every slider in the Lux surface
   (dual-handle price + single beds + future single baths). Without
   these rules the browser falls back to its default blue/native thumb,
   which Kane flagged on the BEDS slider. Setting both -webkit- and
   -moz- variants covers Chromium and Firefox; iOS Safari follows the
   -webkit- pseudo-element. Background defaults to #1a1a1a so the
   thumb stays dark on the cream listing background; theme SCSS in
   giro-2lux-parent can repaint to brand accent if needed. */
.loop-property-g2lux-exp input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    /* Kill the browser's default input box background — otherwise the
       18px-tall input renders a white panel behind the 2px track,
       which looks odd on the cream filter row. Only the styled track
       + thumb should be visible; the input box itself is transparent. */
    background: transparent;
}

.loop-property-g2lux-exp input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 0;
    background: #1a1a1a;
    pointer-events: auto;
    cursor: pointer;
    /* Centre the thumb's mid-line on the 2px track. Without this,
       webkit lays the thumb along the top of the input's content
       box (see explicit input height further down) and the thumb
       hugs the underside of the track instead of straddling it.
       Calc: (track_height - thumb_height) / 2 = (2 - 18) / 2 = -8px. */
    margin-top: -8px;
}

.loop-property-g2lux-exp input[type="range"]::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 0;
    background: #1a1a1a;
    pointer-events: auto;
    cursor: pointer;
}

/* Non-dual-slider single sliders (beds, future baths) need an
   explicit track since they're not stacked over .dual-track. The
   2px hairline matches the price-slider track for visual unity. */
.loop-property-g2lux-exp .range-wrap input[type="range"]::-webkit-slider-runnable-track {
    height: 2px;
    background: rgba(0, 0, 0, 0.18);
}

.loop-property-g2lux-exp .range-wrap input[type="range"]::-moz-range-track {
    height: 2px;
    background: rgba(0, 0, 0, 0.18);
    border: 0;
}

/* Beds / baths range stack — sits beside the price filter on desktop,
   stacks below on mobile (handled by the responsive block). Generous
   row gap so the "Any" pill chip doesn't collide with adjacent labels.
   justify-content: flex-end pushes the visible rows to the BOTTOM of
   whatever stretched height the parent .filter-row.primary gives this
   column. With only BEDS visible (BATHS gated until ENG-2646), the
   slider sits flush at the bottom — when BATHS reappears it lands
   above BEDS, pushing BEDS up to the position the reference shows. */
.loop-property-g2lux-exp .range-stack {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 24px;
    flex: 0 0 280px;
    /* Nudges the flex-end-aligned .range-group up by 8px so the beds
       slider track sits on the same vertical line as the price
       histogram baseline / price slider track to its left. Kane's
       value — visually verified against the reference. */
    padding-bottom: 8px;
}

.loop-property-g2lux-exp .range-group {
    display: flex;
    align-items: center;
    gap: 20px;
}

/* Label sits to the left of the slider track (BEDS / BATHS). Tracked
   caps + subdued opacity so it reads as a label not a value. */
.loop-property-g2lux-exp .range-group .label {
    flex: 0 0 56px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.5;
}

/* The wrap is the positioning context for the floating value chip. The
   slider track fills the full width of the wrap; the chip sits absolute
   above it and tracks the thumb's x position via range-bubbles.js. The
   padding-top reserves the chip's vertical real estate so the slider
   doesn't collide with it. */
.loop-property-g2lux-exp .range-wrap {
    flex: 1 1 auto;
    position: relative;
    padding-top: 32px;
}

.loop-property-g2lux-exp .range-wrap input[type="range"] {
    display: block;
    width: 100%;
    /* Explicit height matches the thumb diameter so the 2px track
       renders centred within and the thumb sits ON the line (not
       hovering above/below it as it did before the explicit height
       was set). */
    height: 18px;
    margin: 0;
    padding: 0;
}

/* Floating value chip ("Any / 1+ / 2+ / 3+") — sits above the slider
   thumb. JS sets `left` to a calc() of percent + thumb-width offset;
   transform: translateX(-50%) centres the chip on the calculated x.
   Deliberately painted white + black regardless of brand accent —
   the chip is a structural read-out of the slider value and needs
   to stay legible on any section-background palette without relying
   on brand-accent contrast. Not an accent-* target. */
.loop-property-g2lux-exp .range-bubble {
    position: absolute;
    top: 0;
    left: 0; /* JS overrides as soon as the page loads */
    transform: translateX(-50%);
    min-width: 44px;
    padding: 5px 11px;
    background: #ffffff;
    color: #000000;
    /* 1px hairline outline — tracks the text colour via currentColor
       so if the paint ever changes the border follows automatically.
       Defines the pill's edge against the cream / white section
       backgrounds it can sit against. */
    border: 1px solid currentColor;
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-align: center;
    line-height: 1;
    border-radius: 999px;
    /* Smooth follow without lag — short transition keeps the chip
       glued to the thumb during a drag rather than catching up
       a frame later. */
    transition: left 0.05s linear;
    pointer-events: none;
}

.loop-property-g2lux-exp .filter-row.sort-row {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    margin: 16px 0 32px;
}

.loop-property-g2lux-exp .filter-row.sort-row .label {
    font-size: 13px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.loop-property-g2lux-exp .filter-row.sort-row select {
    padding: 8px 12px;
    border: 1px solid currentColor;
    background: transparent;
}

/* Reset filters button — sits at the right end of the sort row.
   Defensive baseline styling so it reads as a secondary action
   (smaller padding, transparent bg, underlined affordance on hover).
   Theme can repaint via .filter-reset selector. */
.loop-property-g2lux-exp .filter-row.sort-row .filter-reset {
    padding: 8px 14px;
    margin-left: 8px;
    border: 0;
    background: transparent;
    color: inherit;
    font-size: 12px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
    opacity: 0.55;
    transition: opacity 0.15s ease;
}

.loop-property-g2lux-exp .filter-row.sort-row .filter-reset:hover {
    opacity: 1;
    text-decoration: underline;
    text-underline-offset: 4px;
}

/* === Listings intro =================================================== */

/* Centered editorial block that introduces the listings collection.
   "EXCLUSIVE LISTINGS · 2026" eyebrow, large h2 headline, supporting
   description underneath — all centre-aligned, with the description
   width capped so the line length stays readable on wide screens. */
.loop-property-g2lux-exp .listings-intro {
    padding: 96px 0 56px;
    text-align: center;
}

.loop-property-g2lux-exp .listings-intro .eyebrow {
    display: inline-block;
    margin-bottom: 24px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.7;
}

.loop-property-g2lux-exp .listings-intro h2 {
    margin: 0 auto 24px;
    line-height: 1.0;
    font-weight: 300;
    /* Larger headline than the cards' street-name h3 — this is the
       page-level statement. Scales between mobile and the full 1280px
       container. */
    font-size: clamp(32px, 5vw, 64px);
    letter-spacing: -0.01em;
    max-width: 1000px;
}

/* Per-char fade-in for the listings-intro headline. The .ch spans
   are inserted at runtime by title-fadein.js. Default state in the
   stylesheet leaves the characters fully visible — the JS sets
   data-animated="1" on the <h2> before flipping the .in class, so
   the hidden-initial state below ONLY applies after JS has hooked
   the element. Users with JS disabled (rare) see the heading
   rendered normally rather than a permanent blank. */
.loop-property-g2lux-exp .listings-intro h2 .ch {
    display: inline-block;
    will-change: opacity, transform;
}

.loop-property-g2lux-exp .listings-intro h2[data-animated="1"] .ch {
    opacity: 0;
    transform: translateX(-12px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}

.loop-property-g2lux-exp .listings-intro h2[data-animated="1"] .ch.in {
    opacity: 1;
    transform: translateX(0);
}

.loop-property-g2lux-exp .listings-intro .strap {
    max-width: 640px;
    margin: 0 auto;
    font-size: 16px;
    line-height: 1.6;
    opacity: 0.7;
}

/* === Property cards (magazine layout) ================================ */

.loop-property-g2lux-exp .features .container {
    display: flex;
    flex-direction: column;
    /* Generous spacing between listings — each card is editorial and
       benefits from clear separation rather than the dense grid pattern
       most listings pages use. Kane's call: was 80px, felt tight. */
    gap: 128px;
    padding-top: 24px;
    padding-bottom: 96px;
    /* Smooth fade between filter states so the visitor sees a gentle
       reflow rather than an abrupt swap (or — worse — no apparent
       change at all when a small slider movement triggers a fetch
       that happens to return the same set of cards). */
    transition: opacity 0.18s ease;
}

/* While the filter AJAX request is in flight, .is-filtering is set
   on the listings container by filter.js. Dimming the cards gives the
   user visible feedback that their drag / sort change is being
   processed — without it, small slider movements that return the
   same result set make the filter feel broken. */
.loop-property-g2lux-exp .features .container.is-filtering {
    opacity: 0.55;
}

/* Sold gallery band filter hides non-matching cards outright (no AJAX
   — the set is already loaded). display: none so the flex column
   reflows cleanly without leaving gaps. */
.loop-property-g2lux-exp article.feature.is-filtered-out {
    display: none;
}

/* === SOLD TRACK-RECORD HERO (dark) ============================
   Opening band on the sold gallery. Reuses .lux-section--agent for
   the dark fill + white text; this block adds the layout. Top
   padding clears the transparent fixed header (the page carries
   loop-property-has-hero so the theme header starts transparent
   over this band). */
.loop-property-g2lux-exp .sold-record-hero {
    width: 100%;
    padding: clamp(140px, 18vh, 220px) 0 clamp(64px, 9vh, 110px);
}

/* Track-record hero content sits in the standard 1280px container so
   the "N homes, sold." title lines up with the Recently sold intro,
   the filter row, and the cards below it — same alignment the sales
   page hero uses (its search band is wrapped in a plain .container).
   An earlier full-bleed override (max-width:none + up-to-96px padding)
   pushed the title out of line with everything beneath it on wide
   screens; the base .container rule now governs the width. */

.loop-property-g2lux-exp .sold-record-hero__title {
    margin: 12px 0 0;
    font-size: clamp(40px, 6vw, 84px);
    font-weight: 300;
    letter-spacing: -0.01em;
    line-height: 1.02;
    max-width: 16ch;
}

.loop-property-g2lux-exp .sold-record-hero__strap {
    margin: 28px 0 0;
    max-width: 46ch;
    font-size: clamp(15px, 1.3vw, 18px);
    line-height: 1.65;
    opacity: 0.82;
}

/* === SOLD GALLERY FILTER BAR ==================================
   Compact price-band pills + sort, matching the Karl Ormerod
   03-sold-gallery reference. Built + driven by sold-filter.js. */
.loop-property-g2lux-exp .lux-sold-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 20px 32px;
    margin: 24px 0 8px;
    padding-bottom: 20px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}

.loop-property-g2lux-exp .lux-sold-filter__bands,
.loop-property-g2lux-exp .lux-sold-filter__sort {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 20px;
}

.loop-property-g2lux-exp .lux-sold-filter__label {
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.5;
}

/* Band pill — text button, no box. Active + hover lift the opacity;
   the count sits alongside the label at reduced weight. */
.loop-property-g2lux-exp .sold-band {
    padding: 0;
    border: 0;
    background: transparent;
    font-family: inherit;
    font-size: 12px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    line-height: 1;
    cursor: pointer;
    color: inherit;
    opacity: 0.55;
    transition: opacity 0.2s ease;
}

.loop-property-g2lux-exp .sold-band:hover,
.loop-property-g2lux-exp .sold-band:focus-visible {
    opacity: 0.85;
}

.loop-property-g2lux-exp .sold-band.is-active {
    opacity: 1;
    /* Accent underline marks the active band. accent-text hook isn't
       used here (it's a whole-button state, not an inline colour) —
       the theme can repaint via .sold-band.is-active if it wants a
       brand tint; the plugin default is a neutral underline. */
    text-decoration: underline;
    text-underline-offset: 6px;
    text-decoration-thickness: 1px;
}

.loop-property-g2lux-exp .sold-band__count {
    margin-left: 6px;
    opacity: 0.6;
}

.loop-property-g2lux-exp .lux-sold-filter__sort select {
    padding: 8px 12px;
    border: 1px solid currentColor;
    background: transparent;
    font-family: inherit;
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
}

/* Two-column magazine layout. Image column capped at 600px so the hero
   doesn't balloon on wide viewports (matches Karl Ormerod reference);
   copy column flexes into whatever remains within the 1280px container.
   minmax(0, ...) on both columns prevents intrinsic content (long
   addresses, wide images) from forcing the grid wider than the
   container. The responsive block below collapses to a single column
   under 900px. */
.loop-property-g2lux-exp article.feature {
    display: grid;
    grid-template-columns: minmax(0, 600px) minmax(0, 1fr);
    gap: 48px;
    align-items: center;
}

/* Alternating layout — every other card image on the right */
.loop-property-g2lux-exp article.feature:nth-child(even) .image {
    order: 2;
}

.loop-property-g2lux-exp article.feature .image {
    position: relative;
    width: 100%;
    /* Let the image column shrink inside its grid track instead of
       holding its content's intrinsic width (prevents mobile overflow). */
    min-width: 0;
}

.loop-property-g2lux-exp .prop-slider {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    overflow: hidden;
}

/* No-image placeholder — same footprint as .prop-slider so a card
   without photography keeps the grid's rhythm. Neutral wash + a
   quiet note; the status flag still anchors top-left via .pill. */
.loop-property-g2lux-exp .prop-noimage {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #efeae2;
}

.loop-property-g2lux-exp .prop-noimage__note {
    font-size: 12px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    opacity: 0.55;
}

.loop-property-g2lux-exp .prop-slides {
    position: relative;
    width: 100%;
    height: 100%;
}

.loop-property-g2lux-exp .prop-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.4s ease;
}

.loop-property-g2lux-exp .prop-img.is-active {
    opacity: 1;
}

/* Slider nav buttons — circular, translucent white panels positioned
   on the vertical middle of the image. Matches the reference's
   ghost-circle treatment. */
.loop-property-g2lux-exp .slider-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    font-size: 16px;
    line-height: 1;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.85);
    color: #1a1a1a;
    cursor: pointer;
    z-index: 2;
}

.loop-property-g2lux-exp .slider-btn.slider-prev {
    left: 12px;
}

.loop-property-g2lux-exp .slider-btn.slider-next {
    right: 12px;
}

/* Status pill ("FOR SALE", "SOLD STC", etc.). Sized and positioned
   structurally to match the Karl Ormerod reference. Background and
   text colour are set here as a defensive baseline so the pill is
   legible on any photo when the theme hasn't restyled it; theme
   SCSS in giro-2lux-parent can repaint to brand if needed. Sharp
   corners (no border-radius) on purpose — the reference uses a
   rectangular panel, not a rounded pill. */
.loop-property-g2lux-exp .pill {
    position: absolute;
    top: 20px;
    left: 20px;
    padding: 14px 26px;
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.16em;
    line-height: 1;
    text-transform: uppercase;
    background: #ffffff;
    color: #1a1a1a;
    z-index: 2;
}

/* Status flag defaults — plain black/white, no brand colour in the
   plugin. Available states (For Sale / To Let) keep the white base
   above; closed states (Sold / STC / Under Offer / Let Agreed)
   invert to black — the two opposites. The .pill--{family} class is
   the hook: the theme overrides these with the agency's configured
   Giro Settings > Properties status colours. */
.loop-property-g2lux-exp .pill--sold,
.loop-property-g2lux-exp .pill--stc,
.loop-property-g2lux-exp .pill--under-offer {
    background: #1a1a1a;
    color: #ffffff;
}

/* Photo counter — dark panel bottom-right of the hero image. Sits
   opposite the FOR SALE pill (which anchors top-left), giving the
   card a diagonal visual rhythm between the two chips. Background /
   text colour set defensively so the chip is legible on any photo
   before the theme paints over it. */
.loop-property-g2lux-exp .photo-count {
    position: absolute;
    bottom: 16px;
    right: 16px;
    padding: 6px 14px;
    font-size: 12px;
    letter-spacing: 0.04em;
    line-height: 1;
    background: rgba(30, 30, 30, 0.85);
    color: #ffffff;
    z-index: 2;
}

.loop-property-g2lux-exp .thumb-strip {
    display: flex;
    gap: 8px;
    margin-top: 12px;
    overflow-x: auto;
    scrollbar-width: thin;
}

.loop-property-g2lux-exp .thumb-strip img {
    flex: 0 0 80px;
    width: 80px;
    height: 60px;
    object-fit: cover;
    cursor: pointer;
    opacity: 0.6;
    transition: opacity 0.2s ease;
}

.loop-property-g2lux-exp .thumb-strip img.is-active,
.loop-property-g2lux-exp .thumb-strip img:hover {
    opacity: 1;
}

.loop-property-g2lux-exp .copy {
    width: 100%;
    /* Shrink within the grid track; without this a long unbroken word or
       address sets a min-content floor that widens the column past the
       viewport on mobile. Paired with overflow-wrap so long tokens
       (e.g. a URL in the editorial preview) break instead of overflow. */
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Editorial eyebrow stack. Three rungs read top-to-bottom as
   "establish position → state location → name the street":
     .num       — "No. 01 · Ormskirk"  (sequence + place, soft)
     .loc-stamp — "ORMSKIRK · L40"     (formal caps stamp)
     h3        — "Crosshall Brow."     (large street name + period)
   Each step gains weight to draw the eye toward the heading. */
.loop-property-g2lux-exp .copy .num {
    display: block;
    margin-bottom: 8px;
    font-size: 14px;
    letter-spacing: 0.01em;
    opacity: 0.55;
}

.loop-property-g2lux-exp .copy .loc-stamp {
    display: block;
    margin-bottom: 8px;
    font-size: 12px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
}

/* Street-name heading — deliberately tight vertical breathing room
   above and below (≈8px each) so the address block reads as a single
   visual unit with the eyebrows above and the editorial copy below.
   Karl's reference shows the street name flowing very closely into
   the description rather than floating in white space. */
.loop-property-g2lux-exp .copy h3 {
    margin: 8px 0 8px;
    line-height: 1.0;
    font-weight: 300;
    /* Scales between 30px on narrow column widths and 48px on the
       full 1280px container. clamp() keeps the heading legible on
       every breakpoint without media queries cluttering the rules. */
    font-size: clamp(30px, 3.5vw, 48px);
    letter-spacing: -0.01em;
}

/* Editorial preview — 5-line visual clamp with native ellipsis.
   Full description text remains in the DOM (only visually hidden past
   line 5) so schema.org markup and search-engine crawls still see the
   complete content. The "View property" CTA reveals the expanded version
   on the single page.
   -webkit-line-clamp ships in every modern browser; the standard
   line-clamp property is declared alongside for forward-compat. */
.loop-property-g2lux-exp .copy .editorial {
    margin: 0 0 32px;
    font-size: 15px;
    line-height: 1.7;
    opacity: 0.85;
    display: -webkit-box;
    -webkit-line-clamp: 5;
    line-clamp: 5;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Spec row sits between two hairline dividers. rgba over currentColor
   so the rule renders as a soft hairline regardless of text colour —
   currentColor would match the body text and read as a heavy black
   bar. Theme SCSS can override .spec-row { border-color: … } when
   the brand needs a different tone. */
.loop-property-g2lux-exp .spec-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 40px;
    margin-bottom: 0;
    padding: 24px 0;
    border-top: 1px solid rgba(0, 0, 0, 0.12);
    border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}

.loop-property-g2lux-exp .spec-row .cell {
    display: flex;
    flex-direction: column;
    gap: 10px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.5;
}

.loop-property-g2lux-exp .spec-row .cell .v {
    font-size: 30px;
    font-weight: 300;
    line-height: 1;
    letter-spacing: 0;
    text-transform: none;
    opacity: 1;
    /* Numeric cells own a tabular-style metric — light weight at a
       large size lets the digits anchor the row visually. */
}

/* TYPE cell wraps a text value rather than a digit. Uppercase to match
   the reference ("DETACHED HOUSE"), slightly smaller font-size since
   uppercase letters carry more visual weight than digits at the same
   nominal size — keeps the row visually balanced. Tracked spacing
   reads as a labelled stamp not running text. */
.loop-property-g2lux-exp .spec-row .type-cell .v {
    font-size: 22px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Price row sits below the spec row, sharing its bottom divider as
   an implicit top edge, and adds its own divider underneath so the
   actions block reads as a separate beat. Eyebrow label anchors
   left, formatted price right-aligns at the same baseline. */
.loop-property-g2lux-exp .price-row {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 32px;
    padding: 24px 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}

.loop-property-g2lux-exp .price-row .eyebrow {
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.5;
    flex: 0 0 auto;
}

.loop-property-g2lux-exp .price-row .price {
    font-size: 36px;
    font-weight: 300;
    line-height: 1.0;
    letter-spacing: -0.01em;
    flex: 0 0 auto;
    text-align: right;
}

.loop-property-g2lux-exp .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* === CTA buttons ================================================
   Plugin-owned button system for the Lux surface. Every button
   carries `cta ghost` as its structural base — .ghost supplies
   the baseline transparent panel + border/text rules everything
   else layers on. Modifier classes swap palette + behaviour:

     .cta.ghost                Default for LIGHT section backgrounds.
                               Transparent panel, black border/text.
                               Hover: black fill sweeps in from left
                               to right, text flips to white.

     .cta.ghost.on-image       For DARK section backgrounds (hero
                               photo, video, agent band). Transparent
                               panel, white border/text. Hover: white
                               fill sweeps in from left to right,
                               text flips to black.

     .cta.ghost.overlay-light  Solid WHITE focus button, hero
                               Arrange-a-viewing only. Solid white
                               fill, dark text, white border. Hover
                               nudges the fill a shade darker — no
                               sweep animation (Kane spec: "slightly
                               darker than white background colour,
                               no left to right sweeping"). Distinct
                               from surrounding overlay secondaries
                               which start transparent and sweep
                               white in.

     .cta.ghost.brand-focus    Marker for the property-accent focus
                               CTA. Default shape matches .on-image
                               (transparent + white outline, sweeps
                               to white with text flipping dark on
                               hover, border stays white throughout)
                               so the CTA reads correctly on dark
                               sections before the theme lands. The
                               giro-2lux-parent theme overrides at
                               same specificity with the accent
                               colour from Loop Property settings —
                               no !important needed.

   Editorial voice = 0 border-radius (sharp corners), tracked
   uppercase letter-spacing, sweep-fill hover via a ::before
   pseudo-element scaled from left. Reduced-motion users get an
   instant fill instead of a sweep. */

/* Structural base — plain inline-block (NOT inline-flex; that was
   the layout culprit). Buttons flow inline inside the .actions flex
   parent as ordinary inline-block boxes. Sweep hover uses a
   two-stop linear gradient at 200% width; default position 100%
   shows the transparent half only, hover shifts to 0% revealing
   the coloured half — animated across background-position. */
.loop-property-g2lux-exp .cta {
    display: inline-block;
    padding: 16px 32px;
    font-family: inherit;
    font-size: 12px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    /* Text always sits centred inside the button regardless of the
       parent's text-align. Matters most when buttons are stretched
       full-width in a column-stacked layout (agent card actions on
       any viewport, hero actions on mobile) — otherwise the label
       would inherit the parent's text-align and drift to the left
       edge of the stretched button box. */
    text-align: center;
    background-color: transparent;
    background-size: 200% 100%;
    background-position: 100% 50%;
    background-repeat: no-repeat;
    border: 1px solid transparent;
    border-radius: 0;
    transition: background-position 0.45s ease, color 0.3s ease, border-color 0.3s ease;
}

/* Icon glyphs sit inline with the text. vertical-align: middle
   pushes them onto the text mid-line; small horizontal margin
   creates the icon-to-text gap. :last-child swaps the margin
   sides for icons that appear AFTER the text (Brochure download
   arrow). */
.loop-property-g2lux-exp .cta svg {
    display: inline-block;
    width: 12px;
    height: 12px;
    vertical-align: middle;
    margin: 0 6px 0 -2px;
    fill: currentColor;
    stroke: currentColor;
}

.loop-property-g2lux-exp .cta svg:last-child {
    margin: 0 -2px 0 6px;
}

/* Ghost — default is a SOLID off-black button with white text + border
   (bold + legible on the light editorial sections). On hover the fill
   sweeps out to transparent, with the text + border flipping off-black.
   Gradient runs transparent-left -> colour-right, so the base
   background-position of 100% shows the colour half (= filled by
   default); hover shifts to 0% to reveal the transparent half. */
.loop-property-g2lux-exp .cta.ghost {
    color: #ffffff;
    border-color: #ffffff;
    background-image: linear-gradient(to right, transparent 50%, #1a1a1a 50%);
}

.loop-property-g2lux-exp .cta.ghost:hover,
.loop-property-g2lux-exp .cta.ghost:focus-visible {
    background-position: 0% 50%;
    color: #1a1a1a;
    border-color: #1a1a1a;
}

/* Overlay — dark section variant. Transparent + white border/text.
   Sweep fills white; text flips dark on hover. */
.loop-property-g2lux-exp .cta.ghost.on-image {
    color: #ffffff;
    border-color: #ffffff;
    background-image: linear-gradient(to right, #ffffff 50%, transparent 50%);
}

.loop-property-g2lux-exp .cta.ghost.on-image:hover,
.loop-property-g2lux-exp .cta.ghost.on-image:focus-visible {
    background-position: 0% 50%;
    color: #1a1a1a;
}

/* Overlay-light — solid white focus button for the hero Arrange-a-
   viewing. NO sweep (Kane spec: 'slightly darker than white, no
   left to right sweeping'). Explicit background-image: none +
   background-size: auto override the base sweep setup. */
.loop-property-g2lux-exp .cta.ghost.overlay-light {
    background-color: #ffffff;
    color: #1a1a1a;
    border-color: #ffffff;
    background-image: none;
    background-size: auto;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.loop-property-g2lux-exp .cta.ghost.overlay-light:hover,
.loop-property-g2lux-exp .cta.ghost.overlay-light:focus-visible {
    background-color: #ebebeb;
    border-color: #ebebeb;
}

/* Brand-focus — the inverse of .ghost: default TRANSPARENT with an
   off-black outline + off-black text; on hover the off-black fill
   sweeps in and the text + border flip white. Gradient runs
   colour-left -> transparent-right, so the base background-position
   of 100% shows the transparent half (= empty by default); hover
   shifts to 0% to fill. Theme can repaint the accent later at higher
   specificity (.pill.pill-- pattern) without !important. */
.loop-property-g2lux-exp .cta.ghost.brand-focus {
    color: #1a1a1a;
    border-color: #1a1a1a;
    background-image: linear-gradient(to right, #1a1a1a 50%, transparent 50%);
}

.loop-property-g2lux-exp .cta.ghost.brand-focus:hover,
.loop-property-g2lux-exp .cta.ghost.brand-focus:focus-visible {
    background-position: 0% 50%;
    color: #ffffff;
    border-color: #ffffff;
}

/* Reduced-motion — skip the sweep animation but keep the state change. */
@media (prefers-reduced-motion: reduce) {
    .loop-property-g2lux-exp .cta {
        transition: color 0.15s ease, border-color 0.15s ease;
    }
}

/* Agent-card CTA is compact — smaller padding + font so the buttons
   fit the card layout without dwarfing the surrounding contact
   block. Applies across all ghost variants used inside the card. */
.loop-property-g2lux-exp .lux-agent-card__body .actions .cta {
    padding: 12px 20px;
    font-size: 11px;
}

/* === Responsive ======================================================= */

@media (max-width: 900px) {
    /* minmax(0, 1fr) — NOT a bare 1fr (= minmax(auto, 1fr)). A bare 1fr
       lets the track grow to its content's min-content size, so a long
       word / address in .copy pushes the single column — and the
       width:100% image inside it — past the viewport (Kane's mobile
       overflow). minmax(0, …) caps the track at the container, matching
       the desktop rule's own overflow guard. */
    .loop-property-g2lux-exp article.feature {
        grid-template-columns: minmax(0, 1fr);
        gap: 24px;
    }

    .loop-property-g2lux-exp article.feature:nth-child(even) .image {
        order: 0; /* on mobile, image always on top */
    }

    .loop-property-g2lux-exp .filter-row.primary {
        flex-direction: column;
        gap: 16px;
    }

    .loop-property-g2lux-exp .range-stack {
        flex: 1 1 auto;
    }

    .loop-property-g2lux-exp .features .container {
        gap: 64px;
        padding-bottom: 48px;
    }
}

@media (max-width: 600px) {
    .loop-property-g2lux-exp .container {
        padding-left: 16px;
        padding-right: 16px;
    }

    /* Hero band — preserve some top padding for header clearance but
       drop the desktop excess so the mobile viewport isn't dominated
       by empty space above the H1. */
    .loop-property-g2lux-exp .search-band {
        padding: 72px 0 32px;
    }

    .loop-property-g2lux-exp .bigsearch {
        flex-direction: column;
    }

    /* Sort row right-justifies on desktop; on mobile let it flow left
       so it doesn't shove the label into a weird narrow gutter. */
    .loop-property-g2lux-exp .filter-row.sort-row {
        justify-content: flex-start;
    }

    /* Spec row dividers get tighter padding + smaller gap on mobile
       so the four cells still sit on one row instead of wrapping
       awkwardly. Numbers shrink slightly to match. */
    .loop-property-g2lux-exp .spec-row {
        gap: 20px;
        padding: 16px 0;
    }

    .loop-property-g2lux-exp .spec-row .cell .v {
        font-size: 24px;
    }

    .loop-property-g2lux-exp .spec-row .type-cell .v {
        font-size: 18px;
    }

    /* Price collapses to a more digestible mobile size; the price-
       row layout stays horizontal (justify-content: space-between
       still works), just smaller. */
    .loop-property-g2lux-exp .price-row {
        padding: 18px 0;
        margin-bottom: 24px;
    }

    .loop-property-g2lux-exp .price-row .price {
        font-size: 26px;
    }

    /* Price filter — histogram gets a bit shorter on mobile so the
       whole filter block doesn't dominate the viewport. */
    .loop-property-g2lux-exp .price-histogram {
        height: 48px;
    }

    .loop-property-g2lux-exp .price-readout .pr-range {
        font-size: 18px;
    }

    /* CTAs stack vertically on the narrowest screens — two side-by-side
       buttons compete for thumb space and squeeze the label tracking. */
    .loop-property-g2lux-exp .actions {
        flex-direction: column;
        align-items: stretch;
    }

    /* Slider buttons step in from the edges a touch on mobile so they
       don't crowd the photo counter / pill in tight viewports. */
    .loop-property-g2lux-exp .slider-btn {
        width: 40px;
        height: 40px;
    }
}

/* === Single property hero ============================================= */

/* Full-bleed image slideshow with persistent content overlay. Sits at
   the top of the Lux single property page. min-height fills the
   viewport so the hero dominates the first paint; the site header
   (fixed by theme SCSS) sits ON TOP of the hero via z-index, so the
   hero image runs all the way up to the top of the page.
   Background is transparent so the parallax-fixed slides below show
   through this container. The slides themselves carry the defensive
   dark fallback. */
.loop-property-g2lux-exp .single-hero {
    position: relative;
    width: 100%;
    min-height: 100vh;
    overflow: hidden;
    background: transparent;
    color: #ffffff;
}

/* Parallax: slides are fixed to the viewport so they stay in place
   as the user scrolls, while the hero content overlay above and the
   body content below scroll normally. Once the user has scrolled past
   the hero, the body section's opaque background (.property-single-
   container > .module.property below) rises up and covers the fixed
   slides — the effect Kane asked for: 'house looks like it is being
   covered by the content beneath it in the next row.' */
.loop-property-g2lux-exp .single-hero__slides {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 100vh;
    z-index: 0;
}

.loop-property-g2lux-exp .single-hero__slide {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center center;
    background-repeat: no-repeat;
    /* Defensive dark fallback per-slide — covers the brief moment
       before the slide's background-image paints. Was on the hero
       wrapper itself before the parallax refactor. */
    background-color: #1a1a1a;
    opacity: 0;
    /* Crossfade duration on a CSS custom property so the theme can
       tune it site-wide alongside the homepage lux-hero. 1.2s reads
       as a considered dissolve without feeling like a delay between
       slides — Kane noted 2.4s was too slow on the single page. */
    transition: opacity var(--lps-crossfade-dur, 1.2s) ease-in-out;
}

.loop-property-g2lux-exp .single-hero__slide.is-active {
    opacity: 1;
}

/* Breathing zoom — the Lux hero slideshow scales each slide slowly
   during its display. Directions alternate per slide (in, then out,
   then in...) so the endpoints match at every handoff:
     zoom-in  scale(1)   -> scale(1.1)
     zoom-out scale(1.1) -> scale(1)
   Slide N ends where slide N+1 begins — the imagery keeps "breathing"
   through the rotation instead of snapping back to 1.0 on every
   change. `both` fill holds the end scale while the outgoing slide
   fades out (matching the incoming's start scale).

   Duration comes from --lps-zoom-dur (settable per-theme; matches
   Kane's theme homepage variable name for future consistency).
   Fallback: 6s, synced to single-hero.js rotation interval so a
   slide reaches its end scale exactly as the next takes over.
   Linear easing avoids an easing "bump" at the crossfade. */
.loop-property-g2lux-exp .single-hero__slide--zoom-in {
    animation: lux-single-hero-zoom-in var(--lps-zoom-dur, 6s) linear both;
}

.loop-property-g2lux-exp .single-hero__slide--zoom-out {
    animation: lux-single-hero-zoom-out var(--lps-zoom-dur, 6s) linear both;
}

@keyframes lux-single-hero-zoom-in  { from { transform: scale(1);   } to { transform: scale(1.1); } }
@keyframes lux-single-hero-zoom-out { from { transform: scale(1.1); } to { transform: scale(1);   } }

@media (prefers-reduced-motion: reduce) {
    .loop-property-g2lux-exp .single-hero__slide--zoom-in,
    .loop-property-g2lux-exp .single-hero__slide--zoom-out {
        animation: none;
        transform: none;
    }
}

/* Scrim — gradient over the bottom 60% of each slide so the
   persistent content overlay (white text on busy photography) stays
   legible. Top stays clear so the imagery reads as the primary
   subject; the gradient deepens toward the bottom where the heading
   and price block sit. */
.loop-property-g2lux-exp .single-hero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(0, 0, 0, 0) 0%,
        rgba(0, 0, 0, 0) 35%,
        rgba(0, 0, 0, 0.55) 75%,
        rgba(0, 0, 0, 0.85) 100%
    );
    pointer-events: none;
}

/* Persistent content overlay — same content shown across every slide.
   z-index sits above .single-hero__slides so the text reads against
   the scrim regardless of which slide is active. Two-column flex on
   desktop matches the Karl Ormerod reference: heading anchored
   bottom-left, price block + actions anchored bottom-right; both
   share the same baseline so the composition reads as one row of
   editorial copy. Mobile collapses back to a single column via the
   responsive block lower down. */
.loop-property-g2lux-exp .single-hero__content {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 56px;
    min-height: 100vh;
    /* Bottom padding lifts the price + actions column comfortably
       clear of the slide pill row and the bottom edge of the
       viewport — Kane's call: 'a little too close to the bottom of
       the page for my liking'. */
    padding: 120px 0 160px;
}

/* Hero gets a wider canvas than the editorial body — the editorial
   container caps at 1280px to keep line-lengths comfortable for
   long-form copy, but the hero is a big visual moment where the
   heading and price block want to push toward the screen edges.
   Override the .container max-width for the hero context only;
   padding scales with viewport so the content stays comfortably
   inset on every screen size (24px on mobile, ~96px on 4k). */
.loop-property-g2lux-exp .single-hero .container {
    max-width: none;
    padding-left: clamp(24px, 5vw, 96px);
    padding-right: clamp(24px, 5vw, 96px);
}

.loop-property-g2lux-exp .single-hero__heading {
    flex: 1 1 auto;
    min-width: 0;
    /* max-width keeps the huge street-name h1 from running into the
       price block when the viewport is wide and the street name is
       long. The h1 itself already clamps; this just bounds the
       column. */
    max-width: 760px;
    margin-bottom: 0;
}

/* Status pill on the single hero. Reuses the .pill styling from the
   listings card but sits as part of the content stack (not absolute-
   positioned over the image), so margins behave as block flow. */
.loop-property-g2lux-exp .single-hero .pill {
    position: static;
    display: inline-block;
    margin-bottom: 20px;
}

.loop-property-g2lux-exp .single-hero .loc {
    font-size: 12px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.85;
    margin-bottom: 16px;
}

    /*
    fixes an issue where the module.property class was adding a padding
    to the bottom at the bottom of the single property page, which was 
    causing a gap between the last section and the footer
    */
   .loop-property-g2lux-exp > .module.property {
    padding-bottom: 0;
   }

/* Body section beneath the hero. position: relative + explicit
   z-index above the fixed slides — actual background painting moves
   to the per-section .lux-section--* rules below so the body reads
   as a layered editorial flow with distinct tonal blocks rather than
   one undifferentiated panel. */

.loop-property-g2lux-exp .property-single-container > .module.property {
    position: relative;
    z-index: 5;
    /* Zero the theme's generic .module padding-bottom. The padding
       area carries no background, so the fixed parallax slides
       (z-index 0) showed through as a strip of hero photo between
       the last lux-section and the footer. The lux-sections manage
       their own vertical rhythm; the module wrapper needs none. */
    padding-bottom: 0;
}

/* Section blocks down the body. .lux-section is a full-width wrapper
   that carries the opaque background; the .global-grid inside keeps
   the actual content centred at the editorial max-width. Without
   the full-width wrapper the background would only paint the centred
   column and the parallax hero would bleed through on either side.
   .lux-section--gallery / --content / --location pick the per-section
   tone; themes override via SCSS to match brand palette. */
.loop-property-g2lux-exp .lux-section {
    position: relative;
    width: 100%;
    padding-top: 96px;
    padding-bottom: 96px;
}

/* Per-section palette. Kept plugin-side for now so the surface has
   a coherent editorial rhythm out of the box on every install.
   FUTURE (per Kane): make the "dark section" tone (--video and
   --agent) agency-configurable via Loop Property ACF settings —
   Properties > Section Palette or similar. Two coupled values will
   need to be exposed together, not just the background, since
   flipping the background inverts the required text/contrast:
       property_lux_dark_section_bg    (default #1a1a1a)
       property_lux_dark_section_text  (default #ffffff)
   Same coupling applies if the light-section tones ever become
   configurable — background + text always ship as a pair so
   contrast is preserved without operators having to pick two
   values in isolation. Theme SCSS reads the ACF values into CSS
   custom properties on the surface root; plugin rules then
   reference var(--...) with these hex values as fallbacks. */
.loop-property-g2lux-exp .lux-section--summary    { background: #faf7f2; }
.loop-property-g2lux-exp .lux-section--details    { background: #ffffff; }
.loop-property-g2lux-exp .lux-section--video      { background: #1a1a1a; color: #ffffff; }
.loop-property-g2lux-exp .lux-section--gallery    { background: #faf7f2; }
.loop-property-g2lux-exp .lux-section--floorplan  { background: #ffffff; }
.loop-property-g2lux-exp .lux-section--agent      { background: #1a1a1a; color: #ffffff; }
.loop-property-g2lux-exp .lux-section--similar    { background: #faf7f2; }
.loop-property-g2lux-exp .lux-section--location   { background: #f3ede4; }

/* === FLOORPLAN ============================================ */

.loop-property-g2lux-exp .lux-floorplan__head {
    display: flex;
    justify-content: space-between;
    align-items: end;
    margin-bottom: 32px;
    gap: 24px;
}

.loop-property-g2lux-exp .lux-floorplan__head h3 {
    margin: 0;
}

/* Floorplan canvas — panel showing the floorplan as a background
   image. Interaction lives on the buttons below (Open in lightbox /
   Open in tab) so the canvas itself doesn't have a hidden link
   behaviour; this keeps the keyboard-nav target predictable.
   contain rather than cover so the entire plan is visible without
   cropping; light cream backdrop matches the section bg around any
   transparent edges of the plan PNG. */
.loop-property-g2lux-exp .lux-floorplan__canvas {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    background-color: #faf7f2;
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
}

/* Actions row below the floorplan — 'Open in lightbox' triggers the
   shared lightbox modal (single-image mode), 'Open in tab' opens the
   image at full resolution in a new browser tab. */
.loop-property-g2lux-exp .lux-floorplan__below {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 24px;
}

@media (max-width: 900px) {
    .loop-property-g2lux-exp .lux-floorplan__head {
        flex-direction: column;
        align-items: flex-start;
    }
    .loop-property-g2lux-exp .lux-floorplan__canvas {
        aspect-ratio: 3 / 4;
    }
}

/* === SIMILAR PROPERTIES BAND ============================== */

.loop-property-g2lux-exp .lux-similar__head {
    display: flex;
    justify-content: space-between;
    align-items: end;
    margin-bottom: 32px;
    gap: 24px;
}

.loop-property-g2lux-exp .lux-similar__head h3 {
    margin: 0;
}

/* Tighter eyebrow on the similar band — Karl reference spaces the
   'SIMILAR IN THE BOOK' letters more closely than the page-level
   eyebrows above. Stays uppercase / tracked-caps but at 0.14em
   rather than the default 0.22em. */
.loop-property-g2lux-exp .lux-section--similar .eyebrow {
    letter-spacing: 0.14em;
    margin-bottom: 12px;
}

/* Hug the headline closer to its eyebrow inside this section —
   12px gap between eyebrow + h3, rather than the section-default 24px,
   so the lead reads as one tight composition. */
.loop-property-g2lux-exp .lux-section--similar h3 {
    margin-top: 0;
}

/* The shortcode output renders the editorial Lux property cards
   (large two-column hero + description + spec + price row + actions).
   For the similar-band context we want a compact 3-up grid of
   smaller cards — image on top, minimal copy beneath. CSS scopes
   the rules to .lux-similar-band so the listings page keeps its
   editorial cards untouched.

   The shortcode wraps its cards in something like
   .module-properties > article.feature. The exact wrapper depends
   on the version of the shortcode; we target both .module-properties
   and the direct .feature children inside our band. */
.loop-property-g2lux-exp .lux-similar-band > div,
.loop-property-g2lux-exp .lux-similar-band > section,
.loop-property-g2lux-exp .lux-similar-band > ul,
.loop-property-g2lux-exp .lux-similar-band .module-properties,
.loop-property-g2lux-exp .lux-similar-band .property-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 32px;
    list-style: none;
    padding: 0;
    margin: 0;
}

/* Compact each Lux feature card inside the similar band — image on
   top (5:4 thumbnail), tight copy column underneath. Hide the
   editorial elements the band doesn't need (description, spec
   dividers, full action row). */
.loop-property-g2lux-exp .lux-similar-band article.feature {
    display: flex;
    flex-direction: column;
    grid-template-columns: none; /* override the editorial 2-col grid */
    gap: 16px;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .image,
.loop-property-g2lux-exp .lux-similar-band article.feature:nth-child(even) .image {
    order: 0; /* image always on top in compact mode */
    aspect-ratio: 5 / 4;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .prop-slider {
    aspect-ratio: 5 / 4;
}

/* Hide chrome that doesn't fit the compact card:
     - slide prev/next buttons (no interaction on the thumbnail)
     - thumbnail strip
     - photo counter
     - long editorial description
     - 'No. NN · Town' sequence eyebrow (this is a different list)
     - spec row dividers + spec content (the .compact-spec line below
       takes over with the inline 'X bed · X bath · Type' format)
     - .actions row (the link wraps the whole card via the title)
     - 'Guide Price' eyebrow in the price row — the price reads cleaner
       on its own in a compact card; the qualifier belongs on the
       single property page

   Loc-stamp + h3 are KEPT — the compact card now mirrors the
   listings page composition (LOCATION · POSTCODE -> Street name ->
   spec line -> price) per Kane's observation that Karl's reference
   matches that exact pattern. */
.loop-property-g2lux-exp .lux-similar-band article.feature .pill,
.loop-property-g2lux-exp .lux-similar-band article.feature .slider-btn,
.loop-property-g2lux-exp .lux-similar-band article.feature .thumb-strip,
.loop-property-g2lux-exp .lux-similar-band article.feature .photo-count,
.loop-property-g2lux-exp .lux-similar-band article.feature .editorial,
.loop-property-g2lux-exp .lux-similar-band article.feature .num,
.loop-property-g2lux-exp .lux-similar-band article.feature .spec-row,
.loop-property-g2lux-exp .lux-similar-band article.feature .actions,
.loop-property-g2lux-exp .lux-similar-band article.feature .price-row .eyebrow {
    display: none;
}

/* Compact spec line — '4 bed · 3 bath · Detached'. Hidden in
   editorial mode (.spec-row takes over there); visible only inside
   the similar band where the multi-cell grid would be overkill. */
.loop-property-g2lux-exp article.feature .compact-spec {
    display: none;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .compact-spec {
    display: block;
    margin: 4px 0 8px;
    font-size: 12px;
    letter-spacing: 0.02em;
    opacity: 0.7;
    line-height: 1.4;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .copy {
    padding: 0;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .loc-stamp {
    margin-bottom: 4px;
    font-size: 10px;
}

.loop-property-g2lux-exp .lux-similar-band article.feature h3 {
    font-size: clamp(20px, 1.6vw, 24px);
    margin: 0 0 4px;
    line-height: 1.1;
}

/* Compact price row — eyebrow + price on one line, no top/bottom
   dividers (the band's editorial rhythm doesn't need them). */
.loop-property-g2lux-exp .lux-similar-band article.feature .price-row {
    padding: 0;
    margin: 0;
    border: 0;
    gap: 8px;
    align-items: baseline;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .price-row .eyebrow {
    font-size: 10px;
    letter-spacing: 0.18em;
}

.loop-property-g2lux-exp .lux-similar-band article.feature .price-row .price {
    font-size: 20px;
    text-align: left;
}

@media (max-width: 900px) {
    .loop-property-g2lux-exp .lux-similar__head {
        flex-direction: column;
        align-items: flex-start;
    }
    .loop-property-g2lux-exp .lux-similar-band > div,
    .loop-property-g2lux-exp .lux-similar-band > section,
    .loop-property-g2lux-exp .lux-similar-band > ul,
    .loop-property-g2lux-exp .lux-similar-band .module-properties,
    .loop-property-g2lux-exp .lux-similar-band .property-list {
        grid-template-columns: repeat(2, 1fr);
        gap: 24px;
    }
}

@media (max-width: 600px) {
    .loop-property-g2lux-exp .lux-similar-band > div,
    .loop-property-g2lux-exp .lux-similar-band > section,
    .loop-property-g2lux-exp .lux-similar-band > ul,
    .loop-property-g2lux-exp .lux-similar-band .module-properties,
    .loop-property-g2lux-exp .lux-similar-band .property-list {
        grid-template-columns: 1fr;
        gap: 24px;
    }
}

/* Shared eyebrow + heading rhythm for each section's lede.
   .accent + .accent-text are the existing theme hooks — theme paints
   the brand colour over the defensive baseline. */
.loop-property-g2lux-exp .lux-section .eyebrow {
    display: inline-block;
    margin-bottom: 16px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
}

.loop-property-g2lux-exp .lux-section h2,
.loop-property-g2lux-exp .lux-section h3 {
    margin: 0 0 24px;
    line-height: 1.05;
    font-weight: 300;
    letter-spacing: -0.01em;
}

.loop-property-g2lux-exp .lux-section h2 {
    font-size: clamp(28px, 3.4vw, 44px);
}

.loop-property-g2lux-exp .lux-section h3 {
    font-size: clamp(24px, 2.6vw, 36px);
}

.loop-property-g2lux-exp .lux-section p {
    margin: 0 0 16px;
    font-size: 16px;
    line-height: 1.7;
    opacity: 0.85;
}

/* === SUMMARY ============================================== */

.loop-property-g2lux-exp .lux-summary {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    gap: 64px;
    align-items: start;
}

.loop-property-g2lux-exp .lux-summary__lede p:last-child {
    margin-bottom: 0;
}

/* Six-cell spec grid — 3 columns x 2 rows on desktop, collapses to
   2x3 on tablet, 2-up flex on mobile. Each cell is a label/value
   pair stacked vertically with a subtle divider above. */
.loop-property-g2lux-exp .lux-spec-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 32px 24px;
}

.loop-property-g2lux-exp .lux-spec-grid .cell {
    padding-top: 16px;
    border-top: 1px solid rgba(0, 0, 0, 0.15);
}

.loop-property-g2lux-exp .lux-spec-grid .cell .lbl {
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    opacity: 0.55;
    margin-bottom: 8px;
}

.loop-property-g2lux-exp .lux-spec-grid .cell .v {
    font-size: 28px;
    font-weight: 300;
    line-height: 1;
}

/* === DETAILS / MATERIAL INFORMATION ====================== */

.loop-property-g2lux-exp .lux-details {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
    gap: 64px;
    align-items: start;
}

.loop-property-g2lux-exp .lux-details__lede p:last-child {
    margin-bottom: 0;
}

.loop-property-g2lux-exp .lux-matinfo {
    width: 100%;
    border-collapse: collapse;
}

.loop-property-g2lux-exp .lux-matinfo th,
.loop-property-g2lux-exp .lux-matinfo td {
    padding: 16px 0;
    text-align: left;
    font-weight: 400;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    vertical-align: top;
    font-size: 14px;
    line-height: 1.5;
}

.loop-property-g2lux-exp .lux-matinfo th {
    width: 40%;
    padding-right: 24px;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    opacity: 0.55;
}

/* === VIDEO ================================================ */

.loop-property-g2lux-exp .lux-video__head {
    display: flex;
    justify-content: space-between;
    align-items: end;
    margin-bottom: 32px;
    gap: 24px;
}

.loop-property-g2lux-exp .lux-video__head h3 {
    margin: 0;
}

/* Right-hand action pair on the video section head (Cinema mode +
   Open on YouTube). Flex row so the two transparent buttons sit
   side-by-side with a small gap; wraps to a stack when it can't
   fit on the same line as the title. */
.loop-property-g2lux-exp .lux-video__head-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* Video canvas — large aspect-ratio panel with the thumbnail as
   background and a play button overlay. Click opens the embed (theme
   JS responsibility — replaces innerHTML with the iframe). */
.loop-property-g2lux-exp .lux-video__canvas {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background-size: cover;
    background-position: center;
    background-color: #0a0a0a;
    cursor: pointer;
    overflow: hidden;
}

.loop-property-g2lux-exp .lux-video__canvas::after {
    /* Subtle gradient overlay so the play button stays legible on any
       thumbnail. */
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.45) 100%);
    pointer-events: none;
}

/* Once the visitor clicks Play, single-video.js appends the iframe
   and sets .is-playing. Hide the scrim gradient so the iframe sits
   on a clean panel. */
.loop-property-g2lux-exp .lux-video__canvas.is-playing::after {
    display: none;
}

/* Play-button pill overlaid on the video canvas. Structure only —
   background + text colour supplied by the theme via .accent-bg (in
   the partial markup). */
.loop-property-g2lux-exp .lux-video__play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 18px 28px;
    border-radius: 999px;
    font-size: 12px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
}

/* === GALLERY (bento grid) ================================= */

.loop-property-g2lux-exp .lux-gallery__head {
    display: flex;
    justify-content: space-between;
    align-items: end;
    margin-bottom: 32px;
    gap: 24px;
}

.loop-property-g2lux-exp .lux-gallery__head h3 {
    margin: 0;
}

/* Bento grid — 4-column auto-flow with periodic tiles spanning 2x2
   for visual variety. Every 7th tile gets the larger treatment so the
   composition reads as deliberate editorial rather than uniform. */
.loop-property-g2lux-exp .lux-bento-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: 160px;
    gap: 12px;
}

.loop-property-g2lux-exp .lux-bento-tile {
    background-size: cover;
    background-position: center;
    background-color: #e0d9cb;
    cursor: pointer;
    transition: transform 0.4s ease;
}

.loop-property-g2lux-exp .lux-bento-tile:hover {
    transform: scale(1.01);
}

/* Periodic tile size variation — first tile spans 2x2, then every 7th
   tile (8, 15, 22, ...). Gives the bento its asymmetric rhythm
   without manually authoring tile classes per index. */
.loop-property-g2lux-exp .lux-bento-tile[data-idx="0"],
.loop-property-g2lux-exp .lux-bento-tile[data-idx="7"],
.loop-property-g2lux-exp .lux-bento-tile[data-idx="14"],
.loop-property-g2lux-exp .lux-bento-tile[data-idx="21"] {
    grid-column: span 2;
    grid-row: span 2;
}

/* === AGENT BAND =========================================== */

.loop-property-g2lux-exp .lux-agent {
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    gap: 64px;
    /* Vertically centre the lede against the card's height so the
       two columns read as balanced siblings — as the card grows
       taller (bigger portrait photo, more agent detail), the lede
       keeps its midpoint alongside the card's midpoint. */
    align-items: center;
}

.loop-property-g2lux-exp .lux-section--agent .eyebrow {
    opacity: 0.85;
}

.loop-property-g2lux-exp .lux-agent__lede p:last-child {
    margin-bottom: 0;
}

/* Agent card — vertical composition: portrait photo full-width at
   the top, body stacked beneath. A slightly-lighter panel over the
   dark section background so the card reads as a distinct chip
   rather than floating text. */
.loop-property-g2lux-exp .lux-agent-card {
    display: flex;
    flex-direction: column;
    background: rgba(255, 255, 255, 0.06);
    /* 2px hairline outline just slightly lighter than the card
       fill — reads as a defined chip edge against the dark section
       without competing visually with the content inside. */
    border: 2px solid rgba(255, 255, 255, 0.12);
    /* Padding sits on the body only — the photo extends to the
       card edges. Max-width bounds the card so a full-width column
       parent (mobile / narrow desktop) doesn't stretch the portrait
       photo comically wide; on wider desktops the grid parent
       already gives the column its width. */
    max-width: 420px;
    width: 100%;
}

/* Low-res image fallback — the plugin's agent-card.js measures the
   source image's natural dimensions on load and adds this modifier
   class if either dimension is below the usable-portrait threshold
   (see agent-card.js MIN_WIDTH / MIN_HEIGHT). The photo drops to a
   small circular avatar at its native resolution so browsers don't
   upscale and blur it into an unrecognisable smudge. Also triggered
   by the image failing to load at all — a bare card slot with no
   image looks broken; a small placeholder circle reads better. */
.loop-property-g2lux-exp .lux-agent-card--lowres .lux-agent-card__photo {
    width: 140px;
    height: 140px;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    margin: 32px auto 0;
    display: block;
    object-position: center center;
}

.loop-property-g2lux-exp .lux-agent-card__photo {
    display: block;
    width: 100%;
    /* 4:5 portrait — a natural aspect for headshots and matches the
       Karl reference (tall rectangle, not square). object-fit: cover
       trims to fill so any avatar shape crops sensibly. */
    aspect-ratio: 4 / 5;
    height: auto;
    object-fit: cover;
    object-position: center top;
    background: rgba(255, 255, 255, 0.1);
}

.loop-property-g2lux-exp .lux-agent-card__body {
    padding: 28px 32px 32px;
}

.loop-property-g2lux-exp .lux-agent-card__body h4 {
    margin: 0 0 6px;
    font-size: 22px;
    font-weight: 300;
}

.loop-property-g2lux-exp .lux-agent-card__body .role {
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    opacity: 0.7;
    margin-bottom: 20px;
}

.loop-property-g2lux-exp .lux-agent-card__body .phone a {
    color: inherit;
    text-decoration: none;
    font-size: 18px;
    font-weight: 300;
}

.loop-property-g2lux-exp .lux-agent-card__body .phone {
    margin-bottom: 20px;
}

.loop-property-g2lux-exp .lux-agent-card__body .actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* === RESPONSIVE — section grids collapse to single column ===== */

@media (max-width: 900px) {
    .loop-property-g2lux-exp .lux-summary,
    .loop-property-g2lux-exp .lux-details,
    .loop-property-g2lux-exp .lux-agent {
        grid-template-columns: 1fr;
        gap: 40px;
    }
    .loop-property-g2lux-exp .lux-spec-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .loop-property-g2lux-exp .lux-bento-grid {
        grid-template-columns: repeat(3, 1fr);
        grid-auto-rows: 140px;
    }
    .loop-property-g2lux-exp .lux-video__head,
    .loop-property-g2lux-exp .lux-gallery__head {
        flex-direction: column;
        align-items: flex-start;
    }
    .loop-property-g2lux-exp .lux-agent-card {
        /* Photo already full-width via aspect-ratio; tighten body
           padding a touch so the card doesn't dominate the collapsed
           single-column layout. */
        max-width: none;
    }
    .loop-property-g2lux-exp .lux-agent-card__body {
        padding: 24px 24px 28px;
    }
}

@media (max-width: 600px) {
    /* Tighter vertical rhythm on mobile so the page doesn't feel
       like endless padding between sections. */
    .loop-property-g2lux-exp .lux-section {
        padding-top: 56px;
        padding-bottom: 56px;
    }
    .loop-property-g2lux-exp .lux-bento-grid {
        grid-template-columns: repeat(2, 1fr);
        grid-auto-rows: 120px;
        gap: 8px;
    }
    .loop-property-g2lux-exp .lux-spec-grid .cell .v {
        font-size: 22px;
    }
    /* Card body text centres on mobile so the card reads as a
       balanced chip. Buttons keep their column stack — full-width
       tappable targets are the mobile-friendly pattern here. */
    .loop-property-g2lux-exp .lux-agent-card__body {
        text-align: center;
    }
}

/* === Gallery lightbox ================================================== */

/* Fixed full-viewport overlay used by the gallery (see lightbox.js).
   Rendered ONCE on first interaction and reused — sits inside <body>,
   NOT scoped to .loop-property-g2lux-exp because document.body is the
   parent. .is-open toggles the visible/interactive state. */
.lux-lightbox {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(10, 10, 10, 0.94);
    color: #ffffff;
    padding: 24px;
}

.lux-lightbox.is-open {
    display: flex;
}

/* Body scroll lock while the lightbox is open — set position:fixed so
   the page underneath doesn't scroll on touch + restored on close.
   The top: -Npx is set inline by JS to preserve the scroll position. */
body.lux-lightbox-open {
    position: fixed;
    width: 100%;
    overflow: hidden;
}

.lux-lightbox__stage {
    position: relative;
    max-width: 90vw;
    max-height: 80vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lux-lightbox__img {
    max-width: 100%;
    max-height: 80vh;
    object-fit: contain;
    display: block;
    /* Subtle drop shadow lifts the image off the dark backdrop. */
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
}

/* Close button — top-right of viewport. */
.lux-lightbox__close {
    position: absolute;
    top: 20px;
    right: 24px;
    width: 48px;
    height: 48px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    color: #ffffff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease;
}

.lux-lightbox__close:hover {
    background: rgba(255, 255, 255, 0.22);
}

/* Prev / Next nav — flank the image left and right at vertical centre. */
.lux-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 56px;
    height: 56px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    color: #ffffff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease;
}

.lux-lightbox__nav:hover {
    background: rgba(255, 255, 255, 0.22);
}

.lux-lightbox__nav--prev { left: 24px; }
.lux-lightbox__nav--next { right: 24px; }

/* Caption + counter row, anchored at the bottom of the viewport. */
.lux-lightbox__meta {
    position: absolute;
    bottom: 24px;
    left: 24px;
    right: 24px;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    font-size: 12px;
    letter-spacing: 0.06em;
    opacity: 0.85;
}

.lux-lightbox__caption {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lux-lightbox__counter {
    flex: 0 0 auto;
}

/* Tile becomes focus-visible via keyboard nav (tabindex set by JS) —
   ring colour stays neutral; theme can repaint via SCSS. */
.loop-property-g2lux-exp .lux-bento-tile:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 2px;
}

@media (max-width: 600px) {
    .lux-lightbox__nav {
        width: 44px;
        height: 44px;
        font-size: 22px;
    }
    .lux-lightbox__nav--prev { left: 8px; }
    .lux-lightbox__nav--next { right: 8px; }
    .lux-lightbox__close {
        top: 12px;
        right: 12px;
        width: 40px;
        height: 40px;
        font-size: 22px;
    }
    .lux-lightbox__meta {
        bottom: 12px;
        left: 12px;
        right: 12px;
        font-size: 11px;
    }
}

.loop-property-g2lux-exp .single-hero__title {
    margin: 0;
    line-height: 0.95;
    font-weight: 300;
    /* Bigger than the listings-intro h2 — this is the hero of a single
       property, the largest typography on the surface. */
    font-size: clamp(48px, 7vw, 96px);
    letter-spacing: -0.02em;
}

.loop-property-g2lux-exp .single-hero__price-block {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    /* Children right-align on desktop so the price + spec + actions
       form a clean column anchored to the right edge. Reverts to
       flex-start on mobile (responsive block below). No max-width —
       the .actions row can be wider than the price/spec text so 4
       buttons can fit on a single line. Since children are right-
       aligned via align-items + text-align, price/spec still hug
       the right edge regardless of the block's overall width. */
    align-items: flex-end;
    text-align: right;
    gap: 8px;
}

.loop-property-g2lux-exp .single-hero__price-block .eyebrow {
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.85;
}

.loop-property-g2lux-exp .single-hero__price-block .price {
    font-size: clamp(36px, 4vw, 56px);
    font-weight: 300;
    letter-spacing: -0.01em;
    line-height: 1;
}

.loop-property-g2lux-exp .single-hero__price-block .spec {
    font-size: 14px;
    letter-spacing: 0.04em;
    opacity: 0.85;
    margin-bottom: 28px;
}

/* Action row — primary CTA filled (.accent-bg theme hook), the
   secondary CTAs use the ghost treatment with a translucent white
   border so they read on dark backgrounds without the .ghost class
   inheriting the listing card's currentColor (which is white here
   and would still work, but explicit is cleaner). justify-content:
   flex-end so the row right-aligns within its price-block column. */
.loop-property-g2lux-exp .single-hero .actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 8px;
}

/* Slide navigation arrows — flanking the hero centred vertically.
   Translucent white circles, same visual language as the listings-
   card prev/next buttons but larger for the hero scale. */
.loop-property-g2lux-exp .single-hero__nav {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none; /* the buttons themselves opt back in */
}

.loop-property-g2lux-exp .single-hero__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 0;
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
    pointer-events: auto;
    transition: background 0.15s ease;
}

.loop-property-g2lux-exp .single-hero__arrow:hover {
    background: rgba(255, 255, 255, 0.32);
}

.loop-property-g2lux-exp .single-hero__arrow--prev { left: 28px; }
.loop-property-g2lux-exp .single-hero__arrow--next { right: 28px; }

/* Slide pills — short horizontal bars at the bottom-centre of the
   hero. Active pill is LONGER (32 -> 56px) and gets .accent-bg so
   the theme paints brand colour. Kane's call: "longer and coloured
   using an accent colour to highlight it is active."
   Inactive bars at 50% opacity white so they're clearly visible on
   any photo without disappearing into a bright sky. */
.loop-property-g2lux-exp .single-hero__pills {
    position: absolute;
    bottom: 48px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 10px;
    /* Container swallows no pointer events — the pill buttons opt back
       in individually. Without this the flex container's box (which
       extends 5px above/below the visible 3px-tall pills due to
       align-items) sits over the tail of the .actions row on shorter
       viewports and blocks hover / clicks on the CTA row. Same
       pattern used on .single-hero__nav for the arrows. */
    pointer-events: none;
}

.loop-property-g2lux-exp .single-hero__pills .single-hero__pill {
    pointer-events: auto;
}

.loop-property-g2lux-exp .single-hero__pill {
    width: 32px;
    height: 3px;
    padding: 0;
    border: 0;
    background: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition: width 0.4s ease, background 0.4s ease, opacity 0.4s ease;
}

.loop-property-g2lux-exp .single-hero__pill:hover {
    background: rgba(255, 255, 255, 0.85);
}

.loop-property-g2lux-exp .single-hero__pill.is-active {
    width: 64px;
    /* Defensive solid-white default so the active pill reads as
       distinct from the translucent inactive pills even before the
       theme paints .accent-bg. The plugin JS also toggles .accent-bg
       on the active pill, so a theme rule like
         .loop-property-g2lux-exp .single-hero__pill.accent-bg
         { background: <accent> }
       repaints this at same specificity — theme loads later in the
       cascade so it wins the override cleanly. */
    background-color: #ffffff;
}

@media (max-width: 900px) {
    /* Two-column hero collapses to single column on tablet + mobile —
       price block stacks beneath the heading rather than competing
       with it for the available width. */
    .loop-property-g2lux-exp .single-hero__content {
        flex-direction: column;
        align-items: flex-start;
        gap: 40px;
        padding: 96px 0 120px;
    }
    .loop-property-g2lux-exp .single-hero__heading {
        max-width: none;
    }
    .loop-property-g2lux-exp .single-hero__price-block {
        align-items: flex-start;
        text-align: left;
        max-width: none;
    }
    .loop-property-g2lux-exp .single-hero .actions {
        justify-content: flex-start;
    }
    .loop-property-g2lux-exp .single-hero__title {
        font-size: clamp(40px, 8vw, 64px);
    }
    .loop-property-g2lux-exp .single-hero__arrow {
        width: 44px;
        height: 44px;
        font-size: 20px;
    }
    .loop-property-g2lux-exp .single-hero__arrow--prev { left: 16px; }
    .loop-property-g2lux-exp .single-hero__arrow--next { right: 16px; }
}

@media (max-width: 600px) {
    .loop-property-g2lux-exp .single-hero__title {
        font-size: clamp(34px, 9vw, 48px);
    }
    .loop-property-g2lux-exp .single-hero .actions {
        flex-direction: column;
        align-items: stretch;
    }
    /* On narrow viewports the arrows crowd the content edges — hide
       them and rely on the pill row + touch swipe (theme could wire
       swipe later) for nav. */
    .loop-property-g2lux-exp .single-hero__nav {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .loop-property-g2lux-exp .single-hero__slide,
    .loop-property-g2lux-exp .single-hero__pill {
        transition: none;
    }
}

/* === FOOTER — lift above the fixed parallax slides ==============
   The hero slides are position: fixed at z-index 0. Positioned
   elements paint above static-flow backgrounds, so without a
   stacking fix the theme footer's background renders UNDER the
   fixed slides and the hero photo shows through at the bottom of
   the page. Body sections already occlude the slides via z-index 5
   on .property-single-container > .module.property; the footer
   sits OUTSIDE <main> so it needs its own lift.

   Scoped via body.loop-property-single-hero (the surface class
   .loop-property-g2lux-exp is on <main> and can't reach a sibling
   footer — same constraint as the sticky-bar overrides above). */
body.loop-property-single-hero .site-footer {
    position: relative;
    z-index: 5;
}

/* === STICKY AGENT BAR — Lux-specific overrides =========================
   Structural bar styling (position: fixed at top, slide-in transform,
   layout, transitions) lives in the plugin-wide stylesheet:
     assets/css/property-single-sticky-agent.css
   That file is enqueued once per surface based on the single-UX
   toggle, so it stays the source of truth for cross-surface behaviour.
   The rules below are Lux-only tweaks — targeted via the surface body
   class so G2 and G2X keep the default bar composition unchanged. */

/* Hide the Request-a-viewing CTA (and the fallback "Contact now" tel
   link that takes its place on sold/let listings) from the sticky
   bar on Lux. Rationale: the bar already exposes phone + WhatsApp
   directly in the agent block on both desktop and mobile; a form CTA
   sitting alongside a WhatsApp icon reads as friction, not choice.
   The wrapper is hidden (not just the button) so no empty flex cell
   consumes gap/padding on the right-hand edge.

   Scoped via body.loop-property-single-hero rather than the usual
   .loop-property-g2lux-exp descendant because the sticky bar renders
   in wp_footer — as a SIBLING of <main class="loop-property-g2lux-exp">,
   not a descendant. loop-property-single-hero is a body class added
   by templates/g2lux-exp/single.php and only present on Lux single
   pages, so this rule is naturally single-page-only (which matches
   where the sticky bar renders anyway). */
body.loop-property-single-hero .loop-property-sticky-agent-bar-actions {
    display: none;
}

/* === "Property no longer available" (410) — Lux ======================
   templates/g2lux-exp/property-404.php. Clean, centred editorial page:
   no gradient panel, no card — just eyebrow + title + line + two .cta
   ghost buttons. Generous top padding pushes the content clear of the
   site header (this page has no hero to sit the header over). */
.loop-property-g2lux-exp .lux404 {
    width: 100%;
    /* Top padding clears the site header (no hero on this page); the
       min-height keeps the footer from riding up on this short page. */
    padding: clamp(120px, 16vh, 200px) 0 clamp(72px, 10vh, 120px);
    min-height: 60vh;
    text-align: center;
}

.loop-property-g2lux-exp .lux404__inner {
    max-width: 640px;
}

.loop-property-g2lux-exp .lux404 .eyebrow {
    display: inline-block;
    margin-bottom: 20px;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    opacity: 0.7;
}

.loop-property-g2lux-exp .lux404__title {
    margin: 0 0 20px;
    font-weight: 300;
    line-height: 1.05;
    letter-spacing: -0.01em;
    font-size: clamp(32px, 5vw, 56px);
}

.loop-property-g2lux-exp .lux404__text {
    margin: 0 auto 40px;
    max-width: 42ch;
    font-size: 16px;
    line-height: 1.6;
    opacity: 0.75;
}

/* Two ghost buttons, centred; stack on the narrowest screens. */
.loop-property-g2lux-exp .lux404__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
}

@media (max-width: 480px) {
    .loop-property-g2lux-exp .lux404__actions {
        flex-direction: column;
        align-items: stretch;
    }
}

