/**
 * @purpose Style Overwatch world-map chrome, compact LIVE feed, per-panel overlay controls, and responsive details sheet.
 * @owner cic/topology/overwatch/overwatch.css
 * @inputs Overwatch markup from overwatch.js and map workspace containers.
 * @outputs Compact horizontal LIVE rail and existing drawer/sheet breakpoints.
 * @seam cic/topology/overwatch/overwatch.js
 * @blast-radius World Map layout and LIVE feed presentation.
 * @verification node --test cic/topology/overwatch/imported-reports.test.mjs
 * @tests cic/topology/overwatch/imported-reports.test.mjs
 */
:root{color-scheme:dark;--ow-bg:#071019;--ow-panel:#101d27;--ow-line:#315269;--ow-accent:#53d0be;--ow-text:#e8f2f4;--ow-danger:#ff5c67}*{box-sizing:border-box}body{margin:0;background:var(--ow-bg);color:var(--ow-text);font-family:system-ui,sans-serif}.ow-main{padding:1.5rem 2rem 2rem;width:min(100%,1500px);margin:auto}.ow-heading h1{margin:.15rem 0}.ow-panel,.ow-map-shell{border:1px solid var(--ow-line);background:var(--ow-panel);border-radius:.6rem;overflow:hidden}.ow-toolbar{padding:1rem;display:flex;justify-content:space-between;gap:1rem}.ow-toolbar input{margin-left:.5rem}.ow-catalog{display:grid;grid-template-columns:repeat(3,1fr);gap:.7rem;padding:1rem}.ow-card{padding:1rem;border:1px solid var(--ow-line);border-radius:.45rem}.ow-card.is-implemented{border-inline-start:.3rem solid var(--ow-danger)}.ow-card h2{font-size:1.05rem}.ow-row-state{display:flex;align-items:flex-start;justify-content:space-between;gap:.75rem;font-size:.8rem}.ow-implemented{display:inline-flex;align-items:center;gap:.4rem;font-weight:700}.ow-star{color:var(--ow-danger);font-size:1.2rem;line-height:1}.ow-planned{font-weight:650}.ow-meta,.ow-features{font-size:.82rem;opacity:.78}.ow-rank{display:flex;gap:.3rem}.ow-rank button[aria-pressed=true]{background:var(--ow-accent);color:#061314}.ow-overlaps,.ow-evidence{margin-top:.85rem;border-top:1px solid var(--ow-line);padding-top:.7rem}.ow-overlaps summary,.ow-evidence summary{cursor:pointer;font-weight:700}.ow-overlaps ul,.ow-evidence ul{padding-left:1.1rem}.ow-overlaps li{margin:.55rem 0}.ow-overlaps li span{display:block;font-size:.82rem;opacity:.8}.ow-strike-symbol{display:inline-grid;place-items:center;width:1.4rem;height:1.4rem;color:var(--ow-danger);border:2px solid #fff;border-radius:50%;font-weight:900}.ow-evidence-warning{padding:.6rem;border:1px solid var(--ow-danger);border-radius:.3rem}.ow-map-shell{position:relative;height:72vh}.ow-map-shell #map{position:absolute;inset:0}.ow-layers{position:absolute;z-index:3;right:1rem;top:1rem;width:min(290px,calc(100% - 2rem));padding:1rem;background:color-mix(in srgb,var(--ow-panel) 88%,transparent);border:1px solid var(--ow-line);border-radius:.5rem;backdrop-filter:blur(12px)}.ow-layers[hidden]{display:none}.maplibregl-canvas{outline:none}@media(width < 900px){.ow-catalog{grid-template-columns:1fr 1fr}}@media(width < 700px){.ow-main{padding:1rem .7rem .7rem}.ow-map-shell{height:75vh}.ow-catalog{grid-template-columns:1fr}.ow-row-state{display:block}.ow-row-state label{display:block;margin-top:.5rem}}
.ow-evidence-policy{margin:1rem;padding:.8rem 1rem;border:1px solid var(--ow-danger);border-radius:.4rem;background:var(--ow-bg);line-height:1.45}

/* Timeline: sweep the last 12 months, or type an exact instant. */
.ow-timeline{display:flex;align-items:center;gap:.6rem;margin:.6rem 0 0;flex-wrap:wrap}
.ow-timeline #timeSlider{flex:1 1 22rem;min-width:14rem;accent-color:#2f6fed}
.ow-timeline #timeReadout{font-variant-numeric:tabular-nums;opacity:.85;min-width:16rem}
.ow-timeline button,.ow-timeline input[type=datetime-local]{
  background:rgba(255,255,255,.06);color:inherit;border:1px solid rgba(255,255,255,.18);
  border-radius:.35rem;padding:.25rem .55rem;font:inherit}
/* Native date fields and generated status strings must yield to a narrow map
   column instead of widening the document. The timeline can wrap vertically. */
.ow-timeline #timeInstant{min-width:0;max-width:100%;width:min(100%,16rem)}
.ow-timeline #timeReadout{min-width:0;max-width:100%;overflow-wrap:anywhere}

/* Layers panel hosted in the shell context drawer slot: static, full width, drawer tokens. */
#context-module-slot .ow-layers{position:static;width:auto;margin:0;padding:.55rem;background:var(--surface-subtle);border:1px solid var(--line);border-radius:0;backdrop-filter:none;color:var(--ink)}
#context-module-slot .ow-layers h2{margin:0 0 .35rem;color:var(--ink);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase}
#context-module-slot .ow-layers label{font-size:.7rem}
#context-module-slot .ow-layers input[type=checkbox]{width:auto;min-width:0;margin:0;flex:0 0 auto}
.ow-layers input[type=checkbox]{width:auto;min-width:0;margin:0;flex:0 0 auto}
#context-module-slot .ow-layers #mapStatus{margin:.5rem 0 0;color:var(--muted);font-size:.62rem;line-height:1.35}

/* Planning cards share the page's broad input default, which is sized for a
   standalone field. Keep only text-like controls column-bound; checkboxes
   retain their intrinsic size so they cannot establish a wide grid minimum. */
.ow-toolbar,.ow-toolbar label,.ow-catalog,.ow-card,.ow-row-state{min-width:0}
.ow-toolbar{flex-wrap:wrap}
.ow-toolbar label{flex:1 1 16rem}
.ow-toolbar input[type=search]{width:100%;max-width:100%;min-width:0;margin-left:0}
.ow-toolbar button{flex:0 0 auto}
.ow-catalog{grid-template-columns:repeat(3,minmax(0,1fr))}
.ow-card input[type=checkbox],.ow-card input[type=radio]{width:auto;min-width:0;margin:0;flex:0 0 auto}
.ow-card input:not([type]),.ow-card input[type=text],.ow-card input[type=search],.ow-card input[type=number],.ow-card input[type=date],.ow-card input[type=datetime-local],.ow-card textarea,.ow-card select{width:100%;max-width:100%;min-width:0}
@media(width < 900px){.ow-catalog{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(width < 700px){.ow-catalog{grid-template-columns:minmax(0,1fr)}.ow-toolbar button{width:100%}}

/* ============================================================================
   Globe: overlay canvas, control cluster, and the context-drawer readout.
   Owned by globe-view.js, which creates all of this markup at runtime.
   Deliberately uses this surface's own --ow-* tokens. The taskbar's --bar-*
   tokens live in shell-tokens.css and are not forked here.
   ========================================================================== */

/* The overlay sits above the map canvas and below the timeline and panels, and
   never takes pointer events -- every drag, zoom and rotate must still reach
   MapLibre underneath. */
.ow-globe-layer{position:absolute;inset:0;z-index:2;pointer-events:none}
/* TWO STACKED CANVASES, both filling the layer. The shading is evaluated per
   pixel on the GPU on `.ow-globe-shade`; the vector decoration -- marks, orbit
   track, labels -- stays in 2D on `.ow-globe-canvas` above it. They must
   OVERLAP, so both are absolutely positioned; left in normal flow the second
   one would sit below the first and the overlay would be twice the height of
   the map. */
.ow-globe-layer>canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
.ow-globe-canvas{display:block;width:100%;height:100%}

/* In normal flow directly beneath .ow-timeline, forming a second toolbar row
   over the map. NOT absolutely positioned at the bottom of the shell: the shell's
   identity splash overlaps that region at ordinary viewport heights, and the
   controls were invisible there. */
.ow-globe-controls{
  position:relative;z-index:4;
  display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;
  /* Only as wide as its contents: a full-width bar sits on top of MapLibre's
     own zoom/compass controls in the top-right corner of the map. */
  width:fit-content;max-width:calc(100% - 3.5rem);
  margin:.4rem 0 0;padding:.35rem .5rem;
  background:color-mix(in srgb,var(--ow-panel) 82%,transparent);
  border:1px solid var(--ow-line);border-radius:.4rem;
  backdrop-filter:blur(12px);
  font-size:.72rem
}
.ow-g-btn{
  font:inherit;color:var(--ow-text);cursor:pointer;
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.18);
  border-radius:.3rem;padding:.25rem .5rem;white-space:nowrap
}
.ow-g-btn:hover{background:rgba(255,255,255,.12)}
.ow-g-btn:focus-visible{outline:2px solid var(--ow-accent);outline-offset:1px}
/* "On" is carried by colour AND by the button's own text ("Satellite on" /
   "Satellite off"), so the state does not depend on colour perception. */
.ow-g-btn[data-on="yes"]{background:color-mix(in srgb,var(--ow-accent) 26%,transparent);border-color:var(--ow-accent)}
.ow-g-ghost{opacity:.85}
.ow-g-rate{display:inline-flex;align-items:center;gap:.25rem;opacity:.9}
.ow-g-rate input{
  font:inherit;width:6.2rem;color:var(--ow-text);
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.18);
  border-radius:.3rem;padding:.22rem .3rem;
  font-variant-numeric:tabular-nums
}
.ow-g-rate input:focus-visible{outline:2px solid var(--ow-accent);outline-offset:1px}
.ow-g-rate span{opacity:.6}

/* Readout lives inside the Layers panel, which the shell hosts in its Context
   drawer, so it inherits the drawer tokens there rather than this surface's. */
.ow-globe-readout{margin-top:.6rem;border-top:1px solid var(--ow-line);padding-top:.5rem}
.ow-globe-readout dl{
  display:grid;grid-template-columns:auto 1fr;gap:.12rem .5rem;
  margin:0;font-size:.62rem;line-height:1.35
}
.ow-globe-readout dt{opacity:.62;white-space:nowrap}
.ow-globe-readout dd{margin:0;font-variant-numeric:tabular-nums}
#context-module-slot .ow-globe-readout{border-top-color:var(--line)}
#context-module-slot .ow-globe-readout dt{color:var(--muted)}
#context-module-slot .ow-globe-readout dd{color:var(--ink)}

@media(width < 700px){
  .ow-globe-controls{font-size:.68rem;gap:.3rem}
  .ow-g-rate input{width:5rem}
}

/* A reduced-motion user gets a still globe by default (globe-view.js sets the
   default from this same query); the control stays available either way. */
@media(prefers-reduced-motion:reduce){
  .ow-globe-canvas{transition:none}
}

/* ============================================================================
   Motion control (shell capability, ../motion-control.js) consumed here.
   The drawer itself is styled by ../motion-control.css; these two rules are the
   consumer's side only.
   ========================================================================== */

/* globe-view.js makes #mapView focusable so the arrow-key path can be reached
   with the keyboard alone, not only by clicking the map first. A focusable
   region with no visible focus is a trap for anyone navigating by keyboard, so
   it gets a ring -- :focus-visible, so a mouse click does not draw one. */
.ow-map-shell:focus { outline: none; }
.ow-map-shell:focus-visible { outline: 2px solid var(--ow-accent); outline-offset: 2px }

/* ============================================================================
   Click inspector — the little translucent card (overwatch.js builds the markup)
   ============================================================================
   CONTRAST IS COMPUTED, NOT EYEBALLED. A translucent card over a map has no
   fixed background: it sits over the day side, the night side and the moving
   terminator. So every colour below is chosen to clear WCAG AA (4.5:1) against
   BOTH extremes of what can possibly be behind it -- the card composited at its
   own alpha over pure white, and over pure black. Any real backdrop lies
   between those, and `backdrop-filter: blur()` averages neighbouring pixels so
   it cannot escape them either. Clearing both bounds therefore clears every
   background this card can ever have, in both themes, without sampling a single
   screenshot.

   Measured floors (min of over-white / over-black), dark then light:
     text 13.71 / 13.50 · muted 7.88 / 6.63 · eyebrow 9.73 / 5.26
     banner 10.76 / 6.08 · link 9.51 / 6.96      -- lowest of all ten: 5.26:1
   `motion-control.verify.js` recomputes these from the live computed styles, so
   a later colour change cannot quietly drop below the bar.

   No `saturate()` in the backdrop filter, deliberately: blur is provably
   bounded by the extremes of its input, which is what makes the argument above
   hold. */
.ow-inspect{
  --ins-bg: 6, 13, 20;
  --ins-alpha: .90;
  --ins-text: #eaf3f6;
  --ins-muted: #a9bcc9;
  --ins-eyebrow: #7ee0b8;
  --ins-banner: #ffd08a;
  --ins-link: #9fd0ff;
  --ins-edge: rgba(126, 224, 184, .45);

  position:absolute; z-index:6;
  width:min(19rem, calc(100% - 1.5rem));
  max-height:min(23rem, 70%);
  overflow:auto;
  padding:.55rem .65rem .5rem;
  border:1px solid var(--ins-edge);
  border-radius:.45rem;
  background:rgba(var(--ins-bg), var(--ins-alpha));
  backdrop-filter:blur(10px);
  box-shadow:0 .5rem 1.4rem rgb(0 0 0 / .45);
  color:var(--ins-text);
  font-size:.7rem; line-height:1.4;
}
.ow-inspect[hidden]{display:none}

/* Decoration and placeholders are marked THREE ways at once -- a dashed border,
   a different accent, and the "Not an observation" banner in words -- so the
   distinction never rests on colour perception alone. Fable's first point about
   this surface is that it is read as evidence; a computed position drawn in the
   same idiom as a measured one invites exactly that mistake. */
.ow-inspect[data-tone="decoration"]{ --ins-edge:rgba(255, 208, 138, .6); border-style:dashed }
.ow-inspect[data-tone="empty"]{ --ins-edge:rgba(169, 188, 201, .38); --ins-eyebrow:#a9bcc9 }

.ow-inspect-head{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem}
.ow-inspect-eyebrow{
  margin:0;color:var(--ins-eyebrow);
  font-size:.56rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase
}
.ow-inspect-x{
  flex:none;width:1.25rem;height:1.25rem;margin:-.15rem -.15rem 0 0;padding:0;
  border:1px solid transparent;border-radius:.2rem;background:transparent;
  color:var(--ins-muted);font-size:.95rem;line-height:1;cursor:pointer
}
.ow-inspect-x:hover,.ow-inspect-x:focus-visible{color:var(--ins-text);border-color:var(--ins-edge);outline:none}
.ow-inspect-title{margin:.15rem 0 .35rem;font-size:.82rem;line-height:1.25;color:var(--ins-text)}
.ow-inspect-banner{
  margin:0 0 .4rem;padding:.3rem .4rem;
  border-left:.18rem solid var(--ins-banner);
  background:rgb(255 208 138 / .10);
  color:var(--ins-banner);font-size:.62rem;line-height:1.35
}
.ow-inspect-banner strong{color:var(--ins-banner)}
.ow-inspect-rows{
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:.1rem .55rem;margin:0;
  font-variant-numeric:tabular-nums
}
.ow-inspect-rows dt{color:var(--ins-muted);font-size:.62rem;white-space:nowrap}
.ow-inspect-rows dd{margin:0;font-size:.66rem;overflow-wrap:anywhere}
.ow-inspect-link{margin:.4rem 0 0;font-size:.62rem}
.ow-inspect-link a{color:var(--ins-link)}
.ow-inspect-foot{
  display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;
  margin-top:.45rem;padding-top:.35rem;border-top:1px solid rgb(255 255 255 / .10)
}
.ow-inspect-foot button{
  font:inherit;font-size:.6rem;color:var(--ins-text);cursor:pointer;
  background:rgb(255 255 255 / .07);border:1px solid var(--ins-edge);
  border-radius:.25rem;padding:.2rem .4rem
}
.ow-inspect-foot button:focus-visible{outline:2px solid var(--ins-eyebrow);outline-offset:1px}
.ow-inspect-foot small{color:var(--ins-muted);font-size:.56rem;line-height:1.3}

/* Light theme. Same construction, same bound, recomputed for a light card. */
:root[data-theme="light"] .ow-inspect{
  --ins-bg: 250, 252, 253;
  --ins-alpha: .92;
  --ins-text:#10202b;
  --ins-muted:#3c5262;
  --ins-eyebrow:#0f6b52;
  --ins-banner:#7a4a00;
  --ins-link:#0a4d8c;
  --ins-edge:rgba(15, 107, 82, .45);
  box-shadow:0 .5rem 1.4rem rgb(16 32 43 / .22)
}
:root[data-theme="light"] .ow-inspect[data-tone="decoration"]{ --ins-edge:rgba(122, 74, 0, .55) }
:root[data-theme="light"] .ow-inspect[data-tone="empty"]{ --ins-edge:rgba(60, 82, 98, .38); --ins-eyebrow:#3c5262 }
:root[data-theme="light"] .ow-inspect-banner{ background:rgb(122 74 0 / .10) }
:root[data-theme="light"] .ow-inspect-foot{ border-top-color:rgb(16 32 43 / .12) }
:root[data-theme="light"] .ow-inspect-foot button{ background:rgb(16 32 43 / .05) }

/* The map keeps a pointer cursor so it reads as clickable. */
#map .maplibregl-canvas{cursor:crosshair}

@media (prefers-reduced-motion: reduce){
  .ow-inspect{transition:none}
}

/* ============================================================================
   LAYOUT PASS — globe sized to the frame, controls in a side rail
   ============================================================================
   The map used to be `position:absolute; inset:0` inside the shell, with the
   timeline and the control cluster floating on top of it and the globe adrift
   in the middle of a large empty field. It is now a grid:

       +-----------------+--------+
       |       map       |  rail  |     <- the globe fills this row's height
       +-----------------+--------+
       |          timeline         |
       +---------------------------+

   `#map` is capped at the row height, so the map area is at most SQUARE and the
   globe -- drawn at 0.92 of the frame's shorter side by globe-view.js's
   fitGlobeToFrame() -- fills it. What is left over is the halo and the four
   corners of a circle in a square, which is as tight as a round planet gets.

   Below 820px the rail moves under the globe. That number is where the rail
   (9.5rem = 152px) plus the page gutter starts eating the map column faster
   than the globe can shrink to match: at 820 the map column is ~620px against a
   frame height of 78vh, so the globe is still height-limited; below it the
   globe becomes width-limited and shrinks for no reason other than the rail. */
.ow-map-shell{
  /* The map and its timeline are one operating unit. The old 72vh/780px frame
     pushed time controls below short landscape and tall-tablet viewports while
     leaving unused width beside a square map. */
  --ow-frame-h: min(55vh, 500px);
  position:relative;
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:var(--ow-frame-h) auto;
  grid-template-areas:"rail map" "time time";
  gap:.5rem;
  padding:.5rem;
  height:auto;
  width:min(100%,calc(var(--ow-frame-h) + 11rem));
  margin-inline:auto;
}
/* The rail is on the LEFT and the map hugs it, so the free space is all on the
   right -- which is where the shell's Context drawer slides in. With the rail on
   the right the drawer covered it completely the moment anyone opened Layers,
   and a control hidden behind the panel you opened to use it is worse than one
   that was never added. Screenshot at 1440 is what showed this; the CSS looked
   perfectly reasonable. */
.ow-map-shell #map{
  grid-area:map;
  position:relative; inset:auto;
  width:100%; max-width:var(--ow-frame-h); height:100%;
  justify-self:start;
  border:1px solid var(--ow-line); border-radius:.35rem; overflow:hidden;
}
.ow-map-shell .ow-timeline{grid-area:time;margin:0}

/* The rail. Vertical, narrow, and only as tall as its contents.

   `contain:layout` is the standing guarantee, not a fix for one bug: NOTHING
   inside this box may change the geometry of anything outside it. The rail
   lives in the shell's `auto` grid column, so a child that grew its own box
   would move the MAP. Containment makes that unstateable rather than merely
   currently-untrue -- a future child cannot reintroduce it. */
.ow-globe-controls{
  grid-area:rail; align-self:start;
  display:flex; flex-direction:column; align-items:stretch; gap:.3rem;
  width:9.5rem; max-width:none; margin:0; padding:.4rem;
  background:color-mix(in srgb,var(--ow-panel) 84%,transparent);
  border:1px solid var(--ow-line); border-radius:.4rem;
  backdrop-filter:blur(12px);
  contain:layout;
  font-size:.64rem;
}
.ow-g-padmount{display:flex;justify-content:center}
.ow-g-pad-disabled{opacity:.52;pointer-events:none}
/* THE READOUT IS REWRITTEN ON EVERY POINTERMOVE OF THE PAD. `syncInputs()`
   rebuilds this text for each sample of a drag -- "stopped", "-5.36 / 0.000",
   "-43200 / 43200 °/h" -- so its CONTENT WIDTH changes continuously while the
   lever travels. Any layout that sizes this element from its text therefore
   re-lays-out the whole box mid-drag, and the pad moves out from under the
   finger holding it. Measured at 11.7px of travel on a 360px phone.
   `contain:inline-size` says the width comes from the container and never from
   the text, so the class of bug cannot come back through a different string. */
.ow-g-rate{
  display:block;text-align:center;
  contain:inline-size;
  font-size:.58rem;opacity:.78;font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis
}
.ow-g-buttons{display:grid;gap:.22rem}
/* Small, as asked. State still travels in the button's own words as well as its
   colour, so shrinking them does not make the state colour-dependent. */
.ow-globe-controls .ow-g-btn{
  padding:.2rem .3rem;font-size:.58rem;border-radius:.25rem;text-align:center;
  min-height:var(--ow-g-btn-min-h, 0)
}
/* THE 44px TOUCH FLOOR: SCOPED, AND CURRENTLY OFF BY THE OWNER'S CHOICE.
   These measured 22.3px tall -- half the 44px floor, not above it -- and
   raising them to 44 cost 17.6px of box height. Offered as a trade, the owner
   chose "smaller buttons for now". His device, his call, and he has been using
   this control all evening.
   What he actually asked for twice was SMALLER, and the measurement said the
   oversized dimension was WIDTH (112px, from a two-column 1fr grid), not
   height. So the three-track layout below stays and the floor comes off: he
   gets smaller on both axes.
   "For now" is why this is a variable and not a deletion. Restoring the floor
   is changing the 0 below to 2.75rem -- one line, no re-derivation. The
   `(pointer: coarse)` scoping is kept for the same reason: unconditionally,
   44px would turn the desktop rail's five buttons from 111px into 220px on a
   surface driven by a mouse, and that would still be wrong. */
@media (pointer: coarse){
  .ow-globe-controls{--ow-g-btn-min-h:0}   /* 2.75rem restores the 44px floor */
  .ow-globe-controls .ow-g-btn{padding:.2rem .25rem}
}
/* The exact numeric rates: collapsed by default, so they are available without
   being furniture. They remain the accessible and programmatic route to the
   same two values the pad sets. */
.ow-g-exact{font-size:.56rem;opacity:.85}
.ow-g-exact summary{cursor:pointer;padding:.1rem 0;list-style:none}
.ow-g-exact summary::-webkit-details-marker{display:none}
.ow-g-exact summary::before{content:"▸ ";opacity:.6}
.ow-g-exact[open] summary::before{content:"▾ "}
.ow-g-exact label{display:flex;align-items:center;gap:.25rem;margin-top:.18rem}
.ow-g-exact input{
  flex:1 1 auto;min-width:0;width:100%;
  font:inherit;font-size:.56rem;color:var(--ow-text);
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.18);
  border-radius:.2rem;padding:.12rem .2rem;font-variant-numeric:tabular-nums
}

/* Narrow: the rail goes under the globe and lays out horizontally. */
@media (width < 820px){
  /* The frame goes SQUARE rather than keeping a fixed height. On a phone the
     column is ~350px wide against a 62vh row, so a fixed-height frame is a tall
     portrait box with a small globe stranded in the middle of it -- the exact
     empty space this pass exists to remove. Driving the height from the width
     with aspect-ratio makes the globe fill both dimensions at once. */
  .ow-map-shell{
    /* The stacked phone/tablet layout also has lenses, a rail and a timeline
       below the square map, so it needs a separate remaining-height budget. */
    --ow-frame-h: min(50vh, 420px);
    width:100%;
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:auto auto auto;
    grid-template-areas:"map" "rail" "time";
  }
  .ow-map-shell #map{
    aspect-ratio:1/1;
    width:100%;max-width:none;height:auto;max-height:var(--ow-frame-h)
  }
  /* A FIXED TWO-COLUMN GRID, NOT A CENTRED FLEX ROW.
     The old rule was `flex-wrap:wrap; justify-content:center`, which places
     every item by measuring the line's total content width. The rate readout
     is one of those items and its text changes on every sample of a pad drag,
     so the line re-centred continuously and the pad, the readout and the
     buttons all slid sideways while the lever was being held -- 9.2px at
     430x932 and 11.7px at 360x640, measured under a real pointer drag.
     Grid tracks fix that at the source: column 1 is sized by the pad, which
     has a definite 5.2rem width, and column 2 is `minmax(0,1fr)` -- the
     leftover. Neither track can be moved by a string. The buttons get `1fr`
     columns for the same reason: their labels toggle ("Start spin" /
     "Pause spin") and `auto` tracks would resize with them. */
  .ow-globe-controls{
    width:auto;
    display:grid;
    grid-template-columns:auto minmax(0,1fr);
    align-items:center;
    gap:.4rem .5rem
  }
  .ow-g-padmount{grid-column:1;grid-row:1 / span 2}
  .ow-g-rate{grid-column:2;grid-row:1;text-align:left}
  /* THREE COLUMNS, NOT TWO. Two columns made each button 112px wide at the
     owner's 402px viewport -- "those buttons are bigger than they need to be"
     -- and that width was a side effect of MY earlier fix, which moved these
     tracks to `1fr` to stop the labels sizing them. Three tracks keep that
     property (a `1fr` cannot be sized by a string, so the drag-shift cannot
     return) while cutting each button to ~88px, and they fold five buttons into
     two rows instead of three.

     The tracks are CAPPED, not `1fr`. `1fr` divides whatever the column has, so
     the buttons grew with the viewport -- at 800px, just under the breakpoint,
     three `1fr` tracks are ~229px each, which is the same "bigger than they
     need to be" complaint at a different width. A 5.5rem cap is content-
     INDEPENDENT (it is a length, so no label can size it and the drag-shift
     cannot return) and it puts a real ceiling on how wide these ever get. */
  .ow-g-buttons{grid-column:2;grid-row:2;grid-template-columns:repeat(3,minmax(0,5.5rem))}
  .ow-g-exact{grid-column:1 / -1}
}

