/* ============================================================================
   UNIÓN , 07 Map, travel, info, care. Lane V owns this file.

   Everything here composes from tokens.css and devices.css. No hex, no font
   name, no easing curve and no duration is declared below: every value is a
   var(). The two exceptions the system already documents are the mask gradient
   stops (#000 / #0000, alpha stencils, used on the map's own edge treatment)
   and nothing else.

   Position independence: no rule here uses a page-level selector or a viewport
   assumption, so any V- component lifts into the site or a deck slide unchanged.

   The one place the brand steps back is .v-care. It is deliberately the only
   block on the page that uses no device, no pattern, no mascot and no condensed
   display face. Do not decorate it.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   Shared furniture for this lane only. Small, plain, and named v-.
   ---------------------------------------------------------------------------- */

/* a glyph drawn as a stroke, at the brand's own rule weight. Icons in this lane
   are drawn strokes, never a filled icon set, so they sit beside the soundwave
   without looking bought. */
.v-g{width:1em;height:1em;flex:none;display:block}
.v-g path,.v-g circle,.v-g line{fill:none;stroke:currentColor;stroke-width:2.4;
  stroke-linecap:round;stroke-linejoin:round}

/* the "to confirm" footnote that sits under anything indicative. It is a micro
   label at reduced weight, never a badge, because it is a caveat not a status. */
.v-note{margin:var(--s4) 0 0;opacity:.7}

/* a plain stack, so a component never relies on a page grid for its rhythm */
.v-stack{display:grid;gap:var(--s5);align-content:start;min-width:0}
.v-stack--tight{gap:var(--s3)}

/* ============================================================================
   V-map , the site plan frame with its key
   The supplied map artwork (UNION_MAP-beckenham.svg) carries the real park
   boundary and its own map greens and blues. It is SUPPLIED ARTWORK, the way a
   photograph is: the brand frames it, it does not recolour it. The frame, the
   pins, the key and the compass are ours.
   ============================================================================ */
.v-map{display:grid;grid-template-columns:minmax(0,1fr) minmax(220px,300px);
  gap:clamp(var(--s5),3vw,var(--s7));align-items:start}
@media (max-width:900px){.v-map{grid-template-columns:1fr}}

/* MEASURED off .g2-outer: a cream field, a 3px black border and 10px of padding
   INSIDE it, so the plan never touches its own frame. */
.v-map__frame{position:relative;display:block;min-width:0;padding:10px;
  background:var(--cream);border:var(--rule-heavy) solid var(--black);
  /* the pins size off THIS box, not the viewport: the lab's vw values were
     measured on a page with no documentation sidebar and come out small here. */
  container-type:inline-size}
.v-map__plan{position:relative;display:block}
.v-map__plan u-svg svg{display:block;width:100%;height:auto}

/* the compass, drawn as a label rather than a decorative rose */
.v-map__n{position:absolute;top:var(--s3);right:var(--s3);z-index:var(--z-raise);
  display:flex;flex-direction:column;align-items:center;gap:3px;
  background:var(--black);color:var(--cream);padding:var(--s2) var(--s3)}
.v-map__n svg{width:10px;height:13px}
.v-map__n svg path{fill:currentColor}
/* the N needs its own line box: at line-height 1 a condensed 900 cap is taller
   than its box and the glyph was cut off at the foot of the compass. */
.v-map__n b{font:900 12px/1.25 var(--font);font-stretch:var(--wdth-condensed);
  letter-spacing:.04em;display:block}

/* a pin on the plan. The soundwave IS the pin (device D4, --pin), so a stage pin
   carries the real glyph; a facility pin carries its drawn stroke on a disc of
   the facility's own colour. */
/* MEASURED off .g2-dot: 32 to 42px square, ALWAYS cream with a 3px black
   border (not the facility's colour), the glyph at 62% of the box. The lab's
   3.2vw was measured on a page with no sidebar, so it is sized off the
   component's own container here rather than the viewport. */
.v-map__pin{position:absolute;left:var(--px);top:var(--py);
  transform:translate(-50%,-50%);z-index:var(--z-body);
  width:clamp(32px,3.2cqw,42px);height:clamp(32px,3.2cqw,42px);
  display:grid;place-items:center;appearance:none;cursor:pointer;padding:0;
  background:var(--cream);color:var(--black);
  border:var(--rule-heavy) solid var(--black);border-radius:var(--radius-slab);
  transition:transform var(--dur-bounce) var(--ease-verb-bounce),
             opacity var(--dur-state) var(--ease-out)}
.v-map__pin .v-g{width:62%;height:62%}
.v-map__pin:hover,.v-map__pin.is-hover{transform:translate(-50%,-50%) scale(1.18)}
.v-map__pin[data-off]{transform:translate(-50%,-50%) scale(.28);opacity:0;
  pointer-events:none}
/* a pin shown OUT of the plan, in a state cell or a key. It is the same object
   with its absolute placement removed, so the documented state is the real one. */
.v-map__pin--static{position:static;transform:none}
.v-map__pin--static.is-hover{transform:scale(1.18)}
.v-map__pin--static[data-off]{transform:scale(.28);opacity:.25}
/* a stage pin is the real glyph standing on the zone, not a disc. It is a
   plain span, not a button: the stage zone is already named on the plan and a
   second control over the same thing is a trap for a keyboard user.
   It carries no height, so it centres on its own box rather than on the 34-44px
   square a facility pin gets; without this it floated above its zone. */
.v-map__pin--stage{background:none;border:0;width:clamp(44px,4.6vw,62px);
  height:auto;display:block;pointer-events:none}

.v-map__key{list-style:none;margin:0;padding:0;display:grid;gap:var(--s2)}
.v-map__key li{display:flex;align-items:center;gap:var(--s3);
  font:700 var(--size-micro)/1.3 var(--font);letter-spacing:var(--track-micro);
  text-transform:uppercase}
.v-map__key i{width:20px;height:20px;flex:none;display:grid;place-items:center;
  background:var(--dc);border:var(--rule) solid var(--black)}
