/* ReefNav — compositions.

   PHONE: the map is the entire screen. Three things sit on it and nothing
   else — a centred brand pill at the top (which is also the way into search,
   views, overlays and every other destination), a locate button on the right
   edge, and a forecast deck at the bottom that rests at peek height and rises
   to full. Everything else is summoned and dismissed.

   DESKTOP: not the phone stretched. A permanent dashboard column carries the
   forecast because there is room for it, the map takes the remaining width as
   a hero, and the map's own controls cluster in one corner of it. */

#app{position:fixed;inset:0;overflow:hidden}

/* ---- map ---------------------------------------------------------------- */
#map{position:absolute;inset:0}
#map canvas{outline:none}
/* Particle overlay rides above the map canvas but must never eat a gesture. */
#particles{position:absolute;inset:0;pointer-events:none;z-index:2}
.maplibregl-ctrl-attrib{display:none!important}

/* Our own attribution, because the upstream terms require it to be visible
   and the default control fights the deck for the same corner. */
#credit{
  position:absolute;z-index:3;left:12px;bottom:calc(var(--peek) + 10px + var(--safe-b));
  max-width:min(58vw,420px);font-size:9.5px;line-height:1.35;color:var(--credit);
  text-shadow:var(--credit-shadow);pointer-events:none;
}

/* ---- brand pill: the single entry point --------------------------------- */
#brand{
  position:absolute;z-index:20;top:calc(10px + var(--safe-t));left:50%;
  transform:translateX(-50%);
  height:42px;padding:0 18px 0 14px;border-radius:999px;
  display:flex;align-items:center;gap:9px;
  font-size:15px;font-weight:650;letter-spacing:-.01em;
  transition:transform .2s var(--ease);
}
#brand:active{transform:translateX(-50%) scale(.96)}
/* The mark is a transparent reef/current silhouette. The app tile is reserved
   for iOS and browser chrome; the glass brand control gets the mark alone. */
#brand .mark{width:24px;height:24px;flex:none;border-radius:50%;
  object-fit:contain;filter:var(--mark-shadow)}
#brand .caret{width:13px;height:13px;margin-left:1px;opacity:.5;
  fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}

/* Status dot rides on the pill: fresh / stale / offline, no words needed
   until you open the menu. */
#brand .dot{width:6px;height:6px;border-radius:999px;background:var(--good);flex:none}
#brand .dot[data-state="stale"]{background:var(--warn)}
#brand .dot[data-state="off"]{background:var(--danger)}

/* ---- locate ------------------------------------------------------------- */
#locate{
  position:absolute;z-index:20;right:12px;
  bottom:calc(var(--peek) + 18px + var(--safe-b));
}

/* ---- timeline: a summoned helper, never standing chrome ------------------
   The button stacks above locate on the right edge. The dock is one slim
   glass ribbon that borrows the strip above the deck while it is open and
   gives it straight back when dismissed. */
#timebtn{
  position:absolute;z-index:20;right:12px;
  bottom:calc(var(--peek) + 18px + 44px + 12px + var(--safe-b));
}
#timebtn[aria-pressed="true"]{
  background:color-mix(in srgb,var(--signal) 22%,transparent);
  border-color:color-mix(in srgb,var(--signal) 45%,transparent);
}
#tdock{
  position:absolute;z-index:22;left:12px;right:12px;
  bottom:calc(var(--peek) + 10px + var(--safe-b));
  height:64px;border-radius:20px;
  display:flex;align-items:center;gap:9px;padding:8px 10px 8px 8px;
  /* Lighter than chrome glass: the curve behind it must read through. */
  background:var(--dockbg);
  -webkit-backdrop-filter:blur(30px) saturate(150%);
  backdrop-filter:blur(30px) saturate(150%);
}
#tdock[hidden]{display:none}
/* One round transport key. A circle reads as a single soft object beside the
   ribbon instead of a second rectangular panel bracketing it. */
#tdock .tbtn{
  flex:none;width:36px;height:36px;border-radius:999px;
  display:grid;place-items:center;
  color:var(--ink);background:var(--raise);
  border:.5px solid var(--stroke);
  transition:background .16s var(--ease),transform .16s var(--ease);
}
@media (hover:hover) and (pointer:fine){ #tdock .tbtn:hover{background:var(--raise-2)} }
#tdock .tbtn:active{transform:scale(.93)}
#tdock .tbtn svg{grid-area:1/1;width:15px;height:15px;fill:currentColor;stroke:none}
#tdock .tbtn .pp,#tdock .tbtn.on .pi{opacity:0}
#tdock .tbtn.on .pp{opacity:1}
#tdock .tbtn.on{background:color-mix(in srgb,var(--signal) 30%,transparent);
  border-color:color-mix(in srgb,var(--signal) 52%,transparent)}
#ribwrap{position:relative;flex:1;min-width:0;align-self:stretch;
  touch-action:none;cursor:ew-resize}

/* "Now" only exists while the selection has left the present. It sits inside
   the ribbon's label margin — the one strip of it that carries no data — so
   it costs the curve no width at all. */
#tnow{
  position:absolute;z-index:3;top:3px;right:4px;
  display:flex;align-items:center;gap:4px;height:19px;padding:0 8px 0 6px;
  border-radius:999px;cursor:pointer;
  background:color-mix(in srgb,var(--now) 30%,var(--now-ground));
  border:.5px solid color-mix(in srgb,var(--now) 55%,transparent);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  color:var(--now-ink);font-size:9.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  opacity:0;transform:translateY(-4px);
  transition:opacity .2s var(--ease),transform .2s var(--ease);
}
#tnow[hidden]{display:none}
#tnow.on{opacity:1;transform:none}
@media (hover:hover) and (pointer:fine){ #tnow:hover{background:color-mix(in srgb,var(--now) 44%,var(--now-ground))} }
#tnow svg{width:11px;height:11px;flex:none;
  fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
#ribbon{position:absolute;inset:0;border-radius:12px;overflow:hidden}
#ribdays{position:absolute;left:0;right:0;top:0;height:12px;pointer-events:none}
#ribdays span{position:absolute;top:1px;font:8.5px var(--mono);letter-spacing:.06em;
  color:var(--ink-3);padding-left:4px}