/* ---- coordinates: no boxes, stacked hemispheres, sized to 000.0 N ----
   `000.0 N` is seven characters; with tabular figures that is 7ch and not one
   pixel more. N over E rather than beside it, because two short stacked lines
   read faster in a narrow column than one long one and cost no width at all. */
.ow-ll{
  display:inline-grid;
  grid-auto-rows:1.15em;
  justify-items:end;
  min-width:7ch;
  font-variant-numeric:tabular-nums;
  line-height:1.15;
  border:0;background:none;padding:0
}
.ow-ll span{white-space:nowrap}

/* The shared pad, dressed in THIS surface's palette.
   `createPad` is a shell component and styles itself from the shell tokens, which
   resolve light. On a dark map that renders as a bright white square in the
   corner of the frame — correct by the token contract and wrong on the screen.
   Re-pointing it at the surface's own existing --ow-* tokens is scoped theming
   of a shared component, not a fork: no new token is invented, the component is
   unchanged, and every other consumer keeps the shell's own look. Caught by
   looking at the 1440 screenshot; the CSS read perfectly well. */
/* CONTRAST ENOUGH TO READ AS A CONTROL. At .05 fill over .20 border this was
   very nearly invisible on a phone: with the puck driven to the edge of its
   travel, the only thing left with any contrast was a single axis line, and the
   owner reported the joystick as GONE. It was there and it was working -- it
   just did not look like anything. A control nobody can see is not a control. */
