/* Home — Figma frame 1:11. Pixel reads live in the plan's digest
   (docs/superpowers/plans/2026-07-30-home-page.md); this file implements
   every value 1:1, structural decisions documented in template-home.php's
   file-top comment (esp. the count-ladder ≤1280 branch). */

/* ── Hero — video (optional) over an always-present image fallback ──
   Band: height calc(100svh - header), min-height 560px guard. Video/image
   both position:absolute inset:0 — DOM order (image, then video) puts the
   video on top; js/home.js adds .is-hidden to fall back to the image on
   reduced-motion or playback error. Scrim ink at 50% over whichever medium
   shows. Title centred; three-line white-on-photo title — line-height 1.15
   is a deliberate compromise against the sitewide big-heading lh:1 rule
   (⚠️ flagged for Liam's live review, per the digest). Cue pinned near the
   band foot, smooth-scrolls to #vl-home-devs via CSS only. */
.vl-home-hero {
    position: relative;
    height: calc(100svh - var(--vl-header-h));
    min-height: 560px;
    overflow: hidden;
}

.vl-home-hero__video,
.vl-home-hero__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.vl-home-hero__video.is-hidden {
    display: none; /* js/home.js: reduced-motion or playback error — image shows beneath */
}

.vl-home-hero__scrim {
    position: absolute;
    inset: 0;
    background: rgba(50, 56, 59, 0.5); /* = --vl-ink at 50% */
}

.vl-home-hero__content {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding-inline: var(--vl-edge);
    text-align: center;
}

.vl-home-hero__title {
    margin: 0;
    max-width: 1000px; /* sitewide hero cap, hero-page.css precedent */
    color: var(--vl-white);
    font-size: var(--vl-text-hero);
    font-weight: var(--vl-weight-regular);
    line-height: 1.15; /* ⚠️ compromise, not the sitewide lh:1 rule — see file-top note */
}

.vl-home-hero__title strong {
    font-weight: var(--vl-weight-semibold);
}

.vl-home-hero__cue {
    position: absolute;
    left: 50%;
    bottom: 50px; /* node: cue bottom (1030) → band bottom (1080) = 50px */
    transform: translateX(-50%);
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    color: var(--vl-white);
    text-decoration: none;
}

.vl-home-hero__cue-label {
    font-size: 18px;
    font-weight: var(--vl-weight-semibold);
    letter-spacing: 0.1em; /* node 1.8px at 18px */
}

.vl-home-hero__cue-arrow {
    display: block;
    width: 40px;
    height: 21px; /* node 39.6×21 */
}

/* Continuous drop-bounce on the chevron (Liam live review 2026-07-31 —
   the neweraprinting.co.uk "drop-bounce-icon" scroll-cue genre): a gentle
   endless bob signalling content below. Animation (and the smooth scroll)
   live inside the no-preference gate — reduced-motion users get a static
   chevron. Label stays still; only the arrow moves. */
@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth; /* scoped to this stylesheet — home template only */
    }

    .vl-home-hero__cue-arrow {
        animation: vl-cue-bob 2s ease-in-out infinite;
    }

    @keyframes vl-cue-bob {
        0%,
        100% {
            transform: translateY(0);
        }

        50% {
            transform: translateY(10px);
        }
    }
}

/* ── Our developments — count ladder (see template-home.php file-top) ──
   Section padding 82/100 (also the sticky-zone wait/stop, below). */
.vl-home-devs {
    padding-block: 82px 100px;
}

.vl-home-devs__heading {
    margin: 0 0 63px; /* node: heading bottom → cards top */
    text-align: center;
    color: var(--vl-black);
    font-size: var(--vl-text-display);
    font-weight: var(--vl-weight-regular);
    line-height: 1;
}

.vl-home-devs__heading strong {
    font-weight: var(--vl-weight-semibold);
}

/* Static grid (count 1–3) — columns = count, cards fill the container. */
.vl-home-devs__grid {
    display: grid;
    gap: 30px; /* sitewide card-grid gap */
}