.v-map__key i .v-g{width:62%;height:62%;color:var(--black)}
.v-map__key li:nth-child(1){--dc:var(--stage-main)}
.v-map__key li:nth-child(2){--dc:var(--stage-grove)}
.v-map__key li:nth-child(3){--dc:var(--stage-corner)}
.v-map__key li:nth-child(n+4){--dc:var(--cream)}

/* ============================================================================
   V-facility , G2. Facility filter on the map.
   MEASURED off the starred shot (18.09.58): the filter row is a line of square
   outlined chips on the black slab; the chosen one is pink and FLOODS from the
   left. The results below are full-width rows in the facility's own colour,
   with the walk time hard right at the same weight as the name.
   ============================================================================ */
.v-fac{display:grid;gap:var(--s6)}

.v-fac__bar{display:flex;flex-wrap:wrap;gap:var(--s2);margin:0}
/* the chip is a label in a slab at control scale: square, outlined in the ink of
   its ground, and the pink FLOODS in from the left when chosen. Same verb as the
   .d-slab it is a small cousin of. */
/* MEASURED off the lab's .g2-bar button, relative to the component not the
   viewport: 15px 900 condensed, 3px border, 8/14/9 padding, a 10px gap and a
   19px glyph. My first pass set 13px with the space tokens and came out a step
   small against Louis's shot. */
.v-fac__chip{position:relative;overflow:hidden;appearance:none;cursor:pointer;
  display:inline-flex;align-items:center;gap:10px;
  padding:var(--s2) 14px 9px;min-height:var(--tap-min);
  background:none;color:currentColor;border:var(--rule-heavy) solid currentColor;
  border-radius:var(--radius-slab);
  font:900 15px/1 var(--font);font-stretch:var(--wdth-condensed);
  text-transform:uppercase;letter-spacing:.02em}
.v-fac__chip .v-g{width:19px;height:19px;position:relative;z-index:var(--z-body)}
.v-fac__chip span{position:relative;z-index:var(--z-body)}
.v-fac__chip::before{content:"";position:absolute;inset:0;background:var(--primary);
  z-index:var(--z-river);clip-path:inset(0 100% 0 0);
  transition:clip-path var(--dur-flood) var(--ease-verb-flood)}
.v-fac__chip:hover::before,.v-fac__chip.is-hover::before{clip-path:inset(0 70% 0 0)}
.v-fac__chip[aria-pressed="true"],.v-fac__chip.is-selected{color:var(--black);
  border-color:var(--primary)}
.v-fac__chip[aria-pressed="true"]::before,
.v-fac__chip.is-selected::before{clip-path:inset(0 0 0 0)}
.v-fac__chip.is-focus{outline:var(--focus-width) solid var(--focus-colour);
  outline-offset:var(--focus-offset)}
.v-fac__chip.is-disabled,.v-fac__chip[disabled]{opacity:.45;cursor:not-allowed}

/* MEASURED off .g2-count: the heading is YELLOW on the black slab, capped at
   16ch, with 22px under it. Mine rendered cream, which is a different component
   to look at; Louis's shot is yellow. */
.v-fac__count{margin:0 0 22px;color:var(--stage-corner);max-width:16ch}
.v-fac__sub{margin:0 0 var(--s3);opacity:.8}

/* MEASURED off .g2-near, exactly: a 2px gap between rows (the black ground
   showing through, which is what separates them, NOT a border), 10/12 padding,
   the name at 15px and the time at 16px, the location at 10px / .14em / .6.
   My first pass used a hairline border and equal sizes, which read as a table. */
.v-fac__list{list-style:none;margin:0 0 var(--s4);padding:0;display:grid;gap:2px}
.v-fac__list li{display:flex;align-items:center;gap:var(--s3);
  padding:10px 12px;background:var(--dc,var(--cream));color:var(--black);
  border-radius:var(--radius-slab)}
.v-fac__list .v-g{width:19px;height:19px;flex:none}
.v-fac__list span{display:flex;flex-direction:column;gap:3px;min-width:0}
.v-fac__list b{display:block;font:900 15px/1 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}
.v-fac__list em{display:block;font:700 10px/1 var(--font);font-style:normal;
  letter-spacing:.14em;text-transform:uppercase;opacity:.6}
.v-fac__list strong{margin-left:auto;white-space:nowrap;
  font:900 16px/1 var(--font);font-stretch:var(--wdth-condensed);text-transform:uppercase}

/* ============================================================================
   V-travel , G4. Travel mode switcher with a ribbon wipe.
   MEASURED off the starred shot (18.09.36): five modes across ONE bar with a
   heavy black rule around it, the chosen one solid black; the panel below is one
   box, the mode name at display scale on the left, the facts as micro-label over
   bold value on the right, each fact separated by its own black rule.
   ============================================================================ */
.v-travel{border:var(--rule-heavy) solid var(--black)}

.v-travel__modes{display:flex;flex-wrap:wrap;margin:0}
.v-travel__mode{flex:1 1 0;min-width:120px;appearance:none;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s2);padding:var(--s4) var(--s3);min-height:var(--tap-min);
  background:none;color:var(--black);
  border:0;border-right:var(--rule-heavy) solid var(--black);
  border-bottom:var(--rule-heavy) solid var(--black);
  font:900 12px/1 var(--font);font-stretch:var(--wdth-condensed);
  text-transform:uppercase;letter-spacing:.08em;
  transition:background var(--dur-state) var(--ease-out)}
.v-travel__mode:last-child{border-right:0}
.v-travel__mode .v-g{width:26px;height:26px;
  transition:transform var(--dur-bounce) var(--ease-verb-bounce)}
.v-travel__mode:hover,.v-travel__mode.is-hover{background:var(--yellow)}
.v-travel__mode[aria-pressed="true"],.v-travel__mode.is-selected{
  background:var(--black);color:var(--cream)}
.v-travel__mode[aria-pressed="true"] .v-g,
.v-travel__mode.is-selected .v-g{transform:translateY(-3px) scale(1.14)}
.v-travel__mode.is-focus{outline:var(--focus-width) solid var(--focus-colour);
  outline-offset:calc(var(--focus-offset) * -1)}
.v-travel__mode.is-disabled,.v-travel__mode[disabled]{opacity:.45;cursor:not-allowed}