.ow-globe-controls .cic-pad{
  background:rgba(255,255,255,.09);
  border-color:rgba(255,255,255,.38)
}
.ow-globe-controls .cic-pad-axis{background:rgba(255,255,255,.30)}
.ow-globe-controls .cic-pad-tick{background:var(--ow-accent);opacity:.9;height:.5rem}
.ow-globe-controls .cic-pad-puck{
  background:var(--ow-accent);
  border-color:rgba(7,16,25,.85);
  box-shadow:0 .15rem .45rem rgb(0 0 0 / .5)
}
.ow-globe-controls .cic-pad:focus-visible{outline-color:var(--ow-accent)}

/* ============================================================================
   THEMATIC LENSES  (overwatch.js builds the markup; agent `lens`)

   One button turns on the layers belonging to a theme and turns the rest off.
   The presets live in map.config.json under `lenses`; nothing here or in
   overwatch.js knows the name of any of them, so a new lens is a config edit.

   WHY THIS IS A GRID ROW AND NOT ANOTHER ITEM IN THE GLOBE CONTROL BOX
   The box under the globe already carries a pad, a live readout and five
   buttons, and `jstk` has just spent a whole pass proving nothing in it moves
   during a drag (commit ec88c47, 0.00px at 430x932 and 360x640). Adding three
   or more buttons to that flex/grid would re-enter that geometry for no reason,
   and the box is `contain:layout` precisely so it stays finished. The lenses
   get their own row instead: on a phone it lands directly under the globe,
   which is where the owner is looking.

   HEIGHT IS FIXED AND INDEPENDENT OF THE NUMBER OF LENSES. The button strip
   scrolls sideways rather than wrapping, so a fourth, fifth or tenth preset
   costs zero vertical space and cannot push the globe off a phone screen. The
   note line has a fixed height and scrolls internally for the same reason: its
   text changes every time a lens is pressed, and an element that resizes with
   its string is exactly what moved the joystick box.

   AND THE HEIGHT IS A BUDGET, NOT A FREE CHOICE. The globe frame height above
   was picked so the timeline lands above the fold on a 1000px-tall window, and
   a row inserted between them spends from that same budget. Two lines of note
   rather than three is what keeps the whole cost near 65px; lenses.verify.mjs
   measures and prints it at every viewport so the next person adding a control
   here sees the number instead of discovering it on a laptop.

   The grid declarations below RESTATE .ow-map-shell rather than editing the
   rule above, because that rule is `jstk`'s and two agents share this file.
   Later, equal specificity, so the cascade settles it. Both copies must add the
   same row; if the areas above change, change them here too.
   ========================================================================== */
.ow-map-shell{
  grid-template-rows:var(--ow-frame-h) auto auto;
  grid-template-areas:"rail map" "rail lens" "time time";
}
.ow-lenses{
  grid-area:lens;
  justify-self:start; width:100%; max-width:var(--ow-frame-h);
  display:flex; flex-direction:column; gap:.25rem;
  padding:.35rem .4rem;
  background:color-mix(in srgb,var(--ow-panel) 84%,transparent);
  border:1px solid var(--ow-line); border-radius:.4rem;
  contain:layout;
}
/* Sideways, never wrapping. `flex:0 0 auto` on the children plus overflow-x on
   the strip is what makes the row's height a constant. */
.ow-lens-row{
  display:flex; align-items:center; gap:.3rem;
}
/* The ROW is not the scroller; the STRIP inside it is, and Reset sits beside
   the strip so it is always on screen. With Reset inside the scroller it was
   the last item and got clipped by the frame edge at 430px -- which made the
   way OUT of a lens the one control you had to go looking for. */
.ow-lens-strip{
  flex:1 1 auto; min-width:0;
  display:flex; align-items:center; gap:.3rem;
  overflow-x:auto; overflow-y:hidden;
  scrollbar-width:thin; -webkit-overflow-scrolling:touch;
  padding-bottom:.1rem;
}
.ow-lens{
  flex:0 0 auto;
  font:inherit; font-size:.72rem; line-height:1.2;
  padding:.28rem .55rem; border-radius:.3rem;
  color:var(--ow-text); cursor:pointer; white-space:nowrap;
  background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.18);
}
.ow-lens:focus-visible{outline:2px solid var(--ow-accent);outline-offset:1px}
.ow-lens[aria-pressed="true"]{
  background:color-mix(in srgb,var(--ow-accent) 26%,transparent);
  border-color:var(--ow-accent);
}
/* A lens with no data source is DIMMED AND STILL PRESSABLE. Disabling it would
   hide the reason behind a hover title, and the owner reads this on a phone
   where there is no hover; pressing it writes what is missing into the note. */
.ow-lens[data-blocked]{
  opacity:.55; border-style:dashed; border-color:var(--ow-danger);
}
.ow-lens--reset{flex:0 0 auto}
/* FIXED HEIGHT, SCROLLS INTERNALLY. The text is rewritten on every press, so
   sizing the block from it would move the map. Two lines of room, and anything
   longer -- a blocked lens explaining itself -- is scrolled, not truncated. */