#ribhead{position:absolute;top:2px;bottom:2px;width:2px;border-radius:2px;
  background:var(--head);
  box-shadow:var(--head-glow);
  transform:translateX(-1px);pointer-events:none}
#ribwrap.drag #ribhead{background:var(--now)}

#ribtip{
  position:fixed;z-index:60;transform:translate(-50%,0);
  padding:8px 12px 7px;border-radius:12px;pointer-events:none;
  background:var(--tip);border:.5px solid var(--stroke-2);
  -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
  box-shadow:var(--shadow);
  opacity:0;transition:opacity .16s var(--ease);
}
#ribtip.on{opacity:1}
#ribtip::after{content:"";position:absolute;left:var(--caret,50%);bottom:-4px;width:8px;height:8px;
  transform:translateX(-50%) rotate(45deg);
  background:rgba(10,16,26,.86);border-right:.5px solid var(--stroke-2);border-bottom:.5px solid var(--stroke-2)}
#ribtip .rt-top{display:flex;gap:8px;align-items:baseline;margin-bottom:2px}
#ribtip .rt-top b{font-size:12px;font-weight:650}
#ribtip .rt-top s{text-decoration:none;font-size:10.5px;color:var(--ink-3)}
#ribtip .rt-row{display:flex;gap:14px;align-items:baseline}
#ribtip .rt-wind{display:flex;align-items:baseline;gap:4px;color:var(--rt,var(--ink))}
#ribtip .rt-wind i{display:inline-grid;place-items:center;width:12px;height:12px;
  align-self:center;font-style:normal}
#ribtip .rt-wind i svg{width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
#ribtip .rt-wind b{font:600 17px var(--mono);font-variant-numeric:tabular-nums}
#ribtip .rt-wind u,#ribtip .rt-sea u{text-decoration:none;font-size:10px;color:var(--ink-3)}
#ribtip .rt-wind s{text-decoration:none;font-size:11px;color:var(--ink-2)}
#ribtip .rt-sea b{font:600 13px var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-2)}

/* While the ribbon is summoned, the legend and the attribution step above
   it rather than layering glass over glass. */
body.timeline #legend{bottom:calc(var(--peek) + 10px + 64px + 12px + var(--safe-b))}
body.timeline #credit{bottom:calc(var(--peek) + 10px + 64px + 12px + var(--safe-b))}

/* Phone: a slimmer dock — the buttons are already small, the glass around
   them no longer needs to be. Legend and credit step above the new height. */
@media (max-width:1079px){
  #tdock{height:56px;border-radius:18px;padding:7px 9px 7px 7px;gap:8px}
  #tdock .tbtn{width:34px;height:34px}
  body.timeline #legend{bottom:calc(var(--peek) + 10px + 56px + 12px + var(--safe-b))}
  body.timeline #credit{bottom:calc(var(--peek) + 10px + 56px + 12px + var(--safe-b))}
  /* The ribbon now spans the full width, so the right-hand button column has
     to step above it rather than sit underneath it. Locate was previously
     buried by the dock whenever the timeline was open. */
  body.timeline #locate{
    bottom:calc(var(--peek) + 10px + 56px + 12px + var(--safe-b))}
  body.timeline #timebtn{
    /* Stay above the chart-filter button while the dock occupies its usual
       strip. This remains the timeline's one exit control. */
    bottom:calc(var(--peek) + 10px + 56px + 12px + 44px + 10px + 44px + 12px + var(--safe-b))}
}

@media (min-width:1080px){
  #timebtn{top:auto;bottom:16px;right:calc(16px + 44px + 10px)}
  #tdock{left:calc(var(--dock) + 16px);right:calc(16px + 44px + 10px + 44px + 10px);
    bottom:16px}
  body.timeline #legend{bottom:calc(16px + 64px + 12px)}
  body.timeline #credit{bottom:calc(16px + 64px + 12px)}
}

/* ---- forecast deck ------------------------------------------------------ */
:root{--peek:110px}
#deck{
  position:absolute;z-index:30;left:0;right:0;bottom:0;
  height:min(86dvh,780px);
  border-radius:var(--radius-lg) var(--radius-lg) 0 0;
  display:flex;flex-direction:column;overflow:hidden;
  transform:translateY(calc(100% - var(--peek) - var(--safe-b)));
  transition:transform .38s var(--ease);
  will-change:transform;
}
#deck.open{transform:translateY(0)}
#deck .grab{position:relative;flex:none;height:18px;display:grid;place-items:center;touch-action:none;cursor:grab}
#deck .grab::after{content:"";width:38px;height:4px;border-radius:999px;background:var(--grab)}

/* The peek row is the deck's handle: tapping or dragging it opens the deck,
   so there is no separate "open forecast" affordance to find. */
#peek{
  position:relative;flex:none;height:calc(var(--peek) - 18px);width:100%;
  display:grid;grid-template-columns:54px minmax(0,1fr) auto;align-items:center;
  gap:14px;padding:0 20px 6px;text-align:left;
}
#peek .art{width:54px;height:54px}
#peek .place{min-width:0;display:flex;flex-direction:column;gap:2px}
#peek .place b{font-size:17px;font-weight:650;letter-spacing:-.02em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#peek .place span{font-size:12.5px;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#peek .wind{display:flex;align-items:baseline;gap:5px}
#peek .wind b{font-size:30px;font-weight:600}
#peek .wind i{font-size:12px;font-style:normal;color:var(--ink-3)}

