/* "Your Journey Home" timeline — Figma 39:2917 (heading), 39:2983 / 40:3046 /
   40:3047 / 40:3058 / 40:3071 / 40:3094 (the six steps), 39:2919 (line),
   40:3015 (arrowhead). Spec §10.

   Geometry resolves cleanly against the 1600px container:
     702 (card) + 196 (gutter) + 702 (card) = 1600
     line centred in the gutter
     pointer protrudes 71px from the card's inner edge → its tip stops 27px
       short of the line, symmetrically on both sides.
   The 27px matches the LEFT card's own node read. The frame itself is
   asymmetric (27px left, 77px right) with its step groups at x160 and x1033
   and the gutter centred on 985 rather than 960 — loose. Symmetry is
   deliberate.

   Card reads (39:2925 / 39:2993 and siblings):
     width 702, radius 20, border 2px ink, padding 32
     numeral circle 50, circle→title gap 20, title 30px/34 SemiBold
     title→body 33, body 18/22, bullet indent 27 with 10px between items
     pointer VERTICALLY CENTRED on the card — verified on all six:
       2482/2482, 2703/2703, 2936/2936, 3094/3093.5, 3272/3272, 3414/3413.5

   Alternating fills are in the frame — and are the NO-JS fallback only.
   Liam (2026-07-30): the cornflower card is the FOCUSED state of one card
   design, scroll-driven via js/journey.js — see the .vl-journey--live rules:
     odd  (left)  cornflower card, ink circle, cream numeral
     even (right) CREAM card — on the cream page ground, so outline-only —
                  cornflower circle, ink numeral

   The step "icons" are NUMERALS (path id="1"…"6" inside a 50px circle), so
   they are rendered as live text here. No icon assets are committed. */

.vl-journey {
    padding-block: 100px;
}

/* Intro→first card gap runs BIGGER here than the shared secintro rhythm
   (70px sitewide): 100px desktop+tablet, 70px mobile (≤767 block below) —
   Liam live review 2026-07-30, this section only; the zig-zag needs more
   air under the centred lead. Caller-scoped override of section-intro.css,
   same pattern as about.css's dot-focus override. */
.vl-journey .vl-secintro {
    margin-bottom: 100px;
}