.ow-lens-note{
  margin:0; height:1.7rem; overflow-y:auto; overflow-x:hidden;
  font-size:.64rem; line-height:1.25; opacity:.85;
}
.ow-lens-note[data-tone="empty"],
.ow-lens-note[data-tone="blocked"]{color:var(--ow-danger);opacity:1}
.ow-lens-note[data-tone="partial"]{color:#f6c667;opacity:1}

/* Layer rows carry their real feature count, so "this lens shows nothing" is
   answerable in the panel and not only in the note. */
.ow-layers .ow-layer-row{min-width:0}
.ow-layers .ow-layer{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:baseline;gap:.35rem;min-width:0;padding:.05rem 0}
.ow-layers .ow-layer-id{min-width:0;overflow-wrap:anywhere}
.ow-layers .ow-layer-symbol{width:.48rem;height:.48rem;border-radius:50%;background:currentColor;opacity:.75;font-size:0;flex:0 0 auto}
.ow-layers .ow-layer-n{
  min-width:0;font-size:.62rem;opacity:.7;font-variant-numeric:tabular-nums
}
.ow-layers .ow-layer-n[data-empty="1"]{color:var(--ow-danger);opacity:1}
.ow-embed .ow-layers{overflow-x:hidden}

/* The Mainframe mounts the existing map in its sole application frame.  The
   document deliberately keeps the map's own controls, but does not initialise
   or expose a second CIC shell around them. */
html.ow-embed,html.ow-embed body{min-height:100%;height:100%;overflow:auto}
.ow-embed #shell-taskbar,.ow-embed #scrim,.ow-embed #navigation-drawer,.ow-embed #context-drawer,.ow-embed #help-drawer,.ow-embed #admin-drawer,.ow-embed #repos-drawer,.ow-embed #right-drawer-tabs,.ow-embed #identity-splash{display:none!important}
.ow-embed .ow-main{width:100%;max-width:none;min-height:100%;height:auto;padding:calc(.5rem + var(--ow-content-inset-top,var(--ow-parent-bar-height,0px))) .5rem .5rem}
.ow-embed .ow-heading{display:none}
.ow-embed .ow-map-shell{height:auto;max-height:none;--ow-frame-h:min(62vh,560px);width:min(100%,calc(var(--ow-frame-h) + 24rem));grid-template-columns:minmax(14rem,16rem) auto minmax(0,1fr);grid-template-rows:var(--ow-frame-h) auto auto;grid-template-areas:"layers rail map" "layers rail lens" "time time time"}
/* Layers is its own readable desktop column. It never shares the globe rail or
   covers the rendered map, while its bounded scroll keeps the timeline usable. */
.ow-embed .ow-layers{position:static;grid-area:layers;align-self:start;justify-self:stretch;width:auto;max-height:var(--ow-frame-h);min-width:0;margin:0;padding:.55rem;background:color-mix(in srgb,var(--ow-panel) 88%,transparent);border:1px solid var(--ow-line);border-radius:.4rem;backdrop-filter:blur(12px);overflow:auto}
.ow-embed .ow-layers h2{margin:0 0 .35rem;font-size:.72rem}
.ow-embed .ow-layers,.ow-embed .ow-layers label,.ow-embed .ow-layers #mapStatus{color:var(--ow-text)}
@media (width < 820px){
  .ow-embed .ow-main{overflow:auto;padding-top:calc(.5rem + var(--ow-content-inset-top,var(--ow-parent-bar-height,0px))) }
  .ow-embed .ow-map-shell{height:auto;--ow-frame-h:min(52vh,480px)}
  .ow-embed .ow-map-shell{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto auto auto;grid-template-areas:"map" "layers" "lens" "rail" "time"}
  .ow-embed .ow-layers{grid-area:layers;justify-self:stretch;width:auto;max-height:12rem;margin:0}
}

@media (width < 820px){
  /* Under the globe, above the spin controls. Same restatement caveat as above:
     this must carry every area the narrow rule already declares. */
  .ow-map-shell{
    grid-template-rows:auto auto auto auto;
    grid-template-areas:"map" "lens" "rail" "time";
  }
  .ow-lenses{max-width:none}
}

/* Click details are anchored below the map. A bounded summary keeps the
   marker context visible on phones; raw properties remain behind Details. */
.ow-map-shell .ow-inspect{
  position:static; grid-area:detail; z-index:4;
  width:100%; max-height:12rem; overflow:auto; margin:0;
  font-size:.9rem; line-height:1.45;
}
.ow-map-shell .ow-inspect-title{font-size:1rem}
.ow-map-shell .ow-inspect-eyebrow{font-size:.72rem}
.ow-map-shell .ow-inspect-rows dt,.ow-map-shell .ow-inspect-rows dd{font-size:.84rem}
.ow-inspect-more{margin-top:.45rem}
.ow-inspect-more summary{cursor:pointer;font-weight:700;padding:.25rem 0}
.ow-map-shell .ow-inspect-x{width:2.75rem;height:2.75rem;font-size:1.25rem}
.ow-map-shell .ow-inspect-foot button{min-height:2.75rem;padding:.45rem .7rem;font-size:.8rem}
.ow-map-shell{grid-template-rows:var(--ow-frame-h) auto auto auto;grid-template-areas:"rail map" "rail lens" "detail detail" "time time"}
@media (width < 820px){
  .ow-map-shell{grid-template-rows:auto auto auto auto auto;grid-template-areas:"map" "detail" "lens" "rail" "time"}
  .ow-embed .ow-map-shell{grid-template-rows:auto auto auto auto auto auto;grid-template-areas:"map" "detail" "layers" "lens" "rail" "time"}
  .ow-map-shell .ow-inspect{max-height:12rem}
}
@media (min-width:820px){
  .ow-embed .ow-map-shell{grid-template-rows:var(--ow-frame-h) auto auto auto;grid-template-areas:"layers rail map" "layers rail lens" "detail detail detail" "time time time"}
}
:root[data-theme="light"] .ow-map-shell .ow-inspect{--ins-bg:16,29,39;--ins-alpha:.96;--ins-text:#f2f7fa;--ins-muted:#c5d3dd;--ins-eyebrow:#7ee0b8}

/* Globe controls are an operating row below the map, not a left rail. The old
 * 9.5rem column made the globe needlessly small at tablet widths and separated
 * controls from the lenses that select its content. Layers remains its own
 * Context panel (or embedded column) and is deliberately not part of this move. */
@media (min-width:820px){
  .ow-map-shell{
    width:min(100%,var(--ow-frame-h));
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:var(--ow-frame-h) auto auto auto auto;
    grid-template-areas:"map" "detail" "lens" "rail" "time";
  }
  .ow-map-shell #map{justify-self:center}
  .ow-globe-controls{
    grid-area:rail; width:100%; max-width:none;
    flex-direction:row; align-items:center; justify-content:center;
    flex-wrap:wrap; contain:layout;
  }
  .ow-globe-controls .ow-g-buttons{grid-template-columns:repeat(3,minmax(0,1fr));}
  .ow-embed .ow-map-shell{
    width:min(100%,calc(var(--ow-frame-h) + 16rem));
    grid-template-columns:minmax(14rem,16rem) minmax(0,1fr);
    grid-template-rows:var(--ow-frame-h) auto auto auto auto;
    grid-template-areas:"layers map" "layers detail" "layers lens" "layers rail" "layers time";
  }
  .ow-embed .ow-layers{grid-area:layers}
}
/* The legend belongs to Layers rather than the map canvas: it is visible only
 * while the configured raster is on, so it cannot cover globe controls. */
.ow-altitude-legend{margin:.65rem 0 0;padding:.5rem;border-top:1px solid var(--ow-line);font-size:.66rem;line-height:1.25;color:var(--ow-text)}
.ow-altitude-legend[hidden]{display:none}
.ow-altitude-legend-title{font-weight:700;margin-bottom:.3rem}
.ow-altitude-legend-ramp{float:left;width:.7rem;height:6.7rem;margin:.1rem .45rem .1rem 0;border:1px solid color-mix(in srgb,var(--ow-text) 35%,transparent);border-radius:.15rem}
.ow-altitude-legend-stops{display:flex;flex-direction:column;justify-content:space-between;min-height:6.9rem;margin:0;padding:0;list-style:none;font-variant-numeric:tabular-nums}
.ow-altitude-legend-stops li{display:flex;align-items:center;gap:.25rem;white-space:nowrap}
.ow-altitude-legend-stops span{width:.45rem;height:.45rem;border-radius:50%;border:1px solid color-mix(in srgb,var(--ow-text) 35%,transparent)}
.ow-altitude-legend p{clear:both;margin:.4rem 0 .2rem;opacity:.78;overflow-wrap:anywhere}
.ow-altitude-legend a{color:inherit;font-weight:650}
#context-module-slot .ow-altitude-legend{border-top-color:var(--line);color:var(--ink)}
.ow-embed .ow-altitude-legend{border-top-color:var(--ow-line)}

/* Reuse MapLibre camera controls, compact for pointers and touch-sized on coarse input. */
/* Imported report rows are below the complete map component, so shared desktop,
 * mobile and embedded grid templates need no additional placement branches. */
.ow-report-list{width:min(100%,70rem);margin:1rem auto;padding:.8rem;border:1px solid var(--ow-line);border-radius:.5rem;background:var(--ow-panel);color:var(--ow-text)}
.ow-report-list h2{font-size:1rem;margin:0 0 .5rem}.ow-report-list p{font-size:.8rem;line-height:1.4;margin:.4rem 0}
.ow-report-scroll{max-height:22rem;overflow:auto;border:1px solid var(--ow-line);margin-top:.6rem}
.ow-report-list table{width:100%;border-collapse:collapse;text-align:left;font-size:.8rem;table-layout:fixed}
.ow-report-list th,.ow-report-list td{padding:.55rem;vertical-align:top;border-bottom:1px solid var(--ow-line);overflow-wrap:anywhere}
.ow-report-list th:first-child{width:50%}.ow-report-list th{position:sticky;top:0;background:var(--ow-panel);z-index:1}
.ow-report-list button{display:block;width:100%;text-align:left;white-space:normal;color:inherit;background:transparent;border:0;font:inherit;min-height:2.75rem;cursor:pointer;text-decoration:underline;text-underline-offset:.15em}
.ow-report-list button:focus-visible,.ow-report-details:focus-visible{outline:2px solid var(--ow-accent);outline-offset:2px}
.ow-report-list tr.is-selected{background:color-mix(in srgb,var(--ow-accent) 18%,var(--ow-panel))}
.ow-report-details{padding:.85rem;border:1px solid var(--ow-line);border-radius:.5rem;background:var(--ow-panel);color:var(--ow-text);overflow-wrap:anywhere}
.ow-report-details[hidden]{display:none}.ow-report-details header{display:flex;align-items:start;gap:.75rem}.ow-report-details h2{font-size:1rem;line-height:1.35;margin:0;flex:1}
.ow-report-details button{min-width:2.75rem;min-height:2.75rem;font-size:1.3rem;color:inherit;background:transparent;border:1px solid var(--ow-line);border-radius:.3rem;cursor:pointer}
.ow-report-details dt{font-size:.75rem;margin-top:.65rem;opacity:.8}.ow-report-details dd{margin:.2rem 0;font-size:.85rem;white-space:pre-wrap}.ow-report-details a{color:var(--ow-accent)}
.ow-report-period{font-size:.8rem;border-inline-start:3px solid var(--ow-accent);padding-inline-start:.6rem}
#context-module-slot .ow-report-details{color:var(--ink);background:var(--surface,var(--paper));border-color:var(--line)}
#context-module-slot .ow-report-details h2,#context-module-slot .ow-report-details dd,#context-module-slot .ow-report-details p,#context-module-slot .ow-report-details button{color:var(--ink)}
#context-module-slot .ow-report-details dt{color:var(--muted);opacity:1}
#context-module-slot .ow-report-details a{color:var(--ink);text-decoration:underline}
#context-module-slot .ow-report-details button{border-color:var(--line)}
.ow-report-embed-drawer{position:fixed;z-index:40;inset:calc(var(--ow-content-inset-top,0px) + .5rem) .5rem .5rem auto;width:min(23rem,85vw);overflow:auto;box-shadow:0 8px 30px #0008}
@media(max-width:600px){.ow-report-list{padding:.5rem}.ow-report-list th,.ow-report-list td{padding:.4rem;font-size:.73rem}.ow-report-list th:first-child{width:44%}.ow-report-embed-drawer{inset:auto .5rem .5rem;width:auto;max-height:48vh}}
#map .maplibregl-ctrl-group button { width:28px; height:28px; min-width:28px; min-height:28px; }
#map .maplibregl-ctrl-group { margin-bottom:5px; }
@media (pointer:coarse) { #map .maplibregl-ctrl-group button { width:44px; height:44px; min-width:44px; min-height:44px; } }

/* ============================================================================
   World Map workspace — viewport-filling host, category rail, overlay panel.
   Container queries own module geometry. Media queries stay for pointer,
   reduced motion, reduced transparency and forced colors.
   ========================================================================== */
:root{
  --ow-event-rail-height:112px;
  --ow-glass-bg:rgb(12 16 24 / .92);
  --ow-glass-blur:10px;
  --ow-map-title-height:44px;
}
body[data-shell-node="overwatch-world-map"]{
  display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden;
}
body[data-shell-node="overwatch-world-map"] #shell-taskbar{flex:0 0 auto}
body[data-shell-node="overwatch-world-map"] .ow-main{
  flex:1 1 auto;min-block-size:0;min-inline-size:0;width:100%;max-width:none;margin:0;
  padding:0;display:flex;flex-direction:column;overflow:hidden;
}
body[data-shell-node="overwatch-world-map"] .ow-heading{display:none}
html.ow-embed body[data-shell-node="overwatch-world-map"],
html.ow-embed body[data-shell-node="overwatch-world-map"] .ow-main{
  height:100%;min-height:100%;padding:0;
}

#mapView.ow-map-shell.ow-map-host{
  container-type:size;container-name:world-map;
  flex:1 1 auto;min-block-size:0;min-inline-size:0;
  display:flex;flex-direction:column;
  width:100%;max-width:none;height:auto;margin:0;padding:0;
  border:0;border-radius:0;overflow:hidden;background:var(--ow-bg);
}
#mapView.ow-map-host .ow-map-workspace{
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  grid-template-areas:"toolbar" "stage" "rail";
  flex:1 1 auto;min-block-size:0;min-inline-size:0;width:100%;height:100%;
}
#mapView.ow-map-host .ow-map-toolbar,
#mapView.ow-map-host .ow-map-stage,
#mapView.ow-map-host .ow-event-rail,
#mapView.ow-map-host .ow-event-panel{
  min-block-size:0;min-inline-size:0;
}
#mapView.ow-map-host .ow-map-toolbar{
  grid-area:toolbar;display:flex;align-items:center;gap:.75rem;
  min-height:var(--ow-map-title-height);padding:.25rem .75rem;
  border-bottom:1px solid var(--ow-line);
}
#mapView.ow-map-host .ow-map-toolbar-label{margin:0;font-size:1rem;line-height:1.2}
#mapView.ow-map-host .ow-overlay-dock{
  margin-inline-start:auto;display:flex;flex-wrap:wrap;gap:.25rem;align-items:center;
}
#mapView.ow-map-host .ow-overlay-restore,
#mapView.ow-map-host .ow-overlay-min{
  min-height:44px;min-width:44px;padding:0;flex:0 0 auto;
  font:inherit;color:inherit;cursor:pointer;background:rgba(255,255,255,.08);
  border:1px solid rgba(255,255,255,.18);border-radius:.35rem;
}
#mapView.ow-map-host .ow-overlay-restore[hidden],
#mapView.ow-map-host .ow-overlay-min[hidden]{display:none}
#mapView.ow-map-host .ow-overlay-min{
  position:absolute;top:.2rem;right:.2rem;z-index:7;
}
#mapView.ow-map-host .maplibregl-ctrl-top-right .ow-overlay-min{
  right:100%;top:0;margin-right:6px;
}
.ow-lenses,.ow-altitude-legend,.ow-evidence-policy{position:relative}
#mapView.ow-map-host .ow-timeline[hidden],
#mapView.ow-map-host .ow-lenses[hidden],
#mapView.ow-map-host .ow-globe-controls[hidden],
#mapView.ow-map-host .ow-layers[hidden],
#mapView.ow-map-host .ow-altitude-legend[hidden],
#mapView.ow-map-host .ow-evidence-policy[hidden],
#mapView.ow-map-host .ow-map-legend[hidden],
#mapView.ow-map-host .maplibregl-ctrl-top-right[hidden]{display:none}
@media (max-width: 671px){
  #mapView.ow-map-host .ow-lenses:not(.ow-drawer-open),
  #mapView.ow-map-host .ow-map-legend:not(.ow-drawer-open),
  #mapView.ow-map-host .ow-globe-controls:not(.ow-drawer-open),
  #mapView.ow-map-host .ow-timeline:not(.ow-drawer-open),
  #mapView.ow-map-host .maplibregl-ctrl-top-right:not(.ow-drawer-open){
    display:none !important;
  }
}
#mapView.ow-map-host .ow-event-rail:not([hidden]),
#mapView.ow-map-host .ow-event-panel:not([hidden]),
#mapView.ow-map-host .ow-inspect:not([hidden]),
#mapView.ow-map-host .ow-map-legend,
#mapView.ow-map-host .maplibregl-ctrl-attrib{
  pointer-events:auto;visibility:visible;
}
#mapView.ow-map-host .ow-map-legend{
  position:absolute;left:.5rem;bottom:4.4rem;z-index:6;
  width:min(11.5rem,calc(100% - 1rem));padding:.45rem .55rem;
  background:var(--ow-glass-bg);border:1px solid var(--ow-line);border-radius:.4rem;
  color:var(--ow-text);font-size:.72rem;line-height:1.3;
}
#mapView.ow-map-host .ow-map-legend-title{margin:0 0 .3rem;font-size:.75rem}
#mapView.ow-map-host .ow-map-legend-list{margin:0;padding:0;list-style:none;display:grid;gap:.28rem}
#mapView.ow-map-host .ow-map-legend-item{display:flex;align-items:center;gap:.4rem}
#mapView.ow-map-host .ow-map-legend-swatch{
  width:0.9rem;height:0.9rem;flex:0 0 auto;border:1px solid #c5d3dd;
}
#mapView.ow-map-host .ow-map-legend-diamond{
  background:#ff5c67;transform:rotate(45deg);border-radius:1px;
}
#mapView.ow-map-host .ow-map-legend-circle{
  background:#3b82f6;border-radius:50%;
}
@media (max-width:41.99rem){
  #mapView.ow-map-host .ow-overlay-restore,
  #mapView.ow-map-host .ow-overlay-min{min-height:44px;min-width:44px}
  #mapView.ow-map-host .ow-map-legend{
    bottom:.5rem;max-width:10.5rem;
  }
  #mapView.ow-map-host .ow-event-rail:not([hidden]){height:var(--ow-event-rail-height);min-height:var(--ow-event-rail-height)}
}
#mapView.ow-map-host .ow-feed-status{
  margin:0;font-size:.75rem;opacity:.85;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
#mapView.ow-map-host .ow-map-stage{
  grid-area:stage;position:relative;overflow:hidden;
}
#mapView.ow-map-host .ow-event-rail{grid-area:rail}
#mapView.ow-map-host .ow-event-panel{
  grid-area:stage;z-index:6;pointer-events:auto;
}
#mapView.ow-map-host #map{
  position:absolute;inset:0;width:100%;height:100%;max-width:none;max-height:none;
  aspect-ratio:auto;justify-self:stretch;border:0;border-radius:0;
}
#mapView.ow-map-host .ow-timeline{
  position:absolute;left:.5rem;right:.5rem;bottom:.5rem;z-index:4;margin:0;
  padding:.35rem 3.1rem .35rem .5rem;border-radius:.4rem;
  background:var(--ow-glass-bg);border:1px solid var(--ow-line);
}
#mapView.ow-map-host .ow-lenses{
  position:absolute;left:.5rem;top:.5rem;z-index:4;width:min(36rem,calc(100% - 1rem));
  max-width:none;margin:0;
  background:var(--ow-glass-bg);
}
#mapView.ow-map-host .ow-globe-controls{
  position:absolute;left:.5rem;bottom:3.4rem;z-index:4;width:auto;max-width:calc(100% - 1rem);
  margin:0;
}
#mapView.ow-map-host .ow-inspect{
  position:absolute;left:.5rem;right:auto;bottom:7.2rem;z-index:5;
  width:min(22rem,calc(100% - 1rem));max-height:12rem;grid-area:auto;
}
#mapView.ow-map-host .ow-layers{
  position:absolute;right:.5rem;top:.5rem;z-index:5;width:min(18rem,calc(100% - 1rem));
  max-height:min(40%,16rem);overflow:auto;
}
html:not(.ow-embed) #mapView.ow-map-host .ow-layers{display:none}

