/* Investment Path – the guided planner's path
   The light that draws the question. Two layers: one behind the card carrying
   the world of branches, one in front carrying the card's glowing outline and
   the lights themselves.

   Loaded last. It introduces its own namespace and overrides nothing, so it
   does not disturb the ordering the other ten files depend on. The animated
   selectors here are named in 10-responsive.css's reduced-motion allowlist
   rather than in a second block at the bottom of this file: that allowlist is
   documented as the one place, and a second one is how it goes stale.
   ========================================================================== */

/* These two belong to other components, and are here rather than there
   because they exist only to support the path: the shell has to be a
   positioning context for the layers to fill, and the layout has to sit
   between them. Neither means anything without this file. */
.guide-shell {
    position: relative;
}

/* A shrinking question reduces the shell while the path layers deliberately
   keep their old viewport until touchdown. Clip that temporary overhang so it
   cannot hold the document's scroll range open and release it in one jump. */
.guide-shell.is-path-stage-locked {
    overflow: clip;
}

/* The path and recap animate the effects of a fieldset-height change
   themselves. Browser scroll anchoring would apply a second correction to the
   viewport and make every otherwise-stationary preview appear to jump. */
.has-guide-path .guide-shell {
    overflow-anchor: none;
}

.guide-layout {
    position: relative;
    z-index: 1;
}

/* The one thing the path cannot leave alone.
   `.guide-layout` is two columns – the card, then the "your plan so far"
   recap – and the recap occupies precisely the space to the right of the card
   that the branches need. Drawn there they are behind an opaque panel, which
   is not a fan, it is a rumour of one.
   So with the path on, the layout is one column: the card keeps a readable
   measure on the left and the recap moves underneath it, which is where it
   already goes at every narrow breakpoint. The right of the shell becomes the
   world. This is the only piece of existing layout the feature moves, and it
   is scoped to the class so the planner without it is untouched. */
/* 69% is the width the card already had – the 1.4fr of the two-column grid it
   is replacing. Narrower was tried and is a regression: `.guide-choice-custom`
   puts a label, a slider and a number entry on one row, and below about 600px
   they wrap over each other. The card keeps its measure; the space the recap
   used to occupy becomes the world.

   Capped in px as well as in per cent, because the stage below is allowed to
   grow: a question 1000px wide is a worse question, and the width past the
   cap is worth more to the path than to the paragraph. */
.has-guide-path .guide-layout {
    grid-template-columns: minmax(0, min(69%, 780px));
}

/* The stage takes the width it can get, and the card stands in the middle of
   it.
   Where the card was flush against the left edge there was no world on that
   side at all: travelling forward the reader watched the next question
   approach in the lane to the right, and travelling back the question being
   returned to was off the edge of the screen until the camera had panned most
   of the way to it. It was not a timing problem and no retiming could fix it
   – the light cannot leave the card's left edge until it has walked round the
   frame to get there, so the camera cannot start early either. The path has to
   run THROUGH the card rather than start at it, and that needs a lane on each
   side.
   The width is only worth taking where there is enough of it to be worth
   splitting: two lanes of a hundred-odd pixels are worse than one good one,
   because the boxes in them are the arriving question itself and it has to
   stay readable. Under the breakpoint the card is flush left again and the
   world is one-sided, which is what `guide-path.js` measures rather than
   assumes. */
.has-guide-path .guide-shell {
    width: min(100%, 1460px);
}

/* The extra width is for the world, not for the writing.
   Everything above the card reads as a page – a heading, a step strip, a
   status line – and a page that runs to the window's edges has lost its
   margin, while a step strip stretched over 1400px is nine labels adrift in a
   row of white space. So the stage grows and the things standing on it keep
   the measure they had, centred over the card they belong to. */
.has-guide-path .guide-header,
.has-guide-path .guide-progress-tabs,
.has-guide-path .guide-carry-over {
    width: min(100%, 1120px);
    margin-inline: auto;
}

@media (min-width: 1240px) {
    .has-guide-path .guide-layout {
        justify-content: center;
    }
}

/* The base responsive sheet turns the planner into a single-column layout at
   980px. Keep that decision when the animated path is enabled: otherwise this
   later, more-specific rule restores the 69% track between 621px and 980px,
   squeezing wide form rows past the card edge on tablets. */
