/* Update single — frame 39:2175. Reads in the plan's pixel digest
   (docs/superpowers/plans/2026-07-30-updates-pages.md). Divergences from
   the frame, both deliberate: back bar is the existing 48px component
   (frame reads 50 — Liam-ruled chrome, 2026-07-28) and big-heading
   line-height is 1 per the sitewide rule (node leading 75/74). */

/* ── Back bar rides with the fixed header (Liam live review 2026-07-30):
   fixed under the header, follows its hide-on-scroll-down / return-on-
   scroll-up, and takes over the chrome drop-shadow as the BOTTOM-most
   fixed chrome (the dev/plot stuck-bar rule). Everything is scoped to
   body.single-vl_updates: the plot page's back bar lives under a STATIC
   header (body.vl-header--static, nav.js returns early there) and keeps
   the shared component's in-flow behaviour untouched.
   Mechanism: #vl-header and #vl-main are body-level siblings, so the bar
   tracks nav.js's .vl-nav--hidden / .is-scrolled classes with pure CSS.
   On hide the bar travels its own height PLUS the header's — it moves
   faster than the header and tucks up BEHIND it (bar z-index 45 < header
   50), so the pair read as one unit both ways. ── */
body.single-vl_updates {
    /* The fixed bar leaves the flow — push the page down by its height,
       on top of the --vl-header-h padding header.css already applies. */
    padding-top: calc(var(--vl-header-h) + 48px);
}

body.single-vl_updates .vl-back-bar {
    position: fixed;
    top: var(--vl-header-h);
    left: 0;
    right: 0;
    z-index: 45; /* under the header (50), over the sticky Contact tab (40) */
    transition: transform 0.3s ease, box-shadow 0.25s ease; /* header parity */
}

body.single-vl_updates .vl-nav.vl-nav--hidden ~ .vl-main .vl-back-bar {
    transform: translateY(calc(-100% - var(--vl-header-h)));
}

/* Shadow handoff: the header's .is-scrolled shadow is suppressed on this
   template and the bar carries it instead, gated on the same class (no
   shadow at rest — nothing beneath to cast onto, per the 2026-07-29
   header ruling). */
body.single-vl_updates .vl-nav.is-scrolled {
    box-shadow: none;
}

body.single-vl_updates .vl-nav.is-scrolled ~ .vl-main .vl-back-bar {
    box-shadow: var(--vl-shadow-chrome);
}

/* ── Article section ── */
.vl-update {
    padding-block: 50px 100px; /* bar→hero 50 (node 182−132); share→band 100 */
}

.vl-update__hero {
    max-width: 1000px;
    margin: 0 auto 58px; /* hero→title (node 907−848.67) */
}

.vl-update__hero-img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2; /* node 1000×666.67 */
    object-fit: cover;
    border-radius: 20px;
}

.vl-article {
    max-width: 1000px; /* node: article column 1000 centred in the 1600 container */
    margin: 0 auto;
}

.vl-article__title {
    margin: 0 0 30px; /* title→body (node 1087−1057) */
    color: var(--vl-black);
    font-size: var(--vl-text-display);
    font-weight: var(--vl-weight-regular);
    line-height: 1;
    letter-spacing: -0.025em; /* node −1.75px at 70px */
    overflow-wrap: break-word;
}

.vl-article__title strong {
    font-weight: var(--vl-weight-semibold);
}

/* ── Editor output (.vl-article__body) — the client writes freely; these
   rules ARE the design (spec: prose in the editor, styled by the theme) ── */
.vl-article__body {
    color: var(--vl-black);
    font-size: var(--vl-text-body);
    line-height: 22px;
}

.vl-article__body p {
    margin: 0 0 22px; /* one line's worth — node paragraph rhythm */
}

.vl-article__body h2,
.vl-article__body h3 {
    /* Node "What's Changing?" 30/34 SemiBold −0.75px. Both levels map to the
       same size: one sub-heading scale exists in the frame. Margins 40/25
       (node top gaps 39/45 normalised; sibling collapse rebuilds the frame's
       image-adjacent gaps). */
    margin: 40px 0 25px;
    font-size: var(--vl-text-input);
    font-weight: var(--vl-weight-semibold);
    line-height: 1.1333;
    letter-spacing: -0.025em;
}

.vl-article__body a {
    color: inherit;
    font-weight: var(--vl-weight-semibold); /* node: links SemiBold + underline */
    text-decoration: underline;
    text-underline-offset: 3px;
}

.vl-article__body a:hover {
    text-decoration-color: var(--vl-brick);
    text-decoration-thickness: 2px;
}

.vl-article__body strong {
    font-weight: var(--vl-weight-semibold);
}

.vl-article__body ul,
.vl-article__body ol {
    margin: 0 0 22px;
    padding-left: 27px; /* journey-body parity (list indent convention) */
}

.vl-article__body li {
    margin-bottom: 10px;
}

.vl-article__body img,
.vl-article__body figure {
    display: block;
    max-width: 100%;
    height: auto;
    margin-block: 70px 60px; /* node: body→img 69, img collapses to 64 with h-margins */
    border-radius: 20px;
}

.vl-article__body figure img {
    margin-block: 0; /* the figure carries the rhythm */
}

/* ── Share row ── */
.vl-article__share {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 30px;
    margin-top: 80px; /* node 79 */
}

.vl-article__share-label {
    font-size: var(--vl-text-body);
    line-height: 22px;
}