.ow-event-rail{
  display:flex;flex-direction:column;gap:.22rem;padding:.28rem .4rem;
  padding-bottom:calc(.28rem + env(safe-area-inset-bottom,0px));
  height:var(--ow-event-rail-height);min-height:var(--ow-event-rail-height);
  background:color-mix(in srgb,var(--ow-panel) 92%,transparent);
  border-top:1px solid var(--ow-line);overflow:hidden;
}
.ow-event-rail[hidden]{display:none;height:0;min-height:0;padding:0;border:0}
.ow-event-categories{
  display:flex;gap:.35rem;overflow-x:auto;overflow-y:hidden;
  touch-action:pan-x;scrollbar-width:thin;
}
.ow-event-category,.ow-event-step,.ow-event-load-more,.ow-event-panel button,
.ow-event-card,.ow-event-info{
  min-height:44px;min-width:44px;font:inherit;color:inherit;cursor:pointer;
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.18);border-radius:.35rem;
}
.ow-event-category{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:.35rem;
  padding:.35rem .7rem;white-space:nowrap;
}
.ow-event-category[aria-pressed="true"]{
  background:color-mix(in srgb,var(--ow-accent) 26%,transparent);border-color:var(--ow-accent);
}
.ow-event-count{display:none}
.ow-event-card[data-kind="military-strike"],.ow-event-card-shell[data-event-kind="military-strike"] .ow-event-card{border-inline-start:4px solid #ff5c67}
.ow-event-card[data-kind="military-event"],.ow-event-card-shell[data-event-kind="military-event"] .ow-event-card{border-inline-start:4px solid #e8a14a}
.ow-event-card[data-kind="earthquakes"],.ow-event-card-shell[data-event-kind="earthquakes"] .ow-event-card{border-inline-start:4px solid #a78bfa}
.ow-event-card[data-kind="nws-alerts"],.ow-event-card-shell[data-event-kind="nws-alerts"] .ow-event-card{border-inline-start:4px solid #3db8e8}
.ow-event-card[data-kind="LIVE"],.ow-event-card-shell[data-event-kind="LIVE"] .ow-event-card{border-inline-start:4px solid #53d0be}
.ow-event-card-row{display:flex;align-items:stretch;gap:.35rem;min-width:0}
.ow-event-cards{
  flex:1 1 auto;min-width:0;display:flex;flex-wrap:nowrap;gap:.32rem;
  overflow-x:auto;overflow-y:hidden;touch-action:pan-x;
  overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:thin;
}
.ow-event-group{
  display:flex;flex-flow:row nowrap;align-items:stretch;gap:.32rem;
  flex:0 0 auto;min-width:0;
}
.ow-event-group + .ow-event-group{
  margin-inline-start:.4rem;padding-inline-start:.4rem;
  border-inline-start:1px solid rgba(255,255,255,.18);
}
.ow-event-group-heading{
  margin:0;flex:0 0 auto;max-width:4.8rem;padding:.2rem .35rem;
  display:flex;align-items:center;align-self:stretch;
  font-size:.68rem;font-weight:700;line-height:1.15;white-space:normal;
}
.ow-event-card-shell{
  position:relative;flex:0 0 auto;display:flex;align-items:stretch;
  width:min(14.5rem,72vw);
}
.ow-event-card{
  flex:1 1 auto;width:100%;min-width:0;max-inline-size:65ch;
  display:flex;flex-direction:row;align-items:center;justify-content:flex-start;
  gap:.35rem;padding:.28rem 2.4rem .28rem .4rem;text-align:left;scroll-snap-align:start;
}
.ow-event-card.is-selected,.ow-event-card[aria-pressed="true"]{
  outline:2px solid #fff;outline-offset:1px;
  background:color-mix(in srgb,var(--ow-accent) 18%,transparent);
}
.ow-event-card-title{
  display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;
  font-weight:650;line-height:1.2;font-size:.82rem;min-width:0;flex:1 1 auto;
}
.ow-event-card-meta{display:none}
.ow-event-kind{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  flex:0 0 auto;width:1.15rem;height:1.15rem;color:var(--ow-kind-fg,#e8f2f4);
}
.ow-event-kind-shape{
  position:absolute;inset:.12rem;border:1.5px solid currentColor;background:var(--ow-kind);
  opacity:.95;
}
.ow-event-kind-shape-diamond{transform:rotate(45deg);border-radius:1px}
.ow-event-kind-shape-square{border-radius:1px}
.ow-event-kind-shape-circle{border-radius:50%}
.ow-event-kind-shape-triangle{
  inset:auto;width:0;height:0;border:0;background:transparent;
  border-left:.42rem solid transparent;border-right:.42rem solid transparent;
  border-bottom:.72rem solid var(--ow-kind);left:.16rem;top:.12rem;
}
.ow-event-kind-shape-hex{
  border-radius:.15rem;transform:rotate(90deg) scaleX(.72);
}
.ow-event-kind-letter{
  position:relative;z-index:1;font-size:.58rem;font-weight:800;line-height:1;
  color:#071019;mix-blend-mode:normal;
}
.ow-event-kind-name{
  position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
}
.ow-event-kind[data-kind="military-strike"],.ow-event-kind-military-strike{--ow-kind:#ff5c67}
.ow-event-kind[data-kind="military-event"],.ow-event-kind-military-event{--ow-kind:#e8a14a}
.ow-event-kind[data-kind="earthquakes"]{--ow-kind:#a78bfa}
.ow-event-kind[data-kind="nws-alerts"]{--ow-kind:#3db8e8}
.ow-event-kind[data-kind="LIVE"]{--ow-kind:#53d0be}
.ow-event-age{
  position:absolute;top:.12rem;right:.12rem;z-index:2;
  font-size:.58rem;font-weight:700;font-variant-numeric:tabular-nums;line-height:1;
  padding:.12rem .22rem;border-radius:.2rem;
  background:rgb(7 16 25 / .72);color:var(--ow-text);pointer-events:none;
}
.ow-event-info{
  position:relative;inset:auto;flex:0 0 44px;align-self:stretch;
  width:44px;height:auto;min-height:44px;min-width:44px;padding:0;
  font-size:0;line-height:0;color:inherit;cursor:pointer;
  background:transparent;border:0;border-radius:0;
}
.ow-event-info::after{
  content:'i';position:absolute;right:.18rem;bottom:.08rem;
  font-size:.68rem;font-weight:700;font-style:italic;line-height:1;
  color:var(--ow-text);opacity:.9;
}
.ow-event-abs{
  position:absolute;right:0;bottom:calc(100% + .15rem);z-index:3;
  padding:.2rem .4rem;border:1px solid var(--ow-line);border-radius:.3rem;
  background:var(--ow-panel);color:var(--ow-text);font-size:.7rem;line-height:1.2;
  white-space:nowrap;box-shadow:0 6px 18px #0006;
}
.ow-event-abs[hidden]{display:none}
.ow-event-load-more{flex:0 0 auto;padding:.4rem .8rem;white-space:nowrap}
.ow-event-rail-status{margin:0;font-size:.7rem;opacity:.8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-event-panel{
  display:flex;flex-direction:column;
  background:var(--ow-glass-bg);color:var(--ow-text);
  border:1px solid var(--ow-line);border-radius:.5rem;
  box-shadow:0 8px 30px #0006;overflow:hidden;
}
.ow-event-panel[hidden]{display:none}
.ow-event-panel header{display:flex;align-items:flex-start;gap:.5rem;padding:.7rem .8rem 0}
.ow-event-panel h2{margin:0;flex:1;font-size:1rem;line-height:1.3;max-inline-size:65ch}
.ow-event-panel .ow-report-period,.ow-event-panel dl,.ow-event-panel a,.ow-event-panel p,
.ow-event-panel .ow-event-panel-actions{padding:0 .8rem}
.ow-event-panel dl{margin:.4rem 0 .7rem;overflow:auto}
.ow-event-panel dt{font-size:.75rem;margin-top:.55rem;opacity:.8}
.ow-event-panel dd{margin:.15rem 0;font-size:.85rem;white-space:pre-wrap}
.ow-event-panel a{color:var(--ow-accent)}
.ow-event-evidence{margin:.45rem .8rem 0;padding:.55rem .65rem;border:1px solid var(--ow-line);border-radius:.4rem;background:rgb(255 255 255 / .045)}
.ow-event-evidence h3{margin:0 0 .25rem;font-size:.78rem;letter-spacing:.08em;text-transform:uppercase}
.ow-event-excerpt{margin:.2rem 0 .45rem;padding:0!important;font-size:.84rem;line-height:1.35;white-space:pre-wrap}
.ow-event-citation{display:flex;align-items:center;justify-content:space-between;gap:.6rem;font-size:.74rem}
.ow-event-citation a{padding:0;font-weight:700;white-space:nowrap}
.ow-provider-embed{margin-top:.5rem;border-top:1px solid var(--ow-line);padding-top:.45rem}
.ow-provider-embed iframe{display:block;width:100%;height:13rem;border:0;border-radius:.3rem;background:#fff}
.ow-provider-embed iframe[hidden]{display:none}
.ow-provider-embed p{margin:.3rem 0 0;padding:0!important;font-size:.68rem;opacity:.78}
.ow-event-facts{display:grid;grid-template-columns:max-content minmax(0,1fr);column-gap:.6rem;align-content:start}
.ow-event-facts dt{margin-top:.35rem}.ow-event-facts dd{margin-top:.35rem;min-width:0}
.ow-event-panel-actions{display:flex;gap:.5rem;padding-bottom:.8rem}
@supports (backdrop-filter:blur(10px)){
  #mapView.ow-map-host .ow-timeline,
  #mapView.ow-map-host .ow-lenses,
  #mapView.ow-map-host .ow-globe-controls,
  .ow-event-panel{
    background:rgb(12 16 24 / .9);backdrop-filter:blur(var(--ow-glass-blur));
  }
}

@container world-map (min-width: 64rem){
  #mapView.ow-map-host .ow-event-panel{
    justify-self:end;align-self:stretch;width:clamp(18rem,28cqi,23rem);
    margin:12px 12px 12px 0;max-height:calc(100% - 24px);
  }
}
@container world-map (min-width: 42rem) and (max-width: 63.99rem){
  #mapView.ow-map-host .ow-event-panel{
    justify-self:end;align-self:start;width:min(21rem,38cqi);max-width:40%;
    margin:12px 12px 12px 0;max-height:min(70%,28rem);
  }
}
@container world-map (max-width: 41.99rem){
  /* `--ow-event-rail-height` for this width is set ONCE, in the phone block below
     (search "rail-height:96px"). A 104px declaration used to sit here; the later
     block re-declared it under the same query and specificity, so it never applied. */
  #mapView.ow-map-host .ow-event-panel{
    justify-self:stretch;align-self:end;width:auto;
    height:min(38%,18rem);max-height:min(38%,18rem);
    margin:.5rem;overflow:auto;
  }
  #mapView.ow-map-host .ow-event-card{width:min(13rem,72vw)}
}
@container world-map (max-height: 32rem){
  #mapView.ow-map-host .ow-map-workspace{--ow-event-rail-height:88px}
  #mapView.ow-map-host .ow-event-panel{height:min(32%,12rem);max-height:min(32%,12rem)}
}

.ow-event-category:focus-visible,.ow-event-card:focus-visible,.ow-event-step:focus-visible,
.ow-event-load-more:focus-visible,.ow-event-panel button:focus-visible,.ow-event-info:focus-visible,
#mapView.ow-map-host .ow-map-toolbar:focus-within{
  outline:2px solid var(--ow-accent);outline-offset:2px;
}

@media (pointer:coarse){
  .ow-event-category,.ow-event-card,.ow-event-step,.ow-event-load-more,.ow-event-panel button,
  .ow-overlay-min,.ow-overlay-restore,.ow-event-info{
    min-height:44px;min-width:44px;
  }
}
@media (prefers-reduced-motion:reduce){
  .ow-event-panel,.ow-event-card,.ow-event-cards{transition:none;scroll-behavior:auto}
}
@media (prefers-reduced-transparency:reduce){
  #mapView.ow-map-host .ow-timeline,
  #mapView.ow-map-host .ow-lenses,
  #mapView.ow-map-host .ow-globe-controls,
  .ow-event-panel{background:var(--ow-panel);backdrop-filter:none}
}
@media (forced-colors:active){
  .ow-event-card.is-selected,.ow-event-category[aria-pressed="true"]{outline:2px solid CanvasText}
  .ow-event-panel{background:Canvas;color:CanvasText;border-color:CanvasText}
  .ow-event-kind-shape,.ow-event-kind-letter{forced-color-adjust:none}
  .ow-event-kind-letter{color:Canvas}
}

:root[data-theme="light"]{
  --ow-glass-bg:rgb(244 248 250 / .94);
  --ow-kind-letter:#071019;
}
:root[data-theme="light"] .ow-event-kind[data-kind="military-strike"]{--ow-kind:#b42318}
:root[data-theme="light"] .ow-event-kind[data-kind="military-event"]{--ow-kind:#9a6700}
:root[data-theme="light"] .ow-event-kind[data-kind="earthquakes"]{--ow-kind:#6927da}
:root[data-theme="light"] .ow-event-kind[data-kind="nws-alerts"]{--ow-kind:#0b6e99}
:root[data-theme="light"] .ow-event-kind[data-kind="LIVE"]{--ow-kind:#0d7c6f}
:root[data-theme="light"] .ow-event-age{
  background:rgb(244 248 250 / .92);color:#10202b;
}
:root[data-theme="light"] .ow-event-category,
:root[data-theme="light"] .ow-event-card,
:root[data-theme="light"] .ow-event-step,
:root[data-theme="light"] .ow-event-load-more,
:root[data-theme="light"] .ow-event-info,
:root[data-theme="light"] .ow-event-panel button{
  background:rgb(16 32 43 / .06);border-color:rgb(16 32 43 / .18);color:var(--ow-text);
}

/* ============================================================================
   OVERWATCH PHONE LAYOUT - RECONCILED. Lane `ovrc`, torch 0007.

   This block is the merge of four independent lanes (`rflw`/w28, `ovdk`/w29,
   w30, `ovwq`/w31) that fixed the same defect four ways on 2026-09-15. The
   base is w31; the provenance of every borrowed rule is named at the rule.

   IT IS APPENDED, AND EVERY RULE IN IT IS ADDITIVE. That is not tidiness: an
   all-additive delta can be INJECTED into the live page as a trailing <style>,
   which reproduces the real cascade exactly, so the fix is measured against the
   owner's actual 58 live features before anything is written to the deployed
   tree. Delete this block and the file is byte-identical to the one served.

   ROOT CAUSE 1 - THE RAIL RESERVE WAS ZEROED.
   `styles.css:1537-1545` reserves the fixed tab rail's column on `main[class]`
   (0,1,1) and gives it back under `body.rail-rolled-up`. This surface then set
   `padding:0` in `body[data-shell-node="overwatch-world-map"] .ow-main` (0,2,1)
   and that shorthand silently zeroed the reserve at a specificity the shell
   rule cannot reach. Measured live at 360x640 BEFORE this block:
   `railProbe.reclaimedPx = -16` - retracting the rail made the stage SIXTEEN
   PIXELS NARROWER, the exact inverse of "items should scale when sidebar is
   retracted". `forced.tokenDrivesPadding:false` proves the override: the probe
   set the token to 99px and the computed padding stayed 0.

   ROOT CAUSE 2 - THE STAGE PINNED PANELS WITH HAND-TUNED CONSTANTS.
   `bottom:.5rem/3.4rem/4.4rem/7.2rem` were chosen against a desktop where each
   panel is about 2.9rem tall. A constant cannot know the height of the panel
   above it. Measured live at 360x640 BEFORE this block: `.ow-timeline` is
   344x167 at y=359 and `.ow-globe-controls` is 344x121 at y=359 - the SAME
   ORIGIN, one entirely on top of the other, with `.ow-map-legend` (y=455)
   inside both. 77 interactive pairs overlapped at 360x640, 67 at 390x844.
   THE FIX RETIRES THE CONSTANTS RATHER THAN RETUNING THEM (BIBLE 46b): every
   `bottom`/`top` resolves to `auto` here, so the browser places each panel from
   the real height of the one above it and there is no offset that CAN be wrong
   on a phone. Precisely: they are OVERRIDDEN, not deleted. The literals are
   still at `overwatch.css:800-856` and still run at desktop width, where this
   phone query does not apply. Deleting them is a separate change against a
   surface nobody has measured; saying "deleted" here would have been a claim
   about the file that the file contradicts.

   ROOT CAUSE 3 - MAPLIBRE'S OWN CONTROL COLUMN OUTGREW THE MAP.
   Found by w28 alone. `.maplibregl-ctrl-top-right` measured 54x265 at 360x640:
   four control groups stacked vertically, running 265px down from the top of
   the map and hit-testing over whatever was docked beneath it. w31 worked
   AROUND this by charging every docked panel a 3.5rem gutter - 56px of a 360px
   phone, on all five panels. w28's fix for the control itself is better, and
   this merge TRIED IT AND REJECTED IT ON MEASUREMENT: see the long note at
   "ROOT CAUSE 3 - AND THE ONE CROSS-LANE BORROW THAT MEASURED WORSE" below.
   THIS ROOT CAUSE IS THEREFORE MITIGATED, NOT FIXED. The vendor column is
   still 54x265 and still `float:right`; the gutter is what keeps it off the
   panels. Anyone revisiting it should read that note first.

   SCOPE - WHERE THIS MERGE DEPARTS FROM ITS BASE. w31 applied the stage grid
   unconditionally, which restyles the desktop world map as well. w28, w29 and
   w30 all scoped their docking to the phone. Three lanes to one, and the
   smaller blast radius is the right call for a change the owner has not seen
   yet: the docking below sits inside `@container world-map (max-width:41.99rem)`
   and the desktop DOCK LAYOUT is untouched.
   THE DESKTOP IS NOT ENTIRELY UNTOUCHED, AND AN EARLIER DRAFT OF THIS COMMENT
   CLAIMED IT WAS. The rail reserve is deliberately unscoped, so at 1440px as
   much as at 360px the world-map `main` gains `var(--tab-width,1.45rem)` of
   inline-end padding that `padding:0` had been removing, and the map host gets
   that much narrower. That is the fix working as intended - the rail was
   covering content at every width - but it IS a desktop geometry change and it
   has not been measured at a desktop viewport. Stated here rather than left for
   somebody to discover.

   WHAT THIS BLOCK DELIBERATELY DOES NOT DO, AND WHY (the brief's own warning
   against fixing what is already right):
   - It does NOT restore the 44px height floor on `.ow-g-btn`. Lane `mciv`
     measured those buttons at 77x22 and called it a touch-target defect. It is
     a RECORDED OWNER DECISION: `overwatch.css:353-372` documents that the floor
     was offered as a trade and the owner chose "smaller buttons for now", and
     the screenshots that triggered this work marked the controls as OVERSIZED.
     The real defect in mciv's measurement is not the height - it is that
     `input#timeSlider` sat ON TOP of those buttons (77x10 overlap) and on the
     joystick `.cic-pad` (80x31 overlap), both measured live at 360x640.
     Reachability, not size. The stage grid fixes it without enlarging anything,
     and `--ow-g-btn-min-h` is left as the one-line restore path its author
     built. See `QUESTION-FOR-REVIEW.md`.
   - It does NOT touch `15.04 / 0.000 deg/h`, which is the sidereal rate and
     correct, nor the `Snapshot - N minutes ago` staleness banner, which is
     good behaviour.
   - It does NOT touch any JavaScript. `mciv`'s motion-control JS fix is being
     integrated elsewhere.
   ========================================================================== */
:root{
  /* The fixed right tab rail's width, or zero when it is rolled up. ONE token,
     read by everything that must not sit under the rail. `--tab-width` is the
     shell's own token; this indirection exists so the retracted state is
     expressed once rather than at every consumer. [base: w31] */
  --ow-rail-reserve: var(--tab-width, 1.45rem);
  /* Room for the `.ow-overlay-min` button a panel hosts in its own top-right
     corner. `.ow-timeline` already reserved exactly this as a bare `3.1rem`;
     no other panel reserved anything, which is why the owner's screenshot
     shows `Reset` underneath a minimise button. Measured live at 360x640
     BEFORE: `button.ow-overlay-min` overlaps `button.ow-lens--reset` by
     41x25px. [base: w31] */
  --ow-min-reserve: 3.1rem;
  /* Width of maplibre's own control column, which lives in `#map`'s coordinate
     space and cannot join the stage grid. EVERY docked row reserves it, because
     the column is 265px tall and which panels it reaches depends on their
     heights - see the gutter rule below for the measurement that settled this.
     Measured 54px live at both viewports. [base: w31] */
  --ow-map-ctrl-gutter: 3.5rem;
}
body.rail-rolled-up{ --ow-rail-reserve: 0px; }

/* REQUIREMENT 1 - RETRACTING THE RAIL RECLAIMS ITS WIDTH, AT EVERY WIDTH.
   SPECIFICITY, STATED RATHER THAN STUMBLED INTO. styles.css carries
   `body.rail-rolled-up main[class]{padding-right:clamp(1rem,3vw,2.4rem)}` at
   (0,2,2). A rule written as `body[data-...] .ow-main` is (0,2,1) and LOSES in
   the rolled-up state only - which is how w29 measured the reserve going
   23.2px -> 16px instead of 23.2px -> 0. Leading `html` and naming the `main`
   element takes this to (0,2,3), which wins in BOTH RAIL STATES - out and
   rolled-up - without `!important` and without depending on stylesheet load
   order. "Both states" means the rail, not the embed mode: `html.ow-embed
   body[data-shell-node="overwatch-world-map"] .ow-main{padding:0}`
   (overwatch.css:738-740) is (0,3,2) and BEATS this rule's (0,2,3), so inside
   an `.ow-embed` frame the reserve does not apply. That is left alone on
   purpose - the embed has no tab rail of its own to reserve for - but it means
   this fix is verified on the non-embed surface only, which is the one the
   owner photographed and the one measured at both viewports. Restored as an
   explicit longhand, which a future `padding` shorthand would have to
   overwrite visibly rather than silently. [base: w31] */
html body[data-shell-node="overwatch-world-map"] main.ow-main{
  padding-inline-end: var(--ow-rail-reserve);
  transition: padding-inline-end .2s ease;
}
@media (prefers-reduced-motion:reduce){
  html body[data-shell-node="overwatch-world-map"] main.ow-main{ transition:none }
}

/* ROOT CAUSE 3 - AND THE ONE CROSS-LANE BORROW THAT MEASURED WORSE.
   w28 alone found that `.maplibregl-ctrl-top-right` had outgrown the map:
   54x265 at 360x640, four control groups stacked vertically, reaching 265px
   down and hit-testing over `#owLenses`, its minimise button and `Reset`. Its
   fix lays the groups out in a `row-reverse wrap` flex so the stack is one
   group tall instead of four. That is a real improvement to the CONTROL, and
   on the argument alone it should have let the four lower panels give back the
   3.5rem gutter w31 charged them - 56px of a 360px phone, five times over.

   IT WAS MEASURED AND IT WAS WORSE, SO IT IS NOT TAKEN. Injected live at
   360x640, `row-reverse wrap` turned the 54x265 column into a 156x140 BAND:
   the height halved as w28 predicted, but the width went from 54px to 156px
   and the band then sat across the top-right of the map where `.ow-lenses`
   docks. Three interactive overlaps survived that would not otherwise exist,
   including `button.maplibregl-ctrl-globe-enabled` over `.ow-overlay-min`
   (32x36) and, worst, maplibre's globe toggle back on top of `Reset` (37x25) -
   the owner's own annotation, reintroduced by the fix meant to clear it.

   So w31's design stands, and its stated reason is the right one: a NARROW
   VERTICAL column overlays a gutter that every panel reserves, and that is
   HEIGHT-INDEPENDENT. A short wide band overlays whichever panel happens to be
   in the top rows, which is viewport-dependent - the same hand-tuned-constant
   failure mode this block exists to delete. The gutter below is therefore
   charged to all five panels, as w31 charged it, and this comment is here so
   the next lane does not re-derive w28's idea and re-learn its cost.
   Evidence: `evidence/after-360x640-w28ctrl.json` vs `evidence/after-360x640.json`. */

/* ============================ THE PHONE STAGE ============================= */
@container world-map (max-width: 41.99rem){
  /* THE STAGE GRID. One named row per panel, every dock row `minmax(0,auto)`,
     the whole set pushed to the bottom by the single `1fr` spacer. `#map` is
     `position:absolute;inset:0` and so is out of grid flow entirely - it stays
     full-bleed behind the panels and is not affected by any of this.
     `minmax(0,auto)` and not bare `auto` ON THE FOUR BOTTOM ROWS: an `auto` row
     will not shrink below its content's min-content height, so at 360x640 the
     globe row overflowed into the timeline row and put "Now" back on top of
     "Exact deg/h". The panels scroll inside themselves (`overflow:auto` below),
     so letting their rows shrink is the whole fix - the stage distributes what
     it has and each panel scrolls the remainder.
     THE FIRST ROW IS DELIBERATELY BARE `auto`, and an earlier draft of this
     comment wrongly said "every row". `.ow-lenses` is the filter strip and it
     is the one panel that must not be squeezed to nothing on a short stage: it
     is how the map is filtered at all. It is also the shortest, so it is the
     cheapest row to leave unshrinkable. Measured 67px of a 337px stage at
     360x640 and 71px of 541px at 390x844. [base: w31] */
  #mapView.ow-map-host .ow-map-stage{
    /* The stage is its own size container so a docked panel can be budgeted as
       a SHARE OF THE STAGE (`cqb`) rather than as a pixel number. `world-map`
       is the whole workspace including toolbar and event strip, so a budget
       against it drifts as those change height; against the stage it does not. */
    container-type:size; container-name:ow-stage;
    display:grid;
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:auto minmax(0,1fr) minmax(0,auto) minmax(0,auto) minmax(0,auto) minmax(0,auto);
    grid-template-areas:"ow-lenses" "ow-free" "ow-inspect" "ow-legend" "ow-globe" "ow-time";
    align-content:stretch;
  }
  /* Each panel comes out of the absolute corner it was pinned into and goes
     into its row. `inset:auto` is what actually retires the old constants.
     `justify-self` MUST be stated - `.ow-lenses` inherited `start` from its
     pre-grid rule, and a `start`-aligned grid item sizes to MAX-CONTENT: the
     filter strip measured 416px inside a 367px stage and took its own minimise
     button off the screen with it.
     A DOCKED PANEL IS CLIPPED TO ITS OWN ROW. `.ow-globe-controls` opens a
     `<details class="ow-g-exact">` that painted 23px into the timeline's row.
     Rows cannot protect each other if a panel may paint outside itself, so
     scrolling is the panel's problem to solve inside its own box. [base: w31] */
  #mapView.ow-map-host .ow-lenses,
  #mapView.ow-map-host .ow-inspect,
  #mapView.ow-map-host .ow-map-legend,
  #mapView.ow-map-host .ow-globe-controls,
  #mapView.ow-map-host .ow-timeline{
    position:relative; inset:auto;
    margin:0 .5rem .4rem; width:auto; max-width:none;
    justify-self:stretch;
    min-inline-size:0; max-inline-size:100%;
    overflow:auto; min-block-size:0;
    /* [w28] A panel that reaches its scroll end must not hand the gesture to
       the map underneath it. */
    overscroll-behavior:contain;
  }
  #mapView.ow-map-host .ow-lenses{ grid-area:ow-lenses; margin-block-start:.5rem }
  #mapView.ow-map-host .ow-inspect{ grid-area:ow-inspect }
  #mapView.ow-map-host .ow-map-legend{ grid-area:ow-legend }
  #mapView.ow-map-host .ow-globe-controls{ grid-area:ow-globe }
  #mapView.ow-map-host .ow-timeline{ grid-area:ow-time }

  /* THE MINIMISE GUTTER, STRUCTURALLY - w28's and w30's form, not w31's.
     w31 listed the four panels it had seen carrying a minimise button. A list
     is a constant with a different shape: a panel added later gets no gutter
     and the defect returns silently. `:has(> .ow-overlay-min)` charges the
     gutter to whatever ACTUALLY hosts the button, so the rule stays true by
     construction. [w28 + w30] */
  #mapView.ow-map-host .ow-map-stage > *:has(> .ow-overlay-min){
    padding-inline-end: var(--ow-min-reserve);
  }
  /* EVERY DOCKED PANEL PAYS THE VENDOR GUTTER, NOT ONLY THE TOP ONE.
     maplibre's control column lives in `#map`'s coordinate space and CANNOT
     join this grid, so the panels reserve its width instead. Reserving it only
     on the rows that looked like they collided makes the collision depend on
     PANEL HEIGHT: the column runs 265px down from the top of the map, so at
     390x844 `.ow-globe-controls` started at y=402 and cleared it and at
     360x640 it started at y=347 and did not. A reservation that is correct at
     one viewport and wrong at another is the same hand-tuned-constant defect
     this block exists to delete. Charging every row makes it height-
     independent, which is the only version that stays true. See the w28 note
     above for the variant that was measured and rejected. [base: w31] */
  #mapView.ow-map-host .ow-lenses,
  #mapView.ow-map-host .ow-inspect,
  #mapView.ow-map-host .ow-map-legend,
  #mapView.ow-map-host .ow-globe-controls,
  #mapView.ow-map-host .ow-timeline{
    margin-inline-end: calc(.5rem + var(--ow-map-ctrl-gutter));
  }
  /* The timeline's original shorthand `padding:.35rem 3.1rem .35rem .5rem` is
     restated as longhands so the gutter above is not silently re-zeroed by it. */
  #mapView.ow-map-host .ow-timeline{
    padding-block:.35rem; padding-inline-start:.5rem;
  }

  /* THE HEIGHT BUDGET, AND WHY IT IS NOT THE OLD CONSTANTS COMING BACK.
     The difference is the failure mode, and it is the whole point. A wrong
     `bottom:3.4rem` put one panel ON TOP of another and the page could not
     tell. A wrong budget here makes ONE panel scroll inside its own box, which
     is visible, local, harmless, and cannot reach a neighbour. [base: w31]

     `(min-height: 0px)` IS NOT A SAFETY CHECK AND MUST NOT BE READ AS ONE.
     It is the syntactic wrapper that makes `cqb` resolve against `ow-stage`;
     a container of ZERO height still matches `min-height: 0px`. So if the
     stage ever did collapse, this query would still apply, every budget would
     compute to 0, and the dock would VANISH rather than fall back. The thing
     that actually keeps the stage non-zero is the definite height chain above
     it - `body{height:100dvh}` -> `.ow-main{flex:1 1 auto;min-block-size:0}`
     -> `#mapView{container-type:size;flex:1 1 auto}` -> `.ow-map-workspace`
     `grid-template-rows:auto minmax(0,1fr) auto;height:100%` -> the stage in
     that `1fr` row. Measured 337x337 at 360x640 and 367x541 at 390x844. If a
     future change breaks that chain, the symptom will be a blank dock, and
     THIS is the comment to come back to. */
  @container ow-stage (min-height: 0px){
    #mapView.ow-map-host .ow-lenses{ max-block-size:20cqb }
    #mapView.ow-map-host .ow-map-legend{ max-block-size:14cqb }
    #mapView.ow-map-host .ow-inspect{ max-block-size:26cqb }
    #mapView.ow-map-host .ow-globe-controls{ max-block-size:26cqb }
    #mapView.ow-map-host .ow-timeline{ max-block-size:26cqb }
  }

  /* THE LEGEND IS A KEY, NOT A CONTROL. At phone width it collapses to a
     single scrollable line the reader can still parse, instead of a 71px block
     sitting on the timeline's UTC stamp. [base: w31] */
  #mapView.ow-map-host .ow-map-legend{ padding-block:.3rem }
  #mapView.ow-map-host .ow-map-legend-list{
    grid-auto-flow:column; grid-auto-columns:max-content;
    gap:.5rem; overflow-x:auto; touch-action:pan-x; overscroll-behavior-x:contain;
  }
  /* THE LENSES PANEL IS LEFT TO THE `overflow:auto` IT ALREADY HAS, AND THE
     RULE THAT USED TO BE HERE WAS A REAL DEFECT, NOT JUST A WRONG COMMENT.
     w31 wrote `flex-wrap:nowrap; overflow-x:auto; overflow-y:hidden` here and
     described it as "the filter strip scrolls sideways". All three parts are
     aimed at the wrong element. `.ow-lenses` is `display:flex;
     flex-direction:column` (overwatch.css:528-531), so `flex-wrap:nowrap` on
     it does nothing to the chip row; and the sideways scroller is already
     `.ow-lens-strip` (overwatch.css:546-552), which has carried `overflow-x:auto`
     since the lens work landed, with `Reset` deliberately parked OUTSIDE it so
     it cannot be scrolled off.
     The harmful part was `overflow-y:hidden`, which out-ordered the
     `overflow:auto` this block gives every docked panel. Combined with the
     `max-block-size:20cqb` budget, anything past the cap - `.ow-lens-note`,
     which is where "showing 12 of 58" and the blocked/partial warnings live -
     would have been CLIPPED WITH NO WAY TO REACH IT rather than scrolled. The
     panel measured 67px against a 67px cap at 360x640, i.e. exactly at the
     boundary where that starts to bite.
     Caught by adversarial child `rcav`, not by measurement: a clipped note is
     not an overlap, a truncation or an overflow, so every number in the probe
     stayed green. Second time on this block that reading beat measuring. */

  /* REQUIREMENT 4 - THE EVENTS LIST SURVIVES PHONE WIDTH, AND IS READABLE.
     `--ow-event-rail-height` was applied as BOTH `height` and `min-height` with
     `overflow:hidden`. Measured live at 360x640 BEFORE: the rail is 360x104 and
     `.ow-event-rail-status` inside it is 347x SEVEN PIXELS at y=627 - a line of
     text guillotined to a 7px sliver. `.ow-event-categories` was squashed from
     its 44px floor to 18px while the 44px buttons inside kept their size and
     spilled across the card row. That is the owner's "double-stacked and cut
     mid-word". The constant becomes a FLOOR and the real content decides the
     height; the space comes out of the map. 58 live features carrying real
     categories are worth more than 21px of map. [base: w31] */
  #mapView.ow-map-host .ow-event-rail:not([hidden]){
    height:auto;
    min-height:var(--ow-event-rail-height);
    max-height:none;
    overflow:visible;
  }
  #mapView.ow-map-host .ow-event-categories{ flex:0 0 auto; min-height:44px }
  #mapView.ow-map-host .ow-event-card-row{ flex:0 0 auto }
  #mapView.ow-map-host .ow-event-rail-status{ flex:0 0 auto }
  /* TWO LINES, NOT ONE. `-webkit-line-clamp:1` is what cut "Military strike"
     to "Milit...". The owner said the strip must be READABLE, and one clamped
     line of a two-word category is not. [w29] */
  #mapView.ow-map-host .ow-event-card-title{ -webkit-line-clamp:2 }
  #mapView.ow-map-host .ow-event-card{ width:min(14rem,74cqi) }
  /* A category heading that wraps inside an 18px strip is the other half of
     "cut mid-word". [w30] */
  #mapView.ow-map-host .ow-event-group-heading{ max-width:none; white-space:nowrap }

  /* THE HEADER STAT STOPS TRUNCATING - BOTH COPIES.
     Measured live at 360x640 BEFORE: `#owFeedLive.ow-feed-status` needs 439px
     of a 191px box and `.ow-event-rail-status` needs 410px of 347px, so the
     owner saw "Mapped 58 - unresolved 0 - prove..." ellipsised TWICE in one
     screenshot. Naming only the toolbar copy would have left the defect on the
     screen while the report said it was handled. [base: w31] */
  #mapView.ow-map-host .ow-map-toolbar{
    flex-wrap:wrap; row-gap:.15rem; align-items:flex-start;
    padding-block:.35rem;
  }
  #mapView.ow-map-host .ow-feed-status{
    white-space:normal; overflow:visible; text-overflow:clip;
    flex:1 1 100%; min-width:0; line-height:1.25;
  }
  #mapView.ow-map-host .ow-map-toolbar-label{ font-size:.9rem }
  #mapView.ow-map-host .ow-event-rail-status{
    white-space:normal; overflow:visible; text-overflow:clip; line-height:1.25;
  }
  /* THE MOTION BUTTON LABELS, AND A REGRESSION THIS BLOCK CAUSED AND THEN FIXED.
     `.ow-g-btn` carries `white-space:nowrap`, so "Satellite on" in a 77px
     column was clipped rather than wrapped. w31's fix lets it wrap, with
     `overflow-wrap:anywhere`. Injected live and PHOTOGRAPHED at 360x640, that
     produced "Satelli / te on", "Day/n / ight" and "Motio / n" - the labels
     broken in the middle of words. The owner's complaint was literally "cut
     mid-word"; shipping a fix that breaks five buttons mid-word would have
     traded one instance of his complaint for another, and only the screenshot
     caught it. The measurements were clean for both variants, because a broken
     word is not an overlap, a clipped box, or a truncation.
     `break-word` breaks only when a word cannot fit on a line of its own;
     `anywhere` breaks whenever it helps the line. The column then has to be
     wide enough for the longest label, so the three-track grid becomes two at
     phone width: 3 x 77px becomes 2 x ~125px, which fits "Day/night on" whole.
     THIS DOES NOT CHANGE ANY BUTTON'S HEIGHT - see `QUESTION-FOR-REVIEW.md`
     section 1 on why the 44px floor stays off. It changes how many sit on a
     row, and the panel has its own row now so the extra row costs nothing. */
  #mapView.ow-map-host .ow-globe-controls .ow-g-btn{
    white-space:normal; overflow-wrap:break-word; line-height:1.15;
  }
  #mapView.ow-map-host .ow-globe-controls .ow-g-buttons{
    grid-template-columns:repeat(2,minmax(0,1fr));
  }

  /* owux — owner 2026-09-16. Stacking order is unchanged. Open panels are
     LEFT-EDGE drawers. The host is width 0 unless a child has .ow-drawer-open
     (CSS display:none does not set [hidden], so :has(:not([hidden])) left a
     296px invisible overlay). Right tab rail starts retracted. Event cards
     lose the two meta lines and per-box counts; colour is the type cue.
     Geometry transitions omitted so a probe cannot measure mid-animation. */
  #mapView.ow-phone-drawers .ow-overlay-dock{
    position:absolute;left:0;top:0;bottom:auto;z-index:20;
    margin:0;padding:.2rem 0;width:2.75rem;
    display:flex;flex-direction:column;flex-wrap:nowrap;justify-content:flex-start;
    align-items:stretch;gap:.15rem;
    pointer-events:none;overflow:visible;
  }
  #mapView.ow-phone-drawers .ow-overlay-restore{
    display:flex;align-items:center;justify-content:center;
    pointer-events:auto;width:2.75rem;min-width:44px;min-height:44px;
    border-radius:0 .4rem .4rem 0;
    /* WORDS, upright (rlim 2026-09-18). These were `T Lg Ln M G`, rotated. The
       words come from the context's `railSlots` declaration; at 10px the
       longest, "Globe", measures 42px of the 44 available. */
    writing-mode:horizontal-tb;transform:none;
    font-size:.625rem;font-weight:700;letter-spacing:.01em;line-height:1;
  }
  #mapView.ow-phone-drawers .ow-overlay-restore[hidden]{display:flex}
  #mapView.ow-phone-drawers .ow-phone-drawer-host{
    position:absolute;left:0;top:0;bottom:0;z-index:11;
    width:0;margin:0;padding:0;
    display:flex;flex-direction:column;justify-content:flex-start;gap:2px;
    pointer-events:none;overflow:visible;
  }
  #mapView.ow-phone-drawers .ow-phone-drawer-host[data-open],
  #mapView.ow-phone-drawers .ow-phone-drawer-host.is-open,
  #mapView.ow-phone-drawers .ow-phone-drawer-host:has(.ow-drawer-open){
    width:min(18.5rem,calc(100% - 2.75rem));
    margin:0;padding:2px 0 2px 2.75rem;overflow:auto;
  }
  #mapView.ow-phone-drawers .ow-phone-drawer-host > .ow-drawer-open{
    pointer-events:auto;position:relative;inset:auto;
    width:auto;max-width:none;margin:0;
    border-top-left-radius:0;border-bottom-left-radius:0;
    flex:0 1 auto;max-height:46%;overflow:auto;
  }
  #mapView.ow-phone-drawers .ow-lenses.ow-drawer-open,
  #mapView.ow-phone-drawers .ow-inspect.ow-drawer-open,
  #mapView.ow-phone-drawers .ow-map-legend.ow-drawer-open,
  #mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open,
  #mapView.ow-phone-drawers .ow-timeline.ow-drawer-open{
    margin-block:0;margin-inline-start:0;margin-inline-end:0;
    border-top-left-radius:0;border-bottom-left-radius:0;
  }
  #mapView.ow-phone-drawers .ow-map-toolbar{ padding-block:0; min-height:0; gap:.2rem }
  #mapView.ow-phone-drawers .ow-map-toolbar-label{ font-size:.78rem; line-height:1.1; margin:0 }
  #mapView.ow-phone-drawers .ow-feed-status{ display:none }
  #mapView.ow-phone-drawers .ow-event-count{ display:none }
  #mapView.ow-phone-drawers .ow-event-group-heading{ display:none }
  /* rlim 2026-09-18 - THE ONE-ROW PHONE RAIL SQUEEZED THE CATEGORIES TO 21px.
     owux laid categories and cards out as ONE 48px row (`flex-direction:row`,
     categories `flex:0 1 auto;max-width:38%`). Measured live at 390x844:
     `.ow-event-categories` clientWidth 21 / scrollWidth 381 - the reader saw
     "Al" and side-scrolled a 21px window to reach the other four chips. Cause:
     both items shrink in proportion to their flex BASIS, the card list's basis
     is its whole scroll width (6374px), and a scroll container's automatic
     minimum is 0, so the categories absorbed none of the row. Five 44px chips
     and a 44px card cannot share one 381px line, so the two rows are stacked
     and the content decides the height (the w31 rule above, which owux's fixed
     48px had overridden). This costs the owner's compact rail 48px of map
     (48 -> 96); it reverses owux's NUMBER, not its intent - cards stay one
     line, no counts, no status sentence. Chip labels may wrap onto two lines
     so all five fit WITHOUT sideways scroll down to 360px; the 44px floor on
     every target is unchanged. */
  #mapView.ow-phone-drawers .ow-event-rail:not([hidden]){
    flex-direction:column;align-items:stretch;gap:.2rem;
    height:auto;min-height:var(--ow-event-rail-height);max-height:none;overflow:visible;
    padding:.12rem .28rem;padding-bottom:calc(.12rem + env(safe-area-inset-bottom,0px));
  }
  #mapView.ow-phone-drawers .ow-event-categories{
    flex:0 0 auto;max-width:none;min-height:44px;overflow-x:auto;gap:.25rem;
  }
  #mapView.ow-phone-drawers .ow-event-category{
    flex:1 1 auto;min-width:min-content;width:auto;padding:.2rem .45rem;font-size:.72rem;
    white-space:normal;line-height:1.15;justify-content:center;text-align:center;
  }
  #mapView.ow-phone-drawers .ow-event-category[data-category="LIVE"]{ min-width:44px }
  #mapView.ow-phone-drawers .ow-event-card-row{ flex:0 0 auto;min-width:0;min-height:44px }
  /* The rail's height is its content now, so a 4px scrollbar arriving with the
     cards would move the map (lenses.verify.mjs: map.h 752.98 -> 748.98). On a
     phone both rows are swiped, and the cards also have their step buttons. */
  #mapView.ow-phone-drawers .ow-event-categories,
  #mapView.ow-phone-drawers .ow-event-cards{ scrollbar-width:none }
  #mapView.ow-phone-drawers .ow-event-categories::-webkit-scrollbar,
  #mapView.ow-phone-drawers .ow-event-cards::-webkit-scrollbar{ display:none }
  #mapView.ow-map-host .ow-event-card-title{ -webkit-line-clamp:1 }
  #mapView.ow-map-host .ow-event-card{ width:auto;padding:.2rem 1.7rem .2rem .35rem }
  #mapView.ow-map-host .ow-event-card-shell{ width:min(16rem,82cqi) }
  #mapView.ow-map-host .ow-map-workspace{ --ow-event-rail-height:96px }
  #mapView.ow-map-host .ow-event-rail-status{ display:none }
  #mapView.ow-map-host .ow-map-toolbar{ padding-block:0 }
  #mapView.ow-map-host .ow-map-stage{ padding-block:0 }
  #mapView.ow-phone-drawers .maplibregl-ctrl-bottom-right,
  #mapView.ow-phone-drawers .maplibregl-ctrl-bottom-left{
    margin-bottom:4px;max-width:calc(100% - 3.2rem);
  }
  #mapView.ow-phone-drawers .maplibregl-ctrl-attrib{
    font-size:.62rem;max-width:100%;
  }
}