.v-travel__panel{position:relative;overflow:hidden;background:var(--cream);
  color:var(--black)}
/* the wipe: ONE ribbon stroke sweeping across the panel behind the facts. It is
   a stroke, so it bleeds off the panel's right edge rather than being cut flat,
   and it is held well clear of the fact column, which is why the panel body sits
   in its own z layer with an opaque ground behind the words. */
/* MEASURED against the starred shot: at rest the panel shows NO ribbon behind
   the facts. The stroke lives in a narrow band hard against the panel's right
   edge, outside the fact column's own measure, and it is clipped to that band
   so it can never run under a word. Louis's shot is the reference, not the
   lab's wider wipe. */
/* CORRECTED against the starred shot: at REST his panel shows NO ribbon at all.
   The fragment poking in from the right edge, sliced flat by the panel border,
   is not in his shot; the ribbon exists ONLY during the wipe between modes. So
   the band is hidden by default and only painted while `data-wiping` is set,
   which the switch adds for the length of the sweep and then clears. */
.v-travel__wipe{position:absolute;inset:0 0 0 auto;width:14%;
  pointer-events:none;z-index:var(--z-river);overflow:hidden;
  visibility:hidden;opacity:0}
.v-travel__wipe[data-wiping]{visibility:visible;opacity:1}
.v-travel__wipe u-svg{position:absolute;left:var(--x,46%);top:var(--y,50%);
  width:var(--w,160%);transform:translate(-50%,-50%) rotate(var(--r,-7deg))}
.v-travel__wipe u-svg svg{overflow:visible}
@media (max-width:760px){
  .v-travel__wipe{inset:auto 0 0 0;width:100%;height:34%}
}

/* the body carries the panel's own opaque ground, so even if a stroke were to
   stray it could never sit under a word. It also stops short of the band. */
.v-travel__body{position:relative;z-index:var(--z-body);background:var(--cream);
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);
  gap:clamp(var(--s5),3vw,var(--s7));
  padding:clamp(var(--s5),2.6vw,var(--s6));width:86%}
@media (max-width:760px){.v-travel__body{width:100%}}
@media (max-width:760px){.v-travel__body{grid-template-columns:1fr}}
.v-travel__body h3{margin:0}
.v-travel__body > div > p{margin:var(--s3) 0 0;font-size:var(--size-small);
  line-height:1.55;max-width:40ch}

/* the fact list. This is the FESTIVAL INFO poster's own form: a micro label over
   a bold condensed value, one rule per fact. Used again, unchanged, in V-access
   and V-facts, because the poster only has one way of stating a fact. */
.v-facts{display:grid;gap:0;margin:0;align-content:start}
.v-facts > div{display:grid;grid-template-columns:minmax(96px,auto) 1fr;
  gap:var(--s2) var(--s4);align-items:baseline;
  padding:var(--s3) 0;border-top:var(--rule) solid var(--black)}
.v-facts > div:last-child{border-bottom:var(--rule) solid var(--black)}
.v-facts dt{font:700 var(--size-micro)/1.3 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.7}
.v-facts dd{margin:0;font:900 clamp(15px,1.5vw,19px)/1.1 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}
@media (max-width:500px){.v-facts > div{grid-template-columns:1fr}}

/* the POSTER scale version, used on the accessibility column (A4). Same form,
   bigger value, and the rule is PINK and wipes in from the left as it arrives. */
.v-facts--poster > div{border-top-color:var(--primary);
  grid-template-columns:1fr;gap:var(--s1);padding:var(--s3) 0 var(--s5)}
.v-facts--poster > div:last-child{border-bottom-color:var(--black)}
.v-facts--poster dd{font-size:clamp(24px,3vw,40px);line-height:.95;
  display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s3)}
.v-facts--poster dd small{font:700 var(--size-micro)/1.3 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.7}

/* ============================================================================
   V-stations , G5. Station stubs with a walking line.
   MEASURED off the starred shot (18.09.22): each station is a real .d-stub on a
   yellow ground; the walk is a dotted line with a little figure at its head, and
   the time sits at stub scale, the number huge and the unit small beside it.
   ============================================================================ */
/* MEASURED against the starred shot (18.09.22): the stubs are WIDE and SHORT,
   three across a 1440 stage, the station name on ONE line, everything set
   horizontally. My first render made them tall and narrow at a 300px minimum,
   which clipped every name and stacked the walk time into four lines. The
   minimum is now the width the name actually needs. */
/* MEASURED off the starred shot (18.09.22) as a PROPORTION of its own stage,
   not in the lab's viewport units: each stub is about 300px WIDE by 115px TALL
   per 1000px of stage width, so it is nearly three times as wide as it is high.
   The station name sits on ONE line, the route note is one micro line, then the
   dotted walk, then the time BIG in condensed mixed case with a micro
   "WALK TO THE NORTH GATE" beside it. This DS stage is narrower than the lab's
   full-bleed one, so three at his proportions will not always fit: they wrap to
   two rows rather than being squashed taller and narrower than he drew them. */
