@charset "UTF-8";
/* Declared, not assumed. A stylesheet with no charset of its own is
   decoded as whatever the referring document was, and a host that
   serves CSS as plain text/css turns every separator in here into
   mojibake. The escapes below cover the same ground twice on purpose. */
/* ---- The stage: 3D mountain and, beneath it, the map ----
   The order is the argument. You came to look at a mountain, so the mountain
   and its numbers get the screen; the map is navigation between mountains and
   sits under them. Nothing here is decorative — every pixel of the stage is
   terrain you can turn. */

/* ONE SELECTED COLOUR, everywhere on the stage. A lit control used to be
   teal in some places and blue in others; the blue is the product's, so the
   blue is the answer, and it is declared once here rather than typed out at
   every control that can light up. */
/* ONE SELECTED COLOUR, everywhere on the stage. A lit control used to be
   teal in some places and blue in others; the blue is the product's, so the
   blue is the answer, declared once here rather than typed out at every
   control that can light up.

   AND ONE RESTING COLOUR, WHICH IS NOT WHITE. A near-white glyph over a
   sunlit snowfield is a near-white glyph on near-white ground: the owner
   could not find the controls, and no amount of dark shadow behind a light
   mark fixes a mark that is the same value as its background. The marks are
   DARK now, with a tight bright halo — the cartographer's answer, and the
   only one that survives both a white bowl and a night sky. On snow the
   glyph carries; against the dark the halo draws it as a crisp outline. */
.stage{--sel:#54b4ff;
  /* THE ROUTE COLOUR, toned. #ff8a1f is a traffic cone: at full chroma it
     was the loudest thing on a snow-white stage and it fought the blue
     rather than answering it. Pulled down a notch in both chroma and
     lightness, it still reads unmistakably as "this is your line" while
     sitting in the same register as the product's blue. One declaration,
     used by the route, the pin, the arrow, the armed pin mark and the
     navigate action, so the whole wayfinding language moves together. */
  --route:#e08a45;
  --route-lit:#efa869;
  --route-ink:#e8b083;
  --mark:#132335;
  --markhalo:drop-shadow(0 0 1.5px rgba(244,250,255,.98))
             drop-shadow(0 0 3px rgba(244,250,255,.9))
             drop-shadow(0 1px 5px rgba(244,250,255,.55));
  /* The same idea in type. A dark halo behind an 8px light label is mostly
     halo: the strokes drown in their own shadow and the word greys out. A
     dark word with a bright halo keeps its shape at that size, and it puts
     the quiet label in the same voice as the mark above it — the label
     recedes, the figure carries. */
  /* 9px is the floor and it is not negotiable: this layer is read on a
     phone, in flat light, through a wet screen or a goggle lens. The column
     got lean by STACKING the pairs, not by shrinking the type past the
     point where it can be read. */
  --labelink:#1b3048;
  --labelhalo:0 0 1.5px rgba(246,251,255,.98), 0 0 3px rgba(246,251,255,.92),
              0 1px 4px rgba(246,251,255,.6);
  position:relative;min-height:0;overflow:hidden;background:#060b14;
  border-top:1px solid var(--rule2)}
.m3d{position:absolute;inset:0}
.m3d-canvas{display:block;width:100%;height:100%;outline:0;touch-action:none;cursor:grab}
.m3d-canvas:focus-visible{outline:2px solid var(--info);outline-offset:-2px}

/* Busy and empty states share the centre of the stage. Both name the resort,
   because a status with no subject is useless when you switch mountains fast. */
.m3d-busy,.m3d-empty{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:9px;text-align:center;padding:22px;
  color:var(--dim)}
.m3d-busy strong{font-size:13px;color:var(--ink);font-weight:600}
.m3d-note{font-family:var(--mono);font-size:10.5px;letter-spacing:.02em;
  min-height:14px;max-width:90%}
.m3d-spin{width:26px;height:26px;border-radius:50%;
  border:2px solid var(--rule2);border-top-color:var(--info);
  animation:m3dspin .9s linear infinite}
@keyframes m3dspin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.m3d-spin{animation-duration:2.4s}}

/* ═══ THE LOADING CHIP ════════════════════════════════════════════════════
   Owner, on the first cut, which covered the whole stage: "this is not what i
   wanted. I asked for a near replica of the Extra > ultracode bar. Just like
   that, not the whole screen. We dont want an entire page, just the same
   indicator, bottom right."

   So it is a chip in the bottom-right corner: purple ground, our own range in
   white as the meter, the mountain's name, the step it is on, the level, and
   the chip's own bottom edge as the rail. The stage builds behind it in view
   rather than behind a curtain.
   ═════════════════════════════════════════════════════════════════════════ */
/* ABOVE THE FURNITURE, AND CLEAR OF IT. At z-index 6 the chip sat under the
   hud at 7, the control rail at 9 and the stack at 8, so the stage's own
   glyphs were drawn across the percentage. It is a status message about
   whether the mountain exists yet; nothing on the stage outranks it. And it
   is inset past the right rail rather than under it, because a chip that is
   merely on top of a control is still covering one. */
/* NO CARD. Owner: "remove purple background... no background. Just the bar,
   the skier and the updates and percentage." So this is a layout and nothing
   else: no panel, no border, no shadow, no radius. Three things stand on the
   stage, and the type carries its own legibility instead of borrowing a
   ground, because a loader that needs a box to be readable has been given the
   wrong colours. The shadow is a ring rather than a drop, so the letters read
   over a bright snowfield and over a night sky without a plate under them. */
.m3d-load{position:absolute;right:62px;bottom:14px;z-index:30;
  display:grid;grid-template-columns:1fr auto;align-items:baseline;
  column-gap:12px;row-gap:11px;width:min(320px,72vw);
  font-family:var(--sans);
  transition:opacity .28s ease, transform .28s ease}
/* Gone means gone: faded out AND out of the hit test, so a finished stage
   never has an invisible loader sitting over its bottom corner. */
.m3d-load.out{opacity:0;transform:translateY(6px);pointer-events:none}
.m3d-lockup{display:flex;flex-direction:column;gap:4px;min-width:0}
.m3d-load strong{font-size:13px;color:#fff;font-weight:650;letter-spacing:-.01em;
  line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}