#deckBody{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  padding:0 20px calc(28px + var(--safe-b))}
#deck:not(.open) #deckBody{opacity:0;visibility:hidden;pointer-events:none}
#deck.open #deckBody{opacity:1;visibility:visible;transition:opacity .2s var(--ease) .1s}

/* The collapsed summary keeps its map clearance. Once expanded, reclaim
   the extra handle space for reading; desktop retains its own header. */
@media (max-width:1079px){
  #deck.open #peek{height:78px;padding-bottom:8px}
}

/* ---- menu sheet --------------------------------------------------------- */
#scrim{position:absolute;inset:0;z-index:40;background:var(--scrim);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  opacity:0;pointer-events:none;transition:opacity .22s var(--ease)}
body.menu #scrim{opacity:1;pointer-events:auto}

#menu{
  position:absolute;z-index:41;left:10px;right:10px;top:calc(10px + var(--safe-t));
  border-radius:var(--radius-lg);padding:12px;
  transform:translateY(-12px) scale(.97);opacity:0;visibility:hidden;pointer-events:none;
  transform-origin:top center;
  transition:transform .26s var(--ease),opacity .2s var(--ease);
  max-height:min(76dvh,680px);overflow-y:auto;overscroll-behavior:contain;
}
body.menu #menu{transform:none;opacity:1;visibility:visible;pointer-events:auto}
#menu .search{display:flex;align-items:center;gap:9px;height:40px;padding:0 12px;
  border-radius:var(--radius-sm);background:var(--sunk);border:.5px solid var(--stroke);
  margin-bottom:14px}
#menu .search svg{width:17px;height:17px;flex:none;opacity:.5;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round}
#menu .search input{flex:1;min-width:0;height:40px;background:none;border:0;outline:none;
  font-size:16px}
#menu .search input::placeholder{color:var(--ink-3)}
#menu .group{margin-bottom:10px}
#menu .group>.eyebrow{display:block;margin:0 2px 6px}
#menu .wrap{display:flex;flex-wrap:wrap;gap:5px}
#menu .seg{grid-auto-columns:1fr}
#menu .rows{display:grid;gap:3px}
#menu .row{display:flex;align-items:center;gap:10px;height:42px;padding:0 10px;
  border-radius:var(--radius-sm);font-size:14px;font-weight:550;text-align:left;
  transition:background .14s var(--ease)}
@media (hover:hover) and (pointer:fine){ #menu .row:hover{background:var(--fill)} }
#menu .row svg{width:19px;height:19px;flex:none;opacity:.75;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
#menu .row .go{margin-left:auto;opacity:.3}
#results{display:grid;gap:2px;margin-bottom:12px}
#results button{display:flex;flex-direction:column;align-items:flex-start;gap:1px;
  padding:9px 12px;border-radius:var(--radius-sm);text-align:left}
@media (hover:hover) and (pointer:fine){ #results button:hover{background:var(--fill)} }
#results b{font-size:14.5px;font-weight:600}
#results span{font-size:11.5px;color:var(--ink-3)}

@media (max-width:1079px){
  #menu .chip{height:34px;padding:0 11px;font-size:12.5px}
  #menu .chip svg{width:15px;height:15px}
  #menu .mode{height:36px;padding:0 9px;font-size:12.5px;gap:5px}
  #menu .mode .mi{width:15px;height:15px}
  #menu .mode .base{font-size:8.5px;height:17px;line-height:17px;padding:0 5px}
}

/* ---- desktop: a dashboard, not a stretched phone ------------------------ */
@media (min-width:1080px){
  :root{--dock:400px}

  /* Map is the hero and is never covered: the dashboard takes its own column
     of the window rather than floating a card on top of the map. */
  #map,#particles{left:var(--dock)}
  #credit{left:calc(var(--dock) + 14px);bottom:12px}
  /* The legend takes the bottom-left corner when there is a field to explain,
     so the attribution steps to its right rather than under it. */
  body.legend #credit{left:calc(var(--dock) + 16px + 186px + 14px);bottom:16px}

  /* Brand becomes the dashboard's header — a bare wordmark, not a glass
     bar. A boxed pill here read as a nav bar fighting the deck under it. */
  #brand{top:16px;left:16px;transform:none;z-index:42;
    width:calc(var(--dock) - 32px);height:46px;justify-content:flex-start;
    background:none;border:0;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;
    padding:0 6px}
  #brand:active{transform:scale(.99)}
  #brand .caret{margin-left:auto}

  #deck{
    top:74px;left:0;right:auto;bottom:0;
    height:auto;width:var(--dock);
    border-radius:0;border-right:.5px solid var(--stroke-2);border-top:0;
    transform:none!important;box-shadow:none;
  }
  #deck .grab{display:none}
  #deck #deckBody{opacity:1!important;visibility:visible!important;pointer-events:auto!important;padding:0 24px 28px}
  #peek{
    height:auto;padding:4px 24px 18px;border-bottom:.5px solid var(--stroke);
    grid-template-columns:58px minmax(0,1fr);cursor:default;
  }
  #peek .art{width:58px;height:58px}
  #peek .wind{display:none}   /* the hero inside the deck states it far better */

  /* Menu drops from the brand button, inside the dashboard column. */
  #menu{left:16px;right:auto;top:70px;width:calc(var(--dock) - 32px);
    max-height:calc(100dvh - 90px)}

  /* The map's own control sits in the map's own corner. */
  #locate{right:16px;bottom:auto;top:16px}
}

/* ---- seabed controls ----------------------------------------------------
   Only present in the seabed view. Bottom-left on a phone so it clears both
   the deck and the locate button; beside the dock on desktop. */
