/* ==========================================================================
 * lc-storm-ribbon.css — the storm ribbon: a slim band above the menu bar.
 *
 * REGISTER. Around here a thunderstorm is a thing to WATCH, so this is a cover
 * flash, not an alert bar. No hazard red, no stripes, no siren. The ground is
 * storm-sky slate, the state word is the same lightning yellow the header's
 * weather chip already draws its bolt in (#f6c445), and the sentence is set in
 * the magazine's own PT Serif. An NWS warning escalates by going DARKER and
 * carrying more fact — never by turning into a klaxon.
 *
 * NOTHING HERE IS INVENTED. Every value is the live theme's or an existing
 * file's: PT Serif + Montserrat from theme.css (only the 500 face is served, so
 * chrome labels use the site's established 600 + 2px-tracked caps), the accent
 * #e44e56, the dark glass and mutes from lc-weather-nav.css, the radar frame
 * grammar — flat square, warm inset rule, hard black corner tag — lifted from
 * .lcm-wx-radar in lc-megamenu.css, and the easing/duration tokens from the
 * motion kernel (lc-motion.css: --lc-ease, --lc-t-base).
 *
 * ⚠ NEVER width:100vw in here. It loops the UIkit sticky header on this site.
 *   The ribbon sits inside .tm-header / .tm-header-mobile, both of which are
 *   position:absolute (tm-header-overlay), so a plain block is already full
 *   width and costs the page zero layout.
 * ========================================================================== */

.lc-storm {
  /* Storm slate. Dark enough to sit on a bright sky photo AND on the pale
     Windy map that /weather/ puts behind the header. */
  --lcs-ground:      #141d27;
  --lcs-ground-warn: #0c1219;
  --lcs-ink:         #f2f5f8;
  --lcs-mute:        #93a3b3;   /* 6.5:1 on the ground */
  --lcs-bolt:        #f6c445;   /* the bolt colour lc-weather-nav.js already draws */
  --lcs-accent:      #e44e56;   /* the theme's own accent — warning state only */
  --lcs-rule:        rgba(255, 255, 255, .12);
  --lcs-serif:       'PT Serif', Georgia, 'Times New Roman', serif;
  --lcs-sans:        Montserrat, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --lcs-h:           40px;

  position: relative;
  z-index: 995;                 /* over .uk-navbar-container (990), under the concierge */
  display: block;
  background: var(--lcs-ground);
  color: var(--lcs-ink);
  box-shadow: inset 0 -1px 0 var(--lcs-rule);

  /* The entrance. State arrives over REST a few hundred ms after paint, so the
     ribbon opens rather than snapping in. It costs the PAGE nothing — the header
     is an absolute overlay, so only the floating navbar slides down to make room
     and no content below moves (CLS stays 0).
     0fr→1fr rather than a height, because the bar is not always one line high:
     a warning on a phone runs to two rows and at 360px to three, and any fixed
     height would be a second source of truth that silently drifts from them. */
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--lc-t-base, .32s) var(--lc-ease, cubic-bezier(.22,.72,0,1));
}
/* width:100% is load-bearing: .uk-container carries `margin: 0 auto`, and auto
   margins on a grid item beat the default stretch, which would shrink the bar
   to its content and centre it. */
/* box-sizing is set HERE and not left to the reset below: the theme's own
   .uk-container ties that reset on specificity and loads after it, so the
   container falls back to content-box and its 40px gutters are ADDED to the
   100% width. That pushed the bar 80px past the viewport and carried the
   dismiss control off the right edge entirely. The child combinator outranks
   the theme rule whatever the load order. */
.lc-storm > .uk-container { width: 100%; min-height: 0; overflow: hidden; box-sizing: border-box; }
.lc-storm.is-in { grid-template-rows: 1fr; }
/* Dropped once the entrance has settled so the radar preview can escape the bar. */
.lc-storm.is-settled > .uk-container { overflow: visible; }
/* Collapsed is not enough: a zero-height bar still holds tabbable buttons and a
   landmark. Once the retract has played out the bar leaves the page entirely. */
.lc-storm.is-out { display: none; }

.lc-storm.is-warning {
  background: var(--lcs-ground-warn);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .16);
}

.lc-storm *,
.lc-storm *::before,
.lc-storm *::after { box-sizing: border-box; }

.lc-storm :focus-visible {
  outline: 2px solid var(--lcs-bolt);
  outline-offset: -2px;
  border-radius: 1px;
}

/* The bar sits inside .uk-container.uk-container-xlarge so its left edge lands
   on the logo's, not on some private gutter of our own. */
.lc-storm__bar {
  position: relative;             /* the radar preview aligns to the page gutter,
                                     not to the 90px chip it hangs off */
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: var(--lcs-h);       /* a floor, never a cap */
  min-width: 0;
}

/* ------------------------------------------------------------- the statement */