.m3d-load .m3d-note{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:#cfe2fa;line-height:1;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92)}
.m3d-pct{font-family:var(--mono);font-size:13px;font-weight:700;color:#fff;
  font-variant-numeric:tabular-nums;letter-spacing:.01em;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}

/* THE BAR, IN BETTER PIXELS. Owner: "Make the pixels better, more amazing."
   It was a 3 px square with a 3 px gutter, which is half gutter by area and
   reads as a mesh rather than as a display. The pitch is 5 px now with a 4 px
   cell, so the squares own the bar and the gutter is a hairline between them,
   and the inner height is exactly three cells rather than two and a bit.
   Unlit cells are drawn rather than left empty, so the track has a shape from
   the first frame and the meter reads as a row of lights that are off.
   The lit run carries a gradient along its length, dimmest at the start and
   brightest under the skier, which is what gives the bar its direction. */
/* THE CELL IS CUT BY TWO MASKS, NOT PAINTED BY ONE GRADIENT. The first cut
   of this laid the column pattern in as a background layer and multiplied it
   over the colour, and multiply against a TRANSPARENT stop changes nothing:
   the gutters between columns never appeared and the lit run rendered as
   three solid rules. Both axes are masks, intersected, so the ground can be
   any colour and the squares are still squares. */
.m3d-rail{position:relative;grid-column:1 / -1;height:15px;
  --cols:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 5px);
  --rows:repeating-linear-gradient(180deg,#000 0 4px,transparent 4px 5px)}
.m3d-rail::before,.m3d-rail::after{content:'';position:absolute;inset:0;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect}
/* the lights that are off, drawn rather than left empty, so the track has a
   shape from the first frame */
.m3d-rail::before{background:#ffffff;opacity:.19}
/* and the run that is lit, clipped to the real level: dimmest where the load
   started and brightest under the skier, which is what gives it direction */
.m3d-rail::after{
  background:linear-gradient(90deg,#2f7fd0 0%,#66a7f1 58%,#eaf4ff 100%);
  clip-path:inset(0 calc(100% - var(--p,0) * 100%) 0 0);
  transition:clip-path .45s cubic-bezier(.22,.61,.36,1)}

/* THE PIXELS ARE ALIVE, EVEN STANDING STILL.
   Owner: "Even as the bar is idle and loading I want all of the pixels to
   alternate as if they are alive."
   A meter that only fills is dead between steps, and the steps on a slow
   mountain are seconds apart: for most of a load the bar was a still
   picture. So a third lattice rides over both the lit run and the unlit
   track, carrying two bright bands that sweep the bar at different speeds
   and in opposite directions. Where they cross, a cell flares; where they
   do not, it settles. Two periods that do not divide into each other never
   repeat the same pattern twice in a load, which is what stops it reading
   as a wipe.
   It is one element and two background positions: no canvas, nothing drawn
   per frame by us, and the compositor runs it while the main thread builds
   the mountain, which is the whole point of doing it in CSS. */
.m3d-live{position:absolute;inset:0;pointer-events:none;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect;
  background-image:
    linear-gradient(90deg, transparent 0%, rgba(255,255,255,.00) 34%,
      rgba(255,255,255,.55) 46%, rgba(255,255,255,.85) 50%,
      rgba(255,255,255,.55) 54%, rgba(255,255,255,.00) 66%, transparent 100%),
    linear-gradient(90deg, transparent 0%, rgba(140,200,255,.00) 38%,
      rgba(140,200,255,.60) 50%, rgba(140,200,255,.00) 62%, transparent 100%);
  background-size:190px 100%, 320px 100%;
  background-repeat:repeat-x, repeat-x;
  mix-blend-mode:screen;
  animation:sp-live-a 1.9s linear infinite, sp-live-b 3.1s linear infinite reverse}
@keyframes sp-live-a{ from{background-position-x:0, 0} to{background-position-x:190px, 0} }
@keyframes sp-live-b{ from{background-position-x:0, 0} to{background-position-x:0, 320px} }

@media (prefers-reduced-motion:reduce){.m3d-spin{animation-duration:2.4s}}

/* ═══ THE LOADING CHIP ════════════════════════════════════════════════════
   Owner, on the first cut, which covered the whole stage: "this is not what i
   wanted. I asked for a near replica of the Extra > ultracode bar. Just like
   that, not the whole screen. We dont want an entire page, just the same
   indicator, bottom right."

   So it is a chip in the bottom-right corner: purple ground, our own range in
   white as the meter, the mountain's name, the step it is on, the level, and
   the chip's own bottom edge as the rail. The stage builds behind it in view
   rather than behind a curtain.
   ═════════════════════════════════════════════════════════════════════════ */
/* ABOVE THE FURNITURE, AND CLEAR OF IT. At z-index 6 the chip sat under the
   hud at 7, the control rail at 9 and the stack at 8, so the stage's own
   glyphs were drawn across the percentage. It is a status message about
   whether the mountain exists yet; nothing on the stage outranks it. And it
   is inset past the right rail rather than under it, because a chip that is
   merely on top of a control is still covering one. */
/* NO CARD. Owner: "remove purple background... no background. Just the bar,
   the skier and the updates and percentage." So this is a layout and nothing
   else: no panel, no border, no shadow, no radius. Three things stand on the
   stage, and the type carries its own legibility instead of borrowing a
   ground, because a loader that needs a box to be readable has been given the
   wrong colours. The shadow is a ring rather than a drop, so the letters read
   over a bright snowfield and over a night sky without a plate under them. */
.m3d-load{position:absolute;right:62px;bottom:14px;z-index:30;
  display:grid;grid-template-columns:1fr auto;align-items:baseline;
  column-gap:12px;row-gap:11px;width:min(320px,72vw);
  font-family:var(--sans);
  transition:opacity .28s ease, transform .28s ease}
/* Gone means gone: faded out AND out of the hit test, so a finished stage
   never has an invisible loader sitting over its bottom corner. */
.m3d-load.out{opacity:0;transform:translateY(6px);pointer-events:none}
.m3d-lockup{display:flex;flex-direction:column;gap:4px;min-width:0}
.m3d-load strong{font-size:13px;color:#fff;font-weight:650;letter-spacing:-.01em;
  line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}
.m3d-load .m3d-note{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:#cfe2fa;line-height:1;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92)}
.m3d-pct{font-family:var(--mono);font-size:13px;font-weight:700;color:#fff;
  font-variant-numeric:tabular-nums;letter-spacing:.01em;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}

/* THE BAR, IN BETTER PIXELS. Owner: "Make the pixels better, more amazing."
   It was a 3 px square with a 3 px gutter, which is half gutter by area and
   reads as a mesh rather than as a display. The pitch is 5 px now with a 4 px
   cell, so the squares own the bar and the gutter is a hairline between them,
   and the inner height is exactly three cells rather than two and a bit.
   Unlit cells are drawn rather than left empty, so the track has a shape from
   the first frame and the meter reads as a row of lights that are off.
   The lit run carries a gradient along its length, dimmest at the start and
   brightest under the skier, which is what gives the bar its direction. */
/* THE CELL IS CUT BY TWO MASKS, NOT PAINTED BY ONE GRADIENT. The first cut
   of this laid the column pattern in as a background layer and multiplied it
   over the colour, and multiply against a TRANSPARENT stop changes nothing:
   the gutters between columns never appeared and the lit run rendered as
   three solid rules. Both axes are masks, intersected, so the ground can be
   any colour and the squares are still squares. */
.m3d-rail{position:relative;grid-column:1 / -1;height:15px;
  --cols:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 5px);
  --rows:repeating-linear-gradient(180deg,#000 0 4px,transparent 4px 5px)}
.m3d-rail::before,.m3d-rail::after{content:'';position:absolute;inset:0;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect}
/* the lights that are off, drawn rather than left empty, so the track has a
   shape from the first frame */
.m3d-rail::before{background:#ffffff;opacity:.19}
/* and the run that is lit, clipped to the real level: dimmest where the load
   started and brightest under the skier, which is what gives it direction */
.m3d-rail::after{
  background:linear-gradient(90deg,#2f7fd0 0%,#66a7f1 58%,#eaf4ff 100%);
  clip-path:inset(0 calc(100% - var(--p,0) * 100%) 0 0);
  transition:clip-path .45s cubic-bezier(.22,.61,.36,1)}

/* THE SKIER. Same pixel as the bar, standing proud of it so it is clearly in
   front rather than inside, and travelling with the lit edge: where they are
   IS the progress. The travel is inset by half their own width at each end so
   they never hang off the bar. */
@media (prefers-reduced-motion:reduce){.m3d-spin{animation-duration:2.4s}}

/* ═══ THE LOADING CHIP ════════════════════════════════════════════════════
   Owner, on the first cut, which covered the whole stage: "this is not what i
   wanted. I asked for a near replica of the Extra > ultracode bar. Just like
   that, not the whole screen. We dont want an entire page, just the same
   indicator, bottom right."

   So it is a chip in the bottom-right corner: purple ground, our own range in
   white as the meter, the mountain's name, the step it is on, the level, and
   the chip's own bottom edge as the rail. The stage builds behind it in view
   rather than behind a curtain.
   ═════════════════════════════════════════════════════════════════════════ */
/* ABOVE THE FURNITURE, AND CLEAR OF IT. At z-index 6 the chip sat under the
   hud at 7, the control rail at 9 and the stack at 8, so the stage's own
   glyphs were drawn across the percentage. It is a status message about
   whether the mountain exists yet; nothing on the stage outranks it. And it
   is inset past the right rail rather than under it, because a chip that is
   merely on top of a control is still covering one. */
/* NO CARD. Owner: "remove purple background... no background. Just the bar,
   the skier and the updates and percentage." So this is a layout and nothing
   else: no panel, no border, no shadow, no radius. Three things stand on the
   stage, and the type carries its own legibility instead of borrowing a
   ground, because a loader that needs a box to be readable has been given the
   wrong colours. The shadow is a ring rather than a drop, so the letters read
   over a bright snowfield and over a night sky without a plate under them. */
.m3d-load{position:absolute;right:62px;bottom:14px;z-index:30;
  display:grid;grid-template-columns:1fr auto;align-items:baseline;
  column-gap:12px;row-gap:11px;width:min(320px,72vw);
  font-family:var(--sans);
  transition:opacity .28s ease, transform .28s ease}
/* Gone means gone: faded out AND out of the hit test, so a finished stage
   never has an invisible loader sitting over its bottom corner. */
.m3d-load.out{opacity:0;transform:translateY(6px);pointer-events:none}
.m3d-lockup{display:flex;flex-direction:column;gap:4px;min-width:0}
.m3d-load strong{font-size:13px;color:#fff;font-weight:650;letter-spacing:-.01em;
  line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}
.m3d-load .m3d-note{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:#cfe2fa;line-height:1;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92)}
.m3d-pct{font-family:var(--mono);font-size:13px;font-weight:700;color:#fff;
  font-variant-numeric:tabular-nums;letter-spacing:.01em;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}

/* THE BAR, IN BETTER PIXELS. Owner: "Make the pixels better, more amazing."
   It was a 3 px square with a 3 px gutter, which is half gutter by area and
   reads as a mesh rather than as a display. The pitch is 5 px now with a 4 px
   cell, so the squares own the bar and the gutter is a hairline between them,
   and the inner height is exactly three cells rather than two and a bit.
   Unlit cells are drawn rather than left empty, so the track has a shape from
   the first frame and the meter reads as a row of lights that are off.
   The lit run carries a gradient along its length, dimmest at the start and
   brightest under the skier, which is what gives the bar its direction. */
/* THE CELL IS CUT BY TWO MASKS, NOT PAINTED BY ONE GRADIENT. The first cut
   of this laid the column pattern in as a background layer and multiplied it
   over the colour, and multiply against a TRANSPARENT stop changes nothing:
   the gutters between columns never appeared and the lit run rendered as
   three solid rules. Both axes are masks, intersected, so the ground can be
   any colour and the squares are still squares. */
.m3d-rail{position:relative;grid-column:1 / -1;height:15px;
  --cols:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 5px);
  --rows:repeating-linear-gradient(180deg,#000 0 4px,transparent 4px 5px)}
.m3d-rail::before,.m3d-rail::after{content:'';position:absolute;inset:0;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect}
/* the lights that are off, drawn rather than left empty, so the track has a
   shape from the first frame */
.m3d-rail::before{background:#ffffff;opacity:.19}
/* and the run that is lit, clipped to the real level: dimmest where the load
   started and brightest under the skier, which is what gives it direction */
.m3d-rail::after{
  background:linear-gradient(90deg,#2f7fd0 0%,#66a7f1 58%,#eaf4ff 100%);
  clip-path:inset(0 calc(100% - var(--p,0) * 100%) 0 0);
  transition:clip-path .45s cubic-bezier(.22,.61,.36,1)}

/* THE PIXELS ARE ALIVE, EVEN STANDING STILL.
   Owner: "Even as the bar is idle and loading I want all of the pixels to
   alternate as if they are alive."
   A meter that only fills is dead between steps, and the steps on a slow
   mountain are seconds apart: for most of a load the bar was a still
   picture. So a third lattice rides over both the lit run and the unlit
   track, carrying two bright bands that sweep the bar at different speeds
   and in opposite directions. Where they cross, a cell flares; where they
   do not, it settles. Two periods that do not divide into each other never
   repeat the same pattern twice in a load, which is what stops it reading
   as a wipe.
   It is one element and two background positions: no canvas, nothing drawn
   per frame by us, and the compositor runs it while the main thread builds
   the mountain, which is the whole point of doing it in CSS. */
.m3d-live{position:absolute;inset:0;pointer-events:none;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect;
  background-image:
    linear-gradient(90deg, transparent 0%, rgba(255,255,255,.00) 34%,
      rgba(255,255,255,.55) 46%, rgba(255,255,255,.85) 50%,
      rgba(255,255,255,.55) 54%, rgba(255,255,255,.00) 66%, transparent 100%),
    linear-gradient(90deg, transparent 0%, rgba(140,200,255,.00) 38%,
      rgba(140,200,255,.60) 50%, rgba(140,200,255,.00) 62%, transparent 100%);
  background-size:190px 100%, 320px 100%;
  background-repeat:repeat-x, repeat-x;
  mix-blend-mode:screen;
  animation:sp-live-a 1.9s linear infinite, sp-live-b 3.1s linear infinite reverse}
@keyframes sp-live-a{ from{background-position-x:0, 0} to{background-position-x:190px, 0} }
@keyframes sp-live-b{ from{background-position-x:0, 0} to{background-position-x:0, 320px} }

@media (prefers-reduced-motion:reduce){.m3d-spin{animation-duration:2.4s}}

/* ═══ THE LOADING CHIP ════════════════════════════════════════════════════
   Owner, on the first cut, which covered the whole stage: "this is not what i
   wanted. I asked for a near replica of the Extra > ultracode bar. Just like
   that, not the whole screen. We dont want an entire page, just the same
   indicator, bottom right."

   So it is a chip in the bottom-right corner: purple ground, our own range in
   white as the meter, the mountain's name, the step it is on, the level, and
   the chip's own bottom edge as the rail. The stage builds behind it in view
   rather than behind a curtain.
   ═════════════════════════════════════════════════════════════════════════ */
/* ABOVE THE FURNITURE, AND CLEAR OF IT. At z-index 6 the chip sat under the
   hud at 7, the control rail at 9 and the stack at 8, so the stage's own
   glyphs were drawn across the percentage. It is a status message about
   whether the mountain exists yet; nothing on the stage outranks it. And it
   is inset past the right rail rather than under it, because a chip that is
   merely on top of a control is still covering one. */
/* NO CARD. Owner: "remove purple background... no background. Just the bar,
   the skier and the updates and percentage." So this is a layout and nothing
   else: no panel, no border, no shadow, no radius. Three things stand on the
   stage, and the type carries its own legibility instead of borrowing a
   ground, because a loader that needs a box to be readable has been given the
   wrong colours. The shadow is a ring rather than a drop, so the letters read
   over a bright snowfield and over a night sky without a plate under them. */
.m3d-load{position:absolute;right:62px;bottom:14px;z-index:30;
  display:grid;grid-template-columns:1fr auto;align-items:baseline;
  column-gap:12px;row-gap:11px;width:min(320px,72vw);
  font-family:var(--sans);
  transition:opacity .28s ease, transform .28s ease}
/* Gone means gone: faded out AND out of the hit test, so a finished stage
   never has an invisible loader sitting over its bottom corner. */
.m3d-load.out{opacity:0;transform:translateY(6px);pointer-events:none}
.m3d-lockup{display:flex;flex-direction:column;gap:4px;min-width:0}
.m3d-load strong{font-size:13px;color:#fff;font-weight:650;letter-spacing:-.01em;
  line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}
.m3d-load .m3d-note{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:#cfe2fa;line-height:1;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:38vw;
  text-shadow:0 1px 3px rgba(4,10,20,.92)}
.m3d-pct{font-family:var(--mono);font-size:13px;font-weight:700;color:#fff;
  font-variant-numeric:tabular-nums;letter-spacing:.01em;
  text-shadow:0 1px 3px rgba(4,10,20,.92), 0 0 10px rgba(4,10,20,.7)}

/* THE BAR, IN BETTER PIXELS. Owner: "Make the pixels better, more amazing."
   It was a 3 px square with a 3 px gutter, which is half gutter by area and
   reads as a mesh rather than as a display. The pitch is 5 px now with a 4 px
   cell, so the squares own the bar and the gutter is a hairline between them,
   and the inner height is exactly three cells rather than two and a bit.
   Unlit cells are drawn rather than left empty, so the track has a shape from
   the first frame and the meter reads as a row of lights that are off.
   The lit run carries a gradient along its length, dimmest at the start and
   brightest under the skier, which is what gives the bar its direction. */
/* THE CELL IS CUT BY TWO MASKS, NOT PAINTED BY ONE GRADIENT. The first cut
   of this laid the column pattern in as a background layer and multiplied it
   over the colour, and multiply against a TRANSPARENT stop changes nothing:
   the gutters between columns never appeared and the lit run rendered as
   three solid rules. Both axes are masks, intersected, so the ground can be
   any colour and the squares are still squares. */
.m3d-rail{position:relative;grid-column:1 / -1;height:15px;
  --cols:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 5px);
  --rows:repeating-linear-gradient(180deg,#000 0 4px,transparent 4px 5px)}
.m3d-rail::before,.m3d-rail::after{content:'';position:absolute;inset:0;
  -webkit-mask-image:var(--cols),var(--rows);
  -webkit-mask-composite:source-in;
  mask-image:var(--cols),var(--rows);
  mask-composite:intersect}
/* the lights that are off, drawn rather than left empty, so the track has a
   shape from the first frame */
.m3d-rail::before{background:#ffffff;opacity:.19}
/* and the run that is lit, clipped to the real level: dimmest where the load
   started and brightest under the skier, which is what gives it direction */
.m3d-rail::after{
  background:linear-gradient(90deg,#2f7fd0 0%,#66a7f1 58%,#eaf4ff 100%);
  clip-path:inset(0 calc(100% - var(--p,0) * 100%) 0 0);
  transition:clip-path .45s cubic-bezier(.22,.61,.36,1)}

/* THE SKIER. Same pixel as the bar, standing proud of it so it is clearly in
   front rather than inside, and travelling with the lit edge: where they are
   IS the progress. The travel is inset by half their own width at each end so
   they never hang off the bar. */
@media (prefers-reduced-motion:reduce){
  .m3d-rail::after,.m3d-load{transition:none}
  .m3d-live{animation:none;opacity:.20}}
/* THE STAGE'S CONTROLS STAND DOWN WHILE THE LOADER IS UP.
   Owner: "the weird blue line on the left under the bar."
   It is .views button.on::after, the active tab's 1.5 px rule in the accent
   blue. On a tall phone that row lands at the loader's foot, so the 3D tab's
   underline reads as a blue line drawn under the left end of the meter.
   Nothing on that bar can do anything until the mountain is built, so while
   the loader is up they are not there. Set on the PARENT, because .views
   carries opacity:1 !important further down this file and a parent's opacity
   multiplies whatever the child declares. */
body[data-staging="load"] .stagebar,
body[data-staging="load"] .layers{opacity:0;pointer-events:none}

/* On a phone the weather sheet owns the bottom of the stage, so the loader
   sits above it rather than under it. */
@media (max-width:760px){
  .m3d-load{right:58px;left:10px;width:auto;bottom:calc(var(--sheet, 30svh) + 12px)}}

.m3d-empty h3{font-size:13.5px;color:var(--ink);font-weight:600;margin:2px 0 0}
.m3d-empty p{font-size:11.5px;line-height:1.55;max-width:42ch;color:var(--dim)}
/* The retry control is a WORD, not a box.
 *
 * This was a bordered rectangle in the middle of an otherwise boxless
 * interface — the single most conspicuous button on the page, and the one
 * shown at the exact moment something has already gone wrong. A rule stands
 * for its whole family: nothing in this app is framed to prove it is
 * tappable. The underline carries that, the full 44px target is kept for the
 * thumb, and the frame goes. */
.m3d-retry{margin-top:6px;font-family:var(--mono);font-size:11px;font-weight:700;
  letter-spacing:.09em;padding:0;min-height:44px;
  background:transparent;border:0;border-radius:0;
  color:var(--info);cursor:pointer;
  /* The rule hugs the word rather than floating beneath it: the underline is
     drawn as a text decoration, so it tracks the glyphs, while the 44px touch
     target is carried by the element around them. */
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:5px}
.m3d-retry:hover{border-bottom-color:var(--ink);color:var(--ink)}
.m3d-retry:focus-visible{outline:1.5px solid var(--info);outline-offset:3px}

/* THE HONESTY TITLE CARD. A production credit for the fail state: the drawn
   plate fills the stage behind it, clearly labelled a render, and the words
   carry the contract — survey not on stage, conditions still true. Type does
   the work; nothing here is framed in a box. */
.m3d-title{position:absolute;inset:0;overflow:hidden;display:flex;
  align-items:center;justify-content:center;text-align:center}
.mt-plate{position:absolute;inset:0;opacity:.5;filter:saturate(.8)}
.mt-plate svg{width:100%;height:100%;display:block}
.mt-tag{position:absolute;top:10px;left:50%;transform:translateX(-50%);
  font-family:var(--mono);font-size:8.5px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--dim);white-space:nowrap}
.mt-card{position:relative;max-width:min(86%,380px);padding:18px 8px;
  display:flex;flex-direction:column;align-items:center;gap:4px;
  /* Legibility over the plate comes from a soft ground, not a box. */
  background:radial-gradient(ellipse at center,
    color-mix(in srgb, var(--bg, #0d1117) 62%, transparent) 40%, transparent 75%)}
.mt-card h3{font-size:15px;font-weight:650;letter-spacing:.04em;color:var(--ink);margin:0}
.mt-line{font-size:12.5px;color:var(--ink);margin:6px 0 0;letter-spacing:.02em}
.mt-sub{font-size:12.5px;color:var(--dim);margin:0}
.mt-wx{display:flex;gap:18px;margin:10px 0 2px;justify-content:center}
.mt-wx span{display:flex;flex-direction:column;gap:1px}
.mt-wx b{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:13px;font-weight:700;color:var(--ink)}
.mt-wx i{font-style:normal;font-family:var(--mono);font-size:8.5px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.mt-why{font-size:10.5px;line-height:1.5;max-width:44ch;color:var(--dim);margin:8px 0 0}
.mt-acts{display:flex;gap:22px;align-items:center}
.mt-diag{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.09em;
  padding:0;min-height:44px;background:transparent;border:0;color:var(--dim);cursor:pointer;
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:5px}
.mt-diag:hover{color:var(--ink)}
.mt-diag:focus-visible{outline:1.5px solid var(--info);outline-offset:3px}
/* Verdicts read like a camera report: one leg per line, name then finding. */
.mt-diagout{display:flex;flex-direction:column;gap:3px;margin-top:4px;
  font-family:var(--mono);font-size:10px;line-height:1.5;color:var(--dim)}
.mt-diagout span{display:flex;gap:8px;align-items:baseline;justify-content:center;
  flex-wrap:wrap}
.mt-diagout em{font-style:normal;letter-spacing:.1em;text-transform:uppercase;
  font-size:8.5px;color:var(--dim)}
.mt-diagout span[data-ok="1"]{color:var(--ok, #4cae6e)}
.mt-diagout span[data-ok="0"]{color:var(--warn, #d08a3e)}

/* Control bar. Provenance sits in it on purpose: the claim that this is the
   real mountain has to travel with the picture, not live in a help page. */
.stagebar{position:static;z-index:auto;width:100%;
  display:flex;flex-direction:column;align-items:stretch;gap:0;pointer-events:auto}
.stagebar>*{pointer-events:auto}
/* The .prov stagebar caption is GONE — twice over. Nothing rendered it
   any more, and its mobile position:fixed rule lay in wait to hijack ANY
   element classed "prov": the card's avalanche source line spawned at
   the top-left of the stage in the owner's field screenshot (the same
   class-collision disease that once pinned the rose mid-screen). And by
   the owner's rule the screen never names a vendor or agency anyway —
   provenance lives in the API and the baked files. */
/* A dangling `body:not([data-terrain="ready"])` used to sit here, with no
 * declarations of its own. CSS does not discard such a fragment — it joins it
 * to the NEXT selector, so this rule silently became
 *
 *     body:not([data-terrain="ready"]) .views { display:none }
 *
 * and nothing in this codebase has ever set data-terrain. The guard was
 * therefore always true, and the toggle was hidden permanently — at
 * specificity (0,2,1), which outranks every `.views{display:flex}` below at
 * (0,1,0). A reader who reached the map had no control to get back to the
 * mountain, and the cause was a selector that looks like a comment.
 *
 * The view toggle only exists where both panes cannot be shown at once. On a
 * wide screen you get the mountain and the map together, so a switch would be
 * a control that does nothing. */
.views{display:none;background:rgba(8,13,22,.86)}

.sym{width:11px;height:11px;flex:0 0 auto;background:var(--c,#888);
  box-shadow:0 0 0 1px rgba(6,11,20,.85)}
.sym.circle{border-radius:50%}
/* DOUBLE BLACK IS TWO DIAMONDS, DRAWN.
   It was one rotated square with a second faked by a box-shadow in var(--c),
   which is never set on this rule — so it fell back to #111 on a panel that
   is almost #111, and expert-only rendered identically to most-difficult.
   Two runs of genuinely different difficulty had the same symbol in the key
   that exists to tell them apart. */
.sym.diamond,.sym.double{ position:relative; background:none; box-shadow:none;
  outline:0; height:12px }
.sym.diamond{ width:12px }
.sym.double{ width:19px }
.sym.diamond::before,.sym.double::before,.sym.double::after{
  content:''; position:absolute; top:1.5px; width:9px; height:9px;
  transform:rotate(45deg); background:#0b0f16; outline:1px solid #d2dae6 }
.sym.diamond::before{ left:1.5px }
.sym.double::before{ left:0 }
.sym.double::after{ left:8px }
.sym.lift{width:14px;height:0;background:0;border-top:2px solid #dbe6f2;box-shadow:none}
.sym.road{width:14px;height:0;background:0;border-top:3px solid #82878c;box-shadow:none}
.sym.water{width:14px;height:0;background:0;border-top:3px solid #7fb3cc;box-shadow:none}
.sym.lot{width:12px;height:8px;background:#2b2f36;border:1px solid #9aa3ab;border-radius:2px;box-shadow:none}


/* ---- Regime A: wide screen with a mouse ----
   Conditions on the left at full height, mountain above map on the right.
   This is the layout that answers "desktop feels barren": three live regions
   instead of one panel and a lot of empty map. */
@media(min-width:1024px) and (pointer:fine){
  .app{
    grid-template-columns:minmax(420px,34%) 1fr;
    grid-template-rows:minmax(0,1fr) minmax(190px,34%);
    grid-template-areas:"panel stage" "panel map";
  }
  .panel{grid-area:panel}
  .stage{grid-area:stage;border-top:0;border-left:1px solid var(--rule2)}
  .mapwrap{grid-area:map;border-top:1px solid var(--rule2);border-left:1px solid var(--rule2)}
}

/* ---- Regime B: narrow, still a mouse (a small window) ----
   Not enough height for three bands, so the mountain and the map share the
   upper region and the toggle appears. */
@media(max-width:1023px) and (pointer:fine){
  .app{grid-template-rows:minmax(0,1fr) minmax(0,44%);grid-template-columns:1fr}
  .stage,.mapwrap{grid-row:1;grid-column:1}
  .panel{grid-row:2;grid-column:1;border-top:1px solid var(--rule2)}
  .views{display:flex}
}

/* ---- Regime C: touch ----
   Mountain and map occupy the whole screen behind the conditions sheet, one at
   a time. The toggle is the lowest control on the display because it is the
   one pressed most, and the thumb arc runs out fastest at the top. */
@media (pointer:coarse){
  .app{grid-template-rows:1fr;grid-template-columns:1fr}
  .stage,.mapwrap{grid-row:1;grid-column:1;border-top:0;border-left:0}

  /* One rail, not three floating clusters. The view toggle leads because it
     is pressed most; the mountain actions sit beside it in the same shape and
     the same row, so the whole strip reads as one control surface. */
  /* No surface of its own: the de-boxing rule further down carries
     !important and strips it anyway. Declaring it here only described a panel
     that never renders. */
  .views{display:flex;position:static;border-color:var(--rule2);flex:0 0 auto}
  .views button{min-width:58px;min-height:56px;font-size:12px}

  /* Layer buttons and mountain actions never appear together — they belong to
     different views — so they share the row above the toggle. */
  /* Two rows of controls have to fit between the sheet and the top of the
     thumb arc. At 56px each that leaves almost nothing for gaps, so they are
     6px — enough to read as separate rows, tight enough that the upper row
     stays reachable at the peek snap. */
  .layers{bottom:68px;left:12px;right:72px;top:auto}
  /* POSITION FIRST, or none of the rest of this line applies.
   *
   * The base rule sets position:static — correct on a wide screen, where the
   * bar sits in the heads-up column and flows with it. Here it is meant to
   * float over the pane, and every property below depends on that: on a
   * static element `bottom`, `left`, `right` and `z-index` are all simply
   * ignored. So the bar stayed in normal flow with no stacking order of its
   * own, and .mapwrap — later in the document — painted straight over it. The
   * toggle was fully visible and completely untappable, which reads as a
   * frozen app rather than as a layout fault. */
  .stagebar{position:absolute;top:auto;bottom:12px;left:12px;right:72px;z-index:22;
    flex-direction:row;gap:8px;align-items:flex-end}
        .rctl{bottom:130px}

  /* Controls that belong to one pane are hidden when the other is showing.
     The first of these was self-nested — "map" and then "mountain" on the
     same body — so it never matched and the map's layer switcher stayed on
     screen over the mountain. */
  body[data-view="mountain"] .layers,
  body[data-view="mountain"] .rctl,
  body[data-view="mountain"] .mstat{display:none}

  /* A READOUT MUST NEVER SWALLOW A TAP.
   *
   * .mstat is a line of text reporting what the map is doing, and it overlaps
   * the corner the view toggle sits in. Being later in the document it won
   * the hit test, so in map view the toggle was fully visible and completely
   * unclickable — the worst kind of broken, because nothing looks wrong and
   * the reader concludes the app has frozen. Nothing that only reports gets
   * to intercept a press. */
  .mstat{ pointer-events:none }
}

/* Only one of the two panes is live in the stacked regimes. visibility rather
   than display: a hidden map that has never been laid out reports zero size,
   and MapLibre that resizes to zero comes back blank. */
@media(max-width:1023px), (pointer:coarse){
  body[data-view="mountain"] .mapwrap{visibility:hidden;pointer-events:none}
  body[data-view="map"] .stage{visibility:hidden;pointer-events:none}
  /* THE VIEW TOGGLE MUST SURVIVE ITS OWN PANE BEING HIDDEN.
   *
   * The 3D/MAP switch was grouped into the heads-up cluster, and that cluster
   * lives inside .stage — so hiding .stage in map view hid the only control
   * that gets you back OUT of map view. The rule that used to be here tried
   * to rescue it with pointer-events alone, which cannot work: a
   * visibility:hidden ancestor hides its children whatever their
   * pointer-events say. And the selector was self-nested — a body inside a
   * body — so it never matched anything in the first place. Two failures
   * stacked, and between them a reader who switched to the map had no way
   * back except reloading the page.
   *
   * visibility is explicitly restored, which is what actually brings it back. */
  body[data-view="map"] .stagebar{
    visibility:visible; pointer-events:auto;
  }
  /* Its ancestors inside the hidden stage have to be walked back too, or the
     bar is visible inside an invisible box. */
  body[data-view="map"] .hud,
  /* On the map the rail's marks are mountain controls, so they go; the top
     bar stays, because the view switch in it is the only way back and the
     scale still describes what is on screen. This used to be written as
     "everything except the stagebar", which stopped being true the moment
     the switch moved out of the rail and into the bar. */
  body[data-view="map"] .hud-stack{ visibility:hidden; pointer-events:none }
  body[data-view="map"] .hud-top{ visibility:visible }
}

/* Sheet-aware offsets for everything now living in the thumb arc. */
@media (pointer:coarse) and (orientation:portrait){
  .layers{bottom:calc(var(--sheet, 30svh) + 68px)}
  .stagebar{bottom:calc(var(--sheet, 30svh) + 12px)}
  .rctl{bottom:calc(var(--sheet, 30svh) + 140px)}

  /* The busy and empty states centre themselves in the canvas, and on a phone
     the bottom half of that canvas is behind the sheet — so the message that
     explains why there is no mountain was itself invisible. Only the tip of
     its icon showed above the sheet edge. Reserve the occluded strip. */
  .m3d-busy,.m3d-empty{padding-bottom:calc(var(--sheet, 30svh) + 84px)}
  /* The view toggle is how you reach the map at all, so it survives the
     middle snap; only a sheet pulled to full height takes it away, and at
     that point the map is entirely covered anyway. */
  body[data-sheet="full"] .layers{opacity:0;pointer-events:none;transition:opacity .18s ease}
}

/* ---- Regime C, landscape ----
   A phone on its side has no vertical room for a sheet, so conditions return
   to a column and the stage keeps the rest. Declared after the portrait rules
   because a media query carries no specificity of its own: without this the
   generic touch layout would drop all three regions into one cell. */
@media (pointer:coarse) and (orientation:landscape){
  .app{grid-template-columns:minmax(280px,42%) 1fr;grid-template-rows:minmax(0,1fr)}
  .panel{grid-row:1;grid-column:1;position:static;height:auto;
    border-radius:0;box-shadow:none;border-top:0;border-right:1px solid var(--rule2);padding:12px}
  .stage,.mapwrap{grid-row:1;grid-column:2}
  .grip{display:none}

  /* Every floating control moves out of the panel's column and into the
     stage's, where the thumb actually is when the phone is held sideways. */
  .stagebar{left:auto;right:12px;width:auto;bottom:12px}
  .layers{left:auto;right:12px;width:auto;bottom:76px}
  .rctl{left:44%;right:12px;bottom:140px}
}

/* ---- Place labels ----
   Broadcast weather graphics put the name beside a small tick, not inside a
   pill: the label has to sit on the terrain without covering it. */
.plc{display:flex;align-items:center;gap:6px;pointer-events:none;white-space:nowrap;
  transform:translateX(3px)}
.plc i{width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,.9);
  box-shadow:0 0 0 1.5px rgba(3,8,16,.85);flex:0 0 auto}
.plc span{font-size:12.5px;font-weight:600;letter-spacing:.01em;color:#eaf1f9;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 8px rgba(0,0,0,.8)}
@media(min-width:1024px){.plc span{font-size:13.5px}}

/* ---- Resort preview ----
   The point of the map: sweep the range, see where the snow is. */
.rprev{position:absolute;top:0;left:0;z-index:12;width:210px;pointer-events:auto;
  background:rgba(6,11,20,.94);backdrop-filter:blur(12px);
  border:1px solid var(--rule2);border-radius:var(--r);padding:9px 10px;cursor:pointer;
  box-shadow:0 10px 30px rgba(0,0,0,.6)}
.rprev header{display:flex;align-items:center;gap:8px;padding-bottom:7px;
  border-bottom:1px solid var(--rule)}
.rprev header svg{width:16px;height:16px;flex:0 0 auto;fill:#cfe2f5}
.rprev strong{display:block;font-size:12.5px;font-weight:650;color:var(--ink);line-height:1.25}
.rprev header span{display:block;font-family:var(--mono);font-size:9.5px;color:var(--dim)}
.rprev dl{display:grid;grid-template-columns:1fr 1fr;gap:6px 10px;margin-top:8px}
.rprev dt{font-family:var(--mono);font-size:8.5px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint)}
.rprev dd{font-family:var(--mono);font-size:15px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;line-height:1.15}
.rprev dd em{font-style:normal;font-size:9.5px;color:var(--dim);margin-left:1px}
.rprev dd .na{color:var(--faint);font-weight:400}
.rprev .pend{margin-top:7px;font-family:var(--mono);font-size:9px;color:var(--faint)}

/* ---- Controls without boxes ----
   Said three times and worth stating in the stylesheet: a control here is a
   word, not a container. The map is the object; chrome that draws its own
   outline competes with it. State is carried by weight and a lit underline —
   the same language broadcast graphics use for an active layer. */
.layers{background:transparent !important;border:0 !important;backdrop-filter:none !important;
  gap:2px}
.layers button{background:transparent !important;padding:8px 10px;color:#c3d1e0;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.85)}
.layers button + button{box-shadow:none !important}
.layers button.on{background:transparent !important}
.layers button.on::after{left:10px;right:10px;height:1.5px}

/* In the rail with the rest of the marks, wearing the rail's own law: a
   dark glyph with a bright halo, a full-width 38px target, no container. */
/* .ib is a 44px square by default, which is wider than the 48px rail once
   the rail's own padding is counted — it broke out of the column it had just
   joined. Sized to the rail like every other mark. */
/* .ib[data-hill] gives every header control a 56px glove target, which is
   right in the header and 8px too wide for a 48px rail — the mark broke out
   of the column it had just joined. In the rail the target is the full-width
   row instead, 48 by 38, the same as every other mark standing in it. */
.hud-stack #loc{position:static !important;box-sizing:border-box !important;
  width:100% !important;max-width:100%;min-width:0 !important;margin:0 !important;
  height:auto !important;min-height:38px !important;
  padding:3px 0 !important;display:flex;align-items:center;justify-content:center;
  background:transparent !important;border:0 !important;box-shadow:none !important;
  backdrop-filter:none !important;color:var(--mark) !important;
  filter:var(--markhalo)}
.hud-stack #loc svg{width:20px;height:20px;stroke-width:1.9}
.hud-stack #loc:hover,.hud-stack #loc.on{color:var(--sel) !important;
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}
#loc{background:transparent !important;border:0 !important;box-shadow:none !important;
  backdrop-filter:none !important;filter:drop-shadow(0 1px 3px rgba(0,0,0,.9))}

.views{border:0 !important;background:transparent !important;backdrop-filter:none !important}
.views button{background:transparent !important;color:#c3d1e0;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.85)}
.views button.on{background:transparent !important}
.views button.on::after{left:8px;right:8px;height:1.5px}

.mstat{background:transparent !important;border:0 !important;padding:4px 0;
  color:#9db0c6;text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.85)}
.rctl{background:rgba(6,11,20,.7) !important;border:0 !important;border-radius:0;
  box-shadow:none}

/* An inactive control still has to be readable — dimming it to near-invisible
   over dark terrain is not restraint, it is a control nobody can find. */
.layers button:not(.on),.views button:not(.on){opacity:.72}
.layers button.on,.views button.on{color:#7fd4ff !important;opacity:1}
#loc{color:#c3d1e0 !important}
.hud-stack #loc{color:var(--mark) !important}

/* ---- Run labels ----
   The name sits beside its difficulty mark, the way it does on a paper trail
   map, so the symbol and the word are read as one thing. */
.runlabels{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:4}
.rlab{position:absolute;top:0;left:0;display:flex;align-items:center;gap:5px;
  transform-origin:0 0;white-space:nowrap;
  font-size:11.5px;font-weight:600;color:#f2f7fc;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.9);
  margin:-7px 0 0 -4px}
.rlab i{width:8px;height:8px;flex:0 0 auto;box-shadow:0 0 0 1px rgba(0,0,0,.7)}
.rlab.d-novice i,.rlab.d-easy i{background:#00a651;border-radius:50%}
.rlab.d-intermediate i{background:#0072bc}
.rlab.d-advanced i{background:#111;transform:rotate(45deg)}
.rlab.d-expert i,.rlab.d-freeride i,.rlab.d-extreme i{background:#111;transform:rotate(45deg);
  box-shadow:5px 0 0 #111, 0 0 0 1px rgba(255,255,255,.35)}
@media(min-width:1024px){.rlab{font-size:12.5px}}

/* A ROAD NAME IS A BLADE, NOT A TAG. A run label floats over a hillside and
   is set to read against snow; a street name is a small enamelled plate on a
   post, and it reads as one — boxed, centred on the road, quieter than the
   runs it crosses so a trail map never loses to a service road. The class
   sets the weight: a numbered highway earns more presence than a lane behind
   the lodge. */
.roadlabels{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:3}
.roadlab{position:absolute;top:0;left:0;transform-origin:50% 50%;
  white-space:nowrap;font-size:10.5px;font-weight:600;letter-spacing:.06em;
  color:#eef4fa;background:rgba(21,32,44,.82);border:1px solid rgba(150,170,190,.42);
  border-radius:3px;padding:1px 5px;margin:-8px 0 0 0;translate:-50% 0}
.roadlab.rc-primary,.roadlab.rc-secondary,.roadlab.rc-trunk,.roadlab.rc-motorway{
  font-size:11.5px;background:rgba(24,44,34,.86);border-color:rgba(120,190,150,.5)}
.roadlab.rc-track,.roadlab.rc-service{font-size:9.5px;opacity:.9;
  background:rgba(30,28,24,.78);border-color:rgba(160,150,130,.35)}
.roadlab.rc-footway,.roadlab.rc-path,.roadlab.rc-pedestrian,.roadlab.rc-steps{
  font-size:9.5px;background:rgba(26,34,42,.72);border-style:dashed}
@media(min-width:1024px){.roadlab{font-size:11.5px}}

/* A SUMMIT LABEL IS THE TOP OF THE TYPE HIERARCHY. On a panoramic trail map
   the peak names are the largest thing on the mountain and everything else is
   subordinate to them, because they are what a reader orients the whole range
   by. Name over height, the height quieter, a small triangle mark, and set to
   read against a bright skyline rather than against snow. */
.peaklabels{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:5}
.peaklab{position:absolute;top:0;left:0;display:flex;flex-direction:column;
  align-items:center;white-space:nowrap;translate:-50% -100%;
  text-shadow:0 1px 3px rgba(0,0,0,.95), 0 0 12px rgba(0,0,0,.85)}
.peaklab i{width:0;height:0;margin-bottom:2px;
  border-left:5px solid transparent;border-right:5px solid transparent;
  border-bottom:8px solid rgba(238,246,255,.92);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9))}
.peaklab b{font-size:12.5px;font-weight:700;letter-spacing:.09em;color:#f4f9ff}
.peaklab em{font-size:10.5px;font-style:normal;font-weight:600;letter-spacing:.06em;
  color:#bcd0e4;margin-top:-1px}
@media(min-width:1024px){.peaklab b{font-size:14px}.peaklab em{font-size:11.5px}}

/* ---- Control rail, without collisions ----
   The toggles were absolutely positioned into the same band and landed on top
   of each other. One flex rail owns the row instead, so nothing can overlap
   whatever else is added later. */
.plclayer{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:6}
.plc{position:absolute;top:0;left:0;transform-origin:0 50%;margin-top:-9px}

.ground{display:none}
body[data-view="map"] .ground{display:flex}

@media (pointer:coarse){
  .stagebar{flex-wrap:wrap;gap:6px 8px;align-items:flex-end}
  .layers{position:static !important;width:auto !important;max-width:none !important}
  /* Layers belong to the map, so they ride in the same rail rather than
     floating in their own absolutely-positioned band above it. */
  body[data-view="mountain"] .layers{display:none}
}
@media (pointer:coarse) and (orientation:portrait){
  .layers{bottom:auto}
}
/* A run that lost the space for its name keeps the symbol that identifies it. */
.rlab.markonly{font-size:0}
.rlab.markonly i{width:9px;height:9px}

/* The view toggle is never hidden, at any sheet position. Losing the only
   route between the mountain and the map is not a state worth having. */
.views{opacity:1 !important;pointer-events:auto !important}

/* ---- One control language, everywhere ----
   Said several times and not yet fully done: a control here is a word or a
   mark, never a container. Any residual panel, border or fill on the stage
   controls is removed at the end of the sheet so nothing can reintroduce one. */
.seg,.views,.ground,.layers{
  background:transparent !important;
  border:0 !important;
  border-radius:0 !important;
  backdrop-filter:none !important;
  box-shadow:none !important;
  overflow:visible !important;
}
.seg button,.views button,.ground button,.layers button{
  background:transparent !important;
  border:0 !important;
  box-shadow:none !important;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.85);
}
.seg button + button{box-shadow:none !important}

/* The only "about" on the stage: a mark, not a paragraph. */
.about:hover,.about:active{color:var(--info);opacity:1}

/* Nothing floats in the middle of the picture. The rail owns the bottom-left,
   locate owns the bottom-right, and the stage between them stays clear. */
.stagebar{justify-content:flex-start}

/* THE 3D/MAP SWITCH BELONGS TO THE RAIL, NOT OVER IT.
   On the mountain it was absolutely positioned with no top and no bottom
   resolved, so it fell to its static position and painted straight across
   the skier and the pin beneath it — two controls covered by a third, which
   is exactly the overlap the owner reported. In flow it takes its own row
   and can never land on another mark again. The map view keeps the floating
   placement it was given deliberately, down where a thumb reaches. */
/* The view switch is no longer in the rail at all, so the rules that tried
   to make it fit one are gone. It lives in the top bar, horizontal, where a
   pair of words is the natural shape rather than an overflow. */


/* ---- Heads-up layer ---- */
/* ONE WIDTH FOR THE WHOLE RIGHT-HAND COLUMN.
   The cluster and the legend rail below it are the same object as far as the
   eye is concerned, and they were 104px and 132px — close enough to look like
   a mistake rather than a decision, because a rail wider than the housing it
   hangs from reads as a misalignment. One custom property now sets both, so
   they cannot drift apart again, and it is narrower than either was. */
/* THE INSTRUMENTS ARE MARKS, NOT A PANEL.
 *
 * Every version of this so far has been a housing: a translucent slab with
 * hairline dividers, and the compass, the legend mark and the scale sitting
 * inside it like rows in a table. Making that slab narrower and then more
 * opaque only made it read as a smaller, heavier box — which is the one thing
 * this surface was told not to be, four times over. A box is not a compass.
 *
 * So the housing is gone. What is left is a thin column of marks drawn
 * straight onto the mountain, each carrying its own shadow so it holds over
 * snow, over rock and over night sky. The compass keeps a disc behind its
 * dial, because a compass IS a disc — that is the instrument, not a container
 * around it. The column is 44px wide instead of 104: less than half, and it
 * stops looking like furniture. */
.hud{position:absolute;inset:0;pointer-events:none;z-index:7;
  --rail-w:44px; --legend-w:172px}
.hud > *{pointer-events:auto}

/* Compass. A rose and a letter — no dial housing, no bezel. */
.hud-compass svg{width:100%;height:100%;display:block}
.hud-compass .ring{fill:none;stroke:rgba(232,240,250,.28);stroke-width:1.2}
.hud-compass .north{fill:#ff6b6b}
.hud-compass .south{fill:rgba(232,240,250,.6)}

/* Scale bar, so relief has a size you can name. */
.hud-scale i{display:block;height:3px;background:rgba(232,240,250,.85);
  border-left:1px solid #e8f0fa;border-right:1px solid #e8f0fa;
  box-shadow:0 1px 2px rgba(0,0,0,.9);min-width:24px}
.hud-scale span{font-family:var(--mono);font-size:9.5px;font-weight:700;color:#dbe6f2;
  text-shadow:0 1px 2px rgba(0,0,0,.95)}

/* The legend folds away. Collapsed it is a single line; open it carries the
   conditions, the sun and the run key; full screen it gets the whole stage. */
/* Closed, only the mark shows. The words arrive with the panel. */
.legend2[data-open="true"] .l2-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 14px;padding:4px 12px 10px}
.l2-grid dt{font-family:var(--mono);font-size:8.5px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint)}
.l2-grid dd{font-family:var(--mono);font-size:14px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;line-height:1.2}
.l2-sun{display:flex;flex-direction:column;gap:2px;padding:8px 12px;
  border-top:1px solid rgba(120,145,175,.14);
  font-family:var(--mono);font-size:9.5px;color:var(--dim)}
.l2-runs{list-style:none;display:flex;flex-wrap:wrap;gap:5px 12px;padding:9px 12px;
  border-top:1px solid rgba(120,145,175,.14);font-size:10px;color:var(--dim)}
.l2-runs li{display:flex;align-items:center;gap:5px}
.l2-full{width:100%;min-height:40px;background:transparent;border:0;
  border-top:1px solid rgba(120,145,175,.14);cursor:pointer;
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--info)}

/* Full screen: the mountain takes the window, the sheet and map step aside. */
body[data-stage="full"] .app{grid-template-rows:1fr !important;grid-template-columns:1fr !important}
body[data-stage="full"] .stage{grid-row:1;grid-column:1;z-index:40}
body[data-stage="full"] .mapwrap,
body[data-stage="full"] .panel{display:none !important}
/* Also self-nested and therefore dead: the full-screen button never gained
   its "— exit" suffix, so the control that leaves full screen read exactly
   the same as the one that enters it. */
body[data-stage="full"] .l2-full::after{content:' — exit'}

/* A block was removed here. It had lost its declarations entirely — the
   selector ran straight into the closing brace — so it terminated the media
   query early and took the rule after it with it. Everything it was trying to
   say is already said correctly elsewhere: the 48px touch target at the
   .l2-full rule in the coarse-pointer block below, and the full-screen legend
   height at .legend2[data-open] further down. Deleting it loses nothing and
   stops the stylesheet parsing off a cliff. */

/* ================= Heads-up: one stack, top right =================
   Everything that floats over the mountain lives in a single column in the
   top-right corner. Nothing sits over the terrain a reader is studying, and
   nothing has to be dodged to see the runs. */
/* ---- THE TOP BAR ----
   The two controls that carry words, in the one band of the screen that was
   carrying nothing. Scale left, view switch right, the middle open so the
   summit is never behind a control. Horizontal, because both of them are:
   a word in a 48-pixel column overflows it, and overflowing it is how they
   ended up printed across the marks below. */
/* A breath of shade under the top bar. The bar sits over whatever the
   mountain happens to be doing up there — a run label, a lift line, bare
   snow — and a control has to be readable against all of them. Short and
   very light: enough to seat the bar, not enough to read as a header. */
.hud::before{content:'';position:absolute;left:0;right:0;top:0;height:64px;
  pointer-events:none;
  background:linear-gradient(180deg,rgba(6,12,22,.30),rgba(6,12,22,.10) 55%,transparent)}
.hud-top{position:absolute;top:var(--pad-y, 10px);left:var(--pad-x, 12px);
  right:var(--pad-x, 12px);z-index:9;display:flex;align-items:center;
  justify-content:space-between;gap:14px;min-height:26px;pointer-events:none}
.hud-top > *{pointer-events:auto;flex:0 0 auto}
.hud-top .views{position:static;display:flex;width:auto;border:0;
  background:none;border-radius:0}
.hud-top .views button{min-height:26px;min-width:0;padding:2px 7px;
  font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.1em;
  background:none;border:0;cursor:pointer;color:var(--labelink, #1b3048);
  text-shadow:var(--labelhalo)}
.hud-top .views button.on{color:var(--sel)}
.hud-top .views button.on::after{display:none}

.hud-stack{position:absolute;top:10px;right:10px;z-index:8;
  display:flex;flex-direction:column;align-items:center;gap:12px}

/* THE RAIL FITS THE STAGE, OR IT FOLDS.
   Measured at Solitude with ten marks in the rail: the column is 488 px
   tall, and the stage is 446 px on a 390 by 844 phone and 376 px on a
   1000 by 720 desktop. So the last two marks — the visor and the map —
   were drawn 53 and 123 px BELOW the foot of the picture, where the
   weather sheet covers them. That is the whole of the owner's report: the
   marks were not missing, they were outside the frame.

   Two ways to answer it, and only one keeps a mark pressable: shrink every
   mark until they fit, which at ten marks in 376 px is 33 px including the
   gaps and under the 44 px a thumb needs; or fold the column. It folds.
   A column flex box that is allowed to wrap starts a new column when it
   runs out of height, and wrap-reverse puts that column to the LEFT, which
   is the only side with picture on it: the rail is on the right edge. The
   marks keep their size and their order, and a stage tall enough for all
   ten sees exactly what it saw before, because a column that fits never
   wraps. */
.hud-stack{flex-wrap:wrap-reverse;
  max-height:calc(100% - var(--rail-top, 10px) - 10px);
  align-content:flex-start}
/* The container can be two columns wide now, so it can no longer be the
   thing that gives a mark its width: each mark carries the rail's width
   itself. */
.hud-stack > *{flex:0 0 auto;width:var(--rail-w, 44px)}

/* --- The instrument cluster ---
   Compass, legend mark and scale read as one small unit rather than three
   floating objects. Site colours throughout: the north needle is the same
   teal the brand mark uses, not a generic red. */
/* No surface. Legibility comes from each mark's own shadow, which is what
   the map world does for exactly this reason: a scrim over terrain hides the
   terrain, and terrain is the product. */
.hud-stack{background:none;border:0;box-shadow:none;backdrop-filter:none}

/* The compass is a glance, not an instrument panel: it answers "which way am
   I looking" and the answer is one needle. It used to fill the housing at
   44px with 24 ticks and four letters, none of which were legible at that
   size — the E, S and W were under six pixels tall, which is decoration
   pretending to be information. Now it is a 30px card with 16 ticks, one
   letter, and a north needle in the brand's teal, and every mark on it is
   large enough to actually read. The button stays a full-width, comfortable
   target; only the drawing shrank. */
.hud-compass{position:relative;width:100%;height:auto;padding:0;border:0;
  background:transparent;cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.hud-compass svg{width:38px;height:38px;display:block;
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.85))
         drop-shadow(0 0 3px rgba(244,250,255,.55))}
/* The face carries its own dark ground, so the compass is the one mark here
   that was already legible on snow. Deepened rather than lightened. */
.hud-compass .face{fill:rgba(7,13,23,.82)}
.hud-compass .ring{fill:none;stroke:rgba(214,230,247,.6);stroke-width:1.3}
.hud-compass .ticks line{stroke:rgba(203,220,240,.62);stroke-width:1.2}
.hud-compass .ticks .major{stroke:#eaf2fb;stroke-width:2}
.hud-compass .ticks,.hud-compass .rose{
  transform-origin:50% 50%;transition:transform .12s linear}
/* NORTH IS A TEAL TICK, NOT A LETTER.
   At a 24px dial a glyph is 7 pixels tall and its dark halo is 1.5 of them —
   it stopped reading as an N and started reading as a smudge. Colouring the
   north tick the same teal as the needle says the same thing at any size, and
   is the convention every compass already uses. The bearing in words stays on
   the button's own label for anyone who wants it read out. */
.hud-compass .ticks .n{stroke:#2dd4bf;stroke-width:3;stroke-linecap:round}
/* The brand's teal for north, the way the mark's weak-layer band reads. */
.hud-compass .north{fill:#2dd4bf}
.hud-compass .south{fill:rgba(224,235,248,.78)}
.hud-compass .hub{fill:#eaf2fb}

/* --- Legend mark --- */
.legend2-mark{width:100%;height:auto;min-height:38px;padding:2px;border:0;
  background:transparent;
  cursor:pointer;color:#e6eefa;display:flex;align-items:center;justify-content:center;
  filter:drop-shadow(0 2px 3px rgba(0,0,0,.85))}
.legend2-mark svg{width:21px;height:21px;fill:none;stroke:currentColor;
  stroke-width:2.1;stroke-linecap:round}
.legend2-mark{color:var(--mark);filter:var(--markhalo)}
.legend2-mark:hover{color:var(--sel)}
.legend2-mark.on{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}

/* --- Scale, inside the cluster --- */
/* Horizontal now, in the top bar: the bar and its figure side by side, the
   way a scale is drawn on every map ever printed. */
.hud-scale{display:flex;flex-direction:row;align-items:center;gap:6px;padding:0;
  filter:drop-shadow(0 0 1.5px rgba(244,250,255,.95))
         drop-shadow(0 0 3px rgba(244,250,255,.7))}
.hud-scale i{display:block;height:2px;background:var(--mark);
  border-left:1px solid var(--mark);border-right:1px solid var(--mark);
  min-width:20px;max-width:38px;flex:0 0 auto}
.hud-scale span{font-family:var(--mono);font-size:8.5px;font-weight:700;
  letter-spacing:.04em;color:var(--mark);white-space:nowrap}

/* --- The panel ---
   Anchored under the stack, never taller than the stage, and it can always be
   closed: the mark toggles it, the cross closes it, Minimise closes it, and
   Escape closes it. A panel with no visible way out is the thing that traps
   people, so there are four. */
.legend2{position:absolute;top:10px;right:114px;z-index:9;
  /* Instrument typography: every figure in the legend sits in a fixed-width
     numeral cell, so a refresh that turns 9 into 10 cannot make the whole
     column shiver. This is what separates a readout from a caption. */
  font-variant-numeric:tabular-nums;
  width:0;max-width:0;overflow:hidden;opacity:0;pointer-events:none;
  border-radius:var(--r);
  background:linear-gradient(180deg, rgba(10,16,26,.94), rgba(6,11,20,.97));
  backdrop-filter:blur(16px);border:1px solid rgba(120,145,175,.2);
  box-shadow:0 12px 34px rgba(0,0,0,.55);
  transition:opacity .16s ease}
.legend2[data-open="true"]{
  width:min(300px, calc(100vw - 132px));max-width:none;opacity:1;pointer-events:auto;
  /* Stops short of the control rail along the bottom: covering the 3D/MAP
     tabs would hide the way out of this view entirely. */
  max-height:calc(100% - 92px);display:flex;flex-direction:column}
.l2-head{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:9px 8px 9px 12px;border-bottom:1px solid rgba(120,145,175,.16);flex:0 0 auto}
.l2-title{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:#c3d1e0}
.l2-close{width:30px;height:30px;border:0;background:transparent;cursor:pointer;
  color:var(--dim);font-size:19px;line-height:1}
.l2-close:hover{color:var(--ink)}
.legend2-body{flex:1 1 auto;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch}
.l2-terrain{padding:9px 12px;border-top:1px solid rgba(120,145,175,.14);
  font-family:var(--mono);font-size:9.5px;line-height:1.5;color:var(--dim)}
/* The Source section: the integrity contract as two quiet lines. Station vs
   modelled is a colour the eye learns — measured holds the instrument teal,
   a model or forecast stays in the dim ink that says "estimate". */
.l2-scene{border-top:1px solid rgba(120,145,175,.14);padding-bottom:6px}
.sc-line{margin:0;padding:1px 10px;font-family:var(--mono);font-size:9.5px;
  line-height:1.55;color:var(--dim);font-variant-numeric:tabular-nums}
.sc-line.sc-station{color:var(--info)}
/* The land section: the Atlas's ownership summary as a short ledger —
   percent carries the ink, the owner reads in the dim, the authority is
   named underneath in the same quiet voice as every source line here. */
.l2-land{border-top:1px solid rgba(120,145,175,.14);padding-bottom:6px}
.ld-rows{list-style:none;margin:0;padding:0 10px}
.ld-rows li{display:flex;gap:8px;align-items:baseline;
  font-family:var(--mono);font-size:9.5px;line-height:1.7;
  font-variant-numeric:tabular-nums}
.ld-rows b{flex:0 0 34px;color:var(--ink);font-weight:700}
.ld-rows span{color:var(--dim);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ld-src,.ld-none,.ld-perm{margin:0;padding:3px 10px 1px;font-family:var(--mono);
  font-size:8.5px;line-height:1.5;color:var(--faint)}
/* A boundary that is not all there. Same size and family as the credit line
   above it, in the warning ink rather than the faint one: a reader looking at
   the edge of the ski area needs to know it is partial, not skim past it. */
.ld-part{margin:0;padding:3px 10px 1px;font-family:var(--mono);
  font-size:8.5px;line-height:1.5;color:var(--warn,#d08a3e)}
/* The brief is a word, not a box — same law as every control here. */
.l2-brief{display:block;width:100%;text-align:left;padding:7px 10px 9px;
  background:transparent;border:0;cursor:pointer;
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.08em;
  color:var(--info);text-decoration:underline;
  text-decoration-thickness:1.2px;text-underline-offset:4px}
.l2-brief:hover{color:var(--ink)}
.l2-brief[data-on="1"]{color:var(--warn,#d08a3e)}
.l2-brief:focus-visible{outline:1.5px solid var(--info);outline-offset:2px}
/* The stage switch: two words share a row; the chosen one carries the ink
   and the underline, the other waits in the dim. No boxes, ever. */
.l2-stage{border-top:1px solid rgba(120,145,175,.14)}
.st-row{margin:0;padding:2px 10px;display:flex;gap:18px}
.st-mode,.fr-mode,.rd-mode,.bt-mode{padding:6px 0;background:transparent;border:0;cursor:pointer;
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.08em;
  color:var(--dim)}
.st-mode[data-on="1"],.fr-mode[data-on="1"],.rd-mode[data-on="1"],.bt-mode[data-on="1"]{color:var(--ink);text-decoration:underline;
  text-decoration-thickness:1.2px;text-underline-offset:4px}
.st-mode:hover,.fr-mode:hover,.rd-mode:hover,.bt-mode:hover{color:var(--ink)}
.st-mode:focus-visible,.fr-mode:focus-visible,.rd-mode:focus-visible,.bt-mode:focus-visible{outline:1.5px solid var(--info);outline-offset:2px}
.st-note{margin:0;padding:0 10px 8px;font-family:var(--mono);font-size:8.5px;
  line-height:1.5;color:var(--faint)}
.l2-foot{display:flex;border-top:1px solid rgba(120,145,175,.16);flex:0 0 auto}
.l2-full,.l2-min{flex:1;min-height:42px;background:transparent;border:0;cursor:pointer;
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase}
.l2-full{color:var(--info)}
.l2-min{color:var(--dim);border-left:1px solid rgba(120,145,175,.16)}
.l2-build{margin:0;padding:3px 8px 4px;text-align:center;flex:0 0 auto;
  border-top:1px solid rgba(120,145,175,.14);
  font-family:var(--mono);font-size:8px;letter-spacing:.06em;color:var(--faint)}
/* The fault receipt. Amber, not red: the stage carried on, so this is a
   thing to report rather than a thing that stopped anybody. It only exists
   at all once something has thrown. */
.l2-fault{margin:0;padding:3px 8px 4px;flex:0 0 auto;
  border-top:1px solid rgba(230,170,90,.28);
  font-family:var(--mono);font-size:8px;letter-spacing:.04em;
  line-height:1.5;color:#e9b262;word-break:break-word}

@media (pointer:coarse){
  /* NARROW AND SLEEK. At 158px the key was taking better than a third of a
     phone screen for eight short words, and the owner was right that it read
     as a slab. The fat was in the edges and the leading, not the words: an
     8px gutter, a 6px symbol gap and 10px type hold every label on one line
     inside 120px, which is a quarter of the screen instead of two fifths. */
  .hud{--rail-w:48px; --legend-w:120px}
  .hud-stack{--rail-top:calc(var(--pad-y, 10px) + 34px);
    top:var(--rail-top);right:10px;gap:0}
  .hud-compass svg{width:42px;height:42px}
  .legend2-mark svg{width:23px;height:23px}
  /* The legend's own geometry is set once, with the rail, at the bottom of
     this file — including its coarse case. The width and height it used to be
     given here described the old wide panel and were dead the moment the rail
     landed. */
  .legend2{top:10px;bottom:auto}
}

body[data-stage="full"] .legend2[data-open="true"]{max-height:min(80vh, calc(100% - 20px))}

/* ---- The rail, as part of the instrument cluster ----
   Stacked, full width of the housing, divided by hairlines. The view toggle
   reads as the top of one instrument rather than a separate bar in another
   corner of the picture. */
.hud-stack{width:var(--rail-w)}
.hud-stack .seg{display:flex;width:100%;border-radius:0}
.hud-stack .seg + .seg{border-top:0}
.hud-stack .seg button{flex:1 1 0;min-height:30px;min-width:0;padding:0 2px;
  font-size:9px;letter-spacing:.06em;white-space:nowrap}
/* The map's ground toggle is meaningless while looking at the mountain. */
.hud-stack .ground{display:none}
body[data-view="map"] .hud-stack .ground{display:flex}
.hud-stack .layers{display:flex;flex-direction:column;width:100%;border-top:0}
.hud-stack .layers button{min-height:30px;padding:0 6px;font-size:9px;text-align:center}
.hud-stack .layers button + button{border-top:0}
.hud-stack .stagebar + .hud-compass{border-top:0}

/* The layer buttons belong to the map, the compass and scale to the mountain. */
body[data-view="map"] .hud-compass,
body[data-view="map"] .hud-scale{display:none}
body[data-view="mountain"] .hud-stack .layers{display:none}

@media (pointer:coarse){
  .hud-stack .seg button{min-height:32px;font-size:10px}
  .hud-stack .layers button{min-height:30px;font-size:9.5px}
}

/* ---- what did I just tap ---- */
/* Bottom-centre, clear of the rail on the right and the sheet below, and one
   line of identity over one line of fact. Ski patrol keeps its red. */
.m3d-callout{position:absolute;left:50%;bottom:calc(var(--sheet, 12px) + 14px);
  transform:translateX(-50%);z-index:9;max-width:min(420px, calc(100% - 32px));
  display:grid;grid-template-columns:1fr auto;gap:1px 10px;align-items:baseline;
  padding:9px 12px 10px;border-radius:var(--r);
  background:rgba(8,14,24,.9);backdrop-filter:blur(12px);
  border:1px solid rgba(150,175,205,.28);box-shadow:0 8px 26px rgba(0,0,0,.5);
  animation:co-in .22s var(--ease)}
@keyframes co-in{from{opacity:0;transform:translateX(-50%) translateY(8px)}}
.m3d-callout b{grid-column:1;font-size:14px;color:var(--ink);line-height:1.25}
.m3d-callout .co-kind{grid-column:1;font-family:var(--mono);font-size:9px;
  letter-spacing:.1em;text-transform:uppercase;color:#8fd4c8}
.m3d-callout.co-patrol .co-kind{color:#ff8a8a}
.m3d-callout .co-detail{grid-column:1;font-size:11.5px;color:var(--dim);line-height:1.35}
.m3d-callout .co-x{grid-column:2;grid-row:1;width:26px;height:26px;margin:-4px -6px 0 0;
  border:0;background:transparent;color:var(--dim);font-size:17px;cursor:pointer}
@media (prefers-reduced-motion:reduce){ .m3d-callout{animation:none} }

/* ---- the live position block in the legend ---- */
/* First in the panel because it is the reason the panel gets opened on a
   chairlift. Everything below it is context; this is the answer. */
.l2-fix{padding:9px 11px 10px;border-bottom:1px solid var(--rule2)}
.fx-where{margin:0 0 7px;font-size:12.5px;line-height:1.3}
.fx-run{font-weight:700}
.fx-run.diff-novice,.fx-run.diff-easy{color:#3ddc97}
.fx-run.diff-intermediate{color:#38bdf8}
.fx-run.diff-advanced,.fx-run.diff-expert,.fx-run.diff-freeride,.fx-run.diff-extreme{color:#dbe6f2}
.fx-off{font-weight:700;color:var(--warn)}
.fx-lift{font-weight:700;color:#2dd4bf}
.fx-near{color:var(--dim);font-size:11.5px}
.fx-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px 12px;margin:0}
.fx-grid div{display:flex;justify-content:space-between;gap:8px;min-width:0}
.fx-grid dt{font-family:var(--mono);font-size:9.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--mut)}
.fx-grid dd{margin:0;font-family:var(--mono);font-size:11.5px;color:var(--ink)}

/* ---- the ways out ---- */
/* The direction leads each row, because that is the word being scanned for.
   Somebody reading this has one glove off in the cold and is looking for
   "left" or "right", not for a trail name they may not know yet. */
.l2-ways{padding:9px 11px 10px;border-top:1px solid var(--rule2)}
.wy-head{margin:0 0 6px;font-family:var(--mono);font-size:9.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--mut)}
.wy-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.wy-row{display:grid;grid-template-columns:auto 1fr;gap:2px 9px;align-items:baseline}
.wy-dir{grid-row:1;font-size:12.5px;font-weight:700;color:var(--ink);white-space:nowrap}
.wy-name{grid-row:1;font-size:12.5px;color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wy-meta{grid-row:2;grid-column:1/-1;font-family:var(--mono);font-size:10px;color:var(--dim)}
.wy-meta i{font-style:normal}
.wy-meta i + i::before{content:' · '}
/* Difficulty colours the NAME, not a chip. The piste symbol is already a
   colour convention every skier reads without a legend. */
.wy-row.diff-novice .wy-name,.wy-row.diff-easy .wy-name{color:#3ddc97}
.wy-row.diff-intermediate .wy-name{color:#38bdf8}
.wy-row.diff-advanced .wy-name,.wy-row.diff-expert .wy-name,
.wy-row.diff-freeride .wy-name,.wy-row.diff-extreme .wy-name{color:#dbe6f2}

/* The reader's facing, as a second needle on the compass card.
   Teal, and distinct from the camera needle on purpose: they are different
   facts, and one needle showing whichever updated last would send somebody
   down the wrong side of a ridge. */
/* Sized in pixels against the compass card, not in percent. It is appended
   into the compass button, and until that button was made a positioned box
   this needle resolved against the whole cluster instead — centred on a
   column four times the height of the dial it was supposed to be reading. */
.hud-you{position:absolute;left:50%;bottom:50%;width:1.8px;height:15px;
  /* BOTTOM at the dial centre, not the top. Anchored by its top edge the
     needle hung BELOW the centre and then swung about a pivot 13px south
     of the hub — so a reader facing north got a needle pointing north from
     the wrong place, and one facing south got a needle swinging out of the
     dial entirely. bottom:50% puts the pivot and the hub on the same
     point, which is the only arrangement a compass needle can have. */
  margin-left:-.75px;transform-origin:50% 100%;pointer-events:none;
  background:linear-gradient(180deg,#2dd4bf,rgba(45,212,191,0));
  border-radius:1px;transition:transform .18s var(--ease)}
.hud-you::after{content:'';position:absolute;left:50%;top:-1.5px;width:3px;height:3px;
  margin-left:-1.5px;border-radius:50%;background:#2dd4bf}
/* After the rule it overrides, not before it. A media query adds no
   specificity, so the coarse block further up this file lost to the base rule
   that follows it and the larger dial kept the shorter needle. */
@media (pointer:coarse){ .hud-you{height:12px} }

/* ══════════════════════════════════ THE LEGEND AS A SLIM VERTICAL RAIL
 *
 * It was a 300px panel that opened sideways across the mountain — a card
 * covering the thing it describes, on the one screen the product is about.
 * The instrument cluster above it was already a narrow vertical column, so
 * the panel also broke the shape of its own corner: a thin rail, then a wide
 * slab hanging off its left.
 *
 * Now the whole right edge is ONE column. The legend drops straight down from
 * the cluster at the same width, so the mountain keeps its full frame and the
 * eye has a single vertical thing to read rather than an L.
 *
 * The narrowness forces a real discipline: at this width nothing fits in two
 * columns, so every row is one fact stacked over its label. That is the right
 * form for glancing anyway — a reader on a lift scans down a column, they do
 * not read a grid. When they genuinely want detail, Full screen is one press
 * away and can use the whole display, which is what that control is for.
 */
.legend2{ right:10px; }
/* Its own width. It used to be pinned to the cluster's, which only made
   sense while the cluster was a slab of the same shape — a column of marks
   has no edge to line a panel up against. */
.legend2[data-open="true"]{
  width:var(--legend-w);
  /* Below the cluster, not beside it. The offset is measured from the
     cluster's real height at runtime and published as --hud-h, because that
     height changes with the controls in it — hard-coding it here is how the
     rail ends up either overlapping the compass or floating away from it.
     The fallback covers the frame before the first measurement lands. */
  top:calc(10px + var(--hud-h, 196px) + 8px);
  /* Floored. Every term in here is measured at runtime, and on a short
     landscape phone they can add up to more than the stage is tall — at which
     point the rail computes a negative height and collapses to a hairline
     with no visible cause and no way for the reader to tell what happened.
     A floor turns the worst case into a short scrolling panel instead. */
  /* The floor is a LAST RESORT, not a preference.
     Set to 272px it beat the sheet-aware term — which had correctly worked out
     that only 177px were free — and drove the panel 57px underneath the
     weather sheet, hiding its own controls. The room that exists is the room
     there is; the key was made to fit it instead. */
  max-height:min(calc(100% - 72px),
                 max(168px, calc(100% - var(--hud-h, 140px) - 96px)));
}
/* Everything inside collapses to a single column. */
.legend2[data-open="true"] .l2-grid{
  grid-template-columns:1fr; gap:7px 0; padding:6px 10px 9px;
}
.l2-grid div{ display:flex; flex-direction:column; gap:1px }
.l2-grid dt{ font-size:8.5px; letter-spacing:.09em }
.l2-grid dd{ font-size:12px }

/* THE RUN KEY GOES VERTICAL TOO.
   It was a wrapping row, which at this width produced a ragged two-column
   block where a symbol could end up orphaned from its own words. One key per
   line, symbols in a column: the reader scans down the same edge they scan
   everything else on this rail down, and the colours line up as a legible
   scale from green to black instead of scattering. */
.l2-runs{ flex-direction:column; flex-wrap:nowrap; gap:5px; padding:1px 8px 7px;
  font-size:10px; line-height:1.12; color:var(--ink); border-top:0 }
.l2-key-h{ margin:0; padding:6px 8px 4px;
  font-family:var(--mono); font-size:8.5px; font-weight:700; letter-spacing:.11em;
  text-transform:uppercase; color:var(--faint) }
/* ---- the controls card ---- */
.l2-ctl{ list-style:none; margin:0; display:flex; flex-direction:column; gap:6px;
  padding:2px 8px 9px; font-size:9.5px; line-height:1.22; color:var(--dim) }
.l2-ctl li{ display:flex; align-items:center; gap:7px }
.l2-ctl svg{ width:17px; height:17px; flex:0 0 auto; fill:none;
  stroke:#9fb4cc; stroke-width:1.5; stroke-linecap:round }
.l2-ctl svg .tr{ stroke:var(--sel, #54b4ff) }
.l2-ctl svg circle[r="3"], .l2-ctl svg rect{ fill:rgba(159,180,204,.16) }
.kbd{ display:inline-flex; align-items:center; justify-content:center;
  min-width:15px; height:15px; padding:0 3px; border-radius:3px;
  border:1px solid rgba(159,180,204,.45); border-bottom-width:2px;
  font-family:var(--mono); font-size:8.5px; font-weight:700; color:#c6d6e8 }
.l2-ctl .kbd + .kbd{ margin-left:2px }
/* The set that matches the hand holding the device. Full screen shows both,
   because a touchscreen laptop is both kinds of reader at once. */
@media (pointer:coarse){ .l2-ctl.ctl-mouse{ display:none } }
@media (pointer:fine){ .l2-ctl.ctl-touch{ display:none } }
body[data-stage="full"] .l2-ctl.ctl-mouse,
body[data-stage="full"] .l2-ctl.ctl-touch{ display:flex }

/* The conditions block now follows the key, so it takes the rule. */
.l2-grid{ border-top:1px solid rgba(120,145,175,.14) }
.l2-runs li{ align-items:center; gap:6px }
.l2-runs .sym{ flex:0 0 auto }

/* Everything on the rail shares one 8px gutter. Mixed 10, 11 and 12px
   paddings on a 100px column are half its width in edges. */
.l2-sun{ padding:8px 8px; font-size:9px; line-height:1.5 }
.l2-terrain{ padding:8px; font-size:9px; line-height:1.45 }
.legend2[data-open="true"] .l2-grid{ padding:6px 8px 9px }
.l2-ways, .l2-fix{ padding:8px }
.l2-head{ padding:5px 4px 5px 10px }
.fx-grid dt{ font-size:8.5px }
.fx-grid dd{ font-size:11px }

.fx-grid{ grid-template-columns:1fr; gap:5px 0 }
.fx-grid div{ flex-direction:column; align-items:flex-start; gap:0 }
.fx-grid dt{ line-height:1.4 }
.fx-grid dd{ line-height:1.25 }

/* The ways out stack: direction on its own line, then the run, then the
   detail. At 132px a direction and a trail name cannot share a row, and
   splitting them is better than truncating either. */
.wy-row{ grid-template-columns:1fr; gap:0 }
.wy-dir{ grid-row:auto; font-size:11px; line-height:1.25; white-space:normal }
.wy-name{ grid-row:auto; white-space:normal; font-size:11px; line-height:1.25 }
.wy-meta{ grid-row:auto; grid-column:auto; font-size:9px; line-height:1.35;
  letter-spacing:-.01em }
/* Redundant here: the name above is already in its difficulty colour. */
.wy-meta .wy-diff{ display:none }
.wy-meta .wy-diff + i::before{ content:none }
.wy-list{ gap:8px }
.l2-ways{ padding:8px 10px 9px }
.fx-where{ font-size:11px; line-height:1.25; margin-bottom:6px }

/* Header and footer lose their spare width too. */
.l2-head{ padding:7px 6px 7px 10px }
.l2-title{ font-size:8.5px; letter-spacing:.08em }
.l2-close{ width:26px; height:26px; font-size:17px }
.l2-foot{ flex-direction:column }
.l2-full,.l2-min{ min-height:32px; font-size:9px; letter-spacing:.07em }
/* The rail drops Minimise. There were four ways out of this panel — the mark,
   the cross, Escape and this button — and on a 158px column the fourth costs
   a row of the key it exists to show. Three ways out is still three. */
.l2-min{ display:none }
body[data-stage="full"] .l2-min{ display:block }
.l2-min{ border-left:0; border-top:1px solid rgba(120,145,175,.16) }

@media (pointer:coarse){
  .legend2{ right:10px }
  .legend2[data-open="true"]{
    width:var(--legend-w);
    max-height:min(calc(100% - 64px),
                   max(168px, calc(100% - var(--sheet, 30svh) - var(--hud-h, 140px) - 44px)));
  }
  .l2-full,.l2-min{ min-height:38px }
}

/* Full screen is the escape hatch, and there the rail becomes a real panel —
   the one place there is room to spread out without covering anything. */
body[data-stage="full"] .legend2[data-open="true"]{
  width:min(340px, calc(100vw - 150px));
  max-height:min(82vh, calc(100% - 20px));
}
body[data-stage="full"] .legend2[data-open="true"] .l2-grid{ grid-template-columns:1fr 1fr; gap:8px 14px }
body[data-stage="full"] .fx-grid{ grid-template-columns:1fr 1fr }
body[data-stage="full"] .wy-row{ grid-template-columns:auto 1fr; gap:2px 9px }
body[data-stage="full"] .wy-dir,
body[data-stage="full"] .wy-name{ grid-row:1; white-space:nowrap }
body[data-stage="full"] .wy-meta{ grid-row:2; grid-column:1/-1; font-size:10px }
body[data-stage="full"] .wy-meta .wy-diff{ display:inline }
body[data-stage="full"] .wy-meta .wy-diff + i::before{ content:' · ' }
body[data-stage="full"] .l2-foot{ flex-direction:row }
body[data-stage="full"] .l2-min{ border-left:1px solid rgba(120,145,175,.16); border-top:0 }

/* ====================================================== THE GROUP LINK */
/* The panel is a decision surface, so it gets room the legend does not:
   what sharing means is read BEFORE the button that starts it.
   NAMED .glp, NOT .grp, the hard way: the weather card has used .grp for its
   own sections since the beginning, and the first name for this panel
   collided with it — which pinned the card's avalanche section fixed to the
   centre of the screen, over everything, on load. A selector namespace is a
   real namespace. */
.glp{position:fixed;z-index:60;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(340px, calc(100vw - 28px));max-height:min(84svh, 640px);
  display:flex;flex-direction:column;overflow:auto;
  background:linear-gradient(180deg, rgba(11,17,28,.97), rgba(7,12,21,.985));
  border:1px solid rgba(150,175,205,.3);border-radius:var(--r-lg);
  box-shadow:0 18px 60px rgba(0,0,0,.6);padding:0 16px 16px}
.glp .grp-h{display:flex;align-items:center;justify-content:space-between;
  padding:13px 0 9px;border-bottom:1px solid rgba(120,145,175,.16);margin-bottom:11px}
.glp .grp-h b{font-size:15px;letter-spacing:.02em}
.grp-x{width:30px;height:30px;border:0;background:transparent;color:var(--dim);
  font-size:20px;cursor:pointer;margin-right:-8px}
.grp-lede{margin:0 0 12px;font-size:12px;line-height:1.55;color:var(--dim)}
.grp-l{display:block;margin:0 0 10px;font-family:var(--mono);font-size:9px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.grp-row{display:flex;gap:6px}
.glp input{width:100%;margin-top:4px;padding:9px 10px;border-radius:var(--r-sm);
  border:1px solid rgba(120,145,175,.3);background:rgba(4,8,15,.6);
  color:var(--ink);font-size:13.5px;font-family:inherit}
#grpgen{flex:0 0 auto;width:38px;margin-top:4px;border-radius:var(--r-sm);
  border:1px solid rgba(120,145,175,.3);background:transparent;color:#7fd4ff;
  font-size:16px;cursor:pointer}
.grp-consent{margin:2px 0 10px;font-size:10.5px;line-height:1.5;color:#c9a26a}
.grp-start{width:100%;min-height:44px;border:0;border-radius:var(--r);
  background:#0f766e;color:#eafffb;font-size:13px;font-weight:700;
  letter-spacing:.03em;cursor:pointer}
.grp-err{margin:8px 0 0;font-size:11px;color:var(--warn)}
.grp-code{margin:0 0 8px;font-family:var(--mono);font-size:10px;color:var(--dim)}
.grp-code b{color:#7fd4ff;letter-spacing:.04em;user-select:all}
.grp-roster{list-style:none;margin:0 0 12px;padding:0;display:flex;
  flex-direction:column;gap:8px}
.grp-roster li{display:flex;flex-direction:column;gap:1px;padding:8px 10px;
  border-radius:var(--r-sm);background:rgba(120,145,175,.08)}
.grp-roster li b{font-size:13px}
.grp-roster li span{font-size:10.5px;color:var(--dim)}
.grp-roster li.sos{background:rgba(225,29,46,.16);outline:1px solid rgba(225,29,46,.55)}
.grp-roster li.sos b, .grp-roster li.sos span{color:#ffb3ba}
.grp-roster li.empty{color:var(--faint);font-size:11px}
.grp-sos{width:100%;min-height:46px;border-radius:var(--r);cursor:pointer;
  border:1.5px solid rgba(225,29,46,.6);background:transparent;color:#ff8a94;
  font-size:12px;font-weight:700;letter-spacing:.05em}
.grp-sos.armed{background:#b3121f;color:#fff;animation:sospulse 1.1s ease infinite}
@keyframes sospulse{50%{box-shadow:0 0 0 7px rgba(225,29,46,.25)}}
.grp-sosnote{margin:7px 0 0;font-size:10.5px;line-height:1.5;color:#ff8a94}
/* THE LINE TO PATROL, in its own three states. A call that was sent and a
   call that was received must never read the same, so acknowledged turns
   green and a dark or failed line turns amber: the reader is being told to
   pick up a phone, which is a different instruction from waiting. */
.grp-sosline{margin:7px 0 0;font-size:11px;line-height:1.5;color:#cfe0f5;font-weight:600}
.grp-sosline.has{color:#7ee0a2}
.grp-sosline.dark{color:#ffc46b}
.grp-reach{margin:8px 0 0;display:flex;flex-direction:column;gap:6px}
.grp-reach p{margin:0;font-size:10.5px;line-height:1.45;color:var(--dim)}
.grp-reach p b{font-family:var(--mono);color:var(--ink);font-variant-numeric:tabular-nums}
.grp-tel{display:flex;justify-content:space-between;align-items:baseline;gap:8px;
  padding:8px 10px;border-radius:9px;text-decoration:none;color:#fff;
  background:#b3121f;border:1px solid #d8303f}
.grp-tel span{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:#ffd0d4}
.grp-tel b{font-family:var(--mono);font-size:13px;font-variant-numeric:tabular-nums}
.grp-stop{width:100%;min-height:38px;margin-top:10px;border:0;cursor:pointer;
  border-radius:var(--r);background:transparent;color:var(--dim);
  border:1px solid rgba(120,145,175,.3);font-size:11px;letter-spacing:.04em}

/* The chip: sharing is never invisible. It sits by the rail, breathes, and
   one tap opens the panel where Stop lives. */
.grp-chip{position:absolute;z-index:9;left:10px;top:10px;display:flex;
  align-items:center;gap:6px;padding:6px 11px 6px 8px;border:0;cursor:pointer;
  border-radius:999px;background:rgba(8,14,24,.85);color:#9ff0e4;
  font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.08em;
  border:1px solid rgba(45,212,191,.4)}
.grp-chip i{width:7px;height:7px;border-radius:50%;background:#2dd4bf;
  animation:chipbreathe 2.2s ease infinite}
@keyframes chipbreathe{50%{opacity:.35}}
#grpbtn.on{color:var(--sel, #54b4ff)}   /* the one selected colour */

/* THE PARTY BUBBLES over the 3D world. Each linked member is a small
   glowing bubble in their own colour; tapping one expands the person's
   details. The host stays pointer-transparent so the mountain still
   drags; only the bubbles and the open card take the pointer. */
.party-tags{position:absolute;inset:0;pointer-events:none;z-index:6}
.ptag{position:absolute;left:0;top:0;transform:translate(-50%,-100%);
  pointer-events:auto;cursor:pointer;width:25px;height:25px;border-radius:50%;
  border:1.5px solid rgba(255,255,255,.85);background:var(--pc,#60a5fa);
  color:#fff;font-size:11px;font-weight:800;display:flex;align-items:center;
  justify-content:center;padding:0;font-family:inherit;
  box-shadow:0 0 0 2.5px color-mix(in srgb,var(--pc,#60a5fa) 20%, transparent),
             0 0 9px 1px color-mix(in srgb,var(--pc,#60a5fa) 60%, transparent);
  animation:bubglow 2.6s ease-in-out infinite}
@keyframes bubglow{
  50%{box-shadow:0 0 0 4px color-mix(in srgb,var(--pc,#60a5fa) 13%, transparent),
                 0 0 13px 2px color-mix(in srgb,var(--pc,#60a5fa) 55%, transparent)}}
.ptag.sos{--pc:#e11d2e;animation:sospulse 1.1s ease infinite}
.pcard{position:absolute;left:0;top:0;transform:translate(-50%,-100%) translateY(-40px);
  pointer-events:auto;min-width:172px;max-width:230px;background:rgba(8,14,24,.94);
  border:1px solid rgba(150,175,205,.4);border-radius:12px;padding:10px 12px;
  color:#e6eefa;font-size:12px;line-height:1.5;z-index:7;
  box-shadow:0 10px 30px -8px rgba(0,0,0,.6)}
.pcard b.pn{font-size:13.5px;display:block;margin-bottom:3px}
.pcard .pr{display:flex;justify-content:space-between;gap:12px;color:#b9cade}
.pcard .pr b{color:#fff;font-variant-numeric:tabular-nums}
.pcard .psos{color:#ff8a94;font-weight:700;margin-bottom:4px}
.pcard .px{position:absolute;top:4px;right:7px;background:none;border:0;
  color:#8fa3bd;font-size:14px;cursor:pointer;padding:2px}

/* ===================================================== THE VISOR */
/* Composed on the conventions first-person games settled on, adapted to
   a mountain instead of a firefight:
     - a SAFE AREA. Every cluster sits inside ~94% of the frame and clears
       the notch, because a HUD that touches the edge is a HUD that gets
       cut off on the next aspect ratio.
     - ZONES BY JOB. Identity top left, reference (conditions) top centre,
       alerts top right, live telemetry bottom left, navigation bottom
       right. Nothing in the middle: the centre is the mountain.
     - PERIPHERAL ENCODING. Corners are read out of the corner of the eye,
       where acuity is poor but contrast, size and motion read well — so
       the fastest-changing number (speed) is the biggest thing on screen
       and the guidance arrow moves rather than relying on fine type.
     - A TYPE FLOOR. Nothing under 11px, values 13px and up, because a
       label that is elegant on a desktop mock is unreadable on a phone
       in flat light with the screen half wet.
     - NO DECORATION. Plates and rules only where they carry contrast. */
/* THE SAFE AREA IS THE STAGE'S, NOT THE VISOR'S.
   These lived on .visor, and the visor's own fold control is a sibling of it
   rather than a child (it has to be, or "off" would fold away with everything
   else) — so the control resolved --pad-y to nothing, its top declaration was
   dropped whole, and it fell to its static position off the bottom of the
   stage. Declared on the stage layer instead: every instrument, inside the
   visor or beside it, measures from the same edges. */
.m3d{
  --pad-x:max(14px, env(safe-area-inset-left, 0px) + 10px);
  --pad-y:max(12px, env(safe-area-inset-top, 0px) + 8px);
  --pad-b:max(12px, env(safe-area-inset-bottom, 0px) + 8px)}
.visor{position:absolute;inset:0;pointer-events:none;z-index:5;
  font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;color:#eef4fb;
  --pad-x:max(14px, env(safe-area-inset-left, 0px) + 10px);
  --pad-y:max(12px, env(safe-area-inset-top, 0px) + 8px);
  --pad-b:max(12px, env(safe-area-inset-bottom, 0px) + 8px);
  /* How far under the layer rail the telemetry column starts. This is only
     the FALLBACK: visor.js measures the rail and writes the real figure
     inline, because a hand-counted clearance is a layout fault waiting for
     the next switch to be added (it was, and it landed the readings on top
     of the switches at 360px). Sized here for four targets so the fallback
     is never the tight one. */
  --tel-top:182px;
  /* THE HALO. Every word on this layer is read against a white mountain in
     full sun AND against a night sky, and no single ink survives both. A
     tight dark halo does: it gives light type an edge on snow and vanishes
     against dark. One declaration, used by every readout on the perimeter —
     the owner could not find these instruments, and a single blurred shadow
     was why. */
  --halo:0 1px 2px rgba(0,0,0,.98), 0 0 3px rgba(0,0,0,.95),
         0 0 10px rgba(0,0,0,.8)}
.visor [hidden]{display:none!important}
/* THE SCRIMS. Instrument type has to survive a white bowl in full sun.
   Two soft gradients darken only the top and bottom edges where the
   clusters live; the centre of the frame stays the mountain. */
.visor::before,.visor::after{content:'';position:absolute;left:0;right:0;
  pointer-events:none}
.visor::before{top:0;height:132px;
  background:linear-gradient(180deg,rgba(4,8,15,.72),rgba(4,8,15,.32) 48%,transparent)}
/* THE BOTTOM BAND IS CLEAR. It carried a heavy scrim because the telemetry,
   the bands and the run key all sat in it; none of them do now, and a dark
   wash over the snow the reader is standing on buys nothing. What is left is
   just enough to seat the weather card's edge. */
.visor::after{bottom:0;height:56px;
  background:linear-gradient(0deg,rgba(4,8,15,.42),transparent)}
.visor > *{position:relative;z-index:1}

/* ---- top left: where you are standing ---- */
/* No mark and no wordmark. The site header carries the brand; printing it
   again over the snow told the reader nothing they could not see by
   scrolling up, and on a narrow visor it was the ONLY thing here because
   the resort's name was the part that got hidden. Now the name is the
   type, and the region is what drops when there is no room. */
.vz-place{position:absolute;top:var(--pad-y);left:var(--pad-x)}
.vz-place b{display:block;font-size:13px;letter-spacing:.24em;
  text-shadow:var(--halo)}
.vz-place span{display:block;font-size:11px;letter-spacing:.18em;color:#e2ecf7;
  margin-top:3px;text-shadow:var(--halo)}

/* ---- top centre: the reference strip ---- */
.vz-strip,.vz-live{position:absolute;top:var(--pad-y);left:50%;
  transform:translateX(-50%);display:flex;
  background:rgba(6,10,18,.55);border:1px solid rgba(220,235,255,.24);
  border-radius:2px;backdrop-filter:blur(2px)}
.vz-live{background:rgba(6,10,18,.62);border-color:rgba(220,235,255,.3)}
.vz-cell{padding:7px 16px 6px;text-align:center}
.vz-cell + .vz-cell{border-left:1px solid rgba(220,235,255,.16)}
.vz-cell span{display:block;font-size:11px;letter-spacing:.18em;color:#b7c9de}
.vz-cell b{display:block;font-size:17px;margin-top:3px;font-weight:700;
  font-variant-numeric:tabular-nums}
.vz-cell.big{padding:8px 18px 7px}
.vz-cell.big b{font-size:22px}

/* ---- top right: the alert ---- */
.vz-right{position:absolute;top:calc(var(--pad-y) + 2px);right:56px;display:grid;
  justify-items:end;gap:8px;max-width:280px}
.vz-wxline{font-size:11px;letter-spacing:.1em;color:#eef4fb;text-align:right;
  text-shadow:var(--halo)}
.vz-avy{pointer-events:auto;cursor:pointer;background:none;border:0;padding:0 0 4px;
  color:#fff;font-family:inherit;font-size:12.5px;letter-spacing:.14em;
  font-weight:700;text-shadow:0 1px 4px rgba(0,0,0,.9);
  border-bottom:2px solid #facc15;display:inline-flex;align-items:center;gap:7px}
.vz-avy i{font-style:normal;font-size:11px;color:#facc15;text-shadow:none}
.vz-avy.lvl1{border-color:#4ade80}.vz-avy.lvl1 i{color:#4ade80}
.vz-avy.lvl3{border-color:#ff9c3d}.vz-avy.lvl3 i{color:#ff9c3d}
.vz-avy.lvl4,.vz-avy.lvl5{border-color:#ff5566}
.vz-avy.lvl4 i,.vz-avy.lvl5 i{color:#ff5566}

/* ---- left rail: the layer switches ----
   The same cell as every instrument below them: the mark on top, the word
   beneath. Boxes are gone for good — three containers stacked down the left
   were three arguments with the mountain behind them. Still a 38px target,
   because a gloved thumb has not got smaller. */
.vz-rail{position:absolute;left:var(--pad-x);top:104px;display:grid;gap:7px;
  width:56px}
.vz-rail button{pointer-events:auto;cursor:pointer;background:none;border:0;
  border-radius:0;padding:0;text-align:left;min-height:38px;
  display:grid;gap:2px;justify-items:start;font-family:inherit;
  color:var(--mark)}
.vz-rail button svg{width:17px;height:17px;fill:none;
  stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  filter:var(--markhalo)}
.vz-rail button em{font-style:normal;font-size:9px;letter-spacing:.12em;
  font-weight:700;color:var(--labelink);text-shadow:var(--labelhalo)}
.vz-rail button.on{color:var(--sel)}
.vz-rail button.on svg{filter:drop-shadow(0 1px 2px rgba(0,0,0,.85))
  drop-shadow(0 0 7px rgba(84,180,255,.8))}
.vz-rail button.on em{color:#0d4c7d;text-shadow:var(--labelhalo)}

/* ---- LEFT EDGE: THE INSTRUMENT COLUMN ----
   ONE CELL SHAPE, EVERYWHERE ON THIS LAYER: the word on top, the figure
   beneath it. Never the two side by side. A pair on one line sets its own
   width from the longest label plus the longest value, so the column was
   106 pixels of a 412-pixel screen: a fifth of the mountain spent on six
   short readings. Stacked, the same six live in 56, the eye reads straight
   down one narrow edge, and the picture gets the rest of the screen back.
   Smaller, too. These are read out of the corner of the eye, and peripheral
   vision answers to position and contrast long before it answers to size. */
.vz-tel{position:absolute;left:var(--pad-x);top:calc(104px + var(--tel-top));
  display:grid;gap:9px;justify-items:start;width:56px;
  /* never into the bottom band, whatever the readings add up to */
  max-height:calc(100% - 104px - var(--tel-top) - var(--pad-b) - 40px);
  overflow:hidden}
/* THE SPEEDOMETER. The same cell as the rest, given the one liberty the
   fastest-changing figure on the layer has earned: size, and the light.
   The track fills toward sixty, so speed reads without reading a digit,
   which is the whole job of an instrument in peripheral vision. Blue,
   luminous, quiet: the glow carries the eye, the track stays a hairline. */
.vz-spd{display:grid;gap:1px;justify-items:start;margin-bottom:3px}
.vz-spd span{font-size:9px;letter-spacing:.18em;font-weight:700;
  color:var(--labelink);text-shadow:var(--labelhalo)}
.vz-spd b{font-size:27px;font-weight:800;line-height:.92;
  font-variant-numeric:tabular-nums;letter-spacing:-.03em;color:#cbe6ff;
  /* the dark edge first so the figure has a shape on snow, THEN a small
     blue lift so it reads as lit rather than as a glowing blob */
  text-shadow:0 1px 2px rgba(0,0,0,.98), 0 0 3px rgba(0,0,0,.92),
              0 0 9px rgba(84,180,255,.38)}
.vz-spd-track{width:48px;height:2px;margin-top:3px;background:rgba(6,12,22,.62);
  box-shadow:0 0 0 .5px rgba(0,0,0,.45);overflow:hidden}
.vz-spd-track i{display:block;height:100%;
  background:linear-gradient(90deg,#3f8fdc,#7cc4ff);
  box-shadow:0 0 8px rgba(124,196,255,.85);
  transition:width .18s linear}
/* Every other reading is the same cell, without the light. */
.vz-line{display:grid;gap:0;justify-items:start}
.vz-line span{font-size:9px;letter-spacing:.14em;font-weight:700;
  color:var(--labelink);line-height:1.25;text-shadow:var(--labelhalo)}
.vz-line b{font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums;
  line-height:1.2;color:#f2f8ff;text-shadow:var(--halo)}

/* ---- the world, named: label over leader line over dot ---- */
.vz-tags{position:absolute;inset:0}
.vz-tag{position:absolute;left:0;top:0;transform:translate(-50%,-100%);
  pointer-events:auto;cursor:pointer;background:none;border:0;padding:0;
  display:flex;flex-direction:column;align-items:center;gap:0;
  font-family:inherit;--tc:#6fd3ff}
.vz-tag em{font-style:normal;font-size:11.5px;font-weight:700;
  letter-spacing:.16em;color:var(--tc);white-space:nowrap;
  text-shadow:var(--halo);padding-bottom:5px}
.vz-tag i{width:1px;height:26px;background:var(--tc);opacity:.9}
.vz-tag u{width:7px;height:7px;border-radius:50%;background:var(--tc);
  text-decoration:none;box-shadow:0 0 6px var(--tc)}
.vz-tag.patrol{--tc:#ff5964}
.vz-tag.lodge{--tc:#e8b07a}
.vz-tag.station{--tc:#6fd3ff}

/* ---- bottom right: navigation ---- */
.m3d:has(.destcard) .vz-way{display:none}
.vz-way{position:absolute;right:56px;top:calc(var(--pad-y) + 150px);
  pointer-events:auto;cursor:pointer;background:none;border:0;padding:0;
  text-align:right;font-family:inherit;color:#eef4fb;display:grid;gap:3px;
  justify-items:end}
.vz-way span{font-size:10.5px;letter-spacing:.2em;color:#dceafa;
  text-shadow:var(--halo)}
.vz-way b{font-size:13px;letter-spacing:.08em;text-shadow:var(--halo)}

/* ---- RIGHT EDGE: the band temperatures, one line each ----
   They ran across the bottom centre. Same law as the telemetry: off the
   bottom, onto an edge, one reading per line. */
.vz-bands{position:absolute;right:56px;top:calc(var(--pad-y) + 76px);
  display:grid;gap:1px;justify-items:end;
  font-size:12px;font-weight:700;letter-spacing:.1em;text-shadow:var(--halo)}
.vz-bands span{display:flex;align-items:baseline;gap:7px;min-height:18px}
.vz-bands i{font-style:normal;font-size:9.5px;letter-spacing:.16em;color:#c2d6ea}
.vz-bands b{font-size:13px;font-variant-numeric:tabular-nums}
.vz-bands .b1{color:#8ef0b3}.vz-bands .b2{color:#8fc6ff}.vz-bands .b3{color:#d7c2ff}

/* ---- the fold control ---- */
/* It was a bordered plate reading VISOR ON, parked over the middle of the
   right-hand side of the mountain: the loudest thing on the layer, saying
   the least. The visor's own mark now, lit when the instruments are up. */
/* Under the rail, measured, never guessed. It used to sit at a hard offset
   and landed on whichever mark happened to be at that height — the locate
   mark, once locate joined the rail. The rail publishes its real height. */
/* IN THE RAIL, not ten pixels under it. This was absolutely positioned by
   a computed offset that landed it just below the rail's last mark, at a
   different width and a different inset — so it read as a stray control
   floating on the mountain (owner recording, 2026-08-31). It is a member of
   the rail now and inherits the rail's geometry, which is what the comment
   beside it in visor.js claimed all along. */
.vz-pill{position:static;width:100%;height:auto;min-height:38px;
  pointer-events:auto;cursor:pointer;background:none;border:0;padding:3px 0;
  display:flex;align-items:center;justify-content:center;color:var(--sel)}
.vz-pill svg{width:21px;height:21px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.85))
         drop-shadow(0 0 7px rgba(84,180,255,.75))}
.vz-pill.off{color:var(--mark)}
.vz-pill.off svg{filter:var(--markhalo)}

/* Dwell select: a resting pointer fills the control's underline.
   Drawn as a background layer, NOT a positioned pseudo-element — an
   earlier version set position:relative on every [data-dwell] control,
   which silently un-anchored the visor's absolutely-positioned corner
   clusters and threw the whole layout off the stage. */
[data-dwell].dwelling{
  background-image:linear-gradient(#6fd3ff,#6fd3ff);
  background-repeat:no-repeat;background-position:left bottom;
  background-size:0 2px;animation:dwellfill 1.1s linear forwards}
@keyframes dwellfill{to{background-size:100% 2px}}

/* COMPACT is decided by the STAGE's width, not the window's: the 3D pane
   shares the screen with the weather panel, so the viewport lies about
   the room available. Nothing shrinks below the readable floor; the
   least useful clusters step aside instead. */
.visor.compact .vz-place span{display:none}
.visor.compact .vz-cell{padding:6px 10px 5px}
.visor.compact .vz-cell b{font-size:14px}
.visor.compact .vz-cell span{font-size:9.5px;letter-spacing:.12em}
.visor.compact .vz-wxline{display:none}
.visor.compact .vz-bands{display:none}
.visor.compact .vz-rail{top:86px}
.visor.compact .vz-tel{top:calc(86px + var(--tel-top));
  max-height:calc(100% - 86px - var(--tel-top) - var(--pad-b) - 34px)}
.visor.compact .vz-way{top:calc(var(--pad-y) + 128px)}
.visor.compact .vz-way b{font-size:12px}

/* ============================================== NAVIGATION LAYER */
/* the destination card: distance, vertical, an honest time */
.destcard{position:absolute;right:12px;bottom:56px;z-index:8;min-width:196px;
  max-width:250px;background:rgba(6,10,18,.9);border:1px solid rgba(220,235,255,.24);
  border-radius:2px;padding:9px 12px 8px;color:#e6eefa;font-size:12.5px;line-height:1.35;
  font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  box-shadow:0 12px 34px -10px rgba(0,0,0,.65)}
.destcard .dn{font-size:13.5px;display:block;padding-right:18px;line-height:1.2}
.destcard .dk{display:block;font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--route-ink);margin:2px 0 5px}
.destcard .drow{display:flex;justify-content:space-between;gap:12px;color:#b9cade}
.destcard .drow b{color:#fff;font-variant-numeric:tabular-nums}
.destcard .dnote{margin-top:4px;font-size:9.5px;color:#8fa3bd;line-height:1.35}
.destcard .dx{position:absolute;top:6px;right:9px;background:none;border:0;
  color:#8fa3bd;font-size:15px;cursor:pointer;padding:2px}
/* THE PIN DROP. Anchored by its TIP — the element's origin is the projected
   place, and everything is drawn upward from there, so the point of the pin
   is on the spot at every zoom. Fixed pixel size for the same reason: a
   marker that scales with the world is a speck from the trail-map frame and
   a billboard from the chairlift. Orange, because that is the route's colour
   and this is where the route ends. */
.desttag{position:absolute;left:0;top:0;transform:translate(-50%,-100%);
  pointer-events:none;z-index:7;display:flex;flex-direction:column-reverse;
  align-items:center;gap:3px}
.desttag .dt-pin{width:21px;height:30px;display:block;
  fill:var(--route);stroke:rgba(10,6,2,.7);stroke-width:1.2;
  filter:drop-shadow(0 2px 4px rgba(0,0,0,.75))
         drop-shadow(0 0 9px rgba(224,138,69,.5))}
.desttag .dt-pin circle{fill:#1a0f04;stroke:none}
.desttag .dt-n{font-size:10.5px;font-weight:700;letter-spacing:.16em;
  white-space:nowrap;text-transform:uppercase;color:var(--route-ink);
  font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  text-shadow:0 1px 2px rgba(0,0,0,.98),0 0 3px rgba(0,0,0,.95),
              0 0 10px rgba(0,0,0,.8)}

/* THE EMPTY ANSWER. A legend row pressed on a mountain whose survey has none
   of that thing used to do nothing at all, which reads as a broken control.
   One line, on the stage, gone in a few seconds. */
.catempty{position:absolute;left:50%;top:16%;transform:translateX(-50%);z-index:9;
  margin:0;pointer-events:none;max-width:min(320px, 84%);text-align:center;
  font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  font-size:11.5px;line-height:1.4;letter-spacing:.04em;color:#e6eefa;
  background:rgba(6,11,20,.88);border:1px solid rgba(120,145,175,.28);
  border-radius:2px;padding:8px 12px;
  animation:catfade .25s ease both}
@keyframes catfade{from{opacity:0;transform:translateX(-50%) translateY(-4px)}}

/* the interactive legend's lit-up category marks */
.cat-marks,.patrol-marks{position:absolute;inset:0;pointer-events:none;z-index:6}
.cmark{position:absolute;left:0;top:0;transform:translate(-50%,-100%);
  pointer-events:auto;cursor:pointer;width:21px;height:21px;border-radius:50%;
  border:1.5px solid rgba(255,255,255,.85);background:#1478c9;color:#fff;
  font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;
  padding:0;font-family:inherit;
  box-shadow:0 0 0 2.5px rgba(84,180,255,.18),0 0 8px 1px rgba(84,180,255,.5)}
.cmark.patrol{background:#c8102e}
.cmark.lodge{background:#8a5a2b}
.cmark.lot{background:#3d4c63}
.cmark.station,.cmark.lift{background:#1d4f8f}
.cmark.peak{background:#43536b}

/* ski patrol hovers on its own, always findable */
/* The cross is a marker on a mountain, not a beacon over it. It was 20px,
   full red, ringed in white, glowing and bobbing, and a handful of them
   dominated the snow they were supposed to sit on. Smaller, quieter, no
   glow and no animation; it is only on screen while the Ski patrol row is
   lit, so it does not have to shout to be found. */
.fmark-patrol{position:absolute;left:0;top:0;transform:translate(-50%,-100%);
  pointer-events:auto;cursor:pointer;width:15px;height:15px;border-radius:5px;
  border:1px solid rgba(255,255,255,.85);background:rgba(190,26,50,.92);color:#fff;
  display:flex;align-items:center;justify-content:center;padding:0}
.fmark-patrol i{font-style:normal;font-size:11px;font-weight:800;line-height:1;display:block}

/* legend rows that drive the map */
.l2-runs li[data-cat]{cursor:pointer;border-radius:7px;padding:2px 4px;margin:0 -4px}
.l2-runs li[data-cat]:hover{background:rgba(120,170,230,.12)}
.l2-runs li[data-cat].lit{background:rgba(84,180,255,.18)}

@media (max-width:640px){
  .destcard{right:8px;bottom:52px;max-width:210px;font-size:12px}
}

/* ================================================= FIRST PERSON */
/* THE RAIL, LEGIBLE OVER SNOW.
   These marks sit on a white mountain in full sun, and a thin grey stroke
   with a soft shadow disappears into it — the owner could not find them.
   The ink comes up to near-white and every mark carries a hard black halo
   underneath, which is the only thing that survives both a snowfield and a
   night sky without putting a box around the control. */
.fp-mark,.cc-mark{width:100%;height:auto;min-height:38px;
  display:flex;align-items:center;justify-content:center;
  padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.fp-mark svg,.cc-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
/* ONE SELECTED COLOUR, AND IT IS THE BLUE.
   Selected state used to speak two languages: the walking figure and the
   group mark turned teal, everything else turned blue. Owner's call, and
   the right one — the blue is the product's, the teal was borrowed. Every
   lit control in this rail is now the same blue with the same halo. */
.fp-mark:hover,.cc-mark:hover{color:var(--sel)}
.fp-mark.on,.cc-mark.on{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}

/* THE FRAMING MARK. Same rail, same grammar as every other mark on it:
   no surface, its own halo for legibility, the one blue when lit. The
   caption is the guidance arrow's trick — a mark that changes shape needs
   a word under it or the reader is guessing which of four they are in. */
.cam-mark{width:100%;height:auto;min-height:38px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.cam-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.cam-mark .cm-c{font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:8.5px;
  letter-spacing:.1em;font-weight:700;color:currentColor;
  text-shadow:var(--labelhalo)}
.cam-mark:hover{color:var(--sel)}
/* Out of the body IS a selected state: the rail should say so at a glance,
   because a reader who cannot find their way back to their own eyes is
   lost in their own app. */
.m3d[data-framing="CHASE"] .cam-mark,
.m3d[data-framing="WIDE"] .cam-mark,
.m3d[data-framing="OVERHEAD"] .cam-mark{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}
/* The mark belongs to the walk. No walk, no framing to choose. */
.cam-mark{display:none}
.m3d[data-fp="1"] .cam-mark{display:flex}

/* THE KIT MARK. Same rail, same grammar. Like the ride mark it names what
   the next press does, so it is never lit as a state: it is an offer. */
.kit-mark{width:100%;height:auto;min-height:38px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.kit-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.kit-mark .km-c{font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:8.5px;
  letter-spacing:.1em;font-weight:700;color:currentColor;text-shadow:var(--labelhalo)}
.kit-mark:hover{color:var(--sel)}
.kit-mark{display:none}
.m3d[data-fp="1"] .kit-mark{display:flex}

/* THE RIDE CONTROL. Only while there is a chair under you, and it says
   what the next press does rather than what state you are in — a control
   that names its own state reads as a label, not a button. */
.ride-mark{width:100%;height:auto;min-height:38px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.ride-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ride-mark .rm-c{font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:8.5px;
  letter-spacing:.1em;font-weight:700;color:currentColor;text-shadow:var(--labelhalo)}
.ride-mark:hover{color:var(--sel)}
/* Staying on is the chosen state, so it is lit — the one blue, like every
   other selected mark on this rail. */
.m3d[data-riding="through"] .ride-mark{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}

/* ================================================ THE TRAIL MAP
   Drawn from the survey, so it is the same mountain you are standing on
   rather than a second source of truth about it. The full map takes the
   stage; the corner window is the one you leave up while you ski. */
.tmap{position:absolute;inset:0;z-index:26;background:rgba(6,10,17,.55);
  backdrop-filter:blur(3px)}
.tmap .tm-cv{position:absolute;inset:0;width:100%;height:100%;
  touch-action:none;cursor:grab}
.tmap .tm-cv:active{cursor:grabbing}
.tm-x{position:absolute;top:10px;right:10px;width:38px;height:38px;
  border:0;background:transparent;color:var(--mark);font-size:22px;
  line-height:1;cursor:pointer;filter:var(--markhalo)}
.tm-x:hover{color:var(--sel)}
.tm-z{position:absolute;right:10px;top:56px;display:flex;flex-direction:column;gap:2px}
.tm-z button{width:38px;height:38px;border:0;background:transparent;
  color:var(--mark);font-size:17px;font-weight:700;cursor:pointer;
  filter:var(--markhalo)}
.tm-z button:hover{color:var(--sel)}
/* THE KEY. A trail map without one is a coloured drawing: the whole point
   of the colours is that they mean something specific and standard. */
/* The key sits on its own panel. It wore the white label halo, which is the
   right treatment for a name over bright snow and the wrong one over a dark
   map: a white glow around white letters at nine and a half pixels is not a
   halo, it is a smear, and the owner read it as blurry. Flat panel, no
   glow, a size that can actually be read on a phone. */
.tm-key{position:absolute;left:12px;bottom:12px;display:flex;flex-wrap:wrap;
  gap:5px 14px;max-width:min(74%,520px);
  padding:7px 10px;border-radius:10px;
  background:rgba(8,13,22,.86);border:1px solid rgba(140,170,210,.16);
  font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:10.5px;
  letter-spacing:.06em;font-weight:700;color:#e6eefa;text-shadow:none}
.tm-key span{display:flex;align-items:center;gap:6px}
.tm-key i{width:12px;height:4px;border-radius:2px;display:block}
.tm-key i.lift{background:none;border-top:2px dashed #c9d6e6;height:0}
.tm-key svg.you{width:13px;height:13px;display:block;flex:0 0 auto}
/* The corner window: small, out of the thumb arc, and never over the rail. */
.tmini{position:absolute;left:10px;bottom:calc(var(--sheet, 12px) + 12px);
  width:132px;height:132px;z-index:9;border-radius:14px;overflow:hidden;
  box-shadow:0 6px 22px rgba(0,0,0,.55)}
.tmini .tm-cv{width:100%;height:100%;display:block}
@media (pointer:coarse) and (orientation:portrait){
  .tmini{width:112px;height:112px}
}
/* The rail's map mark, in the rail's own grammar. */
.map-mark{width:100%;height:auto;min-height:38px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.map-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.map-mark .mm-c{font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:8.5px;
  letter-spacing:.1em;font-weight:700;color:currentColor;text-shadow:var(--labelhalo)}
.map-mark:hover{color:var(--sel)}
.m3d[data-map="open"] .map-mark,
.m3d[data-minimap="1"] .map-mark{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}
.map-mark{display:none}
.m3d[data-fp="1"] .map-mark{display:flex}

/* What you face, named. No bubble: letter-spaced type on the view with
   a hairline under it, the same grammar as every other visor readout. */
.fp-face{position:absolute;left:50%;top:15%;transform:translateX(-50%);z-index:4;
  padding:0 0 5px;border-radius:0;pointer-events:none;background:none;
  border:0;border-bottom:1px solid rgba(220,235,255,.5);
  color:#f4f9ff;font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  font-size:11.5px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;
  white-space:nowrap;text-shadow:0 1px 5px rgba(0,0,0,.9),0 0 14px rgba(0,0,0,.7);
  opacity:0;transition:opacity .3s ease}
/* The skier is the way in AND the way out. While the walk is on it burns
   bright with a soft halo — an unambiguous "you are this mode, tap to
   leave" — instead of a floating bubble parked over the world. */
/* Walking, the map furniture steps aside: the rail stays (it holds the way
   out), the run labels go — they are map objects, not world objects. */
[data-fp="1"] .runlabels{display:none}
/* And the control strip goes entirely: 3D/MAP, layers and basemap are map
   decisions, not walk actions — and the strip was physically covering the
   walker mark, the one control that leaves the walk. Nothing may overlay
   the way out. */
body[data-fp="1"] .stagebar{display:none}
/* THE BRAND AND THE SCALE WANTED THE SAME CORNER.
   The visor puts its identity at the top left; the top bar puts the scale
   there too, and in first person the two printed straight through each
   other. The bar steps down below the brand while the walk is on, and the
   view switch goes with the rest of the map furniture — the way out of the
   walk is the skier mark, not a view toggle. */
/* On the snow the top bar stands down altogether. The visor puts its own
   identity in that corner, and the two printed straight through each other;
   and neither of the bar's two controls belongs to a skier anyway. A map
   scale is a map instrument — standing on the mountain, distance is already
   on the perimeter, in feet and miles — and the way out of the walk is the
   skier mark, not a view toggle. */
body[data-fp="1"] .hud-top{display:none}
body[data-fp="1"] .hud::before{display:none}

/* ---- the day, in the legend ---- */
.l2-today{border-bottom:1px solid rgba(120,145,175,.14)}
.td-grid{display:grid;grid-template-columns:1fr;gap:6px 0;margin:0;padding:2px 10px 9px}
.td-grid div{display:flex;flex-direction:column}
.td-grid dt{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint)}
.td-grid dd{margin:0;font-family:var(--mono);font-size:12px;font-weight:600;
  color:var(--ink);font-variant-numeric:tabular-nums}
.td-last{list-style:none;margin:0;padding:0 10px 10px;display:flex;
  flex-direction:column;gap:4px}
.td-last li{display:flex;gap:7px;font-size:9.5px;color:var(--dim);
  font-family:var(--mono)}
.td-last b{color:#7fd4ff}

/* The measured day, withheld. A calm ask in the legend's own voice, never a
   wall: it appears only once there is a day behind it. See hud.js setToday. */
.td-lock{padding:2px 10px 11px}
/* The day that never started. Warned, not dimmed: a reader who is skiing
   needs to know the count is not running, not to skim past it. */
.td-off{margin:0;padding:2px 10px 11px;font-size:10.5px;line-height:1.5;
  color:var(--warn,#d08a3e)}
.td-lock p{margin:0 0 8px;font-size:10.5px;line-height:1.5;color:var(--dim)}
.td-lock b{color:var(--ink);font-weight:600}
.td-go{display:inline-block;padding:5px 11px;border-radius:8px;
  background:rgba(127,212,255,.13);border:1px solid rgba(127,212,255,.32);
  color:#7fd4ff;font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;
  text-transform:uppercase;text-decoration:none}
.td-go:hover{background:rgba(127,212,255,.2)}

/* EMERGENCY, at the top of the legend. Two big dial targets first: 911
   always, the verified patrol line when we have one. Then where you are,
   the nearest post with distance and bearing, and the posts by name. */
.l2-sos{padding:0 0 8px;border-bottom:1px solid var(--rule2)}
.sos-h{color:#ff8a94}
.sos-row{display:flex;gap:6px;padding:0 8px}
.sos-tel{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:1px;
  padding:7px 9px;border-radius:9px;text-decoration:none;
  background:rgba(200,16,46,.16);border:1px solid rgba(255,120,130,.35);color:var(--ink)}
.sos-tel span{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;
  text-transform:uppercase;color:#ff8a94;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sos-tel b{font-family:var(--mono);font-size:13px;font-weight:700;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.sos-911{background:#b3121f;border-color:#d8303f;color:#fff}
.sos-911 span{color:#ffd0d4}
.sos-line{background:rgba(200,16,46,.16)}
.sos-alt{margin:6px 8px 0;font-size:10px;color:var(--dim);line-height:1.5}
.sos-alt a{color:#ffb3b9;text-decoration:none}
.sos-note{margin:6px 8px 0;font-size:10.5px;line-height:1.45;color:var(--dim)}
.sos-note-fail{color:var(--warn,#d08a3e)}
.sos-pos,.sos-near{margin:7px 8px 0;display:flex;flex-wrap:wrap;align-items:baseline;
  gap:2px 8px;font-size:11px;line-height:1.35}
.sos-pos span,.sos-near span{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);flex:0 0 100%}
.sos-pos b,.sos-near b{font-family:var(--mono);font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums}
.sos-pos i,.sos-near i{font-style:normal;color:#7fd4ff;font-family:var(--mono);font-size:10.5px}
.sos-go{margin:0;padding:3px 9px;border-radius:999px;cursor:pointer;font:inherit;
  font-size:10px;font-weight:600;color:#fff;background:#c8102e;border:1px solid rgba(255,255,255,.35)}
.sos-posts{list-style:none;margin:6px 8px 0;padding:0;display:flex;flex-wrap:wrap;gap:4px}
.sos-posts li{display:flex;align-items:center;gap:4px}
.sos-posts .sos-go{background:rgba(200,16,46,.22);border-color:rgba(255,120,130,.4);color:var(--ink)}
.sos-posts i{font-style:normal;font-family:var(--mono);font-size:8.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.fmark-clinic{background:#1f6fd0;box-shadow:0 0 7px 1px rgba(31,111,208,.45)}

/* place emblems in the key, matched to the insignia over the buildings */
.sym.patrol{width:12px;height:12px;border-radius:50%;background:#c22733;
  box-shadow:none;position:relative}
.sym.patrol::before,.sym.patrol::after{content:'';position:absolute;background:#fff}
.sym.patrol::before{left:5px;top:2.5px;width:2px;height:7px}
.sym.patrol::after{left:2.5px;top:5px;width:7px;height:2px}
.sym.lodge{width:12px;height:12px;border-radius:50%;background:#7a5a34;
  box-shadow:none;position:relative}
.sym.lodge::before{content:'';position:absolute;left:2.6px;top:5.4px;
  width:6.8px;height:4px;background:#ffe9c4}
.sym.lodge::after{content:'';position:absolute;left:2px;top:2.2px;width:8px;height:3.4px;
  background:#ffe9c4;clip-path:polygon(50% 0,100% 100%,0 100%)}
.sym.station{width:12px;height:12px;border-radius:50%;background:#3a4c63;
  box-shadow:none;position:relative}
.sym.station::before{content:'';position:absolute;left:1.6px;top:4px;width:8.8px;height:1.6px;
  background:#e8f0fa}
.sym.station::after{content:'';position:absolute;left:4px;top:5.4px;width:4px;height:3.6px;
  background:#e8f0fa}


/* ================================================ COMMAND CENTER */
/* Every way a device joins, one panel, cut from the legend's glass.
   Rows are glove targets: tall, separated, one action each. */
.ccpanel{position:absolute;top:10px;right:114px;z-index:11;
  max-height:calc(100% - 20px);
  width:0;max-width:0;overflow:hidden;opacity:0;pointer-events:none;
  border-radius:var(--r);
  background:linear-gradient(180deg, rgba(10,16,26,.94), rgba(6,11,20,.97));
  backdrop-filter:blur(16px);border:1px solid rgba(120,145,175,.2);
  box-shadow:0 12px 34px rgba(0,0,0,.55);transition:opacity .16s ease}
.ccpanel[data-open="true"]{width:min(320px,calc(100vw - 130px));
  max-width:320px;opacity:1;pointer-events:auto;
  overflow-y:auto;overscroll-behavior:contain}
/* the header stays put while the doors scroll under it */
.ccpanel .cc-head:first-child{position:sticky;top:0;z-index:2;
  background:linear-gradient(180deg,rgba(8,13,22,.98),rgba(8,13,22,.92))}
.cc-head{display:flex;justify-content:space-between;align-items:center;
  padding:12px 14px 8px;font-size:10.5px;letter-spacing:.22em;color:#9fb3cc;
  font-family:ui-monospace,'SF Mono',Menlo,monospace}
.cc-close{background:none;border:0;color:#8fa3bd;font-size:17px;cursor:pointer;
  padding:2px 6px}
.cc-rows{display:grid;gap:8px;padding:0 10px}
.cc-row{display:flex;align-items:center;gap:11px;min-height:52px;
  background:rgba(20,30,46,.55);border:1px solid rgba(120,145,175,.18);
  border-radius:3px;padding:9px 12px}
.cc-row > svg{width:22px;height:22px;flex:none;fill:none;stroke:#7fb7f2;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.cc-row > div{flex:1;min-width:0}
.cc-row b{display:block;font-size:13px;color:#e6eefa}
.cc-row [data-cc-s]{display:block;font-size:11px;color:#93a7c2;margin-top:2px;
  line-height:1.35}
.cc-row [data-cc-go]{flex:none;min-height:34px;min-width:58px;cursor:pointer;
  background:rgba(23,63,116,.85);border:1px solid #54b4ff;border-radius:3px;
  color:#fff;font-family:inherit;font-size:12px;font-weight:700;padding:6px 12px}
.cc-row [data-cc-go].dim{background:rgba(20,30,46,.6);
  border-color:rgba(120,145,175,.3);color:#93a7c2}
.cc-sub{padding:12px 14px 4px;margin:0}
.cc-sub span{font-size:9.5px;letter-spacing:.22em;color:#7f93ae}
.cc-note{padding:10px 14px 4px;font-size:10.5px;color:#7f93ae;line-height:1.5}
.cc-msg{padding:0 14px 12px;font-size:11px;color:#cfe0f2;min-height:14px}

/* ================================================ THE PLACE CARD */
/* What a place is, and whether to go: the survey's own facts with
   navigation at the top. Squared like every other instrument. */
.placecard{position:absolute;left:12px;top:12px;z-index:12;width:min(268px,calc(100% - 24px));
  background:rgba(6,10,18,.94);border:1px solid rgba(220,235,255,.26);border-radius:2px;
  padding:9px 12px 8px;color:#eef4fb;
  font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  box-shadow:0 14px 38px -12px rgba(0,0,0,.7)}
.placecard .pc-n{display:block;font-size:13.5px;font-weight:700;letter-spacing:.04em;
  padding-right:20px;line-height:1.2}
.placecard .pc-k{display:block;font-size:9.5px;letter-spacing:.2em;color:var(--route-ink);
  text-transform:uppercase;margin:2px 0 7px}
.placecard .pc-go{display:flex;gap:6px;margin-bottom:7px}
.placecard .pc-go button{flex:1;min-height:34px;cursor:pointer;border-radius:2px;
  font-family:inherit;font-size:11px;letter-spacing:.08em;font-weight:700;padding:6px 6px}
.placecard .pc-nav{background:var(--route);border:1px solid var(--route);color:#1a1206}
.placecard .pc-walk{background:rgba(20,30,46,.7);border:1px solid rgba(220,235,255,.3);
  color:#dbe7f4}
.placecard .pc-row{display:flex;justify-content:space-between;gap:12px;
  font-size:11.5px;line-height:1.25;color:#a9bdd6;padding:2px 0;
  border-top:1px solid rgba(220,235,255,.1)}
.placecard .pc-row b{color:#fff;font-weight:600;text-align:right;font-variant-numeric:tabular-nums}
.placecard .pc-row a{color:#8fc6ff}
.placecard .pc-note{margin-top:5px;font-size:9.5px;color:#7f93ae;line-height:1.35}
/* THE PATROL CARD'S EMERGENCY ROW AND ITS DIRECT LINE.
   911 is the one number on this card allowed to shout, because it is the one
   that reaches a dispatcher rather than one radio room. The hold button reads
   as an action and not as a link, and the sentence under it changes colour on
   the only distinction that matters: green once patrol has answered, amber
   when the reader is being told to pick up a phone instead of wait. */
.placecard .pc-911 b a{color:#ff5f57;font-weight:800;letter-spacing:.3px}
.placecard .pc-sos{display:block;width:100%;margin-top:7px;padding:9px 10px;
  border:1px solid #7a2b2b;border-radius:8px;background:#2a1418;color:#ffd9d9;
  font:inherit;font-size:10.5px;font-weight:700;cursor:pointer;text-align:center}
.placecard .pc-sos:disabled{opacity:.75;cursor:default}
.placecard .pc-sosline{margin-top:5px;font-size:9.5px;line-height:1.4;color:#cfe0f5;font-weight:600}
.placecard .pc-sosline.has{color:#7ee0a2}
.placecard .pc-sosline.dark{color:#ffc46b}
.placecard .pc-x{position:absolute;top:6px;right:8px;background:none;border:0;
  color:#8fa3bd;font-size:16px;cursor:pointer;padding:2px 4px;line-height:1}
@media (max-width:640px){ .placecard{width:min(240px,calc(100% - 20px))} }

/* the pin, armed: the whole stage says a press will land it */
.m3d.pinning{cursor:crosshair}
/* ONE SELECTED COLOUR AND IT IS THE BLUE (owner, standing rule). Armed,
   this mark lit ORANGE — the route colour — which on a dark mountain read
   as a loose orange pin floating in the rail rather than as the control
   that drops one. Every lit mark in this rail is the same blue. */
.pin-mark.on{color:var(--sel);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.9)) drop-shadow(0 0 7px rgba(84,180,255,.75))}
.pin-mark{width:100%;height:auto;min-height:38px;display:flex;align-items:center;
  justify-content:center;padding:3px 0;border:0;background:transparent;
  cursor:pointer;color:var(--mark);filter:var(--markhalo)}
.pin-mark svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.pin-mark:hover{color:var(--sel)}

/* ============================================ THE GUIDANCE ARROW */
/* Above the compass: where you asked to go, relative to where you are
   looking, and how far. Straight up always means "that way". */
.hud-guide{width:100%;height:auto;min-height:48px;padding:0 0 5px;border:0;
  background:transparent;cursor:pointer;display:flex;flex-direction:column;
  align-items:center;gap:2px;color:var(--route);
  /* Its distance line was landing on the compass face below it: the arrow
     and the rose are two instruments, and two instruments touching read as
     one broken one. The pad is the gap. */
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.9))
         drop-shadow(0 0 3px rgba(244,250,255,.35))}
.hud-guide svg{width:30px;height:30px;display:block;overflow:visible}
.hud-guide .gd-rot{transform-origin:20px 20px;transition:transform .18s ease-out}
.hud-guide .gd-arrow{fill:var(--route);stroke:rgba(10,16,26,.85);stroke-width:1.5;
  stroke-linejoin:round}
.hud-guide .gd-d{font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:9px;
  letter-spacing:.08em;color:#8a4a12;font-weight:700;
  text-shadow:var(--labelhalo)}
.hud-guide:hover .gd-arrow{fill:var(--route-lit)}

/* TIGHT: a phone-sized stage. Fewer instruments, never smaller ones.
   The conditions strip, the band row, the run key and the way-line all
   leave, because the legend still answers every one of them; what stays
   is what only the visor can say. */
.visor.tight .vz-strip,
.visor.tight .vz-live,
.visor.tight .vz-bands,
.visor.tight .vz-wxline,
.visor.tight .vz-way{display:none}
.visor.tight .vz-place span{display:none}
.visor.tight .vz-place b{font-size:12px}
.visor.tight .vz-right{top:var(--pad-y);right:52px;max-width:200px}
.visor.tight .vz-avy{font-size:11.5px;letter-spacing:.1em}
.visor.tight .vz-rail{top:58px;gap:6px}
.visor.tight .vz-rail button{min-height:36px}
.visor.tight .vz-rail button svg{width:16px;height:16px}
/* The column still starts under the rail and still stops short of the
   bottom band — on a phone that band is the whole weather card. */
.visor.tight{--tel-top:170px}
.visor.tight .vz-tel{top:calc(58px + var(--tel-top));
  max-height:calc(100% - 58px - var(--tel-top) - var(--pad-b) - 28px)}
/* The cell shape does not change with the tier: fewer instruments on a
   small screen, never smaller ones. Only the gaps close up. */
.visor.tight .vz-tel{gap:8px}
.visor.tight .vz-spd b{font-size:25px}
.visor.tight .vz-tag em{font-size:10.5px;letter-spacing:.1em}

/* ===================== PANELS ON A NARROW SCREEN =====================
   A phone has one column, so a panel that floats beside the rail on a
   desktop has to become a full-width sheet here or it gets clipped and
   the reader never sees the controls below the fold. Everything scrolls
   inside the stage, with a fade at the bottom edge saying there is more. */
@media (max-width: 760px){
  .ccpanel{top:8px;right:8px;left:56px;max-height:calc(100% - 16px)}
  .ccpanel[data-open="true"]{width:auto;max-width:none}
  .cc-rows{gap:7px}
  .cc-row{min-height:56px}
  /* the fade: content continues below */
  .ccpanel[data-open="true"]::after{content:'';position:sticky;bottom:0;
    display:block;height:26px;margin-top:-26px;pointer-events:none;
    background:linear-gradient(180deg,transparent,rgba(6,11,20,.97))}

  /* the cards keep inside the stage and scroll rather than run off it */
  .placecard{left:8px;right:8px;top:8px;width:auto;
    max-height:calc(100% - 16px);overflow-y:auto;overscroll-behavior:contain}
  /* ABOVE THE SHEET, NOT UNDER IT. Pinned to the stage's own bottom, the
     card ran 49 pixels behind the weather bar and cut its last line in half:
     the reader got Distance and Vertical and then a sliced word where the
     time should be. The sheet publishes its height, so the card clears it. */
  .destcard{right:8px;left:8px;bottom:calc(var(--sheet, 0svh) + 10px);
    max-width:none;min-width:0;max-height:42%;overflow-y:auto}
  /* on a phone the place card owns the top, so the world tags stand clear */
  .m3d:has(.placecard) .vz-tags{opacity:.25}
}

/* THE MINIMISED SHEET IS ONE BAR.
   It used to be the grip with the top sliver of a full card showing under
   it: a card cut in half, not a minimised card, and dead screen the reader
   paid for. At the bottom snap the panel IS the grip — the handle, and one
   line of the three readings worth a glance. Nothing is hidden below it and
   nothing is cut off inside it. The bar stays a full-width glove target, so
   the way back up is as easy as it ever was. */
body[data-sheet="min"] .panel{padding:0;overflow:hidden}
body[data-sheet="min"] #cardhost{display:none}
body[data-sheet="min"] .grip{height:100%;min-height:34px;max-height:58px;
  gap:11px;padding:0 14px;justify-content:flex-start}
body[data-sheet="min"] .grip span{width:30px;flex:0 0 30px}
body[data-sheet="min"] .minirow{display:block}
.minirow{display:none;font-style:normal;font-family:var(--mono);
  font-size:11.5px;font-weight:700;letter-spacing:.08em;color:var(--ink);
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;min-width:0}
/* THE PLACE LEADS, THE READINGS FOLLOW. The name is the one thing that must
   survive when the bar runs out of room, so it holds its size while the
   numbers are what the ellipsis eats. */
body[data-sheet="min"] .minirow{display:flex;align-items:center;gap:8px}
.minirow .mr-pk{width:19px;height:11px;flex:0 0 19px;
  fill:var(--ink);opacity:.9}
.minirow .mr-pk .cap{fill:#fff;opacity:.95}
.minirow .mr-n{flex:0 0 auto;font-weight:800;letter-spacing:.05em;
  font-family:inherit}
.minirow .mr-d{min-width:0;overflow:hidden;text-overflow:ellipsis;
  font-style:normal;color:var(--dim);letter-spacing:.08em}
.minirow .mr-d::before{content:'\00b7';margin-right:8px;opacity:.55}