#seabedCtl{
  position:absolute;z-index:22;left:12px;
  bottom:calc(var(--peek) + 18px + var(--safe-b));
  width:min(268px,calc(100vw - 80px));
  padding:13px;border-radius:var(--radius);
  display:none;flex-direction:column;gap:12px;
}
body.seabed #seabedCtl{display:flex}
#seabedCtl .row{display:flex;flex-direction:column;gap:7px}
#seabedCtl .eyebrow{display:flex;justify-content:space-between;align-items:baseline}
#seabedCtl .eyebrow b{color:var(--signal);font-size:11px;letter-spacing:0}
#seabedCtl .seg{grid-auto-columns:1fr}
#seabedCtl .seg button{height:30px;font-size:12px}
#seabedCtl .chip{width:100%;justify-content:flex-start}
#seabedCtl .note{margin:0;font-size:10.5px;line-height:1.45;color:var(--ink-3)}
#seabedCtl .note b{color:var(--ink-2)}
input[type=range]{
  -webkit-appearance:none;appearance:none;width:100%;height:4px;border-radius:999px;
  background:var(--raise-2);outline:none;
}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:18px;height:18px;border-radius:999px;
  background:var(--ink);box-shadow:var(--thumb-shadow);cursor:grab;
}
input[type=range]::-moz-range-thumb{
  width:18px;height:18px;border:0;border-radius:999px;background:var(--ink);cursor:grab;
}

/* ---- view mode: two instruments ------------------------------------------
   One track, two keys: Weather and Seabed 3D. The base map the weather reads
   over is not a peer of these — it is a setting of the Weather key, shown as
   a tag on it and changed through the disclosure beside it. */
.modes{
  display:flex;align-items:center;gap:3px;padding:3px;border-radius:999px;
  background:var(--sunk);border:.5px solid var(--stroke);
  /* The track is the boundary. Without this, a long base tag ("REEF CHART")
     pushes the Weather key wider than its share and the icon escapes the
     pill's left edge — flex children with nowrap cannot shrink on their own. */
  overflow:hidden;
}
.mode{
  /* flex:1 is 1 1 0% — it ignores content and splits the track 50/50, which
     starves the Weather key: it carries an icon, a word, a base tag AND a
     caret, where Seabed 3D carries an icon and a word. `1 1 auto` grows both
     from their CONTENT width instead, so each gets what it needs and the
     slack is shared. Nothing ellipsises until the track genuinely cannot
     hold them. */
  flex:1 1 auto;min-width:0;display:flex;align-items:center;justify-content:center;gap:7px;
  height:40px;padding:0 12px;border-radius:999px;overflow:hidden;
  color:var(--ink-2);font-size:13.5px;font-weight:600;
  transition:background .18s var(--ease),color .18s var(--ease);
}
@media (hover:hover) and (pointer:fine){ .mode:hover{background:var(--fill)} }
/* The word is the first thing allowed to give way — an ellipsised "Weathe…"
   beside a legible base tag beats a pill whose icon has fallen out of it. */
.mode b{font-weight:650;letter-spacing:-.01em;white-space:nowrap;
  min-width:0;overflow:hidden;text-overflow:ellipsis}
.mode .mi{width:17px;height:17px;flex:none;opacity:.8;
  fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.mode[aria-pressed="true"]{
  background:color-mix(in srgb,var(--signal) 22%,transparent);
  color:var(--ink);box-shadow:inset 0 .5px 0 var(--glass-hi);
}
.mode[aria-pressed="true"] .mi{opacity:1}
/* The chosen base, printed on the control. A disclosure that hides the
   current value forces a trip into the menu just to read the state. */
.mode .base{
  flex:0 1 auto;min-width:0;max-width:96px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  height:19px;line-height:19px;padding:0 7px;border-radius:999px;
  background:var(--raise);
  font-size:9.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-2);
}
.mode[aria-pressed="true"] .base{background:var(--raise-2);color:var(--ink)}
.mode .mc{width:12px;height:12px;flex:none;opacity:.55;
  fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .2s var(--ease),opacity .2s var(--ease)}
.mode[aria-expanded="true"] .mc{transform:rotate(180deg);opacity:.95}

/* ---- base map picker ------------------------------------------------------
   Fixed, anchored in script, and deliberately verbose: each base is named AND
   says what it is for, because "Chart" and "Satellite" do not tell a first-time
   user which one shows the reef. */
#basemenu{
  position:fixed;z-index:60;width:286px;max-width:calc(100vw - 20px);
  padding:6px;border-radius:18px;
  display:flex;flex-direction:column;gap:2px;
  opacity:0;transform:translateY(-6px) scale(.98);transform-origin:top left;
  transition:opacity .18s var(--ease),transform .18s var(--ease);
}
#basemenu[hidden]{display:none}
#basemenu.on{opacity:1;transform:none}
#basemenu button{
  display:grid;grid-template-columns:20px minmax(0,1fr) 16px;align-items:center;gap:11px;
  padding:9px 11px;border-radius:13px;text-align:left;color:var(--ink-2);
  transition:background .14s var(--ease);
}
@media (hover:hover) and (pointer:fine){ #basemenu button:hover{background:var(--fill)} }
#basemenu .bi{width:20px;height:20px;opacity:.72;
  fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
#basemenu span{min-width:0;display:block}
#basemenu b{display:block;font-size:13.5px;font-weight:650;color:var(--ink);letter-spacing:-.01em}
#basemenu small{display:block;margin-top:2px;font-size:11px;line-height:1.35;color:var(--ink-3)}
#basemenu .bk{width:16px;height:16px;opacity:0;color:var(--signal);
  fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
#basemenu button[aria-checked="true"]{background:color-mix(in srgb,var(--signal) 13%,transparent)}
#basemenu button[aria-checked="true"] .bk{opacity:1}
#basemenu button[aria-checked="true"] .bi{opacity:1;color:var(--signal)}

/* The iOS haptic actuator. It must be in the layout tree and activatable —
   display:none or visibility:hidden make the gesture a no-op — so it is
   parked off-screen at 1px instead. */
/* THE SWITCH MUST KEEP ITS NATIVE APPEARANCE.

   `appearance:none` was in this rule and it is why no iPhone ever ticked: it
   turns <input type="checkbox" switch> back into an ordinary checkbox, and an
   ordinary checkbox has no haptic. Nor may this be display:none or
   visibility:hidden — the control has to be laid out and painted to be a
   switch at all. Transparent, real-sized and behind everything is the shape
   that works, and it is what the published implementations use. */
#hapticSwitch,#hapticTap{
  position:fixed;bottom:0;left:0;width:44px;height:26px;
  margin:0;padding:0;border:0;opacity:0;
  /* No pointer-events:none and no negative z-index. Both were here and both
     are gambles: an inert or un-painted control is one WebKit is entitled to
     skip, and skipping it means no haptic. It does not need hiding anyway —
     the forecast deck is opaque, fixed to the bottom of the screen and
     z-index 30, so nothing at bottom-left is reachable by a finger. */
  touch-action:manipulation;-webkit-tap-highlight-color:transparent;
}