.lc-storm__say {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  /* Smaller than the gaps inside the text because the glyph carries its own
     margin of empty artboard on the left. */
  gap: 8px;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
a.lc-storm__say:hover,
a.lc-storm__say:focus-visible { color: inherit; text-decoration: none; }

.lc-storm__glyph {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
}
.lc-storm__glyph svg { display: block; width: 26px; height: 26px; }

/* One shared baseline for the three type registers. On a desktop the state
   word and the fact sit on the same line; the nesting exists so a phone can
   break them without a second markup path. */
.lc-storm__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 11px;
}
.lc-storm__fact {
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 11px;
}

/* The one authored motion moment: the bolt flickers, rarely and briefly, the
   way lightning does. Everything else on this bar is still. */
.lc-storm__glyph .lcs-bolt {
  animation: lcs-flicker 7s steps(1, end) 1.2s infinite;
  transform-origin: center;
}
@keyframes lcs-flicker {
  0%, 3%, 6%, 100% { opacity: 1 }
  1.5%, 4.5%       { opacity: .18 }
}

/* State word. The site's chrome label grammar, verbatim: Montserrat 600, 2px
   tracked, uppercase — the same setting the directories use for every kicker. */
.lc-storm__kicker {
  flex: 0 0 auto;
  font: 600 11px/1 var(--lcs-sans);
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--lcs-bolt);
  white-space: nowrap;
}

/* The news, in the magazine's voice rather than the interface's. It is the
   biggest thing on the bar because the town IS the story. */
.lc-storm__line {
  flex: 0 1 auto;
  min-width: 0;
  font: 400 16px/1 var(--lcs-serif);
  color: var(--lcs-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Fact that only exists in the escalated state: when the warning expires. */
.lc-storm__until {
  flex: 0 0 auto;
  font: 600 10px/1 var(--lcs-sans);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--lcs-mute);
  padding: 5px 0 5px 11px;
  border-left: 1px solid var(--lcs-rule);
}

/* The escalation mark: a small solid square before the event name, the way a
   magazine flags a late-breaking line. Not a stripe, not an icon, not a siren. */
.lc-storm.is-warning .lc-storm__kicker { color: var(--lcs-accent); }
.lc-storm.is-warning .lc-storm__kicker::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 9px;
  vertical-align: 0;
  background: var(--lcs-accent);
}

/* The invitation. Same anchor as the statement, parked on the right, so the
   whole bar is one link and one destination. */
.lc-storm__go {
  flex: 0 0 auto;
  margin-left: auto;
  font: 600 11px/1 var(--lcs-sans);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--lcs-ink);
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: border-color .18s ease, color .18s ease;
}
.lc-storm__go i { font-style: normal; padding-left: 7px; }
.lc-storm__say:hover .lc-storm__go,
.lc-storm__say:focus-visible .lc-storm__go { border-bottom-color: var(--lcs-bolt); }

/* ------------------------------------------------------------------- radar */

/* A hairline groups the two things you can DO with the bar and separates them
   from the thing it SAYS. Without it the right end reads as loose parts. */
.lc-storm__radar {
  position: static;               /* the preview anchors to .lc-storm__bar */
  flex: 0 0 auto;
  padding-left: 15px;
  border-left: 1px solid var(--lcs-rule);
  line-height: 0;
}

.lc-storm__peek {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--lcs-rule);
  border-radius: 999px;
  background: transparent;
  color: var(--lcs-ink);
  font: 600 10px/1 var(--lcs-sans);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color .18s ease, border-color .18s ease;
}
.lc-storm__peek svg { display: block; width: 14px; height: 14px; }
.lc-storm__peek:hover,
.lc-storm__radar.is-open .lc-storm__peek {
  background: rgba(255, 255, 255, .10);
  border-color: rgba(255, 255, 255, .28);
}
.lc-storm__peek[disabled] { opacity: .4; cursor: default; }

/* The preview. Frame grammar inherited from .lcm-wx-radar in lc-megamenu.css —
   flat, square, a hairline inset rule and a hard corner tag. Values retuned for
   a dark ground; the shape is the site's, not a new one. */
.lc-storm__scope {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  width: 350px;
  background: #0d151d;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12),
              0 24px 60px rgba(6, 12, 20, .55);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .2s var(--lc-ease, ease), transform .2s var(--lc-ease, ease),
              visibility 0s linear .2s;
}
.lc-storm__radar.is-open .lc-storm__scope {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity .2s var(--lc-ease, ease), transform .2s var(--lc-ease, ease);
}
/* Hover bridge: the 9px gap between chip and panel must not be a dead zone. */
.lc-storm__scope::before {
  content: '';
  position: absolute;
  top: -11px; left: 0; right: 0; height: 11px;
}