@media (max-width: 41.99rem){
  html body[data-shell-node="overwatch-world-map"].ow-phone-viewport:not(.ow-phone-rail-open){
    --ow-rail-reserve:0px;
  }
  html body[data-shell-node="overwatch-world-map"].ow-phone-viewport:not(.ow-phone-rail-open) #right-drawer-tabs{
    display:none;opacity:0;pointer-events:none;box-shadow:none;width:0;
  }
  html body[data-shell-node="overwatch-world-map"].ow-phone-viewport:not(.ow-phone-rail-open) main.ow-main{
    padding-inline-end:0px;
  }
  @media (prefers-reduced-motion:reduce){
    #mapView.ow-phone-drawers .ow-overlay-restore{ transform:none }
  }
}

/* ---------------------------------------------------------------------------
   RAIL PAGE 2 (rlim 2026-09-18). A panel the shell's rail HOSTS has a tab on
   page 2 of the right rail, so above phone width it no longer also floats over
   the map: `.ow-rail-hosted` is set by syncRailHostedPanels() from the shell's
   declaration, on the panel and on its restore button. While a slot is open the
   shell lends the panel its one module drawer, outside `#mapView`, so this rule
   cannot hide the mounted copy. Phone keeps the owner's left dock (owux), and an
   embed has no rail; neither is marked.
   --------------------------------------------------------------------------- */