/* The direct-touch pad: invisible, fills the ribbon, and DOES take pointer
   events, because being touched for real is the entire point. It sits above
   the canvas so the finger lands on it, below #tnow (z-index 3) so that pill
   still takes its own taps, and it is transparent so #ribhead reads through
   it. No appearance override here either, for the same reason as above. */
/* One recipe, three hosts. iOS 26.5 closed every programmatic route to the
   Taptic Engine, so the ONLY thing that still ticks is a real finger landing
   on a real switch — measured on 26.6.2: a direct tap fires, a scripted one
   does not. Each control that should tick therefore carries its own
   invisible switch, sized to the control, and the events bubble through to
   the handler underneath so nothing about the interaction changes. */
.hapticpad{
  position:absolute;inset:0;width:100%;height:100%;
  margin:0;padding:0;border:0;opacity:0;z-index:1;
  -webkit-tap-highlight-color:transparent;
}
#ribwrap .hapticpad{touch-action:none}
#tdock .tbtn,#tnow{position:relative}
/* The pill's own label must stay above its pad, or the pad eats the glyph's
   place in the flex row. */
#tnow svg,#tnow span{position:relative;z-index:2}

/* ---- tap popover ----------------------------------------------------------
   A small pane of glass pinned to a point on the water. Flat fill, one
   hairline, no gradient — the colour in it belongs to the wind, not the UI. */
#tap{
  position:absolute;z-index:26;width:238px;padding:13px 14px 11px;
  border-radius:var(--radius-sm);
  transform:translate(-50%,calc(-100% - 14px));
}
#tap[hidden]{display:none}
#tap .t-title{margin-bottom:7px;padding-bottom:7px;
  border-bottom:.5px solid var(--stroke)}
#tap .t-title[hidden]{display:none}
#tap .t-title b{display:block;font-size:14px;font-weight:650;letter-spacing:-.01em}
#tap .t-title small{display:block;margin-top:2px;font-size:11px;line-height:1.35;
  color:var(--ink-3)}
#tap .t-read{display:flex;align-items:baseline;gap:6px}
#tap .t-read i{display:inline-grid;place-items:center;width:15px;height:15px;
  align-self:center;font-style:normal}
#tap .t-read i svg{width:13px;height:13px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
#tap .t-read .num{font-size:24px;font-weight:600}
#tap .t-read u{text-decoration:none;font-size:11px;color:var(--ink-3)}
#tap .t-read s{text-decoration:none;font-size:12.5px;color:var(--ink-2)}
#tap .t-sea{margin-left:auto;font-size:12.5px;color:var(--ink-2)}
#tap .t-near{margin-top:3px;font-size:12px;color:var(--ink-2)}
#tap .t-coords{margin-top:1px;font:11px var(--mono);color:var(--ink-3)}
#tap .t-name{margin-top:9px}
#tap .t-name input{
  width:100%;height:34px;padding:0 10px;border-radius:10px;
  background:var(--sunk);border:.5px solid var(--stroke);
  color:var(--ink);font-size:13.5px;outline:none;
}
#tap .t-name input:focus{border-color:color-mix(in srgb,var(--signal) 45%,transparent)}
/* A GRID, NOT A WRAP. Flex-wrap sized every button to its own label, so four
   buttons fell into a ragged 1 + 2 + 1 with three different widths and no
   common edge — the layout reads as an accident because it is one. Two equal
   columns give every button the same width and a shared centre line, and the
   :nth-child(odd):last-child rule spans a lone trailing button across both so
   an odd count can never leave an orphan hanging in column one. The set stays
   symmetrical at four buttons and at five. */
#tap .t-actions{
  display:grid;grid-template-columns:repeat(2,1fr);gap:6px;margin-top:12px;
}
#tap .t-actions button{
  height:34px;padding:0 10px;border-radius:999px;
  display:flex;align-items:center;justify-content:center;
  background:var(--fill);border:.5px solid var(--stroke);
  color:var(--ink-2);font-size:12.5px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:background .16s var(--ease);
}
#tap .t-actions button:nth-child(odd):last-child{grid-column:1 / -1}
#tap .t-actions button[hidden]{display:none}
@media (hover:hover) and (pointer:fine){ #tap .t-actions button:hover{background:var(--fill-2)} }
#tap .t-actions button.primary{
  background:color-mix(in srgb,var(--signal) 22%,transparent);
  border-color:color-mix(in srgb,var(--signal) 45%,transparent);
  color:var(--ink);
}

/* ---- saved marks list (menu) -------------------------------------------- */
#marksList{display:grid;gap:3px;margin-top:9px}
#marksList:empty{display:none}
#marksList .mkempty{margin:0;font-size:12px;color:var(--ink-3)}
#marksList .mkrow{display:flex;align-items:center;gap:9px;height:38px;
  padding:0 6px 0 11px;border-radius:var(--radius-sm)}