.v-stations{display:grid;gap:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
@media (max-width:660px){.v-stations{grid-template-columns:1fr}}
/* the stub fills its column here, rather than sitting at its own default width */
.v-stations .d-stub,.v-stations--states .d-stub{max-width:none;text-decoration:none}
/* short: the body's own rhythm is tight, not the device's roomy default */
.v-stations .d-stub__body,.v-stations--states .d-stub__body{gap:2px;min-height:0;padding:var(--s3) var(--s4)}
/* the station name is ONE line at the width the stub actually has. It shrinks to
   the column rather than wrapping, because his longest name ("Bus, Beckenham
   Hill Road") still sits on a single line. */
.v-stations .d-stub__body b,.v-stations--states .d-stub__body b{font-size:clamp(15px,1.55vw,26px);line-height:1;
  text-decoration:none;white-space:nowrap}
/* the route note is ONE line on his shot. This stage is narrower than his, so
   the line is set a shade smaller and its tracking eased rather than truncated:
   a clipped route note tells the reader less than a small one. */
.v-stations .d-stub__body small{font-size:9px;letter-spacing:.05em;
  white-space:nowrap}
.v-stations .d-stub__body small{text-decoration:none}
/* a stub is a link, and a link underlines every descendant unless it is told
   not to. On the starred shot nothing is underlined. */
.v-stations .d-stub *,.v-stations--states .d-stub *{text-decoration:none}

.v-walk{position:relative;display:block;height:24px;margin:var(--s2) 0 0}
.v-walk__rail{position:absolute;left:0;right:0;bottom:3px;width:100%;height:8px;
  overflow:visible}
.v-walk__rail path{fill:none;stroke:var(--black);stroke-width:5;
  stroke-linecap:round;stroke-dasharray:1 11}
.v-walk__man{position:absolute;left:0;bottom:0;width:18px;height:22px;
  transform:translateX(0);
  transition:transform calc(var(--dur-draw) * 2.4) var(--ease-out)}
.v-walk__man path{fill:none;stroke:var(--black);stroke-width:2.4;
  stroke-linecap:round;stroke-linejoin:round}
.d-stub:hover .v-walk__man,.d-stub:focus-visible .v-walk__man,
.d-stub[data-walking] .v-walk__man{transform:translateX(var(--run,180px))}

/* MEASURED off the starred shot: the time reads as ONE horizontal line, and it
   is set MIXED CASE, not caps: "10 min", condensed, at roughly twice the
   station name's cap height. Beside it, hard on the baseline, the micro
   "WALK TO THE NORTH GATE". Both parts refuse to wrap or the row becomes the
   four-line stack the first render produced. */
.v-walk__time{display:flex;align-items:baseline;gap:var(--s2);margin:var(--s1) 0 0;
  flex-wrap:nowrap;min-width:0}
.v-walk__time b{font:900 clamp(26px,2.9vw,46px)/.85 var(--font);
  font-stretch:var(--wdth-condensed);white-space:nowrap;flex:none;
  text-transform:none}
.v-walk__time span{font:700 10px/1.2 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.75;
  white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}

/* MEASURED off the starred shot: the tab is a SLIM vertical strip, roughly a
   twelfth of the stub's width, carrying one word read bottom to top. The
   device's 130px default is a wide panel and eats the name's measure. */
.v-stations .d-stub__tab,.v-stations--states .d-stub__tab{flex:0 0 34px;padding:var(--s2) 0}
.v-stations .d-stub__tab span,.v-stations--states .d-stub__tab span{writing-mode:vertical-rl;transform:rotate(180deg);
  font-size:9px;letter-spacing:.14em;white-space:nowrap}
@media (max-width:660px){
  .v-stations .d-stub__tab span{writing-mode:horizontal-tb;transform:none}
}
@media (prefers-reduced-motion:reduce){
  .v-walk__man{transition:none;transform:translateX(var(--run,180px))}
}

/* ============================================================================
   V-gates , G9. Gate picker as entrance flags. PORTED AS STARRED.

   Louis starred this exactly as it stands, so it is carried over unchanged:
   same look, same proportions, same mechanism, same copy structure. The only
   edits are raw values swapped for tokens. MEASURED off the starred shot
   (18.08.57): three flags in a row, each on its OWN short cream pole at the
   left edge of its cloth; each cloth is a different height with its own top
   offset; the swallow-tail is cut UP into the foot; the gate number is a micro
   label, the gate name is display type, and a stroked arrow sits at the foot.
   The chosen gate's detail prints in a panel to the RIGHT, under a cream rule.

   The rig redesign (one pole line, flags hung at different drops) is NOT this
   component. It sits below as V5b, headed PROPOSAL, not approved.
   ============================================================================ */
.v-gates{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,300px);
  gap:clamp(var(--s5),4vw,var(--s8));align-items:start}
@media (max-width:900px){.v-gates{grid-template-columns:1fr}}

/* the poles run 24px past the foot of each cloth, and the cloths hang at
   different drops, so the row needs clearance underneath or the caption below
   lands against the longest flag. MEASURED: 24px pole + the deepest drop. */
.v-gates__row{display:flex;flex-wrap:wrap;align-items:flex-start;
  gap:clamp(var(--s4),2.4vw,var(--s6));padding-bottom:var(--s7)}

.v-gates__flag{position:relative;appearance:none;cursor:pointer;
  border:0;background:none;padding:0 0 0 10px;text-align:left;
  flex:0 0 auto;width:clamp(150px,19vw,220px);
  padding-top:var(--drop,0px)}
/* each flag's OWN pole, cream, running the height of its cloth and a little past */
.v-gates__flag::before{content:"";position:absolute;left:0;top:var(--drop,0px);
  bottom:-24px;width:10px;background:var(--cream)}

.v-gates__cloth{position:relative;display:flex;flex-direction:column;
  gap:var(--s2);padding:var(--s4) var(--s4) var(--s6);
  background:var(--dc);color:var(--black);
  /* the swallow-tail is cut UP into the foot of the cloth */
  clip-path:polygon(0 0,100% 0,100% 100%,50% calc(100% - 26px),0 100%);
  min-height:var(--cloth,200px);
  transform-origin:0 50%;
  transition:transform var(--dur-state) var(--ease-out)}
.v-gates__flag .v-gates__no{margin:0;opacity:.8}
/* MEASURED against the starred shot: the gate name sits INSIDE the cloth and
   never runs past its right edge. At .t-head's clamp the longest name
   ("Accessible gate") overflowed a 220px cloth, so the name takes its own
   smaller step on this device and wraps rather than clipping. */
.v-gates__flag h3{margin:var(--s1) 0 0;font-size:clamp(22px,2.2vw,32px);
  overflow-wrap:break-word;hyphens:none}
.v-gates__flag .v-g{width:34px;height:22px;margin-top:auto}
.v-gates__flag .v-g path{stroke-width:3}

/* the cloth RIPPLES on hover, as starred, and the chosen one carries an inset
   black ring. The verb is SWING, because a flag hangs on a fixing. */
.v-gates__flag:hover .v-gates__cloth,
.v-gates__flag.is-hover .v-gates__cloth{
  animation:v-fly 1.5s var(--ease-bounce) infinite alternate}