.lc-storm__frame {
  position: relative;
  display: block;
  height: 350px;
  overflow: hidden;
  background: #0d151d;
  /* The kernel's pending veil paints on --lc-bg; point it at the frame's own
     ground so the 200ms gate never flashes white into a dark panel. */
  --lc-bg: #0d151d;
  --lc-rose: var(--lcs-bolt);
  color: var(--lcs-mute);
}

/* THE CROP. Windy anchors its own furniture — logo, overlay picker, zoom
   buttons, play bar, mm/h legend — to the edges of ITS OWN viewport. Rendering
   the iframe larger than the window it shows through pushes every one of them
   out of sight, and the preview stops being an embedded third-party widget and
   starts being a picture of the sky. The overhang is symmetric, so the town the
   map is centred on stays centred. Anyone who wants the instrument itself —
   the loop, the scrubber, the legend — has "Watch it come in" and "Full map",
   which is exactly what makes those links worth clicking. */
.lc-storm__frame { --lcs-crop-y: 100px; --lcs-crop-x: 46px; }
.lc-storm__frame iframe {
  position: absolute;
  left: calc(-1 * var(--lcs-crop-x));
  top: calc(-1 * var(--lcs-crop-y));
  width: calc(100% + 2 * var(--lcs-crop-x));
  height: calc(100% + 2 * var(--lcs-crop-y));
  border: 0;
  display: block;
  /* The map is a picture here, not a control surface. */
  pointer-events: none;
}
/* The kernel puts its wait label at the host's top-left, which on this frame is
   exactly where the corner tag already is — left alone the two print over each
   other and neither is readable. The wait takes the opposite corner instead, so
   the frame is labelled top-left and narrated bottom-left. */
.lc-storm__frame .lc-pending-label {
  top: auto;
  bottom: 12px;
  left: 10px;
}

/* Above the motion kernel's pending veil (z-index 6) — the frame should be
   labelled while the map is still on its way, not after. */
.lc-storm__tag {
  position: absolute;
  left: 0; top: 0;
  z-index: 8;
  background: #000;
  color: #fff;
  font: 600 9px/1 var(--lcs-sans);
  letter-spacing: 1.8px;
  text-transform: uppercase;
  padding: 5px 9px;
  pointer-events: none;
}

/* ------------------------------------------------------- when it never comes */

/* Windy is a third party and a routinely blocked one — an ad or privacy
   extension takes the iframe out and leaves a 350px black square behind. This
   is what stands there instead: the bar's own three registers, on the frame's
   own ground, in the frame's own colours. Nothing new is introduced; the state
   simply speaks in the voice the ribbon already has. */
.lc-storm__dark {
  position: absolute;
  inset: 0;
  z-index: 7;                     /* over the iframe, under the corner tag (8) */
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0 34px;
  text-align: center;
  background: #0d151d;
}
.lc-storm__frame.is-dark .lc-storm__dark {
  display: flex;
  /* The kernel's own gate tokens, so the swap at the end of the wait reads as
     an answer rather than as the panel breaking. */
  animation: lcs-dark-in var(--lc-t-base, .32s) var(--lc-ease, ease) both;
}
@keyframes lcs-dark-in { from { opacity: 0 } to { opacity: 1 } }

/* The subject of the absence. Muted enough that it reads as a mark rather than
   as a control, and small enough that the sentence still leads. */
.lc-storm__dark-mark {
  display: block;
  margin-bottom: 15px;
  color: var(--lcs-mute);
  opacity: .55;
}
.lc-storm__dark-mark svg { display: block; width: 30px; height: 30px; }

/* The news, in the same serif and at the same size the bar sets the town in. */
.lc-storm__dark-line {
  margin: 0;
  font: 400 16px/1.45 var(--lcs-serif);
  color: var(--lcs-ink);
}
/* The reason, quietly, and kept close to the line it belongs to. Named because
   "could not load" with no cause is a message a reader can do nothing with. */
.lc-storm__dark-why {
  margin: 6px 0 0;
  max-width: 32ch;
  font: 400 12px/1.5 var(--lcs-sans);
  color: var(--lcs-mute);
}
/* The way out, in the caption rail's exact grammar — it IS that link, moved to
   where the reader is now looking. Set further off than the two lines are from
   each other: they are one statement, this is the thing to do about it. */
.lc-storm__dark-go {
  margin-top: 18px;
  font: 600 10px/1 var(--lcs-sans);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--lcs-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--lcs-rule);
  padding-bottom: 3px;
  transition: border-color .18s ease;
}
.lc-storm__dark-go:hover,
.lc-storm__dark-go:focus-visible { color: var(--lcs-ink); border-bottom-color: var(--lcs-bolt); }

/* One way out, not two of them nine pixels apart: while the frame is dark the
   caption rail hands its link over and keeps the place and the close. */
.lc-storm__frame.is-dark + .lc-storm__cap .lc-storm__cap-go { display: none; }