@media (hover:hover) and (pointer:fine){ #marksList .mkrow:hover{background:var(--fill)} }
#marksList .mkdot{width:9px;height:9px;border-radius:999px;flex:none;
  background:color-mix(in srgb,var(--signal) 30%,transparent);border:1.4px solid var(--signal)}
#marksList .mkname{flex:1;min-width:0;background:none;border:0;outline:none;
  font-size:13.5px;color:var(--ink);border-radius:6px;padding:4px 2px}
#marksList .mkname:focus{background:var(--sunk)}
@media (pointer:coarse), (max-width:1079px){
  #tap .t-name input,#marksList .mkname{font-size:16px}
}
#marksList .mkdel{width:28px;height:28px;flex:none;border-radius:999px;
  display:grid;place-items:center;color:var(--ink-3)}
@media (hover:hover) and (pointer:fine){ #marksList .mkdel:hover{background:var(--fill-2);color:var(--ink)} }
#marksList .mkdel svg{width:12px;height:12px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round}

/* ---- map legend for whichever field is showing --------------------------- */
#legend{
  position:absolute;z-index:21;left:12px;
  /* Clears the attribution line, which owns the strip immediately above the
     deck — two translucent things in the same corner is unreadable. */
  bottom:calc(var(--peek) + 44px + var(--safe-b));
  padding:10px 9px;border-radius:var(--radius-sm);width:44px;
  display:none;
}
body.legend #legend{display:block}
body.seabed #legend{display:none}
#legend .eyebrow{display:none}
#legend .ramp{height:122px;width:7px;margin:0 auto;border-radius:999px}
#legend .ticks{height:122px;position:absolute;left:49px;top:9px;display:flex;flex-direction:column-reverse;
  justify-content:space-between;font:9.5px var(--mono);color:var(--ink-3);white-space:nowrap}

@media (min-width:1080px){
  #seabedCtl,#legend{left:calc(var(--dock) + 16px);bottom:16px}
  #legend{bottom:16px}
  body.seabed #legend{display:none}
}

/* Seabed settings live in the menu with the view that needs them. */
#menu .slider{display:flex;flex-direction:column;gap:8px}
#menu .slider .eyebrow{display:flex;justify-content:space-between;align-items:baseline}
#menu .slider .eyebrow b{color:var(--signal);font-size:11px;letter-spacing:0}
/* Weather overlays cannot apply to a seabed render, so they are visibly
   unavailable there rather than silently doing nothing. */
#overlayGroup[data-off="1"]{opacity:.34;pointer-events:none}
#overlayGroup .note{margin:7px 2px 0;font-size:11px;color:var(--ink-3)}

/* ═══ Desktop ═════════════════════════════════════════════════════════════
   Not the phone widened. A phone hides everything behind one door because it
   has one screenful; a desktop has room for the map's controls to be ambient
   and for two reading columns at once, so it gets both — and the menu
   shrinks to what is left: search and the destinations. */

#rail{display:none}
/* Before the controls are relocated into it, the rail has nothing to hold —
   a bare nub of glass over the map for the length of one boot is worse than
   no rail at all. */
#rail:empty{display:none!important}

@media (min-width:1080px){
  /* The map's controls live on the map, centred over it rather than over the
     dashboard, so the segment reads as belonging to what it changes. */
  /* One deliberate horizontal control rail. It never becomes a broken second
     row or lets a control escape the glass; narrower desktop widths scroll. */
  #rail{
    display:flex;align-items:center;justify-content:flex-start;
    box-sizing:border-box;flex-wrap:nowrap;gap:16px;overflow-x:auto;overflow-y:hidden;
    scrollbar-width:none;
    position:absolute;z-index:30;top:16px;
    left:calc(var(--dock) + 16px);right:auto;
    /* Hug the controls rather than stretching a bar of glass across the map:
       in the seabed view there are three of them, and an empty half-window of
       chrome over the chart is chrome that is not earning its place. Overflow
       still scrolls once the controls genuinely need more than the map's
       width. */
    width:max-content;max-width:calc(100% - var(--dock) - 32px);
    /* Both ends reserve a full control gap. Scrollable content can never sit
       under the rounded glass edge or look like it has escaped its rail. */
    padding:8px 28px 8px 20px;border-radius:var(--radius);
    scroll-padding-inline:20px;
  }
  #rail .chip{height:32px;padding:0 11px;font-size:12.5px}
  #rail .chip svg{width:15px;height:15px}
  /* A segmented control is a 36px physical track: 3px inset + a 30px
     button + 3px inset. The prior 32px declaration forced the selected
     button and its focus outline outside the rail. */
  #rail .seg{height:36px;padding:3px;overflow:hidden}
  #rail .seg button{height:30px;line-height:30px;font-size:12.5px}
  #rail::-webkit-scrollbar{display:none}
  #rail .group{display:flex;align-items:center;gap:12px;margin:0;min-width:max-content;flex:none}
  /* display:flex would otherwise beat the hidden attribute and pin the seabed
     controls to the rail in every view. */
  #rail .group[hidden],#rail .divider[hidden]{display:none}
  #rail .group>.eyebrow{display:none}   /* the controls name themselves here */
  #rail .seg{display:inline-grid;grid-auto-flow:column;grid-auto-columns:max-content}
  #rail .seg button{padding:0 13px}
  /* The mode track keeps its shape on the rail — only its scale changes. */
  #rail .modes{flex:none}
  #rail .mode{flex:none;height:34px;padding:0 13px;gap:7px;font-size:12.5px}
  #rail .mode .mi{width:15px;height:15px}
  #rail .mode .base{height:17px;line-height:17px;font-size:9px;padding:0 6px}
  /* Keep keyboard focus visible without allowing its halo to cross the glass
     rail's rounded boundary. */
  #rail .seg button:focus-visible{outline-offset:-2px}
  #rail .wrap{display:flex;gap:6px;flex-wrap:nowrap}
  #rail #seabedGroup{gap:16px}
  #rail #seabedGroup .slider{flex-direction:row;align-items:center;gap:10px;width:auto}
  /* In the rail the label sits BESIDE its slider, so the space-between that
     spreads name and value apart in the menu sheet would open a gap wide
     enough to read as two separate controls. */
  #rail #seabedGroup .slider .eyebrow{display:flex;gap:6px;justify-content:flex-start;
    white-space:nowrap;font-size:9.5px}
  #rail #seabedGroup input[type=range]{width:104px;margin-top:0;flex:none}
  #rail .divider{width:.5px;height:24px;flex:none;background:var(--stroke-2)}
  #rail #overlayGroup .note{display:none}
  #rail #overlayGroup[data-off="1"]{opacity:.3}

  /* The rail owns the top of the map, so the map's own button steps below it
     rather than colliding with it. */
  /* Bottom-right of the map: out of the rail's way at every width, and in the
     corner a desktop user already associates with map controls. */
  #locate{top:auto;bottom:16px;right:16px}
  #rail .wrap .chip,#rail .seg button{white-space:nowrap}

  /* Menu is now only what the rail cannot hold: find a place, go somewhere. */
  #menu .group:empty{display:none}
}