.vl-home-devs__grid > .vl-dev-card {
    min-width: 0; /* grid item content must never widen its track */
}

.vl-home-devs__grid--count-1 {
    grid-template-columns: 1fr;
}

/* Single-development state (Liam live review 2026-07-31): the full-width
   STACKED card read as two giant rows — split it instead: text panel LEFT,
   image RIGHT, 50/50. Page-scoped overrides only — card-development.css is
   untouched for every other consumer. DOM order is media-then-body, so
   order swaps the columns; the media's own 513/342 aspect keeps driving
   the card's height (800×533 at the full container — the node card height,
   coincidentally exactly). */
.vl-home-devs__grid--count-1 .vl-dev-card__link {
    flex-direction: row;
}

.vl-home-devs__grid--count-1 .vl-dev-card__media {
    order: 2; /* image right */
    flex: 0 0 50%;
}

.vl-home-devs__grid--count-1 .vl-dev-card__body {
    order: 1; /* text left */
    justify-content: center;
}

/* Hero-scale type for the single-dev state (Liam live review 2026-07-31:
   card-size text swam in the 800×533 panel). Scoped ≥768 — mobile reverts
   to the stacked card below, where the component's own sizes are right. */
@media (min-width: 768px) {
    .vl-home-devs__grid--count-1 .vl-dev-card__body {
        gap: 20px; /* card rhythm 12 → 20 at this scale */
        padding: 50px 30px; /* tablet: 50 top/bottom, 30 sides (Liam live review 2026-07-31 — was 50 all round for a few minutes) */
    }

    .vl-home-devs__grid--count-1 .vl-dev-card__name {
        font-size: var(--vl-text-value); /* 40px (34 tablet) — banner, not card */
        font-weight: var(--vl-weight-semibold);
        line-height: 1.1; /* the value token's own node ratio (about values) */
    }

    .vl-home-devs__grid--count-1 .vl-dev-card__location {
        font-size: var(--vl-text-card-title); /* 24px */
        line-height: 1.1667;
    }

    .vl-home-devs__grid--count-1 .vl-dev-card__cta {
        margin-top: 30px; /* + the 20px gap = 50 above the pill */
    }
}

@media (min-width: 1025px) {
    .vl-home-devs__grid--count-1 .vl-dev-card__body {
        padding: 50px 100px; /* desktop: 50 top/bottom, 100 sides (Liam, 2026-07-31) */
    }
}

@media (max-width: 767px) {
    /* Back to the standard stacked card on mobile. */
    .vl-home-devs__grid--count-1 .vl-dev-card__link {
        flex-direction: column;
    }

    .vl-home-devs__grid--count-1 .vl-dev-card__media {
        order: 0;
        flex: 0 1 auto;
    }

    .vl-home-devs__grid--count-1 .vl-dev-card__body {
        order: 0;
        justify-content: flex-start;
    }
}

.vl-home-devs__grid--count-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Two-up cards fill the container (Liam's count ladder), which at the
   component's 513/342 image ratio made ~720px-tall cards (Liam live
   review 2026-07-31: too big). Widen the crop to 2:1 — the slim-band
   image's own ratio — so the image drops to ~392px and the card to ~590.
   ≥1281 only: below that the snap carousel owns the layout at the
   component's normal ratio. */
@media (min-width: 1281px) {
    .vl-home-devs__grid--count-2 .vl-dev-card__media {
        aspect-ratio: 2 / 1;
    }
}

.vl-home-devs__grid--count-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Carousel mode (4+) — desktop paged set, hidden ≤1280 (the flat grid takes
   over there instead — see .vl-home-devs__grid--flat below). Structure
   mirrors template-parts/gallery-carousel.php: viewport/track/page/dots. */
.vl-home-devs__viewport {
    overflow: clip; /* not hidden — a clipped box can't be scrolled into (gallery-carousel.css precedent) */
}

