@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. */
/* Snowpack — instrument surface.
   Type: one grotesque for labels, one monospace for every figure. Tabular
   numerals are mandatory: values update live, and proportional digits make a
   column jitter every time a 1 becomes an 8. */
:root{
  --bg:#04070d; --pnl:#080d16; --pnl2:#0b1220; --rule:#1a2635; --rule2:#243244;
  --ink:#e8eef6; --mut:#8ea0b6; --dim:#5d6e84; --faint:#3d4c5e;
  --ok:#3ddc97; --warn:#f0b429; --bad:#ff5a5f; --info:#4fb8ff;
  --mono:ui-monospace,"SF Mono","JetBrains Mono","DejaVu Sans Mono",Menlo,Consolas,monospace;
  --sans:system-ui,-apple-system,"Segoe UI","Liberation Sans",sans-serif;
  --gut:14px;
  /* ── SHAPE, MOTION AND STATE ───────────────────────────────────────────
   * The three things that separate an interface that feels considered from
   * one that feels assembled — and all three were missing, not wrong.
   *
   * RADIUS. Everything was 1-4px. That is a deliberate look — technical,
   * sharp, instrument-like — and it is the wrong one here. A 2px corner reads
   * as a boundary being enforced; an 8-10px corner reads as a surface. This
   * app is read one-handed on a chairlift, and softer geometry is calmer to
   * scan. Three steps only, because a fourth is a decision nobody can make
   * consistently at two in the morning.
   *
   * MOTION. There were eight transitions in the entire application. Every
   * other state change — opening a panel, switching a view, pressing a
   * control — happened in a single frame. Instant is not fast; instant is
   * jarring, because the eye has to re-find what moved. A hundred and forty
   * milliseconds on a decelerating curve is below the threshold where anyone
   * would call it slow and above the one where the change is legible.
   *
   * STATE. Hover and press were signalled by changing BORDER COLOUR, which is
   * a one-pixel event on a dark ground and is invisible on a phone, where
   * there is no hover at all. State now lifts the SURFACE — a faint wash that
   * reads instantly at any size and degrades gracefully to nothing on touch.
   */
  --r-sm:6px; --r:10px; --r-lg:14px;
  --dur:.14s; --dur-slow:.28s;
  --ease:cubic-bezier(.22,.61,.36,1);          /* decelerating; nothing overshoots */
  --lift:rgba(146,180,222,.07);                /* hover wash */
  --lift-2:rgba(146,180,222,.13);              /* pressed / active wash */
  --ring:0 0 0 2px rgba(79,184,255,.38);       /* one focus treatment, everywhere */
  --shadow:0 8px 28px rgba(2,5,11,.5);
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:13px;line-height:1.45;-webkit-font-smoothing:antialiased}

/* The card sizes its own columns.
   These breakpoints used to read the viewport, which is the wrong ruler: on a
   1440px desktop the card is 490px wide in its column, so a "viewport is wide"
   rule gave it six columns of eighty pixels and figures broke mid-number.
   A container query asks the only question that matters — how wide is the
   card — and gives the same answer on a phone and in a narrow panel. */
.pnl{background:var(--pnl);border:1px solid var(--rule);border-radius:var(--r);overflow:hidden;
  container-type:inline-size;container-name:card}

/* Identification strip — who and where, in the notation the audience writes. */
.pnl-id{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;
  padding:11px var(--gut);border-bottom:1px solid var(--rule2);background:var(--pnl2)}
.pnl-id h2{font-size:16px;font-weight:650;letter-spacing:-.01em;line-height:1.2}
.idm{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin-top:4px;
  font-family:var(--mono);font-size:10.5px;color:var(--dim);letter-spacing:.02em}
.idm i{width:1px;height:9px;background:var(--rule2);display:inline-block}
.idm .stn{color:var(--info)}
.idr{display:flex;align-items:center;gap:6px;flex-shrink:0}

.tag{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.09em;
  padding:3px 6px;border-radius:var(--r-sm);border:1px solid transparent}