/* Wide desktops preserve the map's full width: the former Marine Rhythm
   side drawer duplicated forecast data and displaced the primary surface. */
@media (min-width:1500px){
  #map,#particles{right:0}
  /* The map's button belongs in the map's corner, not under the second
     column. */
  #locate{right:16px;bottom:16px;top:auto}
  #credit{right:14px}
  #legend{left:calc(var(--dock) + 16px)}
}
@media (min-width:1080px) and (hover:hover) and (pointer:fine){
  #brand:hover{background:var(--fill);border-radius:999px}
}


/* ---- chart key & search --------------------------------------------------
   Same disclosure grammar as the base picker: one button, one glass sheet,
   gone when dismissed. Sits under the timeline button on the right rail so
   it never competes with the map for standing space. */
#chartbtn{
  position:absolute;z-index:20;right:12px;
  bottom:calc(var(--peek) + 18px + 44px + 12px + 44px + 12px + var(--safe-b));
}
#chartbtn[hidden]{display:none}
#chartbtn[aria-expanded="true"]{
  background:color-mix(in srgb,var(--signal) 22%,transparent);
  border-color:color-mix(in srgb,var(--signal) 45%,transparent);
}
@media (min-width:1080px){
  #chartbtn{top:auto;bottom:16px;right:calc(16px + 44px + 10px + 44px + 10px)}
}

#chartpanel{
  position:fixed;z-index:60;width:296px;max-width:calc(100vw - 20px);
  max-height:min(70dvh,560px);overflow-y:auto;overscroll-behavior:contain;
  padding:10px;border-radius:18px;
  opacity:0;transform:translateY(6px) scale(.98);transform-origin:bottom right;
  transition:opacity .18s var(--ease),transform .18s var(--ease);
}
#chartpanel[hidden]{display:none}
#chartpanel.on{opacity:1;transform:none}

#chartpanel .csearch{display:flex;align-items:center;gap:9px;height:38px;padding:0 12px;
  border-radius:12px;background:var(--sunk);border:.5px solid var(--stroke);margin-bottom:8px}
#chartpanel .csearch svg{width:15px;height:15px;flex:none;opacity:.5;fill:none;
  stroke:currentColor;stroke-width:2;stroke-linecap:round}
#chartpanel .csearch input{flex:1;min-width:0;height:38px;background:none;border:0;outline:none;
  font-size:16px;color:var(--ink)}
#chartpanel .csearch input::placeholder{color:var(--ink-3);font-size:13px}

#cresults{display:grid;gap:2px}
#cresults:empty{display:none}
#cresults button{display:flex;align-items:baseline;gap:8px;padding:8px 10px;border-radius:11px;
  text-align:left;width:100%}
@media (hover:hover) and (pointer:fine){ #cresults button:hover{background:var(--fill)} }
#cresults b{font-size:13.5px;font-weight:600;color:var(--ink)}
#cresults span{font-size:10.5px;color:var(--ink-3);margin-left:auto;white-space:nowrap}

/* A group is a heading you can switch off, and its keys are the legend. */
#cgroups .cg{margin-top:10px}
#cgroups .cgh{display:flex;align-items:center;gap:9px;width:100%;padding:6px 8px;
  border-radius:10px;text-align:left}
@media (hover:hover) and (pointer:fine){ #cgroups .cgh:hover{background:var(--fill)} }
#cgroups .cgh b{flex:1;font-size:12.5px;font-weight:650;letter-spacing:-.01em}
#cgroups .cgh .sw{width:30px;height:17px;border-radius:999px;flex:none;position:relative;
  background:var(--raise-2);transition:background .16s var(--ease)}
#cgroups .cgh .sw::after{content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;
  border-radius:999px;background:var(--ink);transition:transform .16s var(--ease)}
#cgroups .cgh[aria-pressed="true"] .sw{background:color-mix(in srgb,var(--signal) 60%,transparent)}
#cgroups .cgh[aria-pressed="true"] .sw::after{transform:translateX(13px)}
#cgroups .keys{display:grid;gap:3px;padding:2px 8px 0 10px}
#cgroups .cg[data-off="1"] .keys{opacity:.35}
#cgroups .keys div{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--ink-2)}
#cgroups .keys i{width:14px;height:14px;border-radius:3px;flex:none}
/* Each swatch is the shape of the thing it explains. */
#cgroups .keys i.ln{height:0;border-radius:0;border-top:2px solid currentColor;
  background:none!important;box-shadow:inset 0 0 0 99px currentColor}