.vl-home-devs__track {
    display: flex;
    align-items: flex-start;
    transition: transform 0.45s ease;
}

@media (prefers-reduced-motion: reduce) {
    .vl-home-devs__track {
        transition: none;
    }
}

.vl-home-devs__page {
    flex: 0 0 100%; /* one page of 3 cards per carousel slide */
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 30px;
}

.vl-home-devs__page > .vl-dev-card {
    min-width: 0;
}

/* Flat peek-scroller (4+ count, ≤1280 only — see template-home.php's
   file-top ladder comment for why this ISN'T a second JS carousel). Hidden
   by default; the ≤1280 media query below both reveals it and applies the
   88%/44% peek treatment, same block also used by the 2/3-count grid. */
.vl-home-devs__grid--flat {
    display: none;
}

/* ── Dots — shared construction, reused verbatim by the testimonial quote
   carousel below ("identical construction", per the digest): 20px ink-
   outline circles, 30px pitch, active = ink fill. Matches the plot-gallery
   dot pattern (gallery-carousel.css). ── */
.vl-home-dots {
    display: flex;
    justify-content: center;
    gap: 10px; /* 30px pitch − 20px dot */
    margin-top: 50px;
}

/* Arrow row (css/carousel-nav.css). The 50px clearance above moves from the
   dot run to this row, since the dots now sit INSIDE it — see that file. Ink
   to match these dots; the chevron flips to the band colour on hover, and the
   two bands differ: the showcase is on cream, the testimonial on cornflower. */
.vl-carousel-nav {
    margin-top: 50px;
    color: var(--vl-ink);
    --vl-carousel-arrow-on: var(--vl-cream);
}

/* Testimonial: on the cornflower band, and LEFT-aligned following its own dots
   (Liam, 2026-07-31 — node 2:81 sits at the text column's left edge). The
   showcase stays centred on cream. */
.vl-home-testimonial .vl-carousel-nav {
    justify-content: flex-start;
    --vl-carousel-arrow-on: var(--vl-cornflower);
}

/* Testimonial dots LEFT-aligned (Liam live review 2026-07-31 — and the
   frame's own read: node 2:81 sits at x1010, the text column's left edge).
   The showcase dots stay centred per their node. */
.vl-home-testimonial .vl-home-dots {
    justify-content: flex-start;
}

.vl-home-dot {
    width: 20px;
    height: 20px;
    padding: 0;
    border: 1px solid var(--vl-ink);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    transition: background-color 0.2s ease;
}

.vl-home-dot[aria-current="true"] {
    background: var(--vl-ink);
}

.vl-home-dot:focus-visible {
    outline: 2px solid var(--vl-cornflower);
    outline-offset: 2px;
}

/* ── Testimonial band — cornflower, v-cutout + quotes (spec: NO autoplay) ── */
.vl-home-testimonial {
    background: var(--vl-cornflower);
}

.vl-home-testimonial__cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); /* fr, not fixed 750px — feature-row.css precedent (a fixed track overflows under ~1780px) */
    gap: 100px;
    align-items: center;
    padding-block: 65px;
}

.vl-home-testimonial__cols > * {
    min-width: 0;
}

.vl-home-testimonial__cutout {
    display: block;
    width: 100%;
}

/* The photo INSIDE the V (Liam, 2026-07-31): clipped by the inline
   objectBoundingBox clipPath in template-home.php — the logo's own v
   glyph. Box ratio = the artwork's 750×688 (node 2:77); any photo covers
   it and the clip does the rest — no baked backgrounds, works on any
   band colour. */