.tag-station{color:#04120b;background:var(--ok)}
/* An absence of data is not an ALARM.
 *
 * This was a red chip on a red-tinted field. The reasoning behind it was
 * sound as far as it went — a green badge over a panel of dashes would read
 * as "measured, and the mountain is quiet", which is the opposite of the
 * truth — but the cure was worse. Red is the colour this app reserves for
 * danger: avalanche conditions, a warning in force, a reading that should
 * change what somebody does today. A station simply not reporting is none of
 * those. Painting it red tells a reader something is WRONG on the mountain
 * when the only thing wrong is our telemetry, and it spends the alarm colour
 * on the most common state a remote resort is ever in.
 *
 * So: no fill, no frame, no alarm. Absence reads as absence — quiet, dimmed,
 * and stated in words next to the dashes it explains. */
.tag-none{background:transparent;color:var(--mut);border-color:transparent;
  letter-spacing:.06em}
.tag-model{color:#031320;background:var(--info)}
.tag-forecast{color:var(--mut);border-color:var(--rule2)}
.tag-resort{color:#1a1204;background:var(--warn)}
.age-fresh{color:var(--ok);border-color:rgba(61,220,151,.35)}
.age-aging{color:var(--warn);border-color:rgba(240,180,41,.35)}
/* Same rule as .tag-none above: an old reading is not a hazard, it is an old
   reading. It is dimmed and dated, not alarmed. */
.age-stale{color:var(--mut);border-color:var(--rule2)}

/* Icon buttons carry no frame.
 *
 * This was a 27px bordered square with a glyph inside it — the exact shape
 * that has been called out repeatedly, and the reason is worth writing down
 * rather than re-deriving: a frame is how an interface admits its icon is not
 * legible on its own. If a reader needs a box to know the mountain symbol is
 * pressable, the symbol is wrong. Fix the symbol.
 *
 * So the frame goes and the affordance moves to where it belongs — the glyph
 * brightens on hover, and the target grows to 44px so a gloved thumb still
 * finds it while the mark on screen stays small. */
.ib{width:44px;height:44px;border:0;border-radius:0;background:transparent;
  color:var(--mut);display:grid;place-items:center;cursor:pointer;
  transition:color .12s ease, transform .12s ease}
.ib:hover{color:var(--info)}
.ib:active{transform:scale(.92)}
.ib:focus-visible{outline:1.5px solid var(--info);outline-offset:2px}

.grp{border-bottom:1px solid var(--rule)}
.grp:last-of-type{border-bottom:0}
.grp h3{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--faint);padding:8px var(--gut) 0}

/* Data cells: fixed columns so figures line up across rows and panels. */
.cells{display:grid;gap:1px;background:var(--rule);margin-top:8px}
.c6{grid-template-columns:repeat(2,1fr)}
@container card (min-width:400px){.c6{grid-template-columns:repeat(3,1fr)}}
@container card (min-width:700px){.c6{grid-template-columns:repeat(6,1fr)}
  .c6 .wide{grid-column:span 1}}
.c{background:var(--pnl);padding:8px var(--gut) 10px;min-width:0}
.c-l{display:block;font-family:var(--mono);font-size:9px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint);font-weight:700}
.c-v{display:block;font-family:var(--mono);font-size:17px;font-weight:600;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;margin-top:3px;
  /* Break between a figure and its unit, never inside the figure.
     `overflow-wrap:anywhere` split a ceiling of 3,109 m across two lines as
     "3,10" and "9 m" — and "3,10" reads as a whole, wrong number. A reading
     that is silently incorrect is far worse than one that wraps. */
  overflow-wrap:normal;word-break:normal}
.c.bad .c-v,.c.warn .c-v,.c.ok .c-v{font-size:14px;line-height:1.25}
.c-s{display:block;font-size:10.5px;color:var(--dim);margin-top:2px;line-height:1.3}
.c.ok .c-v{color:var(--ok)} .c.warn .c-v{color:var(--warn)} .c.bad .c-v{color:var(--bad)}
.c-s .ok{color:var(--ok)} .c-s .na,.na{color:var(--faint)}
.tr{font-size:9px;margin-left:5px;vertical-align:middle}
/* A falling number is not a warning. Red on a downward trend is a stock
   ticker idiom, and it does not transfer: snow depth settling is what snow
   depth does, every day, and flagging it in the hazard colour tells a reader
   to worry about the ordinary. Direction is carried by the arrow. */
.tr.up{color:var(--ok)} .tr.dn{color:var(--dim)} .tr.fl{color:var(--dim)}

/* Vertical profile as a real table — this is a reading task, not a graphic. */
.prof{width:100%;border-collapse:collapse;margin-top:8px;font-family:var(--mono);font-size:11.5px}
.prof th{font-size:9px;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);
  font-weight:700;text-align:left;padding:0 var(--gut) 6px}