#cgroups .keys i.ln{height:2px;border:0}
#cgroups .keys i.dsh{height:2px;border-radius:0;background:none;
  background-image:repeating-linear-gradient(to right,var(--c) 0 4px,transparent 4px 7px)}
#cgroups .keys i.dt{border-radius:999px;width:9px;height:9px}
#cgroups .keys i.sp{width:16px;height:16px;border-radius:0;background-repeat:no-repeat;
  background-color:transparent;image-rendering:auto}
#cgroups .keys u{text-decoration:none;color:var(--ink-3);font-size:10px;margin-left:auto;
  white-space:nowrap}

/* The signature. 10px, ink-3, no border, no button — if it reads as a call to
   action it has failed. */
.madeby{
  margin:10px 2px 0;text-align:center;
  font-size:10px;line-height:1.4;color:var(--ink-3);letter-spacing:.01em;
}
.madeby a{color:var(--ink-2);text-decoration:none;border-bottom:.5px solid var(--stroke-2)}
@media (hover:hover) and (pointer:fine){ .madeby a:hover{color:var(--signal)} }
.madeby span{color:var(--rough);font-size:9px}

/* ---- appearance ----------------------------------------------------------
   The quietest row in the menu. The eyebrow and the track share a baseline so
   it reads as one line of settings rather than a section, and the keys are
   icon-led with the word beside them — at 11px the word is what makes "Auto"
   unambiguous, and the icon is what makes the row scannable. */
.themerow{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin:2px 0 0;padding-top:10px;border-top:.5px solid var(--stroke)}
.themerow .eyebrow{flex:none}
#themeseg{flex:none;grid-auto-columns:1fr}
#themeseg button{display:flex;align-items:center;justify-content:center;gap:5px;
  height:30px;padding:0 10px;font-size:11.5px}
#themeseg svg{width:13px;height:13px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
#themeseg button[aria-pressed="true"] svg{stroke-width:1.9}
@media (max-width:420px){ #themeseg button span{display:none} #themeseg button{padding:0 13px} }

/* ---- credit + sources ----------------------------------------------------
   The map carries a one-word control and, where it matters, the safety line.
   Everything the licences require is one tap away — the accepted reading of
   "attribution reasonable to the medium" for a map, and what every major map
   provider ships. */
#credit{
  position:absolute;z-index:3;left:12px;bottom:calc(var(--peek) + 10px + var(--safe-b));
  display:flex;align-items:center;gap:7px;max-width:min(70vw,520px);
  font-size:9px;line-height:1.3;pointer-events:none;
}
#creditWarn{color:var(--credit);text-shadow:var(--credit-shadow);
  font-weight:700;letter-spacing:.07em}
#creditWarn:empty{display:none}
#creditBtn{
  pointer-events:auto;flex:none;
  font-size:9px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;
  color:var(--credit);text-shadow:var(--credit-shadow);
  border-bottom:.5px solid var(--stroke-2);padding:0 0 1px;
  transition:color .16s var(--ease);
}
@media (hover:hover) and (pointer:fine){ #creditBtn:hover{color:var(--ink-2)} }

#sourcesWrap[hidden]{display:none}
#sourcesScrim{position:fixed;inset:0;z-index:70;background:var(--scrim);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
#sources{
  position:fixed;z-index:71;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(440px,calc(100vw - 28px));max-height:min(80dvh,660px);
  border-radius:var(--radius-lg);overflow:hidden;
  animation:srcIn .22s var(--ease);
}
@keyframes srcIn{from{opacity:0;transform:translate(-50%,-46%) scale(.97)}}
#sourcesClose{position:absolute;top:8px;right:8px;z-index:2;color:var(--ink-2)}
#sourcesClose svg{width:15px;height:15px;stroke-width:2}
#sources .sbody{padding:22px 22px 20px;overflow-y:auto;max-height:inherit}
#sources h2{margin:0 0 10px;font-size:19px;font-weight:650;letter-spacing:-.02em}
#sources h3{margin:20px 0 8px;font-size:10px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3)}
#sources p{margin:0 0 10px;font-size:13px;line-height:1.5;color:var(--ink-2)}
#sources .src{margin:0;display:grid;gap:9px}
#sources .src dt{font-size:11.5px;font-weight:650;color:var(--ink)}
#sources .src dd{margin:1px 0 0;font-size:11.5px;line-height:1.45;color:var(--ink-3)}
#sources .warn{margin-top:18px;font-size:10.5px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--rough)}
#sources .madeby{margin-top:14px;text-align:left}

/* ---- the one suggestion bar ---------------------------------------------- */
#tipbar{
  position:absolute;z-index:30;left:12px;right:12px;
  bottom:calc(var(--peek) + 14px + var(--safe-b));
  display:flex;align-items:center;gap:10px;padding:9px 8px 9px 12px;
  border-radius:14px;
  animation:tipIn .3s var(--ease) .6s both;
}
#tipbar[hidden]{display:none}
@keyframes tipIn{from{opacity:0;transform:translateY(8px)}}
#tipbar .ti{width:16px;height:16px;flex:none;opacity:.7;
  fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
#tipbar p{margin:0;flex:1;min-width:0;font-size:11.5px;line-height:1.35;color:var(--ink-2)}
#tipbar b{color:var(--ink);font-weight:650}
#tipbar .tshare{width:11px;height:11px;vertical-align:-1px;margin:0 1px;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
#tipbar #tipX{flex:none;width:28px;height:28px;border-radius:999px;display:grid;
  place-items:center;color:var(--ink-3)}
@media (hover:hover) and (pointer:fine){ #tipbar #tipX:hover{background:var(--fill-2);color:var(--ink)} }
#tipbar #tipX svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round}