body:not(.ow-phone-viewport) #mapView.ow-map-host .ow-rail-hosted{display:none !important}

/* ---------------------------------------------------------------------------
   THE EXPANDED PANELS (rlim 2026-09-18, reconciled with expd's specification).
   "Expanded" is a panel that has been OPENED: borrowed by the shell's module
   drawer (rail page 2), or slid out of the phone's left dock. Both get one
   grammar - a quiet title band, content in normal flow, controls sized by what
   they do to the map - and one type scale: 13px on a fine pointer, 14px on a
   coarse one, 12px for explanation, nothing interactive below that. The control
   floor is the shell's own `--control-min-h` (30.4px fine / 44px coarse), so a
   mouse is not handed 44px slabs and a finger is not handed 22px slivers.

   THIS REVERSES AN OWNER CHOICE, DELIBERATELY AND ONLY HERE: the globe buttons
   were left at 22px ("smaller buttons for now") because the panel floated over
   the map and every pixel of it covered globe. An opened drawer is not over the
   globe's working area, so the trade that justified 22px no longer exists. The
   on-map copy (an embed, or a shell that declares no slots) keeps the owner's
   small buttons - `--ow-g-btn-min-h` above is untouched.

   The palette is the map's existing `--ow-*` set. The borrowed panels are drawn
   with white-alpha internals (pad, puck, chips) that only read on a dark
   surface, so the module drawer takes the map's panel colour on this page
   rather than every internal being recoloured for a light one.
   --------------------------------------------------------------------------- */