.prof th:not(:first-child),.prof td:not(:first-child){text-align:right}
.prof th.pb,.prof td.pb{width:34%}
.prof td{padding:5px var(--gut);border-top:1px solid var(--rule)}
.prof .bn{font-family:var(--sans);font-weight:600;font-size:12px;text-align:left}
.prof .num{font-variant-numeric:tabular-nums}
.prof .str{font-weight:700;color:var(--ink)}
.prof .warn{color:var(--warn)}
/* A zero value must draw nothing; a 2px stub reads as 'a little snow'. */
.prof td.pb i{display:block;height:5px;border-radius:var(--r-sm);
  background:linear-gradient(90deg,var(--info),var(--ok))}
.prof td.pb{padding-left:4px;padding-right:4px}

.note{font-size:11px;color:var(--dim);padding:7px var(--gut) 10px}
.note b{color:var(--ink);font-family:var(--mono)}
.note.prob{color:var(--warn)}

/* Danger rose */
.avy-wrap{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start;padding:10px var(--gut) 12px}
/* Active NWS warnings — the one strip allowed to shout. An Avalanche
   Warning outranks everything on the panel and wears the danger red. */
.warns{display:flex;flex-direction:column;gap:6px}
.warn{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;
  padding:8px 11px;border-radius:8px;border:1px solid rgba(237,28,36,.35);
  background:rgba(237,28,36,.10);font-size:12.5px;line-height:1.35}