.v-gates__flag[aria-pressed="true"] .v-gates__cloth,
.v-gates__flag.is-selected .v-gates__cloth{
  box-shadow:inset 0 0 0 var(--rule-heavy) var(--black)}
@keyframes v-fly{
  from{transform:skewY(.7deg) scaleY(1)}
  to{transform:skewY(-.9deg) scaleY(.985)}
}
.v-gates__flag.is-focus{outline:var(--focus-width) solid var(--focus-colour-on-dark);
  outline-offset:var(--focus-offset)}
.v-gates__flag.is-disabled,.v-gates__flag[disabled]{opacity:.4;cursor:not-allowed}
.v-gates__flag.is-disabled .v-gates__cloth{animation:none;transform:none}
@media (prefers-reduced-motion:reduce){
  .v-gates__flag:hover .v-gates__cloth{animation:none}
}

/* the chosen gate's detail, printed in the panel on the right under a cream rule */
.v-gates__print{border-top:var(--rule-heavy) solid var(--ink);padding:var(--s4) 0 0}
.v-gates__print h3{margin:var(--s2) 0 var(--s3);color:var(--stage-corner)}
.v-gates__print p{margin:0;font-size:var(--size-small);line-height:1.5;max-width:40ch}
/* the panel sits on black, so its fact rules take the on-dark hairline */
.v-gates__print .v-facts > div{border-top-color:var(--hairline-on-dark)}
.v-gates__print .v-facts > div:last-child{border-bottom-color:var(--hairline-on-dark)}

/* ============================================================================
   V-rig , THE PROPOSAL. Not approved, not the component.
   One pole line, flags hung at DIFFERENT drops with DIFFERENT cloth lengths,
   the chosen one flying, the detail printed on a totem bar below. Offered as an
   answer to the "three blocks in a row" note, for Louis to judge or bin.
   ============================================================================ */
.v-rig{position:relative}
.v-rig__rig{position:relative;padding:var(--s5) 0 var(--s6);
  display:flex;align-items:flex-start;gap:clamp(var(--s4),3vw,var(--s7));
  flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain}
.v-rig__rig::before{content:"";position:absolute;left:0;right:0;top:var(--s5);
  height:var(--rule-heavy);background:var(--ink);z-index:var(--z-body)}
.v-rig .v-gates__flag{z-index:var(--z-body);padding-left:0}
/* on the rig, the pole becomes a short halyard from the LINE down to the cloth */
.v-rig .v-gates__flag::before{left:var(--s4);top:0;bottom:auto;
  width:var(--rule);height:var(--drop,0px);background:var(--ink)}
.v-rig__print{margin:var(--s5) 0 0}

/* ============================================================================
   V-care , A3. Lost child, welfare, first aid.
   THE ONE PLACE THE BRAND STEPS BACK. No device, no ribbon, no mascot, no
   condensed display, no stage colour. Black on yellow, the numbered step first,
   the phone number last and biggest. This block is deliberately plain and it is
   supposed to look plain beside everything else on this page.

   MEASURED off the starred shot (18.19.28): the heading and the body are the
   SAME upright face at normal width, the steps are numbered rows separated by
   black rules, and the contact bar at the foot is black with the number set
   large and the label small beside it.
   ============================================================================ */
.v-care{background:var(--care);color:var(--black);
  padding:clamp(var(--s5),3vw,var(--s7));max-width:760px;
  border-radius:var(--radius-slab)}
.v-care__kicker{margin:0 0 var(--s4)}
/* NOT .t-head: this heading is upright, normal width, sentence case. That is the
   whole point of the block, and it is the only heading in the system that is. */
.v-care h3{margin:0 0 var(--s3);
  font:700 clamp(26px,3.2vw,38px)/1.08 var(--font);font-stretch:100%;
  text-transform:none;letter-spacing:-.01em}
.v-care__lede{margin:0 0 var(--s5);font-size:var(--size-body);line-height:1.5;
  max-width:52ch}

.v-care__steps{list-style:none;margin:0 0 var(--s5);padding:0;counter-reset:v-step}
.v-care__steps li{display:grid;grid-template-columns:var(--s7) 1fr;gap:var(--s4);
  align-items:start;padding:var(--s4) 0;
  border-top:var(--rule) solid var(--black);font-size:var(--size-body);line-height:1.45}
.v-care__steps li:last-child{border-bottom:var(--rule) solid var(--black)}
.v-care__steps li::before{counter-increment:v-step;content:counter(v-step);
  font:700 clamp(19px,2vw,24px)/1 var(--font);font-stretch:100%}

/* the contact bar. The number is the biggest thing in the block, and it is a real
   tel: link so a thumb can dial it. NUMBER to confirm until the number exists. */
.v-care__contact{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s2) var(--s5);
  background:var(--black);color:var(--cream);
  padding:var(--s4) var(--s5);margin:0 0 var(--s4);text-decoration:none}
.v-care__contact span{font:400 var(--size-small)/1.4 var(--font)}
.v-care__contact b{font:700 clamp(24px,3.2vw,38px)/1.05 var(--font);font-stretch:100%}
.v-care__contact.is-focus{outline:var(--focus-width) solid var(--focus-colour);
  outline-offset:var(--focus-offset)}

/* the more-detail disclosure. A plain outlined button, not a totem bar. */
.v-care__more{appearance:none;cursor:pointer;display:inline-flex;align-items:center;
  gap:var(--s3);padding:var(--s3) var(--s4);min-height:var(--tap-min);
  background:none;color:var(--black);border:var(--rule) solid var(--black);
  border-radius:var(--radius-slab);
  font:700 var(--size-small)/1 var(--font);font-stretch:100%}
.v-care__more .v-g{width:18px;height:18px;
  transition:transform var(--dur-state) var(--ease-out)}
.v-care__more[aria-expanded="true"] .v-g{transform:rotate(180deg)}
.v-care__more:hover,.v-care__more.is-hover{background:var(--cream)}
.v-care__more.is-focus{outline:var(--focus-width) solid var(--focus-colour);
  outline-offset:var(--focus-offset)}
.v-care__detail{margin:var(--s4) 0 0;font-size:var(--size-small);line-height:1.5;
  max-width:56ch}
.v-care__detail[hidden]{display:none}