.vl-article__share-icons {
    display: flex;
    align-items: center;
    gap: 50px; /* node pitch 100 at 50px icons */
    margin: 0;
    padding: 0;
    list-style: none;
}

.vl-article__share-icons li {
    position: relative;
    display: flex;
}

.vl-article__share-icons img {
    display: block;
    width: 50px;
    height: 50px;
    transition: transform 0.25s ease;
}

.vl-article__share-icons a:hover img,
.vl-article__copylink:hover img {
    transform: translateY(-4px); /* sitewide card-lift cue, icon-scaled */
}

.vl-article__copylink {
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.vl-article__share-icons a:focus-visible,
.vl-article__copylink:focus-visible {
    outline: 2px solid var(--vl-ink);
    outline-offset: 3px;
    border-radius: 50%;
}

/* "Copied" feedback — set by js/update-single.js, announced via the
   aria-live region. */
.vl-article__copied {
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    font-size: var(--vl-text-sm);
    line-height: 22px;
    color: var(--vl-ink);
    white-space: nowrap;
}

/* ── Recent updates band ── */
.vl-recent {
    background: #f5f0e8; /* node "Rectangle 54" — the plot gallery band's exact fill (gallery-carousel.css:108) */
    padding-block: 86px 100px; /* node reads */
}

.vl-recent__heading {
    margin: 0 0 58px; /* node 3423−3365 — ⚠️ diverges from the sitewide 70px
                         title→grid rhythm; built to the node, flagged for
                         Liam's live review */
    color: var(--vl-black);
    font-size: var(--vl-text-display);
    font-weight: var(--vl-weight-regular);
    line-height: 1;
    text-align: center;
}

.vl-recent__heading strong {
    font-weight: var(--vl-weight-semibold);
}

.vl-recent__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 30px; /* node column gap 29.67 */
}

/* ── Small desktop / tablet / mobile: cards in an x-axis snap carousel —
   ported VERBATIM from card-development.css's .vl-dev-related__grid block
   (Liam's related-developments pattern 2026-07-24, run through small
   desktop 2026-07-28; extended to Recent updates at the Updates live
   review 2026-07-30 — replaces this file's earlier 1-col stack). 3-col
   grid only ≥1281. Cards snap centred so neighbours peek in from the
   viewport edges; the scroller is full-bleed and scroll-padding re-aligns
   the snapped card with the content gutters. Same sticky-zone caveat as
   plot-morehomes.css: the full-bleed margin trick needs NO ancestor
   overflow-x:hidden — already guaranteed sitewide by the sticky-zone rule
   in CLAUDE.md. Targets the GRID only; card-update.css stays untouched. ── */
@media (max-width: 1280px) {
    .vl-recent__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-recent__grid::-webkit-scrollbar {
        display: none; /* Chrome/Safari/Edge */
    }

    .vl-recent__grid .vl-update-card {
        flex: 0 0 88%; /* < 100% of the content width = the neighbour peek */
        scroll-snap-align: center;
    }
}

/* Two-up band, same as the related-developments 900–1280 rule:
   2 × 44% + 30px gap ≈ 91% of the content width, next card's edge peeks. */
@media (max-width: 1280px) and (min-width: 900px) {
    .vl-recent__grid .vl-update-card {
        flex-basis: 44%;
    }
}

/* ── Sticky Contact tab zone (CLAUDE.md rule): wait = first section's
   padding-top (50), stop = last section's padding-bottom (100; the Recent
   band — or the article section itself when Recent hides: both read 100).
   Singles get the plain body class (no doubled-template quirk — that is
   page templates only). ── */
body.single-vl_updates .vl-sticky-zone {
    /* 148 = header 82 + bar 48 + the same 18px clearance the default 100px
       gives under the bare 82px header (fixed chrome got 48px deeper when
       the back bar joined it, 2026-07-30). */
    --vl-sticky-top: 148px;
    --vl-sticky-wait: 50px;
    --vl-sticky-stop: 100px;
}

/* ── Tablet + mobile ── */
/* (The launch build's ≤1024 single-column stack for .vl-recent__grid is
   gone — superseded by the snap carousel above, Liam 2026-07-30.) */

@media (max-width: 767px) {
    .vl-update {
        padding-block: 50px 60px; /* 60 = mobile band standard; top stays the
                                     50px chrome gap under the back bar */
    }

    .vl-recent {
        padding-block: 60px; /* sitewide mobile section-padding convention (CLAUDE.md) */
    }

    .vl-update__hero {
        margin-bottom: 35px; /* --vl-gap-title-body scale: hero→title reads
                                as the display gap; 58 desktop ~ 35 mobile */
    }

    .vl-article__share {
        flex-direction: column;
        align-items: flex-start;
        gap: 25px;
    }

    .vl-article__share-icons {
        gap: 30px;
    }

    body.single-vl_updates {
        padding-top: calc(var(--vl-header-h) + 40px); /* bar is 40px ≤767 (back-bar.css) */
    }

    body.single-vl_updates .vl-sticky-zone {
        --vl-sticky-top: 140px; /* 82 + 40 + 18 */
        --vl-sticky-wait: 50px;
        --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-recent__grid:has(> .vl-update-card:first-child:nth-last-child(2)),
    .vl-recent__grid:has(> .vl-update-card:only-child) {
        display: grid;
        margin-inline: 0;
        padding-inline: 0;
    }

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

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

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