.warn b{font-size:13px;letter-spacing:.02em}
.warn span{flex:1 1 100%;color:var(--ink-dim, #b6c2d2)}
.warn i{font-style:normal;font-size:11px;opacity:.75}
.warn-avy{border-color:#ed1c24;background:rgba(237,28,36,.18)}
.warn.sev-moderate,.warn.sev-minor{border-color:rgba(247,148,30,.4);background:rgba(247,148,30,.10)}
.avy-meta .advice{margin-top:4px}
/* A measured inversion is a condition worth a raised voice — amber, not
   red: it changes where the good snow is, not whether you are safe. */
.note.inv{color:#d08a3e}
.note.inv b{color:#e8a34f}
.band-prov{margin-top:4px;font-size:10.5px;opacity:.65;letter-spacing:.02em}
.rose{flex-shrink:0}
.avy-meta{flex:1;min-width:170px}
.peak{display:flex;align-items:baseline;gap:9px;padding-bottom:4px}
.peak .pl{font-family:var(--mono);font-size:30px;font-weight:700;line-height:1;color:var(--dc)}
.peak .pn{font-size:13px;font-weight:600;color:var(--ink)}
.dl{list-style:none;margin-top:9px;display:grid;gap:3px}
.dl li{display:flex;align-items:center;gap:7px;font-size:10.5px;color:var(--mut)}
.dl i{width:10px;height:10px;border-radius:var(--r-sm);flex-shrink:0}
.dl span{font-family:var(--mono);color:var(--dim);width:8px}
.dl em{font-style:normal}

.pnl-ft{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:7px var(--gut);
  border-top:1px solid var(--rule2);background:var(--pnl2);
  font-family:var(--mono);font-size:9.5px;letter-spacing:.05em;color:var(--dim)}
.pnl-ft i{width:1px;height:9px;background:var(--rule2)}
/* The third copy of the stale-equals-alarm mistake (see .tag-none and
   .age-stale). An old reading is old, not dangerous. */
.pnl-ft .fresh{color:var(--ok)} .pnl-ft .aging{color:var(--warn)} .pnl-ft .stale{color:var(--mut)}


/* At-a-glance. Consumer-readable, sitting above the instrument detail rather
   than replacing it — a skier gets the answer, a forecaster scrolls on. */
/* Consolidated to one instrument strip: temperature and condition on the
   left, the four decision figures in a single tight rank beside them, the
   secondary air readings one quiet line below. Half the height it was —
   the mountain is the hero, the header is its HUD. */
.glance{display:flex;flex-wrap:wrap;gap:6px 12px;align-items:center;justify-content:space-between;
  padding:9px var(--gut) 8px;border-bottom:1px solid var(--rule)}
.g-main{display:flex;align-items:center;gap:10px;min-width:0}
.g-ico{width:34px;height:34px;flex-shrink:0;color:#cfe0f0}
.g-temp{font-family:var(--mono);font-size:25px;font-weight:600;line-height:1;
  font-variant-numeric:tabular-nums;letter-spacing:-.03em}
.g-word{font-size:11.5px;color:var(--mut);margin-top:2px}
.g-chips{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0 10px;
  flex:1 1 auto;min-width:200px}
@media(min-width:560px){.g-chips{grid-template-columns:repeat(4,auto);justify-content:end;gap:0 18px}}
.g-chip{display:flex;flex-direction:column;gap:1px;min-width:0}
.g-chip span{font-family:var(--mono);font-size:7.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);font-weight:700;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.g-chip b{font-family:var(--mono);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.g-chip.good b{color:var(--ok)} .g-chip.warn b{color:var(--warn)}
.g-airline{flex:1 1 100%;font-family:var(--mono);font-size:9.5px;letter-spacing:.03em;
  color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* "As of" sits inside its section heading, in the heading's own voice —
   context for the numbers below it, not an alarm about the whole card. */
.grp h3 .asof{font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.06em;
  text-transform:none;color:var(--faint);margin-left:8px}
.grp h3 .asof.old{color:var(--warn)}

/* Provenance as a word in the identity line, not a coloured box.
   It still distinguishes a measured reading from a modelled one — that
   distinction is real — but it stops shouting from the corner. */
.idm .src{color:var(--dim)}
.idm .src-station{color:var(--ok)}
/* Not the alarm colour — the same reasoning as .tag-none above, which was
   fixed while THIS rule quietly kept painting the identity line red. Two
   places encoding one idea is how a fix half-lands: the badge went quiet and
   the header did not. Red is reserved for hazard. A station that is not
   reporting is an absence, and it reads as one. */
.idm .src-none{color:var(--mut)}

/* Conditions lead the card. The picture follows the numbers. */
.pnl > .glance:first-child{border-top:0}
.pnl .hero{margin-top:0}


/* ══════════════════════════════════════════════════════ the resort hero
 *
 * One cinematic frame, at the render's OWN aspect ratio.
 *
 * This replaces two stacked bands — a 104px photo strip and a 132px synthetic
 * sky — that between them gave a production render 104 pixels of height and
 * cropped it top and bottom. Both of those decisions were wrong once real
 * images existed, and the cropping one was actively destructive: every render
 * carries its name-and-logo lockup set into the bottom of the frame, and a
 * centre-cropped 3:2 image cuts it in half.
 *
 * So the frame holds 3:2 exactly and the image is never cropped. That costs
 * real estate and is worth it — this is the one place the product gets to
 * look like the mountain instead of like a dashboard.
 */
.hero{
  position:relative; margin:0; overflow:hidden;
  /* WIDTH DECLARED, NOT LEFT TO AUTO — this is what makes the frame span.
     A block box with an aspect-ratio and `width:auto` normally stretches to
     its container and derives its height. But once max-height clamps that
     height the block size becomes definite, and CSS then transfers it back
     through the ratio to size the WIDTH — so on a wide card the frame stopped
     being 956px by 637 and became 510 by 340, a half-width picture with a
     void beside it while every other row in the card ran full bleed. Stating
     the width makes it definite, nothing is transferred, and the max-height
     does what it was put there to do: crop the top. */
  width:100%;
  /* Its own blending world. The grade and the sky layer both composite, and
     without this they reach past the frame and tint whatever the card is
     sitting on. */
  isolation:isolate;
  aspect-ratio:3/2;
  background:linear-gradient(180deg, var(--sky-top,#16274a), var(--sky-hor,#33507a));
  border-bottom:1px solid var(--rule2);
}
/* On a wide desktop card the full 3:2 becomes taller than the readings beside
   it, so the frame is capped and the image is allowed to crop — but only from
   the TOP, which is sky. The lockup at the bottom survives either way. */
.hero-img{
  position:relative; z-index:1;
  width:100%; height:100%; object-fit:cover; object-position:center;
  display:block;
}
/* NO HEIGHT CAP ON A WIDE CARD, AND SO NO CROP ANYWHERE.
 *
 * There used to be `max-height:340px` here for cards 900px and wider, on the
 * reasoning that a full 3:2 frame would stand taller than the readings set
 * beside it. Nothing is ever set beside it — every row of this card runs the
 * full width at every size — so the cap bought nothing and cost a great deal:
 * on a 956px card it left a 2.8:1 letterbox that threw away 47% of the render,
 * and what it threw away was the top, which is the sky. The sun and moon live
 * in that sky. It also took the frame's width down with it, because a definite
 * max-height on a box with an aspect ratio transfers back through the ratio —
 * which is how a full-bleed hero ended up 510px wide with a void beside it.
 *
 * The frame is 3:2 at every width now, and the render is never cropped, which
 * is what this card promised in the first place. */
.hero[data-noimg="1"] .hero-img{ display:none }

/* The grade and the dim sit ABOVE the image and below the text. Blend modes
   are what make this read as light rather than as a coloured sheet laid on
   top: overlay warms without washing out, multiply deepens without muddying. */
.hero-grade,.hero-dim{ position:absolute; inset:0; z-index:2; pointer-events:none }
.hero-dim{ background:#050a14 }

/* ── THE SUN AND MOON LAYER ──
   Above the grade, not below it. The grade is ambient light falling ON the
   scene; these two are the light SOURCE, and dimming the moon with the same
   navy multiply that deepens the snow would be backwards — the one thing in a
   night frame that should not be darkened is the moon. */
/* The drawn scene IS the hero, not a layer over one. It fills the frame and
   carries its own sky, its own light and its own occlusion — which is why
   there is no longer a grade, a dim or a masked celestial layer here. */
.hero-scene{ position:absolute; inset:0; width:100%; height:100%; display:block }

/* Falling weather, drawn only when something is falling. In front of the
   scene, which is where it actually is — and faded out before the bottom of
   the frame, so nothing ever falls across the lockup. */
.hero-fall{ position:absolute; inset:0; z-index:4; pointer-events:none; overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,#000 0 58%,rgba(0,0,0,.42) 80%,transparent 96%);
          mask-image:linear-gradient(180deg,#000 0 58%,rgba(0,0,0,.42) 80%,transparent 96%) }

/* THREE LAYERS, ON THREE ELEMENTS — NOT THREE ANIMATIONS ON ONE.
 *
 * This was three background-image layers on a single box with three animations
 * listed against it, which does not do what it reads as: when several
 * animations touch the same property the LAST one wins outright, so only the
 * third layer ever moved and the other two hung motionless in the frame. The
 * near flakes — the big bright ones — were the frozen ones. Each layer now
 * gets its own element and its own single animation, which is the only way
 * three of them actually run.
 *
 * Why three at all: a single repeating grid is unmistakably a grid, and the
 * eye finds the lattice within a second. Real falling weather has depth — near
 * drops are larger, brighter and fall faster; distant ones are small, faint
 * and almost hang. Three scales beating against each other hide the repeat,
 * and the parallax between them is what reads as depth rather than texture.
 *
 * AND WHY THE TILES ARE DRAWN, NOT GRADIENTS. The rain used to be a diagonal
 * linear-gradient on a 14x22 tile, which is the single thing a tiled gradient
 * cannot do: a diagonal gradient never tiles seamlessly, because being
 * periodic across the tile in x and in y at once demands two whole numbers of
 * periods that have to add up to exactly one. So every streak was cut at the
 * tile edge and the whole thing read as a lattice of bright scratches laid
 * over the mountain — the weird overlay that kept appearing on the card, and
 * only ever on the card, because it is drawn only while something is falling.
 * These tiles are SVG instead: each streak is drawn once and again shifted a
 * whole tile in x, y and both, so whatever leaves one edge arrives at the
 * other and the lines run unbroken. Generated by tool gen-fall.py.
 *
 * Every drift below is one tile across for five down, the same 1:5 the streaks
 * lean at — so drops travel along their own length rather than skating
 * sideways, and land back on the lattice at the end of each cycle with no
 * jump. A hitch every seven seconds is more noticeable than the motion it
 * interrupts. */
/* All three layers are the same kind of box. The third is a real element
   because two pseudo-elements is all a box gets, and an <i> with only a
   background is an inline of zero height — so it has to be laid out here
   with the other two or the nearest, brightest layer never draws at all. */
.hero-fall::before,.hero-fall::after,.hero-fall > i{
  content:''; display:block; position:absolute; inset:-14% -14% }

.hero-fall.snow{ opacity:.78 }
.hero-fall.snow::before{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2280%22%20height%3D%2280%22%3E%3Cpath%20d%3D%22M18.8%2019.7h0M40.7%2018.6h0M53.8%2019.2h0M60.4%2053.8h0M-1.2%2021.1h0M78.8%2021.1h0M-0.9%2057.7h0M79.1%2057.7h0M56.9%201.7h0M56.9%2081.7h0M52.7%2025.1h0M17.2%2050.7h0M41.4%2026.0h0M7.3%204.2h0M43.8%2034.5h0M36.1%2040.2h0M30.7%2033.9h0M54.7%2051.3h0M44.3%2032.9h0M62.6%2054.6h0%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.38%22%20stroke-width%3D%221.2%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:80px 80px; animation:hero-fall-80 16s linear infinite }
.hero-fall.snow::after{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22110%22%20height%3D%22110%22%3E%3Cpath%20d%3D%22M99.2%2082.7h0M45.8%2066.3h0M54.1%2056.8h0M71.9%2076.7h0M-1.1%2047.3h0M108.9%2047.3h0M105.2%2020.2h0M22.4%2036.9h0M55.1%2045.8h0M60.1%2036.5h0M27.3%2058.8h0M38.7%2044.7h0M47.3%207.7h0M69.7%2042.5h0M66.0%2087.8h0M96.7%20103.3h0%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.6%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:110px 110px; animation:hero-fall-110 11s linear infinite }
.hero-fall.snow > i{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22150%22%20height%3D%22150%22%3E%3Cpath%20d%3D%22M97.9%2057.2h0M52.1%2019.5h0M53.4%20108.5h0M124.2%2069.9h0M111.4%20120.8h0M27.2%204.5h0M48.3%2010.6h0M57.4%20-1.1h0M57.4%20148.9h0M81.7%2015.8h0M119.4%2028.9h0M119.6%2025.7h0M126.2%2044.8h0M3.1%20119.9h0%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.92%22%20stroke-width%3D%222.6%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:150px 150px; animation:hero-fall-150 7.5s linear infinite }

/* Rain: narrower, fainter and far denser than snow, because a raindrop over a
   still frame has to be almost invisible on its own and legible only as a
   mass. */
.hero-fall.rain{ opacity:.62 }
.hero-fall.rain::before{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2290%22%20height%3D%2290%22%3E%3Cpath%20d%3D%22M6.2%20-0.1l2.7%2013.7M6.2%2089.9l2.7%2013.7M82.6%2052.4l2.6%2013.1M18.4%2016.6l2.8%2013.9M76.2%2042.5l3.4%2017.2M41.2%2016.1l2.9%2014.6M32.9%20-10.8l2.8%2013.9M32.9%2079.2l2.8%2013.9M79.4%2038.7l3.1%2015.5M20.7%2044.0l2.5%2012.3M3.8%2040.0l2.8%2013.9M10.0%2025.3l2.5%2012.5M50.8%2032.7l3.3%2016.7M68.2%2024.0l2.9%2014.7M31.5%2061.5l3.4%2016.8M5.9%2035.1l3.2%2016.1M25.8%2072.2l2.3%2011.3M9.1%2019.4l2.7%2013.7M1.9%207.1l2.2%2011.0M91.9%207.1l2.2%2011.0M24.9%2069.8l2.5%2012.6M28.5%2015.5l2.7%2013.3%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.15%22%20stroke-width%3D%220.8%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:90px 90px; animation:hero-fall-90 1.4s linear infinite }
.hero-fall.rain::after{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22150%22%20height%3D%22150%22%3E%3Cpath%20d%3D%22M122.8%2094.3l4.2%2020.8M66.8%2067.0l4.6%2022.9M56.8%20-7.0l3.8%2019.1M56.8%20143.0l3.8%2019.1M33.3%20104.6l5.1%2025.3M86.5%20-12.9l4.3%2021.6M86.5%20137.1l4.3%2021.6M141.3%20-18.2l3.9%2019.3M141.3%20131.8l3.9%2019.3M64.6%2013.1l3.7%2018.3M-0.8%20-14.9l4.4%2021.8M-0.8%20135.1l4.4%2021.8M149.2%20-14.9l4.4%2021.8M149.2%20135.1l4.4%2021.8M64.4%20128.6l3.6%2018.2M96.3%20101.7l3.9%2019.4M11.9%20118.7l3.4%2016.8M55.6%205.7l3.6%2018.1M92.7%2065.4l4.6%2023.0M121.2%20120.3l5.4%2027.0M22.2%2082.2l4.0%2019.9M80.6%2030.0l3.4%2016.9M129.4%205.1l3.6%2017.8%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.22%22%20stroke-width%3D%221.0%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:150px 150px; animation:hero-fall-150 .95s linear infinite }
.hero-fall.rain > i{
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22120%22%20height%3D%22120%22%3E%3Cpath%20d%3D%22M71.6%2035.9l5.5%2027.5M83.0%20-0.9l4.8%2024.0M83.0%20119.1l4.8%2024.0M-2.7%20-3.6l5.2%2026.2M-2.7%20116.4l5.2%2026.2M117.3%20-3.6l5.2%2026.2M117.3%20116.4l5.2%2026.2M40.0%2041.1l6.8%2034.1M108.5%2094.8l4.6%2022.8M1.2%2051.9l6.7%2033.3M121.2%2051.9l6.7%2033.3M70.1%2027.3l4.5%2022.6M67.6%2018.6l7.2%2036.1M66.1%2053.6l7.4%2037.0M66.9%20-23.0l5.9%2029.4M66.9%2097.0l5.9%2029.4M51.2%2047.3l7.0%2034.8M16.4%20-19.8l6.7%2033.5M16.4%20100.2l6.7%2033.5M76.0%2012.6l5.4%2027.1M109.5%2073.4l5.7%2028.7M3.0%20-14.7l4.9%2024.7M3.0%20105.3l4.9%2024.7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-opacity%3D%22.34%22%20stroke-width%3D%221.2%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  background-size:120px 120px; animation:hero-fall-120 .62s linear infinite }

@keyframes hero-fall-80 { from{background-position:0 0} to{background-position:80px 400px} }
@keyframes hero-fall-90 { from{background-position:0 0} to{background-position:90px 450px} }
@keyframes hero-fall-110{ from{background-position:0 0} to{background-position:110px 550px} }
@keyframes hero-fall-120{ from{background-position:0 0} to{background-position:120px 600px} }
@keyframes hero-fall-150{ from{background-position:0 0} to{background-position:150px 750px} }
@media (prefers-reduced-motion:reduce){
  .hero-fall,.hero-fall::before,.hero-fall::after{ animation:none }
}

/* THE READINGS GO TOP-LEFT, AND THE BOTTOM IS A PROTECTED ZONE.
 *
 * Every render places its lockup bottom-centre. Nothing this app draws may
 * land there — not a scrim, not a label, not a badge. Top-left is safe on all
 * of them, because a photograph of a mountain has sky in the top corners. */
.hero-read{
  position:absolute; z-index:5; left:0; right:0; top:0;
  display:flex; flex-wrap:wrap; gap:3px 12px; align-items:baseline;
  padding:9px 12px 22px;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.05em;
  color:#eaf2fb; text-shadow:0 1px 3px rgba(3,7,14,.85);
  /* A short wash under the text only — reaching a third of the way down at
     most, so it never approaches the lockup. */
  background:linear-gradient(180deg, rgba(4,9,18,.62), rgba(4,9,18,.22) 58%, transparent);
  pointer-events:none;
}
.hero-when{ font-weight:700 }
.hero-warn{ color:var(--warn) }

/* The drawn fallback for a resort with no render yet: the true skyline of
   that range from its own elevation model. */
.hero-skyline{ position:absolute; inset:0; z-index:0 }
.hero-skyline svg{ width:100%; height:100%; display:block }

/* ══════════════════════════════════════════════════ MOTION AND STATE LAYER
 *
 * One place that decides how every control in this app responds to being
 * touched. Written as a layer rather than sprinkled through each component,
 * because the failure mode being fixed is INCONSISTENCY — a segment control
 * that brightened its border, an icon button that changed colour, a legend
 * that appeared in a single frame, all invented separately.
 */

/* Everything that can be pressed shares one transition and one focus ring. */
button, .seg button, .views button, .ib, .brand, .l2-full, .l2-min, .l2-close,
.legend2-mark, .hits li, .m3d-retry, .grip{
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease),
              opacity var(--dur) var(--ease);
}
button:focus-visible, .ib:focus-visible, .brand:focus-visible,
.seg button:focus-visible, .views button:focus-visible, .legend2-mark:focus-visible{
  outline:none; box-shadow:var(--ring);
}

/* State is a SURFACE WASH, not a border colour. Works on a dark ground, works
   at a glance, and simply does not appear on a touch device — which is
   correct, because on a touch device there is no hover to report. */
@media (hover:hover){
  .seg button:hover, .views button:hover, .ib:hover,
  .l2-full:hover, .l2-min:hover, .l2-close:hover, .legend2-mark:hover{
    background-color:var(--lift);
  }
  .hits li:hover{ background-color:var(--lift) }
}
.seg button:active, .views button:active, .ib:active,
.legend2-mark:active, .brand:active{ background-color:var(--lift-2) }

/* A press is acknowledged physically. Small — 2% — because anything larger
   reads as a toy, and because a gloved thumb is imprecise enough that a
   control which moves much is a control that gets mis-hit. */
.ib:active, .brand:active, .m3d-retry:active{ transform:scale(.97) }

/* Panels arrive rather than appear. */
.legend2, .rprev, .hits{
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}

/* ── ICONS ────────────────────────────────────────────────────────────────
 * One weight, one join, one cap, sized by the text beside them. They were
 * drawn at 15px, 20px, 24px and 27px with stroke widths from 1.4 to 2, which
 * is why a row of them never looked like a set. Inheriting colour is the
 * other half: an icon that specifies its own colour cannot participate in the
 * hover and active states above. */
svg[data-i], .ib svg, .seg svg, .views svg, .find svg{
  width:18px; height:18px; flex:0 0 auto;
  stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round;
  color:inherit;
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
 * Honoured properly: transitions collapse to nothing rather than merely
 * shortening. Someone who asks for no motion has usually asked because
 * motion makes them ill, and a fast animation is still an animation. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    transition-duration:.01ms !important;
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
}

/* The air row sits under the snow row, quieter by a step. Snow is why someone
   opened the app; the atmosphere is what they check second. Same chip shape,
   so the two rows read as one block rather than two competing bands. */
.g-air{margin-top:6px;opacity:.92}
.g-air .g-chip b{font-size:12.5px}

/* ---- the tier: locked sections and the one calm upsell ---- */
.grp.locked .lock-body{
  display:flex; flex-wrap:wrap; align-items:center; gap:12px;
  background:linear-gradient(160deg, rgba(57,208,184,.07), rgba(57,208,184,.02));
  border:1px solid rgba(57,208,184,.22); border-radius:12px; padding:14px 16px;
}
.grp.locked .lock-body p{ margin:0; flex:1 1 220px; font-size:13px;
  line-height:1.5; color:var(--dim, #8fa0b2); }
.grp.locked .lock-body b{ color:#39d0b8; }
.lock-go{
  display:inline-block; background:#39d0b8; color:#04211c; font-weight:700;
  font-size:13px; padding:9px 16px; border-radius:9px; text-decoration:none;
  white-space:nowrap;
}
.lock-go:hover{ filter:brightness(1.08); }

.upsell{
  position:fixed; left:50%; bottom:calc(18px + env(safe-area-inset-bottom));
  transform:translateX(-50%); z-index:60; width:min(420px, calc(100vw - 28px));
  background:#0d141cf2; border:1px solid rgba(57,208,184,.3); border-radius:16px;
  padding:16px 18px; box-shadow:0 18px 50px -12px #000c; backdrop-filter:blur(8px);
}
.upsell .up-title{ font-weight:700; font-size:15px; margin-bottom:6px; color:#e8edf3; }
.upsell .up-body{ font-size:13px; line-height:1.5; color:#8fa0b2; margin-bottom:12px; }
.upsell .up-row{ display:flex; gap:10px; align-items:center; }
.upsell .up-go{
  background:#39d0b8; color:#04211c; font-weight:700; font-size:13.5px;
  padding:9px 18px; border-radius:9px; text-decoration:none;
}
.upsell .up-x{
  background:none; border:none; color:#5c6b7c; font-size:13px; cursor:pointer;
  padding:9px 10px; font-family:inherit;
}
.upsell .up-x:hover{ color:#8fa0b2; }