.lc-storm__cap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  height: 38px;
  padding: 0 11px;
  border-top: 1px solid rgba(255, 255, 255, .10);
}
.lc-storm__cap-where {
  min-width: 0;                   /* or a flex item never lets the ellipsis fire */
  font: 600 10px/1 var(--lcs-sans);
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--lcs-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lc-storm__cap-right {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
}
.lc-storm__cap-go {
  flex: 0 0 auto;
  font: 600 10px/1 var(--lcs-sans);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--lcs-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--lcs-rule);
  padding-bottom: 2px;
  transition: border-color .18s ease;
}
.lc-storm__cap-go:hover,
.lc-storm__cap-go:focus-visible { color: var(--lcs-ink); border-bottom-color: var(--lcs-bolt); }

/* Close, on the preview. Only shown where there is no pointer to move away. */
.lc-storm__scope-x { display: none; }

/* ----------------------------------------------------------------- dismiss */

.lc-storm__x {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin-right: -7px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--lcs-mute);
  cursor: pointer;
  transition: background-color .18s ease, color .18s ease;
}
.lc-storm__x svg { display: block; width: 12px; height: 12px; }
.lc-storm__x:hover { background: rgba(255, 255, 255, .10); color: var(--lcs-ink); }

/* ------------------------------------------------------------------ mobile */

@media (max-width: 959px) {
  .lc-storm { --lcs-h: 44px; }
  .lc-storm__bar { gap: 8px; }
  .lc-storm__glyph svg { width: 21px; height: 21px; }
  .lc-storm__say { gap: 8px; }
  .lc-storm__text { gap: 9px; }
  .lc-storm__fact { gap: 9px; }
  /* Every point given back here is a point the town name gets to keep. */
  .lc-storm__kicker { font-size: 9.5px; letter-spacing: 1.3px; }
  .lc-storm__line { font-size: 14px; }
  .lc-storm__until { font-size: 9px; letter-spacing: 1.2px; padding-left: 9px; }

  /* A National Weather Service event name, a place and an expiry have never
     once fitted across 414px on one line, and squeezing them cost the place
     entirely. The warning takes a second line, and the extra height IS part of
     the escalation — it is the one state that has earned the room. The bar
     grows from the padding, so a third row at 360px needs no new number. */
  .lc-storm.is-warning .lc-storm__text {
    flex-direction: column;
    align-items: flex-start;
    gap: 5px;
  }
  .lc-storm.is-warning .lc-storm__bar { padding: 14px 0; }

  /* The trailing CTA goes first: on a phone the whole bar is already the tap
     target for /weather/, and the radar — the thing a phone actually came for
     — needs the room. */
  .lc-storm__go { display: none; }

  /* Icon only. The word RADAR costs 60px that the town name needs. */
  .lc-storm__peek {
    width: 34px;
    height: 34px;
    padding: 0;
    justify-content: center;
  }
  .lc-storm__peek span { display: none; }
  .lc-storm__peek svg { width: 17px; height: 17px; }

  .lc-storm__x { width: 34px; height: 34px; margin-right: -9px; }

  /* Full width between the page's own gutters rather than hanging off a 34px
     button. It overlays the hero; the navbar never moves. */
  .lc-storm__scope {
    left: 0;
    right: 0;
    width: auto;
    top: calc(100% + 6px);
  }
  .lc-storm__frame { height: 300px; --lcs-crop-y: 98px; --lcs-crop-x: 40px; }

  /* A finger cannot "move away" from a hover, so the panel gets a real close. */
  .lc-storm__scope-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-right: -9px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--lcs-mute);
    cursor: pointer;
  }
  .lc-storm__scope-x svg { display: block; width: 12px; height: 12px; }
}

/* On a phone the place name has first claim on the line. Losing "with hail" is
   a smaller loss than an ellipsis that reads as if the town got cut in half;
   the clause survives in the title either way. */
@media (max-width: 520px) {
  .lc-storm__hail { display: none; }
}

/* At 360px even the warning's second line cannot hold a place AND an expiry,
   and both of those are the reason someone reads a warning at all. The expiry
   wraps to a row of its own rather than either fact being cut. */
@media (max-width: 420px) {
  .lc-storm.is-warning .lc-storm__fact { flex-wrap: wrap; gap: 5px 9px; }
  .lc-storm.is-warning .lc-storm__until {
    border-left: 0;
    padding: 0;
  }
}

/* ------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .lc-storm,
  .lc-storm > .uk-container,
  .lc-storm__scope,
  .lc-storm__go,
  .lc-storm__peek,
  .lc-storm__x { transition: none !important; }
  .lc-storm__glyph .lcs-bolt { animation: none !important; }
  .lc-storm__frame.is-dark .lc-storm__dark { animation: none !important; }
}