.vl-journey__steps {
    display: grid;
    /* Gutter is a FIXED 196px; the CARDS are the fluid part
       (minmax(0, 1fr) each) — not a 702fr/196fr/702fr proportional split.
       The gutter carries the spine and both pointers' 71px clearance each
       side (27px gap to the line by design). If the gutter scaled with the
       viewport those fixed-71px triangles would start crossing: content
       width = viewport − 80px edge padding, and a proportional 196fr gutter
       would fall to 71+71 = 142px of clearance at content ≈1160px, i.e. a
       viewport of ~1240px — squarely inside the desktop tier (≥1025), not
       the ≤1024 tier where pointers are hidden. Fixing the gutter at 196px
       keeps the spine and both pointer clearances pixel-exact at every
       desktop width; cards absorb all the shrinkage instead. At the full
       1600px container this still yields (1600 − 196) / 2 = 702px per card,
       identical to the design. Do not change this back to fr. */
    grid-template-columns: minmax(0, 1fr) 196px minmax(0, 1fr);
    /* row-gap arithmetic (cold review Fix 1): each step spans two auto rows
       via `grid-row: var(--vl-step) / span 2` below (interleaved). With NO
       row-gap, the visible gap between same-column card k and card k+2 is
         [max(H(k-1),H(k)) + max(H(k),H(k+1)) - 2*H(k)] / 2
       which is exactly ZERO whenever card k is at least as tall as both its
       neighbours — the realistic case with six similar-height cards, where
       every same-column gap collapses to 0 and all six fuse into two solid
       columns with doubled border seams.
       Adding a row-gap of G, with roughly equal card heights H:
         - the gap between same-column cards becomes exactly G
         - the overlap between opposite-column neighbours (the zig-zag weave)
           becomes (H - G) / 2
       So G is the single knob trading separation against weave. 100px
       matches the site's 100px section rhythm and sits inside the design
       frame's own same-column gaps (136px and 96px between successive
       left-hand cards — irregular because they're content-driven, not a
       fixed value to reproduce exactly). Exact value flagged for the
       project owner's live review.
       Desktop-only: the @media (max-width: 1024px) block below sets its own
       `gap: 50px` (both axes, single column) — this row-gap must not leak
       past that breakpoint. */
    row-gap: 100px;
    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The spine. Sits behind the cards: it is the first child in paint order and
   every card fill is fully opaque, so the line disappears cleanly behind
   them and shows only in the gaps. */
.vl-journey__steps::before {
    content: "";
    position: absolute;
    top: 0;
    /* Liam live review 2026-07-30 (matches the frame): the spine runs 70px
       past the bottom card, then 10px more THROUGH the terminating chevron's
       opening so the shaft's end meets the vertex tip (.vl-journey__end sits
       70px below the grid, is 10px tall, and its V has no centre ink until
       the vertex — stopping at the chevron's top edge left a visible gap).
       All viewports. */
    bottom: -80px;
    left: 50%;
    width: 2px;
    margin-left: -1px;
    background: var(--vl-ink);
    z-index: 0;
}

/* Interleave: each card spans two auto-sized rows and successive cards start
   one row later, giving a consistent content-driven overlap with no magic
   numbers. In the frame the overlaps are irregular (208/308/139/213/129px
   between card tops) because they are purely content-driven, so an exact
   reproduction is neither possible nor meaningful.
   FALLBACK if row-height distribution misbehaves: delete this rule and the
   grid-row below, giving one row per card (taller, no interleave).
   Flagged for live review — spec §17.1. */
.vl-journey__step {
    grid-row: var(--vl-step) / span 2;
    position: relative;
    z-index: 1;
}

.vl-journey__step--odd {
    grid-column: 1;
    --vl-journey-fill: var(--vl-cornflower);
    --vl-journey-num-bg: var(--vl-ink);
    --vl-journey-num-fg: var(--vl-cream);
}

.vl-journey__step--even {
    grid-column: 3;
    --vl-journey-fill: var(--vl-cream);
    --vl-journey-num-bg: var(--vl-cornflower);
    --vl-journey-num-fg: var(--vl-ink);
}

.vl-journey__card {
    position: relative;
    padding: 32px;
    border: 2px solid var(--vl-ink);
    border-radius: 20px;
    background: var(--vl-journey-fill);
}

.vl-journey__head {
    display: flex;
    align-items: center; /* node: circle centre 2557 = title centre 2557 */
    gap: 20px;
    margin-bottom: 33px;
}

.vl-journey__num {
    flex: 0 0 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background: var(--vl-journey-num-bg);
    color: var(--vl-journey-num-fg);
    font-size: 30px;
    font-weight: var(--vl-weight-semibold);
    line-height: 1;
}

.vl-journey__title {
    margin: 0;
    min-width: 0;
    color: var(--vl-black);
    /* node 39:2994 reads 30px, but that's --vl-text-input — same token every
       other 30px card title uses (card-development.css, dev-plots.css,
       dev-titleband.css, footer.css, plot-titleband.css) — because it steps
       to 20px on mobile (variables.css:92). Line-height must therefore be
       unitless (the node's 34/30 ratio), not a fixed 34px, so it tracks the
       step instead of rendering 1.7 at 20px. */
    font-size: var(--vl-text-input);
    font-weight: var(--vl-weight-semibold);
    line-height: 1.1333;
}

.vl-journey__body {
    color: var(--vl-black);
    font-size: var(--vl-text-body);
    line-height: 22px;
}

.vl-journey__body ul,
.vl-journey__body ol {
    margin: 0;
    padding-left: 27px; /* node: bullet marker indent */
}

/* Marker type kept per-list-type: ACF's basic wysiwyg toolbar offers both a
   bulleted and a numbered list, and combining these into one list-style
   rule would draw bullets on a numbered list. */
.vl-journey__body ul {
    list-style: disc;
}

.vl-journey__body ol {
    list-style: decimal;
}

.vl-journey__body li {
    margin-bottom: 10px; /* node */
}

.vl-journey__body li:last-child,
.vl-journey__body p:last-child {
    margin-bottom: 0;
}

.vl-journey__body p {
    margin: 0 0 22px;
}

/* Pointer. The outer span is the ROTATED footprint (79.5 × 89.96); the inner
   svg is the unrotated artwork (89.96 × 79.5) centred inside it and turned
   90°, so the rotation lands exactly on the footprint.
   8.5px of overlap into the card hides the base seam behind the card fill. */
.vl-journey__pointer {
    position: absolute;
    top: 50%;
    width: 79.5px;
    height: 89.96px;
    transform: translateY(-50%);
}

.vl-journey__pointer svg {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 89.9602px;
    height: 79.5px;
    overflow: visible;
}

.vl-journey__step--odd .vl-journey__pointer {
    left: 100%;
    margin-left: -8.5px;
}

.vl-journey__step--odd .vl-journey__pointer svg {
    transform: translate(-50%, -50%) rotate(90deg);
}

.vl-journey__step--even .vl-journey__pointer {
    right: 100%;
    margin-right: -8.5px;
}

.vl-journey__step--even .vl-journey__pointer svg {
    transform: translate(-50%, -50%) rotate(-90deg);
}

.vl-journey__pointer-fill {
    fill: var(--vl-journey-fill);
}

.vl-journey__pointer-line {
    fill: none;
    stroke: var(--vl-ink);
    stroke-width: 2;
}

/* Terminating arrowhead on the spine (node 40:3015, 18×10). */
.vl-journey__end {
    display: block;
    width: 18px;
    height: 10px;
    /* 70px below the bottom card at every viewport (Liam, 2026-07-30 — per
       the frame); the spine's -80px extension above reaches the vertex. */
    margin: 70px auto 0;
}

.vl-journey__end svg {
    display: block;
    width: 100%;
    height: 100%;
    /* The path (M0 0 L9 10 L18 0, stroke-width 2) puts 1px of stroke outside
       the 0 0 18 10 viewBox on every edge — without this the tip renders
       flattened and both top ends are cut square. Matches .vl-journey__pointer
       svg above, which already sets this for the same reason (cold review
       Fix 4). */
    overflow: visible;
}

.vl-journey__end path {
    fill: none;
    stroke: var(--vl-ink);
    stroke-width: 2;
}

/* ── Scroll-driven focus (spec 2026-07-30-journey-scroll-focus-design.md).
   WITHOUT JS the parity fills above stand — the static alternating look is
   the no-JS fallback. js/journey.js adds .vl-journey--live on init, which
   re-bases every card to the REST state (cream / cornflower circle / ink
   numeral — the static even-card look) regardless of column, and .is-focused
   (exactly one card, nearest viewport centre) gets the cornflower FOCUS
   state plus the sitewide card lift (recipe copied from
   card-development.css:121-124 / dev-plots.css:332-335). Colours, rise and
   shadow share one 0.25s ease curve — the plot card's "one object lifting"
   rule extended to the crossfade. Pointers inherit --vl-journey-fill, so
   they crossfade with their card automatically. ── */

.vl-journey--live .vl-journey__step {
    --vl-journey-fill: var(--vl-cream);
    --vl-journey-num-bg: var(--vl-cornflower);
    --vl-journey-num-fg: var(--vl-ink);
}

.vl-journey--live .vl-journey__step.is-focused {
    --vl-journey-fill: var(--vl-cornflower);
    --vl-journey-num-bg: var(--vl-ink);
    --vl-journey-num-fg: var(--vl-cream);
    /* Cards overlap vertically in the desktop zig-zag weave (row-gap
       interleave above) — the lifted card must paint over both neighbours
       (steps carry z-index: 1 at rest). */
    z-index: 2;
}

.vl-journey--live .vl-journey__card {
    transition: background-color 0.25s ease, transform 0.25s ease,
                box-shadow 0.25s ease;
}

.vl-journey--live .vl-journey__step.is-focused .vl-journey__card {
    transform: translateY(-4px);
    box-shadow: 0 12px 36px rgba(50, 56, 59, 0.35);
}

.vl-journey--live .vl-journey__num {
    transition: background-color 0.25s ease, color 0.25s ease;
}

.vl-journey--live .vl-journey__pointer-fill {
    transition: fill 0.25s ease;
}

/* Reduced motion (spec): focus still MOVES — it is state, not decoration —
   but nothing animates and the card does not rise. */
@media (prefers-reduced-motion: reduce) {
    .vl-journey--live .vl-journey__card,
    .vl-journey--live .vl-journey__num,
    .vl-journey--live .vl-journey__pointer-fill {
        transition: none;
    }

    .vl-journey--live .vl-journey__step.is-focused .vl-journey__card {
        transform: none;
        box-shadow: none;
    }
}

/* ── Tablet + mobile (Liam's ruling, 2026-07-29): full-width cards in one
   column, the spine STAYS CENTRED and runs behind them, showing through the
   gaps. Pointers hidden — they would aim at nothing. Numerals retained;
   fills are scroll-state-driven (or the static alternating no-JS fallback).
   MUST stay below the base rules. ── */
@media (max-width: 1024px) {
    /* Tablet keeps the frame's 100px band padding — sitewide standard (Liam,
       2026-07-30): desktop AND tablet use the frame value, mobile is 60px. */
    .vl-journey__steps {
        grid-template-columns: minmax(0, 1fr);
        gap: 50px;
    }

    .vl-journey__step {
        grid-row: auto;
        grid-column: 1;
    }

    .vl-journey__pointer {
        display: none;
    }
}

@media (max-width: 767px) {
    .vl-journey {
        padding-block: 60px; /* sitewide mobile section-padding convention (CLAUDE.md) */
    }

    .vl-journey .vl-secintro {
        margin-bottom: 70px; /* 100px is desktop+tablet only (Liam, 2026-07-30) */
    }
}

/* ── Sticky Contact tab zone (CLAUDE.md rule): the tab appears where content
   begins and retires where it ends. --vl-sticky-wait = the FIRST section's
   padding-top (the intro feature row, top of page — feature-row.css's own
   100 desktop+tablet / 60 mobile), --vl-sticky-stop = the LAST section's
   padding-bottom, which on Buying is THIS file's own .vl-journey
   padding-block (100px, 60px at ≤767 — sitewide standard above), not a
   feature row. Restated here as the zone's props; this block owns no layout
   of its own — the sticky zone is a page-level concern, not the journey
   part's business, so Buying gets its own rule rather than sharing one with
   About (CLAUDE.md).

   Selector form verified against dev-listing.css/contact.css's own sticky-
   zone blocks (search "-php` suffix below is REQUIRED"): WP core's
   body_class() derives `page-template-` + sanitize_html_class(str_replace(
   '.', '-', get_page_template_slug())) — for template-buying.php that is the
   doubled `page-template-template-buying-php`, not `page-template-buying-php`. */
body.page-template-template-buying-php .vl-sticky-zone {
    --vl-sticky-wait: 100px;
    --vl-sticky-stop: 100px;
}

@media (max-width: 767px) {
    body.page-template-template-buying-php .vl-sticky-zone {
        --vl-sticky-wait: 60px;
        --vl-sticky-stop: 60px;
    }
}