.vl-home-testimonial__photo {
    display: block;
    width: 100%;
    aspect-ratio: 750 / 688;
    object-fit: cover;
    clip-path: url(#vl-v-clip);
}

.vl-home-testimonial__heading {
    margin: 0 0 40px; /* node: heading bottom → quote top */
    color: var(--vl-black);
    font-size: var(--vl-text-display);
    font-weight: var(--vl-weight-regular);
    line-height: 1;
}

.vl-home-testimonial__heading strong {
    font-weight: var(--vl-weight-semibold);
}

/* Half-width-column display headings step to 52px through 1025–1280
   (Liam sitewide ruling, 2026-07-31 — see feature-row.css). */
@media (min-width: 1025px) and (max-width: 1280px) {
    .vl-home-testimonial__heading {
        font-size: 52px;
    }
}

.vl-home-testimonial__viewport {
    overflow: clip;
}

.vl-home-testimonial__track {
    display: flex;
    transition: transform 0.45s ease;
}

@media (prefers-reduced-motion: reduce) {
    .vl-home-testimonial__track {
        transition: none;
    }
}

.vl-home-testimonial__quote {
    flex: 0 0 100%;
    min-width: 0;
    margin: 0;
    color: var(--vl-black);
    font-style: italic; /* Omnes Italic (i5) — the base Regular weight, NOT Light Italic */
    font-weight: var(--vl-weight-regular);
    font-size: 24px;
    line-height: 1.1667; /* 28/24, unitless */
}

/* ── Feature rows: no home-specific CSS — css/feature-row.css (Task 2)
   already carries the CTA pill + flush-band variant this page consumes. ── */

/* ── Latest updates ── */
.vl-home-updates {
    padding-block: 82px 100px; /* also the sticky-zone stop, below */
}

.vl-home-updates__heading {
    margin: 0 0 63px;
    text-align: center;
    color: var(--vl-black);
    font-size: var(--vl-text-display);
    font-weight: var(--vl-weight-regular);
    line-height: 1;
}

.vl-home-updates__heading strong {
    font-weight: var(--vl-weight-semibold);
}

.vl-home-updates__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 30px;
}

/* ── Snap-carousel block — ported VERBATIM from update-single.css's
   .vl-recent__grid rules (same 88%/44% peek pattern), scoped to this
   section's grid AND to the showcase's count-2/3 + flat grids above. ── */
@media (max-width: 1280px) {
    /* count-2 rejoined the carousel (Liam, 2026-07-31 second ruling) BUT
       the sitewide no-peek rule still governs: in the 2-per-view band
       (900–1280) two cards fill as a 50/50 grid — the static override
       below this block — and only BELOW 900 (1 per view) do they snap
       with a genuine peek. */
    .vl-home-devs__grid--count-2,
    .vl-home-devs__grid--count-3,
    .vl-home-devs__grid--flat,
    .vl-home-updates__grid {
        display: flex;
        gap: 30px;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        margin-inline: calc(50% - 50vw);
        padding-inline: calc(50vw - 50%);
        scroll-padding-inline: calc(50vw - 50%);
        scrollbar-width: none; /* Firefox */
    }

    .vl-home-devs__grid--count-2::-webkit-scrollbar,
    .vl-home-devs__grid--count-3::-webkit-scrollbar,
    .vl-home-devs__grid--flat::-webkit-scrollbar,
    .vl-home-updates__grid::-webkit-scrollbar {
        display: none; /* Chrome/Safari/Edge */
    }

    .vl-home-devs__grid--count-2 > .vl-dev-card,
    .vl-home-devs__grid--count-3 > .vl-dev-card,
    .vl-home-devs__grid--flat > .vl-dev-card,
    .vl-home-updates__grid .vl-update-card {
        flex: 0 0 88%; /* < 100% of the content width = the neighbour peek */
        scroll-snap-align: center;
    }

    /* Count-1 never scrolls (nothing to page through) — deliberately excluded above. */

    .vl-home-devs__set--desktop {
        display: none; /* the flat grid takes over below 1281 (4+ count) */
    }

    .vl-home-devs__grid--flat {
        display: flex; /* overrides the desktop-hidden default above this media query */
    }
}