/* ============================================================================
   V-access , A4. Accessibility as the poster's fact column.
   Two columns: the display heading and its honest paragraph on the left, the
   poster's fact column on the right. Values people need, never reassurance.
   ============================================================================ */
.v-access{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(var(--s5),4vw,var(--s8));align-items:start}
@media (max-width:820px){.v-access{grid-template-columns:1fr}}
.v-access__meta{margin:var(--s4) 0 var(--s5);opacity:.75}
.v-access__tag{margin-top:var(--s4)}

/* ============================================================================
   V-food , A5. Food in three stage lanes.
   MEASURED off the starred shot (17.51.33): the stage block is a solid colour
   panel on the LEFT with the stage name at the top and the soundwave at its
   foot; the traders sit in a bordered lane to its right, one rule between each.
   Chips are label-in-slab, outlined, never pills. A trader you cannot eat at is
   struck through IN PLACE so the list never reflows under your thumb.
   ============================================================================ */
.v-food{display:grid;gap:var(--s5)}

.v-food__lane{display:grid;grid-template-columns:minmax(150px,220px) minmax(0,1fr);
  align-items:stretch}
@media (max-width:700px){.v-food__lane{grid-template-columns:1fr}}

/* MEASURED off the starred shot: the stage panel is exactly as tall as its own
   trader list, the name sits on ONE line at the top and the soundwave at the
   foot. A min-height or a forced gap here leaves the dead space under The Grove
   and The Corner that the first render showed. */
.v-food__stage{display:flex;flex-direction:column;justify-content:space-between;
  gap:var(--s6);padding:var(--s4);background:var(--stage);color:var(--black)}
.v-food__stage h3{margin:0;font-size:clamp(20px,2vw,28px);white-space:nowrap}
.v-food__stage .d-wave{--dc:var(--black)}

.v-food__traders{list-style:none;margin:0;padding:0;
  border:var(--rule-heavy) solid var(--black)}
.v-food__traders li{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);
  padding:var(--s3) var(--s4);border-bottom:var(--rule) solid var(--hairline);
  transition:opacity var(--dur-state) var(--ease-out)}
.v-food__traders li:last-child{border-bottom:0}
.v-food__traders b{display:block;font:900 clamp(16px,1.7vw,20px)/1.05 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}
.v-food__traders em{display:block;font:700 10px/1.3 var(--font);font-style:normal;
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.7}
.v-food__tags{display:flex;flex-wrap:wrap;gap:var(--s2);margin-left:auto}
/* the chip: a label in a slab, outlined, square. NOT a pill: the one pill in this
   system is the CTA and nothing else may take that shape. */
.v-food__tag{display:inline-flex;align-items:center;
  padding:5px var(--s3);border:var(--rule) solid var(--black);
  border-radius:var(--radius-slab);
  font:900 10px/1.2 var(--font);font-stretch:var(--wdth-condensed);
  text-transform:uppercase;letter-spacing:.08em;white-space:nowrap}
/* struck through in its lane, not deleted */
.v-food__traders li[data-out]{opacity:.42}
.v-food__traders li[data-out] b{text-decoration:line-through;
  text-decoration-thickness:2px}

/* ============================================================================
   V-refill , A7. Refill points, pin by pin.
   MEASURED off the starred shot (17.51.06): the stage name sits left, the pins
   run in a row beside it, the count is stated in words hard right, and the STAGE
   COLOUR LIVES IN THE RULE UNDER THE ROW, never in a filled block behind the
   pins. A cut-off block of colour is a swatch, which the system forbids.
   ============================================================================ */
.v-refill__line{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s4);
  padding:0 0 var(--s3);border-bottom:var(--rule-heavy) solid var(--stage);
  margin:0 0 var(--s5)}
.v-refill__line:last-of-type{margin-bottom:var(--s4)}
.v-refill__line h3{margin:0;flex:0 0 auto;min-width:clamp(120px,14vw,190px)}
.v-refill__pins{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s4);
  flex:1 1 auto;min-width:0}
/* MEASURED against the starred shot: the count at the right is set in the SAME
   voice as the stage name on the left, bold CONDENSED caps at title scale, not
   the light tracked micro label this page first carried. It is a statement, not
   a caption; it answers the row. */
.v-refill__count{margin-left:auto;flex:0 0 auto;
  font:900 22px/1 var(--font);font-stretch:var(--wdth-condensed);
  letter-spacing:0;text-transform:uppercase}

/* ============================================================================
   V-faq , A1. FAQ as the totem.
   Composed entirely from .d-totem. Nothing is redefined here; this rule only
   holds the stack at a readable measure, which is a placement decision the
   host makes, not the device.
   ============================================================================ */
.v-faq{max-width:900px}

/* The local `.v-totem-arrow` override is DELETED. It existed because the shared
   `.d-totem__arrow` painted a heavy solid block arrow where Louis's A1
   (17.50.14) has a drawn LINE arrow. The planner has since corrected the shared
   device: `.d-totem__arrow[viewBox="0 0 30 18"]` strokes the shaft and the open
   chevron. Every totem on this page now uses the shared class with that
   viewBox, and nothing is redefined here. */

/* MEASURED off the lab's `.a1-row:nth-child(n)` rules: the sequence is
   pink, cream, yellow, coral, cream. The shared `--stack` modifier runs a
   4-step cycle (pink, transparent, yellow, coral) which repeats PINK at the
   fifth bar. Louis's fifth bar is cream, so the sequence is set explicitly
   here rather than taken from the cycle. */
.v-faq > :nth-child(1) .d-totem__bar{background:var(--pink)}
.v-faq > :nth-child(2) .d-totem__bar{background:var(--cream)}
.v-faq > :nth-child(3) .d-totem__bar{background:var(--yellow)}
.v-faq > :nth-child(4) .d-totem__bar{background:var(--coral)}
.v-faq > :nth-child(5) .d-totem__bar{background:var(--cream)}
.v-faq > :nth-child(6) .d-totem__bar{background:var(--pink)}