@media (max-width: 980px) {
    .has-guide-path .guide-layout {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The path is not mounted at the mobile breakpoint. The base responsive
   bundle owns that decision so layout, rendering and the JavaScript media
   query all use the same 720px boundary. */

/* The guide keeps the landing page's two corner glows, which live in their
   own `.landing-glow` layer, but not its trend glyph. Static or moving, that
   line competes with the planner's own branching path. Results keep the same
   quiet backdrop so changing planner state cannot make the wallpaper appear. */
body[data-experience$="guide"] > .landing-path,
body[data-experience$="result"] > .landing-path {
    display: none;
}

.guide-path {
    position: absolute;
    inset: 0;
    overflow: hidden;
    /* Never in the way. The card and its controls are the only things on this
       part of the page that should answer a pointer. */
    pointer-events: none;
}

/* The projection hides the card entirely, and an outline drawn around a card
   that is not there is a rectangle floating in space. */
body[data-experience$="result"] .guide-path {
    display: none;
}

/* Behind the card: the backdrop and every branch. In front: the outline drawn
   around the card and the lights drawing it. The card itself sits between the
   two, which is the only way its own glass still reads while an outline is
   being drawn on top of it. */
.guide-path-back { z-index: 0; }
.guide-path-front { z-index: 2; }

.gp-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* ---------------------------------------------------------------- outline */

/* Both halves are pathLength="1", so the dash offset is the fraction still to
   be drawn and nothing has to measure anything per frame. */
.gp-outline-core,
.gp-outline-halo {
    stroke-dasharray: 1;
    stroke-dashoffset: var(--gp-dash, 0);
    stroke-linecap: round;
}

.gp-outline-core {
    stroke: var(--accent-2);
    stroke-width: 2;
}

.gp-outline-halo {
    stroke: var(--accent);
    stroke-width: 12;
    opacity: 0.22;
    filter: blur(7px);
}

/* Back uses the previous station's own frame while it grows into the card.
   The fixed screen-space outline would already be full size, and can have the
   height of the question being left until the destination is measured. */
.guide-path-front.is-backward .gp-outline {
    opacity: 0;
}

/* ----------------------------------------------------------------- world */

.gp-fan {
    opacity: 0;
    transition: opacity var(--motion-slow) var(--ease-out);
}

/* Fully open at rest. An arriving fan has its own progress below. */
.gp-fan.is-open { opacity: 1; }

/* Except the one being flown. The route the light is on stays lit for the
   whole flight, and the box at the end of it grows into the card's place –
   without them the world is empty while the camera pans, and a camera panning
   past nothing does not read as movement at all. */
.gp-fan.is-flying { opacity: 1; }

/* Reveal every destination branch while its card grows. The chosen branch's
   preview remains represented by the next station, so one station plus the
   remaining previews equals the real number of choices before handoff. */
.gp-fan.is-arriving {
    opacity: var(--gp-arrival-op, 0);
    transition: none;
}

/* And except the one behind. A fan ahead of the reader is a set of choices;
   the same fan behind them is a record, and only one branch of it happened.
   Drawn dimmer than the road ahead, because it is not an invitation. */
.gp-fan.is-behind { opacity: 0.55; }

.gp-fan.is-flying .gp-branch:not(.is-chosen),
.gp-fan.is-behind .gp-branch:not(.is-chosen) { opacity: 0; }

.guide-path-back.is-armed .gp-fan.is-open { opacity: 0.28; }

.gp-branch-core {
    stroke: var(--accent-2);
    stroke-width: 1.6;
    stroke-dasharray: 3 7;
    opacity: 0.34;
    transition: opacity var(--motion-base) var(--ease-out), stroke-width var(--motion-base) var(--ease-out);
}

.gp-branch-halo {
    stroke: var(--accent);
    stroke-width: 9;
    opacity: 0;
    filter: blur(6px);
    transition: opacity var(--motion-base) var(--ease-out);
}

/* The route committed to is a road; the rest are still suggestions. */
.gp-branch.is-chosen .gp-branch-core {
    stroke-width: 2.4;
    stroke-dasharray: none;
    opacity: 1;
}

.gp-branch.is-chosen .gp-branch-halo { opacity: 0.3; }

/* The selected route gets a separate copy of the curve, normalized to a
   one-unit length so it can be drawn from the parked dot to the preview. The
   ordinary branch stays beneath it as a quiet suggestion; fading a complete
   line in would not show which end the decision came from. */
.gp-branch-choice-core,
.gp-branch-choice-halo {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    stroke-linecap: round;
    opacity: 0;
}

.gp-branch-choice-core {
    stroke: var(--accent-2);
    stroke-width: 2.4;
}

.gp-branch-choice-halo {
    stroke: var(--accent);
    stroke-width: 9;
    filter: blur(6px);
}

.gp-fan.is-choice-pending .gp-branch.is-chosen .gp-branch-core {
    stroke-width: 1.6;
    stroke-dasharray: 3 7;
    opacity: 0.34;
}

.gp-fan.is-choice-pending .gp-branch.is-chosen .gp-branch-halo {
    opacity: 0;
}

.gp-fan.is-choice-pending .gp-branch.is-chosen .gp-branch-choice-core,
.gp-fan.is-choice-pending .gp-branch.is-chosen .gp-branch-choice-halo {
    opacity: 1;
    transition: stroke-dashoffset var(--motion-base) ease-out;
}

.gp-fan.is-choice-pending .gp-branch.is-chosen .gp-branch-choice-halo {
    opacity: 0.3;
}

.gp-fan.is-choice-pending.is-choice-ready
    .gp-branch.is-chosen .gp-branch-choice-core,
.gp-fan.is-choice-pending.is-choice-ready
    .gp-branch.is-chosen .gp-branch-choice-halo {
    stroke-dashoffset: 0;
}

/* A step's own box. Faded against the card that stands in the same place, so
   exactly one of the two is ever showing. */
.gp-station-body {
    opacity: var(--gp-station-op, 1);
}

/* The backward destination is highlighted where it actually is: around the
   tiny station preview. Both rectangles sit inside the station's transformed
   group, so their geometry scales with the box during the return journey.
   Non-scaling strokes keep the frame legible while that box is tiny. */
.gp-station-highlight-core,
.gp-station-highlight-halo {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    opacity: 0;
    transition: stroke-dashoffset var(--motion-base) var(--ease-out), opacity var(--motion-base) var(--ease-out);
}

.gp-station-highlight-core {
    stroke: var(--accent-2);
    stroke-width: 2;
}

.gp-station-highlight-halo {
    stroke: var(--accent);
    stroke-width: 12;
    filter: blur(7px);
}

.gp-station.is-back-target .gp-station-highlight-core,
.gp-station.is-back-target .gp-station-highlight-halo {
    stroke-dashoffset: 0;
}

.gp-station.is-back-target .gp-station-highlight-core {
    opacity: var(--gp-highlight-core-op, 0.42);
}

.gp-station.is-back-target .gp-station-highlight-halo {
    opacity: var(--gp-highlight-halo-op, 0.08);
}

/* Once clicked, strength is written on every journey frame. A CSS opacity
   transition here would lag those values and reach full strength after the
   box had already arrived. */
.guide-path-back.is-travelling .gp-station-highlight-core,
.guide-path-back.is-travelling .gp-station-highlight-halo {
    transition: none;
}

/* The step a chosen branch leads to has a box of its own – the station's. A
   second one drawn by the branch would be the same rectangle twice, and would
   only be right in one direction of travel. */
.gp-branch.is-chosen .gp-preview-box {
    display: none;
}

/* Where a branch leads, seen from far off. Empty on purpose: a road not taken
   has no content, and giving it any is what would give away that every road
   arrives at the same question. */
.gp-preview {
    stroke: var(--border-strong);
    stroke-width: 1;
    opacity: 0.55;
}

.gp-branch.is-chosen .gp-preview {
    stroke: color-mix(in srgb, var(--accent-2) 45%, var(--border-strong));
    opacity: 0.75;
}

.gp-preview-bar {
    fill: var(--surface-hover);
    opacity: 0.75;
}

/* A phone has no room for a stack of destinations beside the card, and buying
   that room from the card makes the question harder to read in order to show
   previews too small to read. The branches stay and run off the edge, which is
   what the path does. */
.guide-path-back.is-narrow .gp-preview,
.guide-path-back.is-narrow .gp-preview-bar,
.guide-path-back.is-narrow .gp-station-highlight-core,
.guide-path-back.is-narrow .gp-station-highlight-halo {
    display: none;
}

/* ---------------------------------------------------------------- lights */

.gp-light-far {
    fill: rgba(34, 211, 238, 0.12);
    filter: blur(9px);
}

.gp-light-near {
    fill: rgba(34, 211, 238, 0.38);
    filter: blur(5px);
}

.gp-light-core {
    fill: #fff;
    filter: drop-shadow(0 0 9px rgba(34, 211, 238, 0.95));
}

/* Merged, the two lights are stacked on one point, so the second is simply
   hidden. That is what lets the split and the merge need no special case in
   the geometry: both always exist, and this only decides whether you see it. */
.gp-light.is-b {
    opacity: 0;
    /* Both centres have already met when this fades. Blending the redundant
       glow away avoids a one-frame brightness snap that reads as a positional
       jump, especially around a tall destination frame. */
    transition: opacity var(--motion-fast) ease-out;
}

.gp-lights.is-split .gp-light.is-b { opacity: 1; }

/* ------------------------------------------------------------- the card */

/* The card appears at the rate its outline is drawn.
   `--gp-card-op` is written per frame by `guide-path.js` from the same number
   that draws the outline: nothing during the flight, arriving with the light.
   Without it the next question is on screen from the first frame of the move,
   because `showStep` swaps the fieldsets synchronously – and then the light is
   tracing a route to somewhere the reader is already standing.

   The WHOLE card, not its children. Fading only the contents leaves the glass
   panel, its border and its shadow at full strength: an empty card sitting
   over the exact patch of screen the flight crosses, with the distant box
   growing invisibly behind it. The card is the overlay in that arrangement.
   Emptying it is not the same as getting it out of the way.

   Fading `.guide-card` itself is safe even though it carries a
   backdrop-filter: what breaks a pane is an ANCESTOR fading, because that
   forms a backdrop root the filter cannot sample past. An element's own
   opacity is harmless – `.path-preview` has always done this. See
   docs/design.md, "Nothing above a pane may fade".

   No transition: this value is already animated, and a transition on top of a
   per-frame write only lags it. */
.guide-card {
    opacity: var(--gp-card-op, 1);
}

/* Invisible and still clickable is worse than either. */
.guide-card.gp-travelling {
    pointer-events: none;
}

/* ----------------------------------------------------- the cards in flight */

/* The world the travelling questions cross. Behind the real card, above the
   branches, and never in the way of a pointer. */
.gp-cards {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    will-change: transform;
}

/* A copy of a step, standing at that step's station: the one just left,
   receding, and the one being travelled to, approaching. Each carries
   `.guide-card` as well, so it is the same glass, the same radius and the same
   padding as the real thing without a second description of any of them –
   which is what lets the approaching one hand its picture over to the card at
   the end of the journey without anything moving.

   Anchored top-left and positioned entirely from JS because it scales: a
   percentage translate would be a percentage of the UNSCALED height and would
   drift as the card shrinks. `--gp-ty` is already the corrected top edge. */
.gp-ghost,
.gp-history-card {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--gp-w, 452px);
    transform-origin: 0 0;
    transform: translate(var(--gp-x, 0), var(--gp-ty, 0))
        scale(var(--gp-scale, 1));
}

/* Behind you: a quarter strength, the figure the prototype used for a card you
   have already answered. The one ahead is at full strength, because it is the
   card – from the far end of the branch all the way onto the reader's screen,
   it is the only copy of that question there is. */
.gp-ghost.is-past,
.gp-history-card {
    border-color: color-mix(in srgb, var(--accent-2) 30%, transparent);
    opacity: 0.24;
}

/* It is a picture of a question, not a question. Nothing in it may look
   answerable, and nothing in it may be reached. */
.gp-ghost *,
.gp-history-card * {
    pointer-events: none;
}

/* A picture does not scroll, so it has nothing to stick to. Left sticky, the
   nav row a phone pins to the bottom of the window would be pinned to the
   bottom of the WINDOW from inside a box that is scaled and parked out in the
   world, which is neither where the card's own nav is nor anywhere near it. */
.gp-ghost .guide-nav,
.gp-history-card .guide-nav {
    position: static;
}