/* Two-up band (900–1280) — same ratio as dev-related/update-single. */
@media (max-width: 1280px) and (min-width: 900px) {
    .vl-home-devs__grid--count-3 > .vl-dev-card,
    .vl-home-devs__grid--flat > .vl-dev-card,
    .vl-home-updates__grid .vl-update-card {
        flex-basis: 44%;
    }
}

/* count-2 in the 2-per-view band: both cards fit — the no-peek rule as a
   static override (count is known server-side, no quantity query needed).
   Below 900 the carousel block above owns it: 1-up + genuine peek. */
@media (max-width: 1280px) and (min-width: 900px) {
    .vl-home-devs__grid--count-2 {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        margin-inline: 0;
        padding-inline: 0;
    }
}

/* ── Testimonial stack — no frame below desktop (derived, flagged) ── */
@media (max-width: 1024px) {
    .vl-home-testimonial__cols {
        grid-template-columns: minmax(0, 1fr);
        gap: 70px; /* stacked copy↔image, sitewide 70/50 rhythm */
    }

    .vl-home-testimonial__cutout {
        width: 75%; /* full-column V was too big stacked (Liam, 2026-07-31) */
        margin-inline: auto;
    }
}

@media (max-width: 767px) {
    .vl-home-testimonial__cutout {
        width: 100%; /* the 75% is tablet-only (Liam, 2026-07-31) */
    }
}

/* ── Sticky-tab zone (CLAUDE.md rule): wait = Our-devs padding-top (82,
   the zone opens AFTER the hero), stop = Latest-updates padding-bottom
   (100). Home is the FRONT PAGE but still a page template, so the doubled
   `page-template-template-home-php` class applies (about.css/contact.css
   precedent — WP core's body_class() derives page-template- +
   str_replace('.', '-', template slug)). ── */
body.page-template-template-home-php .vl-sticky-zone {
    --vl-sticky-wait: 82px;
    --vl-sticky-stop: 100px;
}

/* ── ≤767: section paddings 60/60, dot/heading rhythm scaled down ── */
@media (max-width: 767px) {
    .vl-home-devs {
        padding-block: 60px;
    }

    .vl-home-devs__heading {
        margin-bottom: 40px;
    }

    .vl-home-testimonial__cols {
        grid-template-columns: minmax(0, 1fr);
        gap: 50px;
        padding-block: 60px;
    }

    .vl-home-testimonial__heading {
        margin-bottom: 30px;
    }

    .vl-carousel-nav {
        margin-top: 30px;
    }

    .vl-home-updates {
        padding-block: 60px;
    }

    .vl-home-updates__heading {
        margin-bottom: 40px;
    }

    body.page-template-template-home-php .vl-sticky-zone {
        --vl-sticky-wait: 60px;
        --vl-sticky-stop: 60px;
    }
}

/* ── No-peek fallback (Liam sitewide ruling, 2026-07-31): a snap carousel
   only earns its keep when the card count EXCEEDS the band's per-view
   count — otherwise there is nothing to peek and the 44%/88% slides leave
   a dead band at the right. Quantity queries flip the scroller back to a
   filled grid: ≤2 cards in the 2-per-view band (900–1280), a lone card in
   the 1-per-view band (<900). Full-bleed margins/padding zero with it.
   Same rule applied to every snap surface the same day (card-development /
   plot-morehomes / update-single / home). ── */
@media (max-width: 1280px) and (min-width: 900px) {
    .vl-home-updates__grid:has(> .vl-update-card:first-child:nth-last-child(2)),
    .vl-home-updates__grid:has(> .vl-update-card:only-child) {
        display: grid;
        margin-inline: 0;
        padding-inline: 0;
    }

    .vl-home-updates__grid:has(> .vl-update-card:first-child:nth-last-child(2)) {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .vl-home-updates__grid:has(> .vl-update-card:only-child) {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 899px) {
    .vl-home-updates__grid:has(> .vl-update-card:only-child) {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        margin-inline: 0;
        padding-inline: 0;
    }
}