body[data-shell-node="overwatch-world-map"] #module-drawer{
  background:var(--ow-panel);color:var(--ow-text);border-color:var(--ow-line);
}
body[data-shell-node="overwatch-world-map"] #module-drawer > header{
  border-bottom-color:var(--ow-line);background:color-mix(in srgb,var(--ow-text) 5%,transparent);
}
body[data-shell-node="overwatch-world-map"] #module-drawer > header h2{
  color:var(--ow-text);font-size:.875rem;font-weight:600;letter-spacing:.02em;text-transform:none;
}
body[data-shell-node="overwatch-world-map"] #module-drawer .close{
  color:var(--ow-text);background:transparent;border-color:transparent;
}
body[data-shell-node="overwatch-world-map"] #module-drawer .close:hover,
body[data-shell-node="overwatch-world-map"] #module-drawer .close:focus-visible{
  border-color:var(--ow-accent);color:var(--ow-accent);
}
#module-drawer-slot{padding:.75rem;overflow:auto;scroll-padding:.25rem}
#module-drawer-slot > [data-rail-slot-mounted]{
  position:static;inset:auto;z-index:auto;width:auto;max-width:none;max-height:none;margin:0;padding:0;
  /* `.drawer-content` is a grid; a leftover `grid-area:rail|lens` would invent
     named tracks in it and push the panel 86px off the drawer's left edge. */
  grid-area:auto;justify-self:stretch;align-self:start;
  background:transparent;border:0;border-radius:0;box-shadow:none;backdrop-filter:none;contain:none;
  display:flex;flex-direction:column;align-items:stretch;gap:.75rem;
  color:var(--ow-text);font-size:.8125rem;line-height:1.4;
}
/* The drawer's own header carries the title and the one dismissal. */
#module-drawer-slot .ow-overlay-head{display:none}
/* Motion has its own tab one position below; a second launcher here is the
   duplicate btnf found. The phone drawer keeps it - there the rail is rolled up. */
#module-drawer-slot .ow-g-btn[data-g="motion"]{display:none}

/* The title band, wherever a headed panel shows it (on the map and on phone). */
.ow-overlay-head{
  display:flex;align-items:center;gap:.5rem;flex:0 0 auto;
  margin:0 0 .35rem;padding:0 0 .25rem;
  border-bottom:1px solid color-mix(in srgb,var(--ow-line) 70%,transparent);
}
.ow-overlay-title{
  flex:1 1 auto;min-width:0;margin:0;
  font-size:.8125rem;line-height:1.3;font-weight:600;letter-spacing:.01em;color:var(--ow-text);
}
.ow-overlay-headed > .ow-map-legend-title{display:none}
#mapView.ow-map-host .ow-overlay-head .ow-overlay-min{
  position:static;flex:0 0 auto;
  min-width:var(--control-min-h,1.9rem);min-height:var(--control-min-h,1.9rem);
  background:transparent;border-color:transparent;font-size:1rem;line-height:1;opacity:.8;
}
#mapView.ow-map-host .ow-overlay-head .ow-overlay-min:hover,
#mapView.ow-map-host .ow-overlay-head .ow-overlay-min:focus-visible{
  opacity:1;border-color:var(--ow-accent);outline:none;
}
/* The two bare dismissals that remain (timeline, camera) are incidental
   controls: quiet at rest, and on a mouse not the largest thing on the map.
   The 44px floor on a coarse pointer is unchanged. */
#mapView.ow-map-host .ow-overlay-min{background:transparent;border-color:color-mix(in srgb,var(--ow-text) 14%,transparent);opacity:.75}
#mapView.ow-map-host .ow-overlay-min:hover,
#mapView.ow-map-host .ow-overlay-min:focus-visible{opacity:1;border-color:var(--ow-accent)}
@media (pointer:fine){
  #mapView.ow-map-host .ow-overlay-min{min-width:1.9rem;min-height:1.9rem}
  #mapView.ow-map-host .ow-timeline{padding-right:2.6rem}
}
.ow-map-legend-hint{display:none}

/* ---- one body grammar for both opened contexts ---- */
:is(#module-drawer-slot > [data-rail-slot-mounted], #mapView.ow-phone-drawers .ow-overlay-headed.ow-drawer-open){
  font-size:.8125rem;line-height:1.4;
}
@media (pointer:coarse){
  :is(#module-drawer-slot > [data-rail-slot-mounted], #mapView.ow-phone-drawers .ow-overlay-headed.ow-drawer-open){font-size:.875rem}
}
#mapView.ow-phone-drawers .ow-overlay-headed.ow-drawer-open{padding:.5rem;gap:.5rem}
/* Below 820px the globe panel is a two-column grid (pad | buttons) built for a
   strip docked under the map. Opened as a drawer it is a column like the rest:
   the grid squeezed the buttons to 60px and broke "Satellite" mid-word. */
#mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open{
  display:flex;flex-direction:column;flex-wrap:nowrap;align-items:stretch;
}
/* The timeline is an instrument, not a headed panel, but opened from the dock
   it is still a touch surface: its controls take the same 44px floor. */
#mapView.ow-phone-drawers .ow-timeline.ow-drawer-open :is(button:not(.ow-overlay-min),input[type=datetime-local]){
  min-height:var(--control-min-h,2.75rem);font-size:.875rem;
}
#mapView.ow-phone-drawers .ow-phone-drawer-host > .ow-overlay-headed.ow-drawer-open{max-height:64%;width:auto;align-self:stretch}

/* GLOBE: the pad is the instrument. Row 1 is about ROTATION - Start/Stop spin,
   the primary action, beside Earth rate; row 2 is what is DRAWN - Satellite
   and Day/night. Exact rates are a labelled form, one field per row, never two
   inputs sharing a line. */
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-padmount .cic-pad{
  width:7rem;height:7rem;
}
@media (pointer:fine){
  #module-drawer-slot .ow-g-padmount .cic-pad{width:6rem;height:6rem}
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-rate{
  font-size:inherit;opacity:1;text-align:center;color:color-mix(in srgb,var(--ow-text) 72%,transparent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-buttons{
  grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem;
}
:is(#module-drawer-slot > .ow-globe-controls, #mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open) .ow-g-btn{
  min-height:var(--control-min-h,1.9rem);padding:.3rem .6rem;
  font-size:inherit;line-height:1.25;border-radius:.25rem;
  white-space:normal;overflow-wrap:break-word;opacity:1;
  background:color-mix(in srgb,var(--ow-text) 7%,transparent);
  border:1px solid color-mix(in srgb,var(--ow-text) 22%,transparent);
}
:is(#module-drawer-slot > .ow-globe-controls, #mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open) .ow-g-btn:hover{
  background:color-mix(in srgb,var(--ow-text) 13%,transparent);border-color:var(--ow-accent);
}
:is(#module-drawer-slot > .ow-globe-controls, #mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open) .ow-g-btn[data-g="spin"]{
  font-weight:600;border-color:var(--ow-accent);
}
:is(#module-drawer-slot > .ow-globe-controls, #mapView.ow-phone-drawers .ow-globe-controls.ow-drawer-open) .ow-g-btn[data-on="yes"]{
  font-weight:600;border-color:var(--ow-accent);
  background:color-mix(in srgb,var(--ow-accent) 22%,transparent);
  box-shadow:inset 0 -2px 0 var(--ow-accent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact{font-size:inherit;opacity:1}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact summary{
  min-height:var(--control-min-h,1.9rem);display:flex;align-items:center;padding:0;
  color:color-mix(in srgb,var(--ow-text) 82%,transparent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact label{
  display:grid;grid-template-columns:1.25rem minmax(5.5rem,1fr) auto;align-items:center;gap:.5rem;margin-top:.4rem;
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact label::after{
  content:"\00B0/h";font-size:.75rem;color:color-mix(in srgb,var(--ow-text) 65%,transparent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact input{
  min-height:var(--control-min-h,1.9rem);font-size:inherit;padding:.25rem .5rem;border-radius:.25rem;
  background:color-mix(in srgb,var(--ow-bg) 70%,transparent);
  border:1px solid color-mix(in srgb,var(--ow-text) 22%,transparent);
}
@media (pointer:coarse){
  /* 16px stops iOS zooming the page when the field takes focus. */
  :is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-g-exact input{font-size:1rem}
}

/* LENSES: the chips WRAP - an opened panel has the room, and a sideways
   scroller hid the fifth lens. Reset is an ordinary last action on its own
   row, never under a dismissal. */
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens-row{
  flex-direction:column;align-items:stretch;gap:.5rem;
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens-strip{
  flex-wrap:wrap;overflow:visible;gap:.5rem;padding:0;
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens{
  flex:1 1 7rem;min-height:var(--control-min-h,1.9rem);padding:.3rem .7rem;
  font-size:inherit;line-height:1.25;border-radius:.25rem;text-align:center;
  background:color-mix(in srgb,var(--ow-text) 7%,transparent);
  border:1px solid color-mix(in srgb,var(--ow-text) 22%,transparent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens:hover{border-color:var(--ow-accent)}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens[aria-pressed="true"]{
  font-weight:600;box-shadow:inset 0 -2px 0 var(--ow-accent);
  background:color-mix(in srgb,var(--ow-accent) 22%,transparent);
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens--reset{
  flex:0 0 auto;align-self:flex-end;background:transparent;
  border-color:transparent;text-decoration:underline;text-underline-offset:.2em;
}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-lens-note{
  /* STILL A FIXED BOX THAT SCROLLS. The note is rewritten on every press; sized
     from its text the panel changed height with each lens (measured by
     lenses.verify.mjs: 280 -> 416px), and on the phone drawer that is a moving
     target under the thumb. Four lines of room at 12px; longer text scrolls.
     expd asked for an empty note to take no space - declined for this reason. */
  height:4.2rem;font-size:.75rem;line-height:1.35;opacity:1;
  color:color-mix(in srgb,var(--ow-text) 78%,transparent);
}

/* SYMBOLS: reference rows, not controls - 16px swatch, the label, and the
   sentence that says what the symbol stands for (it was in the data all along
   and shown nowhere). Rows are not inflated to a touch floor they do not need. */
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-list{gap:.75rem}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-item{align-items:flex-start;gap:.6rem;min-height:1.5rem}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-swatch{width:1rem;height:1rem;margin-top:.15rem}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-text{display:flex;flex-direction:column;gap:.1rem;min-width:0}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-label{font-weight:600}
:is(#module-drawer-slot, #mapView.ow-phone-drawers .ow-drawer-open) .ow-map-legend-hint{
  display:block;font-size:.75rem;line-height:1.35;color:color-mix(in srgb,var(--ow-text) 72%,transparent);
}
#module-drawer-slot > .ow-map-legend{font-size:.8125rem}
/* The legend's list, row and swatch rules above are scoped to `#mapView`, which
   the borrowed panel has left: without these it rendered as a bulleted list
   with no symbols at all - the one thing a symbol key is for. */
#module-drawer-slot .ow-map-legend-list{margin:0;padding:0;list-style:none;display:grid}
#module-drawer-slot .ow-map-legend-item{display:flex}
#module-drawer-slot .ow-map-legend-swatch{flex:0 0 auto;border:1px solid #c5d3dd}
#module-drawer-slot .ow-map-legend-diamond{background:#ff5c67;transform:rotate(45deg);border-radius:1px}
#module-drawer-slot .ow-map-legend-circle{background:#3b82f6;border-radius:50%}

/* ---------------------------------------------------------------------------
   THE EVENT RAIL ABOVE PHONE WIDTH (rlim 2026-09-18). Measured at 1440 and 820:
   `.ow-event-categories` was 37px tall around 44px chips - the fixed 112px rail
   (`height` AND `min-height`, `overflow:hidden`) split its space over THREE
   rows, and the third was `.ow-event-rail-status`, which prints the very
   sentence the toolbar's `#owFeedLive` already shows 800px above it (both are
   written from one `text` in overwatch.js). So the chips had their bottom 7px
   cut off to make room for a duplicate. The duplicate goes; the toolbar copy is
   the one visible, announced status. The constant stays the rail's FLOOR - the
   categories keep their full 44px and the cards take what is left.
   --------------------------------------------------------------------------- */
.ow-event-rail-status{display:none}
#mapView.ow-map-host .ow-event-rail:not([hidden]){
  height:auto;min-height:var(--ow-event-rail-height);max-height:none;
}
#mapView.ow-map-host .ow-event-categories{flex:0 0 auto;min-height:44px}
#mapView.ow-map-host .ow-event-card-row{flex:1 1 auto}