/* ============================================================================
   V-block , T4. The stack logo as a turning block.
   CSS 3D, no WebGL. Four faces on one cube, each carrying a stage colour; picking
   a stage turns the block a quarter turn and the new face arrives already in that
   colour. The lockup SVG is the same file on every face, tinted by the face.
   ============================================================================ */
/* MEASURED off the starred shot (17.56.06), NOT off the lab's vw values: the lab
   sized by viewport and had no sidebar, so its numbers come out wrong in this
   narrower stage. On his shot the slab is a LARGE solid portrait block on a
   BLACK stage, sitting centre-left, seen at a three-quarter angle: the pink
   front face carries the black portrait lockup, and a dark side face gives it
   real thickness. The front face is about 280px tall per 1000px of stage width,
   roughly 0.55 as wide as it is tall, and the depth is about a third of the
   width. The right-hand column carries the heading, three stage bars and one
   micro caption line; there is no paragraph and no fourth bar. */
.v-block{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,300px);
  gap:clamp(var(--s5),4vw,var(--s7));align-items:center}
@media (max-width:760px){.v-block{grid-template-columns:1fr;justify-items:start}}

.v-block__side{min-width:0;display:grid;gap:var(--s3);align-content:center}
.v-block__side h3{margin:0}

/* isolation:isolate creates a stacking context that FLATTENS the preserve-3d
   chain, so the block rendered as one flat square with no depth. The scene only
   needs the perspective; nothing else may create a compositing context on it. */
/* the states row passes --cube, which stands in for the slab's height */
.v-block__scene{--slabh:var(--cube,clamp(200px,28vw,420px));
  --slabw:calc(var(--slabh) * .55);--slabd:calc(var(--slabw) * .32);
  /* the state cells hang the scene off a <span>, which is inline by default and
     would ignore width and height entirely: it must declare its own box. */
  display:block;width:var(--slabw);height:var(--slabh);
  /* CENTRE-LEFT on his shot, not centred in its column: the slab sits about a
     fifth in from the stage's left edge with open black to its right. */
  perspective:1500px;flex:none;position:relative;justify-self:start;
  margin:clamp(var(--s5),4vw,var(--s7)) 0 clamp(var(--s5),4vw,var(--s7))
         clamp(var(--s5),8vw,140px)}
.spec-states .v-block__scene{margin:0}
/* the rest angle IS part of his look: the slab is never seen square on, so the
   side face is always visible. --turn accumulates quarter turns on top of it. */
.v-block__cube{display:block;position:relative;width:100%;height:100%;
  transform-style:preserve-3d;
  transform:translateZ(calc(var(--slabd) / -2))
            rotateY(calc(-19deg + var(--turn,0deg)));
  transition:transform var(--dur-swing) var(--ease-verb-swing)}
.v-block__face{position:absolute;inset:0;display:flex;
  align-items:center;justify-content:center;
  padding:calc(var(--slabw) * .07);backface-visibility:hidden;
  background:var(--dc);color:var(--black);overflow:hidden}
/* the stack lockup is PORTRAIT, so it is sized by its HEIGHT, not its width, or
   it overflows the face. base.css sets `u-svg svg{width:100%;height:auto}`,
   which is what made the mark take the face's WIDTH, so the override has to be
   on the svg itself; the SVG's own preserveAspectRatio then letterboxes it. */
.v-block__face u-svg{display:block;height:100%;width:100%;min-height:0}
.v-block__face u-svg svg{height:100%;width:100%}
/* four faces: two fronts a half turn apart, and two SIDES whose width is the
   slab's depth, which is what gives the block its real thickness. The sides
   carry no mark and sit a shade darker, as the shot's side face does. */
.v-block__face:nth-child(1){transform:rotateY(0deg) translateZ(calc(var(--slabd) / 2))}
.v-block__face:nth-child(3){transform:rotateY(180deg) translateZ(calc(var(--slabd) / 2))}
.v-block__face:nth-child(2),.v-block__face:nth-child(4){
  left:calc(50% - var(--slabd) / 2);width:var(--slabd);right:auto;
  filter:brightness(.42)}
.v-block__face:nth-child(2) u-svg,.v-block__face:nth-child(4) u-svg{opacity:0}
.v-block__face:nth-child(2){transform:rotateY(90deg) translateZ(calc(var(--slabw) / 2))}
.v-block__face:nth-child(4){transform:rotateY(270deg) translateZ(calc(var(--slabw) / 2))}
/* the flat fallback: one face, no 3D, for anywhere preserve-3d is not honoured */
.v-block__face--flat{position:relative;inset:auto;transform:none;
  width:var(--slabw);height:var(--slabh);filter:none}

/* his three stage bars: a solid bar per stage, the name in condensed caps at the
   left, the real soundwave glyph small at the right end. */
.v-block__picks{display:grid;gap:var(--s2);margin:var(--s2) 0 0}
.v-block__pick{display:flex;width:100%;align-items:center;justify-content:space-between;
  gap:var(--s3);appearance:none;border:0;cursor:pointer;
  background:var(--dc);color:var(--black);
  padding:var(--s3) var(--s4);border-radius:var(--radius-slab,0);
  font:900 14px/1 var(--font);font-stretch:var(--wdth-condensed);
  letter-spacing:var(--track-micro);text-transform:uppercase;
  transition:transform var(--dur-state) var(--ease-out)}
.v-block__pick u-svg{width:34px;flex:0 0 auto;display:block}
.v-block__pick.is-selected{box-shadow:inset 0 0 0 3px var(--cream)}
.v-block__pick:focus-visible{outline:var(--focus-width) solid var(--focus-colour);
  outline-offset:var(--focus-offset)}
.v-block__say{margin:var(--s2) 0 0;opacity:.85}

@media (prefers-reduced-motion:reduce){
  .v-block__cube{transition:none}
  .v-block__pick{transition:none}
}

/* ============================================================================
   V-facts , the opening times and key facts strip, from the FESTIVAL INFO poster.
   Micro label over bold value, in a row, with a black rule above each. It reuses
   .v-facts, so nothing new is defined here beyond the row layout.
   ============================================================================ */
.v-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:0 var(--s5)}
.v-strip > div{border-top:var(--rule-heavy) solid var(--black);
  padding:var(--s3) 0 var(--s5)}
.v-strip dt{margin:0 0 var(--s2);font:700 var(--size-micro)/1.3 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.7}
.v-strip dd{margin:0;font:900 clamp(19px,2.2vw,28px)/1 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}

/* ============================================================================
   V-rules , allowed / not allowed, and what to bring.
   Two plain lists side by side. The tick and the cross are DRAWN strokes at the
   brand's rule weight, never a coloured badge, because a colour-only yes/no is
   unreadable to a third of the people who need it most.
   ============================================================================ */
.v-rules{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  gap:var(--s6)}
.v-rules ul{list-style:none;margin:var(--s4) 0 0;padding:0;display:grid;gap:0}
.v-rules li{display:flex;align-items:flex-start;gap:var(--s3);
  padding:var(--s3) 0;border-top:var(--rule) solid var(--hairline);
  font-size:var(--size-small);line-height:1.45}
.v-rules li:first-child{border-top:var(--rule) solid var(--black)}
.v-rules li .v-g{width:20px;height:20px;margin-top:2px}
.v-rules li .v-g path{stroke-width:3}

/* ============================================================================
   V-family , the family area card, and V-green, the sustainability pledges.
   Both are label-in-slab compositions; nothing new is invented for them.
   ============================================================================ */
.v-pledges{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:var(--s5)}
.v-pledges > div{border-top:var(--rule-heavy) solid var(--black);padding:var(--s3) 0 0}
.v-pledges h4{margin:0 0 var(--s2);font:900 clamp(17px,1.9vw,22px)/1.05 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}
.v-pledges p{margin:0;font-size:var(--size-small);line-height:1.5}

/* ============================================================================
   V-contact , contact and press. Plain rows, because this is reference material.
   ============================================================================ */
.v-contact{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:var(--s5)}
.v-contact a{font-weight:700}

/* ============================================================================
   V-partners , the partner grid. Placeholders are DRAWN as a box with its own
   label, never a grey rectangle, so the grid reads as a design decision rather
   than as missing artwork.
   ============================================================================ */
.v-partners{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:var(--s4)}
.v-partners div{display:grid;place-items:center;aspect-ratio:5/2;
  border:var(--rule) solid var(--black);
  font:700 10px/1.3 var(--font);letter-spacing:var(--track-micro);
  text-transform:uppercase;opacity:.65;text-align:center;padding:var(--s3)}

/* ============================================================================
   V-merch , the merch card, and V-gallery, the masonry.
   HARD RULE, both: the mockup photography is shown at ITS OWN RATIO, UNTREATED
   and NEVER CROPPED. No object-fit:cover anywhere in this lane, no filter, no
   duotone, no overlay. The card sizes itself to the image, not the other way
   round, and the masonry is a column flow so every picture keeps its own shape.
   ============================================================================ */
.v-merch{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
  gap:var(--s5)}
.v-merch figure{margin:0;display:grid;gap:var(--s3);align-content:start}
.v-merch img{width:100%;height:auto;display:block;
  border:var(--rule) solid var(--black)}
.v-merch figcaption{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s2) var(--s4)}
.v-merch figcaption b{font:900 15px/1.05 var(--font);
  font-stretch:var(--wdth-condensed);text-transform:uppercase}
.v-merch figcaption span{margin-left:auto;font:700 var(--size-micro)/1 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.75}

/* the masonry: a column flow, so an image at 3:4 and an image at 2:3 both keep
   their own shape and simply take the height they need. */
.v-gallery{columns:3;column-gap:var(--s4)}
@media (max-width:900px){.v-gallery{columns:2}}
@media (max-width:480px){.v-gallery{columns:1}}
.v-gallery figure{margin:0 0 var(--s4);break-inside:avoid;display:grid;gap:var(--s2)}
.v-gallery img{width:100%;height:auto;display:block}
.v-gallery figcaption{font:700 10px/1.3 var(--font);
  letter-spacing:var(--track-micro);text-transform:uppercase;opacity:.7}

/* ============================================================================
   V-weather , the day-of notice. It is the .d-totem alert bar, reused by markup
   only: nothing is redefined. This rule holds the two stacked examples apart.
   ============================================================================ */
/* the alert bar's marquee run is wider than the viewport by design (it is two
   identical halves so the loop is seamless). The bar itself already hides that
   overflow, but a .spec measures its own scrollWidth, so the notice list owns
   the clip: without it the whole spec reports as escaping the viewport. */
/* MEASURED: the run is two identical halves and is deliberately wider than the
   bar, but the dismiss button sits AFTER the track in the flex row, so once the
   track is allowed its natural width the button is pushed off the right edge.
   The track must be allowed to shrink (min-width:0) for the button to stay on
   screen, which is the whole reason the device gives the track its own overflow. */
.v-notices{display:grid;gap:var(--s5);overflow:hidden;width:100%;min-width:0}
/* every box from the grid cell down to the track has to be allowed to shrink,
   or the run's max-content width propagates outward and pushes the dismiss
   button past the right edge. MEASURED: without min-width:0 on the .d-totem
   wrapper the button sat 645px off screen. */
.v-notices > .d-totem{min-width:0;width:100%;overflow:hidden}
.v-notices .d-totem__bar--alert{min-width:0;width:100%}
.v-notices .d-totem__track{min-width:0;flex:1 1 0;overflow:hidden}
.v-notices .d-totem__x{flex:0 0 auto}

/* ============================================================================
   The state cells on this page carry live components that are wider than a
   cell. Each cell owns its own clip so a demo never makes its spec pan.
   ============================================================================ */
.v-cell-clip{overflow:hidden;max-width:100%}

/* PLANNER, 20 Sep: the station NAME rule (.v-stations .d-stub__body b) also matched the walk
   time and shrank it. In Louis's G5 the time is the biggest thing on the stub, about 2x the
   name's cap height, condensed, mixed case. */
.v-stations .d-stub__body .v-walk__time b{font-size:clamp(28px,3vw,44px);line-height:.85;text-transform:none}
.v-stations .d-stub__body .v-walk__time span{font-size:9px;letter-spacing:.1em;overflow:visible;text-overflow:clip}
.v-stations .d-stub__body .v-walk__time{gap:6px}
